huskies: merge 596_bug_restore_missing_htop_command_in_bot_and_web_ui
This commit is contained in:
@@ -82,6 +82,7 @@ async fn dispatch_command(
|
|||||||
"delete" => dispatch_delete(args, project_root, agents).await,
|
"delete" => dispatch_delete(args, project_root, agents).await,
|
||||||
"rebuild" => dispatch_rebuild(project_root, agents).await,
|
"rebuild" => dispatch_rebuild(project_root, agents).await,
|
||||||
"timer" => dispatch_timer(args, project_root).await,
|
"timer" => dispatch_timer(args, project_root).await,
|
||||||
|
"htop" => dispatch_htop(args, agents).await,
|
||||||
// All other commands go through the synchronous command registry.
|
// All other commands go through the synchronous command registry.
|
||||||
_ => dispatch_sync(cmd, args, project_root, agents),
|
_ => dispatch_sync(cmd, args, project_root, agents),
|
||||||
}
|
}
|
||||||
@@ -230,6 +231,34 @@ async fn dispatch_timer(args: &str, project_root: &std::path::Path) -> String {
|
|||||||
crate::chat::timer::handle_timer_command(timer_cmd, &store, project_root).await
|
crate::chat::timer::handle_timer_command(timer_cmd, &store, project_root).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle the `htop` command from the web UI.
|
||||||
|
///
|
||||||
|
/// The web UI uses a one-shot HTTP request, so live updates are not possible
|
||||||
|
/// here. Returns a static snapshot of the process dashboard. For `htop stop`,
|
||||||
|
/// returns a helpful message (no persistent session state exists in the web UI).
|
||||||
|
async fn dispatch_htop(args: &str, agents: &Arc<crate::agents::AgentPool>) -> String {
|
||||||
|
use crate::chat::transport::matrix::htop::{HtopCommand, build_htop_message};
|
||||||
|
|
||||||
|
// Re-use the existing parser by constructing a synthetic message.
|
||||||
|
let synthetic = if args.is_empty() {
|
||||||
|
"__web_ui__ htop".to_string()
|
||||||
|
} else {
|
||||||
|
format!("__web_ui__ htop {args}")
|
||||||
|
};
|
||||||
|
|
||||||
|
match crate::chat::transport::matrix::htop::extract_htop_command(
|
||||||
|
&synthetic,
|
||||||
|
"__web_ui__",
|
||||||
|
"@__web_ui__:localhost",
|
||||||
|
) {
|
||||||
|
Some(HtopCommand::Stop) => "No active htop session in the web UI. \
|
||||||
|
Live sessions are only supported in chat transports (Matrix, Slack, Discord)."
|
||||||
|
.to_string(),
|
||||||
|
Some(HtopCommand::Start { duration_secs }) => build_htop_message(agents, 0, duration_secs),
|
||||||
|
None => build_htop_message(agents, 0, 300),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Tests
|
// Tests
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -349,6 +378,78 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- htop (web-UI slash-command path) ------------------------------------
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn htop_returns_dashboard_not_unknown_command() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = test_api(&dir);
|
||||||
|
let body = BotCommandRequest {
|
||||||
|
command: "htop".to_string(),
|
||||||
|
args: String::new(),
|
||||||
|
};
|
||||||
|
let result = api.run_command(Json(body)).await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let resp = result.unwrap().0;
|
||||||
|
assert!(
|
||||||
|
!resp.response.contains("Unknown command"),
|
||||||
|
"htop should not return 'Unknown command': {}",
|
||||||
|
resp.response
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
resp.response.contains("htop"),
|
||||||
|
"htop response should contain 'htop': {}",
|
||||||
|
resp.response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn htop_with_duration_returns_dashboard() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = test_api(&dir);
|
||||||
|
let body = BotCommandRequest {
|
||||||
|
command: "htop".to_string(),
|
||||||
|
args: "10m".to_string(),
|
||||||
|
};
|
||||||
|
let result = api.run_command(Json(body)).await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let resp = result.unwrap().0;
|
||||||
|
assert!(
|
||||||
|
!resp.response.contains("Unknown command"),
|
||||||
|
"htop 10m should not return 'Unknown command': {}",
|
||||||
|
resp.response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn htop_stop_returns_response_not_unknown_command() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let api = test_api(&dir);
|
||||||
|
let body = BotCommandRequest {
|
||||||
|
command: "htop".to_string(),
|
||||||
|
args: "stop".to_string(),
|
||||||
|
};
|
||||||
|
let result = api.run_command(Json(body)).await;
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let resp = result.unwrap().0;
|
||||||
|
assert!(
|
||||||
|
!resp.response.contains("Unknown command"),
|
||||||
|
"htop stop should not return 'Unknown command': {}",
|
||||||
|
resp.response
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- htop bot-command path (regression: htop must remain in command registry) --
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn htop_is_registered_in_bot_command_registry() {
|
||||||
|
let commands = crate::chat::commands::commands();
|
||||||
|
assert!(
|
||||||
|
commands.iter().any(|c| c.name == "htop"),
|
||||||
|
"htop must be registered in the bot command registry so /help lists it"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn run_command_requires_project_root() {
|
async fn run_command_requires_project_root() {
|
||||||
// Create a context with no project root set.
|
// Create a context with no project root set.
|
||||||
|
|||||||
Reference in New Issue
Block a user