Skip to main content

objectiveai_sdk/cli/command/db/
mod.rs

1//! `db` — direct database access at the CLI level.
2//!
3//! Leaves:
4//! - `query` — execute arbitrary single-statement read-only SQL
5//!   with a required timeout and an optional per-response token
6//!   budget.
7//! - `spawn` — start the `objectiveai-db` postgres vehicle using
8//!   `config db` connection settings.
9//! - `kill` — stop the postmaster started by `db spawn`.
10
11use crate::cli::command::CommandRequest;
12
13pub mod config;
14pub mod kill;
15pub mod query;
16pub mod spawn;
17
18#[derive(clap::Subcommand)]
19pub enum Command {
20    Config {
21        #[command(subcommand)]
22        command: config::Command,
23    },
24    /// Stop the postmaster started by `db spawn`.
25    /// Idempotent — succeeds with count = 0 if none was running.
26    Kill(kill::Command),
27    /// Execute an arbitrary single-statement read-only SQL query
28    /// against the configured postgres pool.
29    Query(query::Command),
30    /// Start the `objectiveai-db` server in the background using
31    /// `config db` connection settings. Errors if something is
32    /// already listening there.
33    Spawn(spawn::Command),
34}
35
36#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
37#[serde(untagged)]
38#[schemars(rename = "cli.command.db.Request")]
39pub enum Request {
40    #[schemars(title = "Config")]
41    Config(config::Request),
42    #[schemars(title = "Kill")]
43    Kill(kill::Request),
44    #[schemars(title = "KillRequestSchema")]
45    KillRequestSchema(kill::request_schema::Request),
46    #[schemars(title = "KillResponseSchema")]
47    KillResponseSchema(kill::response_schema::Request),
48    #[schemars(title = "Query")]
49    Query(query::Request),
50    #[schemars(title = "QueryRequestSchema")]
51    QueryRequestSchema(query::request_schema::Request),
52    #[schemars(title = "QueryResponseSchema")]
53    QueryResponseSchema(query::response_schema::Request),
54    #[schemars(title = "Spawn")]
55    Spawn(spawn::Request),
56    #[schemars(title = "SpawnRequestSchema")]
57    SpawnRequestSchema(spawn::request_schema::Request),
58    #[schemars(title = "SpawnResponseSchema")]
59    SpawnResponseSchema(spawn::response_schema::Request),
60}
61
62// Exempt from json-schema coverage: tier aggregate (see the root
63// `ResponseItem` in command.rs - TS7056).
64#[objectiveai_sdk_macros::json_schema_ignore]
65#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, schemars::JsonSchema)]
66#[schemars(rename = "cli.command.db.ResponseItem")]
67#[serde(untagged)]
68pub enum ResponseItem {
69    #[schemars(title = "Config")]
70    Config(config::Response),
71    #[schemars(title = "Kill")]
72    Kill(kill::Response),
73    #[schemars(title = "KillRequestSchema")]
74    KillRequestSchema(kill::request_schema::Response),
75    #[schemars(title = "KillResponseSchema")]
76    KillResponseSchema(kill::response_schema::Response),
77    #[schemars(title = "Query")]
78    Query(query::Response),
79    #[schemars(title = "QueryRequestSchema")]
80    QueryRequestSchema(query::request_schema::Response),
81    #[schemars(title = "QueryResponseSchema")]
82    QueryResponseSchema(query::response_schema::Response),
83    #[schemars(title = "Spawn")]
84    Spawn(spawn::Response),
85    #[schemars(title = "SpawnRequestSchema")]
86    SpawnRequestSchema(spawn::request_schema::Response),
87    #[schemars(title = "SpawnResponseSchema")]
88    SpawnResponseSchema(spawn::response_schema::Response),
89}
90
91#[cfg(feature = "mcp")]
92impl crate::cli::command::CommandResponse for ResponseItem {
93    fn into_mcp(self) -> crate::cli::command::McpResponseItem {
94        match self {
95            ResponseItem::Config(v) => v.into_mcp(),
96            ResponseItem::Kill(v) => v.into_mcp(),
97            ResponseItem::KillRequestSchema(v) => v.into_mcp(),
98            ResponseItem::KillResponseSchema(v) => v.into_mcp(),
99            ResponseItem::Query(v) => v.into_mcp(),
100            ResponseItem::QueryRequestSchema(v) => v.into_mcp(),
101            ResponseItem::QueryResponseSchema(v) => v.into_mcp(),
102            ResponseItem::Spawn(v) => v.into_mcp(),
103            ResponseItem::SpawnRequestSchema(v) => v.into_mcp(),
104            ResponseItem::SpawnResponseSchema(v) => v.into_mcp(),
105        }
106    }
107}
108
109impl TryFrom<Command> for Request {
110    type Error = crate::cli::command::FromArgsError;
111    fn try_from(command: Command) -> Result<Self, Self::Error> {
112        match command {
113            Command::Config { command } =>
114                Ok(Request::Config(config::Request::try_from(command)?)),
115            Command::Kill(cmd) => match cmd.schema {
116                None => Ok(Request::Kill(kill::Request::try_from(cmd.args)?)),
117                Some(kill::Schema::RequestSchema(args)) => Ok(
118                    Request::KillRequestSchema(kill::request_schema::Request::try_from(args)?),
119                ),
120                Some(kill::Schema::ResponseSchema(args)) => Ok(
121                    Request::KillResponseSchema(kill::response_schema::Request::try_from(args)?),
122                ),
123            },
124            Command::Query(cmd) => match cmd.schema {
125                None => Ok(Request::Query(query::Request::try_from(cmd.args)?)),
126                Some(query::Schema::RequestSchema(args)) => Ok(
127                    Request::QueryRequestSchema(query::request_schema::Request::try_from(args)?),
128                ),
129                Some(query::Schema::ResponseSchema(args)) => Ok(
130                    Request::QueryResponseSchema(query::response_schema::Request::try_from(args)?),
131                ),
132            },
133            Command::Spawn(cmd) => match cmd.schema {
134                None => Ok(Request::Spawn(spawn::Request::try_from(cmd.args)?)),
135                Some(spawn::Schema::RequestSchema(args)) => Ok(
136                    Request::SpawnRequestSchema(spawn::request_schema::Request::try_from(args)?),
137                ),
138                Some(spawn::Schema::ResponseSchema(args)) => Ok(
139                    Request::SpawnResponseSchema(spawn::response_schema::Request::try_from(args)?),
140                ),
141            },
142        }
143    }
144}
145
146impl CommandRequest for Request {
147    fn request_base(&self) -> &crate::cli::command::RequestBase {
148        match self {
149            Request::Config(inner) => inner.request_base(),
150            Request::Kill(inner) => inner.request_base(),
151            Request::KillRequestSchema(inner) => inner.request_base(),
152            Request::KillResponseSchema(inner) => inner.request_base(),
153            Request::Query(inner) => inner.request_base(),
154            Request::QueryRequestSchema(inner) => inner.request_base(),
155            Request::QueryResponseSchema(inner) => inner.request_base(),
156            Request::Spawn(inner) => inner.request_base(),
157            Request::SpawnRequestSchema(inner) => inner.request_base(),
158            Request::SpawnResponseSchema(inner) => inner.request_base(),
159        }
160    }
161
162    fn request_base_mut(&mut self) -> Option<&mut crate::cli::command::RequestBase> {
163        match self {
164            Request::Config(inner) => inner.request_base_mut(),
165            Request::Kill(inner) => inner.request_base_mut(),
166            Request::KillRequestSchema(inner) => inner.request_base_mut(),
167            Request::KillResponseSchema(inner) => inner.request_base_mut(),
168            Request::Query(inner) => inner.request_base_mut(),
169            Request::QueryRequestSchema(inner) => inner.request_base_mut(),
170            Request::QueryResponseSchema(inner) => inner.request_base_mut(),
171            Request::Spawn(inner) => inner.request_base_mut(),
172            Request::SpawnRequestSchema(inner) => inner.request_base_mut(),
173            Request::SpawnResponseSchema(inner) => inner.request_base_mut(),
174        }
175    }
176}
177
178#[cfg(feature = "cli-executor")]
179pub async fn execute<E: crate::cli::command::CommandExecutor>(
180    executor: &E,
181    request: Request,
182    agent_arguments: Option<&crate::cli::command::AgentArguments>,
183) -> Result<
184    std::pin::Pin<Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>>,
185    E::Error,
186> {
187    use futures::StreamExt;
188    let stream: std::pin::Pin<
189        Box<dyn futures::Stream<Item = Result<ResponseItem, E::Error>> + Send>,
190    > = match request {
191            Request::Config(req) => {
192                let inner = config::execute(executor, req, agent_arguments).await?;
193                Box::pin(inner.map(|r| r.map(ResponseItem::Config)))
194            }
195        Request::Kill(req) => {
196            let value = kill::execute(executor, req, agent_arguments).await?;
197            Box::pin(crate::cli::command::StreamOnce::new(Ok(
198                ResponseItem::Kill(value),
199            )))
200        }
201        Request::KillRequestSchema(req) => {
202            let value = kill::request_schema::execute(executor, req, agent_arguments).await?;
203            Box::pin(crate::cli::command::StreamOnce::new(Ok(
204                ResponseItem::KillRequestSchema(value),
205            )))
206        }
207        Request::KillResponseSchema(req) => {
208            let value = kill::response_schema::execute(executor, req, agent_arguments).await?;
209            Box::pin(crate::cli::command::StreamOnce::new(Ok(
210                ResponseItem::KillResponseSchema(value),
211            )))
212        }
213        Request::Query(req) => {
214            let value = query::execute(executor, req, agent_arguments).await?;
215            Box::pin(crate::cli::command::StreamOnce::new(Ok(
216                ResponseItem::Query(value),
217            )))
218        }
219        Request::QueryRequestSchema(req) => {
220            let value = query::request_schema::execute(executor, req, agent_arguments).await?;
221            Box::pin(crate::cli::command::StreamOnce::new(Ok(
222                ResponseItem::QueryRequestSchema(value),
223            )))
224        }
225        Request::QueryResponseSchema(req) => {
226            let value = query::response_schema::execute(executor, req, agent_arguments).await?;
227            Box::pin(crate::cli::command::StreamOnce::new(Ok(
228                ResponseItem::QueryResponseSchema(value),
229            )))
230        }
231        Request::Spawn(req) => {
232            let value = spawn::execute(executor, req, agent_arguments).await?;
233            Box::pin(crate::cli::command::StreamOnce::new(Ok(
234                ResponseItem::Spawn(value),
235            )))
236        }
237        Request::SpawnRequestSchema(req) => {
238            let value = spawn::request_schema::execute(executor, req, agent_arguments).await?;
239            Box::pin(crate::cli::command::StreamOnce::new(Ok(
240                ResponseItem::SpawnRequestSchema(value),
241            )))
242        }
243        Request::SpawnResponseSchema(req) => {
244            let value = spawn::response_schema::execute(executor, req, agent_arguments).await?;
245            Box::pin(crate::cli::command::StreamOnce::new(Ok(
246                ResponseItem::SpawnResponseSchema(value),
247            )))
248        }
249    };
250    Ok(stream)
251}
252
253#[cfg(feature = "cli-executor")]
254pub async fn execute_transform<E: crate::cli::command::CommandExecutor>(
255    executor: &E,
256    request: Request,
257    transform: crate::cli::command::Transform,
258    agent_arguments: Option<&crate::cli::command::AgentArguments>,
259) -> Result<
260    std::pin::Pin<Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>>,
261    E::Error,
262> {
263    let stream: std::pin::Pin<
264        Box<dyn futures::Stream<Item = Result<serde_json::Value, E::Error>> + Send>,
265    > = match request {
266            Request::Config(req) => {
267                let inner = config::execute_transform(executor, req, transform, agent_arguments).await?;
268                Box::pin(inner)
269            }
270        Request::Kill(req) => {
271            let value = kill::execute_transform(executor, req, transform, agent_arguments).await?;
272            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
273        }
274        Request::KillRequestSchema(req) => {
275            let value =
276                kill::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
277            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
278        }
279        Request::KillResponseSchema(req) => {
280            let value =
281                kill::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
282            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
283        }
284        Request::Query(req) => {
285            let value = query::execute_transform(executor, req, transform, agent_arguments).await?;
286            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
287        }
288        Request::QueryRequestSchema(req) => {
289            let value =
290                query::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
291            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
292        }
293        Request::QueryResponseSchema(req) => {
294            let value =
295                query::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
296            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
297        }
298        Request::Spawn(req) => {
299            let value = spawn::execute_transform(executor, req, transform, agent_arguments).await?;
300            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
301        }
302        Request::SpawnRequestSchema(req) => {
303            let value =
304                spawn::request_schema::execute_transform(executor, req, transform, agent_arguments).await?;
305            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
306        }
307        Request::SpawnResponseSchema(req) => {
308            let value =
309                spawn::response_schema::execute_transform(executor, req, transform, agent_arguments).await?;
310            Box::pin(crate::cli::command::StreamOnce::new(Ok(value)))
311        }
312    };
313    Ok(stream)
314}