stoops 0.3.3 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -721,10 +721,34 @@ function makeIdentityAssigner() {
721
721
  return map.get(name);
722
722
  };
723
723
  }
724
+ function wordWrap(text, width) {
725
+ if (width <= 0) return [text];
726
+ const result = [];
727
+ for (const paragraph of text.split("\n")) {
728
+ if (paragraph.length === 0) {
729
+ result.push("");
730
+ continue;
731
+ }
732
+ let line = "";
733
+ for (const word of paragraph.split(/(\s+)/)) {
734
+ if (line.length + word.length > width && line.length > 0) {
735
+ result.push(line);
736
+ line = word.replace(/^\s+/, "");
737
+ } else {
738
+ line += word;
739
+ }
740
+ }
741
+ if (line.length > 0) result.push(line);
742
+ }
743
+ if (result.length === 0) result.push("");
744
+ return result;
745
+ }
724
746
  var NAME_COL = 12;
747
+ var PREFIX_WIDTH = 27;
725
748
  function EventLine({
726
749
  event,
727
- identify
750
+ identify,
751
+ cols
728
752
  }) {
729
753
  const ts = /* @__PURE__ */ jsxs(Text, { color: C.muted, children: [
730
754
  event.ts,
@@ -737,8 +761,11 @@ function EventLine({
737
761
  const sigilColor = isSelf ? C.dim : event.senderType === "agent" ? color : C.dim;
738
762
  const sigilChar = isSelf ? "\u203A" : event.senderType === "agent" ? sigil : "\xB7";
739
763
  const contentColor = isSelf ? C.text : C.secondary;
740
- return /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
741
- /* @__PURE__ */ jsxs(Box, { flexShrink: 0, children: [
764
+ const replyPrefix = event.replyToName ? `\u2192 ${event.replyToName} ` : "";
765
+ const contentWidth = Math.max(20, cols - PREFIX_WIDTH - 1);
766
+ const wrapped = wordWrap(replyPrefix + event.content, contentWidth);
767
+ return /* @__PURE__ */ jsx(Box, { paddingX: 1, flexDirection: "column", children: wrapped.map((line, i) => /* @__PURE__ */ jsxs(Box, { children: [
768
+ i === 0 ? /* @__PURE__ */ jsxs(Box, { flexShrink: 0, children: [
742
769
  ts,
743
770
  /* @__PURE__ */ jsxs(Text, { color: sigilColor, children: [
744
771
  sigilChar,
@@ -746,16 +773,12 @@ function EventLine({
746
773
  ] }),
747
774
  /* @__PURE__ */ jsx(Text, { color: nameColor, bold: isSelf, children: event.senderName.slice(0, NAME_COL).padEnd(NAME_COL) }),
748
775
  /* @__PURE__ */ jsx(Text, { children: " " })
749
- ] }),
750
- /* @__PURE__ */ jsx(Box, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ jsxs(Text, { wrap: "wrap", children: [
751
- event.replyToName && /* @__PURE__ */ jsxs(Text, { color: C.dim, children: [
752
- "\u2192 ",
753
- event.replyToName,
754
- " "
755
- ] }),
756
- /* @__PURE__ */ jsx(Text, { color: contentColor, children: event.content })
757
- ] }) })
758
- ] });
776
+ ] }) : /* @__PURE__ */ jsx(Text, { children: " ".repeat(PREFIX_WIDTH - 1) }),
777
+ /* @__PURE__ */ jsx(Text, { wrap: "truncate", children: i === 0 && replyPrefix ? /* @__PURE__ */ jsxs(Fragment, { children: [
778
+ /* @__PURE__ */ jsx(Text, { color: C.dim, children: replyPrefix }),
779
+ /* @__PURE__ */ jsx(Text, { color: contentColor, children: line.slice(replyPrefix.length) })
780
+ ] }) : /* @__PURE__ */ jsx(Text, { color: contentColor, children: line }) })
781
+ ] }, i)) });
759
782
  }
760
783
  if (event.kind === "join") {
761
784
  const isAgent = event.participantType === "agent";
@@ -955,7 +978,7 @@ function App({
955
978
  ] })
956
979
  ] }, entry.id);
957
980
  }
958
- return /* @__PURE__ */ jsx(EventLine, { event: entry.event, identify }, entry.id);
981
+ return /* @__PURE__ */ jsx(EventLine, { event: entry.event, identify, cols }, entry.id);
959
982
  } }),
960
983
  /* @__PURE__ */ jsxs(Box, { paddingX: 1, children: [
961
984
  /* @__PURE__ */ jsx(Text, { color: C.purple, children: "\u2500" }),
@@ -1783,7 +1806,7 @@ var TmuxBridge = class {
1783
1806
  injectIdle(text) {
1784
1807
  tmuxInjectText(this.session, text);
1785
1808
  tmuxSendEnter(this.session);
1786
- this.sleep(80);
1809
+ this.sleep(200);
1787
1810
  tmuxSendEnter(this.session);
1788
1811
  }
1789
1812
  /**
@@ -1797,7 +1820,7 @@ var TmuxBridge = class {
1797
1820
  this.sleep(this.keystrokeDelayMs);
1798
1821
  tmuxInjectText(this.session, text);
1799
1822
  tmuxSendEnter(this.session);
1800
- this.sleep(80);
1823
+ this.sleep(200);
1801
1824
  tmuxSendEnter(this.session);
1802
1825
  this.sleep(this.keystrokeDelayMs);
1803
1826
  tmuxSendKey(this.session, "C-y");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/serve.ts","../../src/cli/auth.ts","../../src/cli/join.ts","../../src/cli/tui.tsx","../../src/cli/claude/run.ts","../../src/cli/tmux.ts","../../src/cli/claude/tmux-bridge.ts","../../src/cli/runtime-setup.ts","../../src/cli/opencode/run.ts","../../src/cli/codex/run.ts","../../src/cli/codex/tmux-bridge.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * stoops CLI — shared rooms for AI agents.\n *\n * Usage:\n * stoops [--room <name>] [--port <port>] [--share] Host a room + join it\n * stoops serve [--room <name>] [--port <port>] [--share] Headless server only\n * stoops join <url> [--name <name>] [--guest] Join a room as a human\n * stoops run claude [--name <name>] [--admin] [-- <args>] Connect Claude Code\n * stoops run opencode [--name <name>] [--admin] [-- <args>] Connect OpenCode\n */\n\nimport { createRequire } from \"node:module\";\nimport { serve } from \"./serve.js\";\nimport { join } from \"./join.js\";\nimport { runClaude } from \"./claude/run.js\";\nimport { runOpencode } from \"./opencode/run.js\";\nimport { runCodex } from \"./codex/run.js\";\nimport { buildShareUrl } from \"./auth.js\";\n\nfunction getVersion(): string {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nconst args = process.argv.slice(2);\n\nfunction getFlag(name: string, arr: string[] = args): string | undefined {\n const idx = arr.indexOf(`--${name}`);\n if (idx === -1) return undefined;\n const value = arr[idx + 1];\n if (value === undefined || value.startsWith(\"--\")) return undefined;\n return value;\n}\n\n/** Collect all values for a repeatable flag (e.g. --join url1 --join url2). */\nfunction getAllFlags(name: string, arr: string[] = args): string[] {\n const results: string[] = [];\n const flag = `--${name}`;\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] === flag && arr[i + 1] && !arr[i + 1].startsWith(\"--\")) {\n results.push(arr[i + 1]);\n }\n }\n return results;\n}\n\nfunction printUsage(stream: typeof console.log = console.log): void {\n stream(\"Usage:\");\n stream(\" stoops [--room <name>] [--port <port>] [--share] Host + join\");\n stream(\" stoops serve [--room <name>] [--port <port>] [--share] Headless server\");\n stream(\" stoops join <url> [--name <name>] [--guest] Join a room\");\n stream(\" stoops run claude [--name <name>] [--admin] [-- <args>] Connect Claude Code\");\n stream(\" stoops run opencode [--name <name>] [--admin] [-- <args>] Connect OpenCode\");\n stream(\" stoops run codex [--name <name>] [--admin] [-- <args>] Connect Codex\");\n}\n\nasync function main(): Promise<void> {\n // ── --help anywhere ────────────────────────────────────────────────────\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n printUsage();\n return;\n }\n\n // ── stoops run <runtime> ───────────────────────────────────────────────\n if (args[0] === \"run\" && (args[1] === \"claude\" || args[1] === \"opencode\" || args[1] === \"codex\")) {\n const runtime = args[1];\n const restArgs = args.slice(2);\n\n // Split on -- separator: stoops flags before, passthrough args after\n const ddIndex = restArgs.indexOf(\"--\");\n const stoopsArgs = ddIndex >= 0 ? restArgs.slice(0, ddIndex) : restArgs;\n const extraArgs = ddIndex >= 0 ? restArgs.slice(ddIndex + 1) : [];\n\n const joinUrls = getAllFlags(\"join\", stoopsArgs);\n\n const runtimeOptions = {\n joinUrls: joinUrls.length > 0 ? joinUrls : undefined,\n name: getFlag(\"name\", stoopsArgs),\n admin: stoopsArgs.includes(\"--admin\"),\n headless: stoopsArgs.includes(\"--headless\"),\n extraArgs,\n };\n\n if (runtime === \"claude\") {\n await runClaude(runtimeOptions);\n } else if (runtime === \"opencode\") {\n await runOpencode(runtimeOptions);\n } else {\n await runCodex(runtimeOptions);\n }\n return;\n }\n\n // ── stoops join <url> ──────────────────────────────────────────────────\n if (args[0] === \"join\") {\n const server = args[1];\n if (!server || server.startsWith(\"--\")) {\n console.error(\"Usage: stoops join <url> [--name <name>] [--guest] [--headless]\");\n process.exit(1);\n }\n await join({\n server,\n name: getFlag(\"name\"),\n guest: args.includes(\"--guest\"),\n headless: args.includes(\"--headless\"),\n });\n return;\n }\n\n // ── stoops serve ───────────────────────────────────────────────────────\n if (args[0] === \"serve\") {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n headless: args.includes(\"--headless\"),\n save: getFlag(\"save\"),\n load: getFlag(\"load\"),\n });\n return;\n }\n\n // ── stoops (bare) — host + join ────────────────────────────────────────\n if (args.length === 0 || args[0]?.startsWith(\"--\")) {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const result = await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n quiet: true,\n save: getFlag(\"save\"),\n load: getFlag(\"load\"),\n });\n\n // Host joins locally as admin using the admin share token\n const adminJoinUrl = buildShareUrl(result.serverUrl, result.adminToken);\n const participantShareUrl = buildShareUrl(\n result.publicUrl !== result.serverUrl ? result.publicUrl : result.serverUrl,\n result.memberToken,\n );\n\n await join({\n server: adminJoinUrl,\n name: getFlag(\"name\"),\n shareUrl: participantShareUrl,\n version: getVersion(),\n savePath: result.savePath,\n });\n return;\n }\n\n // ── Unknown command ────────────────────────────────────────────────────\n console.error(`Unknown command: ${args[0]}\\n`);\n printUsage(console.error);\n process.exit(1);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","/**\n * stoops serve — dumb room server.\n *\n * One room, one HTTP API, SSE broadcasting, authority enforcement.\n * No EventProcessor, no tmux, no agent lifecycle — those live client-side.\n * Humans connect via `stoops join`, agents via `stoops run claude`.\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { spawn, execFileSync, type ChildProcess } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { join as pathJoin } from \"node:path\";\n\nimport { Room } from \"../core/room.js\";\nimport { InMemoryStorage, FileBackedStorage } from \"../core/storage.js\";\nimport { randomRoomName, randomName } from \"../core/names.js\";\nimport { createEvent, type ActivityEvent, type AuthorityChangedEvent, type ParticipantKickedEvent, type RoomEvent } from \"../core/events.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport type { Channel } from \"../core/channel.js\";\nimport { formatTimestamp } from \"../agent/prompts.js\";\nimport { TokenManager, buildShareUrl } from \"./auth.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\ninterface ConnectedParticipant {\n id: string;\n name: string;\n authority: AuthorityLevel;\n channel: Channel;\n sessionToken: string;\n}\n\ninterface ConnectedGuest {\n id: string;\n authority: \"guest\";\n channel: Channel;\n sessionToken: string;\n}\n\nexport interface ServeOptions {\n room?: string;\n port?: number;\n share?: boolean;\n quiet?: boolean;\n /** Suppress all human-readable output; emit one JSON line with server info on stdout. */\n headless?: boolean;\n /** Path to save room state to (JSON file, written on every event). */\n save?: string;\n /** Path to load room state from (JSON file). Implies save to the same file. */\n load?: string;\n}\n\nexport interface ServeResult {\n serverUrl: string;\n publicUrl: string;\n roomName: string;\n adminToken: string;\n memberToken: string;\n savePath: string;\n}\n\n// ── SSE helper ───────────────────────────────────────────────────────────────\n\nasync function enrichAndSend(res: ServerResponse, event: RoomEvent, room: Room): Promise<void> {\n if (event.type === \"MessageSent\" && event.message.reply_to_id) {\n const replyMsg = await room.getMessage(event.message.reply_to_id);\n const enriched = {\n ...event,\n _replyToName: replyMsg?.sender_name ?? null,\n };\n res.write(`data: ${JSON.stringify(enriched)}\\n\\n`);\n return;\n }\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n}\n\n// ── Main serve command ───────────────────────────────────────────────────────\n\nexport async function serve(options: ServeOptions): Promise<ServeResult> {\n const roomName = options.room ?? randomRoomName();\n const port = options.port ?? 7890;\n const serverUrl = `http://127.0.0.1:${port}`;\n const log = options.headless ? () => {} : logServer;\n\n let publicUrl = serverUrl;\n let tunnelProcess: ChildProcess | null = null;\n\n // Create room with persistence (default: tmp folder)\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19); // YYYY-MM-DDTHH-MM-SS\n const savePath = options.save ?? options.load ?? pathJoin(tmpdir(), `stoops-${roomName}-${timestamp}.json`);\n let storage;\n if (options.load) {\n try {\n storage = await FileBackedStorage.load(options.load);\n log(`loaded room state from ${options.load}`);\n } catch (err: any) {\n if (err.code === \"ENOENT\") {\n storage = new FileBackedStorage(options.load);\n log(`no existing file at ${options.load}, starting fresh`);\n } else {\n throw err;\n }\n }\n } else {\n storage = new FileBackedStorage(savePath);\n }\n const room = new Room(roomName, storage);\n\n // Auth\n const tokens = new TokenManager();\n\n // Connected participants and guests (by session token for lookup)\n const participants = new Map<string, ConnectedParticipant>();\n const guests = new Map<string, ConnectedGuest>();\n // Reverse lookup: participantId → sessionToken\n const idToSession = new Map<string, string>();\n\n // Track active SSE connections for cleanup\n const sseConnections = new Map<string, ServerResponse>();\n\n // ── JSON body parser helper ──────────────────────────────────────────────\n\n async function parseBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n try { return JSON.parse(Buffer.concat(chunks).toString()); } catch { return {}; }\n }\n\n // ── Auth helper ──────────────────────────────────────────────────────────\n\n function getSession(token: string | null) {\n if (!token) return null;\n const p = participants.get(token);\n if (p) return { ...p, kind: \"participant\" as const };\n const g = guests.get(token);\n if (g) return { ...g, kind: \"guest\" as const };\n return null;\n }\n\n function jsonError(res: ServerResponse, status: number, error: string): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error }));\n }\n\n function jsonOk(res: ServerResponse, data: Record<string, unknown> = {}): void {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, ...data }));\n }\n\n // ── HTTP API ────────────────────────────────────────────────────────────\n\n const httpServer = createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${port}`);\n\n // ── SSE event stream ───────────────────────────────────────────────────\n // ⚠️ MUST accept POST — DO NOT change to GET-only.\n // Cloudflare Quick Tunnels buffer GET streaming responses and only flush\n // when the connection closes. POST streams in real-time. (cloudflared#1449)\n // https://github.com/cloudflare/cloudflared/issues/1449\n if (url.pathname === \"/events\" && (req.method === \"GET\" || req.method === \"POST\")) {\n const authHeader = req.headers.authorization;\n const sessionToken = authHeader?.startsWith(\"Bearer \")\n ? authHeader.slice(7)\n : null;\n const session = getSession(sessionToken);\n\n if (!session) {\n jsonError(res, 401, \"Invalid session token\");\n return;\n }\n\n // SSE headers\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n res.flushHeaders();\n\n // Disable Nagle's algorithm so SSE events flush immediately.\n // Without this, small writes may be delayed up to ~200ms waiting\n // for more data, which can cause events to appear \"stuck\" until\n // the next event (e.g. a MentionedEvent) pushes the buffer.\n res.socket?.setNoDelay(true);\n\n sseConnections.set(session.id, res);\n\n // Send recent history so the joiner has context\n const history = await room.listEvents(undefined, 50);\n for (const event of [...history.items].reverse()) {\n await enrichAndSend(res, event, room);\n }\n\n // Live event stream\n const streamEvents = async () => {\n try {\n for await (const event of session.channel) {\n await enrichAndSend(res, event, room);\n }\n } catch {\n // Channel disconnected\n }\n };\n streamEvents();\n\n // Cleanup on client disconnect\n req.on(\"close\", () => {\n sseConnections.delete(session.id);\n });\n return;\n }\n\n // ── GET endpoints ─────────────────────────────────────────────────────\n\n if (req.method === \"GET\") {\n const sessionToken = url.searchParams.get(\"token\");\n const session = getSession(sessionToken);\n\n // ── GET /participants ────────────────────────────────────────────────\n if (url.pathname === \"/participants\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const list = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ participants: list }));\n return;\n }\n\n // ── GET /message/:id ─────────────────────────────────────────────────\n if (url.pathname.startsWith(\"/message/\")) {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const messageId = url.pathname.slice(\"/message/\".length);\n const msg = await room.getMessage(messageId);\n if (!msg) return jsonError(res, 404, \"Message not found\");\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ message: msg }));\n return;\n }\n\n // ── GET /messages ────────────────────────────────────────────────────\n if (url.pathname === \"/messages\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const count = parseInt(url.searchParams.get(\"count\") ?? \"30\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.listMessages(count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n\n // ── GET /events/history ──────────────────────────────────────────────\n if (url.pathname === \"/events/history\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const category = url.searchParams.get(\"category\") ?? null;\n const count = parseInt(url.searchParams.get(\"count\") ?? \"50\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.listEvents(category as any, count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n\n // ── GET /search ──────────────────────────────────────────────────────\n if (url.pathname === \"/search\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const query = url.searchParams.get(\"query\") ?? \"\";\n if (!query) return jsonError(res, 400, \"Missing query parameter\");\n const count = parseInt(url.searchParams.get(\"count\") ?? \"10\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.searchMessages(query, count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n }\n\n // ── POST endpoints ────────────────────────────────────────────────────\n\n if (req.method === \"POST\") {\n const body = await parseBody(req);\n\n // ── POST /join ──────────────────────────────────────────────────────\n if (url.pathname === \"/join\") {\n // Accept share token OR legacy type-based join\n const shareToken = String(body.token ?? \"\");\n const legacyType = String(body.type ?? \"\");\n\n let authority: AuthorityLevel;\n\n if (shareToken) {\n const tokenAuthority = tokens.validateShareToken(shareToken);\n if (!tokenAuthority) return jsonError(res, 403, \"Invalid share token\");\n authority = tokenAuthority;\n } else if (legacyType === \"guest\") {\n authority = \"guest\";\n } else if (legacyType === \"human\") {\n authority = \"member\";\n } else {\n // Default: agent joins as member\n authority = \"member\";\n }\n\n const participantType = String(body.type ?? \"human\") as \"human\" | \"agent\";\n const name = String(body.name ?? randomName());\n\n if (authority === \"guest\") {\n const id = `obs_${randomUUID().slice(0, 8)}`;\n const channel = room.observe();\n const sessionToken = tokens.createSessionToken(id, \"guest\");\n\n guests.set(sessionToken, { id, authority: \"guest\", channel, sessionToken });\n idToSession.set(id, sessionToken);\n\n const participantList = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n sessionToken,\n participantId: id,\n roomName,\n roomId: room.roomId,\n participants: participantList,\n authority: \"guest\",\n }));\n return;\n }\n\n // admin or participant — connect as a real participant\n const id = `${participantType}_${randomUUID().slice(0, 8)}`;\n const channel = await room.connect(id, name, { type: participantType, authority });\n const sessionToken = tokens.createSessionToken(id, authority);\n\n participants.set(sessionToken, { id, name, authority, channel, sessionToken });\n idToSession.set(id, sessionToken);\n\n const participantList = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n\n log(`${name} joined (${authority})`);\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n sessionToken,\n participantId: id,\n roomName,\n roomId: room.roomId,\n participants: participantList,\n authority,\n }));\n return;\n }\n\n // ── All remaining POST endpoints require a session token ────────────\n\n const sessionToken = String(body.token ?? \"\");\n const session = getSession(sessionToken);\n\n // ── POST /message ───────────────────────────────────────────────────\n if (url.pathname === \"/message\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot send messages\");\n const content = String(body.content ?? \"\");\n const replyTo = body.replyTo ? String(body.replyTo) : undefined;\n if (!content) return jsonError(res, 400, \"Empty message\");\n\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n const msg = await p.channel.sendMessage(content, replyTo);\n jsonOk(res, { messageId: msg.id });\n return;\n }\n\n // ── POST /event ─────────────────────────────────────────────────────\n if (url.pathname === \"/event\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot emit events\");\n const event = body.event as RoomEvent | undefined;\n if (!event) return jsonError(res, 400, \"Missing event\");\n\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n await p.channel.emit(event);\n jsonOk(res);\n return;\n }\n\n // ── POST /set-mode ──────────────────────────────────────────────────\n if (url.pathname === \"/set-mode\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const targetId = body.participantId ? String(body.participantId) : session.id;\n const mode = String(body.mode ?? \"\");\n if (!mode) return jsonError(res, 400, \"Missing mode\");\n\n // Setting someone else's mode requires admin\n if (targetId !== session.id && session.authority !== \"admin\") {\n return jsonError(res, 403, \"Only admins can change other participants' modes\");\n }\n\n // Emit mode_changed activity event\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n await p.channel.emit(createEvent<ActivityEvent>({\n type: \"Activity\",\n category: \"ACTIVITY\",\n room_id: room.roomId,\n participant_id: targetId,\n action: \"mode_changed\",\n detail: { mode },\n }));\n\n jsonOk(res);\n return;\n }\n\n // ── POST /set-authority ──────────────────────────────────────────────\n if (url.pathname === \"/set-authority\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority !== \"admin\") return jsonError(res, 403, \"Only admins can change authority\");\n const targetId = String(body.participantId ?? \"\");\n const newAuthority = String(body.authority ?? \"\") as AuthorityLevel;\n if (!targetId) return jsonError(res, 400, \"Missing participantId\");\n if (![\"admin\", \"member\", \"guest\"].includes(newAuthority)) {\n return jsonError(res, 400, \"Invalid authority. Must be admin, member, or guest.\");\n }\n if (targetId === session.id) return jsonError(res, 400, \"Cannot change own authority\");\n\n // Update all three places: ConnectedParticipant, TokenManager session, Room participant\n const targetSession = idToSession.get(targetId);\n if (!targetSession) return jsonError(res, 404, \"Participant not found\");\n const target = participants.get(targetSession);\n if (!target) return jsonError(res, 404, \"Participant not found\");\n\n target.authority = newAuthority;\n tokens.updateSessionAuthority(targetSession, newAuthority);\n room.setParticipantAuthority(targetId, newAuthority);\n\n // Emit AuthorityChanged event\n const adminP = participants.get(sessionToken);\n const targetParticipant = room.listParticipants().find(p => p.id === targetId);\n if (adminP && targetParticipant) {\n await adminP.channel.emit(createEvent<AuthorityChangedEvent>({\n type: \"AuthorityChanged\",\n category: \"PRESENCE\",\n room_id: room.roomId,\n participant_id: targetId,\n participant: targetParticipant,\n new_authority: newAuthority,\n changed_by: adminP.name,\n }));\n }\n\n log(`${target.name} authority → ${newAuthority}`);\n jsonOk(res);\n return;\n }\n\n // ── POST /kick ──────────────────────────────────────────────────────\n if (url.pathname === \"/kick\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority !== \"admin\") return jsonError(res, 403, \"Only admins can kick\");\n const targetId = String(body.participantId ?? \"\");\n if (!targetId) return jsonError(res, 400, \"Missing participantId\");\n\n // Find and disconnect the target\n const targetSession = idToSession.get(targetId);\n if (targetSession) {\n const target = participants.get(targetSession) ?? guests.get(targetSession);\n if (target) {\n // Emit ParticipantKicked before disconnect so all participants see it\n const adminP = participants.get(sessionToken);\n const targetParticipant = room.listParticipants().find(p => p.id === targetId);\n if (adminP && targetParticipant) {\n await adminP.channel.emit(createEvent<ParticipantKickedEvent>({\n type: \"ParticipantKicked\",\n category: \"PRESENCE\",\n room_id: room.roomId,\n participant_id: targetId,\n participant: targetParticipant,\n kicked_by: adminP.name,\n }));\n }\n // Silent disconnect — the kicked event replaces ParticipantLeft\n await target.channel.disconnect(true);\n participants.delete(targetSession);\n guests.delete(targetSession);\n idToSession.delete(targetId);\n tokens.revokeSessionToken(targetSession);\n // Close SSE connection\n const sse = sseConnections.get(targetId);\n if (sse) {\n sse.end();\n sseConnections.delete(targetId);\n }\n log(`kicked ${targetId}`);\n }\n }\n\n jsonOk(res);\n return;\n }\n\n // ── POST /share ─────────────────────────────────────────────────────\n if (url.pathname === \"/share\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot create share links\");\n\n const targetAuthority = (body.authority as AuthorityLevel) ?? undefined;\n\n const links: Record<string, string> = {};\n\n if (targetAuthority) {\n // Generate a specific link\n const token = tokens.generateShareToken(session.authority, targetAuthority);\n if (!token) return jsonError(res, 403, `Cannot generate ${targetAuthority} link`);\n links[targetAuthority] = buildShareUrl(publicUrl, token);\n } else {\n // Generate all links the caller can create\n const tiers: AuthorityLevel[] = [\"admin\", \"member\", \"guest\"];\n for (const tier of tiers) {\n const token = tokens.generateShareToken(session.authority, tier);\n if (token) links[tier] = buildShareUrl(publicUrl, token);\n }\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ links }));\n return;\n }\n\n // ── POST /disconnect ────────────────────────────────────────────────\n if (url.pathname === \"/disconnect\") {\n // Accept either session token or legacy participantId/agentId\n const token = String(body.token ?? \"\");\n const legacyId = String(body.participantId ?? body.agentId ?? \"\");\n\n let targetToken = token;\n if (!targetToken && legacyId) {\n targetToken = idToSession.get(legacyId) ?? \"\";\n }\n\n if (targetToken) {\n const p = participants.get(targetToken);\n if (p) {\n await p.channel.disconnect();\n participants.delete(targetToken);\n idToSession.delete(p.id);\n tokens.revokeSessionToken(targetToken);\n const sse = sseConnections.get(p.id);\n if (sse) { sse.end(); sseConnections.delete(p.id); }\n log(`${p.name} disconnected`);\n }\n\n const g = guests.get(targetToken);\n if (g) {\n await g.channel.disconnect();\n guests.delete(targetToken);\n idToSession.delete(g.id);\n tokens.revokeSessionToken(targetToken);\n const sse = sseConnections.get(g.id);\n if (sse) { sse.end(); sseConnections.delete(g.id); }\n }\n }\n\n jsonOk(res);\n return;\n }\n }\n\n res.writeHead(404).end(\"Not found\");\n });\n\n httpServer.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n console.error(`\\nPort ${port} is already in use. Another stoops instance may be running.`);\n console.error(` Kill it: lsof -ti :${port} | xargs kill`);\n console.error(` Or use: stoops --port ${port + 1}\\n`);\n process.exit(1);\n }\n throw err;\n });\n\n await new Promise<void>((resolve) => {\n httpServer.listen(port, \"0.0.0.0\", () => resolve());\n });\n\n // Start tunnel if --share\n if (options.share) {\n tunnelProcess = await startTunnel(port);\n if (tunnelProcess) {\n const tunnelUrl = await waitForTunnelUrl(tunnelProcess);\n if (tunnelUrl) publicUrl = tunnelUrl;\n }\n }\n\n // Generate share tokens on boot\n const adminToken = tokens.generateShareToken(\"admin\", \"admin\")!;\n const memberToken = tokens.generateShareToken(\"admin\", \"member\")!;\n\n if (options.headless) {\n process.stdout.write(JSON.stringify({ serverUrl, publicUrl, roomName, adminToken, memberToken, savePath }) + \"\\n\");\n } else if (!options.quiet) {\n let version = process.env.npm_package_version ?? \"\";\n if (!version) {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n version = pkg.version ?? \"unknown\";\n } catch {\n version = \"unknown\";\n }\n }\n const adminUrl = buildShareUrl(publicUrl, adminToken);\n const joinUrl = buildShareUrl(publicUrl, memberToken);\n\n console.log(`\n stoops v${version}\n\n Room: ${roomName}\n Server: ${serverUrl}${publicUrl !== serverUrl ? `\\n Tunnel: ${publicUrl}` : \"\"}\n Saving: ${savePath}\n\n Join: stoops join ${joinUrl}\n Admin: stoops join ${adminUrl}\n Claude: stoops run claude --name MyClaude → then tell agent to join: ${joinUrl}\n Codex: stoops run codex --name MyCodex → then tell agent to join: ${joinUrl}\n`);\n }\n\n // ── Graceful shutdown ──────────────────────────────────────────────────\n\n const shutdown = async () => {\n log(\"shutting down...\");\n if (tunnelProcess) { tunnelProcess.kill(); tunnelProcess = null; }\n for (const [id, sse] of sseConnections) { sse.end(); sseConnections.delete(id); }\n for (const p of participants.values()) { await p.channel.disconnect().catch(() => {}); }\n for (const g of guests.values()) { await g.channel.disconnect().catch(() => {}); }\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => (err ? reject(err) : resolve()));\n });\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return { serverUrl, publicUrl, roomName, adminToken, memberToken, savePath };\n}\n\n// ── Server log ────────────────────────────────────────────────────────────────\n\nfunction logServer(message: string): void {\n console.log(` [${formatTimestamp(new Date())}] ${message}`);\n}\n\n// ── Cloudflared tunnel ───────────────────────────────────────────────────────\n\nfunction cloudflaredAvailable(): boolean {\n try {\n execFileSync(\"which\", [\"cloudflared\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nasync function startTunnel(port: number): Promise<ChildProcess | null> {\n if (!cloudflaredAvailable()) {\n console.error(\" --share requires cloudflared. Install: brew install cloudflared\");\n return null;\n }\n\n const child = spawn(\"cloudflared\", [\"tunnel\", \"--url\", `http://localhost:${port}`], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n\n child.on(\"error\", () => {\n // cloudflared failed to start\n });\n\n return child;\n}\n\nfunction waitForTunnelUrl(child: ChildProcess, timeoutMs = 15000): Promise<string | null> {\n return new Promise((resolve) => {\n let resolved = false;\n let buffer = \"\";\n\n const timer = setTimeout(() => {\n if (!resolved) { resolved = true; resolve(null); }\n }, timeoutMs);\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString();\n const match = buffer.match(/https:\\/\\/[a-z0-9-]+\\.trycloudflare\\.com/);\n if (match && !resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve(match[0]);\n }\n });\n\n child.on(\"exit\", () => {\n if (!resolved) { resolved = true; clearTimeout(timer); resolve(null); }\n });\n });\n}\n","/**\n * Auth token system for stoops share links and session management.\n *\n * Two token types:\n * - Share tokens — embedded in URLs, map to an authority tier.\n * Anyone with the link joins at that tier.\n * - Session tokens — issued on join, identify a participant + authority.\n * Used for all subsequent API calls.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { AuthorityLevel } from \"../core/types.js\";\n\ninterface SessionData {\n participantId: string;\n authority: AuthorityLevel;\n}\n\nexport class TokenManager {\n /** share token hash → authority level */\n private _shareTokens = new Map<string, AuthorityLevel>();\n /** session token → participant data */\n private _sessionTokens = new Map<string, SessionData>();\n\n /**\n * Generate a share token at the given authority tier.\n * Callers can only generate tokens at their own tier or below.\n */\n generateShareToken(callerAuthority: AuthorityLevel, targetAuthority: AuthorityLevel): string | null {\n if (!canGrant(callerAuthority, targetAuthority)) return null;\n const token = randomBytes(16).toString(\"hex\");\n this._shareTokens.set(token, targetAuthority);\n return token;\n }\n\n /** Validate a share token and return its authority level. */\n validateShareToken(token: string): AuthorityLevel | null {\n return this._shareTokens.get(token) ?? null;\n }\n\n /** Create a session token for a participant. */\n createSessionToken(participantId: string, authority: AuthorityLevel): string {\n const token = randomBytes(16).toString(\"hex\");\n this._sessionTokens.set(token, { participantId, authority });\n return token;\n }\n\n /** Validate a session token and return participant data. */\n validateSessionToken(token: string): SessionData | null {\n return this._sessionTokens.get(token) ?? null;\n }\n\n /** Revoke a session token (on disconnect). */\n revokeSessionToken(token: string): void {\n this._sessionTokens.delete(token);\n }\n\n /** Update the authority level for an existing session. */\n updateSessionAuthority(token: string, newAuthority: AuthorityLevel): boolean {\n const data = this._sessionTokens.get(token);\n if (!data) return false;\n data.authority = newAuthority;\n return true;\n }\n\n /** Find a session token by participant ID (for cleanup). */\n findSessionByParticipant(participantId: string): string | null {\n for (const [token, data] of this._sessionTokens) {\n if (data.participantId === participantId) return token;\n }\n return null;\n }\n}\n\n/** Authority tier ordering: admin > member > guest. */\nconst TIER_ORDER: Record<AuthorityLevel, number> = {\n admin: 2,\n member: 1,\n guest: 0,\n};\n\n/** Can a caller at `callerLevel` grant authority at `targetLevel`? */\nfunction canGrant(callerLevel: AuthorityLevel, targetLevel: AuthorityLevel): boolean {\n return TIER_ORDER[callerLevel] >= TIER_ORDER[targetLevel];\n}\n\n/** Build a share URL from a base URL and token. */\nexport function buildShareUrl(baseUrl: string, token: string): string {\n const url = new URL(baseUrl);\n url.searchParams.set(\"token\", token);\n return url.toString();\n}\n\n/** Extract a token from a share URL. */\nexport function extractToken(url: string): string | null {\n try {\n const parsed = new URL(url);\n return parsed.searchParams.get(\"token\");\n } catch {\n return null;\n }\n}\n","/**\n * stoops join — connect to a room as a human participant.\n *\n * Opens the TUI and connects to a stoops server over HTTP.\n * Events stream in via SSE, messages sent via POST /message.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { createInterface } from \"node:readline\";\nimport { randomName } from \"../core/names.js\";\nimport type { RoomEvent } from \"../core/events.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport { formatTimestamp } from \"../agent/prompts.js\";\nimport { startTUI, type TUIHandle, type DisplayEvent } from \"./tui.js\";\nimport { extractToken, buildShareUrl } from \"./auth.js\";\n\nexport interface JoinOptions {\n server: string;\n name?: string;\n guest?: boolean;\n /** Share URL to display before TUI starts (for host+join mode with --share). */\n shareUrl?: string;\n /** Skip TUI — stream events as JSON to stdout, read messages from stdin. */\n headless?: boolean;\n /** Version string to display in the TUI banner. */\n version?: string;\n /** Path where room state is being saved. */\n savePath?: string;\n}\n\nexport async function join(options: JoinOptions): Promise<void> {\n // Extract token from URL if present\n const token = extractToken(options.server);\n // Strip query params to get clean server URL\n let serverUrl: string;\n try {\n const parsed = new URL(options.server);\n parsed.search = \"\";\n serverUrl = parsed.toString().replace(/\\/$/, \"\");\n } catch {\n serverUrl = options.server.replace(/\\/$/, \"\");\n }\n\n const name = options.name ?? randomName();\n const isGuest = options.guest ?? false;\n\n // ── Register with server ────────────────────────────────────────────────\n\n let sessionToken: string;\n let participantId: string;\n let roomName: string;\n let authority: AuthorityLevel;\n let participants: Array<{ id: string; name: string; type: string; authority?: string }>;\n\n try {\n const joinBody: Record<string, unknown> = {};\n if (token) {\n joinBody.token = token;\n joinBody.type = \"human\";\n joinBody.name = name;\n } else if (isGuest) {\n joinBody.type = \"guest\";\n } else {\n joinBody.type = \"human\";\n joinBody.name = name;\n }\n\n const res = await fetch(`${serverUrl}/join`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(joinBody),\n });\n\n if (!res.ok) {\n const err = await res.text();\n console.error(`Failed to join: ${err}`);\n process.exit(1);\n }\n\n const data = await res.json() as Record<string, unknown>;\n sessionToken = String(data.sessionToken ?? \"\");\n participantId = String(data.participantId);\n roomName = String(data.roomName);\n authority = (data.authority as AuthorityLevel) ?? \"member\";\n participants = (data.participants as Array<{ id: string; name: string; type: string; authority?: string }>) ?? [];\n } catch {\n console.error(`Cannot reach stoops server at ${serverUrl}. Is it running?`);\n process.exit(1);\n }\n\n // ── Disconnect helper ───────────────────────────────────────────────────\n\n let disconnected = false;\n let cleanupStream: (() => void) | null = null;\n const disconnect = async () => {\n if (disconnected) return;\n disconnected = true;\n cleanupStream?.();\n try {\n await fetch(`${serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken }),\n });\n } catch {\n // Server may be down\n }\n };\n\n // ── Headless mode — skip TUI, stream events as JSON, read messages from stdin ──\n\n if (options.headless) {\n const sseController = new AbortController();\n const cleanup = async () => {\n sseController.abort();\n await disconnect();\n };\n\n process.on(\"SIGINT\", async () => { await cleanup(); process.exit(0); });\n process.on(\"SIGTERM\", async () => { await cleanup(); process.exit(0); });\n\n // Read messages from stdin and send them\n const rl = createInterface({ input: process.stdin, terminal: false });\n rl.on(\"line\", async (line) => {\n const content = line.trim();\n if (!content || authority === \"guest\") return;\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, content }),\n });\n } catch { /* server may be down */ }\n });\n\n // Stream events from SSE and write as JSON lines to stdout\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: { Accept: \"text/event-stream\", Authorization: `Bearer ${sessionToken}` },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n process.stderr.write(\"Failed to connect event stream\\n\");\n await cleanup();\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const parts = buf.split(\"\\n\\n\");\n buf = parts.pop()!;\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n try {\n const event = JSON.parse(dataLine.slice(6));\n process.stdout.write(JSON.stringify(event) + \"\\n\");\n } catch { /* malformed */ }\n }\n }\n } catch {\n // Stream ended or aborted\n }\n\n if (!disconnected) { await cleanup(); }\n process.exit(0);\n }\n\n // ── Start TUI ───────────────────────────────────────────────────────────\n\n const isReadOnly = authority === \"guest\" || isGuest;\n\n // ── Slash command helper ──────────────────────────────────────────────\n\n function systemEvent(content: string): void {\n tui.push({\n id: randomUUID(),\n ts: formatTimestamp(new Date()),\n kind: \"system\",\n content,\n });\n }\n\n async function handleSlashCommand(input: string): Promise<void> {\n const parts = input.slice(1).split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n const args = parts.slice(1);\n\n switch (cmd) {\n // ── /who ──────────────────────────────────────────────────────\n case \"who\": {\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string; type: string; authority?: string }> };\n const lines = data.participants.map((p) => {\n const auth = p.authority ?? \"member\";\n return ` ${p.type === \"agent\" ? \"agent\" : \"human\"} ${p.name} (${auth})`;\n });\n systemEvent(`Participants:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /leave ────────────────────────────────────────────────────\n case \"leave\": {\n await disconnect();\n tui.stop();\n process.exit(0);\n return;\n }\n\n // ── /kick <name> (admin only) ─────────────────────────────────\n case \"kick\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can kick.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /kick <name>\"); return; }\n\n // Look up participant by name\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const kickRes = await fetch(`${serverUrl}/kick`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id }),\n });\n if (!kickRes.ok) { systemEvent(`Failed to kick: ${await kickRes.text()}`); return; }\n systemEvent(`Kicked ${targetName}.`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /mute <name> (admin only) — demote to observer ────────────\n case \"mute\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can mute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /mute <name>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const authRes = await fetch(`${serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, authority: \"guest\" }),\n });\n if (!authRes.ok) { systemEvent(`Failed to mute: ${await authRes.text()}`); return; }\n systemEvent(`Muted ${targetName} (guest).`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /unmute <name> (admin only) — restore to participant ──────\n case \"unmute\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can unmute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /unmute <name>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const authRes = await fetch(`${serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, authority: \"member\" }),\n });\n if (!authRes.ok) { systemEvent(`Failed to unmute: ${await authRes.text()}`); return; }\n systemEvent(`Unmuted ${targetName} (member).`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /setmode <name> <mode> (admin only) ───────────────────────\n case \"setmode\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can set modes.\"); return; }\n const targetName = args[0];\n const mode = args[1];\n if (!targetName || !mode) { systemEvent(\"Usage: /setmode <name> <mode>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const modeRes = await fetch(`${serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, mode }),\n });\n if (!modeRes.ok) { systemEvent(`Failed to set mode: ${await modeRes.text()}`); return; }\n systemEvent(`Set ${targetName} to ${mode}.`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /share [--as <tier>] ──────────────────────────────────────\n case \"share\": {\n if (authority === \"guest\") { systemEvent(\"Guests cannot create share links.\"); return; }\n\n let targetAuthority: string | undefined;\n if (args[0] === \"--as\" && args[1]) {\n targetAuthority = args[1];\n }\n\n try {\n const body: Record<string, unknown> = { token: sessionToken };\n if (targetAuthority) body.authority = targetAuthority;\n\n const res = await fetch(`${serverUrl}/share`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) { systemEvent(`Failed: ${await res.text()}`); return; }\n const data = (await res.json()) as { links: Record<string, string> };\n const lines = Object.entries(data.links).map(([tier, url]) =>\n ` ${tier}: stoops join ${url}`\n );\n systemEvent(`Share links:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n default:\n systemEvent(`Unknown command: /${cmd}`);\n }\n }\n\n // Print share info via console.log BEFORE Ink renders. This lands in the\n // terminal buffer above Ink's render area — plain text, no ANSI codes,\n // fully selectable. Each line is a complete copyable command.\n if (options.shareUrl) {\n console.log();\n console.log(` Invite a friend: npx stoops join \"${options.shareUrl}\"`);\n console.log(` Connect Claude Code: npx stoops run claude --name MyClaude → then tell agent to join: ${options.shareUrl}`);\n console.log(` Connect Codex: npx stoops run codex --name MyCodex → then tell agent to join: ${options.shareUrl}`);\n console.log();\n }\n\n const tui = startTUI({\n roomName,\n readOnly: isReadOnly,\n isAdmin: authority === \"admin\",\n version: options.version,\n savePath: options.savePath,\n onSend: isReadOnly ? undefined : async (content: string) => {\n // Intercept slash commands\n if (content.startsWith(\"/\")) {\n await handleSlashCommand(content);\n return;\n }\n\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, content }),\n });\n } catch {\n // Server may be down — silently fail\n }\n },\n onCtrlC: async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n },\n });\n\n // Set initial agent names + participant names\n const agentNames = participants\n .filter((p) => p.type === \"agent\")\n .map((p) => p.name);\n if (agentNames.length > 0) {\n tui.setAgentNames(agentNames);\n }\n const participantNames = new Set(\n participants.filter((p) => p.id !== participantId).map((p) => p.name),\n );\n tui.setParticipants([...participantNames]);\n\n // ── Connect SSE event stream ────────────────────────────────────────────\n // ⚠️ MUST use POST — DO NOT change to GET.\n // Cloudflare Quick Tunnels buffer GET streaming responses and only flush\n // when the connection closes. POST streams in real-time. (cloudflared#1449)\n // https://github.com/cloudflare/cloudflared/issues/1449\n\n {\n const participantTypes = new Map<string, \"human\" | \"agent\">();\n for (const p of participants) {\n participantTypes.set(p.id, p.type as \"human\" | \"agent\");\n }\n const currentAgents = new Set(agentNames);\n let sseController: AbortController | null = null;\n\n cleanupStream = () => {\n if (sseController) { sseController.abort(); sseController = null; }\n };\n\n const connectSSE = async () => {\n sseController = new AbortController();\n\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: {\n Accept: \"text/event-stream\",\n Authorization: `Bearer ${sessionToken}`,\n },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n console.error(\"Failed to connect event stream\");\n await disconnect();\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n\n try {\n const event = JSON.parse(dataLine.slice(6)) as RoomEvent & { _replyToName?: string };\n\n if (event.type === \"ParticipantJoined\") {\n participantTypes.set(event.participant.id, event.participant.type);\n }\n\n const displayEvent = toDisplayEvent(event, participantId, participantTypes);\n if (displayEvent) {\n tui.push(displayEvent);\n }\n\n if (event.type === \"ParticipantJoined\") {\n if (event.participant.type === \"agent\") {\n currentAgents.add(event.participant.name);\n tui.setAgentNames([...currentAgents]);\n }\n if (event.participant.id !== participantId) {\n participantNames.add(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n }\n if (event.type === \"ParticipantLeft\") {\n if (event.participant.type === \"agent\") {\n currentAgents.delete(event.participant.name);\n tui.setAgentNames([...currentAgents]);\n }\n participantTypes.delete(event.participant.id);\n participantNames.delete(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n } catch {\n // Malformed event — skip\n }\n }\n }\n\n // Stream ended — server closed connection\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n } catch {\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n }\n };\n\n connectSSE();\n }\n\n // ── Graceful shutdown ───────────────────────────────────────────────────\n\n process.on(\"SIGINT\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n}\n\n// ── RoomEvent → DisplayEvent conversion ───────────────────────────────────────\n\nfunction toDisplayEvent(\n event: RoomEvent & { _replyToName?: string },\n selfId: string,\n participantTypes: Map<string, \"human\" | \"agent\">,\n): DisplayEvent | null {\n const ts = formatTimestamp(new Date(event.timestamp));\n\n switch (event.type) {\n case \"MessageSent\": {\n const msg = event.message;\n const senderType = participantTypes.get(msg.sender_id) ?? \"human\";\n return {\n id: msg.id,\n ts,\n kind: \"message\",\n senderName: msg.sender_name,\n senderType,\n isSelf: msg.sender_id === selfId,\n content: msg.content,\n replyToName: event._replyToName ?? undefined,\n };\n }\n case \"ParticipantJoined\":\n return {\n id: randomUUID(),\n ts,\n kind: \"join\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantLeft\":\n return {\n id: randomUUID(),\n ts,\n kind: \"leave\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantKicked\":\n return {\n id: randomUUID(),\n ts,\n kind: \"system\",\n content: `${event.participant.name} was kicked`,\n };\n case \"AuthorityChanged\": {\n const name = event.participant.name;\n if (event.new_authority === \"guest\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was muted` };\n }\n if (event.new_authority === \"member\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was unmuted` };\n }\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} → ${event.new_authority}` };\n }\n case \"Activity\":\n if (event.action === \"mode_changed\") {\n return {\n id: randomUUID(),\n ts,\n kind: \"mode\",\n mode: String((event.detail as Record<string, unknown>)?.mode ?? \"\"),\n };\n }\n return null;\n default:\n return null;\n }\n}\n","/**\n * stoops TUI — ink-based terminal UI for the room server.\n *\n * Uses ink's <Static> for events (rendered once, selectable terminal text)\n * and a dynamic footer for input + status. Same architecture as Claude Code.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from \"react\";\nimport { render, Box, Text, Static, useStdout, useInput } from \"ink\";\n\n// ── Palette (from stoops-app) ─────────────────────────────────────────────────\n\nconst C = {\n cyan: \"#00d4ff\",\n purple: \"#8b5cf6\",\n orange: \"#ff8c42\",\n pink: \"#f472b6\",\n green: \"#34d399\",\n yellow: \"#fbbf24\",\n danger: \"#f87171\",\n text: \"#eceff4\",\n secondary: \"#b0b7c4\",\n dim: \"#7e8798\",\n muted: \"#5b6679\",\n border: \"#475264\",\n} as const;\n\nconst AGENT_COLORS = [C.cyan, C.purple, C.orange, C.pink, C.green, C.yellow] as const;\nconst SIGILS = [\"◆\", \"▲\", \"●\", \"■\", \"★\", \"◉\", \"◈\", \"▸\"] as const;\n\n// ── Banner ───────────────────────────────────────────────────────────────────\n// Figlet \"slant\" font, colored with a purple → cyan gradient per line.\n\nconst BANNER_LINES = [\n \" __ \",\n \" _____/ /_____ ____ ____ _____\",\n \" / ___/ __/ __ \\\\/ __ \\\\/ __ \\\\/ ___/\",\n \" (__ ) /_/ /_/ / /_/ / /_/ (__ ) \",\n \"/____/\\\\__/\\\\____/\\\\____/ .___/____/ \",\n \" /_/ \",\n];\n\nconst GRADIENT = [\"#9b6dff\", \"#7c8bff\", \"#5da8ff\", \"#3dc4ff\", \"#1ddcff\", \"#00e8ff\"];\n\n// ── Slash commands ────────────────────────────────────────────────────────────\n\ninterface SlashParam {\n label: string; // display hint: \"name\", \"mode\", etc.\n completions?: string[] | \"participants\"; // static values, dynamic lookup, or undefined (hint only)\n}\n\ninterface SlashCommand {\n name: string;\n description: string;\n adminOnly?: boolean;\n params?: SlashParam[];\n}\n\nconst ENGAGEMENT_MODES = [\n \"everyone\", \"people\", \"agents\",\n \"standby-everyone\", \"standby-people\", \"standby-agents\",\n];\n\nconst SLASH_COMMANDS: SlashCommand[] = [\n { name: \"/who\", description: \"List participants\" },\n { name: \"/leave\", description: \"Disconnect and exit\" },\n { name: \"/share\", description: \"Generate share links\" },\n { name: \"/kick\", description: \"Remove a participant\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/mute\", description: \"Make read-only (guest)\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/unmute\", description: \"Restore to member\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/setmode\", description: \"Set engagement mode\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n { label: \"mode\", completions: ENGAGEMENT_MODES },\n ]},\n];\n\nconst CMD_DISPLAY_COL = 26; // width for command + params display column\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DisplayEvent =\n | { id: string; ts: string; kind: \"message\"; senderName: string; senderType: \"human\" | \"agent\"; isSelf: boolean; content: string; replyToName?: string }\n | { id: string; ts: string; kind: \"join\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"leave\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"mode\"; mode: string }\n | { id: string; ts: string; kind: \"system\"; content: string };\n\nexport interface TUIHandle {\n push(event: DisplayEvent): void;\n setAgentNames(names: string[]): void;\n setParticipants(names: string[]): void;\n stop(): void;\n}\n\nexport interface TUIOptions {\n roomName: string;\n onSend?(content: string): void;\n onCtrlC?(): void;\n readOnly?: boolean;\n isAdmin?: boolean;\n version?: string;\n savePath?: string;\n}\n\n// ── Identity (seed → color + sigil) ──────────────────────────────────────────\n\nfunction seedHash(s: string): number {\n let h = 0;\n for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;\n return Math.abs(h);\n}\n\nfunction makeIdentityAssigner(): (name: string) => { color: string; sigil: string } {\n const map = new Map<string, { color: string; sigil: string }>();\n let colorIdx = 0;\n return (name: string) => {\n if (!map.has(name)) {\n const h = seedHash(name);\n map.set(name, {\n color: AGENT_COLORS[colorIdx++ % AGENT_COLORS.length],\n sigil: SIGILS[h % SIGILS.length],\n });\n }\n return map.get(name)!;\n };\n}\n\n// ── Event line ────────────────────────────────────────────────────────────────\n\nconst NAME_COL = 12;\n\nfunction EventLine({\n event,\n identify,\n}: {\n event: DisplayEvent;\n identify: (n: string) => { color: string; sigil: string };\n}) {\n const ts = <Text color={C.muted}>{event.ts}{\" \"}</Text>;\n\n // ── Message ──\n if (event.kind === \"message\") {\n const { color, sigil } = identify(event.senderName);\n const isSelf = event.isSelf;\n const nameColor = isSelf ? C.text : event.senderType === \"agent\" ? color : C.secondary;\n const sigilColor = isSelf ? C.dim : event.senderType === \"agent\" ? color : C.dim;\n const sigilChar = isSelf ? \"›\" : event.senderType === \"agent\" ? sigil : \"·\";\n const contentColor = isSelf ? C.text : C.secondary;\n\n return (\n <Box paddingX={1}>\n <Box flexShrink={0}>\n {ts}\n <Text color={sigilColor}>{sigilChar}{\" \"}</Text>\n <Text color={nameColor} bold={isSelf}>\n {event.senderName.slice(0, NAME_COL).padEnd(NAME_COL)}\n </Text>\n <Text>{\" \"}</Text>\n </Box>\n <Box flexGrow={1} flexShrink={1}>\n <Text wrap=\"wrap\">\n {event.replyToName && <Text color={C.dim}>{\"→ \"}{event.replyToName}{\" \"}</Text>}\n <Text color={contentColor}>{event.content}</Text>\n </Text>\n </Box>\n </Box>\n );\n }\n\n // ── Join ──\n if (event.kind === \"join\") {\n const isAgent = event.participantType === \"agent\";\n const { color, sigil } = isAgent ? identify(event.name) : { color: C.dim, sigil: \"·\" };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={isAgent ? color : C.dim}>{sigil}{\" \"}</Text>\n <Text color={isAgent ? color : C.dim}>{event.name}</Text>\n <Text color={C.green}>{\" joined\"}</Text>\n </Box>\n );\n }\n\n // ── Leave ──\n if (event.kind === \"leave\") {\n const isAgent = event.participantType === \"agent\";\n const { color: nameColor } = isAgent ? identify(event.name) : { color: C.muted };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.muted}>{\"· \"}</Text>\n <Text color={nameColor}>{event.name}</Text>\n <Text color={C.danger}>{\" left\"}</Text>\n </Box>\n );\n }\n\n // ── Mode change ──\n if (event.kind === \"mode\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\"mode → \"}</Text>\n <Text color={C.yellow} bold>{event.mode}</Text>\n </Box>\n );\n }\n\n // ── System message (slash command output) ──\n if (event.kind === \"system\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\" \"}</Text>\n <Text color={C.secondary}>{event.content}</Text>\n </Box>\n );\n }\n\n return null;\n}\n\n// ── Internal bridge ───────────────────────────────────────────────────────────\n\ninterface AppHandle {\n push: (event: DisplayEvent) => void;\n setAgentNames: (names: string[]) => void;\n setParticipants: (names: string[]) => void;\n}\n\n// ── App ───────────────────────────────────────────────────────────────────────\n\ntype StaticEntry = { id: string; event?: DisplayEvent };\n\nfunction App({\n roomName,\n onSend,\n onCtrlC,\n onReady,\n readOnly,\n isAdmin,\n version,\n savePath,\n}: {\n roomName: string;\n onSend?: (content: string) => void;\n onCtrlC?: () => void;\n onReady: (handle: AppHandle) => void;\n readOnly?: boolean;\n isAdmin?: boolean;\n version?: string;\n savePath?: string;\n}) {\n const [events, setEvents] = useState<DisplayEvent[]>([]);\n const [agentNames, setAgentNames] = useState<string[]>([]);\n const [participants, setParticipants] = useState<string[]>([]);\n const [input, setInput] = useState(\"\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n const { stdout } = useStdout();\n const identify = useMemo(makeIdentityAssigner, []);\n\n const push = useCallback((event: DisplayEvent) => {\n setEvents((prev) => [...prev, event]);\n }, []);\n\n useEffect(() => {\n onReady({ push, setAgentNames, setParticipants });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Note: no resize handler — Ink's <Static> items are already committed to the\n // terminal buffer. Forcing a re-render on resize causes cursor position\n // miscalculation and screen corruption. The divider width updates naturally\n // on the next state change (new event, input change, etc.).\n\n // ── Slash command suggestions ──────────────────────────────────────────────\n\n type SuggestionItem =\n | { kind: \"command\"; cmd: SlashCommand; insert: string }\n | { kind: \"param\"; value: string; insert: string }\n | { kind: \"mention\"; value: string; insert: string };\n\n const suggestionState = useMemo((): { items: SuggestionItem[]; ghostHint: string } => {\n // @mention detection — match @partial at end of input\n const mentionMatch = input.match(/@([a-zA-Z0-9_-]*)$/);\n if (mentionMatch && participants.length > 0) {\n const prefix = mentionMatch[1].toLowerCase();\n const filtered = participants.filter((p) => p.toLowerCase().startsWith(prefix));\n if (filtered.length > 0) {\n const before = input.slice(0, mentionMatch.index!);\n const items: SuggestionItem[] = filtered.map((p) => ({\n kind: \"mention\" as const,\n value: p,\n insert: before + \"@\" + p + \" \",\n }));\n const ghostHint = prefix.length === 0 ? \"\" : (filtered[0].slice(prefix.length));\n return { items, ghostHint };\n }\n }\n\n if (!input.startsWith(\"/\")) return { items: [], ghostHint: \"\" };\n\n const spaceIdx = input.indexOf(\" \");\n\n // Phase 1: completing command name (no space yet)\n if (spaceIdx === -1) {\n const prefix = input.toLowerCase();\n const items: SuggestionItem[] = SLASH_COMMANDS\n .filter((cmd) => {\n if (cmd.adminOnly && !isAdmin) return false;\n return cmd.name.startsWith(prefix);\n })\n .map((cmd) => ({\n kind: \"command\" as const,\n cmd,\n insert: cmd.name + \" \",\n }));\n return { items, ghostHint: \"\" };\n }\n\n // Phase 2: completing params\n const cmdName = input.slice(0, spaceIdx).toLowerCase();\n const cmd = SLASH_COMMANDS.find((c) => c.name === cmdName && (!c.adminOnly || isAdmin));\n if (!cmd?.params) return { items: [], ghostHint: \"\" };\n\n const rest = input.slice(spaceIdx + 1);\n const words = rest.split(/\\s+/);\n const hasTrailingSpace = rest.endsWith(\" \") || rest === \"\";\n const completedCount = hasTrailingSpace\n ? words.filter(Boolean).length\n : Math.max(0, words.length - 1);\n const currentPrefix = hasTrailingSpace ? \"\" : (words[words.length - 1] ?? \"\").toLowerCase();\n const paramIdx = completedCount;\n\n // All params filled\n if (paramIdx >= cmd.params.length) return { items: [], ghostHint: \"\" };\n\n const param = cmd.params[paramIdx];\n\n // Ghost hint: remaining unfilled params (skip current if partially typed)\n const ghostStart = currentPrefix ? paramIdx + 1 : paramIdx;\n const ghostHint = cmd.params.slice(ghostStart).map((p) => `<${p.label}>`).join(\" \");\n\n // No completions defined — hint only\n if (!param.completions) return { items: [], ghostHint };\n\n const values = param.completions === \"participants\" ? participants : param.completions;\n const filtered = currentPrefix\n ? values.filter((v) => v.toLowerCase().startsWith(currentPrefix))\n : values;\n\n // Build insert string: full command up to current param + selected value\n const completedWords = words.slice(0, completedCount).filter(Boolean);\n const base = cmdName + (completedWords.length ? \" \" + completedWords.join(\" \") : \"\") + \" \";\n\n const items: SuggestionItem[] = filtered.map((v) => ({\n kind: \"param\" as const,\n value: v,\n insert: base + v + \" \",\n }));\n\n return { items, ghostHint };\n }, [input, isAdmin, participants]);\n\n const suggestions = suggestionState.items;\n\n // Reset selection when input changes (arrow keys don't change input)\n useEffect(() => { setSelectedIndex(0); }, [input]);\n\n // ── Keyboard ───────────────────────────────────────────────────────────────\n // Single useInput handles everything — no TextInput, no dual-handler conflicts.\n\n useInput((char, key) => {\n if (key.ctrl && char === \"c\") { onCtrlC?.(); return; }\n if (readOnly || !onSend) return;\n\n // Suggestion navigation\n if (suggestions.length > 0) {\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(i + 1, suggestions.length - 1));\n return;\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(i - 1, 0));\n return;\n }\n if (key.return || key.tab) {\n const picked = suggestions[selectedIndex];\n if (!picked) return;\n // No-param command + Enter → submit directly\n if (key.return && picked.kind === \"command\" && !picked.cmd.params) {\n onSend(picked.cmd.name);\n setInput(\"\");\n return;\n }\n setInput(picked.insert);\n return;\n }\n if (key.escape) {\n setInput(\"\");\n return;\n }\n }\n\n // Option+Enter → newline\n if (key.return && key.meta) {\n setInput((prev) => prev + \"\\n\");\n return;\n }\n\n // Enter → submit\n if (key.return) {\n const content = input.trim();\n if (content) onSend(content);\n setInput(\"\");\n return;\n }\n\n // Backspace\n if (key.backspace || key.delete) {\n setInput((prev) => prev.slice(0, -1));\n return;\n }\n\n // Ignore special keys\n if (key.ctrl || key.meta || key.escape || key.tab ||\n key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {\n return;\n }\n\n // Regular character\n if (char) {\n setInput((prev) => prev + char);\n }\n });\n\n const cols = stdout.columns ?? 80;\n\n // Static items: banner (rendered once) + events (appended over time)\n const entries: StaticEntry[] = useMemo(\n () => [{ id: \"__banner__\" }, ...events.map((e) => ({ id: e.id, event: e }))],\n [events],\n );\n\n return (\n <>\n {/* Permanent output — rendered once, selectable terminal text */}\n <Static items={entries}>\n {(entry) => {\n if (!entry.event) {\n return (\n <Box key={entry.id} flexDirection=\"column\" paddingX={2} paddingTop={1} paddingBottom={1}>\n {BANNER_LINES.map((line, i) => (\n <Text key={i} color={GRADIENT[i]}>{line}</Text>\n ))}\n {version && <Text color={C.dim}>{\" v\"}{version}</Text>}\n <Text>{\" \"}</Text>\n <Text>\n <Text color={C.dim}>{\" room \"}</Text>\n <Text color={C.cyan} bold>{roomName}</Text>\n </Text>\n {savePath && (\n <Text>\n <Text color={C.dim}>{\" saved \"}</Text>\n <Text color={C.secondary}>{savePath}</Text>\n </Text>\n )}\n </Box>\n );\n }\n return <EventLine key={entry.id} event={entry.event} identify={identify} />;\n }}\n </Static>\n\n {/* Dynamic footer — only this area repaints */}\n <Box paddingX={1}>\n <Text color={C.purple}>{\"─\"}</Text>\n <Text color={C.border}>{\"─\".repeat(Math.max(0, cols - 4))}</Text>\n <Text color={C.cyan}>{\"─\"}</Text>\n </Box>\n {agentNames.length > 0 && (\n <Box paddingX={1}>\n {agentNames.map((name, i) => {\n const { color, sigil } = identify(name);\n return (\n <React.Fragment key={name}>\n {i > 0 && <Text color={C.border}>{\" · \"}</Text>}\n <Text color={color}>{sigil}{\" \"}{name}</Text>\n </React.Fragment>\n );\n })}\n </Box>\n )}\n {readOnly || !onSend ? (\n <Box paddingX={1}>\n <Text color={C.muted}>{\" watching as guest\"}</Text>\n </Box>\n ) : (\n <Box paddingX={1} flexDirection=\"column\">\n {/* Render each line; first line gets the prompt, rest get indentation */}\n {(input || \"\").split(\"\\n\").map((line, i, arr) => (\n <Box key={i}>\n <Text color={C.cyan} bold>{i === 0 ? \"› \" : \" \"}</Text>\n <Text>\n {line}\n {i === arr.length - 1 && <Text inverse>{\" \"}</Text>}\n {i === arr.length - 1 && suggestionState.ghostHint !== \"\" && (\n <Text color={C.muted}>{suggestionState.ghostHint}</Text>\n )}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n {/* Slash command suggestions — below input */}\n {suggestions.length > 0 && (\n <Box flexDirection=\"column\" paddingX={1}>\n {suggestions.map((s, i) => {\n const selected = i === selectedIndex;\n if (s.kind === \"command\") {\n const paramHint = s.cmd.params\n ? \" \" + s.cmd.params.map((p) => `<${p.label}>`).join(\" \")\n : \"\";\n const display = s.cmd.name + paramHint;\n return (\n <Box key={s.cmd.name}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n <Text>\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>\n {s.cmd.name}\n </Text>\n <Text color={C.muted}>\n {paramHint.padEnd(CMD_DISPLAY_COL - display.length + paramHint.length)}\n </Text>\n </Text>\n <Text color={C.dim}>{s.cmd.description}</Text>\n </Box>\n );\n }\n return (\n <Box key={s.value}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n {s.kind === \"mention\" && <Text color={C.dim}>{\"@\"}</Text>}\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>{s.value}</Text>\n </Box>\n );\n })}\n </Box>\n )}\n </>\n );\n}\n\n// ── startTUI ──────────────────────────────────────────────────────────────────\n\nexport function startTUI(opts: TUIOptions): TUIHandle {\n let handle: AppHandle | null = null;\n const queue: DisplayEvent[] = [];\n\n const onReady = (h: AppHandle) => {\n handle = h;\n for (const event of queue.splice(0)) h.push(event);\n };\n\n const { unmount } = render(\n <App\n roomName={opts.roomName}\n onSend={opts.onSend}\n onCtrlC={opts.onCtrlC}\n onReady={onReady}\n readOnly={opts.readOnly}\n isAdmin={opts.isAdmin}\n version={opts.version}\n savePath={opts.savePath}\n />,\n { exitOnCtrlC: false },\n );\n\n return {\n push(event) {\n if (handle) handle.push(event);\n else queue.push(event);\n },\n setAgentNames(names) {\n handle?.setAgentNames(names);\n },\n setParticipants(names) {\n handle?.setParticipants(names);\n },\n stop() {\n unmount();\n },\n };\n}\n","/**\n * stoops run claude — client-side agent runtime for Claude Code.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Claude-specific pieces: tmux session + TmuxBridge delivery.\n *\n * Claude Code connects to the runtime MCP server via a stdio bridge\n * (Claude's HTTP MCP transport requires OAuth, which hangs on localhost).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { writeFileSync, mkdtempSync, rmSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n} from \"../tmux.js\";\nimport { TmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\n\nexport { type AgentRuntimeOptions as RunClaudeOptions };\n\n/**\n * Stdio-to-HTTP bridge script. Written to a temp file and spawned by Claude Code\n * as an MCP stdio server. Proxies JSON-RPC messages to the runtime HTTP MCP server.\n *\n * The HTTP MCP server returns SSE-formatted responses (event: message\\ndata: {...}).\n * The bridge extracts the JSON from data: lines and writes raw JSON-RPC to stdout.\n */\n// CommonJS (.cjs) for minimal startup latency — ESM requires module parsing which\n// can exceed Claude Code's MCP handshake timeout on first run.\nconst MCP_STDIO_BRIDGE = [\n '#!/usr/bin/env node',\n '\"use strict\";',\n 'const { createInterface } = require(\"readline\");',\n 'const url = `http://127.0.0.1:${process.argv[2]}/mcp`;',\n 'const rl = createInterface({ input: process.stdin });',\n '(async () => {',\n ' for await (const line of rl) {',\n ' if (!line.trim()) continue;',\n ' try {',\n ' const res = await fetch(url, {',\n ' method: \"POST\",',\n ' headers: { \"Content-Type\": \"application/json\", Accept: \"application/json, text/event-stream\" },',\n ' body: line,',\n ' });',\n ' if (res.status === 202) continue;',\n ' const body = await res.text();',\n ' for (const bl of body.split(\"\\\\n\")) {',\n ' const m = bl.match(/^data: (.+)/);',\n ' if (m) process.stdout.write(m[1] + \"\\\\n\");',\n ' }',\n ' } catch {',\n ' process.exit(1);',\n ' }',\n ' }',\n '})();',\n].join('\\n');\n\nexport async function runClaude(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Claude Code agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP stdio bridge + config ────────────────────────────────────\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"stoops_agent_\"));\n\n const bridgePath = join(tmpDir, \"mcp-bridge.cjs\");\n writeFileSync(bridgePath, MCP_STDIO_BRIDGE);\n chmodSync(bridgePath, 0o755);\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpConfigPath = join(tmpDir, \"mcp.json\");\n\n // Use process.execPath (absolute path to Node) so the bridge works regardless\n // of whether `node` is in the tmux session's PATH.\n const mcpConfig = {\n mcpServers: {\n stoops: {\n type: \"stdio\",\n command: process.execPath,\n args: [bridgePath, mcpPort],\n },\n },\n };\n writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));\n\n // ── Create tmux session + launch Claude Code ────────────────────────────\n\n const tmuxSession = `stoops_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n console.log(\"Launching Claude Code...\");\n tmuxCreateSession(tmuxSession);\n\n // Launch claude with MCP config + any passthrough args\n const extraArgs = options.extraArgs ?? [];\n const claudeCmd = [`claude --mcp-config ${mcpConfigPath}`, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, claudeCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new TmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Wait for Claude to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Claude Code exited during startup. Try running again.\");\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n return;\n }\n }\n\n console.log(\"Attaching to Claude Code session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n\n console.log(\"Disconnected.\");\n}\n","/**\n * tmux helpers for stoops CLI.\n *\n * Thin wrappers around tmux commands. Used by the server process to\n * inject room events into Claude Code sessions.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\n\n/** Sanitize a string for use as a tmux session name. Replaces tmux-special chars. */\nfunction sanitizeSessionName(name: string): string {\n return name.replace(/[.:$%]/g, \"_\");\n}\n\n/** Check if tmux is installed and available. */\nexport function tmuxAvailable(): boolean {\n try {\n execFileSync(\"tmux\", [\"-V\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Check if a tmux session exists. */\nexport function tmuxSessionExists(session: string): boolean {\n try {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"has-session\", \"-t\", name], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Create a detached tmux session with no status bar. */\nexport function tmuxCreateSession(session: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"new-session\", \"-d\", \"-s\", name]);\n execFileSync(\"tmux\", [\"set\", \"-t\", name, \"status\", \"off\"]);\n}\n\n/** Send a command to a tmux session (types it + presses Enter). */\nexport function tmuxSendCommand(session: string, command: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"-l\", command]);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"Enter\"]);\n}\n\n/**\n * Inject text into a tmux session (literal keys, no Enter).\n * Used for room event injection.\n */\nexport function tmuxInjectText(session: string, text: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"-l\", text]);\n}\n\n/** Send Enter key to a tmux session (submits input). */\nexport function tmuxSendEnter(session: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"Enter\"]);\n}\n\n/** Attach to a tmux session. Returns a promise that resolves when detached/exited.\n *\n * Two modes:\n * - Outside tmux: `tmux attach` (blocks until user detaches, event loop stays free via spawn)\n * - Inside tmux: `tmux switch-client` (exits immediately) + polls until session ends\n */\nexport function tmuxAttach(session: string): Promise<void> {\n const name = sanitizeSessionName(session);\n\n if (process.env.TMUX) {\n // switch-client exits immediately after switching — poll until session is destroyed\n try {\n execFileSync(\"tmux\", [\"switch-client\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n // switch-client failed (e.g. no client) — fall through to polling\n }\n return new Promise<void>((resolve) => {\n const poll = setInterval(() => {\n try {\n execFileSync(\"tmux\", [\"has-session\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n clearInterval(poll);\n resolve();\n }\n }, 500);\n });\n }\n\n return new Promise<void>((resolve) => {\n const child = spawn(\"tmux\", [\"attach\", \"-t\", name], { stdio: \"inherit\" });\n child.on(\"exit\", () => resolve());\n child.on(\"error\", () => resolve());\n });\n}\n\n/** Capture visible screen content as array of lines. */\nexport function tmuxCapturePane(session: string): string[] {\n try {\n const name = sanitizeSessionName(session);\n const output = execFileSync(\"tmux\", [\"capture-pane\", \"-t\", name, \"-p\"], {\n encoding: \"utf-8\",\n });\n return output.split(\"\\n\");\n } catch {\n return [];\n }\n}\n\n/**\n * Send a control key sequence (e.g. \"C-u\", \"C-y\", \"Escape\").\n * Unlike tmuxInjectText, this does NOT use -l, so tmux interprets\n * the key name rather than treating it as literal text.\n */\nexport function tmuxSendKey(session: string, key: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, key]);\n}\n\n/** Kill a tmux session. */\nexport function tmuxKillSession(session: string): void {\n try {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"kill-session\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n // Session may already be dead\n }\n}\n","/**\n * TmuxBridge — state-aware event injection into Claude Code.\n *\n * Reads the Claude Code TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → inject directly\n * typing → Ctrl+U (cut), inject, Ctrl+Y (restore)\n * dialog → queue and poll\n * permission → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Events that can't be injected immediately are queued and drained\n * when the state becomes safe.\n */\n\nimport {\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type TuiState =\n | \"idle\"\n | \"typing\"\n | \"dialog\"\n | \"permission\"\n | \"streaming\"\n | \"unknown\";\n\n// Spinner characters used by Claude Code during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate a selection/question dialog\nconst DIALOG_PATTERNS = [\n \"Enter to select\",\n \"to navigate\",\n \"Esc to cancel\",\n \"Ready to code?\",\n \"Review your answers\",\n \"ctrl+g to edit in\",\n];\n\n// Patterns that indicate a permission/confirmation prompt\nconst PERMISSION_PATTERNS = [\n \"(Y)\",\n \"Allow \",\n \"Deny \",\n \"approve\",\n \"Yes / No\",\n];\n\nexport interface TmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** How long to wait between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n}\n\nexport class TmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n\n constructor(session: string, opts?: TmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n }\n\n /**\n * Delivery callback — drop-in replacement for the raw tmuxDeliver lambda.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[]): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n * Exported for testing — the heuristic logic is in detectStateFromLines().\n */\n detectState(): TuiState {\n const lines = this.captureScreen();\n return detectStateFromLines(lines);\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * If the state is unsafe, queues the text and starts polling.\n *\n * Text is flattened to a single line before injection to avoid triggering\n * Claude Code's paste detection. When multi-line text arrives via\n * `send-keys -l`, Claude Code detects it as a paste and collapses it into\n * \"[Pasted text #1 +N lines]\" which may not reliably submit with Enter.\n */\n private inject(text: string): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // dialog, permission, streaming, unknown — queue it\n this.enqueue(flat);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt: type text + Enter.\n * Sends a second Enter after a short delay as a safety net — if Claude Code's\n * paste detection swallowed the first Enter, the second one submits. If the\n * first Enter worked, Claude is streaming and the second Enter is a no-op.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.sleep(80);\n tmuxSendEnter(this.session);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut line to kill ring\n * 2. Inject our text + Enter\n * 3. Ctrl+Y — paste the user's text back\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event (double-Enter for paste detection resilience)\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.sleep(80);\n tmuxSendEnter(this.session);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string): void {\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n *\n * Drains one event at a time rather than batching all into one multi-line\n * string — multi-line text triggers Claude Code's paste detection which\n * collapses it into \"[Pasted text #1 +N lines]\".\n *\n * After injecting one event, the poll continues. The next cycle re-checks\n * state: if Claude is busy (streaming), remaining events wait. If idle,\n * the next event is injected. Events are already flattened in inject().\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n // else: keep polling to drain remaining events\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Claude Code's TUI layout (v2.1+):\n * ────────────────────────\n * ❯ <user input here>\n * ────────────────────────\n * PR #2 /ide ...\n *\n * The ❯ prompt sits between separator lines (─). No ❯❯ footer in v2.1+.\n */\nexport function detectStateFromLines(lines: string[]): TuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~15 lines (the visible bottom of the screen)\n const tail = lines.slice(-15);\n const tailText = tail.join(\"\\n\");\n\n // 1. Dialog: selection/question/plan approval\n for (const pattern of DIALOG_PATTERNS) {\n if (tailText.includes(pattern)) return \"dialog\";\n }\n\n // 2. Permission prompt\n for (const pattern of PERMISSION_PATTERNS) {\n if (tailText.includes(pattern)) return \"permission\";\n }\n\n // 3. Streaming: spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 4. Look for the ❯/› prompt line near the bottom.\n // Supports both old layout (❯❯ footer) and new layout (separator lines).\n const promptChar = /^[❯›](\\s|$)/;\n const footerChar = /^[❯›]{2}\\s/;\n const separatorLine = /^[─━─\\-]{10,}/;\n\n // Strategy A: old layout — find ❯❯ footer then ❯ prompt above it\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (footerChar.test(line)) {\n // Found old-style footer, look for prompt above\n for (let j = i - 1; j >= 0; j--) {\n const above = tail[j].trimStart();\n if (promptChar.test(above)) {\n const content = above.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n break;\n }\n }\n\n // Strategy B: new layout — find ❯ prompt between/near separator lines\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (promptChar.test(line)) {\n // Verify it's Claude's prompt by checking for separator line nearby\n const above = i > 0 ? tail[i - 1].trimStart() : \"\";\n const below = i < tail.length - 1 ? tail[i + 1].trimStart() : \"\";\n if (separatorLine.test(above) || separatorLine.test(below)) {\n const content = line.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n }\n\n return \"unknown\";\n}\n","/**\n * Shared agent runtime setup — extracted from cli/claude/run.ts.\n *\n * Both `stoops run claude` and `stoops run opencode` use this to:\n * 1. Create SSE multiplexer + EventProcessor\n * 2. Create local runtime MCP server\n * 3. Wire up participant cache updates\n * 4. Provide cleanup\n *\n * Rooms are NOT joined during setup. The agent joins rooms by calling\n * join_room() via MCP. If --join URLs are provided, the startup event\n * asks the agent to call join_room for each URL.\n *\n * Each runtime only needs to provide its own delivery mechanism\n * (TmuxBridge for Claude, HTTP API for OpenCode).\n */\n\nimport { randomName } from \"../core/names.js\";\nimport { extractToken } from \"./auth.js\";\nimport { RemoteRoomDataSource } from \"../agent/remote-room-data-source.js\";\nimport { SseMultiplexer } from \"../agent/sse-multiplexer.js\";\nimport { EventProcessor } from \"../agent/event-processor.js\";\nimport { createRuntimeMcpServer, type RuntimeMcpServer, type JoinRoomResult } from \"../agent/mcp/runtime.js\";\nimport { buildCatchUpLines } from \"../agent/tool-handlers.js\";\nimport type { Participant } from \"../core/types.js\";\nimport type { LabeledEvent } from \"../agent/multiplexer.js\";\nimport type { ContentPart } from \"../agent/types.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface AgentRuntimeOptions {\n joinUrls?: string[];\n name?: string;\n admin?: boolean;\n extraArgs?: string[];\n /** Skip tmux/UI — deliver events as plain text to stdout. MCP server still runs. */\n headless?: boolean;\n /** Called after a room is successfully joined via join_room MCP tool. */\n onRoomJoined?: () => void | Promise<void>;\n}\n\nexport interface JoinResult {\n serverUrl: string;\n sessionToken: string;\n participantId: string;\n roomName: string;\n roomId: string;\n authority: string;\n participants: Participant[];\n dataSource: RemoteRoomDataSource;\n}\n\nexport interface AgentRuntimeSetup {\n agentName: string;\n joinResults: JoinResult[];\n initialParts: ContentPart[] | undefined;\n processor: EventProcessor;\n sseMux: SseMultiplexer;\n mcpServer: RuntimeMcpServer;\n wrappedSource: AsyncIterable<LabeledEvent>;\n cleanup(): Promise<void>;\n}\n\n// ── Setup ─────────────────────────────────────────────────────────────────────\n\nexport async function setupAgentRuntime(options: AgentRuntimeOptions): Promise<AgentRuntimeSetup> {\n const agentName = options.name ?? randomName();\n\n // ── Pending join URLs (not joined yet — agent calls join_room) ─────────\n\n const pendingUrls = options.joinUrls ?? [];\n\n // ── Mutable join results (populated as agent calls join_room) ──────────\n\n const joinResults: JoinResult[] = [];\n\n // ── Create SSE multiplexer (starts empty) ──────────────────────────────\n\n const sseMux = new SseMultiplexer();\n\n // ── Create EventProcessor (selfId set on first join_room) ──────────────\n\n const processor = new EventProcessor(\"\", agentName, {\n defaultMode: \"everyone\",\n });\n\n // ── Create local runtime MCP server ───────────────────────────────────\n\n const mcpServer = await createRuntimeMcpServer({\n resolver: processor,\n toolOptions: {\n isEventSeen: (id) => processor.isEventSeen(id),\n markEventsSeen: (ids) => processor.markEventsSeen(ids),\n assignRef: (id) => processor.assignRef(id),\n resolveRef: (ref) => processor.resolveRef(ref),\n },\n admin: options.admin,\n onSetMode: async (room, mode) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n processor.setModeForRoom(conn.dataSource.roomId, mode as any, false);\n try {\n const ds = conn.dataSource as RemoteRoomDataSource;\n const res = await fetch(`${ds.serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, mode }),\n });\n if (!res.ok) return { success: false, error: `Server rejected: ${await res.text()}` };\n } catch {\n // Server unreachable, local mode still set\n }\n return { success: true };\n },\n onJoinRoom: async (url, alias) => {\n const token = extractToken(url);\n let serverUrl: string;\n try {\n const parsed = new URL(url);\n parsed.search = \"\";\n serverUrl = parsed.toString().replace(/\\/$/, \"\");\n } catch {\n serverUrl = url.replace(/\\/$/, \"\");\n }\n\n try {\n const joinBody: Record<string, unknown> = { type: \"agent\", name: agentName };\n if (token) joinBody.token = token;\n\n const res = await fetch(`${serverUrl}/join`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(joinBody),\n signal: AbortSignal.timeout(15_000),\n });\n if (!res.ok) return { success: false, error: `Failed to join: ${await res.text()}` };\n\n const data = await res.json() as Record<string, unknown>;\n const sessionToken = String(data.sessionToken ?? \"\");\n const roomName = alias ?? String(data.roomName ?? \"\");\n const roomId = String(data.roomId ?? \"\");\n const authority = String(data.authority ?? \"member\");\n const participants = (data.participants as Participant[]) ?? [];\n const newParticipantId = String(data.participantId ?? \"\");\n\n const dataSource = new RemoteRoomDataSource(serverUrl, sessionToken, roomId);\n dataSource.setParticipants(participants);\n dataSource.setSelf(newParticipantId, agentName);\n\n // Set global selfId on first join; always set per-room selfId\n if (joinResults.length === 0) {\n processor.participantId = newParticipantId;\n }\n processor.setRoomParticipantId(roomId, newParticipantId);\n\n // Register in EventProcessor and SSE multiplexer\n const mode = processor.getModeForRoom(roomId) ?? \"everyone\";\n processor.connectRemoteRoom(dataSource, roomName);\n sseMux.addConnection(serverUrl, sessionToken, roomName, roomId);\n\n // Track for cleanup\n const jr: JoinResult = {\n serverUrl,\n sessionToken,\n participantId: newParticipantId,\n roomName,\n roomId,\n authority,\n participants,\n dataSource,\n };\n joinResults.push(jr);\n\n // Build recent activity lines for the response\n const conn = processor.resolve(roomName);\n let recentLines: string[] = [];\n if (conn) {\n recentLines = await buildCatchUpLines(conn, {\n isEventSeen: (id) => processor.isEventSeen(id),\n markEventsSeen: (ids) => processor.markEventsSeen(ids),\n assignRef: (id) => processor.assignRef(id),\n });\n }\n\n await options.onRoomJoined?.();\n\n return {\n success: true,\n roomName,\n agentName,\n authority,\n mode,\n participants: participants\n .filter((p) => p.id !== newParticipantId)\n .map((p) => ({ name: p.name, authority: (p as any).authority ?? \"member\" })),\n recentLines,\n } as JoinRoomResult;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Unable to connect. Is the server running? (${serverUrl}) — ${msg}` };\n }\n },\n onLeaveRoom: async (room) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const roomId = conn.dataSource.roomId;\n\n const idx = joinResults.findIndex((jr) => jr.roomId === roomId);\n if (idx >= 0) {\n const jr = joinResults[idx];\n sseMux.removeConnection(roomId);\n processor.disconnectRemoteRoom(roomId);\n\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n\n joinResults.splice(idx, 1);\n }\n return { success: true };\n },\n onAdminSetModeFor: options.admin ? async (room, participant, mode) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, mode }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminMute: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, authority: \"guest\" }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminUnmute: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, authority: \"member\" }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminKick: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/kick`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n });\n\n // ── Wrap SSE source for participant cache updates ─────────────────────\n\n const wrappedSource: AsyncIterable<LabeledEvent> = {\n [Symbol.asyncIterator]() {\n const inner = sseMux[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await inner.next();\n if (!result.done) {\n const { roomId, event } = result.value;\n const jr = joinResults.find((j) => j.roomId === roomId);\n if (jr) {\n if (event.type === \"ParticipantJoined\") {\n jr.dataSource.addParticipant(event.participant);\n } else if (event.type === \"ParticipantLeft\") {\n jr.dataSource.removeParticipant(event.participant_id);\n }\n }\n }\n return result;\n },\n };\n },\n };\n\n // ── Cleanup function ──────────────────────────────────────────────────\n\n async function cleanup(): Promise<void> {\n await processor.stop();\n sseMux.close();\n await mcpServer.stop();\n\n for (const jr of joinResults) {\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n }\n }\n\n // ── Build startup event (if --join URLs were provided) ────────────────\n\n let initialParts: ContentPart[] | undefined;\n if (pendingUrls.length > 0) {\n if (pendingUrls.length === 1) {\n initialParts = [{ type: \"text\", text: `Use join_room(\"${pendingUrls[0]}\") to connect.` }];\n } else {\n const lines = pendingUrls.map((u) => ` join_room(\"${u}\")`);\n initialParts = [{ type: \"text\", text: `Rooms to join:\\n${lines.join(\"\\n\")}` }];\n }\n }\n\n return {\n agentName,\n joinResults,\n initialParts,\n processor,\n sseMux,\n mcpServer,\n wrappedSource,\n cleanup,\n };\n}\n","/**\n * stoops run opencode — client-side agent runtime for OpenCode.\n *\n * Spawns `opencode serve` with OPENCODE_CONFIG_CONTENT to inject the stoops\n * MCP server. The user opens OpenCode's UI, starts a conversation, and tells\n * the agent to join a room URL.\n *\n * Session detection: we subscribe to OpenCode's global SSE event stream and\n * watch for stoops tool call events. Each event carries the sessionID of the\n * calling session — no guessing, no race conditions.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\n\nexport { type AgentRuntimeOptions as RunOpencodeOptions };\n\nexport async function runOpencode(options: AgentRuntimeOptions): Promise<void> {\n // ── Pick a port for OpenCode ────────────────────────────────────────────\n\n const opencodePort = 14096 + Math.floor(Math.random() * 1000);\n const opencodeUrl = `http://127.0.0.1:${opencodePort}`;\n\n // ── Room → OpenCode session mapping ────────────────────────────────────\n //\n // Each OpenCode session can join different rooms. Lazily detected on first\n // delivery for each room by inspecting session messages for stoops tool calls.\n\n const roomSessions = new Map<string, string>(); // roomId → OpenCode sessionId\n\n /** Find the OpenCode session that most recently called a stoops tool. */\n async function findStoopsSession(): Promise<string | null> {\n try {\n const res = await fetch(`${opencodeUrl}/session`);\n if (!res.ok) return null;\n // Sessions sorted by time.updated desc — first is most recent\n const sessions = await res.json() as Array<{ id: string; time: { updated: number } }>;\n\n // Check the most recently updated sessions for stoops tool parts\n for (const sess of sessions.slice(0, 3)) {\n const msgRes = await fetch(`${opencodeUrl}/session/${sess.id}/message`);\n if (!msgRes.ok) continue;\n const messages = await msgRes.json() as Array<{\n parts?: Array<{ type?: string; tool?: string }>;\n }>;\n for (const msg of messages) {\n for (const part of msg.parts ?? []) {\n if (part.type === \"tool\" && part.tool?.includes(\"stoops__\")) {\n return sess.id;\n }\n }\n }\n }\n // Fallback: most recently updated session\n return sessions.length > 0 ? sessions[0].id : null;\n } catch {\n return null;\n }\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // No --join URLs — the user tells the agent to join from within OpenCode.\n\n const setup = await setupAgentRuntime({\n ...options,\n joinUrls: undefined,\n });\n\n // ── Build MCP config for OpenCode ───────────────────────────────────────\n\n const opencodeConfig = {\n mcp: {\n stoops: {\n type: \"remote\",\n url: setup.mcpServer.url,\n oauth: false,\n },\n },\n };\n\n // ── Spawn OpenCode in headless mode ─────────────────────────────────────\n\n const extraArgs = options.extraArgs ?? [];\n const opencodeArgs = [\"serve\", \"--port\", String(opencodePort), ...extraArgs];\n\n console.log(\"Launching OpenCode...\");\n\n let child: ChildProcess;\n try {\n child = spawn(\"opencode\", opencodeArgs, {\n env: {\n ...process.env,\n OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeConfig),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch {\n console.error(\"Error: opencode is required but not found. Install it from https://opencode.ai\");\n await setup.cleanup();\n process.exit(1);\n }\n\n // Forward stderr to console for visibility\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n process.stderr.write(chunk);\n });\n\n let childExited = false;\n child.on(\"exit\", () => { childExited = true; });\n\n child.on(\"error\", async () => {\n console.error(\"Error: failed to start opencode. Is it installed?\");\n await setup.cleanup();\n process.exit(1);\n });\n\n // ── Wait for OpenCode to be ready ───────────────────────────────────────\n\n const ready = await pollForReady(opencodeUrl, 30_000);\n if (!ready) {\n console.error(\"OpenCode did not become ready within 30 seconds.\");\n child.kill();\n await setup.cleanup();\n process.exit(1);\n }\n\n console.log(` OpenCode running on ${opencodeUrl}`);\n\n // ── Build deliver callback ──────────────────────────────────────────────\n //\n // OpenCode's POST /session/:id/message uses Hono stream() — headers (200)\n // arrive immediately but the LLM runs inside the stream callback. We MUST\n // consume the response body (await res.text()) to block until the LLM\n // finishes, preserving the EventProcessor's _processing lock.\n\n async function deliver(parts: ContentPart[]): Promise<void> {\n const roomId = setup.processor.currentContextRoomId;\n if (!roomId) return;\n\n // Lazy session detection: look up on first delivery for this room\n if (!roomSessions.has(roomId)) {\n const sid = await findStoopsSession();\n if (!sid) return;\n roomSessions.set(roomId, sid);\n console.log(` Linked room ${roomId} → session ${sid}`);\n }\n const targetSession = roomSessions.get(roomId)!;\n\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n try {\n const res = await fetch(`${opencodeUrl}/session/${targetSession}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n parts: [{ type: \"text\", text }],\n }),\n });\n await res.text();\n } catch {\n // OpenCode may have exited\n }\n }\n\n // ── Start the event loop ────────────────────────────────────────────────\n // No initialParts — the user drives the conversation from OpenCode's UI.\n\n const eventLoopPromise = setup.processor.run(deliver, setup.wrappedSource);\n\n console.log(`\\n OpenCode agent running.`);\n console.log(` To watch: opencode attach ${opencodeUrl}\\n`);\n\n // ── Block until child exits or Ctrl+C ───────────────────────────────────\n\n const exitPromise = new Promise<void>((resolve) => {\n child.on(\"exit\", resolve);\n });\n\n const signalPromise = new Promise<void>((resolve) => {\n const handler = () => { resolve(); };\n process.on(\"SIGINT\", handler);\n process.on(\"SIGTERM\", handler);\n });\n\n await Promise.race([exitPromise, signalPromise]);\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n if (!childExited) {\n child.kill();\n }\n await setup.cleanup();\n\n console.log(\"Disconnected.\");\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nasync function pollForReady(url: string, timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const res = await fetch(`${url}/session/status`);\n if (res.ok) return true;\n } catch {\n // Not ready yet\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n","/**\n * stoops run codex — client-side agent runtime for OpenAI Codex CLI.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Codex-specific pieces: tmux session + CodexTmuxBridge delivery.\n *\n * Codex connects to the runtime MCP server natively via config.toml\n * (no stdio bridge needed — Codex supports remote MCP servers via URL).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { writeFileSync, mkdtempSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n} from \"../tmux.js\";\nimport { CodexTmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\n\nexport { type AgentRuntimeOptions as RunCodexOptions };\n\n/** Check if codex CLI is installed and available. */\nfunction codexAvailable(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runCodex(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n if (!codexAvailable()) {\n console.error(\"Error: codex is required but not found. Install it with: npm install -g @openai/codex\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Codex agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP config for Codex ──────────────────────────────────────────\n // Codex supports remote MCP servers natively via url in config.toml.\n // No stdio bridge needed (unlike Claude Code which has an OAuth bug).\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"stoops_codex_\"));\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpUrl = `http://127.0.0.1:${mcpPort}/mcp`;\n\n // Write config.toml in a .codex directory structure\n const codexConfigDir = join(tmpDir, \".codex\");\n mkdirSync(codexConfigDir, { recursive: true });\n\n const configToml = [\n \"[mcp_servers.stoops]\",\n `url = \"${mcpUrl}\"`,\n `startup_timeout_sec = 15`,\n `tool_timeout_sec = 60`,\n ].join(\"\\n\");\n\n writeFileSync(join(codexConfigDir, \"config.toml\"), configToml);\n\n // ── Create tmux session + launch Codex ──────────────────────────────────\n\n const tmuxSession = `stoops_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n console.log(\"Launching Codex...\");\n tmuxCreateSession(tmuxSession);\n\n // Launch codex with config dir pointing to our temp directory + passthrough args\n const extraArgs = options.extraArgs ?? [];\n const codexCmd = [`CODEX_HOME=${codexConfigDir} codex`, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, codexCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new CodexTmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Wait for Codex to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Codex exited during startup. Try running again.\");\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n return;\n }\n }\n\n console.log(\"Attaching to Codex session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n\n console.log(\"Disconnected.\");\n}\n","/**\n * CodexTmuxBridge — state-aware event injection into Codex CLI.\n *\n * Reads the Codex TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → bracketed paste + Enter\n * typing → Ctrl+U (cut), bracketed paste + Enter, Ctrl+Y (restore)\n * approval → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Uses bracketed paste escape sequences to bypass Codex's timing-based\n * paste-burst detector (120ms Enter suppression window). Text wrapped\n * in ESC[200~...ESC[201~ is delivered as a Paste event, not individual\n * keystrokes, so the burst detector never fires.\n */\n\nimport {\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type CodexTuiState =\n | \"idle\"\n | \"typing\"\n | \"approval\"\n | \"streaming\"\n | \"unknown\";\n\n// Braille spinner characters used by Codex during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate an approval overlay\nconst APPROVAL_PATTERNS = [\n \"Would you like to\",\n \"needs your approval\",\n \"Press Enter to confirm or Esc to cancel\",\n \"Do you want to approve\",\n];\n\n// Patterns that indicate the agent is actively working\nconst STREAMING_PATTERNS = [\n /Working\\s*\\(\\d+[smh]/, // \"Working (12s\" or \"Working (1m 30s\"\n /Working\\s*$/, // \"Working\" at end of line (just started)\n /esc to interrupt/, // hint text during streaming\n];\n\nexport interface CodexTmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** Delay after bracketed paste before sending Enter (ms). Default: 150 */\n pasteDelayMs?: number;\n /** Delay between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n}\n\nexport class CodexTmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private pasteDelayMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n\n constructor(session: string, opts?: CodexTmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.pasteDelayMs = opts?.pasteDelayMs ?? 150;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n }\n\n /**\n * Delivery callback — drop-in replacement for EventProcessor's deliver.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[]): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n */\n detectState(): CodexTuiState {\n const lines = this.captureScreen();\n return detectCodexStateFromLines(lines);\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * Text is flattened to a single line to avoid multi-line paste issues.\n */\n private inject(text: string): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // approval, streaming, unknown — queue it\n this.enqueue(flat);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt using bracketed paste.\n *\n * Bracketed paste wraps text in ESC[200~...ESC[201~ so crossterm\n * delivers it as a single Paste event, bypassing the burst detector.\n * After the paste, we wait for the Enter suppression window (120ms)\n * to expire, then send Enter to submit.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, \"\\x1b[200~\");\n tmuxInjectText(this.session, text);\n tmuxInjectText(this.session, \"\\x1b[201~\");\n this.sleep(this.pasteDelayMs);\n tmuxSendEnter(this.session);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut to beginning of line (into kill buffer)\n * 2. Bracketed paste our text + Enter\n * 3. Ctrl+Y — yank user's text back from kill buffer\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event via bracketed paste\n tmuxInjectText(this.session, \"\\x1b[200~\");\n tmuxInjectText(this.session, text);\n tmuxInjectText(this.session, \"\\x1b[201~\");\n this.sleep(this.pasteDelayMs);\n tmuxSendEnter(this.session);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string): void {\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n * Drains one at a time — each injection may trigger streaming,\n * so the next poll re-checks state before injecting more.\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect Codex TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Codex's TUI (Ratatui-based) uses an inline viewport. The screen shows:\n * - Conversation history in the top area\n * - Status indicator (\"Working (12s * esc to interrupt)\") when streaming\n * - Approval overlay when tool/patch approval needed\n * - Composer input area at the bottom\n *\n * Detection priority: approval > streaming > idle/typing > unknown\n */\nexport function detectCodexStateFromLines(lines: string[]): CodexTuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~20 lines (visible bottom of screen)\n const tail = lines.slice(-20);\n const tailText = tail.join(\"\\n\");\n\n // 1. Approval overlay — highest priority\n for (const pattern of APPROVAL_PATTERNS) {\n if (tailText.includes(pattern)) return \"approval\";\n }\n\n // 2. Streaming — agent is working\n for (const pattern of STREAMING_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // Check for spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 3. Idle/Typing — look for the composer input area at the bottom.\n // Codex renders the composer as the last interactive element.\n // When idle, the bottom lines contain just the placeholder or empty input.\n // When typing, the bottom lines contain user-entered text.\n //\n // Heuristic: if none of the blocking states are detected (approval,\n // streaming), and the screen has content, assume the composer is\n // available. Check the last non-empty line for signs of user input.\n //\n // This is intentionally permissive — the worst case for a false\n // \"idle\" is that the injected text arrives during an unexpected\n // state and queues up in the input buffer harmlessly.\n const nonEmpty = tail.filter((l) => l.trim().length > 0);\n if (nonEmpty.length > 0) {\n // Look for signs that the screen is showing normal conversation + composer.\n // If we didn't match approval or streaming above, the composer is likely visible.\n // We need to distinguish idle (empty composer) from typing (text in composer).\n //\n // Codex shows a cursor line at the very bottom. Without empirical data on\n // exact patterns, we check if the last non-empty line looks like user input\n // (not part of conversation output which typically has structure like timestamps,\n // tool names, or markdown formatting).\n //\n // For now: if no blocking state is detected, return \"idle\" as the safe\n // injectable state. The bracketed paste approach is resilient enough that\n // injecting into a \"typing\" state via the idle path still works (text gets\n // appended to whatever the user was typing, and Enter submits all of it).\n return \"idle\";\n }\n\n return \"unknown\";\n}\n"],"mappings":"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","names":["createRequire","id","channel","sessionToken","participantList","require","randomUUID","filtered","items","ghostHint","cmd","randomUUID","args","join","tmpdir","execFileSync","spawn","setup","eventLoopPromise","join","tmpdir","spawn","spawn","execFileSync","writeFileSync","mkdtempSync","rmSync","join","tmpdir","SPINNER_CHARS","execFileSync","setup","eventLoopPromise","mkdtempSync","join","tmpdir","writeFileSync","rmSync","require","createRequire"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/cli/serve.ts","../../src/cli/auth.ts","../../src/cli/join.ts","../../src/cli/tui.tsx","../../src/cli/claude/run.ts","../../src/cli/tmux.ts","../../src/cli/claude/tmux-bridge.ts","../../src/cli/runtime-setup.ts","../../src/cli/opencode/run.ts","../../src/cli/codex/run.ts","../../src/cli/codex/tmux-bridge.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * stoops CLI — shared rooms for AI agents.\n *\n * Usage:\n * stoops [--room <name>] [--port <port>] [--share] Host a room + join it\n * stoops serve [--room <name>] [--port <port>] [--share] Headless server only\n * stoops join <url> [--name <name>] [--guest] Join a room as a human\n * stoops run claude [--name <name>] [--admin] [-- <args>] Connect Claude Code\n * stoops run opencode [--name <name>] [--admin] [-- <args>] Connect OpenCode\n */\n\nimport { createRequire } from \"node:module\";\nimport { serve } from \"./serve.js\";\nimport { join } from \"./join.js\";\nimport { runClaude } from \"./claude/run.js\";\nimport { runOpencode } from \"./opencode/run.js\";\nimport { runCodex } from \"./codex/run.js\";\nimport { buildShareUrl } from \"./auth.js\";\n\nfunction getVersion(): string {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n return pkg.version ?? \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nconst args = process.argv.slice(2);\n\nfunction getFlag(name: string, arr: string[] = args): string | undefined {\n const idx = arr.indexOf(`--${name}`);\n if (idx === -1) return undefined;\n const value = arr[idx + 1];\n if (value === undefined || value.startsWith(\"--\")) return undefined;\n return value;\n}\n\n/** Collect all values for a repeatable flag (e.g. --join url1 --join url2). */\nfunction getAllFlags(name: string, arr: string[] = args): string[] {\n const results: string[] = [];\n const flag = `--${name}`;\n for (let i = 0; i < arr.length; i++) {\n if (arr[i] === flag && arr[i + 1] && !arr[i + 1].startsWith(\"--\")) {\n results.push(arr[i + 1]);\n }\n }\n return results;\n}\n\nfunction printUsage(stream: typeof console.log = console.log): void {\n stream(\"Usage:\");\n stream(\" stoops [--room <name>] [--port <port>] [--share] Host + join\");\n stream(\" stoops serve [--room <name>] [--port <port>] [--share] Headless server\");\n stream(\" stoops join <url> [--name <name>] [--guest] Join a room\");\n stream(\" stoops run claude [--name <name>] [--admin] [-- <args>] Connect Claude Code\");\n stream(\" stoops run opencode [--name <name>] [--admin] [-- <args>] Connect OpenCode\");\n stream(\" stoops run codex [--name <name>] [--admin] [-- <args>] Connect Codex\");\n}\n\nasync function main(): Promise<void> {\n // ── --help anywhere ────────────────────────────────────────────────────\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n printUsage();\n return;\n }\n\n // ── stoops run <runtime> ───────────────────────────────────────────────\n if (args[0] === \"run\" && (args[1] === \"claude\" || args[1] === \"opencode\" || args[1] === \"codex\")) {\n const runtime = args[1];\n const restArgs = args.slice(2);\n\n // Split on -- separator: stoops flags before, passthrough args after\n const ddIndex = restArgs.indexOf(\"--\");\n const stoopsArgs = ddIndex >= 0 ? restArgs.slice(0, ddIndex) : restArgs;\n const extraArgs = ddIndex >= 0 ? restArgs.slice(ddIndex + 1) : [];\n\n const joinUrls = getAllFlags(\"join\", stoopsArgs);\n\n const runtimeOptions = {\n joinUrls: joinUrls.length > 0 ? joinUrls : undefined,\n name: getFlag(\"name\", stoopsArgs),\n admin: stoopsArgs.includes(\"--admin\"),\n headless: stoopsArgs.includes(\"--headless\"),\n extraArgs,\n };\n\n if (runtime === \"claude\") {\n await runClaude(runtimeOptions);\n } else if (runtime === \"opencode\") {\n await runOpencode(runtimeOptions);\n } else {\n await runCodex(runtimeOptions);\n }\n return;\n }\n\n // ── stoops join <url> ──────────────────────────────────────────────────\n if (args[0] === \"join\") {\n const server = args[1];\n if (!server || server.startsWith(\"--\")) {\n console.error(\"Usage: stoops join <url> [--name <name>] [--guest] [--headless]\");\n process.exit(1);\n }\n await join({\n server,\n name: getFlag(\"name\"),\n guest: args.includes(\"--guest\"),\n headless: args.includes(\"--headless\"),\n });\n return;\n }\n\n // ── stoops serve ───────────────────────────────────────────────────────\n if (args[0] === \"serve\") {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n headless: args.includes(\"--headless\"),\n save: getFlag(\"save\"),\n load: getFlag(\"load\"),\n });\n return;\n }\n\n // ── stoops (bare) — host + join ────────────────────────────────────────\n if (args.length === 0 || args[0]?.startsWith(\"--\")) {\n const portStr = getFlag(\"port\");\n const port = portStr ? parseInt(portStr, 10) : undefined;\n if (port !== undefined && (isNaN(port) || port < 0 || port > 65535)) {\n console.error(`Invalid port: ${portStr}`);\n process.exit(1);\n }\n const result = await serve({\n room: getFlag(\"room\"),\n port,\n share: args.includes(\"--share\"),\n quiet: true,\n save: getFlag(\"save\"),\n load: getFlag(\"load\"),\n });\n\n // Host joins locally as admin using the admin share token\n const adminJoinUrl = buildShareUrl(result.serverUrl, result.adminToken);\n const participantShareUrl = buildShareUrl(\n result.publicUrl !== result.serverUrl ? result.publicUrl : result.serverUrl,\n result.memberToken,\n );\n\n await join({\n server: adminJoinUrl,\n name: getFlag(\"name\"),\n shareUrl: participantShareUrl,\n version: getVersion(),\n savePath: result.savePath,\n });\n return;\n }\n\n // ── Unknown command ────────────────────────────────────────────────────\n console.error(`Unknown command: ${args[0]}\\n`);\n printUsage(console.error);\n process.exit(1);\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","/**\n * stoops serve — dumb room server.\n *\n * One room, one HTTP API, SSE broadcasting, authority enforcement.\n * No EventProcessor, no tmux, no agent lifecycle — those live client-side.\n * Humans connect via `stoops join`, agents via `stoops run claude`.\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { spawn, execFileSync, type ChildProcess } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { join as pathJoin } from \"node:path\";\n\nimport { Room } from \"../core/room.js\";\nimport { InMemoryStorage, FileBackedStorage } from \"../core/storage.js\";\nimport { randomRoomName, randomName } from \"../core/names.js\";\nimport { createEvent, type ActivityEvent, type AuthorityChangedEvent, type ParticipantKickedEvent, type RoomEvent } from \"../core/events.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport type { Channel } from \"../core/channel.js\";\nimport { formatTimestamp } from \"../agent/prompts.js\";\nimport { TokenManager, buildShareUrl } from \"./auth.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\ninterface ConnectedParticipant {\n id: string;\n name: string;\n authority: AuthorityLevel;\n channel: Channel;\n sessionToken: string;\n}\n\ninterface ConnectedGuest {\n id: string;\n authority: \"guest\";\n channel: Channel;\n sessionToken: string;\n}\n\nexport interface ServeOptions {\n room?: string;\n port?: number;\n share?: boolean;\n quiet?: boolean;\n /** Suppress all human-readable output; emit one JSON line with server info on stdout. */\n headless?: boolean;\n /** Path to save room state to (JSON file, written on every event). */\n save?: string;\n /** Path to load room state from (JSON file). Implies save to the same file. */\n load?: string;\n}\n\nexport interface ServeResult {\n serverUrl: string;\n publicUrl: string;\n roomName: string;\n adminToken: string;\n memberToken: string;\n savePath: string;\n}\n\n// ── SSE helper ───────────────────────────────────────────────────────────────\n\nasync function enrichAndSend(res: ServerResponse, event: RoomEvent, room: Room): Promise<void> {\n if (event.type === \"MessageSent\" && event.message.reply_to_id) {\n const replyMsg = await room.getMessage(event.message.reply_to_id);\n const enriched = {\n ...event,\n _replyToName: replyMsg?.sender_name ?? null,\n };\n res.write(`data: ${JSON.stringify(enriched)}\\n\\n`);\n return;\n }\n res.write(`data: ${JSON.stringify(event)}\\n\\n`);\n}\n\n// ── Main serve command ───────────────────────────────────────────────────────\n\nexport async function serve(options: ServeOptions): Promise<ServeResult> {\n const roomName = options.room ?? randomRoomName();\n const port = options.port ?? 7890;\n const serverUrl = `http://127.0.0.1:${port}`;\n const log = options.headless ? () => {} : logServer;\n\n let publicUrl = serverUrl;\n let tunnelProcess: ChildProcess | null = null;\n\n // Create room with persistence (default: tmp folder)\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\").slice(0, 19); // YYYY-MM-DDTHH-MM-SS\n const savePath = options.save ?? options.load ?? pathJoin(tmpdir(), `stoops-${roomName}-${timestamp}.json`);\n let storage;\n if (options.load) {\n try {\n storage = await FileBackedStorage.load(options.load);\n log(`loaded room state from ${options.load}`);\n } catch (err: any) {\n if (err.code === \"ENOENT\") {\n storage = new FileBackedStorage(options.load);\n log(`no existing file at ${options.load}, starting fresh`);\n } else {\n throw err;\n }\n }\n } else {\n storage = new FileBackedStorage(savePath);\n }\n const room = new Room(roomName, storage);\n\n // Auth\n const tokens = new TokenManager();\n\n // Connected participants and guests (by session token for lookup)\n const participants = new Map<string, ConnectedParticipant>();\n const guests = new Map<string, ConnectedGuest>();\n // Reverse lookup: participantId → sessionToken\n const idToSession = new Map<string, string>();\n\n // Track active SSE connections for cleanup\n const sseConnections = new Map<string, ServerResponse>();\n\n // ── JSON body parser helper ──────────────────────────────────────────────\n\n async function parseBody(req: IncomingMessage): Promise<Record<string, unknown>> {\n const chunks: Buffer[] = [];\n for await (const chunk of req) chunks.push(chunk as Buffer);\n try { return JSON.parse(Buffer.concat(chunks).toString()); } catch { return {}; }\n }\n\n // ── Auth helper ──────────────────────────────────────────────────────────\n\n function getSession(token: string | null) {\n if (!token) return null;\n const p = participants.get(token);\n if (p) return { ...p, kind: \"participant\" as const };\n const g = guests.get(token);\n if (g) return { ...g, kind: \"guest\" as const };\n return null;\n }\n\n function jsonError(res: ServerResponse, status: number, error: string): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error }));\n }\n\n function jsonOk(res: ServerResponse, data: Record<string, unknown> = {}): void {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ ok: true, ...data }));\n }\n\n // ── HTTP API ────────────────────────────────────────────────────────────\n\n const httpServer = createServer(async (req, res) => {\n const url = new URL(req.url ?? \"/\", `http://localhost:${port}`);\n\n // ── SSE event stream ───────────────────────────────────────────────────\n // ⚠️ MUST accept POST — DO NOT change to GET-only.\n // Cloudflare Quick Tunnels buffer GET streaming responses and only flush\n // when the connection closes. POST streams in real-time. (cloudflared#1449)\n // https://github.com/cloudflare/cloudflared/issues/1449\n if (url.pathname === \"/events\" && (req.method === \"GET\" || req.method === \"POST\")) {\n const authHeader = req.headers.authorization;\n const sessionToken = authHeader?.startsWith(\"Bearer \")\n ? authHeader.slice(7)\n : null;\n const session = getSession(sessionToken);\n\n if (!session) {\n jsonError(res, 401, \"Invalid session token\");\n return;\n }\n\n // SSE headers\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n res.flushHeaders();\n\n // Disable Nagle's algorithm so SSE events flush immediately.\n // Without this, small writes may be delayed up to ~200ms waiting\n // for more data, which can cause events to appear \"stuck\" until\n // the next event (e.g. a MentionedEvent) pushes the buffer.\n res.socket?.setNoDelay(true);\n\n sseConnections.set(session.id, res);\n\n // Send recent history so the joiner has context\n const history = await room.listEvents(undefined, 50);\n for (const event of [...history.items].reverse()) {\n await enrichAndSend(res, event, room);\n }\n\n // Live event stream\n const streamEvents = async () => {\n try {\n for await (const event of session.channel) {\n await enrichAndSend(res, event, room);\n }\n } catch {\n // Channel disconnected\n }\n };\n streamEvents();\n\n // Cleanup on client disconnect\n req.on(\"close\", () => {\n sseConnections.delete(session.id);\n });\n return;\n }\n\n // ── GET endpoints ─────────────────────────────────────────────────────\n\n if (req.method === \"GET\") {\n const sessionToken = url.searchParams.get(\"token\");\n const session = getSession(sessionToken);\n\n // ── GET /participants ────────────────────────────────────────────────\n if (url.pathname === \"/participants\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const list = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ participants: list }));\n return;\n }\n\n // ── GET /message/:id ─────────────────────────────────────────────────\n if (url.pathname.startsWith(\"/message/\")) {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const messageId = url.pathname.slice(\"/message/\".length);\n const msg = await room.getMessage(messageId);\n if (!msg) return jsonError(res, 404, \"Message not found\");\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ message: msg }));\n return;\n }\n\n // ── GET /messages ────────────────────────────────────────────────────\n if (url.pathname === \"/messages\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const count = parseInt(url.searchParams.get(\"count\") ?? \"30\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.listMessages(count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n\n // ── GET /events/history ──────────────────────────────────────────────\n if (url.pathname === \"/events/history\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const category = url.searchParams.get(\"category\") ?? null;\n const count = parseInt(url.searchParams.get(\"count\") ?? \"50\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.listEvents(category as any, count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n\n // ── GET /search ──────────────────────────────────────────────────────\n if (url.pathname === \"/search\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const query = url.searchParams.get(\"query\") ?? \"\";\n if (!query) return jsonError(res, 400, \"Missing query parameter\");\n const count = parseInt(url.searchParams.get(\"count\") ?? \"10\", 10);\n const cursor = url.searchParams.get(\"cursor\") ?? null;\n const result = await room.searchMessages(query, count, cursor);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n return;\n }\n }\n\n // ── POST endpoints ────────────────────────────────────────────────────\n\n if (req.method === \"POST\") {\n const body = await parseBody(req);\n\n // ── POST /join ──────────────────────────────────────────────────────\n if (url.pathname === \"/join\") {\n // Accept share token OR legacy type-based join\n const shareToken = String(body.token ?? \"\");\n const legacyType = String(body.type ?? \"\");\n\n let authority: AuthorityLevel;\n\n if (shareToken) {\n const tokenAuthority = tokens.validateShareToken(shareToken);\n if (!tokenAuthority) return jsonError(res, 403, \"Invalid share token\");\n authority = tokenAuthority;\n } else if (legacyType === \"guest\") {\n authority = \"guest\";\n } else if (legacyType === \"human\") {\n authority = \"member\";\n } else {\n // Default: agent joins as member\n authority = \"member\";\n }\n\n const participantType = String(body.type ?? \"human\") as \"human\" | \"agent\";\n const name = String(body.name ?? randomName());\n\n if (authority === \"guest\") {\n const id = `obs_${randomUUID().slice(0, 8)}`;\n const channel = room.observe();\n const sessionToken = tokens.createSessionToken(id, \"guest\");\n\n guests.set(sessionToken, { id, authority: \"guest\", channel, sessionToken });\n idToSession.set(id, sessionToken);\n\n const participantList = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n sessionToken,\n participantId: id,\n roomName,\n roomId: room.roomId,\n participants: participantList,\n authority: \"guest\",\n }));\n return;\n }\n\n // admin or participant — connect as a real participant\n const id = `${participantType}_${randomUUID().slice(0, 8)}`;\n const channel = await room.connect(id, name, { type: participantType, authority });\n const sessionToken = tokens.createSessionToken(id, authority);\n\n participants.set(sessionToken, { id, name, authority, channel, sessionToken });\n idToSession.set(id, sessionToken);\n\n const participantList = room.listParticipants().map((p) => ({\n id: p.id,\n name: p.name,\n type: p.type,\n authority: p.authority ?? \"member\",\n }));\n\n log(`${name} joined (${authority})`);\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({\n sessionToken,\n participantId: id,\n roomName,\n roomId: room.roomId,\n participants: participantList,\n authority,\n }));\n return;\n }\n\n // ── All remaining POST endpoints require a session token ────────────\n\n const sessionToken = String(body.token ?? \"\");\n const session = getSession(sessionToken);\n\n // ── POST /message ───────────────────────────────────────────────────\n if (url.pathname === \"/message\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot send messages\");\n const content = String(body.content ?? \"\");\n const replyTo = body.replyTo ? String(body.replyTo) : undefined;\n if (!content) return jsonError(res, 400, \"Empty message\");\n\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n const msg = await p.channel.sendMessage(content, replyTo);\n jsonOk(res, { messageId: msg.id });\n return;\n }\n\n // ── POST /event ─────────────────────────────────────────────────────\n if (url.pathname === \"/event\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot emit events\");\n const event = body.event as RoomEvent | undefined;\n if (!event) return jsonError(res, 400, \"Missing event\");\n\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n await p.channel.emit(event);\n jsonOk(res);\n return;\n }\n\n // ── POST /set-mode ──────────────────────────────────────────────────\n if (url.pathname === \"/set-mode\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n const targetId = body.participantId ? String(body.participantId) : session.id;\n const mode = String(body.mode ?? \"\");\n if (!mode) return jsonError(res, 400, \"Missing mode\");\n\n // Setting someone else's mode requires admin\n if (targetId !== session.id && session.authority !== \"admin\") {\n return jsonError(res, 403, \"Only admins can change other participants' modes\");\n }\n\n // Emit mode_changed activity event\n const p = participants.get(sessionToken);\n if (!p) return jsonError(res, 403, \"Not a participant\");\n\n await p.channel.emit(createEvent<ActivityEvent>({\n type: \"Activity\",\n category: \"ACTIVITY\",\n room_id: room.roomId,\n participant_id: targetId,\n action: \"mode_changed\",\n detail: { mode },\n }));\n\n jsonOk(res);\n return;\n }\n\n // ── POST /set-authority ──────────────────────────────────────────────\n if (url.pathname === \"/set-authority\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority !== \"admin\") return jsonError(res, 403, \"Only admins can change authority\");\n const targetId = String(body.participantId ?? \"\");\n const newAuthority = String(body.authority ?? \"\") as AuthorityLevel;\n if (!targetId) return jsonError(res, 400, \"Missing participantId\");\n if (![\"admin\", \"member\", \"guest\"].includes(newAuthority)) {\n return jsonError(res, 400, \"Invalid authority. Must be admin, member, or guest.\");\n }\n if (targetId === session.id) return jsonError(res, 400, \"Cannot change own authority\");\n\n // Update all three places: ConnectedParticipant, TokenManager session, Room participant\n const targetSession = idToSession.get(targetId);\n if (!targetSession) return jsonError(res, 404, \"Participant not found\");\n const target = participants.get(targetSession);\n if (!target) return jsonError(res, 404, \"Participant not found\");\n\n target.authority = newAuthority;\n tokens.updateSessionAuthority(targetSession, newAuthority);\n room.setParticipantAuthority(targetId, newAuthority);\n\n // Emit AuthorityChanged event\n const adminP = participants.get(sessionToken);\n const targetParticipant = room.listParticipants().find(p => p.id === targetId);\n if (adminP && targetParticipant) {\n await adminP.channel.emit(createEvent<AuthorityChangedEvent>({\n type: \"AuthorityChanged\",\n category: \"PRESENCE\",\n room_id: room.roomId,\n participant_id: targetId,\n participant: targetParticipant,\n new_authority: newAuthority,\n changed_by: adminP.name,\n }));\n }\n\n log(`${target.name} authority → ${newAuthority}`);\n jsonOk(res);\n return;\n }\n\n // ── POST /kick ──────────────────────────────────────────────────────\n if (url.pathname === \"/kick\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority !== \"admin\") return jsonError(res, 403, \"Only admins can kick\");\n const targetId = String(body.participantId ?? \"\");\n if (!targetId) return jsonError(res, 400, \"Missing participantId\");\n\n // Find and disconnect the target\n const targetSession = idToSession.get(targetId);\n if (targetSession) {\n const target = participants.get(targetSession) ?? guests.get(targetSession);\n if (target) {\n // Emit ParticipantKicked before disconnect so all participants see it\n const adminP = participants.get(sessionToken);\n const targetParticipant = room.listParticipants().find(p => p.id === targetId);\n if (adminP && targetParticipant) {\n await adminP.channel.emit(createEvent<ParticipantKickedEvent>({\n type: \"ParticipantKicked\",\n category: \"PRESENCE\",\n room_id: room.roomId,\n participant_id: targetId,\n participant: targetParticipant,\n kicked_by: adminP.name,\n }));\n }\n // Silent disconnect — the kicked event replaces ParticipantLeft\n await target.channel.disconnect(true);\n participants.delete(targetSession);\n guests.delete(targetSession);\n idToSession.delete(targetId);\n tokens.revokeSessionToken(targetSession);\n // Close SSE connection\n const sse = sseConnections.get(targetId);\n if (sse) {\n sse.end();\n sseConnections.delete(targetId);\n }\n log(`kicked ${targetId}`);\n }\n }\n\n jsonOk(res);\n return;\n }\n\n // ── POST /share ─────────────────────────────────────────────────────\n if (url.pathname === \"/share\") {\n if (!session) return jsonError(res, 401, \"Invalid session token\");\n if (session.authority === \"guest\") return jsonError(res, 403, \"Guests cannot create share links\");\n\n const targetAuthority = (body.authority as AuthorityLevel) ?? undefined;\n\n const links: Record<string, string> = {};\n\n if (targetAuthority) {\n // Generate a specific link\n const token = tokens.generateShareToken(session.authority, targetAuthority);\n if (!token) return jsonError(res, 403, `Cannot generate ${targetAuthority} link`);\n links[targetAuthority] = buildShareUrl(publicUrl, token);\n } else {\n // Generate all links the caller can create\n const tiers: AuthorityLevel[] = [\"admin\", \"member\", \"guest\"];\n for (const tier of tiers) {\n const token = tokens.generateShareToken(session.authority, tier);\n if (token) links[tier] = buildShareUrl(publicUrl, token);\n }\n }\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ links }));\n return;\n }\n\n // ── POST /disconnect ────────────────────────────────────────────────\n if (url.pathname === \"/disconnect\") {\n // Accept either session token or legacy participantId/agentId\n const token = String(body.token ?? \"\");\n const legacyId = String(body.participantId ?? body.agentId ?? \"\");\n\n let targetToken = token;\n if (!targetToken && legacyId) {\n targetToken = idToSession.get(legacyId) ?? \"\";\n }\n\n if (targetToken) {\n const p = participants.get(targetToken);\n if (p) {\n await p.channel.disconnect();\n participants.delete(targetToken);\n idToSession.delete(p.id);\n tokens.revokeSessionToken(targetToken);\n const sse = sseConnections.get(p.id);\n if (sse) { sse.end(); sseConnections.delete(p.id); }\n log(`${p.name} disconnected`);\n }\n\n const g = guests.get(targetToken);\n if (g) {\n await g.channel.disconnect();\n guests.delete(targetToken);\n idToSession.delete(g.id);\n tokens.revokeSessionToken(targetToken);\n const sse = sseConnections.get(g.id);\n if (sse) { sse.end(); sseConnections.delete(g.id); }\n }\n }\n\n jsonOk(res);\n return;\n }\n }\n\n res.writeHead(404).end(\"Not found\");\n });\n\n httpServer.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n console.error(`\\nPort ${port} is already in use. Another stoops instance may be running.`);\n console.error(` Kill it: lsof -ti :${port} | xargs kill`);\n console.error(` Or use: stoops --port ${port + 1}\\n`);\n process.exit(1);\n }\n throw err;\n });\n\n await new Promise<void>((resolve) => {\n httpServer.listen(port, \"0.0.0.0\", () => resolve());\n });\n\n // Start tunnel if --share\n if (options.share) {\n tunnelProcess = await startTunnel(port);\n if (tunnelProcess) {\n const tunnelUrl = await waitForTunnelUrl(tunnelProcess);\n if (tunnelUrl) publicUrl = tunnelUrl;\n }\n }\n\n // Generate share tokens on boot\n const adminToken = tokens.generateShareToken(\"admin\", \"admin\")!;\n const memberToken = tokens.generateShareToken(\"admin\", \"member\")!;\n\n if (options.headless) {\n process.stdout.write(JSON.stringify({ serverUrl, publicUrl, roomName, adminToken, memberToken, savePath }) + \"\\n\");\n } else if (!options.quiet) {\n let version = process.env.npm_package_version ?? \"\";\n if (!version) {\n try {\n const require = createRequire(import.meta.url);\n const pkg = require(\"../../package.json\");\n version = pkg.version ?? \"unknown\";\n } catch {\n version = \"unknown\";\n }\n }\n const adminUrl = buildShareUrl(publicUrl, adminToken);\n const joinUrl = buildShareUrl(publicUrl, memberToken);\n\n console.log(`\n stoops v${version}\n\n Room: ${roomName}\n Server: ${serverUrl}${publicUrl !== serverUrl ? `\\n Tunnel: ${publicUrl}` : \"\"}\n Saving: ${savePath}\n\n Join: stoops join ${joinUrl}\n Admin: stoops join ${adminUrl}\n Claude: stoops run claude --name MyClaude → then tell agent to join: ${joinUrl}\n Codex: stoops run codex --name MyCodex → then tell agent to join: ${joinUrl}\n`);\n }\n\n // ── Graceful shutdown ──────────────────────────────────────────────────\n\n const shutdown = async () => {\n log(\"shutting down...\");\n if (tunnelProcess) { tunnelProcess.kill(); tunnelProcess = null; }\n for (const [id, sse] of sseConnections) { sse.end(); sseConnections.delete(id); }\n for (const p of participants.values()) { await p.channel.disconnect().catch(() => {}); }\n for (const g of guests.values()) { await g.channel.disconnect().catch(() => {}); }\n await new Promise<void>((resolve, reject) => {\n httpServer.close((err) => (err ? reject(err) : resolve()));\n });\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n return { serverUrl, publicUrl, roomName, adminToken, memberToken, savePath };\n}\n\n// ── Server log ────────────────────────────────────────────────────────────────\n\nfunction logServer(message: string): void {\n console.log(` [${formatTimestamp(new Date())}] ${message}`);\n}\n\n// ── Cloudflared tunnel ───────────────────────────────────────────────────────\n\nfunction cloudflaredAvailable(): boolean {\n try {\n execFileSync(\"which\", [\"cloudflared\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nasync function startTunnel(port: number): Promise<ChildProcess | null> {\n if (!cloudflaredAvailable()) {\n console.error(\" --share requires cloudflared. Install: brew install cloudflared\");\n return null;\n }\n\n const child = spawn(\"cloudflared\", [\"tunnel\", \"--url\", `http://localhost:${port}`], {\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n\n child.on(\"error\", () => {\n // cloudflared failed to start\n });\n\n return child;\n}\n\nfunction waitForTunnelUrl(child: ChildProcess, timeoutMs = 15000): Promise<string | null> {\n return new Promise((resolve) => {\n let resolved = false;\n let buffer = \"\";\n\n const timer = setTimeout(() => {\n if (!resolved) { resolved = true; resolve(null); }\n }, timeoutMs);\n\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n buffer += chunk.toString();\n const match = buffer.match(/https:\\/\\/[a-z0-9-]+\\.trycloudflare\\.com/);\n if (match && !resolved) {\n resolved = true;\n clearTimeout(timer);\n resolve(match[0]);\n }\n });\n\n child.on(\"exit\", () => {\n if (!resolved) { resolved = true; clearTimeout(timer); resolve(null); }\n });\n });\n}\n","/**\n * Auth token system for stoops share links and session management.\n *\n * Two token types:\n * - Share tokens — embedded in URLs, map to an authority tier.\n * Anyone with the link joins at that tier.\n * - Session tokens — issued on join, identify a participant + authority.\n * Used for all subsequent API calls.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport type { AuthorityLevel } from \"../core/types.js\";\n\ninterface SessionData {\n participantId: string;\n authority: AuthorityLevel;\n}\n\nexport class TokenManager {\n /** share token hash → authority level */\n private _shareTokens = new Map<string, AuthorityLevel>();\n /** session token → participant data */\n private _sessionTokens = new Map<string, SessionData>();\n\n /**\n * Generate a share token at the given authority tier.\n * Callers can only generate tokens at their own tier or below.\n */\n generateShareToken(callerAuthority: AuthorityLevel, targetAuthority: AuthorityLevel): string | null {\n if (!canGrant(callerAuthority, targetAuthority)) return null;\n const token = randomBytes(16).toString(\"hex\");\n this._shareTokens.set(token, targetAuthority);\n return token;\n }\n\n /** Validate a share token and return its authority level. */\n validateShareToken(token: string): AuthorityLevel | null {\n return this._shareTokens.get(token) ?? null;\n }\n\n /** Create a session token for a participant. */\n createSessionToken(participantId: string, authority: AuthorityLevel): string {\n const token = randomBytes(16).toString(\"hex\");\n this._sessionTokens.set(token, { participantId, authority });\n return token;\n }\n\n /** Validate a session token and return participant data. */\n validateSessionToken(token: string): SessionData | null {\n return this._sessionTokens.get(token) ?? null;\n }\n\n /** Revoke a session token (on disconnect). */\n revokeSessionToken(token: string): void {\n this._sessionTokens.delete(token);\n }\n\n /** Update the authority level for an existing session. */\n updateSessionAuthority(token: string, newAuthority: AuthorityLevel): boolean {\n const data = this._sessionTokens.get(token);\n if (!data) return false;\n data.authority = newAuthority;\n return true;\n }\n\n /** Find a session token by participant ID (for cleanup). */\n findSessionByParticipant(participantId: string): string | null {\n for (const [token, data] of this._sessionTokens) {\n if (data.participantId === participantId) return token;\n }\n return null;\n }\n}\n\n/** Authority tier ordering: admin > member > guest. */\nconst TIER_ORDER: Record<AuthorityLevel, number> = {\n admin: 2,\n member: 1,\n guest: 0,\n};\n\n/** Can a caller at `callerLevel` grant authority at `targetLevel`? */\nfunction canGrant(callerLevel: AuthorityLevel, targetLevel: AuthorityLevel): boolean {\n return TIER_ORDER[callerLevel] >= TIER_ORDER[targetLevel];\n}\n\n/** Build a share URL from a base URL and token. */\nexport function buildShareUrl(baseUrl: string, token: string): string {\n const url = new URL(baseUrl);\n url.searchParams.set(\"token\", token);\n return url.toString();\n}\n\n/** Extract a token from a share URL. */\nexport function extractToken(url: string): string | null {\n try {\n const parsed = new URL(url);\n return parsed.searchParams.get(\"token\");\n } catch {\n return null;\n }\n}\n","/**\n * stoops join — connect to a room as a human participant.\n *\n * Opens the TUI and connects to a stoops server over HTTP.\n * Events stream in via SSE, messages sent via POST /message.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { createInterface } from \"node:readline\";\nimport { randomName } from \"../core/names.js\";\nimport type { RoomEvent } from \"../core/events.js\";\nimport type { AuthorityLevel } from \"../core/types.js\";\nimport { formatTimestamp } from \"../agent/prompts.js\";\nimport { startTUI, type TUIHandle, type DisplayEvent } from \"./tui.js\";\nimport { extractToken, buildShareUrl } from \"./auth.js\";\n\nexport interface JoinOptions {\n server: string;\n name?: string;\n guest?: boolean;\n /** Share URL to display before TUI starts (for host+join mode with --share). */\n shareUrl?: string;\n /** Skip TUI — stream events as JSON to stdout, read messages from stdin. */\n headless?: boolean;\n /** Version string to display in the TUI banner. */\n version?: string;\n /** Path where room state is being saved. */\n savePath?: string;\n}\n\nexport async function join(options: JoinOptions): Promise<void> {\n // Extract token from URL if present\n const token = extractToken(options.server);\n // Strip query params to get clean server URL\n let serverUrl: string;\n try {\n const parsed = new URL(options.server);\n parsed.search = \"\";\n serverUrl = parsed.toString().replace(/\\/$/, \"\");\n } catch {\n serverUrl = options.server.replace(/\\/$/, \"\");\n }\n\n const name = options.name ?? randomName();\n const isGuest = options.guest ?? false;\n\n // ── Register with server ────────────────────────────────────────────────\n\n let sessionToken: string;\n let participantId: string;\n let roomName: string;\n let authority: AuthorityLevel;\n let participants: Array<{ id: string; name: string; type: string; authority?: string }>;\n\n try {\n const joinBody: Record<string, unknown> = {};\n if (token) {\n joinBody.token = token;\n joinBody.type = \"human\";\n joinBody.name = name;\n } else if (isGuest) {\n joinBody.type = \"guest\";\n } else {\n joinBody.type = \"human\";\n joinBody.name = name;\n }\n\n const res = await fetch(`${serverUrl}/join`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(joinBody),\n });\n\n if (!res.ok) {\n const err = await res.text();\n console.error(`Failed to join: ${err}`);\n process.exit(1);\n }\n\n const data = await res.json() as Record<string, unknown>;\n sessionToken = String(data.sessionToken ?? \"\");\n participantId = String(data.participantId);\n roomName = String(data.roomName);\n authority = (data.authority as AuthorityLevel) ?? \"member\";\n participants = (data.participants as Array<{ id: string; name: string; type: string; authority?: string }>) ?? [];\n } catch {\n console.error(`Cannot reach stoops server at ${serverUrl}. Is it running?`);\n process.exit(1);\n }\n\n // ── Disconnect helper ───────────────────────────────────────────────────\n\n let disconnected = false;\n let cleanupStream: (() => void) | null = null;\n const disconnect = async () => {\n if (disconnected) return;\n disconnected = true;\n cleanupStream?.();\n try {\n await fetch(`${serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken }),\n });\n } catch {\n // Server may be down\n }\n };\n\n // ── Headless mode — skip TUI, stream events as JSON, read messages from stdin ──\n\n if (options.headless) {\n const sseController = new AbortController();\n const cleanup = async () => {\n sseController.abort();\n await disconnect();\n };\n\n process.on(\"SIGINT\", async () => { await cleanup(); process.exit(0); });\n process.on(\"SIGTERM\", async () => { await cleanup(); process.exit(0); });\n\n // Read messages from stdin and send them\n const rl = createInterface({ input: process.stdin, terminal: false });\n rl.on(\"line\", async (line) => {\n const content = line.trim();\n if (!content || authority === \"guest\") return;\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, content }),\n });\n } catch { /* server may be down */ }\n });\n\n // Stream events from SSE and write as JSON lines to stdout\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: { Accept: \"text/event-stream\", Authorization: `Bearer ${sessionToken}` },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n process.stderr.write(\"Failed to connect event stream\\n\");\n await cleanup();\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const parts = buf.split(\"\\n\\n\");\n buf = parts.pop()!;\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n try {\n const event = JSON.parse(dataLine.slice(6));\n process.stdout.write(JSON.stringify(event) + \"\\n\");\n } catch { /* malformed */ }\n }\n }\n } catch {\n // Stream ended or aborted\n }\n\n if (!disconnected) { await cleanup(); }\n process.exit(0);\n }\n\n // ── Start TUI ───────────────────────────────────────────────────────────\n\n const isReadOnly = authority === \"guest\" || isGuest;\n\n // ── Slash command helper ──────────────────────────────────────────────\n\n function systemEvent(content: string): void {\n tui.push({\n id: randomUUID(),\n ts: formatTimestamp(new Date()),\n kind: \"system\",\n content,\n });\n }\n\n async function handleSlashCommand(input: string): Promise<void> {\n const parts = input.slice(1).split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n const args = parts.slice(1);\n\n switch (cmd) {\n // ── /who ──────────────────────────────────────────────────────\n case \"who\": {\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string; type: string; authority?: string }> };\n const lines = data.participants.map((p) => {\n const auth = p.authority ?? \"member\";\n return ` ${p.type === \"agent\" ? \"agent\" : \"human\"} ${p.name} (${auth})`;\n });\n systemEvent(`Participants:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /leave ────────────────────────────────────────────────────\n case \"leave\": {\n await disconnect();\n tui.stop();\n process.exit(0);\n return;\n }\n\n // ── /kick <name> (admin only) ─────────────────────────────────\n case \"kick\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can kick.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /kick <name>\"); return; }\n\n // Look up participant by name\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const kickRes = await fetch(`${serverUrl}/kick`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id }),\n });\n if (!kickRes.ok) { systemEvent(`Failed to kick: ${await kickRes.text()}`); return; }\n systemEvent(`Kicked ${targetName}.`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /mute <name> (admin only) — demote to observer ────────────\n case \"mute\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can mute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /mute <name>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const authRes = await fetch(`${serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, authority: \"guest\" }),\n });\n if (!authRes.ok) { systemEvent(`Failed to mute: ${await authRes.text()}`); return; }\n systemEvent(`Muted ${targetName} (guest).`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /unmute <name> (admin only) — restore to participant ──────\n case \"unmute\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can unmute.\"); return; }\n const targetName = args[0];\n if (!targetName) { systemEvent(\"Usage: /unmute <name>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const authRes = await fetch(`${serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, authority: \"member\" }),\n });\n if (!authRes.ok) { systemEvent(`Failed to unmute: ${await authRes.text()}`); return; }\n systemEvent(`Unmuted ${targetName} (member).`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /setmode <name> <mode> (admin only) ───────────────────────\n case \"setmode\": {\n if (authority !== \"admin\") { systemEvent(\"Only admins can set modes.\"); return; }\n const targetName = args[0];\n const mode = args[1];\n if (!targetName || !mode) { systemEvent(\"Usage: /setmode <name> <mode>\"); return; }\n\n try {\n const res = await fetch(`${serverUrl}/participants?token=${sessionToken}`);\n if (!res.ok) { systemEvent(\"Failed to get participant list.\"); return; }\n const data = (await res.json()) as { participants: Array<{ id: string; name: string }> };\n const target = data.participants.find((p) => p.name.toLowerCase() === targetName.toLowerCase());\n if (!target) { systemEvent(`Participant \"${targetName}\" not found.`); return; }\n\n const modeRes = await fetch(`${serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, participantId: target.id, mode }),\n });\n if (!modeRes.ok) { systemEvent(`Failed to set mode: ${await modeRes.text()}`); return; }\n systemEvent(`Set ${targetName} to ${mode}.`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n // ── /share [--as <tier>] ──────────────────────────────────────\n case \"share\": {\n if (authority === \"guest\") { systemEvent(\"Guests cannot create share links.\"); return; }\n\n let targetAuthority: string | undefined;\n if (args[0] === \"--as\" && args[1]) {\n targetAuthority = args[1];\n }\n\n try {\n const body: Record<string, unknown> = { token: sessionToken };\n if (targetAuthority) body.authority = targetAuthority;\n\n const res = await fetch(`${serverUrl}/share`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n if (!res.ok) { systemEvent(`Failed: ${await res.text()}`); return; }\n const data = (await res.json()) as { links: Record<string, string> };\n const lines = Object.entries(data.links).map(([tier, url]) =>\n ` ${tier}: stoops join ${url}`\n );\n systemEvent(`Share links:\\n${lines.join(\"\\n\")}`);\n } catch {\n systemEvent(\"Failed to reach server.\");\n }\n return;\n }\n\n default:\n systemEvent(`Unknown command: /${cmd}`);\n }\n }\n\n // Print share info via console.log BEFORE Ink renders. This lands in the\n // terminal buffer above Ink's render area — plain text, no ANSI codes,\n // fully selectable. Each line is a complete copyable command.\n if (options.shareUrl) {\n console.log();\n console.log(` Invite a friend: npx stoops join \"${options.shareUrl}\"`);\n console.log(` Connect Claude Code: npx stoops run claude --name MyClaude → then tell agent to join: ${options.shareUrl}`);\n console.log(` Connect Codex: npx stoops run codex --name MyCodex → then tell agent to join: ${options.shareUrl}`);\n console.log();\n }\n\n const tui = startTUI({\n roomName,\n readOnly: isReadOnly,\n isAdmin: authority === \"admin\",\n version: options.version,\n savePath: options.savePath,\n onSend: isReadOnly ? undefined : async (content: string) => {\n // Intercept slash commands\n if (content.startsWith(\"/\")) {\n await handleSlashCommand(content);\n return;\n }\n\n try {\n await fetch(`${serverUrl}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: sessionToken, content }),\n });\n } catch {\n // Server may be down — silently fail\n }\n },\n onCtrlC: async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n },\n });\n\n // Set initial agent names + participant names\n const agentNames = participants\n .filter((p) => p.type === \"agent\")\n .map((p) => p.name);\n if (agentNames.length > 0) {\n tui.setAgentNames(agentNames);\n }\n const participantNames = new Set(\n participants.filter((p) => p.id !== participantId).map((p) => p.name),\n );\n tui.setParticipants([...participantNames]);\n\n // ── Connect SSE event stream ────────────────────────────────────────────\n // ⚠️ MUST use POST — DO NOT change to GET.\n // Cloudflare Quick Tunnels buffer GET streaming responses and only flush\n // when the connection closes. POST streams in real-time. (cloudflared#1449)\n // https://github.com/cloudflare/cloudflared/issues/1449\n\n {\n const participantTypes = new Map<string, \"human\" | \"agent\">();\n for (const p of participants) {\n participantTypes.set(p.id, p.type as \"human\" | \"agent\");\n }\n const currentAgents = new Set(agentNames);\n let sseController: AbortController | null = null;\n\n cleanupStream = () => {\n if (sseController) { sseController.abort(); sseController = null; }\n };\n\n const connectSSE = async () => {\n sseController = new AbortController();\n\n try {\n const res = await fetch(`${serverUrl}/events`, {\n method: \"POST\",\n headers: {\n Accept: \"text/event-stream\",\n Authorization: `Bearer ${sessionToken}`,\n },\n signal: sseController.signal,\n });\n\n if (!res.ok || !res.body) {\n console.error(\"Failed to connect event stream\");\n await disconnect();\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split(\"\\n\\n\");\n buffer = parts.pop()!;\n\n for (const part of parts) {\n const dataLine = part.split(\"\\n\").find((l) => l.startsWith(\"data: \"));\n if (!dataLine) continue;\n\n try {\n const event = JSON.parse(dataLine.slice(6)) as RoomEvent & { _replyToName?: string };\n\n if (event.type === \"ParticipantJoined\") {\n participantTypes.set(event.participant.id, event.participant.type);\n }\n\n const displayEvent = toDisplayEvent(event, participantId, participantTypes);\n if (displayEvent) {\n tui.push(displayEvent);\n }\n\n if (event.type === \"ParticipantJoined\") {\n if (event.participant.type === \"agent\") {\n currentAgents.add(event.participant.name);\n tui.setAgentNames([...currentAgents]);\n }\n if (event.participant.id !== participantId) {\n participantNames.add(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n }\n if (event.type === \"ParticipantLeft\") {\n if (event.participant.type === \"agent\") {\n currentAgents.delete(event.participant.name);\n tui.setAgentNames([...currentAgents]);\n }\n participantTypes.delete(event.participant.id);\n participantNames.delete(event.participant.name);\n tui.setParticipants([...participantNames]);\n }\n } catch {\n // Malformed event — skip\n }\n }\n }\n\n // Stream ended — server closed connection\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n } catch {\n if (!disconnected) {\n tui.stop();\n console.log(\"\\nServer disconnected.\");\n process.exit(0);\n }\n }\n };\n\n connectSSE();\n }\n\n // ── Graceful shutdown ───────────────────────────────────────────────────\n\n process.on(\"SIGINT\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n await disconnect();\n tui.stop();\n process.exit(0);\n });\n}\n\n// ── RoomEvent → DisplayEvent conversion ───────────────────────────────────────\n\nfunction toDisplayEvent(\n event: RoomEvent & { _replyToName?: string },\n selfId: string,\n participantTypes: Map<string, \"human\" | \"agent\">,\n): DisplayEvent | null {\n const ts = formatTimestamp(new Date(event.timestamp));\n\n switch (event.type) {\n case \"MessageSent\": {\n const msg = event.message;\n const senderType = participantTypes.get(msg.sender_id) ?? \"human\";\n return {\n id: msg.id,\n ts,\n kind: \"message\",\n senderName: msg.sender_name,\n senderType,\n isSelf: msg.sender_id === selfId,\n content: msg.content,\n replyToName: event._replyToName ?? undefined,\n };\n }\n case \"ParticipantJoined\":\n return {\n id: randomUUID(),\n ts,\n kind: \"join\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantLeft\":\n return {\n id: randomUUID(),\n ts,\n kind: \"leave\",\n name: event.participant.name,\n participantType: event.participant.type,\n };\n case \"ParticipantKicked\":\n return {\n id: randomUUID(),\n ts,\n kind: \"system\",\n content: `${event.participant.name} was kicked`,\n };\n case \"AuthorityChanged\": {\n const name = event.participant.name;\n if (event.new_authority === \"guest\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was muted` };\n }\n if (event.new_authority === \"member\") {\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} was unmuted` };\n }\n return { id: randomUUID(), ts, kind: \"system\", content: `${name} → ${event.new_authority}` };\n }\n case \"Activity\":\n if (event.action === \"mode_changed\") {\n return {\n id: randomUUID(),\n ts,\n kind: \"mode\",\n mode: String((event.detail as Record<string, unknown>)?.mode ?? \"\"),\n };\n }\n return null;\n default:\n return null;\n }\n}\n","/**\n * stoops TUI — ink-based terminal UI for the room server.\n *\n * Uses ink's <Static> for events (rendered once, selectable terminal text)\n * and a dynamic footer for input + status. Same architecture as Claude Code.\n */\n\nimport React, { useState, useEffect, useCallback, useMemo } from \"react\";\nimport { render, Box, Text, Static, useStdout, useInput } from \"ink\";\n\n// ── Palette (from stoops-app) ─────────────────────────────────────────────────\n\nconst C = {\n cyan: \"#00d4ff\",\n purple: \"#8b5cf6\",\n orange: \"#ff8c42\",\n pink: \"#f472b6\",\n green: \"#34d399\",\n yellow: \"#fbbf24\",\n danger: \"#f87171\",\n text: \"#eceff4\",\n secondary: \"#b0b7c4\",\n dim: \"#7e8798\",\n muted: \"#5b6679\",\n border: \"#475264\",\n} as const;\n\nconst AGENT_COLORS = [C.cyan, C.purple, C.orange, C.pink, C.green, C.yellow] as const;\nconst SIGILS = [\"◆\", \"▲\", \"●\", \"■\", \"★\", \"◉\", \"◈\", \"▸\"] as const;\n\n// ── Banner ───────────────────────────────────────────────────────────────────\n// Figlet \"slant\" font, colored with a purple → cyan gradient per line.\n\nconst BANNER_LINES = [\n \" __ \",\n \" _____/ /_____ ____ ____ _____\",\n \" / ___/ __/ __ \\\\/ __ \\\\/ __ \\\\/ ___/\",\n \" (__ ) /_/ /_/ / /_/ / /_/ (__ ) \",\n \"/____/\\\\__/\\\\____/\\\\____/ .___/____/ \",\n \" /_/ \",\n];\n\nconst GRADIENT = [\"#9b6dff\", \"#7c8bff\", \"#5da8ff\", \"#3dc4ff\", \"#1ddcff\", \"#00e8ff\"];\n\n// ── Slash commands ────────────────────────────────────────────────────────────\n\ninterface SlashParam {\n label: string; // display hint: \"name\", \"mode\", etc.\n completions?: string[] | \"participants\"; // static values, dynamic lookup, or undefined (hint only)\n}\n\ninterface SlashCommand {\n name: string;\n description: string;\n adminOnly?: boolean;\n params?: SlashParam[];\n}\n\nconst ENGAGEMENT_MODES = [\n \"everyone\", \"people\", \"agents\",\n \"standby-everyone\", \"standby-people\", \"standby-agents\",\n];\n\nconst SLASH_COMMANDS: SlashCommand[] = [\n { name: \"/who\", description: \"List participants\" },\n { name: \"/leave\", description: \"Disconnect and exit\" },\n { name: \"/share\", description: \"Generate share links\" },\n { name: \"/kick\", description: \"Remove a participant\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/mute\", description: \"Make read-only (guest)\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/unmute\", description: \"Restore to member\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n ]},\n { name: \"/setmode\", description: \"Set engagement mode\", adminOnly: true, params: [\n { label: \"name\", completions: \"participants\" },\n { label: \"mode\", completions: ENGAGEMENT_MODES },\n ]},\n];\n\nconst CMD_DISPLAY_COL = 26; // width for command + params display column\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport type DisplayEvent =\n | { id: string; ts: string; kind: \"message\"; senderName: string; senderType: \"human\" | \"agent\"; isSelf: boolean; content: string; replyToName?: string }\n | { id: string; ts: string; kind: \"join\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"leave\"; name: string; participantType: \"human\" | \"agent\" }\n | { id: string; ts: string; kind: \"mode\"; mode: string }\n | { id: string; ts: string; kind: \"system\"; content: string };\n\nexport interface TUIHandle {\n push(event: DisplayEvent): void;\n setAgentNames(names: string[]): void;\n setParticipants(names: string[]): void;\n stop(): void;\n}\n\nexport interface TUIOptions {\n roomName: string;\n onSend?(content: string): void;\n onCtrlC?(): void;\n readOnly?: boolean;\n isAdmin?: boolean;\n version?: string;\n savePath?: string;\n}\n\n// ── Identity (seed → color + sigil) ──────────────────────────────────────────\n\nfunction seedHash(s: string): number {\n let h = 0;\n for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;\n return Math.abs(h);\n}\n\nfunction makeIdentityAssigner(): (name: string) => { color: string; sigil: string } {\n const map = new Map<string, { color: string; sigil: string }>();\n let colorIdx = 0;\n return (name: string) => {\n if (!map.has(name)) {\n const h = seedHash(name);\n map.set(name, {\n color: AGENT_COLORS[colorIdx++ % AGENT_COLORS.length],\n sigil: SIGILS[h % SIGILS.length],\n });\n }\n return map.get(name)!;\n };\n}\n\n// ── Text wrapping ─────────────────────────────────────────────────────────────\n\n/**\n * Word-wrap text to a given width, returning an array of lines.\n * Breaks on spaces; words longer than width are force-broken.\n */\nfunction wordWrap(text: string, width: number): string[] {\n if (width <= 0) return [text];\n const result: string[] = [];\n // Handle explicit newlines in the input\n for (const paragraph of text.split(\"\\n\")) {\n if (paragraph.length === 0) { result.push(\"\"); continue; }\n let line = \"\";\n for (const word of paragraph.split(/(\\s+)/)) {\n if (line.length + word.length > width && line.length > 0) {\n result.push(line);\n line = word.replace(/^\\s+/, \"\"); // trim leading space on new line\n } else {\n line += word;\n }\n }\n if (line.length > 0) result.push(line);\n }\n if (result.length === 0) result.push(\"\");\n return result;\n}\n\n// ── Event line ────────────────────────────────────────────────────────────────\n\nconst NAME_COL = 12;\n// Prefix width: paddingX(1) + ts(8) + \" \"(2) + sigil(1) + \" \"(1) + name(12) + \" \"(2) = 27\nconst PREFIX_WIDTH = 27;\n\nfunction EventLine({\n event,\n identify,\n cols,\n}: {\n event: DisplayEvent;\n identify: (n: string) => { color: string; sigil: string };\n cols: number;\n}) {\n const ts = <Text color={C.muted}>{event.ts}{\" \"}</Text>;\n\n // ── Message ──\n if (event.kind === \"message\") {\n const { color, sigil } = identify(event.senderName);\n const isSelf = event.isSelf;\n const nameColor = isSelf ? C.text : event.senderType === \"agent\" ? color : C.secondary;\n const sigilColor = isSelf ? C.dim : event.senderType === \"agent\" ? color : C.dim;\n const sigilChar = isSelf ? \"›\" : event.senderType === \"agent\" ? sigil : \"·\";\n const contentColor = isSelf ? C.text : C.secondary;\n\n const replyPrefix = event.replyToName ? `→ ${event.replyToName} ` : \"\";\n const contentWidth = Math.max(20, cols - PREFIX_WIDTH - 1); // -1 for right padding\n const wrapped = wordWrap(replyPrefix + event.content, contentWidth);\n\n return (\n <Box paddingX={1} flexDirection=\"column\">\n {wrapped.map((line, i) => (\n <Box key={i}>\n {i === 0 ? (\n <Box flexShrink={0}>\n {ts}\n <Text color={sigilColor}>{sigilChar}{\" \"}</Text>\n <Text color={nameColor} bold={isSelf}>\n {event.senderName.slice(0, NAME_COL).padEnd(NAME_COL)}\n </Text>\n <Text>{\" \"}</Text>\n </Box>\n ) : (\n <Text>{\" \".repeat(PREFIX_WIDTH - 1)}</Text>\n )}\n <Text wrap=\"truncate\">\n {i === 0 && replyPrefix ? (\n <>\n <Text color={C.dim}>{replyPrefix}</Text>\n <Text color={contentColor}>{line.slice(replyPrefix.length)}</Text>\n </>\n ) : (\n <Text color={contentColor}>{line}</Text>\n )}\n </Text>\n </Box>\n ))}\n </Box>\n );\n }\n\n // ── Join ──\n if (event.kind === \"join\") {\n const isAgent = event.participantType === \"agent\";\n const { color, sigil } = isAgent ? identify(event.name) : { color: C.dim, sigil: \"·\" };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={isAgent ? color : C.dim}>{sigil}{\" \"}</Text>\n <Text color={isAgent ? color : C.dim}>{event.name}</Text>\n <Text color={C.green}>{\" joined\"}</Text>\n </Box>\n );\n }\n\n // ── Leave ──\n if (event.kind === \"leave\") {\n const isAgent = event.participantType === \"agent\";\n const { color: nameColor } = isAgent ? identify(event.name) : { color: C.muted };\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.muted}>{\"· \"}</Text>\n <Text color={nameColor}>{event.name}</Text>\n <Text color={C.danger}>{\" left\"}</Text>\n </Box>\n );\n }\n\n // ── Mode change ──\n if (event.kind === \"mode\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\"mode → \"}</Text>\n <Text color={C.yellow} bold>{event.mode}</Text>\n </Box>\n );\n }\n\n // ── System message (slash command output) ──\n if (event.kind === \"system\") {\n return (\n <Box paddingX={1}>\n {ts}\n <Text color={C.dim}>{\" \"}</Text>\n <Text color={C.secondary}>{event.content}</Text>\n </Box>\n );\n }\n\n return null;\n}\n\n// ── Internal bridge ───────────────────────────────────────────────────────────\n\ninterface AppHandle {\n push: (event: DisplayEvent) => void;\n setAgentNames: (names: string[]) => void;\n setParticipants: (names: string[]) => void;\n}\n\n// ── App ───────────────────────────────────────────────────────────────────────\n\ntype StaticEntry = { id: string; event?: DisplayEvent };\n\nfunction App({\n roomName,\n onSend,\n onCtrlC,\n onReady,\n readOnly,\n isAdmin,\n version,\n savePath,\n}: {\n roomName: string;\n onSend?: (content: string) => void;\n onCtrlC?: () => void;\n onReady: (handle: AppHandle) => void;\n readOnly?: boolean;\n isAdmin?: boolean;\n version?: string;\n savePath?: string;\n}) {\n const [events, setEvents] = useState<DisplayEvent[]>([]);\n const [agentNames, setAgentNames] = useState<string[]>([]);\n const [participants, setParticipants] = useState<string[]>([]);\n const [input, setInput] = useState(\"\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n const { stdout } = useStdout();\n const identify = useMemo(makeIdentityAssigner, []);\n\n const push = useCallback((event: DisplayEvent) => {\n setEvents((prev) => [...prev, event]);\n }, []);\n\n useEffect(() => {\n onReady({ push, setAgentNames, setParticipants });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Note: no resize handler — Ink's <Static> items are already committed to the\n // terminal buffer. Forcing a re-render on resize causes cursor position\n // miscalculation and screen corruption. The divider width updates naturally\n // on the next state change (new event, input change, etc.).\n\n // ── Slash command suggestions ──────────────────────────────────────────────\n\n type SuggestionItem =\n | { kind: \"command\"; cmd: SlashCommand; insert: string }\n | { kind: \"param\"; value: string; insert: string }\n | { kind: \"mention\"; value: string; insert: string };\n\n const suggestionState = useMemo((): { items: SuggestionItem[]; ghostHint: string } => {\n // @mention detection — match @partial at end of input\n const mentionMatch = input.match(/@([a-zA-Z0-9_-]*)$/);\n if (mentionMatch && participants.length > 0) {\n const prefix = mentionMatch[1].toLowerCase();\n const filtered = participants.filter((p) => p.toLowerCase().startsWith(prefix));\n if (filtered.length > 0) {\n const before = input.slice(0, mentionMatch.index!);\n const items: SuggestionItem[] = filtered.map((p) => ({\n kind: \"mention\" as const,\n value: p,\n insert: before + \"@\" + p + \" \",\n }));\n const ghostHint = prefix.length === 0 ? \"\" : (filtered[0].slice(prefix.length));\n return { items, ghostHint };\n }\n }\n\n if (!input.startsWith(\"/\")) return { items: [], ghostHint: \"\" };\n\n const spaceIdx = input.indexOf(\" \");\n\n // Phase 1: completing command name (no space yet)\n if (spaceIdx === -1) {\n const prefix = input.toLowerCase();\n const items: SuggestionItem[] = SLASH_COMMANDS\n .filter((cmd) => {\n if (cmd.adminOnly && !isAdmin) return false;\n return cmd.name.startsWith(prefix);\n })\n .map((cmd) => ({\n kind: \"command\" as const,\n cmd,\n insert: cmd.name + \" \",\n }));\n return { items, ghostHint: \"\" };\n }\n\n // Phase 2: completing params\n const cmdName = input.slice(0, spaceIdx).toLowerCase();\n const cmd = SLASH_COMMANDS.find((c) => c.name === cmdName && (!c.adminOnly || isAdmin));\n if (!cmd?.params) return { items: [], ghostHint: \"\" };\n\n const rest = input.slice(spaceIdx + 1);\n const words = rest.split(/\\s+/);\n const hasTrailingSpace = rest.endsWith(\" \") || rest === \"\";\n const completedCount = hasTrailingSpace\n ? words.filter(Boolean).length\n : Math.max(0, words.length - 1);\n const currentPrefix = hasTrailingSpace ? \"\" : (words[words.length - 1] ?? \"\").toLowerCase();\n const paramIdx = completedCount;\n\n // All params filled\n if (paramIdx >= cmd.params.length) return { items: [], ghostHint: \"\" };\n\n const param = cmd.params[paramIdx];\n\n // Ghost hint: remaining unfilled params (skip current if partially typed)\n const ghostStart = currentPrefix ? paramIdx + 1 : paramIdx;\n const ghostHint = cmd.params.slice(ghostStart).map((p) => `<${p.label}>`).join(\" \");\n\n // No completions defined — hint only\n if (!param.completions) return { items: [], ghostHint };\n\n const values = param.completions === \"participants\" ? participants : param.completions;\n const filtered = currentPrefix\n ? values.filter((v) => v.toLowerCase().startsWith(currentPrefix))\n : values;\n\n // Build insert string: full command up to current param + selected value\n const completedWords = words.slice(0, completedCount).filter(Boolean);\n const base = cmdName + (completedWords.length ? \" \" + completedWords.join(\" \") : \"\") + \" \";\n\n const items: SuggestionItem[] = filtered.map((v) => ({\n kind: \"param\" as const,\n value: v,\n insert: base + v + \" \",\n }));\n\n return { items, ghostHint };\n }, [input, isAdmin, participants]);\n\n const suggestions = suggestionState.items;\n\n // Reset selection when input changes (arrow keys don't change input)\n useEffect(() => { setSelectedIndex(0); }, [input]);\n\n // ── Keyboard ───────────────────────────────────────────────────────────────\n // Single useInput handles everything — no TextInput, no dual-handler conflicts.\n\n useInput((char, key) => {\n if (key.ctrl && char === \"c\") { onCtrlC?.(); return; }\n if (readOnly || !onSend) return;\n\n // Suggestion navigation\n if (suggestions.length > 0) {\n if (key.downArrow) {\n setSelectedIndex((i) => Math.min(i + 1, suggestions.length - 1));\n return;\n }\n if (key.upArrow) {\n setSelectedIndex((i) => Math.max(i - 1, 0));\n return;\n }\n if (key.return || key.tab) {\n const picked = suggestions[selectedIndex];\n if (!picked) return;\n // No-param command + Enter → submit directly\n if (key.return && picked.kind === \"command\" && !picked.cmd.params) {\n onSend(picked.cmd.name);\n setInput(\"\");\n return;\n }\n setInput(picked.insert);\n return;\n }\n if (key.escape) {\n setInput(\"\");\n return;\n }\n }\n\n // Option+Enter → newline\n if (key.return && key.meta) {\n setInput((prev) => prev + \"\\n\");\n return;\n }\n\n // Enter → submit\n if (key.return) {\n const content = input.trim();\n if (content) onSend(content);\n setInput(\"\");\n return;\n }\n\n // Backspace\n if (key.backspace || key.delete) {\n setInput((prev) => prev.slice(0, -1));\n return;\n }\n\n // Ignore special keys\n if (key.ctrl || key.meta || key.escape || key.tab ||\n key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) {\n return;\n }\n\n // Regular character\n if (char) {\n setInput((prev) => prev + char);\n }\n });\n\n const cols = stdout.columns ?? 80;\n\n // Static items: banner (rendered once) + events (appended over time)\n const entries: StaticEntry[] = useMemo(\n () => [{ id: \"__banner__\" }, ...events.map((e) => ({ id: e.id, event: e }))],\n [events],\n );\n\n return (\n <>\n {/* Permanent output — rendered once, selectable terminal text */}\n <Static items={entries}>\n {(entry) => {\n if (!entry.event) {\n return (\n <Box key={entry.id} flexDirection=\"column\" paddingX={2} paddingTop={1} paddingBottom={1}>\n {BANNER_LINES.map((line, i) => (\n <Text key={i} color={GRADIENT[i]}>{line}</Text>\n ))}\n {version && <Text color={C.dim}>{\" v\"}{version}</Text>}\n <Text>{\" \"}</Text>\n <Text>\n <Text color={C.dim}>{\" room \"}</Text>\n <Text color={C.cyan} bold>{roomName}</Text>\n </Text>\n {savePath && (\n <Text>\n <Text color={C.dim}>{\" saved \"}</Text>\n <Text color={C.secondary}>{savePath}</Text>\n </Text>\n )}\n </Box>\n );\n }\n return <EventLine key={entry.id} event={entry.event} identify={identify} cols={cols} />;\n }}\n </Static>\n\n {/* Dynamic footer — only this area repaints */}\n <Box paddingX={1}>\n <Text color={C.purple}>{\"─\"}</Text>\n <Text color={C.border}>{\"─\".repeat(Math.max(0, cols - 4))}</Text>\n <Text color={C.cyan}>{\"─\"}</Text>\n </Box>\n {agentNames.length > 0 && (\n <Box paddingX={1}>\n {agentNames.map((name, i) => {\n const { color, sigil } = identify(name);\n return (\n <React.Fragment key={name}>\n {i > 0 && <Text color={C.border}>{\" · \"}</Text>}\n <Text color={color}>{sigil}{\" \"}{name}</Text>\n </React.Fragment>\n );\n })}\n </Box>\n )}\n {readOnly || !onSend ? (\n <Box paddingX={1}>\n <Text color={C.muted}>{\" watching as guest\"}</Text>\n </Box>\n ) : (\n <Box paddingX={1} flexDirection=\"column\">\n {/* Render each line; first line gets the prompt, rest get indentation */}\n {(input || \"\").split(\"\\n\").map((line, i, arr) => (\n <Box key={i}>\n <Text color={C.cyan} bold>{i === 0 ? \"› \" : \" \"}</Text>\n <Text>\n {line}\n {i === arr.length - 1 && <Text inverse>{\" \"}</Text>}\n {i === arr.length - 1 && suggestionState.ghostHint !== \"\" && (\n <Text color={C.muted}>{suggestionState.ghostHint}</Text>\n )}\n </Text>\n </Box>\n ))}\n </Box>\n )}\n {/* Slash command suggestions — below input */}\n {suggestions.length > 0 && (\n <Box flexDirection=\"column\" paddingX={1}>\n {suggestions.map((s, i) => {\n const selected = i === selectedIndex;\n if (s.kind === \"command\") {\n const paramHint = s.cmd.params\n ? \" \" + s.cmd.params.map((p) => `<${p.label}>`).join(\" \")\n : \"\";\n const display = s.cmd.name + paramHint;\n return (\n <Box key={s.cmd.name}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n <Text>\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>\n {s.cmd.name}\n </Text>\n <Text color={C.muted}>\n {paramHint.padEnd(CMD_DISPLAY_COL - display.length + paramHint.length)}\n </Text>\n </Text>\n <Text color={C.dim}>{s.cmd.description}</Text>\n </Box>\n );\n }\n return (\n <Box key={s.value}>\n <Text color={selected ? C.cyan : C.muted}>{selected ? \"› \" : \" \"}</Text>\n {s.kind === \"mention\" && <Text color={C.dim}>{\"@\"}</Text>}\n <Text color={selected ? C.cyan : C.secondary} bold={selected}>{s.value}</Text>\n </Box>\n );\n })}\n </Box>\n )}\n </>\n );\n}\n\n// ── startTUI ──────────────────────────────────────────────────────────────────\n\nexport function startTUI(opts: TUIOptions): TUIHandle {\n let handle: AppHandle | null = null;\n const queue: DisplayEvent[] = [];\n\n const onReady = (h: AppHandle) => {\n handle = h;\n for (const event of queue.splice(0)) h.push(event);\n };\n\n const { unmount } = render(\n <App\n roomName={opts.roomName}\n onSend={opts.onSend}\n onCtrlC={opts.onCtrlC}\n onReady={onReady}\n readOnly={opts.readOnly}\n isAdmin={opts.isAdmin}\n version={opts.version}\n savePath={opts.savePath}\n />,\n { exitOnCtrlC: false },\n );\n\n return {\n push(event) {\n if (handle) handle.push(event);\n else queue.push(event);\n },\n setAgentNames(names) {\n handle?.setAgentNames(names);\n },\n setParticipants(names) {\n handle?.setParticipants(names);\n },\n stop() {\n unmount();\n },\n };\n}\n","/**\n * stoops run claude — client-side agent runtime for Claude Code.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Claude-specific pieces: tmux session + TmuxBridge delivery.\n *\n * Claude Code connects to the runtime MCP server via a stdio bridge\n * (Claude's HTTP MCP transport requires OAuth, which hangs on localhost).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { writeFileSync, mkdtempSync, rmSync, chmodSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n} from \"../tmux.js\";\nimport { TmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\n\nexport { type AgentRuntimeOptions as RunClaudeOptions };\n\n/**\n * Stdio-to-HTTP bridge script. Written to a temp file and spawned by Claude Code\n * as an MCP stdio server. Proxies JSON-RPC messages to the runtime HTTP MCP server.\n *\n * The HTTP MCP server returns SSE-formatted responses (event: message\\ndata: {...}).\n * The bridge extracts the JSON from data: lines and writes raw JSON-RPC to stdout.\n */\n// CommonJS (.cjs) for minimal startup latency — ESM requires module parsing which\n// can exceed Claude Code's MCP handshake timeout on first run.\nconst MCP_STDIO_BRIDGE = [\n '#!/usr/bin/env node',\n '\"use strict\";',\n 'const { createInterface } = require(\"readline\");',\n 'const url = `http://127.0.0.1:${process.argv[2]}/mcp`;',\n 'const rl = createInterface({ input: process.stdin });',\n '(async () => {',\n ' for await (const line of rl) {',\n ' if (!line.trim()) continue;',\n ' try {',\n ' const res = await fetch(url, {',\n ' method: \"POST\",',\n ' headers: { \"Content-Type\": \"application/json\", Accept: \"application/json, text/event-stream\" },',\n ' body: line,',\n ' });',\n ' if (res.status === 202) continue;',\n ' const body = await res.text();',\n ' for (const bl of body.split(\"\\\\n\")) {',\n ' const m = bl.match(/^data: (.+)/);',\n ' if (m) process.stdout.write(m[1] + \"\\\\n\");',\n ' }',\n ' } catch {',\n ' process.exit(1);',\n ' }',\n ' }',\n '})();',\n].join('\\n');\n\nexport async function runClaude(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Claude Code agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP stdio bridge + config ────────────────────────────────────\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"stoops_agent_\"));\n\n const bridgePath = join(tmpDir, \"mcp-bridge.cjs\");\n writeFileSync(bridgePath, MCP_STDIO_BRIDGE);\n chmodSync(bridgePath, 0o755);\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpConfigPath = join(tmpDir, \"mcp.json\");\n\n // Use process.execPath (absolute path to Node) so the bridge works regardless\n // of whether `node` is in the tmux session's PATH.\n const mcpConfig = {\n mcpServers: {\n stoops: {\n type: \"stdio\",\n command: process.execPath,\n args: [bridgePath, mcpPort],\n },\n },\n };\n writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));\n\n // ── Create tmux session + launch Claude Code ────────────────────────────\n\n const tmuxSession = `stoops_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n console.log(\"Launching Claude Code...\");\n tmuxCreateSession(tmuxSession);\n\n // Launch claude with MCP config + any passthrough args\n const extraArgs = options.extraArgs ?? [];\n const claudeCmd = [`claude --mcp-config ${mcpConfigPath}`, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, claudeCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new TmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Wait for Claude to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Claude Code exited during startup. Try running again.\");\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n return;\n }\n }\n\n console.log(\"Attaching to Claude Code session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n\n console.log(\"Disconnected.\");\n}\n","/**\n * tmux helpers for stoops CLI.\n *\n * Thin wrappers around tmux commands. Used by the server process to\n * inject room events into Claude Code sessions.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\n\n/** Sanitize a string for use as a tmux session name. Replaces tmux-special chars. */\nfunction sanitizeSessionName(name: string): string {\n return name.replace(/[.:$%]/g, \"_\");\n}\n\n/** Check if tmux is installed and available. */\nexport function tmuxAvailable(): boolean {\n try {\n execFileSync(\"tmux\", [\"-V\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Check if a tmux session exists. */\nexport function tmuxSessionExists(session: string): boolean {\n try {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"has-session\", \"-t\", name], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Create a detached tmux session with no status bar. */\nexport function tmuxCreateSession(session: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"new-session\", \"-d\", \"-s\", name]);\n execFileSync(\"tmux\", [\"set\", \"-t\", name, \"status\", \"off\"]);\n}\n\n/** Send a command to a tmux session (types it + presses Enter). */\nexport function tmuxSendCommand(session: string, command: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"-l\", command]);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"Enter\"]);\n}\n\n/**\n * Inject text into a tmux session (literal keys, no Enter).\n * Used for room event injection.\n */\nexport function tmuxInjectText(session: string, text: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"-l\", text]);\n}\n\n/** Send Enter key to a tmux session (submits input). */\nexport function tmuxSendEnter(session: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, \"Enter\"]);\n}\n\n/** Attach to a tmux session. Returns a promise that resolves when detached/exited.\n *\n * Two modes:\n * - Outside tmux: `tmux attach` (blocks until user detaches, event loop stays free via spawn)\n * - Inside tmux: `tmux switch-client` (exits immediately) + polls until session ends\n */\nexport function tmuxAttach(session: string): Promise<void> {\n const name = sanitizeSessionName(session);\n\n if (process.env.TMUX) {\n // switch-client exits immediately after switching — poll until session is destroyed\n try {\n execFileSync(\"tmux\", [\"switch-client\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n // switch-client failed (e.g. no client) — fall through to polling\n }\n return new Promise<void>((resolve) => {\n const poll = setInterval(() => {\n try {\n execFileSync(\"tmux\", [\"has-session\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n clearInterval(poll);\n resolve();\n }\n }, 500);\n });\n }\n\n return new Promise<void>((resolve) => {\n const child = spawn(\"tmux\", [\"attach\", \"-t\", name], { stdio: \"inherit\" });\n child.on(\"exit\", () => resolve());\n child.on(\"error\", () => resolve());\n });\n}\n\n/** Capture visible screen content as array of lines. */\nexport function tmuxCapturePane(session: string): string[] {\n try {\n const name = sanitizeSessionName(session);\n const output = execFileSync(\"tmux\", [\"capture-pane\", \"-t\", name, \"-p\"], {\n encoding: \"utf-8\",\n });\n return output.split(\"\\n\");\n } catch {\n return [];\n }\n}\n\n/**\n * Send a control key sequence (e.g. \"C-u\", \"C-y\", \"Escape\").\n * Unlike tmuxInjectText, this does NOT use -l, so tmux interprets\n * the key name rather than treating it as literal text.\n */\nexport function tmuxSendKey(session: string, key: string): void {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"send-keys\", \"-t\", name, key]);\n}\n\n/** Kill a tmux session. */\nexport function tmuxKillSession(session: string): void {\n try {\n const name = sanitizeSessionName(session);\n execFileSync(\"tmux\", [\"kill-session\", \"-t\", name], { stdio: \"ignore\" });\n } catch {\n // Session may already be dead\n }\n}\n","/**\n * TmuxBridge — state-aware event injection into Claude Code.\n *\n * Reads the Claude Code TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → inject directly\n * typing → Ctrl+U (cut), inject, Ctrl+Y (restore)\n * dialog → queue and poll\n * permission → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Events that can't be injected immediately are queued and drained\n * when the state becomes safe.\n */\n\nimport {\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type TuiState =\n | \"idle\"\n | \"typing\"\n | \"dialog\"\n | \"permission\"\n | \"streaming\"\n | \"unknown\";\n\n// Spinner characters used by Claude Code during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate a selection/question dialog\nconst DIALOG_PATTERNS = [\n \"Enter to select\",\n \"to navigate\",\n \"Esc to cancel\",\n \"Ready to code?\",\n \"Review your answers\",\n \"ctrl+g to edit in\",\n];\n\n// Patterns that indicate a permission/confirmation prompt\nconst PERMISSION_PATTERNS = [\n \"(Y)\",\n \"Allow \",\n \"Deny \",\n \"approve\",\n \"Yes / No\",\n];\n\nexport interface TmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** How long to wait between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n}\n\nexport class TmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n\n constructor(session: string, opts?: TmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n }\n\n /**\n * Delivery callback — drop-in replacement for the raw tmuxDeliver lambda.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[]): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n * Exported for testing — the heuristic logic is in detectStateFromLines().\n */\n detectState(): TuiState {\n const lines = this.captureScreen();\n return detectStateFromLines(lines);\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * If the state is unsafe, queues the text and starts polling.\n *\n * Text is flattened to a single line before injection to avoid triggering\n * Claude Code's paste detection. When multi-line text arrives via\n * `send-keys -l`, Claude Code detects it as a paste and collapses it into\n * \"[Pasted text #1 +N lines]\" which may not reliably submit with Enter.\n */\n private inject(text: string): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // dialog, permission, streaming, unknown — queue it\n this.enqueue(flat);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt: type text + Enter.\n * Sends a second Enter after a short delay as a safety net — if Claude Code's\n * paste detection swallowed the first Enter, the second one submits. If the\n * first Enter worked, Claude is streaming and the second Enter is a no-op.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.sleep(200);\n tmuxSendEnter(this.session);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut line to kill ring\n * 2. Inject our text + Enter\n * 3. Ctrl+Y — paste the user's text back\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event (double-Enter for paste detection resilience)\n tmuxInjectText(this.session, text);\n tmuxSendEnter(this.session);\n this.sleep(200);\n tmuxSendEnter(this.session);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string): void {\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n *\n * Drains one event at a time rather than batching all into one multi-line\n * string — multi-line text triggers Claude Code's paste detection which\n * collapses it into \"[Pasted text #1 +N lines]\".\n *\n * After injecting one event, the poll continues. The next cycle re-checks\n * state: if Claude is busy (streaming), remaining events wait. If idle,\n * the next event is injected. Events are already flattened in inject().\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n // else: keep polling to drain remaining events\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Claude Code's TUI layout (v2.1+):\n * ────────────────────────\n * ❯ <user input here>\n * ────────────────────────\n * PR #2 /ide ...\n *\n * The ❯ prompt sits between separator lines (─). No ❯❯ footer in v2.1+.\n */\nexport function detectStateFromLines(lines: string[]): TuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~15 lines (the visible bottom of the screen)\n const tail = lines.slice(-15);\n const tailText = tail.join(\"\\n\");\n\n // 1. Dialog: selection/question/plan approval\n for (const pattern of DIALOG_PATTERNS) {\n if (tailText.includes(pattern)) return \"dialog\";\n }\n\n // 2. Permission prompt\n for (const pattern of PERMISSION_PATTERNS) {\n if (tailText.includes(pattern)) return \"permission\";\n }\n\n // 3. Streaming: spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 4. Look for the ❯/› prompt line near the bottom.\n // Supports both old layout (❯❯ footer) and new layout (separator lines).\n const promptChar = /^[❯›](\\s|$)/;\n const footerChar = /^[❯›]{2}\\s/;\n const separatorLine = /^[─━─\\-]{10,}/;\n\n // Strategy A: old layout — find ❯❯ footer then ❯ prompt above it\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (footerChar.test(line)) {\n // Found old-style footer, look for prompt above\n for (let j = i - 1; j >= 0; j--) {\n const above = tail[j].trimStart();\n if (promptChar.test(above)) {\n const content = above.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n break;\n }\n }\n\n // Strategy B: new layout — find ❯ prompt between/near separator lines\n for (let i = tail.length - 1; i >= 0; i--) {\n const line = tail[i].trimStart();\n if (promptChar.test(line)) {\n // Verify it's Claude's prompt by checking for separator line nearby\n const above = i > 0 ? tail[i - 1].trimStart() : \"\";\n const below = i < tail.length - 1 ? tail[i + 1].trimStart() : \"\";\n if (separatorLine.test(above) || separatorLine.test(below)) {\n const content = line.replace(/^[❯›]\\s*/, \"\").trim();\n return content.length === 0 ? \"idle\" : \"typing\";\n }\n }\n }\n\n return \"unknown\";\n}\n","/**\n * Shared agent runtime setup — extracted from cli/claude/run.ts.\n *\n * Both `stoops run claude` and `stoops run opencode` use this to:\n * 1. Create SSE multiplexer + EventProcessor\n * 2. Create local runtime MCP server\n * 3. Wire up participant cache updates\n * 4. Provide cleanup\n *\n * Rooms are NOT joined during setup. The agent joins rooms by calling\n * join_room() via MCP. If --join URLs are provided, the startup event\n * asks the agent to call join_room for each URL.\n *\n * Each runtime only needs to provide its own delivery mechanism\n * (TmuxBridge for Claude, HTTP API for OpenCode).\n */\n\nimport { randomName } from \"../core/names.js\";\nimport { extractToken } from \"./auth.js\";\nimport { RemoteRoomDataSource } from \"../agent/remote-room-data-source.js\";\nimport { SseMultiplexer } from \"../agent/sse-multiplexer.js\";\nimport { EventProcessor } from \"../agent/event-processor.js\";\nimport { createRuntimeMcpServer, type RuntimeMcpServer, type JoinRoomResult } from \"../agent/mcp/runtime.js\";\nimport { buildCatchUpLines } from \"../agent/tool-handlers.js\";\nimport type { Participant } from \"../core/types.js\";\nimport type { LabeledEvent } from \"../agent/multiplexer.js\";\nimport type { ContentPart } from \"../agent/types.js\";\n\n// ── Types ─────────────────────────────────────────────────────────────────────\n\nexport interface AgentRuntimeOptions {\n joinUrls?: string[];\n name?: string;\n admin?: boolean;\n extraArgs?: string[];\n /** Skip tmux/UI — deliver events as plain text to stdout. MCP server still runs. */\n headless?: boolean;\n /** Called after a room is successfully joined via join_room MCP tool. */\n onRoomJoined?: () => void | Promise<void>;\n}\n\nexport interface JoinResult {\n serverUrl: string;\n sessionToken: string;\n participantId: string;\n roomName: string;\n roomId: string;\n authority: string;\n participants: Participant[];\n dataSource: RemoteRoomDataSource;\n}\n\nexport interface AgentRuntimeSetup {\n agentName: string;\n joinResults: JoinResult[];\n initialParts: ContentPart[] | undefined;\n processor: EventProcessor;\n sseMux: SseMultiplexer;\n mcpServer: RuntimeMcpServer;\n wrappedSource: AsyncIterable<LabeledEvent>;\n cleanup(): Promise<void>;\n}\n\n// ── Setup ─────────────────────────────────────────────────────────────────────\n\nexport async function setupAgentRuntime(options: AgentRuntimeOptions): Promise<AgentRuntimeSetup> {\n const agentName = options.name ?? randomName();\n\n // ── Pending join URLs (not joined yet — agent calls join_room) ─────────\n\n const pendingUrls = options.joinUrls ?? [];\n\n // ── Mutable join results (populated as agent calls join_room) ──────────\n\n const joinResults: JoinResult[] = [];\n\n // ── Create SSE multiplexer (starts empty) ──────────────────────────────\n\n const sseMux = new SseMultiplexer();\n\n // ── Create EventProcessor (selfId set on first join_room) ──────────────\n\n const processor = new EventProcessor(\"\", agentName, {\n defaultMode: \"everyone\",\n });\n\n // ── Create local runtime MCP server ───────────────────────────────────\n\n const mcpServer = await createRuntimeMcpServer({\n resolver: processor,\n toolOptions: {\n isEventSeen: (id) => processor.isEventSeen(id),\n markEventsSeen: (ids) => processor.markEventsSeen(ids),\n assignRef: (id) => processor.assignRef(id),\n resolveRef: (ref) => processor.resolveRef(ref),\n },\n admin: options.admin,\n onSetMode: async (room, mode) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n processor.setModeForRoom(conn.dataSource.roomId, mode as any, false);\n try {\n const ds = conn.dataSource as RemoteRoomDataSource;\n const res = await fetch(`${ds.serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, mode }),\n });\n if (!res.ok) return { success: false, error: `Server rejected: ${await res.text()}` };\n } catch {\n // Server unreachable, local mode still set\n }\n return { success: true };\n },\n onJoinRoom: async (url, alias) => {\n const token = extractToken(url);\n let serverUrl: string;\n try {\n const parsed = new URL(url);\n parsed.search = \"\";\n serverUrl = parsed.toString().replace(/\\/$/, \"\");\n } catch {\n serverUrl = url.replace(/\\/$/, \"\");\n }\n\n try {\n const joinBody: Record<string, unknown> = { type: \"agent\", name: agentName };\n if (token) joinBody.token = token;\n\n const res = await fetch(`${serverUrl}/join`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(joinBody),\n signal: AbortSignal.timeout(15_000),\n });\n if (!res.ok) return { success: false, error: `Failed to join: ${await res.text()}` };\n\n const data = await res.json() as Record<string, unknown>;\n const sessionToken = String(data.sessionToken ?? \"\");\n const roomName = alias ?? String(data.roomName ?? \"\");\n const roomId = String(data.roomId ?? \"\");\n const authority = String(data.authority ?? \"member\");\n const participants = (data.participants as Participant[]) ?? [];\n const newParticipantId = String(data.participantId ?? \"\");\n\n const dataSource = new RemoteRoomDataSource(serverUrl, sessionToken, roomId);\n dataSource.setParticipants(participants);\n dataSource.setSelf(newParticipantId, agentName);\n\n // Set global selfId on first join; always set per-room selfId\n if (joinResults.length === 0) {\n processor.participantId = newParticipantId;\n }\n processor.setRoomParticipantId(roomId, newParticipantId);\n\n // Register in EventProcessor and SSE multiplexer\n const mode = processor.getModeForRoom(roomId) ?? \"everyone\";\n processor.connectRemoteRoom(dataSource, roomName);\n sseMux.addConnection(serverUrl, sessionToken, roomName, roomId);\n\n // Track for cleanup\n const jr: JoinResult = {\n serverUrl,\n sessionToken,\n participantId: newParticipantId,\n roomName,\n roomId,\n authority,\n participants,\n dataSource,\n };\n joinResults.push(jr);\n\n // Build recent activity lines for the response\n const conn = processor.resolve(roomName);\n let recentLines: string[] = [];\n if (conn) {\n recentLines = await buildCatchUpLines(conn, {\n isEventSeen: (id) => processor.isEventSeen(id),\n markEventsSeen: (ids) => processor.markEventsSeen(ids),\n assignRef: (id) => processor.assignRef(id),\n });\n }\n\n await options.onRoomJoined?.();\n\n return {\n success: true,\n roomName,\n agentName,\n authority,\n mode,\n participants: participants\n .filter((p) => p.id !== newParticipantId)\n .map((p) => ({ name: p.name, authority: (p as any).authority ?? \"member\" })),\n recentLines,\n } as JoinRoomResult;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return { success: false, error: `Unable to connect. Is the server running? (${serverUrl}) — ${msg}` };\n }\n },\n onLeaveRoom: async (room) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const roomId = conn.dataSource.roomId;\n\n const idx = joinResults.findIndex((jr) => jr.roomId === roomId);\n if (idx >= 0) {\n const jr = joinResults[idx];\n sseMux.removeConnection(roomId);\n processor.disconnectRemoteRoom(roomId);\n\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n\n joinResults.splice(idx, 1);\n }\n return { success: true };\n },\n onAdminSetModeFor: options.admin ? async (room, participant, mode) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-mode`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, mode }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminMute: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, authority: \"guest\" }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminUnmute: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/set-authority`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id, authority: \"member\" }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n onAdminKick: options.admin ? async (room, participant) => {\n const conn = processor.resolve(room);\n if (!conn) return { success: false, error: `Unknown room \"${room}\".` };\n const ds = conn.dataSource as RemoteRoomDataSource;\n\n const p = conn.dataSource.listParticipants().find((pp) => pp.name === participant);\n if (!p) return { success: false, error: `Unknown participant \"${participant}\".` };\n\n try {\n const res = await fetch(`${ds.serverUrl}/kick`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: ds.sessionToken, participantId: p.id }),\n });\n if (!res.ok) return { success: false, error: await res.text() };\n return { success: true };\n } catch {\n return { success: false, error: \"Server unreachable.\" };\n }\n } : undefined,\n });\n\n // ── Wrap SSE source for participant cache updates ─────────────────────\n\n const wrappedSource: AsyncIterable<LabeledEvent> = {\n [Symbol.asyncIterator]() {\n const inner = sseMux[Symbol.asyncIterator]();\n return {\n async next() {\n const result = await inner.next();\n if (!result.done) {\n const { roomId, event } = result.value;\n const jr = joinResults.find((j) => j.roomId === roomId);\n if (jr) {\n if (event.type === \"ParticipantJoined\") {\n jr.dataSource.addParticipant(event.participant);\n } else if (event.type === \"ParticipantLeft\") {\n jr.dataSource.removeParticipant(event.participant_id);\n }\n }\n }\n return result;\n },\n };\n },\n };\n\n // ── Cleanup function ──────────────────────────────────────────────────\n\n async function cleanup(): Promise<void> {\n await processor.stop();\n sseMux.close();\n await mcpServer.stop();\n\n for (const jr of joinResults) {\n try {\n await fetch(`${jr.serverUrl}/disconnect`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ token: jr.sessionToken }),\n });\n } catch {\n // Server may be down\n }\n }\n }\n\n // ── Build startup event (if --join URLs were provided) ────────────────\n\n let initialParts: ContentPart[] | undefined;\n if (pendingUrls.length > 0) {\n if (pendingUrls.length === 1) {\n initialParts = [{ type: \"text\", text: `Use join_room(\"${pendingUrls[0]}\") to connect.` }];\n } else {\n const lines = pendingUrls.map((u) => ` join_room(\"${u}\")`);\n initialParts = [{ type: \"text\", text: `Rooms to join:\\n${lines.join(\"\\n\")}` }];\n }\n }\n\n return {\n agentName,\n joinResults,\n initialParts,\n processor,\n sseMux,\n mcpServer,\n wrappedSource,\n cleanup,\n };\n}\n","/**\n * stoops run opencode — client-side agent runtime for OpenCode.\n *\n * Spawns `opencode serve` with OPENCODE_CONFIG_CONTENT to inject the stoops\n * MCP server. The user opens OpenCode's UI, starts a conversation, and tells\n * the agent to join a room URL.\n *\n * Session detection: we subscribe to OpenCode's global SSE event stream and\n * watch for stoops tool call events. Each event carries the sessionID of the\n * calling session — no guessing, no race conditions.\n */\n\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\n\nexport { type AgentRuntimeOptions as RunOpencodeOptions };\n\nexport async function runOpencode(options: AgentRuntimeOptions): Promise<void> {\n // ── Pick a port for OpenCode ────────────────────────────────────────────\n\n const opencodePort = 14096 + Math.floor(Math.random() * 1000);\n const opencodeUrl = `http://127.0.0.1:${opencodePort}`;\n\n // ── Room → OpenCode session mapping ────────────────────────────────────\n //\n // Each OpenCode session can join different rooms. Lazily detected on first\n // delivery for each room by inspecting session messages for stoops tool calls.\n\n const roomSessions = new Map<string, string>(); // roomId → OpenCode sessionId\n\n /** Find the OpenCode session that most recently called a stoops tool. */\n async function findStoopsSession(): Promise<string | null> {\n try {\n const res = await fetch(`${opencodeUrl}/session`);\n if (!res.ok) return null;\n // Sessions sorted by time.updated desc — first is most recent\n const sessions = await res.json() as Array<{ id: string; time: { updated: number } }>;\n\n // Check the most recently updated sessions for stoops tool parts\n for (const sess of sessions.slice(0, 3)) {\n const msgRes = await fetch(`${opencodeUrl}/session/${sess.id}/message`);\n if (!msgRes.ok) continue;\n const messages = await msgRes.json() as Array<{\n parts?: Array<{ type?: string; tool?: string }>;\n }>;\n for (const msg of messages) {\n for (const part of msg.parts ?? []) {\n if (part.type === \"tool\" && part.tool?.includes(\"stoops__\")) {\n return sess.id;\n }\n }\n }\n }\n // Fallback: most recently updated session\n return sessions.length > 0 ? sessions[0].id : null;\n } catch {\n return null;\n }\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // No --join URLs — the user tells the agent to join from within OpenCode.\n\n const setup = await setupAgentRuntime({\n ...options,\n joinUrls: undefined,\n });\n\n // ── Build MCP config for OpenCode ───────────────────────────────────────\n\n const opencodeConfig = {\n mcp: {\n stoops: {\n type: \"remote\",\n url: setup.mcpServer.url,\n oauth: false,\n },\n },\n };\n\n // ── Spawn OpenCode in headless mode ─────────────────────────────────────\n\n const extraArgs = options.extraArgs ?? [];\n const opencodeArgs = [\"serve\", \"--port\", String(opencodePort), ...extraArgs];\n\n console.log(\"Launching OpenCode...\");\n\n let child: ChildProcess;\n try {\n child = spawn(\"opencode\", opencodeArgs, {\n env: {\n ...process.env,\n OPENCODE_CONFIG_CONTENT: JSON.stringify(opencodeConfig),\n },\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch {\n console.error(\"Error: opencode is required but not found. Install it from https://opencode.ai\");\n await setup.cleanup();\n process.exit(1);\n }\n\n // Forward stderr to console for visibility\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n process.stderr.write(chunk);\n });\n\n let childExited = false;\n child.on(\"exit\", () => { childExited = true; });\n\n child.on(\"error\", async () => {\n console.error(\"Error: failed to start opencode. Is it installed?\");\n await setup.cleanup();\n process.exit(1);\n });\n\n // ── Wait for OpenCode to be ready ───────────────────────────────────────\n\n const ready = await pollForReady(opencodeUrl, 30_000);\n if (!ready) {\n console.error(\"OpenCode did not become ready within 30 seconds.\");\n child.kill();\n await setup.cleanup();\n process.exit(1);\n }\n\n console.log(` OpenCode running on ${opencodeUrl}`);\n\n // ── Build deliver callback ──────────────────────────────────────────────\n //\n // OpenCode's POST /session/:id/message uses Hono stream() — headers (200)\n // arrive immediately but the LLM runs inside the stream callback. We MUST\n // consume the response body (await res.text()) to block until the LLM\n // finishes, preserving the EventProcessor's _processing lock.\n\n async function deliver(parts: ContentPart[]): Promise<void> {\n const roomId = setup.processor.currentContextRoomId;\n if (!roomId) return;\n\n // Lazy session detection: look up on first delivery for this room\n if (!roomSessions.has(roomId)) {\n const sid = await findStoopsSession();\n if (!sid) return;\n roomSessions.set(roomId, sid);\n console.log(` Linked room ${roomId} → session ${sid}`);\n }\n const targetSession = roomSessions.get(roomId)!;\n\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n try {\n const res = await fetch(`${opencodeUrl}/session/${targetSession}/message`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n parts: [{ type: \"text\", text }],\n }),\n });\n await res.text();\n } catch {\n // OpenCode may have exited\n }\n }\n\n // ── Start the event loop ────────────────────────────────────────────────\n // No initialParts — the user drives the conversation from OpenCode's UI.\n\n const eventLoopPromise = setup.processor.run(deliver, setup.wrappedSource);\n\n console.log(`\\n OpenCode agent running.`);\n console.log(` To watch: opencode attach ${opencodeUrl}\\n`);\n\n // ── Block until child exits or Ctrl+C ───────────────────────────────────\n\n const exitPromise = new Promise<void>((resolve) => {\n child.on(\"exit\", resolve);\n });\n\n const signalPromise = new Promise<void>((resolve) => {\n const handler = () => { resolve(); };\n process.on(\"SIGINT\", handler);\n process.on(\"SIGTERM\", handler);\n });\n\n await Promise.race([exitPromise, signalPromise]);\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n if (!childExited) {\n child.kill();\n }\n await setup.cleanup();\n\n console.log(\"Disconnected.\");\n}\n\n// ── Helpers ─────────────────────────────────────────────────────────────────\n\nasync function pollForReady(url: string, timeoutMs: number): Promise<boolean> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const res = await fetch(`${url}/session/status`);\n if (res.ok) return true;\n } catch {\n // Not ready yet\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n return false;\n}\n","/**\n * stoops run codex — client-side agent runtime for OpenAI Codex CLI.\n *\n * Uses the shared runtime setup (EventProcessor, MCP server)\n * then adds Codex-specific pieces: tmux session + CodexTmuxBridge delivery.\n *\n * Codex connects to the runtime MCP server natively via config.toml\n * (no stdio bridge needed — Codex supports remote MCP servers via URL).\n * The agent joins rooms by calling join_room() — no auto-injection needed.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { writeFileSync, mkdtempSync, mkdirSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nimport {\n tmuxAvailable,\n tmuxCreateSession,\n tmuxSendCommand,\n tmuxAttach,\n tmuxKillSession,\n tmuxSessionExists,\n} from \"../tmux.js\";\nimport { CodexTmuxBridge } from \"./tmux-bridge.js\";\nimport { setupAgentRuntime, type AgentRuntimeOptions } from \"../runtime-setup.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\n\nexport { type AgentRuntimeOptions as RunCodexOptions };\n\n/** Check if codex CLI is installed and available. */\nfunction codexAvailable(): boolean {\n try {\n execFileSync(\"codex\", [\"--version\"], { stdio: \"ignore\" });\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function runCodex(options: AgentRuntimeOptions): Promise<void> {\n // ── Headless mode — skip tmux, deliver events as plain text to stdout ────\n\n if (options.headless) {\n const setup = await setupAgentRuntime(options);\n\n const deliver = async (parts: Parameters<typeof contentPartsToString>[0]) => {\n const text = contentPartsToString(parts);\n if (text.trim()) process.stdout.write(text + \"\\n\");\n };\n\n const eventLoopPromise = setup.processor\n .run(deliver, setup.wrappedSource, setup.initialParts)\n .catch(() => {});\n\n process.stderr.write(`MCP server: ${setup.mcpServer.url}\\n`);\n\n await new Promise<void>((resolve) => {\n process.on(\"SIGINT\", resolve);\n process.on(\"SIGTERM\", resolve);\n });\n\n await setup.cleanup();\n await eventLoopPromise;\n return;\n }\n\n // ── Preflight checks ────────────────────────────────────────────────────\n\n if (!tmuxAvailable()) {\n console.error(\"Error: tmux is required but not found. Install it with: brew install tmux\");\n process.exit(1);\n }\n\n if (!codexAvailable()) {\n console.error(\"Error: codex is required but not found. Install it with: npm install -g @openai/codex\");\n process.exit(1);\n }\n\n // ── Shared runtime setup ────────────────────────────────────────────────\n // Don't pass joinUrls — Codex agents join rooms manually via join_room()\n\n const setup = await setupAgentRuntime({ ...options, joinUrls: undefined });\n\n // ── Write MCP config for Codex ──────────────────────────────────────────\n // Codex supports remote MCP servers natively via url in config.toml.\n // No stdio bridge needed (unlike Claude Code which has an OAuth bug).\n\n const tmpDir = mkdtempSync(join(tmpdir(), \"stoops_codex_\"));\n\n const mcpPort = new URL(setup.mcpServer.url).port;\n const mcpUrl = `http://127.0.0.1:${mcpPort}/mcp`;\n\n // Write config.toml in a .codex directory structure\n const codexConfigDir = join(tmpDir, \".codex\");\n mkdirSync(codexConfigDir, { recursive: true });\n\n const configToml = [\n \"[mcp_servers.stoops]\",\n `url = \"${mcpUrl}\"`,\n `startup_timeout_sec = 15`,\n `tool_timeout_sec = 60`,\n ].join(\"\\n\");\n\n writeFileSync(join(codexConfigDir, \"config.toml\"), configToml);\n\n // ── Create tmux session + launch Codex ──────────────────────────────────\n\n const tmuxSession = `stoops_${setup.agentName}`;\n\n if (tmuxSessionExists(tmuxSession)) {\n tmuxKillSession(tmuxSession);\n }\n\n console.log(\"Launching Codex...\");\n tmuxCreateSession(tmuxSession);\n\n // Launch codex with config dir pointing to our temp directory + passthrough args\n const extraArgs = options.extraArgs ?? [];\n const codexCmd = [`CODEX_HOME=${codexConfigDir} codex`, ...extraArgs].join(\" \");\n tmuxSendCommand(tmuxSession, codexCmd);\n\n // ── Start event loop + attach ──────────────────────────────────────────\n\n const bridge = new CodexTmuxBridge(tmuxSession);\n\n // Start the event loop in the background — no initial injection.\n // The agent joins rooms by calling join_room() when the user tells it to.\n const eventLoopPromise = setup.processor.run(bridge.deliver.bind(bridge), setup.wrappedSource)\n .catch(() => {}); // Prevent unhandled rejection from crashing the process\n\n // Wait for Codex to start, checking the session is still alive\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!tmuxSessionExists(tmuxSession)) {\n console.error(\"Error: Codex exited during startup. Try running again.\");\n bridge.stop();\n await setup.cleanup();\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n return;\n }\n }\n\n console.log(\"Attaching to Codex session...\\n\");\n\n try {\n await tmuxAttach(tmuxSession);\n } catch {\n // User detached or session ended\n }\n\n // ── Cleanup ─────────────────────────────────────────────────────────────\n\n bridge.stop();\n await setup.cleanup();\n tmuxKillSession(tmuxSession);\n try { rmSync(tmpDir, { recursive: true }); } catch { /* ok */ }\n\n console.log(\"Disconnected.\");\n}\n","/**\n * CodexTmuxBridge — state-aware event injection into Codex CLI.\n *\n * Reads the Codex TUI screen via `tmux capture-pane`, detects the\n * current UI state, and applies the right injection strategy:\n *\n * idle → bracketed paste + Enter\n * typing → Ctrl+U (cut), bracketed paste + Enter, Ctrl+Y (restore)\n * approval → queue and poll\n * streaming → queue and poll\n * unknown → queue and poll (safe default)\n *\n * Uses bracketed paste escape sequences to bypass Codex's timing-based\n * paste-burst detector (120ms Enter suppression window). Text wrapped\n * in ESC[200~...ESC[201~ is delivered as a Paste event, not individual\n * keystrokes, so the burst detector never fires.\n */\n\nimport {\n tmuxCapturePane,\n tmuxInjectText,\n tmuxSendEnter,\n tmuxSendKey,\n} from \"../tmux.js\";\nimport { contentPartsToString } from \"../../agent/prompts.js\";\nimport type { ContentPart } from \"../../agent/types.js\";\n\nexport type CodexTuiState =\n | \"idle\"\n | \"typing\"\n | \"approval\"\n | \"streaming\"\n | \"unknown\";\n\n// Braille spinner characters used by Codex during streaming\nconst SPINNER_CHARS = \"⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏\";\n\n// Patterns that indicate an approval overlay\nconst APPROVAL_PATTERNS = [\n \"Would you like to\",\n \"needs your approval\",\n \"Press Enter to confirm or Esc to cancel\",\n \"Do you want to approve\",\n];\n\n// Patterns that indicate the agent is actively working\nconst STREAMING_PATTERNS = [\n /Working\\s*\\(\\d+[smh]/, // \"Working (12s\" or \"Working (1m 30s\"\n /Working\\s*$/, // \"Working\" at end of line (just started)\n /esc to interrupt/, // hint text during streaming\n];\n\nexport interface CodexTmuxBridgeOptions {\n /** How often to poll when events are queued (ms). Default: 200 */\n pollIntervalMs?: number;\n /** Delay after bracketed paste before sending Enter (ms). Default: 150 */\n pasteDelayMs?: number;\n /** Delay between Ctrl+U/inject/Ctrl+Y steps (ms). Default: 50 */\n keystrokeDelayMs?: number;\n}\n\nexport class CodexTmuxBridge {\n private session: string;\n private queue: string[] = [];\n private pollTimer: ReturnType<typeof setInterval> | null = null;\n private pollIntervalMs: number;\n private pasteDelayMs: number;\n private keystrokeDelayMs: number;\n private stopped = false;\n\n constructor(session: string, opts?: CodexTmuxBridgeOptions) {\n this.session = session;\n this.pollIntervalMs = opts?.pollIntervalMs ?? 200;\n this.pasteDelayMs = opts?.pasteDelayMs ?? 150;\n this.keystrokeDelayMs = opts?.keystrokeDelayMs ?? 50;\n }\n\n /**\n * Delivery callback — drop-in replacement for EventProcessor's deliver.\n * Pass `bridge.deliver.bind(bridge)` to EventProcessor.run().\n */\n async deliver(parts: ContentPart[]): Promise<void> {\n const text = contentPartsToString(parts);\n if (!text.trim()) return;\n\n this.inject(text);\n }\n\n /**\n * Detect the current TUI state by reading the screen.\n */\n detectState(): CodexTuiState {\n const lines = this.captureScreen();\n return detectCodexStateFromLines(lines);\n }\n\n /**\n * Try to inject text, choosing strategy based on TUI state.\n * Text is flattened to a single line to avoid multi-line paste issues.\n */\n private inject(text: string): void {\n const flat = text.replace(/\\n/g, \" \");\n const state = this.detectState();\n\n switch (state) {\n case \"idle\":\n this.injectIdle(flat);\n break;\n case \"typing\":\n this.injectWhileTyping(flat);\n break;\n default:\n // approval, streaming, unknown — queue it\n this.enqueue(flat);\n break;\n }\n }\n\n /** Capture the screen via tmux capture-pane. */\n private captureScreen(): string[] {\n return tmuxCapturePane(this.session);\n }\n\n /**\n * Inject into an idle prompt using bracketed paste.\n *\n * Bracketed paste wraps text in ESC[200~...ESC[201~ so crossterm\n * delivers it as a single Paste event, bypassing the burst detector.\n * After the paste, we wait for the Enter suppression window (120ms)\n * to expire, then send Enter to submit.\n */\n private injectIdle(text: string): void {\n tmuxInjectText(this.session, \"\\x1b[200~\");\n tmuxInjectText(this.session, text);\n tmuxInjectText(this.session, \"\\x1b[201~\");\n this.sleep(this.pasteDelayMs);\n tmuxSendEnter(this.session);\n }\n\n /**\n * Inject while the user is typing:\n * 1. Ctrl+U — cut to beginning of line (into kill buffer)\n * 2. Bracketed paste our text + Enter\n * 3. Ctrl+Y — yank user's text back from kill buffer\n */\n private injectWhileTyping(text: string): void {\n // Cut user's current input\n tmuxSendKey(this.session, \"C-u\");\n this.sleep(this.keystrokeDelayMs);\n\n // Inject our event via bracketed paste\n tmuxInjectText(this.session, \"\\x1b[200~\");\n tmuxInjectText(this.session, text);\n tmuxInjectText(this.session, \"\\x1b[201~\");\n this.sleep(this.pasteDelayMs);\n tmuxSendEnter(this.session);\n this.sleep(this.keystrokeDelayMs);\n\n // Restore user's text\n tmuxSendKey(this.session, \"C-y\");\n }\n\n /** Add to queue and start polling if not already. */\n private enqueue(text: string): void {\n this.queue.push(text);\n this.startPolling();\n }\n\n /** Start the polling timer to drain queued events. */\n private startPolling(): void {\n if (this.pollTimer || this.stopped) return;\n this.pollTimer = setInterval(() => this.drainQueue(), this.pollIntervalMs);\n }\n\n /** Stop the polling timer. */\n private stopPolling(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n }\n\n /**\n * Try to drain one queued event if the state is safe.\n * Drains one at a time — each injection may trigger streaming,\n * so the next poll re-checks state before injecting more.\n */\n private drainQueue(): void {\n if (this.queue.length === 0) {\n this.stopPolling();\n return;\n }\n\n const state = this.detectState();\n if (state === \"idle\" || state === \"typing\") {\n const text = this.queue.shift()!;\n\n if (state === \"idle\") {\n this.injectIdle(text);\n } else {\n this.injectWhileTyping(text);\n }\n\n if (this.queue.length === 0) {\n this.stopPolling();\n }\n }\n // else: still blocked, keep polling\n }\n\n /** Cleanup. */\n stop(): void {\n this.stopped = true;\n this.stopPolling();\n this.queue.length = 0;\n }\n\n /** Synchronous sleep — only used for tiny keystroke delays. */\n private sleep(ms: number): void {\n if (ms <= 0) return;\n Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);\n }\n}\n\n// ── State detection heuristics ──────────────────────────────────────────────\n\n/**\n * Detect Codex TUI state from capture-pane output lines.\n * Exported separately so it can be unit-tested without tmux.\n *\n * Codex's TUI (Ratatui-based) uses an inline viewport. The screen shows:\n * - Conversation history in the top area\n * - Status indicator (\"Working (12s * esc to interrupt)\") when streaming\n * - Approval overlay when tool/patch approval needed\n * - Composer input area at the bottom\n *\n * Detection priority: approval > streaming > idle/typing > unknown\n */\nexport function detectCodexStateFromLines(lines: string[]): CodexTuiState {\n if (lines.length === 0) return \"unknown\";\n\n // Work with the last ~20 lines (visible bottom of screen)\n const tail = lines.slice(-20);\n const tailText = tail.join(\"\\n\");\n\n // 1. Approval overlay — highest priority\n for (const pattern of APPROVAL_PATTERNS) {\n if (tailText.includes(pattern)) return \"approval\";\n }\n\n // 2. Streaming — agent is working\n for (const pattern of STREAMING_PATTERNS) {\n if (pattern.test(tailText)) return \"streaming\";\n }\n\n // Check for spinner characters in the last few lines\n const lastFew = tail.slice(-5).join(\"\");\n for (const ch of SPINNER_CHARS) {\n if (lastFew.includes(ch)) return \"streaming\";\n }\n\n // 3. Idle/Typing — look for the composer input area at the bottom.\n // Codex renders the composer as the last interactive element.\n // When idle, the bottom lines contain just the placeholder or empty input.\n // When typing, the bottom lines contain user-entered text.\n //\n // Heuristic: if none of the blocking states are detected (approval,\n // streaming), and the screen has content, assume the composer is\n // available. Check the last non-empty line for signs of user input.\n //\n // This is intentionally permissive — the worst case for a false\n // \"idle\" is that the injected text arrives during an unexpected\n // state and queues up in the input buffer harmlessly.\n const nonEmpty = tail.filter((l) => l.trim().length > 0);\n if (nonEmpty.length > 0) {\n // Look for signs that the screen is showing normal conversation + composer.\n // If we didn't match approval or streaming above, the composer is likely visible.\n // We need to distinguish idle (empty composer) from typing (text in composer).\n //\n // Codex shows a cursor line at the very bottom. Without empirical data on\n // exact patterns, we check if the last non-empty line looks like user input\n // (not part of conversation output which typically has structure like timestamps,\n // tool names, or markdown formatting).\n //\n // For now: if no blocking state is detected, return \"idle\" as the safe\n // injectable state. The bracketed paste approach is resilient enough that\n // injecting into a \"typing\" state via the idle path still works (text gets\n // appended to whatever the user was typing, and Enter submits all of it).\n return \"idle\";\n }\n\n return \"unknown\";\n}\n"],"mappings":"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","names":["createRequire","id","channel","sessionToken","participantList","require","randomUUID","filtered","items","ghostHint","cmd","randomUUID","args","join","tmpdir","execFileSync","spawn","setup","eventLoopPromise","join","tmpdir","spawn","spawn","execFileSync","writeFileSync","mkdtempSync","rmSync","join","tmpdir","SPINNER_CHARS","execFileSync","setup","eventLoopPromise","mkdtempSync","join","tmpdir","writeFileSync","rmSync","require","createRequire"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stoops",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Multiplayer rooms for AI agents",
5
5
  "keywords": [
6
6
  "ai",