From 80943143ef86a455afdad5fb54e386b370783e56 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:24:02 +0100 Subject: [PATCH] fix(gossamer): wire groove server + llm_coding command surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #17 — groove discovery server is launched once at startup via groove::spawn() before the webview opens. spawn() also starts the mesh-monitor thread internally, so peers in the mesh (Burble, Vext, Hypatia, VeriSimDB) can probe `/.well-known/groove` on 127.0.0.1:8000 for the entire app lifetime. The previous commented-out groove_discover/groove_status command shims were architecturally wrong: groove is an *external* discovery surface, not something the local webview needs to call into itself. Replaced with a comment explaining where groove actually lives. Closes #18 — llm_coding gets nine real app.command registrations matching the actual `llm_coding_*` API in src-gossamer/src/llm_coding/ commands.rs: llm_coding_init / llm_coding_spawn / llm_coding_freeze / llm_coding_thaw / llm_coding_terminate / llm_coding_list_sessions / llm_coding_session_status / llm_coding_append_message / llm_coding_get_messages The pre-Tauri→gossamer-rs commented-out block called start_session, stop_session, session_status, list_sessions — none of which exist. llm_coding_spawn deserialises a SpawnRequest struct via serde_json::from_value so payloads keep their typed shape. Adds result_str_to_json helper next to result_to_json to avoid double-encoding: the llm_coding_* functions return Result where the String is itself JSON, so the helper parses it before wrapping in `{"ok": true, "result": }`. cargo check + cargo test --no-run clean. The 15 unused-function warnings in settings.rs are pre-existing and out of scope. Co-Authored-By: Claude Opus 4.7 (1M context) --- src-gossamer/src/main.rs | 98 ++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/src-gossamer/src/main.rs b/src-gossamer/src/main.rs index a2c424d..488b72a 100644 --- a/src-gossamer/src/main.rs +++ b/src-gossamer/src/main.rs @@ -79,6 +79,18 @@ fn result_to_json(result: Result) -> Result}` instead of a +// string-wrapped string the client would have to parse twice. +fn result_str_to_json(result: Result) -> Result { + match result { + Ok(s) => result_to_json(Ok( + serde_json::from_str::(&s).unwrap_or(serde_json::Value::Null), + )), + Err(e) => result_to_json::(Err(e)), + } +} + fn blocking_get(url: &str, timeout_secs: u64) -> Result { let client = reqwest::blocking::Client::builder() .timeout(std::time::Duration::from_secs(timeout_secs)) @@ -123,6 +135,13 @@ fn blocking_post_empty(url: &str, timeout_secs: u64) -> Result { // ----------------------------------------------------------------------- fn main() { + // Spawn the groove discovery server (HTTP on 127.0.0.1:8000) on its own + // thread before the webview starts. This lets peers in the mesh + // (Burble, Vext, Hypatia, VeriSimDB) probe `/.well-known/groove` and + // discover PanLL's panel-ui capability for the entire app lifetime. + // `spawn()` also starts the mesh-monitor thread internally. + groove::spawn(); + // Initialize Gossamer app let app = App::new("PanLL", 1280, 800); @@ -140,37 +159,66 @@ fn register_commands(app: &mut gossamer_rs::App) { // LLM Coding commands // ----------------------------------------------------------------------- - // app.command("llm_coding_start", |payload| { - // let session_id = get_str(&payload, "session_id").unwrap_or_default(); - // let prompt = get_str(&payload, "prompt").unwrap_or_default(); - // result_to_json(llm_coding::start_session(session_id, prompt)) - // }); + app.command("llm_coding_init", |_payload| { + result_str_to_json(llm_coding::commands::llm_coding_init()) + }); - // app.command("llm_coding_stop", |payload| { - // let session_id = get_str(&payload, "session_id").unwrap_or_default(); - // result_to_json(llm_coding::stop_session(session_id)) - // }); + app.command("llm_coding_spawn", |payload: serde_json::Value| { + match serde_json::from_value::(payload) { + Ok(request) => result_str_to_json(llm_coding::commands::llm_coding_spawn(request)), + Err(e) => result_to_json::(Err(format!( + "Invalid SpawnRequest payload: {}", + e + ))), + } + }); - // app.command("llm_coding_status", |payload| { - // let session_id = get_str(&payload, "session_id").unwrap_or_default(); - // result_to_json(llm_coding::session_status(session_id)) - // }); + app.command("llm_coding_freeze", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_freeze(session_id)) + }); - // app.command("llm_coding_list", |_payload| { - // result_to_json(llm_coding::list_sessions()) - // }); + app.command("llm_coding_thaw", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_thaw(session_id)) + }); - // ----------------------------------------------------------------------- - // Groove commands - // ----------------------------------------------------------------------- + app.command("llm_coding_terminate", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_terminate(session_id)) + }); - // app.command("groove_discover", |_payload| { - // result_to_json(groove::discover()) - // }); + app.command("llm_coding_list_sessions", |_payload| { + result_str_to_json(llm_coding::commands::llm_coding_list_sessions()) + }); - // app.command("groove_status", |_payload| { - // result_to_json(groove::status()) - // }); + app.command("llm_coding_session_status", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_session_status(session_id)) + }); + + app.command("llm_coding_append_message", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + let content = get_str(&payload, "content").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_append_message( + session_id, content, + )) + }); + + app.command("llm_coding_get_messages", |payload| { + let session_id = get_str(&payload, "session_id").unwrap_or_default(); + result_str_to_json(llm_coding::commands::llm_coding_get_messages(session_id)) + }); + + // ----------------------------------------------------------------------- + // Groove discovery — no commands. + // + // Groove is an external discovery surface: peers probe + // `GET /.well-known/groove` on port 8000 to find PanLL's panel-ui + // capability. The server is launched in `main()` via `groove::spawn()`, + // which also starts the mesh monitor. There is nothing for the webview + // to call locally. + // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- // Service Registry commands (Connected Workbench v0.2.0)