@scp-dev/cli 0.2.4 → 0.3.0

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.
@@ -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/pi.ts
@@ -10,22 +10,21 @@ import { fileURLToPath } from "url";
10
10
  import { CLI_NAME, validHandle } from "@scp-dev/core";
11
11
  var PI_HELP = `${CLI_NAME} pi [--name NAME | --as NAME] [--] [pi args...]
12
12
  runs pi with the bundled SCP extension: the relay tools, and each new message as a turn, waking the session when idle.
13
+ pi's own --name and every other argument after the leading launcher flags go to pi untouched.
13
14
  $SCP_PI_BIN picks the pi binary (default pi on PATH), $SCP_PI_EXTENSION the extension file.`;
14
15
  function piLaunch(args, env, extension) {
15
16
  let name;
16
- const rest = [];
17
- for (let i = 0; i < args.length; i++) {
17
+ let i = 0;
18
+ for (; i < args.length; i++) {
18
19
  const arg = args[i];
19
- if (arg === "--") {
20
- rest.push(...args.slice(i + 1));
21
- break;
22
- }
23
20
  if (arg === "--name" || arg === "--as") {
24
21
  name = args[++i];
25
22
  if (name === void 0) throw new RelayUsage(`${CLI_NAME} pi ${arg} takes a name`);
26
23
  } else if (arg.startsWith("--name=") || arg.startsWith("--as=")) name = arg.slice(arg.indexOf("=") + 1);
27
- else rest.push(arg);
24
+ else break;
28
25
  }
26
+ if (args[i] === "--") i++;
27
+ const rest = args.slice(i);
29
28
  name = name?.replace(/^me\//, "");
30
29
  if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`${JSON.stringify(name)} is not a valid agent handle`);
31
30
  return {
@@ -42,8 +41,7 @@ function extensionPath(env, base = import.meta.url) {
42
41
  throw new RelayUsage(`${CLI_NAME} pi found no pi extension next to ${CLI_NAME}: reinstall it, or set $SCP_PI_EXTENSION to the extension file`);
43
42
  }
44
43
  async function piCommand(args) {
45
- const own = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
46
- if (own.includes("--help") || own.includes("-h")) {
44
+ if (args[0] === "--help" || args[0] === "-h") {
47
45
  process.stdout.write(`${PI_HELP}
48
46
  `);
49
47
  return 0;
@@ -13,7 +13,7 @@ import {
13
13
  saveCredential,
14
14
  setJoinMode,
15
15
  writePrivateJson
16
- } from "./chunk-E7JG5PJB.js";
16
+ } from "./chunk-2LIJXXTL.js";
17
17
  import "./chunk-MNTIEWKF.js";
18
18
  export {
19
19
  DEFAULT_RELAY,
@@ -9,8 +9,8 @@ import {
9
9
  machinesVerb,
10
10
  openCloud,
11
11
  readCloudFile
12
- } from "./chunk-ZDU53ZRF.js";
13
- import "./chunk-E7JG5PJB.js";
12
+ } from "./chunk-IEFXYBX7.js";
13
+ import "./chunk-2LIJXXTL.js";
14
14
  import "./chunk-MNTIEWKF.js";
15
15
  export {
16
16
  CloudRelayClient,
@@ -15,11 +15,11 @@ import {
15
15
  verifyMount,
16
16
  verifyMusePlugin,
17
17
  whichOnPath
18
- } from "./chunk-W3VNCRBX.js";
19
- import "./chunk-ZMVFN6MU.js";
20
- import "./chunk-X4ZLEM47.js";
18
+ } from "./chunk-2OSUVJ2J.js";
19
+ import "./chunk-Q5UI4Z7H.js";
20
+ import "./chunk-QRWQIHV6.js";
21
21
  import "./chunk-YYSLYVNZ.js";
22
- import "./chunk-E7JG5PJB.js";
22
+ import "./chunk-2LIJXXTL.js";
23
23
  import "./chunk-MNTIEWKF.js";
24
24
  export {
25
25
  SETUP_HARNESSES,
@@ -1,4 +1,5 @@
1
1
  import {
2
+ CODEX_VALUED,
2
3
  MESSAGING,
3
4
  SPAWN_RUNNER,
4
5
  STDIN_WAIT_MS,
@@ -27,19 +28,21 @@ import {
27
28
  splitSpawn,
28
29
  stderrOutput,
29
30
  steerArgv,
31
+ toml,
30
32
  transcriptReplies,
31
33
  workerName
32
- } from "./chunk-IXCHAWOL.js";
34
+ } from "./chunk-3SHVJIXP.js";
33
35
  import {
34
36
  SPAWN_HELP
35
37
  } from "./chunk-KN7WEIE4.js";
36
- import "./chunk-W3VNCRBX.js";
37
- import "./chunk-ZMVFN6MU.js";
38
- import "./chunk-X4ZLEM47.js";
38
+ import "./chunk-2OSUVJ2J.js";
39
+ import "./chunk-Q5UI4Z7H.js";
40
+ import "./chunk-QRWQIHV6.js";
39
41
  import "./chunk-YYSLYVNZ.js";
40
- import "./chunk-E7JG5PJB.js";
42
+ import "./chunk-2LIJXXTL.js";
41
43
  import "./chunk-MNTIEWKF.js";
42
44
  export {
45
+ CODEX_VALUED,
43
46
  MESSAGING,
44
47
  SPAWN_HELP,
45
48
  SPAWN_RUNNER,
@@ -69,6 +72,7 @@ export {
69
72
  splitSpawn,
70
73
  stderrOutput,
71
74
  steerArgv,
75
+ toml,
72
76
  transcriptReplies,
73
77
  workerName
74
78
  };
@@ -0,0 +1,57 @@
1
+ import {
2
+ RelayUsage,
3
+ activeRelay,
4
+ relayCredential
5
+ } from "./chunk-2LIJXXTL.js";
6
+ import "./chunk-MNTIEWKF.js";
7
+
8
+ // src/up.ts
9
+ import { CLI_NAME } from "@scp-dev/core";
10
+ var WAKE_LAUNCH = [
11
+ ["claude", `${CLI_NAME} claude --name <name> Claude Code with the scp channel: messages wake it`],
12
+ ["codex", `${CLI_NAME} codex --name <name> messages start or steer its turns`],
13
+ ["pi", `${CLI_NAME} pi --name <name> messages arrive as turns`],
14
+ ["opencode", "opencode its scp plugin wakes an idle session (on by default)"]
15
+ ];
16
+ function nextSteps(installed) {
17
+ const launches = WAKE_LAUNCH.filter(([harness]) => installed.has(harness)).map(([, line]) => ` ${line}`);
18
+ return [
19
+ "next: start each agent session wake-enabled:",
20
+ ...launches,
21
+ ` ${CLI_NAME} join <name> in a session already running: it sees messages only at turn boundaries until relaunched as above`,
22
+ ` ${CLI_NAME} setup checks the hooks and MCP mounts (it lists nothing to do once they are installed)`
23
+ ];
24
+ }
25
+ async function up(positionals, values, cwd, brokerStatus) {
26
+ if (positionals.length) throw new RelayUsage(`${CLI_NAME} up takes only flags`);
27
+ const out = (line) => process.stdout.write(`${line}
28
+ `);
29
+ const { url } = activeRelay(process.env);
30
+ const headless = values.token !== void 0 || Boolean(process.env.SCP_TOKEN?.trim());
31
+ if (headless || !relayCredential(process.env, url).token) {
32
+ const code = await (await import("./login-AUMLI4FS.js")).login({
33
+ ...values.token !== void 0 ? { token: values.token } : {},
34
+ ...values.label !== void 0 ? { label: values.label } : {},
35
+ ...values["no-browser"] ? { "no-browser": true } : {}
36
+ });
37
+ if (code !== 0) return code;
38
+ } else out(`already logged in to ${url}`);
39
+ const enabled = await (await import("./remote-P4GFUIAP.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
40
+ if (enabled !== 0) return enabled;
41
+ const setup = (await import("./setup-MGDWKCNU.js")).relaySetupCommand(["--write"], cwd, out);
42
+ if (setup !== 0) return setup;
43
+ const broker = await brokerStatus();
44
+ if (!broker.enabled) out(`the broker is still local-only; run \`${CLI_NAME} daemon restart\` to link it to ${url}`);
45
+ else out(`broker linked to ${broker.url ?? url}${broker.connected ? "" : " (connecting)"}`);
46
+ const { detectHarnesses, whichOnPath } = await import("./setup-MGDWKCNU.js");
47
+ const which = (name) => whichOnPath(name, process.env);
48
+ const installed = new Set(detectHarnesses(process.env, which).keys());
49
+ if (which("pi")) installed.add("pi");
50
+ out("");
51
+ for (const line of nextSteps(installed)) out(line);
52
+ return 0;
53
+ }
54
+ export {
55
+ nextSteps,
56
+ up
57
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scp-dev/cli",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "The swarm command line tool.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://scp.dev",
@@ -24,15 +24,15 @@
24
24
  "typecheck": "tsc -p ."
25
25
  },
26
26
  "dependencies": {
27
- "@scp-dev/core": "0.1.4",
28
- "@scp-dev/broker": "0.1.6",
29
- "@scp-dev/hub": "0.4.0",
30
- "@scp-dev/mcp": "0.1.6",
27
+ "@scp-dev/core": "0.1.5",
28
+ "@scp-dev/broker": "0.2.0",
29
+ "@scp-dev/hub": "0.5.0",
30
+ "@scp-dev/mcp": "0.1.7",
31
31
  "smol-toml": "^1.9.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@openai/codex": "0.159.0",
35
- "@scp-dev/mesh": "0.0.3",
35
+ "@scp-dev/mesh": "0.0.4",
36
36
  "opencode-ai": "1.18.34",
37
37
  "tsup": "^8.5.1"
38
38
  },
@@ -1,98 +0,0 @@
1
- import {
2
- entryArgv,
3
- mcpFile,
4
- mountedEntry,
5
- whichOnPath
6
- } from "./chunk-W3VNCRBX.js";
7
- import "./chunk-ZMVFN6MU.js";
8
- import "./chunk-X4ZLEM47.js";
9
- import "./chunk-YYSLYVNZ.js";
10
- import {
11
- CHANNELS_ENV,
12
- RelayUsage,
13
- deriveHandle,
14
- joinMode
15
- } from "./chunk-E7JG5PJB.js";
16
- import "./chunk-MNTIEWKF.js";
17
-
18
- // src/claude.ts
19
- import { spawn } from "child_process";
20
- import { CLI_NAME, validHandle } from "@scp-dev/core";
21
- var CHANNEL_FLAGS = ["--dangerously-load-development-channels", "server:scp"];
22
- function claudeArgv(argv) {
23
- let name;
24
- let i = 0;
25
- if (argv[0] === "--name") {
26
- if (argv[1] === void 0) throw new RelayUsage(`${CLI_NAME} claude --name takes a handle`);
27
- name = argv[1];
28
- i = 2;
29
- } else if (argv[0]?.startsWith("--name=")) {
30
- name = argv[0].slice("--name=".length);
31
- i = 1;
32
- }
33
- if (argv[i] === "--") i++;
34
- if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`--name ${JSON.stringify(name)} is not a valid agent handle`);
35
- return { ...name !== void 0 ? { name } : {}, args: argv.slice(i) };
36
- }
37
- function namePrompt(name, args, env, cwd) {
38
- if (name !== void 0 || joinMode(env) === "manual" || args.some((arg) => /^--(append-)?system-prompt/.test(arg))) return [];
39
- const handle = env.SCP_NAME || deriveHandle("claude-code", cwd);
40
- return [
41
- "--append-system-prompt",
42
- `You are on the SCP network as ${handle}. Pick a short descriptive name for yourself with \`${CLI_NAME} name <name>\` when you know what you're working on.`
43
- ];
44
- }
45
- function mountHint(env) {
46
- const file = mcpFile("claude", env);
47
- let entry;
48
- try {
49
- entry = mountedEntry("claude", file);
50
- } catch {
51
- return `${CLI_NAME} claude: ${file} is unreadable, so the scp mount is unchecked`;
52
- }
53
- if (!entry) return `${CLI_NAME} claude: Claude Code has no scp MCP mount; run ${CLI_NAME} setup --write`;
54
- if (!entryArgv("claude", entry).includes("--channels"))
55
- return `${CLI_NAME} claude: the scp mount lacks --channels, so messages will not wake this session; re-run ${CLI_NAME} setup --write`;
56
- return void 0;
57
- }
58
- async function claudeCommand(argv, env = process.env, err = (line) => void process.stderr.write(`${line}
59
- `), cwd = process.cwd()) {
60
- if (argv[0] === "--help" || argv[0] === "-h") {
61
- process.stdout.write(`${CLI_NAME} claude [--name N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake it
62
- `);
63
- return 0;
64
- }
65
- const { name, args } = claudeArgv(argv);
66
- const bin = whichOnPath("claude", env);
67
- if (!bin) throw new RelayUsage("claude is not on PATH");
68
- const hint = mountHint(env);
69
- if (hint) err(hint);
70
- const child = spawn(bin, [...CHANNEL_FLAGS, ...namePrompt(name, args, env, cwd), ...args], {
71
- stdio: "inherit",
72
- env: { ...env, ...name !== void 0 ? { SCP_NAME: name } : {}, [CHANNELS_ENV]: String(process.pid) }
73
- });
74
- const forward = (signal) => () => void child.kill(signal);
75
- const handlers = ["SIGTERM", "SIGHUP"].map((signal) => [signal, forward(signal)]);
76
- const ignore = () => {
77
- };
78
- process.on("SIGINT", ignore);
79
- for (const [signal, handler] of handlers) process.on(signal, handler);
80
- try {
81
- return await new Promise((resolve, reject) => {
82
- child.once("error", reject);
83
- child.once("exit", (code, signal) => resolve(code ?? (signal ? 128 + (signalNumber(signal) ?? 1) : 1)));
84
- });
85
- } finally {
86
- process.off("SIGINT", ignore);
87
- for (const [signal, handler] of handlers) process.off(signal, handler);
88
- }
89
- }
90
- var SIGNALS = { SIGHUP: 1, SIGINT: 2, SIGKILL: 9, SIGTERM: 15 };
91
- var signalNumber = (signal) => SIGNALS[signal];
92
- export {
93
- CHANNEL_FLAGS,
94
- claudeArgv,
95
- claudeCommand,
96
- mountHint,
97
- namePrompt
98
- };
@@ -1,35 +0,0 @@
1
- import {
2
- RelayUsage,
3
- activeRelay,
4
- relayCredential
5
- } from "./chunk-E7JG5PJB.js";
6
- import "./chunk-MNTIEWKF.js";
7
-
8
- // src/up.ts
9
- import { CLI_NAME } from "@scp-dev/core";
10
- async function up(positionals, values, cwd, brokerStatus) {
11
- if (positionals.length) throw new RelayUsage(`${CLI_NAME} up takes only flags`);
12
- const out = (line) => process.stdout.write(`${line}
13
- `);
14
- const { url } = activeRelay(process.env);
15
- const headless = values.token !== void 0 || Boolean(process.env.SCP_TOKEN?.trim());
16
- if (headless || !relayCredential(process.env, url).token) {
17
- const code = await (await import("./login-IPS75GLQ.js")).login({
18
- ...values.token !== void 0 ? { token: values.token } : {},
19
- ...values.label !== void 0 ? { label: values.label } : {},
20
- ...values["no-browser"] ? { "no-browser": true } : {}
21
- });
22
- if (code !== 0) return code;
23
- } else out(`already logged in to ${url}`);
24
- const enabled = await (await import("./remote-NTHRHVEP.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
25
- if (enabled !== 0) return enabled;
26
- const setup = (await import("./setup-RO7356M4.js")).relaySetupCommand(["--write"], cwd, out);
27
- if (setup !== 0) return setup;
28
- const broker = await brokerStatus();
29
- if (!broker.enabled) out(`the broker is still local-only; run \`${CLI_NAME} daemon restart\` to link it to ${url}`);
30
- else out(`broker linked to ${broker.url ?? url}${broker.connected ? "" : " (connecting)"}`);
31
- return 0;
32
- }
33
- export {
34
- up
35
- };