Skip to main content

objectiveai_cli/command/agents/instances/
mod.rs

1//! `agents instances` — CLI-side dispatch for the surviving
2//! instances subtree. Two leaves: `get`, `list`. `spawn`, `message`,
3//! and `read` moved up to `agents spawn`, `agents message`, and
4//! `agents logs read`.
5
6use std::pin::Pin;
7
8use futures::{Stream, StreamExt};
9use objectiveai_sdk::cli::command::agents::instances::list::Target;
10use objectiveai_sdk::cli::command::agents::instances::{Request, ResponseItem};
11
12use crate::context::Context;
13use crate::db::tags;
14use crate::error::Error;
15
16pub mod get;
17pub mod list;
18
19type ItemStream = Pin<Box<dyn Stream<Item = Result<ResponseItem, Error>> + Send>>;
20
21fn once<T: Send + 'static>(
22    item: Result<T, Error>,
23) -> Pin<Box<dyn Stream<Item = Result<T, Error>> + Send>> {
24    Box::pin(futures::stream::once(async move { item }))
25}
26
27/// Resolve a `Target` to the AIH it designates. `Me`/`Direct` always
28/// resolve; a `Tag` resolves only when BOUND — GROUPED/ABSENT yield
29/// `None` (silently skipped by both `list` and `get`). Shared by the
30/// `list` and `get` handlers.
31async fn resolve_target(
32    db: &crate::db::Pool,
33    target: Target,
34    default_parent: &str,
35) -> Result<Option<String>, Error> {
36    match target {
37        Target::Me => Ok(Some(default_parent.to_string())),
38        Target::Direct {
39            parent_agent_instance_hierarchy,
40            agent_instance,
41        } => {
42            let parent = parent_agent_instance_hierarchy
43                .unwrap_or_else(|| default_parent.to_string());
44            Ok(Some(format!("{parent}/{agent_instance}")))
45        }
46        Target::Tag { agent_tag } => match tags::lookup(db, &agent_tag).await? {
47            tags::LookupState::Bound {
48                agent_instance_hierarchy,
49            } => Ok(Some(agent_instance_hierarchy)),
50            tags::LookupState::Grouped { .. } | tags::LookupState::Absent => Ok(None),
51        },
52    }
53}
54
55pub async fn execute(ctx: &Context, request: Request) -> Result<ItemStream, Error> {
56    let stream: ItemStream = match request {
57        Request::Get(req) => {
58            let inner = get::execute(ctx, req).await?;
59            Box::pin(inner.map(|r| r.map(ResponseItem::Get)))
60        }
61        Request::GetRequestSchema(req) => {
62            let value = get::request_schema::execute(ctx, req).await?;
63            once(Ok(ResponseItem::GetRequestSchema(value)))
64        }
65        Request::GetResponseSchema(req) => {
66            let value = get::response_schema::execute(ctx, req).await?;
67            once(Ok(ResponseItem::GetResponseSchema(value)))
68        }
69        Request::List(req) => {
70            let inner = list::execute(ctx, req).await?;
71            Box::pin(inner.map(|r| r.map(ResponseItem::List)))
72        }
73        Request::ListRequestSchema(req) => {
74            let value = list::request_schema::execute(ctx, req).await?;
75            once(Ok(ResponseItem::ListRequestSchema(value)))
76        }
77        Request::ListResponseSchema(req) => {
78            let value = list::response_schema::execute(ctx, req).await?;
79            once(Ok(ResponseItem::ListResponseSchema(value)))
80        }
81    };
82    Ok(stream)
83}