daemon status: add in-flight tasks, recent completions, and node history command
Show running/pending tasks with elapsed time, progress, and last 3 output lines. Show last 20 completed/failed jobs from daemon log. Both displayed before the existing grouped task view. Add 'poc-memory history KEY' command that replays the append-only node log to show all versions of a key with version number, weight, timestamp, and content preview. Useful for auditing what modified a node.
This commit is contained in:
parent
f4c4e1bb39
commit
851fc0d417
2 changed files with 134 additions and 0 deletions
|
|
@ -627,6 +627,53 @@ fn status_symbol(t: &TaskInfo) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
/// Shorten a job name for display: "experience-mine /long/path/uuid.jsonl" → "experience-mine uuid…"
|
||||
fn short_job_name(job: &str) -> String {
|
||||
// Split "verb path" or just return as-is
|
||||
if let Some((verb, path)) = job.split_once(' ') {
|
||||
let file = path.rsplit('/').next().unwrap_or(path);
|
||||
let file = file.strip_suffix(".jsonl").unwrap_or(file);
|
||||
let short = if file.len() > 12 { &file[..12] } else { file };
|
||||
format!("{} {}", verb, short)
|
||||
} else {
|
||||
job.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn show_recent_completions(n: usize) {
|
||||
let path = log_path();
|
||||
let content = match fs::read_to_string(&path) {
|
||||
Ok(c) => c,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let recent: Vec<&str> = content.lines().rev()
|
||||
.filter(|line| {
|
||||
line.contains("\"event\":\"completed\"") || line.contains("\"event\":\"failed\"")
|
||||
})
|
||||
.take(n)
|
||||
.collect();
|
||||
|
||||
if recent.is_empty() { return; }
|
||||
|
||||
eprintln!(" Recent:");
|
||||
for line in recent.iter().rev() {
|
||||
if let Ok(obj) = serde_json::from_str::<serde_json::Value>(line) {
|
||||
let ts = obj.get("ts").and_then(|v| v.as_str()).unwrap_or("?");
|
||||
let job = obj.get("job").and_then(|v| v.as_str()).unwrap_or("?");
|
||||
let event = obj.get("event").and_then(|v| v.as_str()).unwrap_or("?");
|
||||
let detail = obj.get("detail").and_then(|v| v.as_str()).unwrap_or("");
|
||||
|
||||
let time = if ts.len() >= 19 { &ts[11..19] } else { ts };
|
||||
let sym = if event == "completed" { "✓" } else { "✗" };
|
||||
let name = short_job_name(job);
|
||||
|
||||
eprintln!(" {} {} {:30} {}", sym, time, name, detail);
|
||||
}
|
||||
}
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
pub fn show_status() -> Result<(), String> {
|
||||
let status = match read_status_socket() {
|
||||
Some(s) => s,
|
||||
|
|
@ -665,6 +712,41 @@ pub fn show_status() -> Result<(), String> {
|
|||
("other", "Other"),
|
||||
];
|
||||
|
||||
// In-flight tasks first (running + pending)
|
||||
let in_flight: Vec<&TaskInfo> = status.tasks.iter()
|
||||
.filter(|t| matches!(t.status, TaskStatus::Running | TaskStatus::Pending))
|
||||
.collect();
|
||||
|
||||
if !in_flight.is_empty() {
|
||||
eprintln!(" In flight:");
|
||||
for t in &in_flight {
|
||||
let sym = status_symbol(t);
|
||||
let e = task_elapsed(t);
|
||||
let elapsed = if !e.is_zero() {
|
||||
format!(" {}", format_duration_human(e.as_millis()))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let progress = t.progress.as_deref()
|
||||
.filter(|p| *p != "idle")
|
||||
.map(|p| format!(" {}", p))
|
||||
.unwrap_or_default();
|
||||
let name = short_job_name(&t.name);
|
||||
eprintln!(" {} {:30}{}{}", sym, name, elapsed, progress);
|
||||
if matches!(t.status, TaskStatus::Running) && !t.output_log.is_empty() {
|
||||
let skip = t.output_log.len().saturating_sub(3);
|
||||
for line in &t.output_log[skip..] {
|
||||
eprintln!(" │ {}", line);
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!();
|
||||
}
|
||||
|
||||
// Recent completions from log file
|
||||
show_recent_completions(20);
|
||||
|
||||
// Detailed group view only if there are failures worth showing
|
||||
for (group_id, group_label) in groups {
|
||||
let tasks: Vec<&TaskInfo> = status.tasks.iter()
|
||||
.filter(|t| task_group(&t.name) == *group_id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue