1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Agent role methods for [`IronflowClient`]: the cost each role
//! consumed over its period, and its cap.
use ironflow_types::ApiResponse;
use serde::Serialize;
use crate::client::IronflowClient;
use crate::error::Error;
use crate::provider_account_methods::segment;
use crate::types;
/// Page of `GET /api/v1/agent-roles`.
#[derive(Debug, Serialize)]
struct PageQuery {
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u32>,
}
/// Path segment of a role name. Dots are encoded too, so a name such as `..`
/// can never be resolved as a relative path by the URL parser.
fn role_segment(name: &str) -> String {
segment(name).replace('.', "%2E")
}
impl IronflowClient {
/// List the agent roles, ordered by name, with their cost and cap.
///
/// `page` is 1-based (default 1); `per_page` defaults to 50, max 100.
///
/// # Errors
///
/// Returns an error if the HTTP request fails, the API key lacks the
/// `stats_read` scope, or the response cannot be deserialized.
///
/// # Examples
///
/// ```no_run
/// use ironflow_sdk::IronflowClient;
///
/// # async fn example() -> Result<(), ironflow_sdk::Error> {
/// let client = IronflowClient::new("https://ironflow.example.com", "key");
/// let roles = client.list_agent_roles(None, None).await?;
/// for role in &roles.data {
/// println!("{}: {} spent", role.name, role.spent_usd);
/// }
/// # Ok(())
/// # }
/// ```
pub async fn list_agent_roles(
&self,
page: Option<u32>,
per_page: Option<u32>,
) -> Result<ApiResponse<Vec<types::AgentRoleResponse>>, Error> {
let query = PageQuery { page, per_page };
self.send_envelope(self.get("/api/v1/agent-roles").query(&query))
.await
}
/// Get the cost spent, reserved and left of one agent role.
///
/// # Errors
///
/// Returns an error if the HTTP request fails, the name is invalid, no
/// step of the role ever ran, or the response cannot be deserialized.
///
/// # Examples
///
/// ```no_run
/// use ironflow_sdk::IronflowClient;
///
/// # async fn example() -> Result<(), ironflow_sdk::Error> {
/// let client = IronflowClient::new("https://ironflow.example.com", "key");
/// let role = client.get_agent_role("reviewer").await?;
/// println!("{:?} left", role.data.remaining_usd);
/// # Ok(())
/// # }
/// ```
pub async fn get_agent_role(
&self,
name: &str,
) -> Result<ApiResponse<types::AgentRoleResponse>, Error> {
self.send_envelope(self.get(&format!("/api/v1/agent-roles/{}", role_segment(name))))
.await
}
/// Set or clear (`operator_cap_usd: None`) the operator cap of an agent
/// role. Admin only. The cap enforced is the lower of the code and
/// operator caps: an operator cap can only lower it.
///
/// # Errors
///
/// Returns an error if the HTTP request fails, the caller is not an
/// admin, the cap is negative, the role does not exist, or the response
/// cannot be deserialized.
///
/// # Examples
///
/// ```no_run
/// use ironflow_sdk::IronflowClient;
/// use ironflow_sdk::types::UpdateAgentRoleCostCapRequest;
///
/// # async fn example() -> Result<(), ironflow_sdk::Error> {
/// let client = IronflowClient::new("https://ironflow.example.com", "key");
/// let request = UpdateAgentRoleCostCapRequest {
/// operator_cap_usd: Some(5.0),
/// };
/// let role = client.set_agent_role_cost_cap("reviewer", &request).await?;
/// println!("{:?} cap", role.data.cap_usd);
/// # Ok(())
/// # }
/// ```
pub async fn set_agent_role_cost_cap(
&self,
name: &str,
request: &types::UpdateAgentRoleCostCapRequest,
) -> Result<ApiResponse<types::AgentRoleResponse>, Error> {
self.send_envelope(
self.put(&format!(
"/api/v1/agent-roles/{}/cost-cap",
role_segment(name)
))
.json(request),
)
.await
}
}
#[cfg(test)]
mod tests {
use super::role_segment;
#[test]
fn role_segment_encodes_dots_and_separators() {
assert_eq!(role_segment("code-reviewer"), "code-reviewer");
assert_eq!(role_segment("reviewer.v2"), "reviewer%2Ev2");
assert_eq!(role_segment(".."), "%2E%2E");
assert_eq!(role_segment("a/b"), "a%2Fb");
}
}