@scp-dev/cli 0.1.0 → 0.1.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.
- package/dist/{chunk-WSLKRAB7.js → chunk-4DNQFATX.js} +1 -1
- package/dist/{chunk-IE5VI25P.js → chunk-KN7WEIE4.js} +1 -1
- package/dist/{chunk-O4J5G2X7.js → chunk-N3ZROLFA.js} +2 -2
- package/dist/{spawn-7P4EMSVB.js → chunk-V5I7KEU7.js} +113 -50
- package/dist/{chunk-RO7CUOOH.js → chunk-XWLF7ON2.js} +15 -6
- package/dist/cli.js +1 -1
- package/dist/{hooks-MBKU56HG.js → hooks-O3UZXDYG.js} +1 -1
- package/dist/{install-4HATFIFI.js → install-HFBSKSTG.js} +2 -2
- package/dist/{main-QP32TYTL.js → main-5CAHHN55.js} +40 -14
- package/dist/{mcp-EQCSCD3X.js → mcp-P2AZYP5G.js} +24 -3
- package/dist/{setup-T4ZZKV7K.js → setup-RDPVGE4H.js} +3 -3
- package/dist/spawn-TQ3HOZTQ.js +74 -0
- package/dist/{up-Z4RSJMED.js → up-KXOYO7QV.js} +1 -1
- package/package.json +5 -5
|
@@ -29,7 +29,7 @@ Steer: ${CLI_NAME} send <name> <text>; a claude -p or codex exec prompt gets a n
|
|
|
29
29
|
A codex exec worker that exited before a late steer is resumed with it (codex exec resume, at most twice).
|
|
30
30
|
Harnesses: codex gets -c overrides that hand it the grant (also behind env, timeout, nohup or nice); a ${CLI_NAME}
|
|
31
31
|
command from its workspace-write sandbox needs -c sandbox_workspace_write.network_access=true. codex exec
|
|
32
|
-
gets --skip-git-repo-check outside a git repo and
|
|
32
|
+
gets --skip-git-repo-check outside a git repo, and with its prompt in argv piped stdin only under --fg. Workers
|
|
33
33
|
message with the relay MCP tools (relay_send, relay_inbox, relay_reply). claude -p and grok -p get those tools allowed
|
|
34
34
|
unless you pass a permission flag. muse runs with --contributor-mode off unless you pass on. agy needs the allow rules ${CLI_NAME}
|
|
35
35
|
setup writes.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hooksTarget
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4DNQFATX.js";
|
|
4
4
|
import {
|
|
5
5
|
RelayUsage
|
|
6
6
|
} from "./chunk-7KP6TAJW.js";
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
mergeHooks,
|
|
16
16
|
musePlugin,
|
|
17
17
|
opencodePlugin
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-XWLF7ON2.js";
|
|
19
19
|
import {
|
|
20
20
|
gitToplevel
|
|
21
21
|
} from "./chunk-DA3EB2YF.js";
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SPAWN_HELP
|
|
3
|
-
} from "./chunk-IE5VI25P.js";
|
|
4
1
|
import {
|
|
5
2
|
agyRules,
|
|
6
3
|
agySettingsFile,
|
|
7
4
|
mcpFile,
|
|
8
5
|
mountedEntry
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-WSLKRAB7.js";
|
|
6
|
+
} from "./chunk-N3ZROLFA.js";
|
|
11
7
|
import {
|
|
12
8
|
OPTIONS,
|
|
13
9
|
RelayUsage,
|
|
@@ -20,8 +16,7 @@ import {
|
|
|
20
16
|
} from "./chunk-7KP6TAJW.js";
|
|
21
17
|
import {
|
|
22
18
|
steerPrompt
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-YYSLYVNZ.js";
|
|
19
|
+
} from "./chunk-XWLF7ON2.js";
|
|
25
20
|
import {
|
|
26
21
|
SELF,
|
|
27
22
|
detectHarness,
|
|
@@ -297,6 +292,35 @@ function closedStdin(command) {
|
|
|
297
292
|
if (run.sub?.name === "resume") return run.operands.length >= (hasFlag(args, ["--last"]) ? 1 : 2);
|
|
298
293
|
return run.sub !== void 0 || run.operands.length > 0;
|
|
299
294
|
}
|
|
295
|
+
var STDIN_WAIT_MS = 300;
|
|
296
|
+
async function harnessStdin(waitMs) {
|
|
297
|
+
if (process.stdin.isTTY === true) return "ignore";
|
|
298
|
+
let stat;
|
|
299
|
+
try {
|
|
300
|
+
stat = fstatSync(0);
|
|
301
|
+
} catch {
|
|
302
|
+
return "ignore";
|
|
303
|
+
}
|
|
304
|
+
if (stat.isFile()) return stat.size > 0 ? "inherit" : "ignore";
|
|
305
|
+
if (!stat.isFIFO() && !stat.isSocket()) return "ignore";
|
|
306
|
+
const input = process.stdin;
|
|
307
|
+
return new Promise((resolve2) => {
|
|
308
|
+
const settle = (result) => {
|
|
309
|
+
clearTimeout(timer);
|
|
310
|
+
input.off("data", data);
|
|
311
|
+
input.off("end", ended);
|
|
312
|
+
input.off("error", ended);
|
|
313
|
+
input.pause();
|
|
314
|
+
resolve2(result);
|
|
315
|
+
};
|
|
316
|
+
const data = (chunk) => settle({ first: chunk, rest: input });
|
|
317
|
+
const ended = () => settle("ignore");
|
|
318
|
+
const timer = waitMs === void 0 ? void 0 : setTimeout(ended, waitMs);
|
|
319
|
+
input.on("data", data);
|
|
320
|
+
input.once("end", ended);
|
|
321
|
+
input.once("error", ended);
|
|
322
|
+
});
|
|
323
|
+
}
|
|
300
324
|
function harnessArgv(command, env, self = SELF, mount = codexMount, cwd = process.cwd(), probe = codexSkipsGitCheck) {
|
|
301
325
|
const at = programAt(command);
|
|
302
326
|
const program = command[at];
|
|
@@ -492,11 +516,19 @@ function runHarness(argv, env, options = {}) {
|
|
|
492
516
|
return new Promise((resolve2) => {
|
|
493
517
|
const tty = process.stdin.isTTY === true;
|
|
494
518
|
const group = options.group === true && !tty && process.stdout.isTTY !== true && process.stderr.isTTY !== true;
|
|
519
|
+
const input = options.stdin ?? "inherit";
|
|
520
|
+
const piped = typeof input === "object" ? input : void 0;
|
|
495
521
|
const child = spawn(argv[0], argv.slice(1), {
|
|
496
|
-
stdio: [
|
|
522
|
+
stdio: [typeof input === "object" ? "pipe" : input, capture ? "pipe" : "inherit", captureErr ? "pipe" : "inherit"],
|
|
497
523
|
env,
|
|
498
524
|
detached: group
|
|
499
525
|
});
|
|
526
|
+
if (piped && child.stdin) {
|
|
527
|
+
child.stdin.on("error", () => {
|
|
528
|
+
});
|
|
529
|
+
child.stdin.write(piped.first);
|
|
530
|
+
piped.rest.pipe(child.stdin);
|
|
531
|
+
}
|
|
500
532
|
const pipes = [child.stdout, child.stderr].filter((pipe) => pipe !== null);
|
|
501
533
|
const stdout = child.stdout ? tee(child.stdout, out) : void 0;
|
|
502
534
|
const stderr = child.stderr ? tee(child.stderr, options.err ?? process.stderr) : void 0;
|
|
@@ -638,7 +670,11 @@ ${LIMIT_CUT}`);
|
|
|
638
670
|
return replies;
|
|
639
671
|
}
|
|
640
672
|
function repliesOutput(replies, bytes = LIMITS.handbackBytes) {
|
|
641
|
-
const
|
|
673
|
+
const lines = (text) => text.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
674
|
+
const all = replies.map((reply) => handbackOutput(reply.replace(/\r\n?/g, "\n"), false, Number.MAX_SAFE_INTEGER)).filter(Boolean);
|
|
675
|
+
const restated = (text, later) => lines(text).length >= 2 && lines(text).every((line) => lines(later).includes(line));
|
|
676
|
+
const clean = all.filter((text, i) => text === HANDBACK_CUT || !all.slice(i + 1).some((later) => restated(text, later)));
|
|
677
|
+
if (clean.every((text) => text === HANDBACK_CUT)) return "";
|
|
642
678
|
const size = (text) => new TextEncoder().encode(text).length;
|
|
643
679
|
const framed = (from) => {
|
|
644
680
|
const text = clean.slice(from).join("\n\n");
|
|
@@ -648,7 +684,7 @@ ${text}` : text;
|
|
|
648
684
|
};
|
|
649
685
|
let fit;
|
|
650
686
|
for (let from = clean.length - 1; from >= 0 && size(framed(from)) <= bytes; from--) fit = framed(from);
|
|
651
|
-
if (fit !== void 0
|
|
687
|
+
if (fit !== void 0) return fit;
|
|
652
688
|
return keepLast(clean.at(-1), bytes, clean.length > 1);
|
|
653
689
|
}
|
|
654
690
|
function splitSpawn(args) {
|
|
@@ -682,20 +718,9 @@ function handbackBytes(value) {
|
|
|
682
718
|
}
|
|
683
719
|
var STEER_RESUMES = 2;
|
|
684
720
|
async function spawnCommand(args, cwd) {
|
|
685
|
-
const
|
|
686
|
-
let values;
|
|
687
|
-
|
|
688
|
-
values = parseArgs({ args: split.flags, options: OPTIONS, strict: true }).values;
|
|
689
|
-
} catch (error) {
|
|
690
|
-
throw new RelayUsage(error instanceof Error ? error.message : String(error));
|
|
691
|
-
}
|
|
692
|
-
for (const key of Object.keys(values)) if (!SPAWN_OPTIONS.includes(key)) throw new RelayUsage(`${CLI_NAME} spawn does not take --${key}`);
|
|
693
|
-
if (values.bg && values.fg) throw new RelayUsage("--bg and --fg: pick one");
|
|
694
|
-
const command = split.command;
|
|
695
|
-
if (!command.length)
|
|
696
|
-
throw new RelayUsage(`${CLI_NAME} spawn runs a harness command: ${CLI_NAME} spawn [flags] [--] <harness cmd...>, e.g. ${CLI_NAME} spawn codex exec "..."`);
|
|
697
|
-
const refusal = contributorRefusal(command);
|
|
698
|
-
if (refusal) throw new RelayUsage(refusal);
|
|
721
|
+
const parsed = parseSpawn(args);
|
|
722
|
+
let values = parsed.values;
|
|
723
|
+
const { command } = parsed;
|
|
699
724
|
const bytes = values["handback-bytes"] === void 0 ? void 0 : handbackBytes(values["handback-bytes"]);
|
|
700
725
|
const runner = process.env[SPAWN_RUNNER];
|
|
701
726
|
delete process.env[SPAWN_RUNNER];
|
|
@@ -737,8 +762,10 @@ async function spawnCommand(args, cwd) {
|
|
|
737
762
|
const agy = agyNote(command, process.env);
|
|
738
763
|
if (agy) note(agy);
|
|
739
764
|
const started = handshake && ((failed) => handshake(failed ? { error: failed.message, code: failed.code } : { name, pid: process.pid }));
|
|
740
|
-
const launch = (argv, extra = {}) => runHarness(harnessArgv(argv, env), env, {
|
|
741
|
-
|
|
765
|
+
const launch = (argv, extra = {}) => runHarness(harnessArgv(argv, env), env, { group: runner !== void 0, ...closedStdin(argv) ? { stdin: "ignore" } : {}, ...extra });
|
|
766
|
+
const first = steerArgv(command, steer);
|
|
767
|
+
const stdin = closedStdin(first) ? await harnessStdin(agentShell(process.env) ? STDIN_WAIT_MS : void 0) : void 0;
|
|
768
|
+
let run = await launch(first, { ...started ? { started } : {}, ...stdin ? { stdin } : {} });
|
|
742
769
|
if (!run.started) {
|
|
743
770
|
await spawnExit(values, cwd, grant.SCP_GRANT, run.code, name, "", walk, [], [], false);
|
|
744
771
|
return run.code;
|
|
@@ -761,6 +788,32 @@ async function spawnCommand(args, cwd) {
|
|
|
761
788
|
await spawnExit(values, cwd, grant.SCP_GRANT, run.code, name, replies || harnessOutput(run, bytes), walk, unapplied, exits, true, stderrOutput(run));
|
|
762
789
|
return run.code;
|
|
763
790
|
}
|
|
791
|
+
function parseSpawn(args) {
|
|
792
|
+
const split = splitSpawn(args);
|
|
793
|
+
let values;
|
|
794
|
+
try {
|
|
795
|
+
values = parseArgs({ args: split.flags, options: OPTIONS, strict: true }).values;
|
|
796
|
+
} catch (error) {
|
|
797
|
+
throw new RelayUsage(error instanceof Error ? error.message : String(error));
|
|
798
|
+
}
|
|
799
|
+
if (values.help) return { values, command: split.command };
|
|
800
|
+
for (const key of Object.keys(values)) if (!SPAWN_OPTIONS.includes(key)) throw new RelayUsage(`${CLI_NAME} spawn does not take --${key}`);
|
|
801
|
+
if (values.bg && values.fg) throw new RelayUsage("--bg and --fg: pick one");
|
|
802
|
+
const command = split.command;
|
|
803
|
+
if (!command.length)
|
|
804
|
+
throw new RelayUsage(`${CLI_NAME} spawn runs a harness command: ${CLI_NAME} spawn [flags] [--] <harness cmd...>, e.g. ${CLI_NAME} spawn codex exec "..."`);
|
|
805
|
+
const refusal = contributorRefusal(command);
|
|
806
|
+
if (refusal) throw new RelayUsage(refusal);
|
|
807
|
+
if (values["handback-bytes"] !== void 0) handbackBytes(values["handback-bytes"]);
|
|
808
|
+
return { values, command };
|
|
809
|
+
}
|
|
810
|
+
async function spawnDetached(args, cwd) {
|
|
811
|
+
const { values } = parseSpawn(args);
|
|
812
|
+
if (values.help || values.fg) throw new RelayUsage("a detached spawn takes no --help or --fg");
|
|
813
|
+
const reply = await detachedRunner(args, cwd);
|
|
814
|
+
if ("error" in reply) throw new Error(reply.error);
|
|
815
|
+
return reply;
|
|
816
|
+
}
|
|
764
817
|
function agentShell(env) {
|
|
765
818
|
return detectHarness(env) !== void 0 || ["SCP_NAME", "SCP_GRANT", "SCP_SESSION"].some((key) => env[key]);
|
|
766
819
|
}
|
|
@@ -782,6 +835,17 @@ function once(fn) {
|
|
|
782
835
|
};
|
|
783
836
|
}
|
|
784
837
|
async function detachSpawn(args, cwd) {
|
|
838
|
+
const reply = await detachedRunner(args, cwd);
|
|
839
|
+
if ("error" in reply) {
|
|
840
|
+
note(reply.error);
|
|
841
|
+
return reply.code;
|
|
842
|
+
}
|
|
843
|
+
console.log(
|
|
844
|
+
`${reply.name} started (pid ${reply.pid}) and runs in the background: its result arrives as a relay message by itself; steer it with ${CLI_NAME} send ${reply.name} <text>`
|
|
845
|
+
);
|
|
846
|
+
return 0;
|
|
847
|
+
}
|
|
848
|
+
async function detachedRunner(args, cwd) {
|
|
785
849
|
const walk = walkNow();
|
|
786
850
|
const child = spawn(SELF[0], [...SELF.slice(1), "spawn", ...args], {
|
|
787
851
|
cwd,
|
|
@@ -810,13 +874,9 @@ async function detachSpawn(args, cwd) {
|
|
|
810
874
|
if ("error" in reply) {
|
|
811
875
|
if (reply.usage) throw new RelayUsage(reply.error);
|
|
812
876
|
if (reply.code === void 0) throw new Error(reply.error);
|
|
813
|
-
|
|
814
|
-
return reply.code;
|
|
877
|
+
return { error: reply.error, code: reply.code };
|
|
815
878
|
}
|
|
816
|
-
|
|
817
|
-
`${reply.name} started (pid ${reply.pid}) and runs in the background: its result arrives as a relay message by itself; steer it with ${CLI_NAME} send ${reply.name} <text>`
|
|
818
|
-
);
|
|
819
|
-
return 0;
|
|
879
|
+
return reply;
|
|
820
880
|
}
|
|
821
881
|
async function spawnSteers(values, cwd, token, walk, take) {
|
|
822
882
|
try {
|
|
@@ -853,33 +913,36 @@ async function spawnExit(values, cwd, token, code, name, output, walk, unapplied
|
|
|
853
913
|
}
|
|
854
914
|
}
|
|
855
915
|
var SPAWN_GRANT_TTL_S = 3600;
|
|
916
|
+
|
|
856
917
|
export {
|
|
857
|
-
MESSAGING,
|
|
858
|
-
SPAWN_HELP,
|
|
859
918
|
SPAWN_RUNNER,
|
|
860
|
-
|
|
861
|
-
agyNote,
|
|
862
|
-
claudeTranscripts,
|
|
863
|
-
closedStdin,
|
|
919
|
+
spawnEnvironment,
|
|
864
920
|
codexMount,
|
|
865
|
-
codexOverrideAt,
|
|
866
921
|
codexOverrides,
|
|
867
|
-
|
|
922
|
+
codexOverrideAt,
|
|
923
|
+
programAt,
|
|
924
|
+
MESSAGING,
|
|
868
925
|
contributorRefusal,
|
|
926
|
+
codexSkipsGitCheck,
|
|
927
|
+
closedStdin,
|
|
928
|
+
STDIN_WAIT_MS,
|
|
929
|
+
harnessStdin,
|
|
869
930
|
harnessArgv,
|
|
870
|
-
harnessOutput,
|
|
871
|
-
programAt,
|
|
872
|
-
repliesOutput,
|
|
873
931
|
resumeArgv,
|
|
874
|
-
runHarness,
|
|
875
|
-
spawnCommand,
|
|
876
|
-
spawnDefaults,
|
|
877
|
-
spawnEnvironment,
|
|
878
932
|
spawnPrompt,
|
|
933
|
+
steerArgv,
|
|
879
934
|
spawnTitle,
|
|
880
|
-
|
|
935
|
+
workerName,
|
|
936
|
+
agyNote,
|
|
937
|
+
runHarness,
|
|
938
|
+
harnessOutput,
|
|
881
939
|
stderrOutput,
|
|
882
|
-
|
|
940
|
+
claudeTranscripts,
|
|
883
941
|
transcriptReplies,
|
|
884
|
-
|
|
942
|
+
repliesOutput,
|
|
943
|
+
splitSpawn,
|
|
944
|
+
spawnDefaults,
|
|
945
|
+
spawnCommand,
|
|
946
|
+
spawnDetached,
|
|
947
|
+
agentShell
|
|
885
948
|
};
|
|
@@ -238,9 +238,16 @@ function pushText(result) {
|
|
|
238
238
|
`Push budget used up for this prompt; read the rest with ${CLI_NAME} inbox.${running.length ? ` Workers you spawned still running: ${running.join(", ")}.` : ""}`
|
|
239
239
|
);
|
|
240
240
|
} else {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
const background = result.background ?? [];
|
|
242
|
+
if (running.length) {
|
|
243
|
+
parts.push(
|
|
244
|
+
`Workers you spawned still running: ${running.join(", ")}. End your turn to wait for them; no reply needed.${background.length ? ` Also running with --no-push: ${background.join(", ")}.` : ""}`
|
|
245
|
+
);
|
|
246
|
+
} else if (background.length) {
|
|
247
|
+
parts.push(
|
|
248
|
+
`No worker your stop waits on is still running; ${background.join(", ")} (--no-push) still ${background.length === 1 ? "runs" : "run"} in the background.`
|
|
249
|
+
);
|
|
250
|
+
} else parts.push("No worker you spawned is still running.");
|
|
244
251
|
}
|
|
245
252
|
return parts.join("\n\n");
|
|
246
253
|
}
|
|
@@ -290,7 +297,8 @@ function hookReply(harness, kind, text, unread = 0, continues = true) {
|
|
|
290
297
|
const hookEventName = kind === "tool" ? "PostToolUse" : kind === "session_start" ? "SessionStart" : "UserPromptSubmit";
|
|
291
298
|
return JSON.stringify({ hookSpecificOutput: { hookEventName, additionalContext: text } });
|
|
292
299
|
}
|
|
293
|
-
var
|
|
300
|
+
var DEFERS = /* @__PURE__ */ new Set(["claude-code", "codex"]);
|
|
301
|
+
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.`;
|
|
294
302
|
async function runHook(args, options) {
|
|
295
303
|
const began = Date.now();
|
|
296
304
|
const blockStop = args.includes(BLOCK_STOP);
|
|
@@ -391,7 +399,8 @@ async function runHook(args, options) {
|
|
|
391
399
|
...nudged.length ? { nudged: nudged.map((n) => n.seq) } : {},
|
|
392
400
|
...directive ? { restated: directive.restated } : {},
|
|
393
401
|
...full ? { preamble: true } : {},
|
|
394
|
-
...intro ? { intro: true } : {}
|
|
402
|
+
...intro ? { intro: true } : {},
|
|
403
|
+
...event.kind === "tool" && DEFERS.has(claim.harness) ? { deferred: true } : {}
|
|
395
404
|
}).catch(() => {
|
|
396
405
|
});
|
|
397
406
|
}
|
|
@@ -442,7 +451,7 @@ async function subagentHook(client, claim, sub, event, takes, options) {
|
|
|
442
451
|
}
|
|
443
452
|
async function push(client, harness, waitMs, began, prompts, options, continues = true) {
|
|
444
453
|
const end = Date.now() + waitMs;
|
|
445
|
-
if (waitMs > 0) options.deadline?.(end - began + PUSH_PRINT_MS);
|
|
454
|
+
if (waitMs > 0) options.deadline?.(end - began + LIMITS.pushGraceS * 1e3 + PUSH_PRINT_MS);
|
|
446
455
|
let result;
|
|
447
456
|
for (; ; ) {
|
|
448
457
|
result = await client.request("stop_wait", {
|
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-5CAHHN55.js");
|
|
21
21
|
await run(process.argv.slice(2));
|
|
22
22
|
}
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
hooksTarget,
|
|
3
3
|
installHooks,
|
|
4
4
|
mcpInstall
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-4DNQFATX.js";
|
|
6
6
|
import "./chunk-7KP6TAJW.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-XWLF7ON2.js";
|
|
8
8
|
import "./chunk-YYSLYVNZ.js";
|
|
9
9
|
import "./chunk-DA3EB2YF.js";
|
|
10
10
|
export {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SPAWN_HELP
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KN7WEIE4.js";
|
|
4
4
|
import {
|
|
5
5
|
OPTIONS,
|
|
6
6
|
RelayUsage,
|
|
@@ -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.1.
|
|
36
|
+
version: "0.1.2",
|
|
37
37
|
description: "The swarm command line tool.",
|
|
38
38
|
license: "Apache-2.0",
|
|
39
39
|
homepage: "https://scp.dev",
|
|
@@ -78,6 +78,7 @@ import {
|
|
|
78
78
|
LIMITS,
|
|
79
79
|
PREAMBLE,
|
|
80
80
|
reachLines,
|
|
81
|
+
receiptLines,
|
|
81
82
|
RelayError,
|
|
82
83
|
shortId,
|
|
83
84
|
stripControl,
|
|
@@ -96,14 +97,16 @@ results back, as with native subagents.
|
|
|
96
97
|
${CLI_NAME} ask <to> [text|-] [--wait S] send a request and wait for its reply: ${CLI_NAME} ask lead "which branch?"
|
|
97
98
|
${CLI_NAME} inbox [--wait S] your unread messages, then marked read: ${CLI_NAME} inbox
|
|
98
99
|
${CLI_NAME} list [--all] agents on the relay: address, title, presence, status
|
|
99
|
-
${CLI_NAME} status
|
|
100
|
+
${CLI_NAME} status [id] the fleet in short; with the id of a message you sent: who read it when
|
|
100
101
|
${CLI_NAME} peek <ep> a short, redacted view of a peekable agent's last turns: ${CLI_NAME} peek rev
|
|
101
102
|
${CLI_NAME} set-status <state> [note] working, waiting, blocked or done: ${CLI_NAME} set-status done "PR open"
|
|
102
103
|
|
|
103
104
|
- spawn returns at once with the worker's name: do not background it (no &, no run_in_background).
|
|
104
105
|
- A worker's messages and its handback (exit code and its whole reply) arrive by themselves: when you stop, your stop
|
|
105
106
|
waits for them and continues with them, and new messages also reach you after your tool calls. No polling needed.
|
|
106
|
-
- Address a worker by the name spawn printed. Steer it meanwhile with ${CLI_NAME} send <name> <text
|
|
107
|
+
- Address a worker by the name spawn printed. Steer it meanwhile with ${CLI_NAME} send <name> <text>: it gets
|
|
108
|
+
the message after its next tool call or before it finishes, so its task need not tell it to check an inbox.
|
|
109
|
+
- With the relay MCP tools: relay_spawn starts a worker in one call, relay_send steers it, relay_status reads receipts.
|
|
107
110
|
- --no-push: a worker your stop does not wait on; its messages and handback still reach you after your tool calls
|
|
108
111
|
or with another worker's push.
|
|
109
112
|
- An <id> is a message id, its short id (the last 6 characters, shown with every message) or last.
|
|
@@ -132,6 +135,9 @@ usage:
|
|
|
132
135
|
${CLI_NAME} ask-wait <id> [--wait S] keep waiting for the reply to a request
|
|
133
136
|
${CLI_NAME} status [--all] [--activity] the fleet in short: you, agents by presence and status, who waits or is blocked,
|
|
134
137
|
unread counts; --activity adds the latest activity, --all counts offline agents
|
|
138
|
+
${CLI_NAME} status <id> a message you sent, a line per recipient: read at HH:MM:SS, delivered at
|
|
139
|
+
HH:MM:SS (a background hook handed it over; it reaches the model with its next
|
|
140
|
+
request), shown by a hook at HH:MM:SS (in its context, not yet marked read), or unread
|
|
135
141
|
${CLI_NAME} watch a live tail of the fleet: presence, messages, status, activity
|
|
136
142
|
${CLI_NAME} set-status <state> [note] your status line: working, waiting, blocked or done
|
|
137
143
|
${CLI_NAME} set-title <text> what you work on, shown in list, peers and whoami ("" clears it)
|
|
@@ -188,8 +194,10 @@ scopes (selectors are comma-separated, e.g. tree:parent,tree:children,group:team
|
|
|
188
194
|
still reach you after your tool calls, with another worker's push, or in your inbox.
|
|
189
195
|
codex gets -c overrides that hand the grant to its scp MCP server
|
|
190
196
|
(also behind env, VAR=value, timeout, nohup or nice). codex exec outside a git
|
|
191
|
-
repo gets --skip-git-repo-check when its codex has it
|
|
192
|
-
runs on /dev/null for stdin
|
|
197
|
+
repo gets --skip-git-repo-check when its codex has it. With its prompt in argv it
|
|
198
|
+
runs on /dev/null for stdin, except in the foreground (--fg), where piped context
|
|
199
|
+
or a file reaches it (from an agent's shell only a pipe with data within 300 ms);
|
|
200
|
+
a - prompt, or none, keeps stdin.
|
|
193
201
|
claude -p and grok -p get the relay messaging tools allowed (--allowedTools,
|
|
194
202
|
grok --allow) unless you pass a permission flag. muse runs with
|
|
195
203
|
--contributor-mode off unless you pass --contributor-mode on (or a -c contributor
|
|
@@ -348,6 +356,7 @@ var VERBS = {
|
|
|
348
356
|
machines: IDENTITY
|
|
349
357
|
};
|
|
350
358
|
var MESSAGE_REF = /^(last|[0-9a-z]{4,})$/;
|
|
359
|
+
var ACK_ALL_PAGES = Math.ceil(2 * LIMITS.inboxMax / LIMITS.inboxPage) + 1;
|
|
351
360
|
async function lookup(client, id, member = false) {
|
|
352
361
|
try {
|
|
353
362
|
return (await client.request("message", { id, ...member ? { member: true } : {} })).message;
|
|
@@ -386,6 +395,7 @@ function printMessage(message) {
|
|
|
386
395
|
out(
|
|
387
396
|
`#${message.seq} ${message.kind} from ${message.from.address} (${harness}) ${time(message.created_at)} id ${shortId(message.id)} (${message.id})${message.reply_to ? ` reply to ${message.reply_to}` : ""}`
|
|
388
397
|
);
|
|
398
|
+
if (message.shown_at) out(`(a hook already showed you this at ${time(message.shown_at)}; that copy may reach you after this command)`);
|
|
389
399
|
out(wrapMessage(message));
|
|
390
400
|
out();
|
|
391
401
|
}
|
|
@@ -481,10 +491,10 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
481
491
|
out(package_default.version);
|
|
482
492
|
return 0;
|
|
483
493
|
}
|
|
484
|
-
if (verb === "hook") return (await import("./hooks-
|
|
485
|
-
if (verb === "setup") return (await import("./setup-
|
|
494
|
+
if (verb === "hook") return (await import("./hooks-O3UZXDYG.js")).relayHookCommand(rest, startDaemon);
|
|
495
|
+
if (verb === "setup") return (await import("./setup-RDPVGE4H.js")).relaySetupCommand(rest, cwd, out);
|
|
486
496
|
if (verb === "spawn") {
|
|
487
|
-
const { splitSpawn, spawnCommand } = await import("./spawn-
|
|
497
|
+
const { splitSpawn, spawnCommand } = await import("./spawn-TQ3HOZTQ.js");
|
|
488
498
|
const { flags } = splitSpawn(rest);
|
|
489
499
|
if (flags.includes("--help") || flags.includes("-h")) {
|
|
490
500
|
out(flags.includes("--all") ? RELAY_HELP : SPAWN_HELP);
|
|
@@ -508,11 +518,11 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
508
518
|
for (const key of Object.keys(values)) if (!allowed.includes(key)) throw new RelayUsage(`${CLI_NAME} ${verb} does not take --${key}`);
|
|
509
519
|
if (verb === "daemon") return daemon(values.idle, values.policy);
|
|
510
520
|
if (verb === "mcp")
|
|
511
|
-
return (await import("./mcp-
|
|
521
|
+
return (await import("./mcp-P2AZYP5G.js")).relayMcpCommand(values, positionals);
|
|
512
522
|
if (verb === "hooks") {
|
|
513
523
|
if (positionals[0] !== "install" || positionals.length > 1) throw new RelayUsage(`${CLI_NAME} hooks takes install --harness claude|codex|opencode|agy|muse`);
|
|
514
524
|
if (values.write && values.print) throw new RelayUsage("--print and --write: pick one");
|
|
515
|
-
return (await import("./install-
|
|
525
|
+
return (await import("./install-HFBSKSTG.js")).installHooks({
|
|
516
526
|
harness: values.harness,
|
|
517
527
|
scope: values.scope,
|
|
518
528
|
write: values.write === true,
|
|
@@ -524,7 +534,7 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
524
534
|
});
|
|
525
535
|
}
|
|
526
536
|
if (verb === "relay") return (await import("./relay-VVJPYVXE.js")).relayVerb(positionals, values.json === true);
|
|
527
|
-
if (verb === "up") return (await import("./up-
|
|
537
|
+
if (verb === "up") return (await import("./up-KXOYO7QV.js")).up(positionals, values, cwd, cloudStatus);
|
|
528
538
|
if (verb === "login")
|
|
529
539
|
return (await import("./relay-VVJPYVXE.js")).loginMain(positionals, values);
|
|
530
540
|
if (verb === "logout") return (await import("./relay-VVJPYVXE.js")).logoutMain(positionals, values.json === true);
|
|
@@ -697,6 +707,8 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
697
707
|
const lines = reachLines(result2.deliveries);
|
|
698
708
|
out(`sent ${result2.id} to ${result2.deliveries.map((d) => lines.length ? d.address : deliveryText(d)).join(", ")}${redactedText(result2.redacted)}`);
|
|
699
709
|
for (const line of lines) out(` ${line}`);
|
|
710
|
+
if (result2.deliveries.some((d) => d.state === "running" || d.state === "starting"))
|
|
711
|
+
out(` ${CLI_NAME} status ${shortId(result2.id)} shows when it is read`);
|
|
700
712
|
}
|
|
701
713
|
return 0;
|
|
702
714
|
}
|
|
@@ -757,9 +769,9 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
757
769
|
result.acked += (await client.request("ack", { seqs: seqs.slice(i, i + LIMITS.inboxPage) })).acked;
|
|
758
770
|
};
|
|
759
771
|
if (values.all) {
|
|
760
|
-
for (let
|
|
761
|
-
before = result.acked;
|
|
772
|
+
for (let page = 0; page < ACK_ALL_PAGES; page++) {
|
|
762
773
|
const { messages } = await client.request("inbox", { wait_s: 0 });
|
|
774
|
+
if (!messages.length) break;
|
|
763
775
|
await ack(messages.flatMap((m) => m.seq === void 0 ? [] : [m.seq]));
|
|
764
776
|
}
|
|
765
777
|
} else {
|
|
@@ -778,6 +790,20 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
778
790
|
return 0;
|
|
779
791
|
}
|
|
780
792
|
case "status": {
|
|
793
|
+
if (positionals.length) {
|
|
794
|
+
if (positionals.length > 1 || values.all || values.activity)
|
|
795
|
+
throw new RelayUsage(`${CLI_NAME} status takes one message id, or --all and --activity for the fleet`);
|
|
796
|
+
if (!broker) throw new RelayUsage(`${CLI_NAME} status <id> is the local broker's; it has no --cloud form`);
|
|
797
|
+
if (!me) throw new RelayUsage(`${CLI_NAME} status <id> reads a message you sent: pass --as HANDLE, or run it from the agent's shell or your terminal`);
|
|
798
|
+
const receipts = await client.request("receipts", { id: positionals[0] });
|
|
799
|
+
if (asJson) json(receipts);
|
|
800
|
+
else {
|
|
801
|
+
out(`${receipts.id}:`);
|
|
802
|
+
const lines = receiptLines(receipts);
|
|
803
|
+
for (const line of lines.length ? lines : ["no deliveries of it are kept any more"]) out(` ${line}`);
|
|
804
|
+
}
|
|
805
|
+
return 0;
|
|
806
|
+
}
|
|
781
807
|
const snapshot = await client.request("snapshot", { all: values.all === true });
|
|
782
808
|
if (asJson) {
|
|
783
809
|
json(snapshot);
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
spawnDetached
|
|
3
|
+
} from "./chunk-V5I7KEU7.js";
|
|
4
|
+
import "./chunk-KN7WEIE4.js";
|
|
5
|
+
import "./chunk-N3ZROLFA.js";
|
|
1
6
|
import {
|
|
2
7
|
mcpInstall
|
|
3
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4DNQFATX.js";
|
|
4
9
|
import {
|
|
5
10
|
RelayUsage,
|
|
6
11
|
startDaemon
|
|
7
12
|
} from "./chunk-7KP6TAJW.js";
|
|
8
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-XWLF7ON2.js";
|
|
9
14
|
import "./chunk-YYSLYVNZ.js";
|
|
10
15
|
import {
|
|
11
16
|
harnessPid,
|
|
@@ -41,11 +46,27 @@ async function relayMcpCommand(values, positionals) {
|
|
|
41
46
|
output: (line) => void process.stdout.write(line),
|
|
42
47
|
env: process.env,
|
|
43
48
|
channels: values.channels === true,
|
|
49
|
+
spawn: (request) => spawnDetached(spawnWords(request), request.cwd ?? claim.cwd),
|
|
44
50
|
log: (line) => void process.stderr.write(`${line}
|
|
45
51
|
`)
|
|
46
52
|
});
|
|
47
53
|
return 0;
|
|
48
54
|
}
|
|
55
|
+
function spawnWords(request) {
|
|
56
|
+
return [
|
|
57
|
+
"--bg",
|
|
58
|
+
"--as",
|
|
59
|
+
request.as,
|
|
60
|
+
...request.harness !== void 0 ? ["--harness", request.harness] : [],
|
|
61
|
+
...request.name !== void 0 ? ["--name", request.name] : [],
|
|
62
|
+
...request.title !== void 0 ? ["--title", request.title] : [],
|
|
63
|
+
...request.no_push ? ["--no-push"] : [],
|
|
64
|
+
...request.handback_bytes !== void 0 ? ["--handback-bytes", String(request.handback_bytes)] : [],
|
|
65
|
+
"--",
|
|
66
|
+
...request.command
|
|
67
|
+
];
|
|
68
|
+
}
|
|
49
69
|
export {
|
|
50
|
-
relayMcpCommand
|
|
70
|
+
relayMcpCommand,
|
|
71
|
+
spawnWords
|
|
51
72
|
};
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
verifyMount,
|
|
15
15
|
verifyMusePlugin,
|
|
16
16
|
whichOnPath
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-
|
|
17
|
+
} from "./chunk-N3ZROLFA.js";
|
|
18
|
+
import "./chunk-4DNQFATX.js";
|
|
19
19
|
import "./chunk-7KP6TAJW.js";
|
|
20
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-XWLF7ON2.js";
|
|
21
21
|
import "./chunk-YYSLYVNZ.js";
|
|
22
22
|
import "./chunk-DA3EB2YF.js";
|
|
23
23
|
export {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MESSAGING,
|
|
3
|
+
SPAWN_RUNNER,
|
|
4
|
+
STDIN_WAIT_MS,
|
|
5
|
+
agentShell,
|
|
6
|
+
agyNote,
|
|
7
|
+
claudeTranscripts,
|
|
8
|
+
closedStdin,
|
|
9
|
+
codexMount,
|
|
10
|
+
codexOverrideAt,
|
|
11
|
+
codexOverrides,
|
|
12
|
+
codexSkipsGitCheck,
|
|
13
|
+
contributorRefusal,
|
|
14
|
+
harnessArgv,
|
|
15
|
+
harnessOutput,
|
|
16
|
+
harnessStdin,
|
|
17
|
+
programAt,
|
|
18
|
+
repliesOutput,
|
|
19
|
+
resumeArgv,
|
|
20
|
+
runHarness,
|
|
21
|
+
spawnCommand,
|
|
22
|
+
spawnDefaults,
|
|
23
|
+
spawnDetached,
|
|
24
|
+
spawnEnvironment,
|
|
25
|
+
spawnPrompt,
|
|
26
|
+
spawnTitle,
|
|
27
|
+
splitSpawn,
|
|
28
|
+
stderrOutput,
|
|
29
|
+
steerArgv,
|
|
30
|
+
transcriptReplies,
|
|
31
|
+
workerName
|
|
32
|
+
} from "./chunk-V5I7KEU7.js";
|
|
33
|
+
import {
|
|
34
|
+
SPAWN_HELP
|
|
35
|
+
} from "./chunk-KN7WEIE4.js";
|
|
36
|
+
import "./chunk-N3ZROLFA.js";
|
|
37
|
+
import "./chunk-4DNQFATX.js";
|
|
38
|
+
import "./chunk-7KP6TAJW.js";
|
|
39
|
+
import "./chunk-XWLF7ON2.js";
|
|
40
|
+
import "./chunk-YYSLYVNZ.js";
|
|
41
|
+
import "./chunk-DA3EB2YF.js";
|
|
42
|
+
export {
|
|
43
|
+
MESSAGING,
|
|
44
|
+
SPAWN_HELP,
|
|
45
|
+
SPAWN_RUNNER,
|
|
46
|
+
STDIN_WAIT_MS,
|
|
47
|
+
agentShell,
|
|
48
|
+
agyNote,
|
|
49
|
+
claudeTranscripts,
|
|
50
|
+
closedStdin,
|
|
51
|
+
codexMount,
|
|
52
|
+
codexOverrideAt,
|
|
53
|
+
codexOverrides,
|
|
54
|
+
codexSkipsGitCheck,
|
|
55
|
+
contributorRefusal,
|
|
56
|
+
harnessArgv,
|
|
57
|
+
harnessOutput,
|
|
58
|
+
harnessStdin,
|
|
59
|
+
programAt,
|
|
60
|
+
repliesOutput,
|
|
61
|
+
resumeArgv,
|
|
62
|
+
runHarness,
|
|
63
|
+
spawnCommand,
|
|
64
|
+
spawnDefaults,
|
|
65
|
+
spawnDetached,
|
|
66
|
+
spawnEnvironment,
|
|
67
|
+
spawnPrompt,
|
|
68
|
+
spawnTitle,
|
|
69
|
+
splitSpawn,
|
|
70
|
+
stderrOutput,
|
|
71
|
+
steerArgv,
|
|
72
|
+
transcriptReplies,
|
|
73
|
+
workerName
|
|
74
|
+
};
|
|
@@ -25,7 +25,7 @@ async function up(positionals, values, cwd, brokerStatus) {
|
|
|
25
25
|
} else out(`already logged in to ${url}`);
|
|
26
26
|
const enabled = await (await import("./remote-3BWFISXJ.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
|
|
27
27
|
if (enabled !== 0) return enabled;
|
|
28
|
-
const setup = (await import("./setup-
|
|
28
|
+
const setup = (await import("./setup-RDPVGE4H.js")).relaySetupCommand(["--write"], cwd, out);
|
|
29
29
|
if (setup !== 0) return setup;
|
|
30
30
|
const broker = await brokerStatus();
|
|
31
31
|
if (!broker.enabled) out(`the broker that was already running stays local-only until it exits (it stops when idle); the next one links to ${url}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scp-dev/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The swarm command line tool.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://scp.dev",
|
|
@@ -24,10 +24,10 @@
|
|
|
24
24
|
"typecheck": "tsc -p ."
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@scp-dev/core": "0.1.
|
|
28
|
-
"@scp-dev/broker": "0.1.
|
|
29
|
-
"@scp-dev/hub": "0.1.
|
|
30
|
-
"@scp-dev/mcp": "0.1.
|
|
27
|
+
"@scp-dev/core": "0.1.1",
|
|
28
|
+
"@scp-dev/broker": "0.1.1",
|
|
29
|
+
"@scp-dev/hub": "0.1.1",
|
|
30
|
+
"@scp-dev/mcp": "0.1.1",
|
|
31
31
|
"smol-toml": "^1.9.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|