Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions cli/src/cli_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ pub const COMPLETION_CLAP_ABOUT: &str = "Generate deterministic shell completion
pub const COMPLETION_TOP_LEVEL_PURPOSE: &str = "Generate deterministic shell completion scripts";
pub const COMPLETION_SHOW_IN_TOP_LEVEL_HELP: bool = true;

pub const TRACE_CLAP_ABOUT: &str = "Inspect Agent Trace databases and recorded activity";
pub const TRACE_TOP_LEVEL_PURPOSE: &str = "Inspect Agent Trace databases and recorded activity";
pub const TRACE_SHOW_IN_TOP_LEVEL_HELP: bool = true;

pub const TOP_LEVEL_COMMANDS: &[TopLevelCommandMetadata] = &[
TopLevelCommandMetadata {
name: crate::services::auth_command::NAME,
Expand Down Expand Up @@ -88,6 +92,11 @@ pub const TOP_LEVEL_COMMANDS: &[TopLevelCommandMetadata] = &[
purpose: COMPLETION_TOP_LEVEL_PURPOSE,
show_in_top_level_help: COMPLETION_SHOW_IN_TOP_LEVEL_HELP,
},
TopLevelCommandMetadata {
name: crate::services::trace::NAME,
purpose: TRACE_TOP_LEVEL_PURPOSE,
show_in_top_level_help: TRACE_SHOW_IN_TOP_LEVEL_HELP,
},
];

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -184,9 +193,6 @@ pub enum Commands {

#[arg(long, value_enum, default_value_t = OutputFormat::Text)]
format: OutputFormat,

#[command(subcommand)]
subcommand: Option<DoctorSubcommand>,
},

#[command(about = HOOKS_CLAP_ABOUT, hide = !HOOKS_SHOW_IN_TOP_LEVEL_HELP)]
Expand All @@ -212,15 +218,45 @@ pub enum Commands {
#[arg(long, value_enum)]
shell: CompletionShell,
},

#[command(about = TRACE_CLAP_ABOUT, hide = !TRACE_SHOW_IN_TOP_LEVEL_HELP)]
Trace {
#[command(subcommand)]
subcommand: TraceSubcommand,
},
}

#[derive(Subcommand, Debug, Clone, PartialEq, Eq)]
pub enum TraceSubcommand {
#[command(about = "Inspect discovered Agent Trace databases")]
Db {
#[command(subcommand)]
subcommand: TraceDbSubcommand,
},

#[command(about = "Show Agent Trace activity for the current checkout (or all with --all)")]
Status {
#[arg(long)]
all: bool,

#[arg(long, value_enum, default_value_t = OutputFormat::Text)]
format: OutputFormat,
},
}

#[derive(Subcommand, Debug, Clone, PartialEq, Eq)]
pub enum DoctorSubcommand {
#[command(about = "List registered Agent Trace checkouts and databases")]
Dbs {
pub enum TraceDbSubcommand {
#[command(about = "List discovered Agent Trace databases with readiness")]
List {
#[arg(long, value_enum, default_value_t = OutputFormat::Text)]
format: OutputFormat,
},

#[command(about = "Open an embedded SQL shell for a discovered Agent Trace database")]
Shell {
#[arg(value_name = "uuid-or-alias")]
identifier: String,
},
}

#[derive(Subcommand, Debug, Clone, PartialEq, Eq)]
Expand Down
4 changes: 2 additions & 2 deletions cli/src/services/checkout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
//! string, consistent with the existing `agent_trace_id` convention in this
//! codebase.
//!
//! Checkout databases are now discovered via filesystem scan in `sce doctor dbs`
//! (see `cli/src/services/doctor/mod.rs`). There is no central registry file.
//! Checkout databases are discovered via filesystem scan in `sce trace db list`
//! (see `cli/src/services/trace/`). There is no central registry file.

use std::path::{Path, PathBuf};
use std::process::Command;
Expand Down
15 changes: 14 additions & 1 deletion cli/src/services/command_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const DEFAULT_COMMAND_NAMES: &[&str] = &[
services::hooks::NAME,
services::bash_policy::NAME,
services::setup::NAME,
services::trace::NAME,
services::version::NAME,
];

Expand All @@ -32,6 +33,7 @@ pub enum RuntimeCommand {
Policy(services::bash_policy::command::PolicyCommand),
Version(services::version::command::VersionCommand),
Completion(services::completion::command::CompletionCommand),
Trace(services::trace::command::TraceCommand),
}

impl RuntimeCommand {
Expand All @@ -47,6 +49,7 @@ impl RuntimeCommand {
Self::Policy(_) => Cow::Borrowed(services::bash_policy::NAME),
Self::Version(_) => Cow::Borrowed(services::version::NAME),
Self::Completion(_) => Cow::Borrowed(services::completion::NAME),
Self::Trace(_) => Cow::Borrowed(services::trace::NAME),
}
}

Expand All @@ -65,6 +68,7 @@ impl RuntimeCommand {
Self::Policy(command) => command.execute(),
Self::Version(command) => command.execute(context),
Self::Completion(command) => Ok(command.execute(context)),
Self::Trace(command) => command.execute(context),
}
}
}
Expand Down Expand Up @@ -139,7 +143,6 @@ pub fn default_runtime_command(name: &str) -> Option<RuntimeCommand> {
services::doctor::NAME => Some(RuntimeCommand::Doctor(
services::doctor::command::DoctorCommand {
request: services::doctor::DoctorRequest {
action: services::doctor::DoctorAction::Report,
mode: services::doctor::DoctorMode::Diagnose,
format: services::doctor::DoctorFormat::Text,
},
Expand Down Expand Up @@ -172,6 +175,15 @@ pub fn default_runtime_command(name: &str) -> Option<RuntimeCommand> {
},
},
)),
services::trace::NAME => Some(RuntimeCommand::Trace(
services::trace::command::TraceCommand {
request: services::trace::TraceRequest {
subcommand: services::trace::TraceSubcommandRequest::DbList {
format: services::output_format::OutputFormat::Text,
},
},
},
)),
_ => None,
}
}
Expand All @@ -195,6 +207,7 @@ mod tests {
"hooks",
"policy",
"setup",
"trace",
"version"
]
);
Expand Down
61 changes: 61 additions & 0 deletions cli/src/services/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{
};

use anyhow::{Context, Result};
use turso::Value as TursoValue;

use crate::services::lifecycle::{
HealthCategory, HealthFixability, HealthProblem, HealthProblemKind, HealthSeverity,
Expand Down Expand Up @@ -312,6 +313,15 @@ pub struct TursoDb<M: DbSpec> {
core: TursoConnectionCore<M>,
}

/// Fully fetched SQL query result for deterministic rendering outside the
/// async Turso row iterator lifetime.
#[derive(Clone, Debug, PartialEq)]
#[allow(dead_code)]
pub struct QueryRows {
pub columns: Vec<String>,
pub rows: Vec<Vec<TursoValue>>,
}

/// Generic encrypted Turso database adapter.
///
/// Mirrors the structural seams of [`TursoDb`] while reserving encrypted local
Expand Down Expand Up @@ -469,6 +479,57 @@ impl<M: DbSpec> TursoDb<M> {
)
}

/// Execute a SQL query and synchronously fetch column names plus raw values.
#[allow(dead_code)]
pub fn query_values(
&self,
sql: &str,
params: impl turso::params::IntoParams,
) -> Result<QueryRows> {
let params = turso::params::IntoParams::into_params(params).map_err(|e| {
anyhow::anyhow!("{} parameter conversion failed: {sql}: {e}", M::db_name())
})?;
let operation_name = format!("query and fetch {} database values", M::db_name());

run_with_retry_sync(
resolve_query_retry_policy::<M>(),
&operation_name,
QUERY_RETRY_HINT,
|_| {
self.core.runtime.block_on(async {
let mut rows =
self.core
.conn
.query(sql, params.clone())
.await
.map_err(|e| {
anyhow::anyhow!("{} query failed: {sql}: {e}", M::db_name())
})?;
let columns = rows.column_names();
let column_count = rows.column_count();
let mut fetched_rows = Vec::new();

while let Some(row) = rows.next().await.map_err(|e| {
anyhow::anyhow!("{} row fetch failed: {sql}: {e}", M::db_name())
})? {
let mut values = Vec::with_capacity(column_count);
for column_index in 0..column_count {
values.push(row.get_value(column_index).map_err(|e| {
anyhow::anyhow!("{} value fetch failed: {sql}: {e}", M::db_name())
})?);
}
fetched_rows.push(values);
}

Ok(QueryRows {
columns,
rows: fetched_rows,
})
})
},
)
}

/// Execute a SQL query and synchronously map all returned rows.
pub fn query_map<T, F>(
&self,
Expand Down
Loading
Loading