Skip to main content

objectiveai_cli/command/db/
spawn.rs

1//! `db spawn` — start the `objectiveai-db` postgres supervisor in the
2//! background.
3//!
4//! The supervisor is per-state: its lock lives at
5//! `<dir>/state/<state>/locks` key `db`, and the lock contents are the
6//! cluster's `postgresql://...` connection URL (the postmaster binds
7//! 127.0.0.1 on a random free port). If the lock is already held the
8//! cluster is already up and its published URL is returned as-is.
9
10use objectiveai_sdk::cli::command::db::spawn::{Request, Response};
11
12use crate::context::Context;
13use crate::error::Error;
14use crate::filesystem::config::DB_DEFAULT_PASSWORD;
15
16/// The spawn flow itself, callable in-process (used by
17/// `Context::db_client()` as well as the `db spawn` command).
18/// Idempotent and cheap when the cluster is already up: a try_read of
19/// the lock returns the published `postgresql://` URL without
20/// spawning.
21pub async fn spawn(ctx: &Context) -> Result<String, Error> {
22    let mut config = ctx
23        .filesystem
24        .read_config_view(objectiveai_sdk::cli::command::GetScope::Final)
25        .await?;
26    let password = config
27        .db()
28        .get_password()
29        .unwrap_or(DB_DEFAULT_PASSWORD)
30        .to_string();
31
32    let bin = if cfg!(windows) {
33        "objectiveai-db.exe"
34    } else {
35        "objectiveai-db"
36    };
37    let exe = ctx.filesystem.bin_dir().join(bin);
38    let lock_dir = ctx.filesystem.state_dir().join("locks");
39
40    // objectiveai-db is clap-args-only (no env): the layout
41    // coordinates tell it to provision THIS cli's tree — postgres
42    // binaries into the shared <dir>/bin/pg-bin, the cluster into
43    // <dir>/state/<state>/db.
44    crate::spawn::spawn_until_lock_published(&exe, &lock_dir, "db", |cmd| {
45        cmd.arg("--objectiveai-dir")
46            .arg(ctx.filesystem.dir())
47            .arg("--objectiveai-state")
48            .arg(ctx.filesystem.state())
49            .arg("--pg-password")
50            .arg(password);
51    })
52    .await
53}
54
55pub async fn execute(ctx: &Context, _request: Request) -> Result<Response, Error> {
56    Ok(Response {
57        listening: spawn(ctx).await?,
58    })
59}
60
61pub mod request_schema {
62    use objectiveai_sdk::cli::command::db::spawn as sdk;
63    use objectiveai_sdk::cli::command::db::spawn::request_schema::{Request, Response};
64
65    use crate::context::Context;
66    use crate::error::Error;
67
68    pub async fn execute(_ctx: &Context, _request: Request) -> Result<Response, Error> {
69        Ok(objectiveai_sdk::cli::command::ResponseSchema(schemars::schema_for!(sdk::Request)))
70    }
71}
72
73pub mod response_schema {
74    use objectiveai_sdk::cli::command::db::spawn as sdk;
75    use objectiveai_sdk::cli::command::db::spawn::response_schema::{Request, Response};
76
77    use crate::context::Context;
78    use crate::error::Error;
79
80    pub async fn execute(_ctx: &Context, _request: Request) -> Result<Response, Error> {
81        Ok(objectiveai_sdk::cli::command::ResponseSchema(schemars::schema_for!(sdk::Response)))
82    }
83}