feat(cli): add 'deepseek metrics' command (closes #70)
Implement `deepseek metrics` as a dispatcher-handled subcommand (no TUI binary roundtrip) that reads ~/.deepseek/audit.log, session JSON files, and tasks runtime JSONL event streams, then prints a human-readable usage rollup aggregated by tool name, compaction events, sub-agent spawns, and capacity-controller interventions. Flags: --json (machine-readable) and --since DURATION (e.g. 7d, 24h, 30m, now-2h, 2h30m). Empty/missing audit log exits 0 with an empty rollup; malformed lines are skipped silently via tracing::trace!. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+4
@@ -999,8 +999,12 @@ dependencies = [
|
|||||||
"deepseek-execpolicy",
|
"deepseek-execpolicy",
|
||||||
"deepseek-mcp",
|
"deepseek-mcp",
|
||||||
"deepseek-state",
|
"deepseek-state",
|
||||||
|
"dirs",
|
||||||
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -21,5 +21,11 @@ deepseek-execpolicy = { path = "../execpolicy", version = "0.6.0" }
|
|||||||
deepseek-mcp = { path = "../mcp", version = "0.6.0" }
|
deepseek-mcp = { path = "../mcp", version = "0.6.0" }
|
||||||
deepseek-state = { path = "../state", version = "0.6.0" }
|
deepseek-state = { path = "../state", version = "0.6.0" }
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
|
dirs.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
tracing.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3.16"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
mod metrics;
|
||||||
|
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -135,6 +137,18 @@ enum Commands {
|
|||||||
#[arg(value_enum)]
|
#[arg(value_enum)]
|
||||||
shell: Shell,
|
shell: Shell,
|
||||||
},
|
},
|
||||||
|
/// Print a usage rollup from the audit log and session store.
|
||||||
|
Metrics(MetricsArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
struct MetricsArgs {
|
||||||
|
/// Emit machine-readable JSON.
|
||||||
|
#[arg(long)]
|
||||||
|
json: bool,
|
||||||
|
/// Restrict to events newer than this duration (e.g. 7d, 24h, 30m, now-2h).
|
||||||
|
#[arg(long, value_name = "DURATION")]
|
||||||
|
since: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
@@ -392,6 +406,7 @@ fn run() -> Result<()> {
|
|||||||
generate(shell, &mut cmd, "deepseek", &mut io::stdout());
|
generate(shell, &mut cmd, "deepseek", &mut io::stdout());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Some(Commands::Metrics(args)) => run_metrics_command(args),
|
||||||
None => {
|
None => {
|
||||||
let mut forwarded = Vec::new();
|
let mut forwarded = Vec::new();
|
||||||
if let Some(prompt) = cli.prompt.clone() {
|
if let Some(prompt) = cli.prompt.clone() {
|
||||||
@@ -865,6 +880,19 @@ fn delegate_simple_tui(args: Vec<String>) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_metrics_command(args: MetricsArgs) -> Result<()> {
|
||||||
|
let since = match args.since.as_deref() {
|
||||||
|
Some(s) => {
|
||||||
|
Some(metrics::parse_since(s).with_context(|| format!("invalid --since value: {s:?}"))?)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
metrics::run(metrics::MetricsArgs {
|
||||||
|
json: args.json,
|
||||||
|
since,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn read_api_key_from_stdin() -> Result<String> {
|
fn read_api_key_from_stdin() -> Result<String> {
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
io::stdin()
|
io::stdin()
|
||||||
@@ -1234,6 +1262,7 @@ mod tests {
|
|||||||
"sandbox",
|
"sandbox",
|
||||||
"app-server",
|
"app-server",
|
||||||
"completion",
|
"completion",
|
||||||
|
"metrics",
|
||||||
"--provider",
|
"--provider",
|
||||||
"--model",
|
"--model",
|
||||||
"--config",
|
"--config",
|
||||||
@@ -1279,6 +1308,7 @@ mod tests {
|
|||||||
vec!["--host", "--port", "--config", "--stdio"],
|
vec!["--host", "--port", "--config", "--stdio"],
|
||||||
),
|
),
|
||||||
("completion", vec!["<SHELL>", "bash"]),
|
("completion", vec!["<SHELL>", "bash"]),
|
||||||
|
("metrics", vec!["--json", "--since"]),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (subcommand, expected_tokens) in cases {
|
for (subcommand, expected_tokens) in cases {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user