@scp-dev/cli 0.2.3 → 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.
- package/dist/{chunk-E7JG5PJB.js → chunk-2LIJXXTL.js} +53 -9
- package/dist/{chunk-W3VNCRBX.js → chunk-2OSUVJ2J.js} +3 -3
- package/dist/{chunk-IXCHAWOL.js → chunk-3SHVJIXP.js} +5 -3
- package/dist/{chunk-ZDU53ZRF.js → chunk-IEFXYBX7.js} +6 -3
- package/dist/{chunk-ZMVFN6MU.js → chunk-Q5UI4Z7H.js} +2 -2
- package/dist/{chunk-X4ZLEM47.js → chunk-QRWQIHV6.js} +56 -8
- package/dist/claude-EQVLST73.js +130 -0
- package/dist/cli.js +1 -1
- package/dist/{codex-4IIM6ZE2.js → codex-5GZ7YPES.js} +73 -24
- package/dist/{hooks-P4J6OZCO.js → hooks-B34E2HIP.js} +6 -2
- package/dist/{install-FKGGPT6L.js → install-6KXJY27V.js} +3 -3
- package/dist/{join-RGFHOSLE.js → join-FITJVZCA.js} +3 -1
- package/dist/{login-IPS75GLQ.js → login-AUMLI4FS.js} +2 -2
- package/dist/{main-SQKCKV3B.js → main-BR6TYEGJ.js} +46 -26
- package/dist/{mcp-TUWMLGE4.js → mcp-TUXJ7LE7.js} +8 -6
- package/dist/{mesh-VNZV3POO.js → mesh-H4KL5XAR.js} +1 -1
- package/dist/opencode-ORLGNHWR.js +116 -0
- package/dist/{peek-4D4VGKOW.js → peek-YTCWVPF7.js} +1 -1
- package/dist/{pi-JZ4DXY7V.js → pi-QEZVFDNR.js} +8 -10
- package/dist/{relay-U634RHIJ.js → relay-QWJM372C.js} +1 -1
- package/dist/{remote-NTHRHVEP.js → remote-P4GFUIAP.js} +2 -2
- package/dist/{setup-RO7356M4.js → setup-MGDWKCNU.js} +4 -4
- package/dist/{spawn-BYU2X46L.js → spawn-GG7VFZA6.js} +9 -5
- package/dist/up-B2NEOOC6.js +57 -0
- package/package.json +6 -6
- package/dist/claude-JD7WXMAT.js +0 -98
- package/dist/up-GDD24GPL.js +0 -35
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CODEX_VALUED,
|
|
2
3
|
codexMount,
|
|
3
|
-
codexOverrides
|
|
4
|
-
|
|
4
|
+
codexOverrides,
|
|
5
|
+
toml
|
|
6
|
+
} from "./chunk-3SHVJIXP.js";
|
|
5
7
|
import "./chunk-KN7WEIE4.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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 (
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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-
|
|
43
|
+
} from "./chunk-QRWQIHV6.js";
|
|
42
44
|
import {
|
|
43
45
|
SUBAGENT
|
|
44
46
|
} from "./chunk-YYSLYVNZ.js";
|
|
45
|
-
import "./chunk-
|
|
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-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-Q5UI4Z7H.js";
|
|
6
|
+
import "./chunk-QRWQIHV6.js";
|
|
7
7
|
import "./chunk-YYSLYVNZ.js";
|
|
8
|
-
import "./chunk-
|
|
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-
|
|
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-
|
|
3
|
+
} from "./chunk-IEFXYBX7.js";
|
|
4
4
|
import {
|
|
5
5
|
RelayUsage,
|
|
6
6
|
activeRelay,
|
|
7
7
|
saveCredential
|
|
8
|
-
} from "./chunk-
|
|
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-
|
|
18
|
+
} from "./chunk-2LIJXXTL.js";
|
|
19
19
|
import {
|
|
20
20
|
FAILED,
|
|
21
21
|
exitFlushed,
|
|
@@ -33,7 +33,7 @@ import { parseArgs } from "util";
|
|
|
33
33
|
// package.json
|
|
34
34
|
var package_default = {
|
|
35
35
|
name: "@scp-dev/cli",
|
|
36
|
-
version: "0.
|
|
36
|
+
version: "0.3.0",
|
|
37
37
|
description: "The swarm command line tool.",
|
|
38
38
|
license: "Apache-2.0",
|
|
39
39
|
homepage: "https://scp.dev",
|
|
@@ -137,6 +137,8 @@ usage:
|
|
|
137
137
|
${CLI_NAME} inbox [--wait S] [--keep] your unread messages, then marked read (--keep leaves them unread); --wait waits up
|
|
138
138
|
to S seconds (max 50)
|
|
139
139
|
${CLI_NAME} ack <id|seq>... | --all mark messages read, by message id or seq; --all marks every unread one
|
|
140
|
+
${CLI_NAME} show <id> one message delivered to you, whole, read or not; it stays as it is (a hook
|
|
141
|
+
shows a long message cut, ending in [truncated, N more chars: ${CLI_NAME} show <id>])
|
|
140
142
|
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
143
|
or last: the newest message you were shown, refused while a newer one is unseen.
|
|
142
144
|
${CLI_NAME} ask <to> [text|-] [--wait S] send a request and wait for its reply (default 50 s); --require-reply holds
|
|
@@ -236,6 +238,9 @@ scopes (selectors are comma-separated, e.g. tree:parent,tree:children,group:team
|
|
|
236
238
|
message as a turn (steering a running one, waking an idle session)
|
|
237
239
|
${CLI_NAME} codex [--name N] [--headless] [--] [codex args...]
|
|
238
240
|
runs codex with relay messages waking an idle thread or steering a running turn
|
|
241
|
+
${CLI_NAME} opencode [--name N] [--] [opencode args...]
|
|
242
|
+
runs OpenCode with your own config and arguments, wake on: the plugin wakes an
|
|
243
|
+
idle session, or opens one; refuses to start without the current plugin
|
|
239
244
|
${CLI_NAME} peers [--all] [--roots] who you may message: title, status and the rule that admits each; offline ones and
|
|
240
245
|
sessions nobody spawned that spawned nobody only with --all (--roots: those sessions)
|
|
241
246
|
${CLI_NAME} why <from> <to> whether from may message to, and why
|
|
@@ -268,7 +273,7 @@ agent sessions:
|
|
|
268
273
|
A Claude Code subagent's hooks give it a child endpoint, <session handle>-<short type>-<first 4 of its id>, whose messages
|
|
269
274
|
reach it after its tools; its shell is the session's, so it acts as that child with --as.
|
|
270
275
|
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
|
|
276
|
+
${CLI_NAME} claude [--name N | --as N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake
|
|
272
277
|
it while idle (claude --dangerously-load-development-channels server:scp; SCP_NAME=N)
|
|
273
278
|
--channels pushes messages into a Claude Code that ${CLI_NAME} claude started; setup mounts claude's shim with it.
|
|
274
279
|
Without --name, ${CLI_NAME} claude asks the session to pick its own name with ${CLI_NAME} name <name>.
|
|
@@ -299,7 +304,8 @@ agent sessions:
|
|
|
299
304
|
relay:
|
|
300
305
|
${CLI_NAME} relay use <url> | show select a relay, or show its source and credential status
|
|
301
306
|
${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
|
|
307
|
+
${CLI_NAME} up [--token T] [--machine NAME] [--no-browser] log in if needed, link the broker to the hub, run setup --write,
|
|
308
|
+
then print the wake-enabled launch of each installed harness
|
|
303
309
|
${CLI_NAME} logout remove the stored credential for the active relay
|
|
304
310
|
${CLI_NAME} cloud enable [--machine NAME] | disable | status link the local broker to the hub, or inspect its link
|
|
305
311
|
${CLI_NAME} machines [rm NAME] list the hub's machines, or release one
|
|
@@ -308,8 +314,8 @@ relay:
|
|
|
308
314
|
|
|
309
315
|
identity:
|
|
310
316
|
--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
|
|
317
|
+
($SCP_HARNESS, default the harness this shell runs under, else cli), $SCP_LABELS (role=impl,lane=relay) and $SCP_PARENT. An offline
|
|
318
|
+
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
319
|
suffixed. Run from that agent's own process, it acts as the agent, narrowing only. A session renamed with join or
|
|
314
320
|
name keeps its new handle: its hooks, its shim and commands with $SCP_NAME from its shell claim that one.
|
|
315
321
|
Without --as, at a terminal or with --human you are your own human inbox (me/); messages sent with --human and no
|
|
@@ -370,6 +376,7 @@ var VERBS = {
|
|
|
370
376
|
inbox: [...IDENTITY, "wait", "ack", "keep"],
|
|
371
377
|
reply: IDENTITY,
|
|
372
378
|
ack: [...IDENTITY, "all"],
|
|
379
|
+
show: IDENTITY,
|
|
373
380
|
ask: [...IDENTITY, "wait", "require-reply", "thread", "private", "attach"],
|
|
374
381
|
"ask-wait": [...IDENTITY, "wait"],
|
|
375
382
|
status: [...IDENTITY, "all", "activity"],
|
|
@@ -538,13 +545,14 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
538
545
|
out(package_default.version);
|
|
539
546
|
return 0;
|
|
540
547
|
}
|
|
541
|
-
if (verb === "hook") return (await import("./hooks-
|
|
542
|
-
if (verb === "setup") return (await import("./setup-
|
|
543
|
-
if (verb === "claude") return (await import("./claude-
|
|
544
|
-
if (verb === "pi") return (await import("./pi-
|
|
545
|
-
if (verb === "codex") return (await import("./codex-
|
|
548
|
+
if (verb === "hook") return (await import("./hooks-B34E2HIP.js")).relayHookCommand(rest, startDaemon);
|
|
549
|
+
if (verb === "setup") return (await import("./setup-MGDWKCNU.js")).relaySetupCommand(rest, cwd, out);
|
|
550
|
+
if (verb === "claude") return (await import("./claude-EQVLST73.js")).claudeCommand(rest);
|
|
551
|
+
if (verb === "pi") return (await import("./pi-QEZVFDNR.js")).piCommand(rest);
|
|
552
|
+
if (verb === "codex") return (await import("./codex-5GZ7YPES.js")).codexCommand(rest);
|
|
553
|
+
if (verb === "opencode") return (await import("./opencode-ORLGNHWR.js")).opencodeCommand(rest, process.env, void 0, cwd);
|
|
546
554
|
if (verb === "spawn") {
|
|
547
|
-
const { splitSpawn, spawnCommand } = await import("./spawn-
|
|
555
|
+
const { splitSpawn, spawnCommand } = await import("./spawn-GG7VFZA6.js");
|
|
548
556
|
const { flags } = splitSpawn(rest);
|
|
549
557
|
if (flags.includes("--help") || flags.includes("-h")) {
|
|
550
558
|
out(flags.includes("--all") ? RELAY_HELP : SPAWN_HELP);
|
|
@@ -552,7 +560,7 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
552
560
|
}
|
|
553
561
|
return spawnCommand(rest, cwd);
|
|
554
562
|
}
|
|
555
|
-
if (verb === "mesh") return (await import("./mesh-
|
|
563
|
+
if (verb === "mesh") return (await import("./mesh-H4KL5XAR.js")).meshCommand(rest);
|
|
556
564
|
const allowed = VERBS[verb];
|
|
557
565
|
if (!allowed) throw new RelayUsage(`unknown command "${verb}"`);
|
|
558
566
|
let parsed;
|
|
@@ -572,11 +580,11 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
572
580
|
return daemon(values.idle, values.policy);
|
|
573
581
|
}
|
|
574
582
|
if (verb === "mcp")
|
|
575
|
-
return (await import("./mcp-
|
|
583
|
+
return (await import("./mcp-TUXJ7LE7.js")).relayMcpCommand(values, positionals);
|
|
576
584
|
if (verb === "hooks") {
|
|
577
585
|
if (positionals[0] !== "install" || positionals.length > 1) throw new RelayUsage(`${CLI_NAME} hooks takes install --harness claude|codex|opencode|agy|muse`);
|
|
578
586
|
if (values.write && values.print) throw new RelayUsage("--print and --write: pick one");
|
|
579
|
-
return (await import("./install-
|
|
587
|
+
return (await import("./install-6KXJY27V.js")).installHooks({
|
|
580
588
|
harness: values.harness,
|
|
581
589
|
scope: values.scope,
|
|
582
590
|
write: values.write === true,
|
|
@@ -587,26 +595,26 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
587
595
|
out
|
|
588
596
|
});
|
|
589
597
|
}
|
|
590
|
-
if (verb === "relay") return (await import("./relay-
|
|
591
|
-
if (verb === "up") return (await import("./up-
|
|
598
|
+
if (verb === "relay") return (await import("./relay-QWJM372C.js")).relayVerb(positionals, values.json === true);
|
|
599
|
+
if (verb === "up") return (await import("./up-B2NEOOC6.js")).up(positionals, values, cwd, relinkBroker);
|
|
592
600
|
if (verb === "login") {
|
|
593
|
-
const code = await (await import("./relay-
|
|
601
|
+
const code = await (await import("./relay-QWJM372C.js")).loginMain(positionals, values);
|
|
594
602
|
if (code === 0) await relinkRunning();
|
|
595
603
|
return code;
|
|
596
604
|
}
|
|
597
|
-
if (verb === "logout") return (await import("./relay-
|
|
605
|
+
if (verb === "logout") return (await import("./relay-QWJM372C.js")).logoutMain(positionals, values.json === true);
|
|
598
606
|
if (verb === "cloud") {
|
|
599
|
-
const code = await (await import("./remote-
|
|
607
|
+
const code = await (await import("./remote-P4GFUIAP.js")).cloudMain(positionals, values, cloudStatus);
|
|
600
608
|
if (code === 0 && positionals[0] === "enable") await relinkRunning();
|
|
601
609
|
return code;
|
|
602
610
|
}
|
|
603
|
-
if (verb === "join") return (await import("./join-
|
|
611
|
+
if (verb === "join") return (await import("./join-FITJVZCA.js")).joinCommand(values, positionals, cwd, out);
|
|
604
612
|
if (verb === "holds") holdsArgs(values, positionals);
|
|
605
613
|
const bodyFromStdin = (verb === "send" || verb === "ask" || verb === "reply") && (positionals[1] === void 0 || positionals[1] === "-");
|
|
606
614
|
const opened = await open(verb, values, cwd, bodyFromStdin);
|
|
607
615
|
const { client } = opened;
|
|
608
616
|
try {
|
|
609
|
-
if (verb === "machines") return await (await import("./remote-
|
|
617
|
+
if (verb === "machines") return await (await import("./remote-P4GFUIAP.js")).machinesVerb(client, positionals, values.json === true);
|
|
610
618
|
return await runVerb(opened, verb, values, positionals, cwd);
|
|
611
619
|
} finally {
|
|
612
620
|
client.close();
|
|
@@ -783,9 +791,9 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
783
791
|
return 0;
|
|
784
792
|
}
|
|
785
793
|
case "peek":
|
|
786
|
-
return (await import("./peek-
|
|
794
|
+
return (await import("./peek-YTCWVPF7.js")).peekCommand(localClient(client, broker, verb), values, positionals);
|
|
787
795
|
case "set-peek":
|
|
788
|
-
return (await import("./peek-
|
|
796
|
+
return (await import("./peek-YTCWVPF7.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
|
|
789
797
|
case "why": {
|
|
790
798
|
const [from, to] = positionals;
|
|
791
799
|
if (!from || !to || positionals.length > 2) throw new RelayUsage(`${CLI_NAME} why takes <from> <to>`);
|
|
@@ -870,6 +878,18 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
870
878
|
if (!values.keep && messages.length) await client.request("ack", { seqs: messages.map((m) => m.seq) });
|
|
871
879
|
return 0;
|
|
872
880
|
}
|
|
881
|
+
case "show": {
|
|
882
|
+
if (positionals.length !== 1) throw new RelayUsage(`${CLI_NAME} show takes one message id`);
|
|
883
|
+
const message = await lookup(client, positionals[0]);
|
|
884
|
+
if (!message) throw new RelayUsage(`no message ${positionals[0]} was delivered to you`);
|
|
885
|
+
if (asJson) json({ preamble: PREAMBLE, message });
|
|
886
|
+
else {
|
|
887
|
+
out(PREAMBLE);
|
|
888
|
+
out();
|
|
889
|
+
printMessage(message);
|
|
890
|
+
}
|
|
891
|
+
return 0;
|
|
892
|
+
}
|
|
873
893
|
case "ack": {
|
|
874
894
|
const usage = `${CLI_NAME} ack takes message ids or seq numbers, or --all`;
|
|
875
895
|
if (values.all ? positionals.length : !positionals.length) throw new RelayUsage(usage);
|
|
@@ -1383,11 +1403,11 @@ async function daemon(idle, policyFile) {
|
|
|
1383
1403
|
});
|
|
1384
1404
|
let broker;
|
|
1385
1405
|
try {
|
|
1386
|
-
const { daemonBridge } = await import("./remote-
|
|
1406
|
+
const { daemonBridge } = await import("./remote-P4GFUIAP.js");
|
|
1387
1407
|
const bridge = await daemonBridge();
|
|
1388
1408
|
broker = await startBroker({
|
|
1389
1409
|
dir: relayDir(),
|
|
1390
|
-
...bridge ? { bridge } : await (await import("./mesh-
|
|
1410
|
+
...bridge ? { bridge } : await (await import("./mesh-H4KL5XAR.js")).meshBrokerOptions(),
|
|
1391
1411
|
bridgeConfig: () => daemonBridge(),
|
|
1392
1412
|
...idleMs !== void 0 ? { idleMs } : {},
|
|
1393
1413
|
...policyFile ? { policyPath: resolve(policyFile) } : {}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
spawnDetached
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3SHVJIXP.js";
|
|
4
4
|
import "./chunk-KN7WEIE4.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-2OSUVJ2J.js";
|
|
6
6
|
import {
|
|
7
7
|
mcpInstall
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
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-
|
|
20
|
+
} from "./chunk-2LIJXXTL.js";
|
|
20
21
|
import "./chunk-MNTIEWKF.js";
|
|
21
22
|
|
|
22
23
|
// src/mcp.ts
|
|
@@ -37,7 +38,8 @@ async function relayMcpCommand(values, positionals) {
|
|
|
37
38
|
fallbackHarness: "mcp",
|
|
38
39
|
cwd: process.cwd(),
|
|
39
40
|
env: process.env,
|
|
40
|
-
harnessPid: () => harnessPid()
|
|
41
|
+
harnessPid: () => harnessPid(),
|
|
42
|
+
inferHarness: (pid) => harnessAt(pid)
|
|
41
43
|
});
|
|
42
44
|
if (!validHandle(claim.handle)) throw new RelayUsage(`${JSON.stringify(claim.handle)} is not a valid agent handle (me, user and relay are reserved)`);
|
|
43
45
|
const dir = relayDir();
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mcpFile,
|
|
3
|
+
mountedEntry,
|
|
4
|
+
whichOnPath
|
|
5
|
+
} from "./chunk-2OSUVJ2J.js";
|
|
6
|
+
import {
|
|
7
|
+
hooksTarget
|
|
8
|
+
} from "./chunk-Q5UI4Z7H.js";
|
|
9
|
+
import {
|
|
10
|
+
opencodePlugin
|
|
11
|
+
} from "./chunk-QRWQIHV6.js";
|
|
12
|
+
import "./chunk-YYSLYVNZ.js";
|
|
13
|
+
import {
|
|
14
|
+
RelayUsage,
|
|
15
|
+
gitToplevel
|
|
16
|
+
} from "./chunk-2LIJXXTL.js";
|
|
17
|
+
import "./chunk-MNTIEWKF.js";
|
|
18
|
+
|
|
19
|
+
// src/opencode.ts
|
|
20
|
+
import { spawn } from "child_process";
|
|
21
|
+
import { existsSync, readFileSync } from "fs";
|
|
22
|
+
import { join } from "path";
|
|
23
|
+
import { CLI_NAME, validHandle } from "@scp-dev/core";
|
|
24
|
+
var OPENCODE_HELP = `${CLI_NAME} opencode [--name N | --as N] [--] [opencode args...]
|
|
25
|
+
runs OpenCode with your own config and arguments, wake on: the ${CLI_NAME} plugin wakes the session on a new message, and opens
|
|
26
|
+
one when none is open yet. Refuses to start without the current plugin; ${CLI_NAME} setup --write installs it.
|
|
27
|
+
$SCP_OPENCODE_BIN picks the opencode binary (default opencode on PATH).`;
|
|
28
|
+
function opencodeLaunch(args, env, bin) {
|
|
29
|
+
let name;
|
|
30
|
+
const rest = [];
|
|
31
|
+
for (let i = 0; i < args.length; i++) {
|
|
32
|
+
const arg = args[i];
|
|
33
|
+
if (arg === "--") {
|
|
34
|
+
rest.push(...args.slice(i + 1));
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
if (arg === "--name" || arg === "--as") {
|
|
38
|
+
name = args[++i];
|
|
39
|
+
if (name === void 0) throw new RelayUsage(`${CLI_NAME} opencode ${arg} takes a name`);
|
|
40
|
+
} else if (arg.startsWith("--name=") || arg.startsWith("--as=")) name = arg.slice(arg.indexOf("=") + 1);
|
|
41
|
+
else rest.push(arg);
|
|
42
|
+
}
|
|
43
|
+
name = name?.replace(/^me\//, "");
|
|
44
|
+
if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`${JSON.stringify(name)} is not a valid agent handle`);
|
|
45
|
+
return {
|
|
46
|
+
argv: [bin, ...rest],
|
|
47
|
+
env: { ...env, SCP_HARNESS: "opencode", SCP_WAKE: "1", ...name !== void 0 ? { SCP_NAME: name } : {} }
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function pluginState(env, cwd) {
|
|
51
|
+
const current = /* @__PURE__ */ new Set([opencodePlugin({ blockStop: false }), opencodePlugin({ blockStop: true })]);
|
|
52
|
+
const files = [hooksTarget("opencode", "project", env, cwd), hooksTarget("opencode", "user", env, cwd)].filter((file) => existsSync(file));
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
try {
|
|
55
|
+
if (current.has(readFileSync(file, "utf8"))) return { file, current: true };
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { ...files[0] ? { file: files[0] } : {}, current: false };
|
|
60
|
+
}
|
|
61
|
+
function pluginProblem(env, cwd) {
|
|
62
|
+
const plugin = pluginState(env, cwd);
|
|
63
|
+
if (plugin.current) return void 0;
|
|
64
|
+
const fix = `run ${CLI_NAME} setup --write (or ${CLI_NAME} hooks install --harness opencode --scope user --write)`;
|
|
65
|
+
return plugin.file ? `${CLI_NAME} opencode: ${plugin.file} is not this ${CLI_NAME}'s plugin, so wake is not guaranteed; ${fix}` : `${CLI_NAME} opencode: OpenCode has no ${CLI_NAME} plugin, so messages would not wake it; ${fix}`;
|
|
66
|
+
}
|
|
67
|
+
function opencodeMountHint(env, cwd) {
|
|
68
|
+
const project = gitToplevel(cwd) ?? cwd;
|
|
69
|
+
const files = [mcpFile("opencode", env), ...["opencode.json", "opencode.jsonc"].map((name) => join(project, name))];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
try {
|
|
72
|
+
if (existsSync(file) && mountedEntry("opencode", file)) return void 0;
|
|
73
|
+
} catch {
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return `${CLI_NAME} opencode: OpenCode has no scp MCP mount, so the session has no relay tools; run ${CLI_NAME} setup --write`;
|
|
77
|
+
}
|
|
78
|
+
async function opencodeCommand(args, env = process.env, err = (line) => void process.stderr.write(`${line}
|
|
79
|
+
`), cwd = process.cwd()) {
|
|
80
|
+
const own = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
|
|
81
|
+
if (own.includes("--help") || own.includes("-h")) {
|
|
82
|
+
process.stdout.write(`${OPENCODE_HELP}
|
|
83
|
+
`);
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
const bin = env.SCP_OPENCODE_BIN || whichOnPath("opencode", env);
|
|
87
|
+
if (!bin) throw new RelayUsage(`opencode is not on PATH: install it with npm i -g opencode-ai, then run ${CLI_NAME} setup --write`);
|
|
88
|
+
const launch = opencodeLaunch(args, env, bin);
|
|
89
|
+
const problem = pluginProblem(env, cwd);
|
|
90
|
+
if (problem) throw new RelayUsage(problem);
|
|
91
|
+
const hint = opencodeMountHint(env, cwd);
|
|
92
|
+
if (hint) err(hint);
|
|
93
|
+
return await new Promise((resolve, reject) => {
|
|
94
|
+
const child = spawn(launch.argv[0], launch.argv.slice(1), { env: launch.env, stdio: "inherit" });
|
|
95
|
+
const forward = (signal) => child.kill(signal);
|
|
96
|
+
const signals = ["SIGTERM", "SIGHUP"];
|
|
97
|
+
const ignore = () => {
|
|
98
|
+
};
|
|
99
|
+
for (const signal of signals) process.on(signal, forward);
|
|
100
|
+
process.on("SIGINT", ignore);
|
|
101
|
+
child.once("error", (error) => reject(new RelayUsage(`could not start ${launch.argv[0]}: ${error.message}`)));
|
|
102
|
+
child.once("exit", (code, signal) => {
|
|
103
|
+
for (const each of signals) process.off(each, forward);
|
|
104
|
+
process.off("SIGINT", ignore);
|
|
105
|
+
resolve(code ?? (signal ? 1 : 0));
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
OPENCODE_HELP,
|
|
111
|
+
opencodeCommand,
|
|
112
|
+
opencodeLaunch,
|
|
113
|
+
opencodeMountHint,
|
|
114
|
+
pluginProblem,
|
|
115
|
+
pluginState
|
|
116
|
+
};
|