~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs-socialweb-computer-requester

socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons

0
0
64 lines · 3.4 KB Raw History
// process-mcp: computer.socialweb.cicd.process.* over a guest agent's MCP shell, until the guest agent
// serves the process contract as plain HTTP. Each entry is one MCP tools/call of the same name to `url`
// (iroh:///<ref>/shell/mcp, webrtc:///<ref>/shell/mcp) through com.fedfork.exec.http.fetch; its
// structuredContent is the result. The arguments are the input less url and what the host adds.
//   exec start read write kill list  {url, ...the tool's arguments} -> the tool's result
use abi::{Call, Event, Handle, Run, entries};
use serde_json::{Map, Value, json};

const HOST_KEYS: [&str; 4] = ["url", "did", "now", "caller"];

fn tool(name: &str, run: Run<Map<String, Value>>) -> Result<Handle, String> {
    Ok(match run.event {
        Event::Start => {
            let url = run.args.get("url").and_then(Value::as_str).ok_or("url is required: the machine's shell (iroh:///... or webrtc:///...)")?;
            let arguments: Map<String, Value> = run.args.iter().filter(|(k, _)| !HOST_KEYS.contains(&k.as_str())).map(|(k, v)| (k.clone(), v.clone())).collect();
            let body = json!({ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": name, "arguments": arguments } });
            let headers = json!({ "content-type": "application/json", "accept": "application/json, text/event-stream", "mcp-protocol-version": "2025-11-25" });
            Handle::new().call(Call::new("mcp", "com.fedfork.exec.http.fetch").input(json!({ "method": "POST", "url": url, "headers": headers, "body": body.to_string() })))
        }
        Event::Result { result, .. } => {
            let res = result.output()?;
            let status = res["status"].as_u64().unwrap_or(0);
            let text = res["body"].as_str().unwrap_or_default();
            if status != 200 {
                return Err(format!("{name}: HTTP {status} {}", text.chars().take(200).collect::<String>()));
            }
            let msg: Value = serde_json::from_str(text).map_err(|_| format!("{name}: the answer is not JSON"))?;
            if let Some(e) = msg.get("error") {
                return Err(format!("{name}: {}", e["message"].as_str().unwrap_or("error")));
            }
            let out = &msg["result"];
            if out["isError"] == true {
                return Err(format!("{name}: {}", out["content"][0]["text"].as_str().unwrap_or("error")));
            }
            let value = match out.get("structuredContent") {
                Some(v) => v.clone(),
                None => serde_json::from_str(out["content"][0]["text"].as_str().unwrap_or("null")).map_err(|_| format!("{name}: no result"))?,
            };
            Handle::new().result(value).done()
        }
        _ => Handle::new(),
    })
}

fn exec_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("exec", run)
}
fn start_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("start", run)
}
fn read_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("read", run)
}
fn write_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("write", run)
}
fn kill_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("kill", run)
}
fn list_handler(run: Run<Map<String, Value>>) -> Result<Handle, String> {
    tool("list", run)
}

entries!(exec => exec_handler, start => start_handler, read => read_handler, write => write_handler, kill => kill_handler, list => list_handler);