@scp-dev/cli 0.2.0 → 0.2.2

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 (31) hide show
  1. package/README.md +1 -0
  2. package/dist/chunk-E7JG5PJB.js +806 -0
  3. package/dist/{chunk-5DYFYLAQ.js → chunk-F5QVHJIT.js} +3 -5
  4. package/dist/{chunk-GH7AXFUL.js → chunk-G2SQPY6E.js} +8 -10
  5. package/dist/{chunk-M2DCR2J4.js → chunk-MNTIEWKF.js} +13 -3
  6. package/dist/{chunk-WV5FINTW.js → chunk-NOYMSD4F.js} +20 -10
  7. package/dist/{chunk-RXKBND3T.js → chunk-OTO6DBU6.js} +229 -13
  8. package/dist/{chunk-U2RSSTNW.js → chunk-ZDU53ZRF.js} +3 -5
  9. package/dist/{claude-YJLGFQ2P.js → claude-CVWECMRQ.js} +21 -11
  10. package/dist/cli.js +1 -1
  11. package/dist/codex-Y7SGLZ64.js +565 -0
  12. package/dist/{daemon-SUTLTY5U.js → daemon-OMJY7T2F.js} +1 -1
  13. package/dist/{hooks-EI7M3YKY.js → hooks-EKGY5O5N.js} +19 -3
  14. package/dist/install-4VBSX6RI.js +14 -0
  15. package/dist/join-RGFHOSLE.js +48 -0
  16. package/dist/{login-V2WT7QX5.js → login-IPS75GLQ.js} +4 -7
  17. package/dist/{main-HPBTO3JK.js → main-YN3J6MGW.js} +116 -32
  18. package/dist/{mcp-DGAQNFKO.js → mcp-BVC4PLZ7.js} +11 -11
  19. package/dist/mesh-VNZV3POO.js +28 -0
  20. package/dist/{peek-WLG3ZROU.js → peek-4D4VGKOW.js} +2 -3
  21. package/dist/{pi-HMESQA64.js → pi-R23XR4AC.js} +2 -3
  22. package/dist/{relay-5Z7IEXKD.js → relay-U634RHIJ.js} +6 -4
  23. package/dist/{remote-FYN5ZXE4.js → remote-NTHRHVEP.js} +3 -5
  24. package/dist/{setup-LXG346FJ.js → setup-H7GGUID2.js} +5 -6
  25. package/dist/{spawn-DLPMAS5M.js → spawn-AJAYD6CW.js} +6 -7
  26. package/dist/{up-GPM4LWY3.js → up-HZKGURPI.js} +6 -9
  27. package/package.json +8 -5
  28. package/dist/chunk-3RRPUYTE.js +0 -147
  29. package/dist/chunk-3V3DRXB2.js +0 -340
  30. package/dist/chunk-E53YHYEP.js +0 -305
  31. package/dist/install-MYHVFKT4.js +0 -15
@@ -0,0 +1,48 @@
1
+ import {
2
+ RelayUsage,
3
+ localHandle,
4
+ relayIdentity,
5
+ sessionKey,
6
+ startDaemon,
7
+ walkNow
8
+ } from "./chunk-E7JG5PJB.js";
9
+ import "./chunk-MNTIEWKF.js";
10
+
11
+ // src/join.ts
12
+ import { CLI_NAME } from "@scp-dev/core";
13
+ import { connectBroker, readSessionMarker, relayDir } from "@scp-dev/broker";
14
+ async function joinCommand(values, positionals, cwd, out) {
15
+ if (positionals.length > 1) throw new RelayUsage(`${CLI_NAME} join takes one handle, or none to keep the session's name`);
16
+ const handle = positionals[0] === void 0 ? void 0 : localHandle(positionals[0]);
17
+ const { keys, self } = walkNow();
18
+ const key = self.at(-1);
19
+ const match = key === void 0 ? null : /^pid:(\d+):(.+)$/.exec(key);
20
+ if (!match) throw new RelayUsage(`${CLI_NAME} join runs in an agent session's shell: no harness process runs this shell`);
21
+ const dir = relayDir();
22
+ const known = readSessionMarker(dir, sessionKey(process.env, key))?.handle;
23
+ const claim = relayIdentity({
24
+ ...known ? { as: known } : {},
25
+ ...typeof values.harness === "string" ? { harness: values.harness } : {},
26
+ fallbackHarness: "cli",
27
+ cwd,
28
+ env: process.env,
29
+ harnessPid: () => Number(match[1]),
30
+ startOf: () => match[2],
31
+ lineageOf: () => keys.slice(Math.max(0, keys.indexOf(key)))
32
+ });
33
+ const client = await connectBroker(dir, () => startDaemon(dir));
34
+ try {
35
+ const under = typeof values.under === "string" ? values.under : void 0;
36
+ const hello = await client.hello({ transient: true, claim, join: { ...handle ? { handle } : {}, ...under ? { under } : {} } });
37
+ const view = hello.endpoint;
38
+ if (values.json === true) out(JSON.stringify(view, null, 2));
39
+ else
40
+ out(`joined as ${view.address}${view.parent ? ` under ${view.parent}` : ""}: peers can message it, and its hooks hand it new messages at turn boundaries`);
41
+ } finally {
42
+ client.close();
43
+ }
44
+ return 0;
45
+ }
46
+ export {
47
+ joinCommand
48
+ };
@@ -1,17 +1,14 @@
1
1
  import {
2
2
  machineName
3
- } from "./chunk-U2RSSTNW.js";
3
+ } from "./chunk-ZDU53ZRF.js";
4
4
  import {
5
+ RelayUsage,
5
6
  activeRelay,
6
7
  saveCredential
7
- } from "./chunk-3RRPUYTE.js";
8
- import {
9
- RelayUsage
10
- } from "./chunk-3V3DRXB2.js";
11
- import "./chunk-E53YHYEP.js";
8
+ } from "./chunk-E7JG5PJB.js";
12
9
  import {
13
10
  note
14
- } from "./chunk-M2DCR2J4.js";
11
+ } from "./chunk-MNTIEWKF.js";
15
12
 
16
13
  // src/login.ts
17
14
  import { spawn } from "child_process";
@@ -11,20 +11,18 @@ import {
11
11
  open,
12
12
  preset,
13
13
  selectorList,
14
+ shortHostname,
14
15
  stamp,
15
16
  startDaemon,
16
17
  time
17
- } from "./chunk-3V3DRXB2.js";
18
- import {
19
- shortHostname
20
- } from "./chunk-E53YHYEP.js";
18
+ } from "./chunk-E7JG5PJB.js";
21
19
  import {
22
20
  FAILED,
23
21
  exitFlushed,
24
22
  note,
25
23
  parseSeconds,
26
24
  printTable
27
- } from "./chunk-M2DCR2J4.js";
25
+ } from "./chunk-MNTIEWKF.js";
28
26
 
29
27
  // src/main.ts
30
28
  import { text as readAll } from "stream/consumers";
@@ -35,7 +33,7 @@ import { parseArgs } from "util";
35
33
  // package.json
36
34
  var package_default = {
37
35
  name: "@scp-dev/cli",
38
- version: "0.2.0",
36
+ version: "0.2.2",
39
37
  description: "The swarm command line tool.",
40
38
  license: "Apache-2.0",
41
39
  homepage: "https://scp.dev",
@@ -66,6 +64,9 @@ var package_default = {
66
64
  "smol-toml": "^1.9.1"
67
65
  },
68
66
  devDependencies: {
67
+ "@openai/codex": "0.159.0",
68
+ "@scp-dev/mesh": "workspace:*",
69
+ "opencode-ai": "1.18.34",
69
70
  tsup: "^8.5.1"
70
71
  },
71
72
  publishConfig: {
@@ -98,7 +99,9 @@ results back, as with native subagents.
98
99
  ${CLI_NAME} reply <id> [text|-] answer a message in its thread: ${CLI_NAME} reply 5af1f2 "done"
99
100
  ${CLI_NAME} ask <to> [text|-] [--wait S] send a request and wait for its reply: ${CLI_NAME} ask lead "which branch?"
100
101
  ${CLI_NAME} inbox [--wait S] your unread messages, then marked read: ${CLI_NAME} inbox
101
- ${CLI_NAME} list [--all] agents on the relay: address, title, presence, status
102
+ ${CLI_NAME} list [--all] agents on the relay: address, title, presence, status; --all adds offline ones
103
+ ${CLI_NAME} join [name] put this session on the relay, named once: ${CLI_NAME} join lead (${CLI_NAME} name renames it)
104
+ ${CLI_NAME} join --under <parent> link this session under an orchestrator, as a spawned worker sits: ${CLI_NAME} join --under lead
102
105
  ${CLI_NAME} status [id] the fleet in short; with the id of a message you sent: who read it when
103
106
  ${CLI_NAME} peek <ep> a short, redacted view of a peekable agent's last turns: ${CLI_NAME} peek rev
104
107
  ${CLI_NAME} set-status <state> [note] working, waiting, blocked or done: ${CLI_NAME} set-status done "PR open"
@@ -123,8 +126,10 @@ usage:
123
126
  sessions nobody spawned that spawned nobody fold into one line, --roots (or --all) lists
124
127
  them; --tree nests them by parent; (you) marks your own
125
128
  ${CLI_NAME} list --contacts the endpoints of other accounts your contacts let you reach: address, harness, presence
126
- ${CLI_NAME} send <to> [text|-] send a message (- or no text reads stdin); a line per recipient says when it sees it
127
- [--reply-to ID] [--thread ID] [--private] [--attach PATH]... --thread takes a thread id or the <id> of a message in it
129
+ ${CLI_NAME} send <to> [text|-] send a message (- or no text reads stdin); a line per recipient says when it sees it,
130
+ [--reply-to ID] [--thread ID] [--private] [--attach PATH]... [--live] and "queued: no live session holds <addr>" for one
131
+ that is offline; --live refuses those (not_live) and queues nothing;
132
+ --thread takes a thread id or the <id> of a message in it
128
133
  ${CLI_NAME} reply <id> [text|-] reply to message <id>: to its sender, in its thread, marking it read; <id> may
129
134
  also be another's message in a thread you sent or got a message in
130
135
  ${CLI_NAME} inbox [--wait S] [--keep] your unread messages, then marked read (--keep leaves them unread); --wait waits up
@@ -227,6 +232,8 @@ scopes (selectors are comma-separated, e.g. tree:parent,tree:children,group:team
227
232
  -c sandbox_workspace_write.network_access=true (the relay socket counts as network)
228
233
  ${CLI_NAME} pi [--name N] [--] [pi args...] runs pi with the @scp-dev/pi extension: the relay tools, and each new
229
234
  message as a turn (steering a running one, waking an idle session)
235
+ ${CLI_NAME} codex [--name N] [--headless] [--] [codex args...]
236
+ runs codex with relay messages waking an idle thread or steering a running turn
230
237
  ${CLI_NAME} peers [--all] [--roots] who you may message: title, status and the rule that admits each; offline ones and
231
238
  sessions nobody spawned that spawned nobody only with --all (--roots: those sessions)
232
239
  ${CLI_NAME} why <from> <to> whether from may message to, and why
@@ -262,6 +269,30 @@ agent sessions:
262
269
  ${CLI_NAME} claude [--name N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake
263
270
  it while idle (claude --dangerously-load-development-channels server:scp; SCP_NAME=N)
264
271
  --channels pushes messages into a Claude Code that ${CLI_NAME} claude started; setup mounts claude's shim with it.
272
+ Without --name, ${CLI_NAME} claude asks the session to pick its own name with ${CLI_NAME} name <name>.
273
+ ${CLI_NAME} join [<handle>] put the session that runs this shell on the relay (it then shows in list,
274
+ peers can message it, and its hooks hand it new messages), named <handle>
275
+ when given; on a joined session it renames it in place. Works for a session
276
+ started before the hooks were installed too. Tell any session: ${CLI_NAME} join <name>
277
+ ${CLI_NAME} name [<handle>] [--endpoint EP] print this session's address, or rename it in place: id, mail, threads, status
278
+ and title stay; a handle another endpoint holds, live or offline, is refused;
279
+ a spawned worker is named by its spawner (policy defaults.rename: self lets it);
280
+ --endpoint renames another agent, for the human at a terminal
281
+ ${CLI_NAME} join [<handle>] --under <parent> link the session under the agent <parent> (local, one it may message), as if
282
+ <parent> had spawned it: it shows under <parent> in list --tree, the dashboard
283
+ and the graph, and takes on a worker's scope (the policy's child_default: by
284
+ default it reaches only what <parent> reaches). Narrowing, so always allowed
285
+ ${CLI_NAME} adopt <child> [--parent <p>] link a local root agent and its subtree under <p>, for the human at a terminal;
286
+ from an agent's shell with a terminal attached <p> defaults to that agent.
287
+ An agent without a terminal is refused: the session joins with --under itself
288
+ ${CLI_NAME} unlink [<child>] put a linked agent back at the root, its subtree along and the child scope off;
289
+ widening, so the human at a terminal only (policy defaults.unlink: self lets
290
+ an agent unlink itself). A link that would put a parent under its own child
291
+ (a cycle) is refused; both are audited, as adopt and unlink
292
+ ${CLI_NAME} leave [--endpoint EP] take this session off the relay: offline, no messages pushed, mail kept until
293
+ ${CLI_NAME} join; a spawned worker stays joined while it runs
294
+ With setup --join manual a session stays off the relay until it runs ${CLI_NAME} join; the default, auto, joins every
295
+ hooked session when it starts.
265
296
 
266
297
  relay:
267
298
  ${CLI_NAME} relay use <url> | show select a relay, or show its source and credential status
@@ -277,7 +308,8 @@ identity:
277
308
  --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
278
309
  ($SCP_HARNESS, default cli), $SCP_LABELS (role=impl,lane=relay) and $SCP_PARENT. An offline
279
310
  agent of the same handle and harness is taken back with its inbox; a handle a live session holds is refused, never
280
- renamed. Run from that agent's own process, it acts as the agent, narrowing only.
311
+ suffixed. Run from that agent's own process, it acts as the agent, narrowing only. A session renamed with join or
312
+ name keeps its new handle: its hooks, its shim and commands with $SCP_NAME from its shell claim that one.
281
313
  Without --as, at a terminal or with --human you are your own human inbox (me/); messages sent with --human and no
282
314
  terminal are marked tty="false" and get no scope exemption. From an agent's process, --human acts as that agent.
283
315
  Without either and without a terminal, a command acts as the agent whose harness process runs this shell (the
@@ -327,7 +359,12 @@ var VERB_OPTIONS = {
327
359
  var IDENTITY = ["as", "harness", "human", "json", "cloud", "machine"];
328
360
  var VERBS = {
329
361
  list: [...IDENTITY, "all", "tree", "roots", "contacts"],
330
- send: [...IDENTITY, "reply-to", "thread", "private", "attach"],
362
+ send: [...IDENTITY, "reply-to", "thread", "private", "attach", "live"],
363
+ join: ["harness", "json", "under"],
364
+ adopt: [...IDENTITY, "parent"],
365
+ unlink: IDENTITY,
366
+ leave: [...IDENTITY, "endpoint"],
367
+ name: [...IDENTITY, "endpoint"],
331
368
  inbox: [...IDENTITY, "wait", "ack", "keep"],
332
369
  reply: IDENTITY,
333
370
  ack: [...IDENTITY, "all"],
@@ -498,12 +535,13 @@ async function relayMain(argv, cwd = process.cwd()) {
498
535
  out(package_default.version);
499
536
  return 0;
500
537
  }
501
- if (verb === "hook") return (await import("./hooks-EI7M3YKY.js")).relayHookCommand(rest, startDaemon);
502
- if (verb === "setup") return (await import("./setup-LXG346FJ.js")).relaySetupCommand(rest, cwd, out);
503
- if (verb === "claude") return (await import("./claude-YJLGFQ2P.js")).claudeCommand(rest);
504
- if (verb === "pi") return (await import("./pi-HMESQA64.js")).piCommand(rest);
538
+ if (verb === "hook") return (await import("./hooks-EKGY5O5N.js")).relayHookCommand(rest, startDaemon);
539
+ if (verb === "setup") return (await import("./setup-H7GGUID2.js")).relaySetupCommand(rest, cwd, out);
540
+ if (verb === "claude") return (await import("./claude-CVWECMRQ.js")).claudeCommand(rest);
541
+ if (verb === "pi") return (await import("./pi-R23XR4AC.js")).piCommand(rest);
542
+ if (verb === "codex") return (await import("./codex-Y7SGLZ64.js")).codexCommand(rest);
505
543
  if (verb === "spawn") {
506
- const { splitSpawn, spawnCommand } = await import("./spawn-DLPMAS5M.js");
544
+ const { splitSpawn, spawnCommand } = await import("./spawn-AJAYD6CW.js");
507
545
  const { flags } = splitSpawn(rest);
508
546
  if (flags.includes("--help") || flags.includes("-h")) {
509
547
  out(flags.includes("--all") ? RELAY_HELP : SPAWN_HELP);
@@ -511,6 +549,7 @@ async function relayMain(argv, cwd = process.cwd()) {
511
549
  }
512
550
  return spawnCommand(rest, cwd);
513
551
  }
552
+ if (verb === "mesh") return (await import("./mesh-VNZV3POO.js")).meshCommand(rest);
514
553
  const allowed = VERBS[verb];
515
554
  if (!allowed) throw new RelayUsage(`unknown command "${verb}"`);
516
555
  let parsed;
@@ -530,11 +569,11 @@ async function relayMain(argv, cwd = process.cwd()) {
530
569
  return daemon(values.idle, values.policy);
531
570
  }
532
571
  if (verb === "mcp")
533
- return (await import("./mcp-DGAQNFKO.js")).relayMcpCommand(values, positionals);
572
+ return (await import("./mcp-BVC4PLZ7.js")).relayMcpCommand(values, positionals);
534
573
  if (verb === "hooks") {
535
574
  if (positionals[0] !== "install" || positionals.length > 1) throw new RelayUsage(`${CLI_NAME} hooks takes install --harness claude|codex|opencode|agy|muse`);
536
575
  if (values.write && values.print) throw new RelayUsage("--print and --write: pick one");
537
- return (await import("./install-MYHVFKT4.js")).installHooks({
576
+ return (await import("./install-4VBSX6RI.js")).installHooks({
538
577
  harness: values.harness,
539
578
  scope: values.scope,
540
579
  write: values.write === true,
@@ -545,25 +584,26 @@ async function relayMain(argv, cwd = process.cwd()) {
545
584
  out
546
585
  });
547
586
  }
548
- if (verb === "relay") return (await import("./relay-5Z7IEXKD.js")).relayVerb(positionals, values.json === true);
549
- if (verb === "up") return (await import("./up-GPM4LWY3.js")).up(positionals, values, cwd, relinkBroker);
587
+ if (verb === "relay") return (await import("./relay-U634RHIJ.js")).relayVerb(positionals, values.json === true);
588
+ if (verb === "up") return (await import("./up-HZKGURPI.js")).up(positionals, values, cwd, relinkBroker);
550
589
  if (verb === "login") {
551
- const code = await (await import("./relay-5Z7IEXKD.js")).loginMain(positionals, values);
590
+ const code = await (await import("./relay-U634RHIJ.js")).loginMain(positionals, values);
552
591
  if (code === 0) await relinkRunning();
553
592
  return code;
554
593
  }
555
- if (verb === "logout") return (await import("./relay-5Z7IEXKD.js")).logoutMain(positionals, values.json === true);
594
+ if (verb === "logout") return (await import("./relay-U634RHIJ.js")).logoutMain(positionals, values.json === true);
556
595
  if (verb === "cloud") {
557
- const code = await (await import("./remote-FYN5ZXE4.js")).cloudMain(positionals, values, cloudStatus);
596
+ const code = await (await import("./remote-NTHRHVEP.js")).cloudMain(positionals, values, cloudStatus);
558
597
  if (code === 0 && positionals[0] === "enable") await relinkRunning();
559
598
  return code;
560
599
  }
600
+ if (verb === "join") return (await import("./join-RGFHOSLE.js")).joinCommand(values, positionals, cwd, out);
561
601
  if (verb === "holds") holdsArgs(values, positionals);
562
602
  const bodyFromStdin = (verb === "send" || verb === "ask" || verb === "reply") && (positionals[1] === void 0 || positionals[1] === "-");
563
603
  const opened = await open(verb, values, cwd, bodyFromStdin);
564
604
  const { client } = opened;
565
605
  try {
566
- if (verb === "machines") return await (await import("./remote-FYN5ZXE4.js")).machinesVerb(client, positionals, values.json === true);
606
+ if (verb === "machines") return await (await import("./remote-NTHRHVEP.js")).machinesVerb(client, positionals, values.json === true);
567
607
  return await runVerb(opened, verb, values, positionals, cwd);
568
608
  } finally {
569
609
  client.close();
@@ -594,7 +634,7 @@ async function relinkBroker() {
594
634
  client.close();
595
635
  }
596
636
  if (!result.restarting) return result;
597
- await (await import("./daemon-SUTLTY5U.js")).exited(pid);
637
+ await (await import("./daemon-OMJY7T2F.js")).exited(pid);
598
638
  return cloudStatus();
599
639
  }
600
640
  async function relinkRunning() {
@@ -614,7 +654,7 @@ async function daemonControl(positionals, values) {
614
654
  if (positionals.length !== 1 || action !== "stop" && action !== "restart" || values.idle !== void 0 || values.policy !== void 0)
615
655
  throw new RelayUsage(`${CLI_NAME} daemon takes stop or restart, or --idle and --policy to run in the foreground`);
616
656
  const dir = relayDir();
617
- const pid = await (await import("./daemon-SUTLTY5U.js")).stopBroker(dir);
657
+ const pid = await (await import("./daemon-OMJY7T2F.js")).stopBroker(dir);
618
658
  if (action === "stop") {
619
659
  out(pid === null ? "no relay broker runs" : `stopped the relay broker (pid ${pid}); the next command starts a fresh one`);
620
660
  return 0;
@@ -740,9 +780,9 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
740
780
  return 0;
741
781
  }
742
782
  case "peek":
743
- return (await import("./peek-WLG3ZROU.js")).peekCommand(localClient(client, broker, verb), values, positionals);
783
+ return (await import("./peek-4D4VGKOW.js")).peekCommand(localClient(client, broker, verb), values, positionals);
744
784
  case "set-peek":
745
- return (await import("./peek-WLG3ZROU.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
785
+ return (await import("./peek-4D4VGKOW.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
746
786
  case "why": {
747
787
  const [from, to] = positionals;
748
788
  if (!from || !to || positionals.length > 2) throw new RelayUsage(`${CLI_NAME} why takes <from> <to>`);
@@ -768,13 +808,13 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
768
808
  ...attachments.length ? { attachments } : {}
769
809
  };
770
810
  if (verb === "send") {
771
- const result2 = await client.request("send", params);
811
+ const result2 = await client.request("send", { ...params, ...values.live ? { live: true } : {} });
772
812
  if (asJson) json(result2);
773
813
  else if (result2.held) out(heldText(result2.held));
774
814
  else {
775
815
  const lines = reachLines(result2.deliveries);
776
816
  out(`sent ${result2.id} to ${result2.deliveries.map((d) => lines.length ? d.address : deliveryText(d)).join(", ")}${redactedText(result2.redacted)}`);
777
- for (const line of lines) out(` ${line}`);
817
+ for (const line of [...lines, ...result2.warnings ?? []]) out(` ${line}`);
778
818
  if (result2.deliveries.some((d) => d.state === "running" || d.state === "starting"))
779
819
  out(` ${CLI_NAME} status ${shortId(result2.id)} shows when it is read`);
780
820
  }
@@ -943,6 +983,50 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
943
983
  else out(`${view.address}: ${view.status ? statusText(view.status) : "-"}`);
944
984
  return 0;
945
985
  }
986
+ case "name": {
987
+ if (positionals.length > 1) throw new RelayUsage(`${CLI_NAME} name takes one handle, or none to print your address`);
988
+ const endpoint = values.endpoint;
989
+ if (!positionals.length) {
990
+ if (endpoint !== void 0) throw new RelayUsage(`${CLI_NAME} name --endpoint EP takes the new handle`);
991
+ if (me?.kind !== "agent") throw new RelayUsage(`${CLI_NAME} name: this shell is no joined session; ${CLI_NAME} join <name> joins it`);
992
+ if (asJson) json({ address: me.address });
993
+ else out(me.address);
994
+ return 0;
995
+ }
996
+ const before = endpoint ?? me?.address;
997
+ const view = await client.request("rename", { handle: positionals[0], ...endpoint !== void 0 ? { endpoint } : {} });
998
+ if (asJson) json(view);
999
+ else out(before && before !== view.address ? `${before} is now ${view.address}` : `${view.address}`);
1000
+ return 0;
1001
+ }
1002
+ case "adopt": {
1003
+ if (positionals.length !== 1) throw new RelayUsage(`${CLI_NAME} adopt takes the child to link: ${CLI_NAME} adopt <child> [--parent <p>]`);
1004
+ const parent = values.parent;
1005
+ if (parent === void 0 && me?.kind !== "agent")
1006
+ throw new RelayUsage(`${CLI_NAME} adopt: name the parent with --parent <p> (run from an agent's shell, it defaults to that agent)`);
1007
+ const result = await client.request("adopt", {
1008
+ endpoint: positionals[0],
1009
+ ...parent !== void 0 ? { parent } : {}
1010
+ });
1011
+ if (asJson) json(result);
1012
+ else out(`${result.endpoint.address} now sits under ${result.parent}`);
1013
+ return 0;
1014
+ }
1015
+ case "unlink": {
1016
+ if (positionals.length > 1) throw new RelayUsage(`${CLI_NAME} unlink takes one child, or none for this session`);
1017
+ const result = await client.request("unlink", positionals.length ? { endpoint: positionals[0] } : {});
1018
+ if (asJson) json(result);
1019
+ else out(`${result.endpoint.address} is a root again (was under ${result.parent})`);
1020
+ return 0;
1021
+ }
1022
+ case "leave": {
1023
+ if (positionals.length) throw new RelayUsage(`${CLI_NAME} leave takes no arguments (--endpoint EP for the human at a terminal)`);
1024
+ const endpoint = values.endpoint;
1025
+ const view = await client.request("leave", endpoint !== void 0 ? { endpoint } : {});
1026
+ if (asJson) json(view);
1027
+ else out(`${view.address} left the relay: offline, no messages pushed; its mail is kept until ${CLI_NAME} join`);
1028
+ return 0;
1029
+ }
946
1030
  case "set-title": {
947
1031
  if (!positionals.length) throw new RelayUsage(`${CLI_NAME} set-title takes the title text ("" clears it)`);
948
1032
  const view = await client.request("title", { text: positionals.join(" ") });
@@ -1295,11 +1379,11 @@ async function daemon(idle, policyFile) {
1295
1379
  });
1296
1380
  let broker;
1297
1381
  try {
1298
- const { daemonBridge } = await import("./remote-FYN5ZXE4.js");
1382
+ const { daemonBridge } = await import("./remote-NTHRHVEP.js");
1299
1383
  const bridge = await daemonBridge();
1300
1384
  broker = await startBroker({
1301
1385
  dir: relayDir(),
1302
- ...bridge ? { bridge } : {},
1386
+ ...bridge ? { bridge } : await (await import("./mesh-VNZV3POO.js")).meshBrokerOptions(),
1303
1387
  bridgeConfig: () => daemonBridge(),
1304
1388
  ...idleMs !== void 0 ? { idleMs } : {},
1305
1389
  ...policyFile ? { policyPath: resolve(policyFile) } : {}
@@ -1,24 +1,23 @@
1
1
  import {
2
2
  spawnDetached
3
- } from "./chunk-GH7AXFUL.js";
3
+ } from "./chunk-G2SQPY6E.js";
4
4
  import "./chunk-KN7WEIE4.js";
5
- import "./chunk-WV5FINTW.js";
5
+ import "./chunk-NOYMSD4F.js";
6
6
  import {
7
7
  mcpInstall
8
- } from "./chunk-5DYFYLAQ.js";
8
+ } from "./chunk-F5QVHJIT.js";
9
+ import "./chunk-OTO6DBU6.js";
10
+ import "./chunk-YYSLYVNZ.js";
9
11
  import {
10
12
  CHANNELS_ENV,
11
13
  RelayUsage,
12
- startDaemon
13
- } from "./chunk-3V3DRXB2.js";
14
- import "./chunk-RXKBND3T.js";
15
- import "./chunk-YYSLYVNZ.js";
16
- import {
17
14
  harnessPid,
15
+ membership,
18
16
  psParent,
19
- relayIdentity
20
- } from "./chunk-E53YHYEP.js";
21
- import "./chunk-M2DCR2J4.js";
17
+ relayIdentity,
18
+ startDaemon
19
+ } from "./chunk-E7JG5PJB.js";
20
+ import "./chunk-MNTIEWKF.js";
22
21
 
23
22
  // src/mcp.ts
24
23
  import { CLI_NAME, validHandle } from "@scp-dev/core";
@@ -45,6 +44,7 @@ async function relayMcpCommand(values, positionals) {
45
44
  await runRelayMcp({
46
45
  connect: () => connectBroker(dir, () => startDaemon(dir)),
47
46
  claim,
47
+ membership: () => membership(process.env, claim.session),
48
48
  input: process.stdin,
49
49
  output: (line) => void process.stdout.write(line),
50
50
  env: process.env,
@@ -0,0 +1,28 @@
1
+ import {
2
+ RelayUsage
3
+ } from "./chunk-E7JG5PJB.js";
4
+ import "./chunk-MNTIEWKF.js";
5
+
6
+ // src/mesh.ts
7
+ import { existsSync } from "fs";
8
+ import { join } from "path";
9
+ import { relayDir } from "@scp-dev/broker";
10
+ var MESH_MODULE = "@scp-dev/mesh";
11
+ async function load() {
12
+ try {
13
+ return await import(MESH_MODULE);
14
+ } catch {
15
+ throw new RelayUsage("swarm mesh is experimental and runs only from a source checkout of the swarm CLI");
16
+ }
17
+ }
18
+ async function meshCommand(argv) {
19
+ return (await load()).meshCommand(argv);
20
+ }
21
+ async function meshBrokerOptions() {
22
+ if (!existsSync(join(relayDir(), "mesh", "mesh.json"))) return {};
23
+ return (await load()).meshBrokerOptions();
24
+ }
25
+ export {
26
+ meshBrokerOptions,
27
+ meshCommand
28
+ };
@@ -2,11 +2,10 @@ import {
2
2
  RelayUsage,
3
3
  peekGrant,
4
4
  stamp
5
- } from "./chunk-3V3DRXB2.js";
6
- import "./chunk-E53YHYEP.js";
5
+ } from "./chunk-E7JG5PJB.js";
7
6
  import {
8
7
  note
9
- } from "./chunk-M2DCR2J4.js";
8
+ } from "./chunk-MNTIEWKF.js";
10
9
 
11
10
  // src/peek.ts
12
11
  import { PREAMBLE, wrapPeek, CLI_NAME } from "@scp-dev/core";
@@ -1,8 +1,7 @@
1
1
  import {
2
2
  RelayUsage
3
- } from "./chunk-3V3DRXB2.js";
4
- import "./chunk-E53YHYEP.js";
5
- import "./chunk-M2DCR2J4.js";
3
+ } from "./chunk-E7JG5PJB.js";
4
+ import "./chunk-MNTIEWKF.js";
6
5
 
7
6
  // src/pi.ts
8
7
  import { spawn } from "child_process";
@@ -3,6 +3,7 @@ import {
3
3
  activeRelay,
4
4
  checkRelayUrl,
5
5
  configDir,
6
+ joinMode,
6
7
  loginMain,
7
8
  logoutMain,
8
9
  readCredentials,
@@ -10,16 +11,16 @@ import {
10
11
  relayVerb,
11
12
  removeCredential,
12
13
  saveCredential,
14
+ setJoinMode,
13
15
  writePrivateJson
14
- } from "./chunk-3RRPUYTE.js";
15
- import "./chunk-3V3DRXB2.js";
16
- import "./chunk-E53YHYEP.js";
17
- import "./chunk-M2DCR2J4.js";
16
+ } from "./chunk-E7JG5PJB.js";
17
+ import "./chunk-MNTIEWKF.js";
18
18
  export {
19
19
  DEFAULT_RELAY,
20
20
  activeRelay,
21
21
  checkRelayUrl,
22
22
  configDir,
23
+ joinMode,
23
24
  loginMain,
24
25
  logoutMain,
25
26
  readCredentials,
@@ -27,5 +28,6 @@ export {
27
28
  relayVerb,
28
29
  removeCredential,
29
30
  saveCredential,
31
+ setJoinMode,
30
32
  writePrivateJson
31
33
  };
@@ -9,11 +9,9 @@ import {
9
9
  machinesVerb,
10
10
  openCloud,
11
11
  readCloudFile
12
- } from "./chunk-U2RSSTNW.js";
13
- import "./chunk-3RRPUYTE.js";
14
- import "./chunk-3V3DRXB2.js";
15
- import "./chunk-E53YHYEP.js";
16
- import "./chunk-M2DCR2J4.js";
12
+ } from "./chunk-ZDU53ZRF.js";
13
+ import "./chunk-E7JG5PJB.js";
14
+ import "./chunk-MNTIEWKF.js";
17
15
  export {
18
16
  CloudRelayClient,
19
17
  CloudUsage,
@@ -15,13 +15,12 @@ import {
15
15
  verifyMount,
16
16
  verifyMusePlugin,
17
17
  whichOnPath
18
- } from "./chunk-WV5FINTW.js";
19
- import "./chunk-5DYFYLAQ.js";
20
- import "./chunk-3V3DRXB2.js";
21
- import "./chunk-RXKBND3T.js";
18
+ } from "./chunk-NOYMSD4F.js";
19
+ import "./chunk-F5QVHJIT.js";
20
+ import "./chunk-OTO6DBU6.js";
22
21
  import "./chunk-YYSLYVNZ.js";
23
- import "./chunk-E53YHYEP.js";
24
- import "./chunk-M2DCR2J4.js";
22
+ import "./chunk-E7JG5PJB.js";
23
+ import "./chunk-MNTIEWKF.js";
25
24
  export {
26
25
  SETUP_HARNESSES,
27
26
  SETUP_HELP,
@@ -29,17 +29,16 @@ import {
29
29
  steerArgv,
30
30
  transcriptReplies,
31
31
  workerName
32
- } from "./chunk-GH7AXFUL.js";
32
+ } from "./chunk-G2SQPY6E.js";
33
33
  import {
34
34
  SPAWN_HELP
35
35
  } from "./chunk-KN7WEIE4.js";
36
- import "./chunk-WV5FINTW.js";
37
- import "./chunk-5DYFYLAQ.js";
38
- import "./chunk-3V3DRXB2.js";
39
- import "./chunk-RXKBND3T.js";
36
+ import "./chunk-NOYMSD4F.js";
37
+ import "./chunk-F5QVHJIT.js";
38
+ import "./chunk-OTO6DBU6.js";
40
39
  import "./chunk-YYSLYVNZ.js";
41
- import "./chunk-E53YHYEP.js";
42
- import "./chunk-M2DCR2J4.js";
40
+ import "./chunk-E7JG5PJB.js";
41
+ import "./chunk-MNTIEWKF.js";
43
42
  export {
44
43
  MESSAGING,
45
44
  SPAWN_HELP,
@@ -1,12 +1,9 @@
1
1
  import {
2
+ RelayUsage,
2
3
  activeRelay,
3
4
  relayCredential
4
- } from "./chunk-3RRPUYTE.js";
5
- import {
6
- RelayUsage
7
- } from "./chunk-3V3DRXB2.js";
8
- import "./chunk-E53YHYEP.js";
9
- import "./chunk-M2DCR2J4.js";
5
+ } from "./chunk-E7JG5PJB.js";
6
+ import "./chunk-MNTIEWKF.js";
10
7
 
11
8
  // src/up.ts
12
9
  import { CLI_NAME } from "@scp-dev/core";
@@ -17,16 +14,16 @@ async function up(positionals, values, cwd, brokerStatus) {
17
14
  const { url } = activeRelay(process.env);
18
15
  const headless = values.token !== void 0 || Boolean(process.env.SCP_TOKEN?.trim());
19
16
  if (headless || !relayCredential(process.env, url).token) {
20
- const code = await (await import("./login-V2WT7QX5.js")).login({
17
+ const code = await (await import("./login-IPS75GLQ.js")).login({
21
18
  ...values.token !== void 0 ? { token: values.token } : {},
22
19
  ...values.label !== void 0 ? { label: values.label } : {},
23
20
  ...values["no-browser"] ? { "no-browser": true } : {}
24
21
  });
25
22
  if (code !== 0) return code;
26
23
  } else out(`already logged in to ${url}`);
27
- const enabled = await (await import("./remote-FYN5ZXE4.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
24
+ const enabled = await (await import("./remote-NTHRHVEP.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
28
25
  if (enabled !== 0) return enabled;
29
- const setup = (await import("./setup-LXG346FJ.js")).relaySetupCommand(["--write"], cwd, out);
26
+ const setup = (await import("./setup-H7GGUID2.js")).relaySetupCommand(["--write"], cwd, out);
30
27
  if (setup !== 0) return setup;
31
28
  const broker = await brokerStatus();
32
29
  if (!broker.enabled) out(`the broker is still local-only; run \`${CLI_NAME} daemon restart\` to link it to ${url}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scp-dev/cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "The swarm command line tool.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scp.dev",
@@ -24,13 +24,16 @@
24
24
  "typecheck": "tsc -p ."
25
25
  },
26
26
  "dependencies": {
27
- "@scp-dev/core": "0.1.1",
28
- "@scp-dev/broker": "0.1.2",
29
- "@scp-dev/hub": "0.1.1",
30
- "@scp-dev/mcp": "0.1.2",
27
+ "@scp-dev/core": "0.1.3",
28
+ "@scp-dev/broker": "0.1.4",
29
+ "@scp-dev/hub": "0.3.0",
30
+ "@scp-dev/mcp": "0.1.4",
31
31
  "smol-toml": "^1.9.1"
32
32
  },
33
33
  "devDependencies": {
34
+ "@openai/codex": "0.159.0",
35
+ "@scp-dev/mesh": "0.0.1",
36
+ "opencode-ai": "1.18.34",
34
37
  "tsup": "^8.5.1"
35
38
  },
36
39
  "publishConfig": {