codex-mobile-bridge 0.3.10

Remote bridge and service manager for codex-mobile.
Documentation
use rusqlite::{OptionalExtension, params};

use super::decode::decode_json_row;
use super::{PRIMARY_RUNTIME_ID, Storage};
use crate::bridge_protocol::{RuntimeRecord, now_millis};

impl Storage {
    pub fn ensure_primary_runtime(
        &self,
        codex_home: Option<String>,
        codex_binary: String,
    ) -> anyhow::Result<RuntimeRecord> {
        if let Some(existing) = self.get_runtime(PRIMARY_RUNTIME_ID)? {
            let desired_home = codex_home.or(existing.codex_home.clone());
            let desired_binary = if codex_binary.trim().is_empty() {
                existing.codex_binary.clone()
            } else {
                codex_binary
            };
            let needs_update = existing.codex_home != desired_home
                || existing.codex_binary != desired_binary
                || !existing.is_primary
                || !existing.auto_start;

            if !needs_update {
                return Ok(existing);
            }

            let updated = RuntimeRecord {
                codex_home: desired_home,
                codex_binary: desired_binary,
                is_primary: true,
                auto_start: true,
                updated_at_ms: now_millis(),
                ..existing
            };
            self.upsert_runtime(&updated)?;
            return Ok(updated);
        }

        let now = now_millis();
        let record = RuntimeRecord {
            runtime_id: PRIMARY_RUNTIME_ID.to_string(),
            display_name: "Primary".to_string(),
            codex_home,
            codex_binary,
            is_primary: true,
            auto_start: true,
            created_at_ms: now,
            updated_at_ms: now,
        };
        self.upsert_runtime(&record)?;
        Ok(record)
    }

    pub fn list_runtimes(&self) -> anyhow::Result<Vec<RuntimeRecord>> {
        let conn = self.connect()?;
        let mut stmt = conn.prepare(
            "SELECT raw_json
             FROM runtimes
             ORDER BY is_primary DESC, created_at_ms ASC",
        )?;

        let rows = stmt.query_map([], |row| {
            let raw: String = row.get(0)?;
            decode_json_row(raw)
        })?;

        Ok(rows.collect::<rusqlite::Result<Vec<_>>>()?)
    }

    pub fn get_runtime(&self, runtime_id: &str) -> anyhow::Result<Option<RuntimeRecord>> {
        let conn = self.connect()?;
        let record = conn
            .query_row(
                "SELECT raw_json FROM runtimes WHERE runtime_id = ?1",
                params![runtime_id],
                |row| {
                    let raw: String = row.get(0)?;
                    decode_json_row(raw)
                },
            )
            .optional()?;
        Ok(record)
    }

    pub fn upsert_runtime(&self, runtime: &RuntimeRecord) -> anyhow::Result<()> {
        let conn = self.connect()?;
        conn.execute(
            "INSERT INTO runtimes (
                 runtime_id, display_name, codex_home, codex_binary, is_primary,
                 auto_start, created_at_ms, updated_at_ms, raw_json
             )
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9)
             ON CONFLICT(runtime_id) DO UPDATE SET
                 display_name = excluded.display_name,
                 codex_home = excluded.codex_home,
                 codex_binary = excluded.codex_binary,
                 is_primary = excluded.is_primary,
                 auto_start = excluded.auto_start,
                 created_at_ms = excluded.created_at_ms,
                 updated_at_ms = excluded.updated_at_ms,
                 raw_json = excluded.raw_json",
            params![
                runtime.runtime_id,
                runtime.display_name,
                runtime.codex_home,
                runtime.codex_binary,
                if runtime.is_primary { 1_i64 } else { 0_i64 },
                if runtime.auto_start { 1_i64 } else { 0_i64 },
                runtime.created_at_ms,
                runtime.updated_at_ms,
                serde_json::to_string(runtime)?,
            ],
        )?;
        Ok(())
    }

    pub fn remove_runtime(&self, runtime_id: &str) -> anyhow::Result<()> {
        let conn = self.connect()?;
        conn.execute(
            "DELETE FROM runtimes WHERE runtime_id = ?1",
            params![runtime_id],
        )?;
        Ok(())
    }
}