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.