Skip to main content

MUXOPEN_JS

Constant MUXOPEN_JS 

Source
const MUXOPEN_JS: &str = "#!/usr/bin/env node\n// Copyright 2026, AgentMux Corp.\n// SPDX-License-Identifier: Apache-2.0\n//\n// muxopen \u{2014} launch an agent into a pane from a terminal, no GUI required.\n//\n// The constructive sibling of the stop verbs: muxspect answers \"what is\n// running\", FleetBulkStop stops things, and until this existed nothing on the\n// command line could START an agent \u{2014} automation could stop cross-channel but\n// not launch anywhere (docs/reports/REPORT_AGENT_OPEN_API_GAP_2026_09_06.md).\n//\n// Thin wrapper over `POST /api/v1/agent/open` (the same `agent.open` impl the\n// UI uses, including its TOCTOU serialization), authenticated exactly the way\n// muxspect and agentmux-mcp already are: $AGENTMUX_LOCAL_URL +\n// $AGENTMUX_AUTH_KEY inherited from the pane environment. No new IPC, no new\n// auth scheme.\n//\n// Deployed by agentmux-srv next to muxlog.mjs/muxspect.mjs\n// (~/.agentmux/shell/muxopen.mjs); the shell `muxopen` functions delegate\n// here. From a tool-spawned subshell call the core directly:\n//   node ~/.agentmux/shell/muxopen.mjs <agent>\n//\n// Exit codes: 0 opened (or already open \u{2014} idempotent, `created:false`),\n// 1 usage/environment error, 2 the server rejected the open or is unreachable.\n\nimport { pathToFileURL } from \"node:url\";\n\nimport { agentmuxFetch, readAgentmuxEnv } from \"./lib/muxclient.mjs\";\n\nconst HELP = `muxopen \u{2014} launch an agent into a pane (no GUI required)\n\nusage:\n  muxopen <agent>              open by name (case-insensitive) or definition id\n  muxopen <agent> --tab <id>   target a specific tab (default: active tab)\n  muxopen <agent> --no-focus   open without focusing the new pane\n  muxopen help                 this text\n\nIdempotent: an agent already open in the target tab returns its existing\npane (\"already open\") instead of opening a second one. On success the agent\nbecomes addressable for agent-to-agent messages.\n\nRequires $AGENTMUX_LOCAL_URL and $AGENTMUX_AUTH_KEY (present in any\nAgentMux-opened pane). This opens the agent in the instance this pane\nbelongs to; cross-instance opening is not implemented here.`;\n\n/** Parse argv (already stripped of node + script path).\n *\n * Returns `{help}` | `{error}` | `{agent, tabId, focus}`. Pure \u{2014} no I/O, no\n * process.exit \u{2014} so the flag handling is unit-testable (same contract as\n * muxspect\'s parseArgs, whose raw-index parser bug is why these are tested).\n */\nexport function parseArgs(argv) {\n    if (argv.length === 0) return { help: true, exitCode: 1 };\n    if (argv[0] === \"help\" || argv[0] === \"--help\" || argv[0] === \"-h\") {\n        return { help: true, exitCode: 0 };\n    }\n    const agent = argv[0];\n    if (agent.startsWith(\"-\")) {\n        return { error: `first argument must be an agent name/id, got \'${agent}\'` };\n    }\n    let tabId = null;\n    let focus = true;\n    for (let i = 1; i < argv.length; i++) {\n        if (argv[i] === \"--tab\") {\n            tabId = argv[++i];\n            if (!tabId) return { error: \"--tab requires a tab id\" };\n        } else if (argv[i] === \"--no-focus\") {\n            focus = false;\n        } else {\n            return { error: `unknown argument \'${argv[i]}\'` };\n        }\n    }\n    return { agent, tabId, focus };\n}\n\n/** Render the success line(s) for an /api/v1/agent/open response body.\n * Pure \u{2014} returns the text rather than printing, for testability. */\nexport function renderResult(body) {\n    const verb = body.created ? \"opened\" : \"already open\";\n    const lines = [\n        `${verb}: ${body.agent_id} (${body.provider}, ${body.controller_type})`,\n        `  block ${body.block_id}`,\n        `  tab   ${body.tab_id}`,\n    ];\n    if (!body.created) lines.push(`  status ${body.status}`);\n    return lines.join(\"\\n\");\n}\n\nfunction fail(msg, code = 1) {\n    process.stderr.write(`muxopen: ${msg}\\n`);\n    process.exit(code);\n}\n\nasync function main() {\n    const parsed = parseArgs(process.argv.slice(2));\n    if (parsed.help) {\n        console.log(HELP);\n        process.exit(parsed.exitCode);\n    }\n    if (parsed.error) fail(`${parsed.error}\\n\\n${HELP}`);\n\n    const { url, authKey } = readAgentmuxEnv();\n    if (!url || !authKey) {\n        fail(\n            \"AGENTMUX_LOCAL_URL / AGENTMUX_AUTH_KEY not set \u{2014} run from a pane \" +\n            \"AgentMux opened (agent or shell), or export them from one.\",\n        );\n    }\n\n    let result;\n    try {\n        result = await agentmuxFetch(url, authKey, \"/api/v1/agent/open\", {\n            method: \"POST\",\n            body: { agent_id: parsed.agent, tab_id: parsed.tabId, focus: parsed.focus },\n        });\n    } catch (e) {\n        fail(`cannot reach ${url}: ${e.message ?? e}`, 2);\n    }\n\n    let body;\n    try {\n        body = await result.resp.json();\n    } catch {\n        body = {};\n    }\n\n    if (!result.ok) {\n        // Surface the impl\'s own vocabulary (AGENT_NOT_FOUND / CLI_NOT_AVAILABLE\n        // / \u{2026}) verbatim \u{2014} it names the remedy better than a paraphrase would.\n        // A 404 here most likely means the running srv predates this route.\n        const hint = result.status === 404\n            ? \" (HTTP 404 \u{2014} this AgentMux instance may predate /api/v1/agent/open)\"\n            : \"\";\n        fail(`${body.error ?? `HTTP ${result.status}`}${hint}`, 2);\n    }\n\n    console.log(renderResult(body));\n}\n\nif (import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href) {\n    main().catch((e) => fail(e.message ?? String(e), 2));\n}\n";
Expand description

muxopen — launch an agent into a pane from a terminal (the constructive sibling of the stop verbs; REPORT_AGENT_OPEN_API_GAP_2026_09_06.md). Deployed beside muxlog.mjs/muxspect.mjs; shell muxopen functions delegate here.