@scp-dev/cli 0.5.0 → 0.7.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-XQHPDNVG.js → chunk-277BAY7J.js} +23 -3
- package/dist/{chunk-AAFHLHXG.js → chunk-3SOIVANO.js} +49 -4
- package/dist/{chunk-2342GTME.js → chunk-LRQP25RI.js} +2 -2
- package/dist/{chunk-H2XQXMZU.js → chunk-LUDLDWH2.js} +1 -1
- package/dist/{codex-OSGHQMK6.js → chunk-LWMTZH36.js} +6 -10
- package/dist/{chunk-7RA7E5WR.js → chunk-RZUQ422C.js} +14 -6
- package/dist/{chunk-UKQBH4AQ.js → chunk-SNW53KT3.js} +3 -3
- package/dist/claude-WEHOLJ26.js +290 -0
- package/dist/cli.js +1 -1
- package/dist/codex-54UWPDY5.js +26 -0
- package/dist/{hooks-BZANUJPY.js → hooks-ULKKUHPF.js} +6 -2
- package/dist/{install-25J5QB63.js → install-5A3ETGF6.js} +3 -3
- package/dist/{join-EOLHE3LJ.js → join-FFT7RQRW.js} +1 -1
- package/dist/{login-6MVD3VLC.js → login-DCNCC3RS.js} +2 -2
- package/dist/{main-F2NFQ2KC.js → main-CDLW3MV5.js} +45 -26
- package/dist/{mcp-BH526TOG.js → mcp-7GB35CTY.js} +7 -6
- package/dist/{mesh-OZILTV5P.js → mesh-TDOVAHZ3.js} +1 -1
- package/dist/muse-S7FPY4CL.js +429 -0
- package/dist/{opencode-XV33XTL5.js → opencode-WFMQYCI2.js} +4 -4
- package/dist/{peek-LFQR6TCS.js → peek-X4QQFMQJ.js} +1 -1
- package/dist/{pi-N3DX37PD.js → pi-5IPU6GA4.js} +1 -1
- package/dist/{relay-SF3UWIPM.js → relay-7WBNUACH.js} +1 -1
- package/dist/{remote-UQJ5YYCC.js → remote-YO5B2HQI.js} +2 -2
- package/dist/resume-3ZRDXSTX.js +308 -0
- package/dist/{setup-UJCIMYU4.js → setup-U7CWN5QP.js} +4 -4
- package/dist/{spawn-JYD7NCQT.js → spawn-VZK5MD3R.js} +13 -5
- package/dist/{up-3YJU7YX6.js → up-S4KJBWI6.js} +8 -6
- package/package.json +6 -6
- package/dist/claude-RNH2E4GU.js +0 -130
|
@@ -13,11 +13,11 @@ import {
|
|
|
13
13
|
psStart,
|
|
14
14
|
relayIdentity,
|
|
15
15
|
sessionKey
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-3SOIVANO.js";
|
|
17
17
|
|
|
18
18
|
// src/hooks.ts
|
|
19
19
|
import { Readable } from "stream";
|
|
20
|
-
import { closeSync, fstatSync, openSync, readFileSync, readSync, writeSync } from "fs";
|
|
20
|
+
import { closeSync, fstatSync, openSync, readFileSync, readSync, writeFileSync, writeSync } from "fs";
|
|
21
21
|
import { homedir } from "os";
|
|
22
22
|
import { basename, join } from "path";
|
|
23
23
|
import {
|
|
@@ -304,7 +304,9 @@ function titleText(text) {
|
|
|
304
304
|
return line || void 0;
|
|
305
305
|
}
|
|
306
306
|
function promptTitle(prompt) {
|
|
307
|
-
|
|
307
|
+
const first = prompt?.split("\n").find((line) => line.trim());
|
|
308
|
+
if (first?.startsWith(PREAMBLE) || first?.startsWith(PREAMBLE_BRIEF)) return void 0;
|
|
309
|
+
return titleText(first);
|
|
308
310
|
}
|
|
309
311
|
function transcriptTitle(path, session) {
|
|
310
312
|
if (!path || !session || !path.startsWith("/") || !path.endsWith(`/${session}.jsonl`)) return void 0;
|
|
@@ -409,6 +411,17 @@ function tldrEvery(env) {
|
|
|
409
411
|
return raw && /^\d{1,6}$/.test(raw) ? Number(raw) : TLDR_EVERY_DEFAULT;
|
|
410
412
|
}
|
|
411
413
|
var NUDGE = `${CLI_NAME} (optional): a title and a one-line now (relay_set_title / relay_set_now) help peers find you; skip it when you only run workers.`;
|
|
414
|
+
var AGY_CONVERSATION = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
415
|
+
function recordAgyConversation(payload, env) {
|
|
416
|
+
const file = env.SCP_AGY_CONVERSATION_FILE;
|
|
417
|
+
if (!file || field(payload, "parentConversationId", "parent_conversation_id") !== void 0) return;
|
|
418
|
+
const id = asString(field(payload, "conversationId", "conversation_id")) ?? env.ANTIGRAVITY_CONVERSATION_ID;
|
|
419
|
+
if (!id || !AGY_CONVERSATION.test(id)) return;
|
|
420
|
+
try {
|
|
421
|
+
writeFileSync(file, id, { flag: "wx", mode: 384 });
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
424
|
+
}
|
|
412
425
|
async function runHook(args, options) {
|
|
413
426
|
const began = Date.now();
|
|
414
427
|
const blockStop = args.includes(BLOCK_STOP);
|
|
@@ -427,6 +440,7 @@ async function runHook(args, options) {
|
|
|
427
440
|
} catch {
|
|
428
441
|
payload = {};
|
|
429
442
|
}
|
|
443
|
+
if (harnessArg === "agy") recordAgyConversation(payload, options.env);
|
|
430
444
|
if (Number.isFinite(wakeS) && wakeS > 0) return wakeHook(harnessArg, payload, wakeS, options);
|
|
431
445
|
const event = mapHookPayload(payload, eventArg);
|
|
432
446
|
if (!event || options.env.SCP_TRANSPORT === "cloud") return;
|
|
@@ -445,8 +459,10 @@ async function runHook(args, options) {
|
|
|
445
459
|
)
|
|
446
460
|
);
|
|
447
461
|
if ("dark" in joined) return;
|
|
462
|
+
const harnessSession = event.agent ? void 0 : asString(field(payload, "session_id", "sessionId"));
|
|
448
463
|
const claim = relayIdentity({
|
|
449
464
|
...joined.handle ? { as: joined.handle } : {},
|
|
465
|
+
...harnessSession ? { harnessSession } : {},
|
|
450
466
|
harness,
|
|
451
467
|
fallbackHarness: "hook",
|
|
452
468
|
cwd: event.cwd ?? options.cwd,
|
|
@@ -556,8 +572,10 @@ async function wakeHook(harnessArg, payload, waitS, options) {
|
|
|
556
572
|
)
|
|
557
573
|
);
|
|
558
574
|
if ("dark" in joined) return;
|
|
575
|
+
const harnessSession = asString(field(payload, "session_id", "sessionId"));
|
|
559
576
|
const claim = relayIdentity({
|
|
560
577
|
...joined.handle ? { as: joined.handle } : {},
|
|
578
|
+
...harnessSession ? { harnessSession } : {},
|
|
561
579
|
harness,
|
|
562
580
|
fallbackHarness: "hook",
|
|
563
581
|
cwd: asString(payload.cwd) ?? options.cwd,
|
|
@@ -1034,6 +1052,8 @@ export {
|
|
|
1034
1052
|
TLDR_EVERY_DEFAULT,
|
|
1035
1053
|
TLDR,
|
|
1036
1054
|
tldrEvery,
|
|
1055
|
+
AGY_CONVERSATION,
|
|
1056
|
+
recordAgyConversation,
|
|
1037
1057
|
runHook,
|
|
1038
1058
|
subagentIntro,
|
|
1039
1059
|
relayHookCommand,
|
|
@@ -67,6 +67,38 @@ function writeShimBinding(claim, env) {
|
|
|
67
67
|
}
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
+
function museLaunchFile(key, env) {
|
|
71
|
+
const pid = BINDING_PROCESS.exec(key)?.[1];
|
|
72
|
+
return pid ? join(relayDir(env), "launch", `muse-${pid}.json`) : void 0;
|
|
73
|
+
}
|
|
74
|
+
function writeMuseLaunch(key, launch, env) {
|
|
75
|
+
const pid = BINDING_PROCESS.exec(key)?.[1];
|
|
76
|
+
if (!pid) throw new Error(`muse serve has no process key (${key})`);
|
|
77
|
+
const path = join(privateDir(join(relayDir(env), "launch")), `muse-${pid}.json`);
|
|
78
|
+
const text = JSON.stringify({ process: key, ...launch });
|
|
79
|
+
writeFileSync(path, text, { mode: 384 });
|
|
80
|
+
chmodSync(path, 384);
|
|
81
|
+
return () => {
|
|
82
|
+
try {
|
|
83
|
+
if (readFileSync(path, "utf8") === text) rmSync(path);
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function readMuseLaunch(key, env) {
|
|
89
|
+
const path = museLaunchFile(key, env);
|
|
90
|
+
if (!path) return void 0;
|
|
91
|
+
try {
|
|
92
|
+
const launch = JSON.parse(readFileSync(path, "utf8"));
|
|
93
|
+
if (launch.process !== key) return void 0;
|
|
94
|
+
return {
|
|
95
|
+
...typeof launch.name === "string" && validHandle(launch.name) ? { name: launch.name } : {},
|
|
96
|
+
...typeof launch.grant === "string" && launch.grant ? { grant: launch.grant } : {}
|
|
97
|
+
};
|
|
98
|
+
} catch {
|
|
99
|
+
return void 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
70
102
|
function readShimBinding(env, pid, table) {
|
|
71
103
|
const read = (name, expectedPid) => {
|
|
72
104
|
try {
|
|
@@ -374,14 +406,23 @@ function hookHarnessPid(env, harness, ppid = process.ppid, parentOf = psParent)
|
|
|
374
406
|
if (Number.isInteger(pid) && pid > 1) return pid;
|
|
375
407
|
return harnessPid(ppid, parentOf);
|
|
376
408
|
}
|
|
409
|
+
function launcherPid(env, harness = harnessPid, parentOf = psParent) {
|
|
410
|
+
const pid = Number(env.SCP_HARNESS_PID);
|
|
411
|
+
if (!Number.isInteger(pid) || pid <= 1) return void 0;
|
|
412
|
+
return parentOf(harness())?.ppid === pid ? pid : void 0;
|
|
413
|
+
}
|
|
414
|
+
var HARNESS_SESSION = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
377
415
|
function relayIdentity(input) {
|
|
378
416
|
const resolved = resolveHarness(input.harness, input.env, input.fallbackHarness);
|
|
379
417
|
const pid = input.harnessPid(resolved);
|
|
380
418
|
const harness = GENERIC_HARNESSES.has(resolved) && input.inferHarness?.(pid) || resolved;
|
|
381
419
|
const cwd = isAbsolute(input.cwd) ? input.cwd : resolve(input.cwd);
|
|
382
|
-
const handle = input.as ?? input.env.SCP_NAME ?? deriveHandle(harness, cwd);
|
|
383
420
|
const harnessProcess = processKey(pid, input.startOf);
|
|
421
|
+
const launched = harness === "muse" && !input.env.SCP_NAME && !input.env.SCP_GRANT ? readMuseLaunch(harnessProcess, input.env) : void 0;
|
|
422
|
+
const handle = input.as ?? input.env.SCP_NAME ?? launched?.name ?? deriveHandle(harness, cwd);
|
|
423
|
+
const grant = input.env.SCP_GRANT || launched?.grant;
|
|
384
424
|
const keys = input.lineageOf ? input.lineageOf(pid) : lineage(pid);
|
|
425
|
+
const harnessSession = harness === "claude-code" ? [input.harnessSession, input.env.CLAUDE_CODE_SESSION_ID].find((id) => id !== void 0 && HARNESS_SESSION.test(id)) : void 0;
|
|
385
426
|
return {
|
|
386
427
|
handle,
|
|
387
428
|
harness,
|
|
@@ -391,7 +432,8 @@ function relayIdentity(input) {
|
|
|
391
432
|
labels: labelsFor(input.env),
|
|
392
433
|
...input.env.SCP_PARENT ? { parent: input.env.SCP_PARENT } : {},
|
|
393
434
|
...keys.length ? { lineage: keys } : {},
|
|
394
|
-
...
|
|
435
|
+
...grant ? { grant } : {},
|
|
436
|
+
...harnessSession ? { harness_session: harnessSession } : {}
|
|
395
437
|
};
|
|
396
438
|
}
|
|
397
439
|
|
|
@@ -466,6 +508,7 @@ var OPTIONS = {
|
|
|
466
508
|
who: { type: "boolean" },
|
|
467
509
|
peek: { type: "string" },
|
|
468
510
|
endpoint: { type: "string" },
|
|
511
|
+
"take-over": { type: "boolean" },
|
|
469
512
|
live: { type: "boolean" },
|
|
470
513
|
parent: { type: "string" }
|
|
471
514
|
};
|
|
@@ -591,7 +634,7 @@ async function open(verb, values, cwd, bodyFromStdin, walk) {
|
|
|
591
634
|
`${CLI_NAME2} ${verb} needs an identity: --as HANDLE (or $SCP_NAME) for an agent, or --human for your own inbox me/ (no terminal is attached, so me/ is not assumed)`
|
|
592
635
|
);
|
|
593
636
|
}
|
|
594
|
-
const { machineName, openCloud } = await import("./remote-
|
|
637
|
+
const { machineName, openCloud } = await import("./remote-YO5B2HQI.js");
|
|
595
638
|
const handle2 = named ? localHandle(named) : void 0;
|
|
596
639
|
const claim = handle2 ? {
|
|
597
640
|
handle: handle2,
|
|
@@ -849,7 +892,7 @@ function relayVerb(positionals, asJson, env = process.env) {
|
|
|
849
892
|
}
|
|
850
893
|
async function loginMain(positionals, values) {
|
|
851
894
|
if (positionals.length) throw new RelayUsage(`${CLI_NAME3} login takes only flags`);
|
|
852
|
-
return (await import("./login-
|
|
895
|
+
return (await import("./login-DCNCC3RS.js")).login(values);
|
|
853
896
|
}
|
|
854
897
|
function logoutMain(positionals, asJson, env = process.env) {
|
|
855
898
|
if (positionals.length) throw new RelayUsage(`${CLI_NAME3} logout takes no arguments`);
|
|
@@ -866,6 +909,7 @@ export {
|
|
|
866
909
|
detectHarness,
|
|
867
910
|
codexShell,
|
|
868
911
|
writeShimBinding,
|
|
912
|
+
writeMuseLaunch,
|
|
869
913
|
normalizeHarness,
|
|
870
914
|
hookHarness,
|
|
871
915
|
gitToplevel,
|
|
@@ -879,6 +923,7 @@ export {
|
|
|
879
923
|
psParent,
|
|
880
924
|
harnessPid,
|
|
881
925
|
hookHarnessPid,
|
|
926
|
+
launcherPid,
|
|
882
927
|
relayIdentity,
|
|
883
928
|
DEFAULT_RELAY,
|
|
884
929
|
configDir,
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
mergeHooks,
|
|
11
11
|
musePlugin,
|
|
12
12
|
opencodePlugin
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-277BAY7J.js";
|
|
14
14
|
import {
|
|
15
15
|
RelayUsage,
|
|
16
16
|
gitToplevel
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-3SOIVANO.js";
|
|
18
18
|
|
|
19
19
|
// src/install.ts
|
|
20
20
|
import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -3,18 +3,13 @@ import {
|
|
|
3
3
|
codexMount,
|
|
4
4
|
codexOverrides,
|
|
5
5
|
toml
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-KN7WEIE4.js";
|
|
8
|
-
import "./chunk-UKQBH4AQ.js";
|
|
9
|
-
import "./chunk-2342GTME.js";
|
|
10
|
-
import "./chunk-XQHPDNVG.js";
|
|
11
|
-
import "./chunk-YYSLYVNZ.js";
|
|
6
|
+
} from "./chunk-RZUQ422C.js";
|
|
12
7
|
import {
|
|
13
8
|
RelayUsage,
|
|
14
9
|
readPsTable,
|
|
15
10
|
relayIdentity,
|
|
16
11
|
startDaemon
|
|
17
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-3SOIVANO.js";
|
|
18
13
|
import {
|
|
19
14
|
SELF
|
|
20
15
|
} from "./chunk-MNTIEWKF.js";
|
|
@@ -611,12 +606,13 @@ ${stderr.toString().trim()}` : ""}`
|
|
|
611
606
|
rmSync(dir, { recursive: true, force: true });
|
|
612
607
|
}
|
|
613
608
|
}
|
|
609
|
+
|
|
614
610
|
export {
|
|
615
611
|
CODEX_HELP,
|
|
612
|
+
codexLaunch,
|
|
616
613
|
CodexSocket,
|
|
617
614
|
CodexWaker,
|
|
618
|
-
|
|
619
|
-
codexLaunch,
|
|
615
|
+
serverOptions,
|
|
620
616
|
connectCodexChannel,
|
|
621
|
-
|
|
617
|
+
codexCommand
|
|
622
618
|
};
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
agySettingsFile,
|
|
4
4
|
mcpFile,
|
|
5
5
|
mountedEntry
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SNW53KT3.js";
|
|
7
7
|
import {
|
|
8
8
|
steerPrompt
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-277BAY7J.js";
|
|
10
10
|
import {
|
|
11
11
|
OPTIONS,
|
|
12
12
|
RelayUsage,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
selectorList,
|
|
19
19
|
spawnerKey,
|
|
20
20
|
walkNow
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-3SOIVANO.js";
|
|
22
22
|
import {
|
|
23
23
|
SELF,
|
|
24
24
|
note
|
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "@scp-dev/core";
|
|
45
45
|
var SERVER = "scp";
|
|
46
46
|
var SPAWN_RUNNER = "SCP_SPAWN_RUNNER";
|
|
47
|
-
var SPAWNER_IDENTITY = ["SCP_NAME", "SCP_SESSION", "SCP_GRANT", "SCP_PARENT", "SCP_HARNESS", "SCP_SHIM_BINDING", SPAWN_RUNNER];
|
|
47
|
+
var SPAWNER_IDENTITY = ["SCP_NAME", "SCP_SESSION", "SCP_GRANT", "SCP_PARENT", "SCP_HARNESS", "SCP_SHIM_BINDING", "SCP_HARNESS_PID", SPAWN_RUNNER];
|
|
48
48
|
var SHIM_ENV = ["SCP_GRANT", "SCP_NAME", "SCP_LABELS", "SCP_DIR", "XDG_STATE_HOME", "SCP_SHIM_BINDING"];
|
|
49
49
|
function spawnEnvironment(parent, grant, labels) {
|
|
50
50
|
const env = {};
|
|
@@ -164,6 +164,10 @@ var PRESETS = {
|
|
|
164
164
|
allow: MESSAGING.flatMap((tool) => ["--allow", tool])
|
|
165
165
|
}
|
|
166
166
|
};
|
|
167
|
+
function messagingAllow(harness, args) {
|
|
168
|
+
const preset = PRESETS[harness];
|
|
169
|
+
return preset && !hasFlag(args, preset.own) ? preset.allow : [];
|
|
170
|
+
}
|
|
167
171
|
var MUSE_VALUED = /* @__PURE__ */ new Set([
|
|
168
172
|
"--agents",
|
|
169
173
|
"--provider",
|
|
@@ -477,7 +481,7 @@ function workerName(command, random = nameSuffix) {
|
|
|
477
481
|
const harness = (program === void 0 ? "" : basename(program)).toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, "").slice(0, 57);
|
|
478
482
|
return `${harness || "worker"}-${random()}`;
|
|
479
483
|
}
|
|
480
|
-
function agyNote(command, env) {
|
|
484
|
+
function agyNote(command, env, verb = "spawn") {
|
|
481
485
|
const at = programAt(command);
|
|
482
486
|
const args = command.slice(at + 1);
|
|
483
487
|
if (command[at] === void 0 || basename(command[at]) !== "agy" || !hasFlag(args, ["-p", "--print", "--prompt"]) || hasFlag(args, ["--dangerously-skip-permissions"]))
|
|
@@ -488,7 +492,7 @@ function agyNote(command, env) {
|
|
|
488
492
|
} catch {
|
|
489
493
|
}
|
|
490
494
|
if (Array.isArray(allow) && agyRules().every((rule) => allow.includes(rule))) return null;
|
|
491
|
-
return `${CLI_NAME}
|
|
495
|
+
return `${CLI_NAME} ${verb}: headless agy denies the relay tools without the permission rules ${CLI_NAME} setup writes to ${agySettingsFile(env)}; run ${CLI_NAME} setup`;
|
|
492
496
|
}
|
|
493
497
|
var FORWARDED = ["SIGINT", "SIGQUIT", "SIGTERM", "SIGHUP"];
|
|
494
498
|
var TAIL_BYTES = 16 * 1024;
|
|
@@ -924,6 +928,10 @@ export {
|
|
|
924
928
|
codexOverrideAt,
|
|
925
929
|
programAt,
|
|
926
930
|
MESSAGING,
|
|
931
|
+
messagingAllow,
|
|
932
|
+
MUSE_MODE,
|
|
933
|
+
MUSE_MODES_ALLOWED,
|
|
934
|
+
museModes,
|
|
927
935
|
contributorRefusal,
|
|
928
936
|
codexSkipsGitCheck,
|
|
929
937
|
closedStdin,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hooksTarget
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LRQP25RI.js";
|
|
4
4
|
import {
|
|
5
5
|
AGY_HOOK_SET,
|
|
6
6
|
BLOCK_STOP,
|
|
@@ -12,12 +12,12 @@ import {
|
|
|
12
12
|
mergeHooks,
|
|
13
13
|
musePlugin,
|
|
14
14
|
opencodePlugin
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-277BAY7J.js";
|
|
16
16
|
import {
|
|
17
17
|
RelayUsage,
|
|
18
18
|
gitToplevel,
|
|
19
19
|
setJoinMode
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-3SOIVANO.js";
|
|
21
21
|
|
|
22
22
|
// src/setup.ts
|
|
23
23
|
import { spawnSync } from "child_process";
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import {
|
|
2
|
+
entryArgv,
|
|
3
|
+
mcpFile,
|
|
4
|
+
mountedEntry,
|
|
5
|
+
whichOnPath
|
|
6
|
+
} from "./chunk-SNW53KT3.js";
|
|
7
|
+
import "./chunk-LRQP25RI.js";
|
|
8
|
+
import "./chunk-277BAY7J.js";
|
|
9
|
+
import "./chunk-YYSLYVNZ.js";
|
|
10
|
+
import {
|
|
11
|
+
CHANNELS_ENV,
|
|
12
|
+
RelayUsage,
|
|
13
|
+
deriveHandle,
|
|
14
|
+
joinMode
|
|
15
|
+
} from "./chunk-3SOIVANO.js";
|
|
16
|
+
import {
|
|
17
|
+
SELF
|
|
18
|
+
} from "./chunk-MNTIEWKF.js";
|
|
19
|
+
|
|
20
|
+
// src/claude.ts
|
|
21
|
+
import { spawn, spawnSync } from "child_process";
|
|
22
|
+
import { randomBytes } from "crypto";
|
|
23
|
+
import { existsSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from "fs";
|
|
24
|
+
import { basename, join } from "path";
|
|
25
|
+
import { privateDir, relayDir } from "@scp-dev/broker";
|
|
26
|
+
import { CLI_NAME, validHandle } from "@scp-dev/core";
|
|
27
|
+
var CHANNEL_FLAGS = ["--dangerously-load-development-channels", "server:scp"];
|
|
28
|
+
function claudeArgv(argv) {
|
|
29
|
+
let name;
|
|
30
|
+
let bg = false;
|
|
31
|
+
let i = 0;
|
|
32
|
+
for (; ; ) {
|
|
33
|
+
const arg = argv[i];
|
|
34
|
+
if (arg === "--bg" || arg === "--background") {
|
|
35
|
+
bg = true;
|
|
36
|
+
i++;
|
|
37
|
+
} else if (arg === "--name" || arg === "--as") {
|
|
38
|
+
if (argv[i + 1] === void 0) throw new RelayUsage(`${CLI_NAME} claude ${arg} takes a handle`);
|
|
39
|
+
name = argv[i + 1];
|
|
40
|
+
i += 2;
|
|
41
|
+
} else if (arg?.startsWith("--name=") || arg?.startsWith("--as=")) {
|
|
42
|
+
name = arg.slice(arg.indexOf("=") + 1);
|
|
43
|
+
i++;
|
|
44
|
+
} else break;
|
|
45
|
+
}
|
|
46
|
+
if (argv[i] === "--") i++;
|
|
47
|
+
name = name?.replace(/^me\//, "");
|
|
48
|
+
if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`--name ${JSON.stringify(name)} is not a valid agent handle`);
|
|
49
|
+
const args = argv.slice(i);
|
|
50
|
+
if (!bg) bg = optionsOf(args).some((arg) => arg === "--bg" || arg === "--background");
|
|
51
|
+
return { ...name !== void 0 ? { name } : {}, ...bg ? { bg: true } : {}, args };
|
|
52
|
+
}
|
|
53
|
+
var optionsOf = (args) => args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
|
|
54
|
+
function beforeDashes(args, added) {
|
|
55
|
+
const at = optionsOf(args).length;
|
|
56
|
+
return [...args.slice(0, at), ...added, ...args.slice(at)];
|
|
57
|
+
}
|
|
58
|
+
function claudeLaunchArgs(args, added) {
|
|
59
|
+
const channels = optionsOf(args).some((arg) => arg === "server:scp" || arg === `${CHANNEL_FLAGS[0]}=server:scp`) ? [] : CHANNEL_FLAGS;
|
|
60
|
+
return beforeDashes(args, [...channels, ...added]);
|
|
61
|
+
}
|
|
62
|
+
var LAUNCHER_IDENTITY = ["SCP_NAME", "SCP_GRANT", "SCP_SESSION", "SCP_PARENT", "SCP_HARNESS", "SCP_SHIM_BINDING", "SCP_SPAWN_RUNNER", CHANNELS_ENV];
|
|
63
|
+
var SHIM_VARS = ["SCP_LABELS", "SCP_DIR", "XDG_STATE_HOME"];
|
|
64
|
+
var bgDir = (env) => join(relayDir(env), "claude-bg");
|
|
65
|
+
function bgMount(env, name) {
|
|
66
|
+
let entry;
|
|
67
|
+
try {
|
|
68
|
+
entry = mountedEntry("claude", mcpFile("claude", env));
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
const mounted = entry ? entryArgv("claude", entry) : [];
|
|
72
|
+
const argv = mounted.includes("--channels") ? mounted : [...SELF, "mcp", "--harness", "claude", "--channels"];
|
|
73
|
+
const base = entry?.env && typeof entry.env === "object" ? entry.env : {};
|
|
74
|
+
const vars = Object.fromEntries(SHIM_VARS.flatMap((key) => env[key] ? [[key, env[key]]] : []));
|
|
75
|
+
return {
|
|
76
|
+
type: "stdio",
|
|
77
|
+
command: argv[0],
|
|
78
|
+
args: argv.slice(1),
|
|
79
|
+
env: { ...base, ...vars, ...name !== void 0 ? { SCP_NAME: name } : {}, SCP_GRANT: env.SCP_GRANT ?? "" }
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
var STRICT_HINT = `${CLI_NAME} claude: --strict-mcp-config without --mcp-config drops the scp mount, so messages cannot wake this session`;
|
|
83
|
+
function externalMcp(args) {
|
|
84
|
+
return args.some((arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config="));
|
|
85
|
+
}
|
|
86
|
+
function namePrompt(name, args, env, cwd) {
|
|
87
|
+
if (name !== void 0 || joinMode(env) === "manual" || args.some((arg) => /^--(append-)?system-prompt/.test(arg))) return [];
|
|
88
|
+
const handle = env.SCP_NAME || deriveHandle("claude-code", cwd);
|
|
89
|
+
return [
|
|
90
|
+
"--append-system-prompt",
|
|
91
|
+
`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.`
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
function mountHint(env, cwd = process.cwd()) {
|
|
95
|
+
const file = mcpFile("claude", env);
|
|
96
|
+
const entries = [];
|
|
97
|
+
try {
|
|
98
|
+
const user = mountedEntry("claude", file);
|
|
99
|
+
if (user) entries.push(user);
|
|
100
|
+
const config = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : {};
|
|
101
|
+
const local = config.projects?.[cwd]?.mcpServers?.scp;
|
|
102
|
+
if (local && typeof local === "object") entries.push(local);
|
|
103
|
+
} catch {
|
|
104
|
+
return `${CLI_NAME} claude: ${file} is unreadable, so the scp mount is unchecked; fix it or run ${CLI_NAME} setup --write`;
|
|
105
|
+
}
|
|
106
|
+
const project = join(cwd, ".mcp.json");
|
|
107
|
+
try {
|
|
108
|
+
const entry = existsSync(project) ? mountedEntry("claude", project) : void 0;
|
|
109
|
+
if (entry) entries.push(entry);
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
if (!entries.length) return `${CLI_NAME} claude: Claude Code has no scp MCP mount, so messages cannot wake this session; run ${CLI_NAME} setup --write`;
|
|
113
|
+
if (!entries.some((entry) => entryArgv("claude", entry).includes("--channels")))
|
|
114
|
+
return `${CLI_NAME} claude: the scp mount lacks --channels, so messages cannot wake this session; run ${CLI_NAME} setup --write`;
|
|
115
|
+
return void 0;
|
|
116
|
+
}
|
|
117
|
+
var SHORT_ID = /^[0-9a-f]{8}$/;
|
|
118
|
+
var BACKGROUNDED = /backgrounded\s*\S\s*([0-9a-f]{8})/;
|
|
119
|
+
function liveAgents(bin, env) {
|
|
120
|
+
const run = spawnSync(bin, ["agents", "--json"], { env, encoding: "utf8", timeout: 15e3 });
|
|
121
|
+
try {
|
|
122
|
+
const listed = JSON.parse(run.stdout);
|
|
123
|
+
return Array.isArray(listed) ? listed.filter((agent) => typeof agent?.id === "string" && agent.state !== "failed") : [];
|
|
124
|
+
} catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function resumeTarget(args) {
|
|
129
|
+
const options = optionsOf(args);
|
|
130
|
+
for (const [i, arg] of options.entries()) {
|
|
131
|
+
if (arg.startsWith("--resume=")) return arg.slice("--resume=".length);
|
|
132
|
+
if ((arg === "--resume" || arg === "-r") && options[i + 1] && !options[i + 1].startsWith("-")) return options[i + 1];
|
|
133
|
+
}
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
async function runClaude(bin, args, env, cwd, stdout = "inherit", onOut) {
|
|
137
|
+
const child = spawn(bin, args, { cwd, stdio: ["inherit", stdout, "inherit"], env });
|
|
138
|
+
child.stdout?.on("data", (chunk) => {
|
|
139
|
+
process.stdout.write(chunk);
|
|
140
|
+
onOut?.(chunk.toString("utf8"));
|
|
141
|
+
});
|
|
142
|
+
const forward = (signal) => () => void child.kill(signal);
|
|
143
|
+
const handlers = ["SIGTERM", "SIGHUP"].map((signal) => [signal, forward(signal)]);
|
|
144
|
+
const ignore = () => {
|
|
145
|
+
};
|
|
146
|
+
process.on("SIGINT", ignore);
|
|
147
|
+
for (const [signal, handler] of handlers) process.on(signal, handler);
|
|
148
|
+
try {
|
|
149
|
+
return await new Promise((resolve, reject) => {
|
|
150
|
+
child.once("error", reject);
|
|
151
|
+
child.once("close", (code, signal) => resolve(code ?? (signal ? 128 + (signalNumber(signal) ?? 1) : 1)));
|
|
152
|
+
});
|
|
153
|
+
} finally {
|
|
154
|
+
process.off("SIGINT", ignore);
|
|
155
|
+
for (const [signal, handler] of handlers) process.off(signal, handler);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function backgroundCommand(name, args, env, cwd, bin, out) {
|
|
159
|
+
const handle = name ?? (env.SCP_NAME || void 0);
|
|
160
|
+
const dir = privateDir(bgDir(env));
|
|
161
|
+
const file = join(dir, `${randomBytes(8).toString("hex")}.json`);
|
|
162
|
+
writeFileSync(file, `${JSON.stringify({ mcpServers: { scp: bgMount(env, handle) } }, null, 2)}
|
|
163
|
+
`, { mode: 384 });
|
|
164
|
+
const options = optionsOf(args);
|
|
165
|
+
const added = [
|
|
166
|
+
...options.includes("--bg") || options.includes("--background") ? [] : ["--bg"],
|
|
167
|
+
...handle !== void 0 && !options.some((arg) => arg === "-n" || arg === "--name" || arg.startsWith("--name=")) ? ["--name", handle] : [],
|
|
168
|
+
...namePrompt(handle, args, env, cwd),
|
|
169
|
+
"--mcp-config",
|
|
170
|
+
file
|
|
171
|
+
];
|
|
172
|
+
const clean = Object.fromEntries(Object.entries(env).filter(([key]) => !LAUNCHER_IDENTITY.includes(key)));
|
|
173
|
+
let printed = "";
|
|
174
|
+
const code = await runClaude(bin, beforeDashes(args, added), clean, cwd, "pipe", (text) => {
|
|
175
|
+
printed += text;
|
|
176
|
+
});
|
|
177
|
+
const id = BACKGROUNDED.exec(printed)?.[1];
|
|
178
|
+
if (!id) {
|
|
179
|
+
unlinkSync(file);
|
|
180
|
+
return code || 1;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
unlinkSync(join(dir, `${id}.json`));
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
symlinkSync(basename(file), join(dir, `${id}.json`));
|
|
187
|
+
const address = `me/${handle ?? deriveHandle("claude-code", cwd)}`;
|
|
188
|
+
out(`${CLI_NAME} claude: background session ${id} is ${address} on SCP; messages wake it through its inbox. ${CLI_NAME} claude attach ${id} opens it`);
|
|
189
|
+
return code;
|
|
190
|
+
}
|
|
191
|
+
async function attachCommand(argv, env, err, cwd, bin, deps) {
|
|
192
|
+
let target;
|
|
193
|
+
let rename;
|
|
194
|
+
for (let i = 0; i < argv.length; i++) {
|
|
195
|
+
const arg = argv[i];
|
|
196
|
+
if (arg === "--name" || arg === "--as") {
|
|
197
|
+
rename = argv[++i];
|
|
198
|
+
if (rename === void 0) throw new RelayUsage(`${CLI_NAME} claude attach ${arg} takes a handle`);
|
|
199
|
+
} else if (arg.startsWith("--name=") || arg.startsWith("--as=")) rename = arg.slice(arg.indexOf("=") + 1);
|
|
200
|
+
else if (target === void 0) target = arg;
|
|
201
|
+
else throw new RelayUsage(`${CLI_NAME} claude attach takes one session: <id|name> [--name N]`);
|
|
202
|
+
}
|
|
203
|
+
if (target === void 0) throw new RelayUsage(`${CLI_NAME} claude attach takes a background session: <id|name> [--name N]`);
|
|
204
|
+
rename = rename?.replace(/^me\//, "");
|
|
205
|
+
if (rename !== void 0) {
|
|
206
|
+
if (!validHandle(rename)) throw new RelayUsage(`--name ${JSON.stringify(rename)} is not a valid agent handle`);
|
|
207
|
+
await renameBackground(target, rename, env, err, bin, deps);
|
|
208
|
+
}
|
|
209
|
+
return runClaude(bin, ["attach", target], env, cwd);
|
|
210
|
+
}
|
|
211
|
+
async function renameBackground(target, to, env, err, bin, deps) {
|
|
212
|
+
const agent = SHORT_ID.test(target) ? void 0 : liveAgents(bin, env).find((a) => a.name === target || a.sessionId === target);
|
|
213
|
+
const id = SHORT_ID.test(target) ? target : agent?.id;
|
|
214
|
+
const link = id ? join(bgDir(env), `${id}.json`) : void 0;
|
|
215
|
+
let config;
|
|
216
|
+
try {
|
|
217
|
+
config = link ? JSON.parse(readFileSync(link, "utf8")) : void 0;
|
|
218
|
+
} catch {
|
|
219
|
+
}
|
|
220
|
+
const from = config?.mcpServers?.scp?.env?.SCP_NAME ?? (SHORT_ID.test(target) ? void 0 : target);
|
|
221
|
+
if (!link || !config?.mcpServers?.scp?.env || from === void 0) {
|
|
222
|
+
err(`${CLI_NAME} claude: ${target} was not started by ${CLI_NAME} claude --bg --name, so its name is unknown; run ${CLI_NAME} name ${to} in it`);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
if (from === to) return;
|
|
226
|
+
const code = await (deps.rename ?? renameEndpoint)(from, to);
|
|
227
|
+
if (code !== 0) {
|
|
228
|
+
err(`${CLI_NAME} claude: me/${from} keeps its name`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
config.mcpServers.scp.env.SCP_NAME = to;
|
|
232
|
+
writeFileSync(link, `${JSON.stringify(config, null, 2)}
|
|
233
|
+
`, { mode: 384 });
|
|
234
|
+
}
|
|
235
|
+
async function renameEndpoint(from, to) {
|
|
236
|
+
return (await import("./main-CDLW3MV5.js")).relayMain(["name", "--endpoint", `me/${from}`, to]);
|
|
237
|
+
}
|
|
238
|
+
async function claudeCommand(argv, env = process.env, err = (line) => void process.stderr.write(`${line}
|
|
239
|
+
`), cwd = process.cwd(), deps = {}) {
|
|
240
|
+
if (argv[0] === "--help" || argv[0] === "-h") {
|
|
241
|
+
process.stdout.write(CLAUDE_HELP);
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
const out = deps.out ?? ((line) => void process.stdout.write(`${line}
|
|
245
|
+
`));
|
|
246
|
+
const bin = whichOnPath("claude", env);
|
|
247
|
+
if (argv[0] === "attach") {
|
|
248
|
+
if (!bin) throw new RelayUsage("claude is not on PATH");
|
|
249
|
+
return attachCommand(argv.slice(1), env, err, cwd, bin, deps);
|
|
250
|
+
}
|
|
251
|
+
const { name, bg, args } = claudeArgv(argv);
|
|
252
|
+
if (!bin) throw new RelayUsage("claude is not on PATH");
|
|
253
|
+
if (bg) return backgroundCommand(name, args, env, cwd, bin, out);
|
|
254
|
+
const resume = resumeTarget(args);
|
|
255
|
+
const live = resume === void 0 ? void 0 : liveAgents(bin, env).find((agent) => agent.id === resume || agent.sessionId === resume);
|
|
256
|
+
if (live) {
|
|
257
|
+
err(`${CLI_NAME} claude: ${resume} is a live background session, already woken through claude-inbox; attaching to it (claude attach ${live.id})`);
|
|
258
|
+
return runClaude(bin, ["attach", live.id], env, cwd);
|
|
259
|
+
}
|
|
260
|
+
const external = externalMcp(args);
|
|
261
|
+
const hint = external ? void 0 : args.includes("--strict-mcp-config") ? STRICT_HINT : mountHint(env, cwd);
|
|
262
|
+
if (hint) throw new RelayUsage(hint);
|
|
263
|
+
if (external) err(`${CLI_NAME} claude: using your --mcp-config; it needs the scp server with --channels for messages to wake this session`);
|
|
264
|
+
return runClaude(
|
|
265
|
+
bin,
|
|
266
|
+
claudeLaunchArgs(args, namePrompt(name, args, env, cwd)),
|
|
267
|
+
{ ...env, ...name !== void 0 ? { SCP_NAME: name } : {}, [CHANNELS_ENV]: String(process.pid) },
|
|
268
|
+
cwd
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
var CLAUDE_HELP = `${CLI_NAME} claude [--name N | --as N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake it
|
|
272
|
+
${CLI_NAME} claude --bg [--name N] [--] [claude args...] start it in the background (claude --bg) with an scp mount that claims N and
|
|
273
|
+
$SCP_GRANT, so env $(${CLI_NAME} spawn-env --name N) links it under you; messages wake it
|
|
274
|
+
through its inbox. Prints its short id and address
|
|
275
|
+
${CLI_NAME} claude attach <id|name> [--name N] claude attach, renaming its endpoint to N first
|
|
276
|
+
--resume <id> of a live background session attaches to it: it is already woken through claude-inbox
|
|
277
|
+
`;
|
|
278
|
+
var SIGNALS = { SIGHUP: 1, SIGINT: 2, SIGKILL: 9, SIGTERM: 15 };
|
|
279
|
+
var signalNumber = (signal) => SIGNALS[signal];
|
|
280
|
+
export {
|
|
281
|
+
CHANNEL_FLAGS,
|
|
282
|
+
bgDir,
|
|
283
|
+
bgMount,
|
|
284
|
+
claudeArgv,
|
|
285
|
+
claudeCommand,
|
|
286
|
+
claudeLaunchArgs,
|
|
287
|
+
mountHint,
|
|
288
|
+
namePrompt,
|
|
289
|
+
resumeTarget
|
|
290
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -17,6 +17,6 @@ function quietSqliteWarning() {
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
if (!await subagentFastPath(process.argv.slice(2))) {
|
|
19
19
|
quietSqliteWarning();
|
|
20
|
-
const { run } = await import("./main-
|
|
20
|
+
const { run } = await import("./main-CDLW3MV5.js");
|
|
21
21
|
await run(process.argv.slice(2));
|
|
22
22
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CODEX_HELP,
|
|
3
|
+
CodexSocket,
|
|
4
|
+
CodexWaker,
|
|
5
|
+
codexCommand,
|
|
6
|
+
codexLaunch,
|
|
7
|
+
connectCodexChannel,
|
|
8
|
+
serverOptions
|
|
9
|
+
} from "./chunk-LWMTZH36.js";
|
|
10
|
+
import "./chunk-RZUQ422C.js";
|
|
11
|
+
import "./chunk-KN7WEIE4.js";
|
|
12
|
+
import "./chunk-SNW53KT3.js";
|
|
13
|
+
import "./chunk-LRQP25RI.js";
|
|
14
|
+
import "./chunk-277BAY7J.js";
|
|
15
|
+
import "./chunk-YYSLYVNZ.js";
|
|
16
|
+
import "./chunk-3SOIVANO.js";
|
|
17
|
+
import "./chunk-MNTIEWKF.js";
|
|
18
|
+
export {
|
|
19
|
+
CODEX_HELP,
|
|
20
|
+
CodexSocket,
|
|
21
|
+
CodexWaker,
|
|
22
|
+
codexCommand,
|
|
23
|
+
codexLaunch,
|
|
24
|
+
connectCodexChannel,
|
|
25
|
+
serverOptions
|
|
26
|
+
};
|