@scp-dev/cli 0.2.4 → 0.3.1

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.
Files changed (28) hide show
  1. package/dist/{chunk-E7JG5PJB.js → chunk-2LIJXXTL.js} +53 -9
  2. package/dist/{chunk-W3VNCRBX.js → chunk-2OSUVJ2J.js} +3 -3
  3. package/dist/{chunk-IXCHAWOL.js → chunk-3SHVJIXP.js} +5 -3
  4. package/dist/{chunk-ZDU53ZRF.js → chunk-IEFXYBX7.js} +6 -3
  5. package/dist/{chunk-ZMVFN6MU.js → chunk-Q5UI4Z7H.js} +2 -2
  6. package/dist/{chunk-X4ZLEM47.js → chunk-QRWQIHV6.js} +56 -8
  7. package/dist/claude-EQVLST73.js +130 -0
  8. package/dist/cli.js +1 -1
  9. package/dist/{codex-4IIM6ZE2.js → codex-5GZ7YPES.js} +73 -24
  10. package/dist/{hooks-P4J6OZCO.js → hooks-B34E2HIP.js} +6 -2
  11. package/dist/{install-FKGGPT6L.js → install-6KXJY27V.js} +3 -3
  12. package/dist/{join-RGFHOSLE.js → join-FITJVZCA.js} +3 -1
  13. package/dist/{login-IPS75GLQ.js → login-AUMLI4FS.js} +2 -2
  14. package/dist/{main-MCNGHJP2.js → main-ZAYVXFAG.js} +63 -27
  15. package/dist/{mcp-TUWMLGE4.js → mcp-VXBRMQB7.js} +14 -6
  16. package/dist/{mesh-VNZV3POO.js → mesh-H4KL5XAR.js} +1 -1
  17. package/dist/opencode-ORLGNHWR.js +116 -0
  18. package/dist/{peek-4D4VGKOW.js → peek-YTCWVPF7.js} +1 -1
  19. package/dist/{pi-JZ4DXY7V.js → pi-QEZVFDNR.js} +8 -10
  20. package/dist/pi-extension.js +22 -7
  21. package/dist/{relay-U634RHIJ.js → relay-QWJM372C.js} +1 -1
  22. package/dist/{remote-NTHRHVEP.js → remote-P4GFUIAP.js} +2 -2
  23. package/dist/{setup-RO7356M4.js → setup-MGDWKCNU.js} +4 -4
  24. package/dist/{spawn-BYU2X46L.js → spawn-GG7VFZA6.js} +9 -5
  25. package/dist/up-B2NEOOC6.js +57 -0
  26. package/package.json +6 -6
  27. package/dist/claude-JD7WXMAT.js +0 -98
  28. package/dist/up-GDD24GPL.js +0 -35
@@ -1,18 +1,20 @@
1
1
  import {
2
+ CODEX_VALUED,
2
3
  codexMount,
3
- codexOverrides
4
- } from "./chunk-IXCHAWOL.js";
4
+ codexOverrides,
5
+ toml
6
+ } from "./chunk-3SHVJIXP.js";
5
7
  import "./chunk-KN7WEIE4.js";
6
- import "./chunk-W3VNCRBX.js";
7
- import "./chunk-ZMVFN6MU.js";
8
- import "./chunk-X4ZLEM47.js";
8
+ import "./chunk-2OSUVJ2J.js";
9
+ import "./chunk-Q5UI4Z7H.js";
10
+ import "./chunk-QRWQIHV6.js";
9
11
  import "./chunk-YYSLYVNZ.js";
10
12
  import {
11
13
  RelayUsage,
12
14
  readPsTable,
13
15
  relayIdentity,
14
16
  startDaemon
15
- } from "./chunk-E7JG5PJB.js";
17
+ } from "./chunk-2LIJXXTL.js";
16
18
  import {
17
19
  SELF
18
20
  } from "./chunk-MNTIEWKF.js";
@@ -20,27 +22,30 @@ import {
20
22
  // src/codex.ts
21
23
  import { spawn } from "child_process";
22
24
  import { createHash, randomBytes } from "crypto";
23
- import { mkdtempSync, rmSync } from "fs";
25
+ import { existsSync, mkdtempSync, readFileSync, rmSync } from "fs";
24
26
  import { connect } from "net";
25
- import { tmpdir } from "os";
27
+ import { homedir, tmpdir } from "os";
26
28
  import { join } from "path";
27
29
  import { PassThrough } from "stream";
28
30
  import { setTimeout as sleep } from "timers/promises";
29
31
  import { connectBroker, relayDir } from "@scp-dev/broker";
30
32
  import { CLI_NAME, validHandle } from "@scp-dev/core";
31
33
  import { MCP_PROTOCOLS, runRelayMcp } from "@scp-dev/mcp";
34
+ import { parse } from "smol-toml";
32
35
  var CODEX_HELP = `${CLI_NAME} codex [--name N | --as N] [--headless] [--resume THREAD] [--] [codex args...]
36
+ every codex argument, profile and config value is honoured; a -- after codex arguments is passed through.
33
37
  runs codex with relay messages waking an idle thread or steering a running turn; approvals stay with the TUI.
34
38
  --headless runs an ambient worker without a TUI; --resume THREAD reopens its thread.
35
39
  $SCP_CODEX_BIN picks the codex binary (default codex on PATH).`;
36
40
  function codexLaunch(args, env, _self) {
37
41
  let name;
42
+ let resume;
38
43
  let headless = false;
39
44
  const codexArgs = [];
40
45
  for (let i = 0; i < args.length; i++) {
41
46
  const arg = args[i];
42
47
  if (arg === "--") {
43
- codexArgs.push(...args.slice(i + 1));
48
+ codexArgs.push(...args.slice(codexArgs.length ? i : i + 1));
44
49
  break;
45
50
  }
46
51
  if (arg === "--name" || arg === "--as") {
@@ -48,6 +53,10 @@ function codexLaunch(args, env, _self) {
48
53
  if (name === void 0) throw new RelayUsage(`${CLI_NAME} codex ${arg} takes a name`);
49
54
  } else if (arg.startsWith("--name=") || arg.startsWith("--as=")) name = arg.slice(arg.indexOf("=") + 1);
50
55
  else if (arg === "--headless") headless = true;
56
+ else if (arg === "--resume") {
57
+ resume = args[++i];
58
+ if (resume === void 0) throw new RelayUsage(`${CLI_NAME} codex --resume takes a thread`);
59
+ } else if (arg.startsWith("--resume=")) resume = arg.slice("--resume=".length);
51
60
  else codexArgs.push(arg);
52
61
  }
53
62
  name = name?.replace(/^me\//, "");
@@ -56,7 +65,8 @@ function codexLaunch(args, env, _self) {
56
65
  return {
57
66
  ...name !== void 0 ? { name } : {},
58
67
  headless,
59
- codexArgs,
68
+ ...resume !== void 0 ? { resume } : {},
69
+ codexArgs: resume !== void 0 && !headless ? ["resume", resume, ...codexArgs] : codexArgs,
60
70
  env: { ...clean, SCP_HARNESS: "codex", ...name !== void 0 ? { SCP_NAME: name } : {} }
61
71
  };
62
72
  }
@@ -314,18 +324,57 @@ function option(args, ...names) {
314
324
  }
315
325
  return value;
316
326
  }
317
- function serverOptions(args) {
318
- const result = [];
319
- const names = ["-c", "--config", "--enable", "--disable"];
327
+ var SERVER_CONFIG = ["-c", "--config", "--enable", "--disable"];
328
+ var SERVER_SETTINGS = {
329
+ "-m": "model",
330
+ "--model": "model",
331
+ "-s": "sandbox_mode",
332
+ "--sandbox": "sandbox_mode",
333
+ "-a": "approval_policy",
334
+ "--ask-for-approval": "approval_policy"
335
+ };
336
+ function leaves(value, path, out) {
337
+ if (value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date)) {
338
+ for (const [key, child] of Object.entries(value)) leaves(child, [...path, key], out);
339
+ } else if (path.length) out.push("-c", `${path.join(".")}=${toml(value instanceof Date ? value.toISOString() : value)}`);
340
+ }
341
+ function serverOptions(args, env, warn = (line) => void process.stderr.write(`${line}
342
+ `)) {
343
+ const profile = [];
344
+ const config = [];
345
+ const settings = [];
346
+ const set = (key, value) => settings.push("-c", `${key}=${toml(value)}`);
347
+ let conflict = false;
320
348
  for (let i = 0; i < args.length; i++) {
321
349
  const arg = args[i];
322
- if (names.includes(arg)) {
323
- const value = args[++i];
324
- if (value === void 0) throw new RelayUsage(`${CLI_NAME} codex ${arg} takes a value`);
325
- result.push(arg, value);
326
- } else if (names.some((name) => arg.startsWith(`${name}=`))) result.push(arg);
350
+ if (arg === "--") break;
351
+ const eq = arg.startsWith("--") ? arg.indexOf("=") : -1;
352
+ const flag = eq > 0 ? arg.slice(0, eq) : arg;
353
+ if (flag === "--strict-config") config.push(flag);
354
+ else if (flag === "--dangerously-bypass-approvals-and-sandbox" || flag === "--yolo") {
355
+ set("approval_policy", "never");
356
+ set("sandbox_mode", "danger-full-access");
357
+ }
358
+ if (!CODEX_VALUED.has(flag)) continue;
359
+ const value = eq > 0 ? arg.slice(eq + 1) : args[++i];
360
+ if (value === void 0) throw new RelayUsage(`${CLI_NAME} codex ${flag} takes a value`);
361
+ if (SERVER_CONFIG.includes(flag)) {
362
+ config.push(flag, value);
363
+ if (/^mcp_servers\.("scp"|scp)\./.test(value)) conflict = true;
364
+ } else if (flag === "-p" || flag === "--profile") {
365
+ const file = join(env.CODEX_HOME || join(env.HOME || homedir(), ".codex"), `${value}.config.toml`);
366
+ profile.length = 0;
367
+ if (existsSync(file)) {
368
+ try {
369
+ leaves(parse(readFileSync(file, "utf8")), [], profile);
370
+ } catch (error) {
371
+ warn(`${CLI_NAME} codex: could not read profile ${file}: ${error instanceof Error ? error.message : String(error)}`);
372
+ }
373
+ }
374
+ } else if (SERVER_SETTINGS[flag]) set(SERVER_SETTINGS[flag], value);
327
375
  }
328
- return result;
376
+ if (conflict) warn(`${CLI_NAME} codex: your -c mcp_servers.scp settings override the ones ${CLI_NAME} sets, so messages may not wake this session`);
377
+ return [...profile, ...config, ...settings];
329
378
  }
330
379
  async function terminate(child) {
331
380
  if (child.exitCode !== null || child.signalCode !== null || !child.pid) return;
@@ -350,8 +399,7 @@ function appServerPid(pid) {
350
399
  return pid;
351
400
  }
352
401
  async function codexCommand(args) {
353
- const own = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
354
- if (own.includes("--help") || own.includes("-h")) {
402
+ if (args[0] === "--help" || args[0] === "-h") {
355
403
  process.stdout.write(`${CODEX_HELP}
356
404
  `);
357
405
  return 0;
@@ -359,7 +407,7 @@ async function codexCommand(args) {
359
407
  const launch = codexLaunch(args, process.env, SELF);
360
408
  const cwd = process.cwd();
361
409
  const bin = launch.env.SCP_CODEX_BIN || "codex";
362
- const overrides = [...codexOverrides(launch.env, codexMount(launch.env), SELF), ...serverOptions(launch.codexArgs)];
410
+ const overrides = [...codexOverrides(launch.env, codexMount(launch.env), SELF), ...serverOptions(launch.codexArgs, launch.env)];
363
411
  const dir = mkdtempSync(join(tmpdir(), "scp-codex-"));
364
412
  const path = join(dir, "app.sock");
365
413
  const server = spawn(bin, ["app-server", "--listen", `unix://${path}`, ...overrides], { env: launch.env, stdio: ["ignore", "ignore", "pipe"] });
@@ -448,7 +496,7 @@ ${stderr.toString().trim()}` : ""}`
448
496
  if (!socket || died) throw failure();
449
497
  let threadId;
450
498
  if (launch.headless) {
451
- const resume = option(own, "--resume");
499
+ const resume = launch.resume;
452
500
  const model = option(launch.codexArgs, "-m", "--model");
453
501
  const sandbox = option(launch.codexArgs, "-s", "--sandbox");
454
502
  const result = await socket.request(resume ? "thread/resume" : "thread/start", {
@@ -562,5 +610,6 @@ export {
562
610
  CodexSocket,
563
611
  CodexWaker,
564
612
  codexCommand,
565
- codexLaunch
613
+ codexLaunch,
614
+ serverOptions
566
615
  };
@@ -2,6 +2,7 @@ import {
2
2
  AGY_HOOK_SET,
3
3
  AGY_UNVERIFIED,
4
4
  BLOCK_STOP,
5
+ CLAUDE_CAPS,
5
6
  DIRECTIVE_CLOSE,
6
7
  HOOK_COMMAND,
7
8
  NO_INJECT,
@@ -15,6 +16,7 @@ import {
15
16
  WAKE,
16
17
  agyHookSet,
17
18
  claudeVersionFile,
19
+ fitText,
18
20
  hasOurHooks,
19
21
  hookEntries,
20
22
  hookReply,
@@ -38,16 +40,17 @@ import {
38
40
  tldrEvery,
39
41
  transcriptTitle,
40
42
  unreadPointer
41
- } from "./chunk-X4ZLEM47.js";
43
+ } from "./chunk-QRWQIHV6.js";
42
44
  import {
43
45
  SUBAGENT
44
46
  } from "./chunk-YYSLYVNZ.js";
45
- import "./chunk-E7JG5PJB.js";
47
+ import "./chunk-2LIJXXTL.js";
46
48
  import "./chunk-MNTIEWKF.js";
47
49
  export {
48
50
  AGY_HOOK_SET,
49
51
  AGY_UNVERIFIED,
50
52
  BLOCK_STOP,
53
+ CLAUDE_CAPS,
51
54
  DIRECTIVE_CLOSE,
52
55
  HOOK_COMMAND,
53
56
  NO_INJECT,
@@ -62,6 +65,7 @@ export {
62
65
  WAKE,
63
66
  agyHookSet,
64
67
  claudeVersionFile,
68
+ fitText,
65
69
  hasOurHooks,
66
70
  hookEntries,
67
71
  hookReply,
@@ -2,10 +2,10 @@ import {
2
2
  hooksTarget,
3
3
  installHooks,
4
4
  mcpInstall
5
- } from "./chunk-ZMVFN6MU.js";
6
- import "./chunk-X4ZLEM47.js";
5
+ } from "./chunk-Q5UI4Z7H.js";
6
+ import "./chunk-QRWQIHV6.js";
7
7
  import "./chunk-YYSLYVNZ.js";
8
- import "./chunk-E7JG5PJB.js";
8
+ import "./chunk-2LIJXXTL.js";
9
9
  import "./chunk-MNTIEWKF.js";
10
10
  export {
11
11
  hooksTarget,
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  RelayUsage,
3
+ harnessAt,
3
4
  localHandle,
4
5
  relayIdentity,
5
6
  sessionKey,
6
7
  startDaemon,
7
8
  walkNow
8
- } from "./chunk-E7JG5PJB.js";
9
+ } from "./chunk-2LIJXXTL.js";
9
10
  import "./chunk-MNTIEWKF.js";
10
11
 
11
12
  // src/join.ts
@@ -27,6 +28,7 @@ async function joinCommand(values, positionals, cwd, out) {
27
28
  cwd,
28
29
  env: process.env,
29
30
  harnessPid: () => Number(match[1]),
31
+ inferHarness: (pid) => harnessAt(pid),
30
32
  startOf: () => match[2],
31
33
  lineageOf: () => keys.slice(Math.max(0, keys.indexOf(key)))
32
34
  });
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  machineName
3
- } from "./chunk-ZDU53ZRF.js";
3
+ } from "./chunk-IEFXYBX7.js";
4
4
  import {
5
5
  RelayUsage,
6
6
  activeRelay,
7
7
  saveCredential
8
- } from "./chunk-E7JG5PJB.js";
8
+ } from "./chunk-2LIJXXTL.js";
9
9
  import {
10
10
  note
11
11
  } from "./chunk-MNTIEWKF.js";
@@ -15,7 +15,7 @@ import {
15
15
  stamp,
16
16
  startDaemon,
17
17
  time
18
- } from "./chunk-E7JG5PJB.js";
18
+ } from "./chunk-2LIJXXTL.js";
19
19
  import {
20
20
  FAILED,
21
21
  exitFlushed,
@@ -26,6 +26,7 @@ import {
26
26
 
27
27
  // src/main.ts
28
28
  import { text as readAll } from "stream/consumers";
29
+ import { readFileSync } from "fs";
29
30
  import { homedir } from "os";
30
31
  import { resolve } from "path";
31
32
  import { parseArgs } from "util";
@@ -33,7 +34,7 @@ import { parseArgs } from "util";
33
34
  // package.json
34
35
  var package_default = {
35
36
  name: "@scp-dev/cli",
36
- version: "0.2.4",
37
+ version: "0.3.1",
37
38
  description: "The swarm command line tool.",
38
39
  license: "Apache-2.0",
39
40
  homepage: "https://scp.dev",
@@ -137,6 +138,8 @@ usage:
137
138
  ${CLI_NAME} inbox [--wait S] [--keep] your unread messages, then marked read (--keep leaves them unread); --wait waits up
138
139
  to S seconds (max 50)
139
140
  ${CLI_NAME} ack <id|seq>... | --all mark messages read, by message id or seq; --all marks every unread one
141
+ ${CLI_NAME} show <id> one message delivered to you, whole, read or not; it stays as it is (a hook
142
+ shows a long message cut, ending in [truncated, N more chars: ${CLI_NAME} show <id>])
140
143
  An <id> is a message id, its short id (the last 6 characters, shown with every message) or any unique suffix of at least 4,
141
144
  or last: the newest message you were shown, refused while a newer one is unseen.
142
145
  ${CLI_NAME} ask <to> [text|-] [--wait S] send a request and wait for its reply (default 50 s); --require-reply holds
@@ -236,6 +239,9 @@ scopes (selectors are comma-separated, e.g. tree:parent,tree:children,group:team
236
239
  message as a turn (steering a running one, waking an idle session)
237
240
  ${CLI_NAME} codex [--name N] [--headless] [--] [codex args...]
238
241
  runs codex with relay messages waking an idle thread or steering a running turn
242
+ ${CLI_NAME} opencode [--name N] [--] [opencode args...]
243
+ runs OpenCode with your own config and arguments, wake on: the plugin wakes an
244
+ idle session, or opens one; refuses to start without the current plugin
239
245
  ${CLI_NAME} peers [--all] [--roots] who you may message: title, status and the rule that admits each; offline ones and
240
246
  sessions nobody spawned that spawned nobody only with --all (--roots: those sessions)
241
247
  ${CLI_NAME} why <from> <to> whether from may message to, and why
@@ -268,7 +274,7 @@ agent sessions:
268
274
  A Claude Code subagent's hooks give it a child endpoint, <session handle>-<short type>-<first 4 of its id>, whose messages
269
275
  reach it after its tools; its shell is the session's, so it acts as that child with --as.
270
276
  Grok runs the Claude Code hooks, labelled grok; Muse takes them as a plugin; Codex runs new hooks after /hooks review.
271
- ${CLI_NAME} claude [--name N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake
277
+ ${CLI_NAME} claude [--name N | --as N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake
272
278
  it while idle (claude --dangerously-load-development-channels server:scp; SCP_NAME=N)
273
279
  --channels pushes messages into a Claude Code that ${CLI_NAME} claude started; setup mounts claude's shim with it.
274
280
  Without --name, ${CLI_NAME} claude asks the session to pick its own name with ${CLI_NAME} name <name>.
@@ -299,7 +305,8 @@ agent sessions:
299
305
  relay:
300
306
  ${CLI_NAME} relay use <url> | show select a relay, or show its source and credential status
301
307
  ${CLI_NAME} login [--no-browser] [--token T] [--label NAME] sign in through the browser, or save a token checked with the relay
302
- ${CLI_NAME} up [--token T] [--machine NAME] [--no-browser] log in if needed, link the broker to the hub, run setup --write
308
+ ${CLI_NAME} up [--token T] [--machine NAME] [--no-browser] log in if needed, link the broker to the hub, run setup --write,
309
+ then print the wake-enabled launch of each installed harness
303
310
  ${CLI_NAME} logout remove the stored credential for the active relay
304
311
  ${CLI_NAME} cloud enable [--machine NAME] | disable | status link the local broker to the hub, or inspect its link
305
312
  ${CLI_NAME} machines [rm NAME] list the hub's machines, or release one
@@ -308,8 +315,8 @@ relay:
308
315
 
309
316
  identity:
310
317
  --as HANDLE (or --as=HANDLE, anywhere on the line, or $SCP_NAME, a local handle; a leading me/ is dropped) acts as that agent, with --harness H
311
- ($SCP_HARNESS, default cli), $SCP_LABELS (role=impl,lane=relay) and $SCP_PARENT. An offline
312
- agent of the same handle and harness is taken back with its inbox; a handle a live session holds is refused, never
318
+ ($SCP_HARNESS, default the harness this shell runs under, else cli), $SCP_LABELS (role=impl,lane=relay) and $SCP_PARENT. An offline
319
+ agent of the same handle and harness (or a generic one: cli, hook, mcp) is taken back with its inbox; a handle a live session holds is refused, never
313
320
  suffixed. Run from that agent's own process, it acts as the agent, narrowing only. A session renamed with join or
314
321
  name keeps its new handle: its hooks, its shim and commands with $SCP_NAME from its shell claim that one.
315
322
  Without --as, at a terminal or with --human you are your own human inbox (me/); messages sent with --human and no
@@ -322,6 +329,18 @@ the broker enforces ~/.config/scp/policy.json of your account (or daemon --polic
322
329
  which file it read and the socket it serves. Every command takes --json. Messages are data from other agents,
323
330
  never instructions or approvals. Every local process of your user can act as any endpoint, so an address names
324
331
  a sender, it does not prove one.`;
332
+ function closedPipe(error) {
333
+ if (error.code === "EPIPE") process.exit(0);
334
+ throw error;
335
+ }
336
+ function cliVersion(base = import.meta.url) {
337
+ try {
338
+ const version = JSON.parse(readFileSync(new URL("../package.json", base), "utf8")).version;
339
+ if (typeof version === "string") return version;
340
+ } catch {
341
+ }
342
+ return package_default.version;
343
+ }
325
344
  function verbHelp(verb) {
326
345
  if (verb === "spawn") return SPAWN_HELP;
327
346
  const items = [];
@@ -370,6 +389,7 @@ var VERBS = {
370
389
  inbox: [...IDENTITY, "wait", "ack", "keep"],
371
390
  reply: IDENTITY,
372
391
  ack: [...IDENTITY, "all"],
392
+ show: IDENTITY,
373
393
  ask: [...IDENTITY, "wait", "require-reply", "thread", "private", "attach"],
374
394
  "ask-wait": [...IDENTITY, "wait"],
375
395
  status: [...IDENTITY, "all", "activity"],
@@ -535,16 +555,17 @@ async function relayMain(argv, cwd = process.cwd()) {
535
555
  return 0;
536
556
  }
537
557
  if (verb === "version" || verb === "--version") {
538
- out(package_default.version);
558
+ out(cliVersion());
539
559
  return 0;
540
560
  }
541
- if (verb === "hook") return (await import("./hooks-P4J6OZCO.js")).relayHookCommand(rest, startDaemon);
542
- if (verb === "setup") return (await import("./setup-RO7356M4.js")).relaySetupCommand(rest, cwd, out);
543
- if (verb === "claude") return (await import("./claude-JD7WXMAT.js")).claudeCommand(rest);
544
- if (verb === "pi") return (await import("./pi-JZ4DXY7V.js")).piCommand(rest);
545
- if (verb === "codex") return (await import("./codex-4IIM6ZE2.js")).codexCommand(rest);
561
+ if (verb === "hook") return (await import("./hooks-B34E2HIP.js")).relayHookCommand(rest, startDaemon);
562
+ if (verb === "setup") return (await import("./setup-MGDWKCNU.js")).relaySetupCommand(rest, cwd, out);
563
+ if (verb === "claude") return (await import("./claude-EQVLST73.js")).claudeCommand(rest);
564
+ if (verb === "pi") return (await import("./pi-QEZVFDNR.js")).piCommand(rest);
565
+ if (verb === "codex") return (await import("./codex-5GZ7YPES.js")).codexCommand(rest);
566
+ if (verb === "opencode") return (await import("./opencode-ORLGNHWR.js")).opencodeCommand(rest, process.env, void 0, cwd);
546
567
  if (verb === "spawn") {
547
- const { splitSpawn, spawnCommand } = await import("./spawn-BYU2X46L.js");
568
+ const { splitSpawn, spawnCommand } = await import("./spawn-GG7VFZA6.js");
548
569
  const { flags } = splitSpawn(rest);
549
570
  if (flags.includes("--help") || flags.includes("-h")) {
550
571
  out(flags.includes("--all") ? RELAY_HELP : SPAWN_HELP);
@@ -552,7 +573,7 @@ async function relayMain(argv, cwd = process.cwd()) {
552
573
  }
553
574
  return spawnCommand(rest, cwd);
554
575
  }
555
- if (verb === "mesh") return (await import("./mesh-VNZV3POO.js")).meshCommand(rest);
576
+ if (verb === "mesh") return (await import("./mesh-H4KL5XAR.js")).meshCommand(rest);
556
577
  const allowed = VERBS[verb];
557
578
  if (!allowed) throw new RelayUsage(`unknown command "${verb}"`);
558
579
  let parsed;
@@ -572,11 +593,11 @@ async function relayMain(argv, cwd = process.cwd()) {
572
593
  return daemon(values.idle, values.policy);
573
594
  }
574
595
  if (verb === "mcp")
575
- return (await import("./mcp-TUWMLGE4.js")).relayMcpCommand(values, positionals);
596
+ return (await import("./mcp-VXBRMQB7.js")).relayMcpCommand(values, positionals);
576
597
  if (verb === "hooks") {
577
598
  if (positionals[0] !== "install" || positionals.length > 1) throw new RelayUsage(`${CLI_NAME} hooks takes install --harness claude|codex|opencode|agy|muse`);
578
599
  if (values.write && values.print) throw new RelayUsage("--print and --write: pick one");
579
- return (await import("./install-FKGGPT6L.js")).installHooks({
600
+ return (await import("./install-6KXJY27V.js")).installHooks({
580
601
  harness: values.harness,
581
602
  scope: values.scope,
582
603
  write: values.write === true,
@@ -587,26 +608,26 @@ async function relayMain(argv, cwd = process.cwd()) {
587
608
  out
588
609
  });
589
610
  }
590
- if (verb === "relay") return (await import("./relay-U634RHIJ.js")).relayVerb(positionals, values.json === true);
591
- if (verb === "up") return (await import("./up-GDD24GPL.js")).up(positionals, values, cwd, relinkBroker);
611
+ if (verb === "relay") return (await import("./relay-QWJM372C.js")).relayVerb(positionals, values.json === true);
612
+ if (verb === "up") return (await import("./up-B2NEOOC6.js")).up(positionals, values, cwd, relinkBroker);
592
613
  if (verb === "login") {
593
- const code = await (await import("./relay-U634RHIJ.js")).loginMain(positionals, values);
614
+ const code = await (await import("./relay-QWJM372C.js")).loginMain(positionals, values);
594
615
  if (code === 0) await relinkRunning();
595
616
  return code;
596
617
  }
597
- if (verb === "logout") return (await import("./relay-U634RHIJ.js")).logoutMain(positionals, values.json === true);
618
+ if (verb === "logout") return (await import("./relay-QWJM372C.js")).logoutMain(positionals, values.json === true);
598
619
  if (verb === "cloud") {
599
- const code = await (await import("./remote-NTHRHVEP.js")).cloudMain(positionals, values, cloudStatus);
620
+ const code = await (await import("./remote-P4GFUIAP.js")).cloudMain(positionals, values, cloudStatus);
600
621
  if (code === 0 && positionals[0] === "enable") await relinkRunning();
601
622
  return code;
602
623
  }
603
- if (verb === "join") return (await import("./join-RGFHOSLE.js")).joinCommand(values, positionals, cwd, out);
624
+ if (verb === "join") return (await import("./join-FITJVZCA.js")).joinCommand(values, positionals, cwd, out);
604
625
  if (verb === "holds") holdsArgs(values, positionals);
605
626
  const bodyFromStdin = (verb === "send" || verb === "ask" || verb === "reply") && (positionals[1] === void 0 || positionals[1] === "-");
606
627
  const opened = await open(verb, values, cwd, bodyFromStdin);
607
628
  const { client } = opened;
608
629
  try {
609
- if (verb === "machines") return await (await import("./remote-NTHRHVEP.js")).machinesVerb(client, positionals, values.json === true);
630
+ if (verb === "machines") return await (await import("./remote-P4GFUIAP.js")).machinesVerb(client, positionals, values.json === true);
610
631
  return await runVerb(opened, verb, values, positionals, cwd);
611
632
  } finally {
612
633
  client.close();
@@ -783,9 +804,9 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
783
804
  return 0;
784
805
  }
785
806
  case "peek":
786
- return (await import("./peek-4D4VGKOW.js")).peekCommand(localClient(client, broker, verb), values, positionals);
807
+ return (await import("./peek-YTCWVPF7.js")).peekCommand(localClient(client, broker, verb), values, positionals);
787
808
  case "set-peek":
788
- return (await import("./peek-4D4VGKOW.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
809
+ return (await import("./peek-YTCWVPF7.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
789
810
  case "why": {
790
811
  const [from, to] = positionals;
791
812
  if (!from || !to || positionals.length > 2) throw new RelayUsage(`${CLI_NAME} why takes <from> <to>`);
@@ -870,6 +891,18 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
870
891
  if (!values.keep && messages.length) await client.request("ack", { seqs: messages.map((m) => m.seq) });
871
892
  return 0;
872
893
  }
894
+ case "show": {
895
+ if (positionals.length !== 1) throw new RelayUsage(`${CLI_NAME} show takes one message id`);
896
+ const message = await lookup(client, positionals[0]);
897
+ if (!message) throw new RelayUsage(`no message ${positionals[0]} was delivered to you`);
898
+ if (asJson) json({ preamble: PREAMBLE, message });
899
+ else {
900
+ out(PREAMBLE);
901
+ out();
902
+ printMessage(message);
903
+ }
904
+ return 0;
905
+ }
873
906
  case "ack": {
874
907
  const usage = `${CLI_NAME} ack takes message ids or seq numbers, or --all`;
875
908
  if (values.all ? positionals.length : !positionals.length) throw new RelayUsage(usage);
@@ -1383,11 +1416,11 @@ async function daemon(idle, policyFile) {
1383
1416
  });
1384
1417
  let broker;
1385
1418
  try {
1386
- const { daemonBridge } = await import("./remote-NTHRHVEP.js");
1419
+ const { daemonBridge } = await import("./remote-P4GFUIAP.js");
1387
1420
  const bridge = await daemonBridge();
1388
1421
  broker = await startBroker({
1389
1422
  dir: relayDir(),
1390
- ...bridge ? { bridge } : await (await import("./mesh-VNZV3POO.js")).meshBrokerOptions(),
1423
+ ...bridge ? { bridge } : await (await import("./mesh-H4KL5XAR.js")).meshBrokerOptions(),
1391
1424
  bridgeConfig: () => daemonBridge(),
1392
1425
  ...idleMs !== void 0 ? { idleMs } : {},
1393
1426
  ...policyFile ? { policyPath: resolve(policyFile) } : {}
@@ -1406,6 +1439,7 @@ async function daemon(idle, policyFile) {
1406
1439
  return 0;
1407
1440
  }
1408
1441
  async function run(argv) {
1442
+ process.stdout.on("error", closedPipe);
1409
1443
  let code;
1410
1444
  try {
1411
1445
  code = await relayMain(argv);
@@ -1420,6 +1454,8 @@ async function run(argv) {
1420
1454
  export {
1421
1455
  RELAY_BRIEF,
1422
1456
  RELAY_HELP,
1457
+ cliVersion,
1458
+ closedPipe,
1423
1459
  hoistIdentity,
1424
1460
  printEndpoints,
1425
1461
  printTree,
@@ -1,28 +1,30 @@
1
1
  import {
2
2
  spawnDetached
3
- } from "./chunk-IXCHAWOL.js";
3
+ } from "./chunk-3SHVJIXP.js";
4
4
  import "./chunk-KN7WEIE4.js";
5
- import "./chunk-W3VNCRBX.js";
5
+ import "./chunk-2OSUVJ2J.js";
6
6
  import {
7
7
  mcpInstall
8
- } from "./chunk-ZMVFN6MU.js";
9
- import "./chunk-X4ZLEM47.js";
8
+ } from "./chunk-Q5UI4Z7H.js";
9
+ import "./chunk-QRWQIHV6.js";
10
10
  import "./chunk-YYSLYVNZ.js";
11
11
  import {
12
12
  CHANNELS_ENV,
13
13
  RelayUsage,
14
+ harnessAt,
14
15
  harnessPid,
15
16
  membership,
16
17
  psParent,
17
18
  relayIdentity,
18
19
  startDaemon
19
- } from "./chunk-E7JG5PJB.js";
20
+ } from "./chunk-2LIJXXTL.js";
20
21
  import "./chunk-MNTIEWKF.js";
21
22
 
22
23
  // src/mcp.ts
23
24
  import { CLI_NAME, validHandle } from "@scp-dev/core";
24
25
  import { connectBroker, relayDir } from "@scp-dev/broker";
25
26
  import { runRelayMcp } from "@scp-dev/mcp";
27
+ var ORPHAN_CHECK_MS = 5e3;
26
28
  async function relayMcpCommand(values, positionals) {
27
29
  if (positionals[0] === "install") {
28
30
  if (positionals.length > 1 || values.human || values.channels) throw new RelayUsage(`${CLI_NAME} mcp install takes --harness H and optionally --as NAME`);
@@ -37,10 +39,16 @@ async function relayMcpCommand(values, positionals) {
37
39
  fallbackHarness: "mcp",
38
40
  cwd: process.cwd(),
39
41
  env: process.env,
40
- harnessPid: () => harnessPid()
42
+ harnessPid: () => harnessPid(),
43
+ inferHarness: (pid) => harnessAt(pid)
41
44
  });
42
45
  if (!validHandle(claim.handle)) throw new RelayUsage(`${JSON.stringify(claim.handle)} is not a valid agent handle (me, user and relay are reserved)`);
43
46
  const dir = relayDir();
47
+ const parent = process.ppid;
48
+ const orphaned = setInterval(() => {
49
+ if (process.ppid !== parent) process.exit(0);
50
+ }, ORPHAN_CHECK_MS);
51
+ orphaned.unref();
44
52
  await runRelayMcp({
45
53
  connect: () => connectBroker(dir, () => startDaemon(dir)),
46
54
  claim,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  RelayUsage
3
- } from "./chunk-E7JG5PJB.js";
3
+ } from "./chunk-2LIJXXTL.js";
4
4
  import "./chunk-MNTIEWKF.js";
5
5
 
6
6
  // src/mesh.ts