objectiveai_sdk/cli/command/laboratories/create/
mod.rs1use std::str::FromStr;
9
10use crate::cli::command::CommandRequest;
11use crate::cli::command::path_ref::tokenize;
12
13fn default_cwd() -> String {
16 "/".to_string()
17}
18
19#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
20#[schemars(rename = "cli.command.laboratories.create.Request")]
21pub struct Request {
22 pub path_type: Path,
23 pub kind: Kind,
24 pub id: String,
25 pub image: String,
26 pub mounts: Vec<Mount>,
27 pub env: Vec<EnvVar>,
28 #[serde(default = "default_cwd")]
30 pub cwd: String,
31 #[serde(flatten)]
32 pub base: crate::cli::command::RequestBase,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
36#[schemars(rename = "cli.command.laboratories.create.Path")]
37pub enum Path {
38 #[serde(rename = "laboratories/create")]
39 LaboratoriesCreate,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
46#[serde(tag = "by", rename_all = "snake_case")]
47#[schemars(rename = "cli.command.laboratories.create.Kind")]
48pub enum Kind {
49 Client,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
59#[schemars(rename = "cli.command.laboratories.create.Mount")]
60pub struct Mount {
61 pub host: String,
62 pub container: String,
63}
64
65impl FromStr for Mount {
66 type Err = String;
67 fn from_str(s: &str) -> Result<Self, Self::Err> {
70 let mut host: Option<String> = None;
71 let mut container: Option<String> = None;
72 for (k, v) in tokenize(s)? {
73 match k {
74 "host" => host = Some(v.to_string()),
75 "container" => container = Some(v.to_string()),
76 other => return Err(format!("unknown key: {other}")),
77 }
78 }
79 match (host, container) {
80 (Some(host), Some(container)) => Ok(Mount { host, container }),
81 (None, _) => Err("host is required".to_string()),
82 (_, None) => Err("container is required".to_string()),
83 }
84 }
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
90#[schemars(rename = "cli.command.laboratories.create.EnvVar")]
91pub struct EnvVar {
92 pub key: String,
93 pub value: String,
94}
95
96impl FromStr for EnvVar {
97 type Err = String;
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match s.split_once('=') {
102 Some((key, value)) => Ok(EnvVar {
103 key: key.to_string(),
104 value: value.to_string(),
105 }),
106 None => Err(format!("expected KEY=VALUE, got: {s}")),
107 }
108 }
109}
110
111impl CommandRequest for Request {
112 fn request_base(&self) -> &crate::cli::command::RequestBase {
113 &self.base
114 }
115
116 fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
117 Some(&mut self.base)
118 }
119}
120
121#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
123#[schemars(rename = "cli.command.laboratories.create.Response")]
124pub struct Response {
125 pub id: String,
126 pub image: String,
127 pub mounts: Vec<Mount>,
128 pub env: Vec<EnvVar>,
129 pub cwd: String,
130}
131
132#[derive(clap::Args)]
133#[command(
134 group(clap::ArgGroup::new("side").required(true).args(["client"])),
135 group(clap::ArgGroup::new("id_required").required(true).args(["id"])),
136 group(clap::ArgGroup::new("image_required").required(true).args(["image"])),
137)]
138pub struct Args {
139 #[arg(long)]
142 pub client: bool,
143 #[arg(long)]
145 pub id: Option<String>,
146 #[arg(long)]
148 pub image: Option<String>,
149 #[arg(long = "mount")]
151 pub mounts: Vec<String>,
152 #[arg(long = "env")]
154 pub env: Vec<String>,
155 #[arg(long)]
157 pub cwd: Option<String>,
158 #[command(flatten)]
159 pub base: crate::cli::command::RequestBaseArgs,
160}
161
162#[derive(clap::Args)]
163#[command(args_conflicts_with_subcommands = true)]
164pub struct Command {
165 #[command(flatten)]
166 pub args: Args,
167 #[command(subcommand)]
168 pub schema: Option<Schema>,
169}
170
171#[derive(clap::Subcommand)]
172pub enum Schema {
173 RequestSchema(request_schema::Args),
175 ResponseSchema(response_schema::Args),
177}
178
179impl TryFrom<Args> for Request {
180 type Error = crate::cli::command::FromArgsError;
181 fn try_from(args: Args) -> Result<Self, Self::Error> {
182 let id = args.id.ok_or_else(|| {
183 crate::cli::command::FromArgsError::path_parse("id", "--id is required".to_string())
184 })?;
185 let image = args.image.ok_or_else(|| {
186 crate::cli::command::FromArgsError::path_parse(
187 "image",
188 "--image is required".to_string(),
189 )
190 })?;
191 if !args.client {
192 return Err(crate::cli::command::FromArgsError::path_parse(
193 "client",
194 "--client is required".to_string(),
195 ));
196 }
197 let mounts = args
198 .mounts
199 .iter()
200 .map(|s| {
201 s.parse::<Mount>()
202 .map_err(|m| crate::cli::command::FromArgsError::path_parse("mount", m))
203 })
204 .collect::<Result<Vec<_>, _>>()?;
205 let env = args
206 .env
207 .iter()
208 .map(|s| {
209 s.parse::<EnvVar>()
210 .map_err(|m| crate::cli::command::FromArgsError::path_parse("env", m))
211 })
212 .collect::<Result<Vec<_>, _>>()?;
213 let cwd = args.cwd.unwrap_or_else(default_cwd);
214 Ok(Self {
215 path_type: Path::LaboratoriesCreate,
216 kind: Kind::Client,
217 id,
218 image,
219 mounts,
220 env,
221 cwd,
222 base: args.base.into(),
223 })
224 }
225}
226
227#[cfg(feature = "cli-executor")]
228pub async fn execute<E: crate::cli::command::CommandExecutor>(
229 executor: &E,
230 mut request: Request,
231 agent_arguments: Option<&crate::cli::command::AgentArguments>,
232) -> Result<Response, E::Error> {
233 request.base.clear_transform();
234 executor.execute_one(request, agent_arguments).await
235}
236
237#[cfg(feature = "cli-executor")]
238pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
239 executor: &E,
240 mut request: Request,
241 transform: crate::cli::command::Transform,
242 agent_arguments: Option<&crate::cli::command::AgentArguments>,
243) -> Result<serde_json::Value, E::Error> {
244 request.base.set_transform(transform);
245 executor.execute_one(request, agent_arguments).await
246}
247
248#[cfg(feature = "mcp")]
249impl crate::cli::command::CommandResponse for Response {
250 fn into_mcp(self) -> crate::cli::command::McpResponseItem {
251 crate::cli::command::McpResponseItem::JSONL(serde_json::to_value(self).unwrap())
252 }
253}
254
255pub mod request_schema;
256
257pub mod response_schema;
258
259#[cfg(feature = "cli-listener")]
264pub struct ListenerExecution {
265 pub request: Request,
266 pub agent_arguments: crate::cli::command::AgentArguments,
267 pub response: crate::cli::websocket_listener::UnaryResponse<Response>,
268}