feat: replace prompt with inform (fire-and-forget), logs default 20 lines

This commit is contained in:
Helios Agent 2026-03-06 03:13:42 +01:00
parent d2f77f8054
commit af0b6b5ddb
No known key found for this signature in database
GPG key ID: C8259547CD8309B5
5 changed files with 51 additions and 13 deletions

View file

@ -275,8 +275,8 @@ enum Commands {
device: String,
},
/// Show a MessageBox asking the user to do something
Prompt {
/// Show a notification to the user (fire-and-forget, no response needed)
Inform {
/// Device label
device: String,
/// Message to display
@ -342,7 +342,7 @@ enum Commands {
/// Device label
device: String,
/// Number of lines
#[arg(long, default_value = "100")]
#[arg(long, default_value = "20")]
lines: u32,
},
}
@ -537,7 +537,7 @@ fn main() {
println!("All windows minimized on {}.", device);
}
Commands::Prompt {
Commands::Inform {
device,
message,
title,
@ -547,19 +547,14 @@ fn main() {
if let Some(t) = title {
body["title"] = json!(t);
}
let data = req(
req(
&cfg,
"POST",
&format!("/devices/{}/prompt", device),
&format!("/devices/{}/inform", device),
Some(body),
30,
10,
);
let answer = data["answer"].as_str().unwrap_or("");
if !answer.is_empty() {
println!("{}", answer);
} else {
println!("Prompt confirmed.");
}
println!("User informed on {}.", device);
}
Commands::Run {

View file

@ -430,6 +430,26 @@ async fn handle_message(
}
}
ServerMessage::InformRequest { request_id, message, title } => {
let msg = message.clone();
let ttl = title.clone().unwrap_or_else(|| "Helios".to_string());
// Fire-and-forget: show MessageBox in background thread, don't block
std::thread::spawn(move || {
#[cfg(windows)]
unsafe {
use windows::core::PCWSTR;
use windows::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_OK, MB_ICONINFORMATION, HWND_DESKTOP};
let msg_w: Vec<u16> = msg.encode_utf16().chain(std::iter::once(0)).collect();
let ttl_w: Vec<u16> = ttl.encode_utf16().chain(std::iter::once(0)).collect();
MessageBoxW(HWND_DESKTOP, PCWSTR(msg_w.as_ptr()), PCWSTR(ttl_w.as_ptr()), MB_OK | MB_ICONINFORMATION);
}
#[cfg(not(windows))]
let _ = (msg, ttl);
});
display::log_ok("inform", &message);
ClientMessage::Ack { request_id }
}
ServerMessage::PromptRequest { request_id, message, title: _ } => {
display::prompt_waiting(&message);
let answer = tokio::task::spawn_blocking(|| {

View file

@ -58,6 +58,12 @@ pub enum ServerMessage {
message: String,
title: Option<String>,
},
/// Show a non-blocking notification to the user (fire-and-forget)
InformRequest {
request_id: Uuid,
message: String,
title: Option<String>,
},
/// Execute a shell command on the client
ExecRequest {
request_id: Uuid,

View file

@ -401,6 +401,22 @@ pub async fn clipboard_set(
}
}
/// POST /devices/:label/inform
pub async fn inform_user(
Path(label): Path<String>,
State(state): State<AppState>,
Json(body): Json<PromptBody>,
) -> impl IntoResponse {
match dispatch(&state, &label, "inform", |rid| ServerMessage::InformRequest {
request_id: rid,
message: body.message.clone(),
title: body.title.clone(),
}).await {
Ok(_) => (StatusCode::OK, Json(serde_json::json!({ "ok": true }))).into_response(),
Err(e) => e.into_response(),
}
}
/// POST /devices/:label/prompt
#[derive(Deserialize)]
pub struct PromptBody {

View file

@ -49,6 +49,7 @@ async fn main() -> anyhow::Result<()> {
.route("/devices/:label/screenshot", post(api::request_screenshot))
.route("/devices/:label/exec", post(api::request_exec))
.route("/devices/:label/prompt", post(api::prompt_user))
.route("/devices/:label/inform", post(api::inform_user))
.route("/devices/:label/windows", get(api::list_windows))
.route("/devices/:label/windows/minimize-all", post(api::minimize_all))
.route("/devices/:label/logs", get(api::logs))