@scp-dev/cli 0.2.1 → 0.2.3
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-ZLL5EGI2.js → chunk-E7JG5PJB.js} +4 -3
- package/dist/{chunk-25ZVL5LD.js → chunk-IXCHAWOL.js} +3 -3
- package/dist/{chunk-NJLFHBSS.js → chunk-W3VNCRBX.js} +34 -11
- package/dist/{chunk-HF4KP6AR.js → chunk-X4ZLEM47.js} +14 -1
- package/dist/{chunk-MXQ7B3CQ.js → chunk-ZDU53ZRF.js} +1 -1
- package/dist/{chunk-KC5AV5NQ.js → chunk-ZMVFN6MU.js} +3 -3
- package/dist/{claude-WTCPUYU3.js → claude-JD7WXMAT.js} +4 -4
- package/dist/cli.js +1 -1
- package/dist/{codex-YZZNH6IQ.js → codex-4IIM6ZE2.js} +6 -5
- package/dist/{hooks-4B5NOH7X.js → hooks-P4J6OZCO.js} +2 -2
- package/dist/{install-2ZM3IYXG.js → install-FKGGPT6L.js} +3 -3
- package/dist/{join-BH5CS6SL.js → join-RGFHOSLE.js} +7 -4
- package/dist/{login-UB72MU7W.js → login-IPS75GLQ.js} +2 -2
- package/dist/{main-I4F53MMA.js → main-SQKCKV3B.js} +73 -33
- package/dist/{mcp-RXAVQBMP.js → mcp-TUWMLGE4.js} +5 -5
- package/dist/mesh-VNZV3POO.js +28 -0
- package/dist/{peek-M2HQVO4O.js → peek-4D4VGKOW.js} +1 -1
- package/dist/{pi-IAVFVOOO.js → pi-JZ4DXY7V.js} +9 -7
- package/dist/pi-extension.js +137 -0
- package/dist/{relay-QDX2BNZH.js → relay-U634RHIJ.js} +1 -1
- package/dist/{remote-2OWBWLJH.js → remote-NTHRHVEP.js} +2 -2
- package/dist/{setup-HPH7HE6O.js → setup-RO7356M4.js} +4 -4
- package/dist/{spawn-V66GYUNC.js → spawn-BYU2X46L.js} +5 -5
- package/dist/{up-2YGEDA5J.js → up-GDD24GPL.js} +4 -4
- package/package.json +6 -5
|
@@ -374,7 +374,8 @@ var OPTIONS = {
|
|
|
374
374
|
who: { type: "boolean" },
|
|
375
375
|
peek: { type: "string" },
|
|
376
376
|
endpoint: { type: "string" },
|
|
377
|
-
live: { type: "boolean" }
|
|
377
|
+
live: { type: "boolean" },
|
|
378
|
+
parent: { type: "string" }
|
|
378
379
|
};
|
|
379
380
|
var SPAWN_OPTIONS = [
|
|
380
381
|
"as",
|
|
@@ -486,7 +487,7 @@ async function open(verb, values, cwd, bodyFromStdin, walk) {
|
|
|
486
487
|
`${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)`
|
|
487
488
|
);
|
|
488
489
|
}
|
|
489
|
-
const { machineName, openCloud } = await import("./remote-
|
|
490
|
+
const { machineName, openCloud } = await import("./remote-NTHRHVEP.js");
|
|
490
491
|
const handle2 = named ? localHandle(named) : void 0;
|
|
491
492
|
const claim = handle2 ? {
|
|
492
493
|
handle: handle2,
|
|
@@ -743,7 +744,7 @@ function relayVerb(positionals, asJson, env = process.env) {
|
|
|
743
744
|
}
|
|
744
745
|
async function loginMain(positionals, values) {
|
|
745
746
|
if (positionals.length) throw new RelayUsage(`${CLI_NAME3} login takes only flags`);
|
|
746
|
-
return (await import("./login-
|
|
747
|
+
return (await import("./login-IPS75GLQ.js")).login(values);
|
|
747
748
|
}
|
|
748
749
|
function logoutMain(positionals, asJson, env = process.env) {
|
|
749
750
|
if (positionals.length) throw new RelayUsage(`${CLI_NAME3} logout takes no arguments`);
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
agySettingsFile,
|
|
4
4
|
mcpFile,
|
|
5
5
|
mountedEntry
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-W3VNCRBX.js";
|
|
7
7
|
import {
|
|
8
8
|
steerPrompt
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-X4ZLEM47.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-E7JG5PJB.js";
|
|
22
22
|
import {
|
|
23
23
|
SELF,
|
|
24
24
|
note
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hooksTarget
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZMVFN6MU.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-X4ZLEM47.js";
|
|
16
16
|
import {
|
|
17
17
|
RelayUsage,
|
|
18
18
|
gitToplevel,
|
|
19
19
|
setJoinMode
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-E7JG5PJB.js";
|
|
21
21
|
|
|
22
22
|
// src/setup.ts
|
|
23
23
|
import { spawnSync } from "child_process";
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
mkdtempSync,
|
|
36
36
|
readFileSync,
|
|
37
37
|
readlinkSync,
|
|
38
|
+
realpathSync,
|
|
38
39
|
renameSync,
|
|
39
40
|
rmSync,
|
|
40
41
|
statSync,
|
|
@@ -611,6 +612,23 @@ function museInstalled(env) {
|
|
|
611
612
|
}
|
|
612
613
|
}
|
|
613
614
|
var installedFrom = (record) => record && isObject(record.source) ? String(record.source.path) : void 0;
|
|
615
|
+
function samePath(a, b) {
|
|
616
|
+
if (a === void 0) return false;
|
|
617
|
+
if (a === b) return true;
|
|
618
|
+
try {
|
|
619
|
+
return realpathSync(a) === realpathSync(b);
|
|
620
|
+
} catch {
|
|
621
|
+
return false;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
function museReviewNeeded(cli, run) {
|
|
625
|
+
try {
|
|
626
|
+
const caps = JSON.parse(run([cli, "plugins", "inspect", SERVER, "--json"]).output).runtime_capabilities;
|
|
627
|
+
return Array.isArray(caps) && caps.some((cap) => isObject(cap) && cap.status === "review_needed");
|
|
628
|
+
} catch {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
614
632
|
function pluginDir(manifest) {
|
|
615
633
|
return dirname(dirname(manifest));
|
|
616
634
|
}
|
|
@@ -655,19 +673,24 @@ function planHooks(plan, harness, ctx) {
|
|
|
655
673
|
const from = installedFrom(record);
|
|
656
674
|
const current = content ?? readFileSync(resolved(file), "utf8");
|
|
657
675
|
const disabled = record?.enabled === false;
|
|
658
|
-
const install = content !== void 0 || from
|
|
659
|
-
|
|
676
|
+
const install = content !== void 0 || !samePath(from, dir) || record?.manifest_sha256 !== sha256(current);
|
|
677
|
+
const cli = ctx.found.get("muse");
|
|
678
|
+
const review = install || museReviewNeeded(cli, run);
|
|
679
|
+
if (!install && !disabled && !review) {
|
|
660
680
|
note(plan, harness, `hooks: up to date in ${file}${held}`);
|
|
661
681
|
return;
|
|
662
682
|
}
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
683
|
+
const commands = [
|
|
684
|
+
...install ? [[cli, "plugins", "install", dir, "--scope", "user"]] : [],
|
|
685
|
+
...disabled ? [[cli, "plugins", "enable", SERVER]] : [],
|
|
686
|
+
...review ? [[cli, "plugins", "approve", SERVER]] : []
|
|
687
|
+
];
|
|
688
|
+
const head = install ? `${content ? "write" : "install"} the ${CLI_NAME} plugin ${file}${held}${from && !samePath(from, dir) ? `, replacing the scp plugin installed from ${from}` : ""}${disabled ? ", and enable it, which Muse has disabled" : ""}` : disabled ? `enable the ${CLI_NAME} plugin ${file}, which Muse has disabled` : `approve the hooks of the ${CLI_NAME} plugin ${file}, which Muse holds for review`;
|
|
666
689
|
addStep(plan, {
|
|
667
690
|
harness,
|
|
668
691
|
label: "hooks",
|
|
669
692
|
file,
|
|
670
|
-
plan: [head, ...commands.map(shown), ...
|
|
693
|
+
plan: [head, ...commands.map(shown), ...review ? ["approves its hooks: a headless muse exec runs no hook Muse holds for review"] : []],
|
|
671
694
|
apply: () => {
|
|
672
695
|
const next = install ? desiredHooks(kind, file, blockStop) : void 0;
|
|
673
696
|
const saved = next === void 0 ? void 0 : writeFile(file, next, now, { mode: 420 });
|
|
@@ -741,7 +764,7 @@ function planUnhook(plan, harness, kind, file, items, ctx) {
|
|
|
741
764
|
const gone = { harness, label: "hooks", file, plan: [`forget ${file}, which is gone`], apply: () => void 0, manifest: forgotten };
|
|
742
765
|
if (kind === "muse") {
|
|
743
766
|
const cli = ctx.found.get("muse");
|
|
744
|
-
const installed = installedFrom(museInstalled(ctx.env))
|
|
767
|
+
const installed = samePath(installedFrom(museInstalled(ctx.env)), pluginDir(file));
|
|
745
768
|
if (!existsSync(target) && !installed) {
|
|
746
769
|
addStep(plan, gone);
|
|
747
770
|
return;
|
|
@@ -936,7 +959,7 @@ function verifyMusePlugin(cli, dir, run, manifest) {
|
|
|
936
959
|
} catch {
|
|
937
960
|
return { ok: false, line: `unknown: muse plugins list --json exited ${result.code}: ${lastLine(result.output)}` };
|
|
938
961
|
}
|
|
939
|
-
const ours = installedFrom(record)
|
|
962
|
+
const ours = samePath(installedFrom(record), dir);
|
|
940
963
|
if (manifest === void 0) return { ok: !ours, line: ours ? "still installed (muse plugins list)" : "removed (muse plugins list)" };
|
|
941
964
|
if (!ours) return { ok: false, line: "failed: muse plugins list does not show the scp plugin from setup" };
|
|
942
965
|
const current = record.manifest_sha256 === sha256(manifest) && record.enabled !== false;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
psStart,
|
|
13
13
|
relayIdentity,
|
|
14
14
|
sessionKey
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-E7JG5PJB.js";
|
|
16
16
|
|
|
17
17
|
// src/hooks.ts
|
|
18
18
|
import { Readable } from "stream";
|
|
@@ -345,6 +345,15 @@ function inferredTitle(harness, event, named, payload) {
|
|
|
345
345
|
const text = harness === "claude-code" ? transcriptTitle(asString(field(payload, "transcript_path", "transcriptPath")), asString(field(payload, "session_id", "sessionId"))) : own && !OPENCODE_UNNAMED.test(own) ? titleText(own) : void 0;
|
|
346
346
|
return text ? { text, from: "summary" } : void 0;
|
|
347
347
|
}
|
|
348
|
+
function launchedSubagent(harness, event, payload) {
|
|
349
|
+
if (harness !== "claude-code" || event.kind !== "tool" || event.agent || !/^(agent|task)$/i.test(event.tool ?? "")) return void 0;
|
|
350
|
+
const input = field(payload, "tool_input", "toolInput");
|
|
351
|
+
const response = field(payload, "tool_response", "toolResponse");
|
|
352
|
+
if (typeof input !== "object" || typeof response !== "object" || Array.isArray(response)) return void 0;
|
|
353
|
+
const id = asString(response.agentId);
|
|
354
|
+
const title = titleText(asString(input.description));
|
|
355
|
+
return id && title && /^[A-Za-z0-9_-]{1,64}$/.test(id) ? { id, title } : void 0;
|
|
356
|
+
}
|
|
348
357
|
function hookReply(harness, kind, text, unread = 0, continues = true) {
|
|
349
358
|
if (kind === "stop" && harness === "claude-code" && continues)
|
|
350
359
|
return JSON.stringify({ hookSpecificOutput: { hookEventName: "Stop", additionalContext: text } });
|
|
@@ -448,6 +457,8 @@ async function runHook(args, options) {
|
|
|
448
457
|
if (turn) posted.push(client.request("peek_turn", turn));
|
|
449
458
|
const title = inferredTitle(claim.harness, event, named, payload);
|
|
450
459
|
if (title) posted.push(client.request("title_infer", title));
|
|
460
|
+
const launched = launchedSubagent(claim.harness, event, payload);
|
|
461
|
+
if (launched) posted.push(client.request("subagent_title", launched));
|
|
451
462
|
const injected = inject ? client.request(
|
|
452
463
|
"inject",
|
|
453
464
|
counts ? { count: true, ...stop ? { stop: true, directives: true, ...blockStop ? { hold_unread: true } : {} } : {} } : {}
|
|
@@ -510,6 +521,8 @@ async function wakeHook(harnessArg, payload, waitS, options) {
|
|
|
510
521
|
const client = await options.connect(false);
|
|
511
522
|
try {
|
|
512
523
|
await client.hello({ transient: true, claim });
|
|
524
|
+
await client.request("wake", { via: OPENCODE_WAKE }).catch(() => {
|
|
525
|
+
});
|
|
513
526
|
const result = await client.request("inject", { wait_s: waitMs / 1e3 });
|
|
514
527
|
if (!result.offered.length) return;
|
|
515
528
|
const intro = result.intro ? introText(result.intro, result.intro_key) : void 0;
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
mergeHooks,
|
|
11
11
|
musePlugin,
|
|
12
12
|
opencodePlugin
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-X4ZLEM47.js";
|
|
14
14
|
import {
|
|
15
15
|
RelayUsage,
|
|
16
16
|
gitToplevel
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-E7JG5PJB.js";
|
|
18
18
|
|
|
19
19
|
// src/install.ts
|
|
20
20
|
import { chmodSync, copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync } from "fs";
|
|
@@ -166,7 +166,7 @@ function injectionNotes(harness, scope, target, blockStop, wait) {
|
|
|
166
166
|
];
|
|
167
167
|
case "muse":
|
|
168
168
|
return [
|
|
169
|
-
`Muse loads hooks only from plugins: muse plugins install ${dirname(dirname(target))} --scope ${scope}`,
|
|
169
|
+
`Muse loads hooks only from plugins: muse plugins install ${dirname(dirname(target))} --scope ${scope}, then muse plugins approve scp (a headless muse exec runs no hook Muse holds for review)`,
|
|
170
170
|
`new relay messages reach the model after each tool call and with each prompt; ${held}`,
|
|
171
171
|
pushed,
|
|
172
172
|
"Muse hands plugin hooks no SCP_* variables: they key the session by the harness process, so set no SCP_SESSION for Muse's shim"
|
|
@@ -3,16 +3,16 @@ import {
|
|
|
3
3
|
mcpFile,
|
|
4
4
|
mountedEntry,
|
|
5
5
|
whichOnPath
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-W3VNCRBX.js";
|
|
7
|
+
import "./chunk-ZMVFN6MU.js";
|
|
8
|
+
import "./chunk-X4ZLEM47.js";
|
|
9
9
|
import "./chunk-YYSLYVNZ.js";
|
|
10
10
|
import {
|
|
11
11
|
CHANNELS_ENV,
|
|
12
12
|
RelayUsage,
|
|
13
13
|
deriveHandle,
|
|
14
14
|
joinMode
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-E7JG5PJB.js";
|
|
16
16
|
import "./chunk-MNTIEWKF.js";
|
|
17
17
|
|
|
18
18
|
// src/claude.ts
|
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-SQKCKV3B.js");
|
|
21
21
|
await run(process.argv.slice(2));
|
|
22
22
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
codexMount,
|
|
3
3
|
codexOverrides
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-IXCHAWOL.js";
|
|
5
5
|
import "./chunk-KN7WEIE4.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-W3VNCRBX.js";
|
|
7
|
+
import "./chunk-ZMVFN6MU.js";
|
|
8
|
+
import "./chunk-X4ZLEM47.js";
|
|
9
9
|
import "./chunk-YYSLYVNZ.js";
|
|
10
10
|
import {
|
|
11
11
|
RelayUsage,
|
|
12
12
|
readPsTable,
|
|
13
13
|
relayIdentity,
|
|
14
14
|
startDaemon
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-E7JG5PJB.js";
|
|
16
16
|
import {
|
|
17
17
|
SELF
|
|
18
18
|
} from "./chunk-MNTIEWKF.js";
|
|
@@ -486,6 +486,7 @@ ${stderr.toString().trim()}` : ""}`
|
|
|
486
486
|
},
|
|
487
487
|
claim,
|
|
488
488
|
channels: true,
|
|
489
|
+
wake: "codex-app-server",
|
|
489
490
|
input,
|
|
490
491
|
env: launch.env,
|
|
491
492
|
output: (chunk) => {
|
|
@@ -38,11 +38,11 @@ import {
|
|
|
38
38
|
tldrEvery,
|
|
39
39
|
transcriptTitle,
|
|
40
40
|
unreadPointer
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-X4ZLEM47.js";
|
|
42
42
|
import {
|
|
43
43
|
SUBAGENT
|
|
44
44
|
} from "./chunk-YYSLYVNZ.js";
|
|
45
|
-
import "./chunk-
|
|
45
|
+
import "./chunk-E7JG5PJB.js";
|
|
46
46
|
import "./chunk-MNTIEWKF.js";
|
|
47
47
|
export {
|
|
48
48
|
AGY_HOOK_SET,
|
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
hooksTarget,
|
|
3
3
|
installHooks,
|
|
4
4
|
mcpInstall
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
5
|
+
} from "./chunk-ZMVFN6MU.js";
|
|
6
|
+
import "./chunk-X4ZLEM47.js";
|
|
7
7
|
import "./chunk-YYSLYVNZ.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-E7JG5PJB.js";
|
|
9
9
|
import "./chunk-MNTIEWKF.js";
|
|
10
10
|
export {
|
|
11
11
|
hooksTarget,
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
sessionKey,
|
|
6
6
|
startDaemon,
|
|
7
7
|
walkNow
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-E7JG5PJB.js";
|
|
9
9
|
import "./chunk-MNTIEWKF.js";
|
|
10
10
|
|
|
11
11
|
// src/join.ts
|
|
@@ -32,9 +32,12 @@ async function joinCommand(values, positionals, cwd, out) {
|
|
|
32
32
|
});
|
|
33
33
|
const client = await connectBroker(dir, () => startDaemon(dir));
|
|
34
34
|
try {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const under = typeof values.under === "string" ? values.under : void 0;
|
|
36
|
+
const hello = await client.hello({ transient: true, claim, join: { ...handle ? { handle } : {}, ...under ? { under } : {} } });
|
|
37
|
+
const view = hello.endpoint;
|
|
38
|
+
if (values.json === true) out(JSON.stringify(view, null, 2));
|
|
39
|
+
else
|
|
40
|
+
out(`joined as ${view.address}${view.parent ? ` under ${view.parent}` : ""}: peers can message it, and its hooks hand it new messages at turn boundaries`);
|
|
38
41
|
} finally {
|
|
39
42
|
client.close();
|
|
40
43
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
machineName
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ZDU53ZRF.js";
|
|
4
4
|
import {
|
|
5
5
|
RelayUsage,
|
|
6
6
|
activeRelay,
|
|
7
7
|
saveCredential
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-E7JG5PJB.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-E7JG5PJB.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.2.
|
|
36
|
+
version: "0.2.3",
|
|
37
37
|
description: "The swarm command line tool.",
|
|
38
38
|
license: "Apache-2.0",
|
|
39
39
|
homepage: "https://scp.dev",
|
|
@@ -65,6 +65,7 @@ var package_default = {
|
|
|
65
65
|
},
|
|
66
66
|
devDependencies: {
|
|
67
67
|
"@openai/codex": "0.159.0",
|
|
68
|
+
"@scp-dev/mesh": "workspace:*",
|
|
68
69
|
"opencode-ai": "1.18.34",
|
|
69
70
|
tsup: "^8.5.1"
|
|
70
71
|
},
|
|
@@ -79,11 +80,13 @@ import {
|
|
|
79
80
|
deliveryText,
|
|
80
81
|
LIMITS,
|
|
81
82
|
PREAMBLE,
|
|
83
|
+
presenceText,
|
|
82
84
|
reachLines,
|
|
83
85
|
receiptLines,
|
|
84
86
|
RelayError,
|
|
85
87
|
shortId,
|
|
86
88
|
stripControl,
|
|
89
|
+
wakeText,
|
|
87
90
|
widenText,
|
|
88
91
|
wrapMessage,
|
|
89
92
|
CLI_NAME
|
|
@@ -98,8 +101,9 @@ results back, as with native subagents.
|
|
|
98
101
|
${CLI_NAME} reply <id> [text|-] answer a message in its thread: ${CLI_NAME} reply 5af1f2 "done"
|
|
99
102
|
${CLI_NAME} ask <to> [text|-] [--wait S] send a request and wait for its reply: ${CLI_NAME} ask lead "which branch?"
|
|
100
103
|
${CLI_NAME} inbox [--wait S] your unread messages, then marked read: ${CLI_NAME} inbox
|
|
101
|
-
${CLI_NAME} list [--all] agents on the relay: address, title, presence, status; --all adds offline ones
|
|
104
|
+
${CLI_NAME} list [--all] agents on the relay: address, title, presence, wake, status; --all adds offline ones
|
|
102
105
|
${CLI_NAME} join [name] put this session on the relay, named once: ${CLI_NAME} join lead (${CLI_NAME} name renames it)
|
|
106
|
+
${CLI_NAME} join --under <parent> link this session under an orchestrator, as a spawned worker sits: ${CLI_NAME} join --under lead
|
|
103
107
|
${CLI_NAME} status [id] the fleet in short; with the id of a message you sent: who read it when
|
|
104
108
|
${CLI_NAME} peek <ep> a short, redacted view of a peekable agent's last turns: ${CLI_NAME} peek rev
|
|
105
109
|
${CLI_NAME} set-status <state> [note] working, waiting, blocked or done: ${CLI_NAME} set-status done "PR open"
|
|
@@ -120,7 +124,7 @@ More: ${CLI_NAME} help --all (the full reference), ${CLI_NAME} <verb> --help (on
|
|
|
120
124
|
var RELAY_HELP = `${CLI_NAME}: messages between the agent sessions on this machine, and a live view of the fleet
|
|
121
125
|
|
|
122
126
|
usage:
|
|
123
|
-
${CLI_NAME} list [--all] [--roots] [--tree] agents on the relay: address, title, presence, status, cwd; offline
|
|
127
|
+
${CLI_NAME} list [--all] [--roots] [--tree] agents on the relay: address, title, presence, wake, status, cwd; offline only with --all;
|
|
124
128
|
sessions nobody spawned that spawned nobody fold into one line, --roots (or --all) lists
|
|
125
129
|
them; --tree nests them by parent; (you) marks your own
|
|
126
130
|
${CLI_NAME} list --contacts the endpoints of other accounts your contacts let you reach: address, harness, presence
|
|
@@ -228,7 +232,7 @@ scopes (selectors are comma-separated, e.g. tree:parent,tree:children,group:team
|
|
|
228
232
|
messages with the relay MCP tools (relay_send, relay_inbox, relay_reply). A
|
|
229
233
|
${CLI_NAME} command from codex's workspace-write sandbox also needs
|
|
230
234
|
-c sandbox_workspace_write.network_access=true (the relay socket counts as network)
|
|
231
|
-
${CLI_NAME} pi [--name N] [--] [pi args...] runs pi with the
|
|
235
|
+
${CLI_NAME} pi [--name N] [--] [pi args...] runs pi with the bundled SCP extension: the relay tools, and each new
|
|
232
236
|
message as a turn (steering a running one, waking an idle session)
|
|
233
237
|
${CLI_NAME} codex [--name N] [--headless] [--] [codex args...]
|
|
234
238
|
runs codex with relay messages waking an idle thread or steering a running turn
|
|
@@ -261,7 +265,7 @@ agent sessions:
|
|
|
261
265
|
${CLI_NAME} hook <harness> [event] what those hooks run: one event from the hook JSON on stdin; prints only the reply
|
|
262
266
|
A session's shim and hooks share one endpoint, keyed by $SCP_SESSION, else the harness process's pid and start time.
|
|
263
267
|
Without --as or $SCP_NAME the handle is <harness>-<worktree>; labels gain machine=<short hostname>.
|
|
264
|
-
A Claude Code subagent's hooks give it a child endpoint, <session handle>-<type>-<first
|
|
268
|
+
A Claude Code subagent's hooks give it a child endpoint, <session handle>-<short type>-<first 4 of its id>, whose messages
|
|
265
269
|
reach it after its tools; its shell is the session's, so it acts as that child with --as.
|
|
266
270
|
Grok runs the Claude Code hooks, labelled grok; Muse takes them as a plugin; Codex runs new hooks after /hooks review.
|
|
267
271
|
${CLI_NAME} claude [--name N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake
|
|
@@ -276,6 +280,17 @@ agent sessions:
|
|
|
276
280
|
and title stay; a handle another endpoint holds, live or offline, is refused;
|
|
277
281
|
a spawned worker is named by its spawner (policy defaults.rename: self lets it);
|
|
278
282
|
--endpoint renames another agent, for the human at a terminal
|
|
283
|
+
${CLI_NAME} join [<handle>] --under <parent> link the session under the agent <parent> (local, one it may message), as if
|
|
284
|
+
<parent> had spawned it: it shows under <parent> in list --tree, the dashboard
|
|
285
|
+
and the graph, and takes on a worker's scope (the policy's child_default: by
|
|
286
|
+
default it reaches only what <parent> reaches). Narrowing, so always allowed
|
|
287
|
+
${CLI_NAME} adopt <child> [--parent <p>] link a local root agent and its subtree under <p>, for the human at a terminal;
|
|
288
|
+
from an agent's shell with a terminal attached <p> defaults to that agent.
|
|
289
|
+
An agent without a terminal is refused: the session joins with --under itself
|
|
290
|
+
${CLI_NAME} unlink [<child>] put a linked agent back at the root, its subtree along and the child scope off;
|
|
291
|
+
widening, so the human at a terminal only (policy defaults.unlink: self lets
|
|
292
|
+
an agent unlink itself). A link that would put a parent under its own child
|
|
293
|
+
(a cycle) is refused; both are audited, as adopt and unlink
|
|
279
294
|
${CLI_NAME} leave [--endpoint EP] take this session off the relay: offline, no messages pushed, mail kept until
|
|
280
295
|
${CLI_NAME} join; a spawned worker stays joined while it runs
|
|
281
296
|
With setup --join manual a session stays off the relay until it runs ${CLI_NAME} join; the default, auto, joins every
|
|
@@ -347,7 +362,9 @@ var IDENTITY = ["as", "harness", "human", "json", "cloud", "machine"];
|
|
|
347
362
|
var VERBS = {
|
|
348
363
|
list: [...IDENTITY, "all", "tree", "roots", "contacts"],
|
|
349
364
|
send: [...IDENTITY, "reply-to", "thread", "private", "attach", "live"],
|
|
350
|
-
join: ["harness", "json"],
|
|
365
|
+
join: ["harness", "json", "under"],
|
|
366
|
+
adopt: [...IDENTITY, "parent"],
|
|
367
|
+
unlink: IDENTITY,
|
|
351
368
|
leave: [...IDENTITY, "endpoint"],
|
|
352
369
|
name: [...IDENTITY, "endpoint"],
|
|
353
370
|
inbox: [...IDENTITY, "wait", "ack", "keep"],
|
|
@@ -449,15 +466,16 @@ function printEndpoints(endpoints, me, host = shortHostname()) {
|
|
|
449
466
|
const row = (e, indent) => [
|
|
450
467
|
`${indent}${addressCell(e, me)}`,
|
|
451
468
|
e.title ?? "",
|
|
452
|
-
e
|
|
469
|
+
presenceText(e),
|
|
470
|
+
wakeText(e),
|
|
453
471
|
e.status ? statusText(e.status) : e.kind === "human" ? "" : "-",
|
|
454
472
|
Object.entries(e.labels).filter(([k]) => k !== "machine").map(([k, v]) => `${k}=${v}`).join(","),
|
|
455
473
|
shortCwd(e.cwd),
|
|
456
474
|
...peek ? [e.peekable ?? ""] : []
|
|
457
475
|
];
|
|
458
|
-
const blank = (first) => [first, "", "", "", "", "", ...peek ? [""] : []];
|
|
476
|
+
const blank = (first) => [first, "", "", "", "", "", "", ...peek ? [""] : []];
|
|
459
477
|
const rows = [
|
|
460
|
-
["ADDRESS", "TITLE", "PRESENCE", "STATUS", "LABELS", "CWD", ...peek ? ["PEEK"] : []],
|
|
478
|
+
["ADDRESS", "TITLE", "PRESENCE", "WAKE", "STATUS", "LABELS", "CWD", ...peek ? ["PEEK"] : []],
|
|
461
479
|
...endpoints.filter((e) => e.kind === "human").map((e) => row(e, ""))
|
|
462
480
|
];
|
|
463
481
|
const machines = /* @__PURE__ */ new Map();
|
|
@@ -520,13 +538,13 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
520
538
|
out(package_default.version);
|
|
521
539
|
return 0;
|
|
522
540
|
}
|
|
523
|
-
if (verb === "hook") return (await import("./hooks-
|
|
524
|
-
if (verb === "setup") return (await import("./setup-
|
|
525
|
-
if (verb === "claude") return (await import("./claude-
|
|
526
|
-
if (verb === "pi") return (await import("./pi-
|
|
527
|
-
if (verb === "codex") return (await import("./codex-
|
|
541
|
+
if (verb === "hook") return (await import("./hooks-P4J6OZCO.js")).relayHookCommand(rest, startDaemon);
|
|
542
|
+
if (verb === "setup") return (await import("./setup-RO7356M4.js")).relaySetupCommand(rest, cwd, out);
|
|
543
|
+
if (verb === "claude") return (await import("./claude-JD7WXMAT.js")).claudeCommand(rest);
|
|
544
|
+
if (verb === "pi") return (await import("./pi-JZ4DXY7V.js")).piCommand(rest);
|
|
545
|
+
if (verb === "codex") return (await import("./codex-4IIM6ZE2.js")).codexCommand(rest);
|
|
528
546
|
if (verb === "spawn") {
|
|
529
|
-
const { splitSpawn, spawnCommand } = await import("./spawn-
|
|
547
|
+
const { splitSpawn, spawnCommand } = await import("./spawn-BYU2X46L.js");
|
|
530
548
|
const { flags } = splitSpawn(rest);
|
|
531
549
|
if (flags.includes("--help") || flags.includes("-h")) {
|
|
532
550
|
out(flags.includes("--all") ? RELAY_HELP : SPAWN_HELP);
|
|
@@ -534,6 +552,7 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
534
552
|
}
|
|
535
553
|
return spawnCommand(rest, cwd);
|
|
536
554
|
}
|
|
555
|
+
if (verb === "mesh") return (await import("./mesh-VNZV3POO.js")).meshCommand(rest);
|
|
537
556
|
const allowed = VERBS[verb];
|
|
538
557
|
if (!allowed) throw new RelayUsage(`unknown command "${verb}"`);
|
|
539
558
|
let parsed;
|
|
@@ -553,11 +572,11 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
553
572
|
return daemon(values.idle, values.policy);
|
|
554
573
|
}
|
|
555
574
|
if (verb === "mcp")
|
|
556
|
-
return (await import("./mcp-
|
|
575
|
+
return (await import("./mcp-TUWMLGE4.js")).relayMcpCommand(values, positionals);
|
|
557
576
|
if (verb === "hooks") {
|
|
558
577
|
if (positionals[0] !== "install" || positionals.length > 1) throw new RelayUsage(`${CLI_NAME} hooks takes install --harness claude|codex|opencode|agy|muse`);
|
|
559
578
|
if (values.write && values.print) throw new RelayUsage("--print and --write: pick one");
|
|
560
|
-
return (await import("./install-
|
|
579
|
+
return (await import("./install-FKGGPT6L.js")).installHooks({
|
|
561
580
|
harness: values.harness,
|
|
562
581
|
scope: values.scope,
|
|
563
582
|
write: values.write === true,
|
|
@@ -568,26 +587,26 @@ async function relayMain(argv, cwd = process.cwd()) {
|
|
|
568
587
|
out
|
|
569
588
|
});
|
|
570
589
|
}
|
|
571
|
-
if (verb === "relay") return (await import("./relay-
|
|
572
|
-
if (verb === "up") return (await import("./up-
|
|
590
|
+
if (verb === "relay") return (await import("./relay-U634RHIJ.js")).relayVerb(positionals, values.json === true);
|
|
591
|
+
if (verb === "up") return (await import("./up-GDD24GPL.js")).up(positionals, values, cwd, relinkBroker);
|
|
573
592
|
if (verb === "login") {
|
|
574
|
-
const code = await (await import("./relay-
|
|
593
|
+
const code = await (await import("./relay-U634RHIJ.js")).loginMain(positionals, values);
|
|
575
594
|
if (code === 0) await relinkRunning();
|
|
576
595
|
return code;
|
|
577
596
|
}
|
|
578
|
-
if (verb === "logout") return (await import("./relay-
|
|
597
|
+
if (verb === "logout") return (await import("./relay-U634RHIJ.js")).logoutMain(positionals, values.json === true);
|
|
579
598
|
if (verb === "cloud") {
|
|
580
|
-
const code = await (await import("./remote-
|
|
599
|
+
const code = await (await import("./remote-NTHRHVEP.js")).cloudMain(positionals, values, cloudStatus);
|
|
581
600
|
if (code === 0 && positionals[0] === "enable") await relinkRunning();
|
|
582
601
|
return code;
|
|
583
602
|
}
|
|
584
|
-
if (verb === "join") return (await import("./join-
|
|
603
|
+
if (verb === "join") return (await import("./join-RGFHOSLE.js")).joinCommand(values, positionals, cwd, out);
|
|
585
604
|
if (verb === "holds") holdsArgs(values, positionals);
|
|
586
605
|
const bodyFromStdin = (verb === "send" || verb === "ask" || verb === "reply") && (positionals[1] === void 0 || positionals[1] === "-");
|
|
587
606
|
const opened = await open(verb, values, cwd, bodyFromStdin);
|
|
588
607
|
const { client } = opened;
|
|
589
608
|
try {
|
|
590
|
-
if (verb === "machines") return await (await import("./remote-
|
|
609
|
+
if (verb === "machines") return await (await import("./remote-NTHRHVEP.js")).machinesVerb(client, positionals, values.json === true);
|
|
591
610
|
return await runVerb(opened, verb, values, positionals, cwd);
|
|
592
611
|
} finally {
|
|
593
612
|
client.close();
|
|
@@ -663,7 +682,7 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
663
682
|
out(`${me.address} (${me.kind}${me.harness ? `, ${me.harness}` : ""})`);
|
|
664
683
|
const how = bound === "as" ? "--as" : bound === "lineage" ? "this shell's harness process" : bound === "human" ? "human inbox" : "-";
|
|
665
684
|
const fields = [
|
|
666
|
-
["presence", me
|
|
685
|
+
["presence", presenceText(me)],
|
|
667
686
|
["title", me.title ?? "-"],
|
|
668
687
|
...me.kind === "human" ? [] : [
|
|
669
688
|
["status", me.status?.state ?? "-"],
|
|
@@ -764,9 +783,9 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
764
783
|
return 0;
|
|
765
784
|
}
|
|
766
785
|
case "peek":
|
|
767
|
-
return (await import("./peek-
|
|
786
|
+
return (await import("./peek-4D4VGKOW.js")).peekCommand(localClient(client, broker, verb), values, positionals);
|
|
768
787
|
case "set-peek":
|
|
769
|
-
return (await import("./peek-
|
|
788
|
+
return (await import("./peek-4D4VGKOW.js")).setPeekCommand(localClient(client, broker, verb), values, positionals);
|
|
770
789
|
case "why": {
|
|
771
790
|
const [from, to] = positionals;
|
|
772
791
|
if (!from || !to || positionals.length > 2) throw new RelayUsage(`${CLI_NAME} why takes <from> <to>`);
|
|
@@ -983,6 +1002,26 @@ async function runVerb({ client, me, broker, where, bound, harnessPid }, verb, v
|
|
|
983
1002
|
else out(before && before !== view.address ? `${before} is now ${view.address}` : `${view.address}`);
|
|
984
1003
|
return 0;
|
|
985
1004
|
}
|
|
1005
|
+
case "adopt": {
|
|
1006
|
+
if (positionals.length !== 1) throw new RelayUsage(`${CLI_NAME} adopt takes the child to link: ${CLI_NAME} adopt <child> [--parent <p>]`);
|
|
1007
|
+
const parent = values.parent;
|
|
1008
|
+
if (parent === void 0 && me?.kind !== "agent")
|
|
1009
|
+
throw new RelayUsage(`${CLI_NAME} adopt: name the parent with --parent <p> (run from an agent's shell, it defaults to that agent)`);
|
|
1010
|
+
const result = await client.request("adopt", {
|
|
1011
|
+
endpoint: positionals[0],
|
|
1012
|
+
...parent !== void 0 ? { parent } : {}
|
|
1013
|
+
});
|
|
1014
|
+
if (asJson) json(result);
|
|
1015
|
+
else out(`${result.endpoint.address} now sits under ${result.parent}`);
|
|
1016
|
+
return 0;
|
|
1017
|
+
}
|
|
1018
|
+
case "unlink": {
|
|
1019
|
+
if (positionals.length > 1) throw new RelayUsage(`${CLI_NAME} unlink takes one child, or none for this session`);
|
|
1020
|
+
const result = await client.request("unlink", positionals.length ? { endpoint: positionals[0] } : {});
|
|
1021
|
+
if (asJson) json(result);
|
|
1022
|
+
else out(`${result.endpoint.address} is a root again (was under ${result.parent})`);
|
|
1023
|
+
return 0;
|
|
1024
|
+
}
|
|
986
1025
|
case "leave": {
|
|
987
1026
|
if (positionals.length) throw new RelayUsage(`${CLI_NAME} leave takes no arguments (--endpoint EP for the human at a terminal)`);
|
|
988
1027
|
const endpoint = values.endpoint;
|
|
@@ -1187,7 +1226,7 @@ function printTree(endpoints, me) {
|
|
|
1187
1226
|
if (e.parent && byAddress.has(e.parent) && e.parent !== e.address) children.set(e.parent, [...children.get(e.parent) ?? [], e]);
|
|
1188
1227
|
else roots.push(e);
|
|
1189
1228
|
}
|
|
1190
|
-
const rows = [["ADDRESS", "SCOPE", "TITLE", "PRESENCE", "STATUS", "LABELS", "CWD"]];
|
|
1229
|
+
const rows = [["ADDRESS", "SCOPE", "TITLE", "PRESENCE", "WAKE", "STATUS", "LABELS", "CWD"]];
|
|
1191
1230
|
const seen = /* @__PURE__ */ new Set();
|
|
1192
1231
|
const walk = (e, depth) => {
|
|
1193
1232
|
if (seen.has(e.address)) return;
|
|
@@ -1197,7 +1236,8 @@ function printTree(endpoints, me) {
|
|
|
1197
1236
|
`${" ".repeat(depth)}${addressCell(e, me)}`,
|
|
1198
1237
|
e.kind === "human" ? "" : e.scope ?? "open",
|
|
1199
1238
|
e.title ?? "",
|
|
1200
|
-
e
|
|
1239
|
+
presenceText(e),
|
|
1240
|
+
wakeText(e),
|
|
1201
1241
|
e.status ? statusText(e.status) : e.kind === "human" ? "" : "-",
|
|
1202
1242
|
labels,
|
|
1203
1243
|
shortCwd(e.cwd)
|
|
@@ -1211,7 +1251,7 @@ var STATES = ["working", "waiting", "blocked", "done"];
|
|
|
1211
1251
|
function printSummary(snapshot, me, all) {
|
|
1212
1252
|
if (me)
|
|
1213
1253
|
out(
|
|
1214
|
-
`you: ${me.address} (${me.kind}${me.harness ? `, ${me.harness}` : ""}) ${me
|
|
1254
|
+
`you: ${me.address} (${me.kind}${me.harness ? `, ${me.harness}` : ""}) ${presenceText(me)}${me.title ? ` ${quoted(me.title)}` : ""}${me.kind === "human" ? "" : `, status ${me.status ? statusText(me.status) : "-"}`}`
|
|
1215
1255
|
);
|
|
1216
1256
|
const agents = snapshot.endpoints.filter((e) => e.kind !== "human");
|
|
1217
1257
|
const count = (presence) => agents.filter((e) => e.presence === presence).length;
|
|
@@ -1343,11 +1383,11 @@ async function daemon(idle, policyFile) {
|
|
|
1343
1383
|
});
|
|
1344
1384
|
let broker;
|
|
1345
1385
|
try {
|
|
1346
|
-
const { daemonBridge } = await import("./remote-
|
|
1386
|
+
const { daemonBridge } = await import("./remote-NTHRHVEP.js");
|
|
1347
1387
|
const bridge = await daemonBridge();
|
|
1348
1388
|
broker = await startBroker({
|
|
1349
1389
|
dir: relayDir(),
|
|
1350
|
-
...bridge ? { bridge } :
|
|
1390
|
+
...bridge ? { bridge } : await (await import("./mesh-VNZV3POO.js")).meshBrokerOptions(),
|
|
1351
1391
|
bridgeConfig: () => daemonBridge(),
|
|
1352
1392
|
...idleMs !== void 0 ? { idleMs } : {},
|
|
1353
1393
|
...policyFile ? { policyPath: resolve(policyFile) } : {}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
spawnDetached
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IXCHAWOL.js";
|
|
4
4
|
import "./chunk-KN7WEIE4.js";
|
|
5
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-W3VNCRBX.js";
|
|
6
6
|
import {
|
|
7
7
|
mcpInstall
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-
|
|
8
|
+
} from "./chunk-ZMVFN6MU.js";
|
|
9
|
+
import "./chunk-X4ZLEM47.js";
|
|
10
10
|
import "./chunk-YYSLYVNZ.js";
|
|
11
11
|
import {
|
|
12
12
|
CHANNELS_ENV,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
psParent,
|
|
17
17
|
relayIdentity,
|
|
18
18
|
startDaemon
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-E7JG5PJB.js";
|
|
20
20
|
import "./chunk-MNTIEWKF.js";
|
|
21
21
|
|
|
22
22
|
// src/mcp.ts
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RelayUsage
|
|
3
|
+
} from "./chunk-E7JG5PJB.js";
|
|
4
|
+
import "./chunk-MNTIEWKF.js";
|
|
5
|
+
|
|
6
|
+
// src/mesh.ts
|
|
7
|
+
import { existsSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { relayDir } from "@scp-dev/broker";
|
|
10
|
+
var MESH_MODULE = "@scp-dev/mesh";
|
|
11
|
+
async function load() {
|
|
12
|
+
try {
|
|
13
|
+
return await import(MESH_MODULE);
|
|
14
|
+
} catch {
|
|
15
|
+
throw new RelayUsage("swarm mesh is experimental and runs only from a source checkout of the swarm CLI");
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
async function meshCommand(argv) {
|
|
19
|
+
return (await load()).meshCommand(argv);
|
|
20
|
+
}
|
|
21
|
+
async function meshBrokerOptions() {
|
|
22
|
+
if (!existsSync(join(relayDir(), "mesh", "mesh.json"))) return {};
|
|
23
|
+
return (await load()).meshBrokerOptions();
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
meshBrokerOptions,
|
|
27
|
+
meshCommand
|
|
28
|
+
};
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RelayUsage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-E7JG5PJB.js";
|
|
4
4
|
import "./chunk-MNTIEWKF.js";
|
|
5
5
|
|
|
6
6
|
// src/pi.ts
|
|
7
7
|
import { spawn } from "child_process";
|
|
8
|
+
import { existsSync } from "fs";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
9
10
|
import { CLI_NAME, validHandle } from "@scp-dev/core";
|
|
10
11
|
var PI_HELP = `${CLI_NAME} pi [--name NAME | --as NAME] [--] [pi args...]
|
|
11
|
-
runs pi with the
|
|
12
|
+
runs pi with the bundled SCP extension: the relay tools, and each new message as a turn, waking the session when idle.
|
|
12
13
|
$SCP_PI_BIN picks the pi binary (default pi on PATH), $SCP_PI_EXTENSION the extension file.`;
|
|
13
14
|
function piLaunch(args, env, extension) {
|
|
14
15
|
let name;
|
|
@@ -32,13 +33,13 @@ function piLaunch(args, env, extension) {
|
|
|
32
33
|
env: { ...env, SCP_HARNESS: "pi", ...name !== void 0 ? { SCP_NAME: name } : {} }
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
|
-
function extensionPath(env) {
|
|
36
|
+
function extensionPath(env, base = import.meta.url) {
|
|
36
37
|
if (env.SCP_PI_EXTENSION) return env.SCP_PI_EXTENSION;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
throw new RelayUsage(`${CLI_NAME} pi needs the @scp-dev/pi extension: install it next to ${CLI_NAME}, or set $SCP_PI_EXTENSION to its file`);
|
|
38
|
+
for (const candidate of ["./pi-extension.js", "../../pi/src/index.ts"]) {
|
|
39
|
+
const file = fileURLToPath(new URL(candidate, base));
|
|
40
|
+
if (existsSync(file)) return file;
|
|
41
41
|
}
|
|
42
|
+
throw new RelayUsage(`${CLI_NAME} pi found no pi extension next to ${CLI_NAME}: reinstall it, or set $SCP_PI_EXTENSION to the extension file`);
|
|
42
43
|
}
|
|
43
44
|
async function piCommand(args) {
|
|
44
45
|
const own = args.includes("--") ? args.slice(0, args.indexOf("--")) : args;
|
|
@@ -66,6 +67,7 @@ async function piCommand(args) {
|
|
|
66
67
|
}
|
|
67
68
|
export {
|
|
68
69
|
PI_HELP,
|
|
70
|
+
extensionPath,
|
|
69
71
|
piCommand,
|
|
70
72
|
piLaunch
|
|
71
73
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// ../pi/src/index.ts
|
|
2
|
+
import { spawn } from "child_process";
|
|
3
|
+
import { createInterface } from "readline";
|
|
4
|
+
var PROTOCOL = "2025-06-18";
|
|
5
|
+
var CHANNEL = "notifications/claude/channel";
|
|
6
|
+
function swarmWords(env) {
|
|
7
|
+
const raw = env.SCP_PI_SWARM;
|
|
8
|
+
if (!raw) return ["swarm"];
|
|
9
|
+
const words = JSON.parse(raw);
|
|
10
|
+
if (!Array.isArray(words) || !words.length || !words.every((word) => typeof word === "string"))
|
|
11
|
+
throw new Error("SCP_PI_SWARM must be a JSON array of strings");
|
|
12
|
+
return words;
|
|
13
|
+
}
|
|
14
|
+
function shimArgv(env) {
|
|
15
|
+
return [...swarmWords(env), "mcp", "--harness", "pi", "--channels", ...env.SCP_NAME ? ["--as", env.SCP_NAME] : []];
|
|
16
|
+
}
|
|
17
|
+
var McpLink = class {
|
|
18
|
+
child;
|
|
19
|
+
pending = /* @__PURE__ */ new Map();
|
|
20
|
+
next = 1;
|
|
21
|
+
closed = false;
|
|
22
|
+
constructor(argv, env, onPush, onExit) {
|
|
23
|
+
this.child = spawn(argv[0], argv.slice(1), { env, stdio: ["pipe", "pipe", "ignore"] });
|
|
24
|
+
this.child.stdin.on("error", () => {
|
|
25
|
+
});
|
|
26
|
+
createInterface({ input: this.child.stdout }).on("line", (line) => {
|
|
27
|
+
let message;
|
|
28
|
+
try {
|
|
29
|
+
message = JSON.parse(line);
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (typeof message.id === "number" && this.pending.has(message.id)) {
|
|
34
|
+
const waiter = this.pending.get(message.id);
|
|
35
|
+
this.pending.delete(message.id);
|
|
36
|
+
const error = message.error;
|
|
37
|
+
if (error) waiter.reject(new Error(error.message ?? "mcp error"));
|
|
38
|
+
else waiter.resolve(message.result ?? {});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (message.method === CHANNEL) {
|
|
42
|
+
const params = message.params ?? {};
|
|
43
|
+
if (typeof params.content === "string") onPush(params.content, params.meta ?? {});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
this.child.once("error", () => this.fail(null, onExit));
|
|
47
|
+
this.child.once("exit", (code) => this.fail(code, onExit));
|
|
48
|
+
}
|
|
49
|
+
fail(code, onExit) {
|
|
50
|
+
if (this.closed) return;
|
|
51
|
+
this.closed = true;
|
|
52
|
+
for (const waiter of this.pending.values()) waiter.reject(new Error("the scp shim exited"));
|
|
53
|
+
this.pending.clear();
|
|
54
|
+
onExit(code);
|
|
55
|
+
}
|
|
56
|
+
request(method, params = {}) {
|
|
57
|
+
if (this.closed) return Promise.reject(new Error("the scp shim exited"));
|
|
58
|
+
const id = this.next++;
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
this.pending.set(id, { resolve, reject });
|
|
61
|
+
this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
62
|
+
`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
notify(method, params = {}) {
|
|
66
|
+
if (!this.closed) this.child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, params })}
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
close() {
|
|
70
|
+
if (this.closed) return;
|
|
71
|
+
this.closed = true;
|
|
72
|
+
this.child.stdin.end();
|
|
73
|
+
this.child.kill();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
function toolResult(result) {
|
|
77
|
+
const content = (result.content ?? []).flatMap(
|
|
78
|
+
(part) => part.type === "text" && typeof part.text === "string" ? [{ type: "text", text: part.text }] : []
|
|
79
|
+
);
|
|
80
|
+
if (result.isError) throw new Error(content.map((part) => part.text).join("\n") || "the relay tool failed");
|
|
81
|
+
return { content, details: {} };
|
|
82
|
+
}
|
|
83
|
+
function scp(pi) {
|
|
84
|
+
let link;
|
|
85
|
+
const warn = (ctx, text) => {
|
|
86
|
+
if (ctx.hasUI) ctx.ui.notify(text, "warning");
|
|
87
|
+
};
|
|
88
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
89
|
+
if (process.env.SCP_PI === "0") return;
|
|
90
|
+
link?.close();
|
|
91
|
+
const current = new McpLink(
|
|
92
|
+
shimArgv(process.env),
|
|
93
|
+
process.env,
|
|
94
|
+
(content, meta) => {
|
|
95
|
+
pi.sendMessage({ customType: "scp", content, display: true, details: meta }, { triggerTurn: true, deliverAs: "steer" });
|
|
96
|
+
},
|
|
97
|
+
(code) => {
|
|
98
|
+
if (link === current) warn(ctx, `scp: the relay shim exited (${code ?? "error"}); relay tools are unavailable`);
|
|
99
|
+
}
|
|
100
|
+
);
|
|
101
|
+
link = current;
|
|
102
|
+
try {
|
|
103
|
+
await current.request("initialize", {
|
|
104
|
+
protocolVersion: PROTOCOL,
|
|
105
|
+
capabilities: { experimental: { "claude/channel": {} } },
|
|
106
|
+
clientInfo: { name: "pi", version: "1" }
|
|
107
|
+
});
|
|
108
|
+
const { tools } = await current.request("tools/list");
|
|
109
|
+
for (const tool of tools ?? []) {
|
|
110
|
+
pi.registerTool({
|
|
111
|
+
name: tool.name,
|
|
112
|
+
label: tool.name,
|
|
113
|
+
description: tool.description ?? tool.name,
|
|
114
|
+
parameters: tool.inputSchema ?? { type: "object", properties: {} },
|
|
115
|
+
async execute(_id, params) {
|
|
116
|
+
if (link !== current) throw new Error("this scp session ended");
|
|
117
|
+
return toolResult(await current.request("tools/call", { name: tool.name, arguments: params ?? {} }));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
current.notify("notifications/initialized");
|
|
122
|
+
} catch (error) {
|
|
123
|
+
warn(ctx, `scp: could not start the relay shim: ${error instanceof Error ? error.message : String(error)}`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
pi.on("session_shutdown", async () => {
|
|
127
|
+
link?.close();
|
|
128
|
+
link = void 0;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export {
|
|
132
|
+
McpLink,
|
|
133
|
+
scp as default,
|
|
134
|
+
shimArgv,
|
|
135
|
+
swarmWords,
|
|
136
|
+
toolResult
|
|
137
|
+
};
|
|
@@ -15,11 +15,11 @@ import {
|
|
|
15
15
|
verifyMount,
|
|
16
16
|
verifyMusePlugin,
|
|
17
17
|
whichOnPath
|
|
18
|
-
} from "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
18
|
+
} from "./chunk-W3VNCRBX.js";
|
|
19
|
+
import "./chunk-ZMVFN6MU.js";
|
|
20
|
+
import "./chunk-X4ZLEM47.js";
|
|
21
21
|
import "./chunk-YYSLYVNZ.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-E7JG5PJB.js";
|
|
23
23
|
import "./chunk-MNTIEWKF.js";
|
|
24
24
|
export {
|
|
25
25
|
SETUP_HARNESSES,
|
|
@@ -29,15 +29,15 @@ import {
|
|
|
29
29
|
steerArgv,
|
|
30
30
|
transcriptReplies,
|
|
31
31
|
workerName
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-IXCHAWOL.js";
|
|
33
33
|
import {
|
|
34
34
|
SPAWN_HELP
|
|
35
35
|
} from "./chunk-KN7WEIE4.js";
|
|
36
|
-
import "./chunk-
|
|
37
|
-
import "./chunk-
|
|
38
|
-
import "./chunk-
|
|
36
|
+
import "./chunk-W3VNCRBX.js";
|
|
37
|
+
import "./chunk-ZMVFN6MU.js";
|
|
38
|
+
import "./chunk-X4ZLEM47.js";
|
|
39
39
|
import "./chunk-YYSLYVNZ.js";
|
|
40
|
-
import "./chunk-
|
|
40
|
+
import "./chunk-E7JG5PJB.js";
|
|
41
41
|
import "./chunk-MNTIEWKF.js";
|
|
42
42
|
export {
|
|
43
43
|
MESSAGING,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
RelayUsage,
|
|
3
3
|
activeRelay,
|
|
4
4
|
relayCredential
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-E7JG5PJB.js";
|
|
6
6
|
import "./chunk-MNTIEWKF.js";
|
|
7
7
|
|
|
8
8
|
// src/up.ts
|
|
@@ -14,16 +14,16 @@ async function up(positionals, values, cwd, brokerStatus) {
|
|
|
14
14
|
const { url } = activeRelay(process.env);
|
|
15
15
|
const headless = values.token !== void 0 || Boolean(process.env.SCP_TOKEN?.trim());
|
|
16
16
|
if (headless || !relayCredential(process.env, url).token) {
|
|
17
|
-
const code = await (await import("./login-
|
|
17
|
+
const code = await (await import("./login-IPS75GLQ.js")).login({
|
|
18
18
|
...values.token !== void 0 ? { token: values.token } : {},
|
|
19
19
|
...values.label !== void 0 ? { label: values.label } : {},
|
|
20
20
|
...values["no-browser"] ? { "no-browser": true } : {}
|
|
21
21
|
});
|
|
22
22
|
if (code !== 0) return code;
|
|
23
23
|
} else out(`already logged in to ${url}`);
|
|
24
|
-
const enabled = await (await import("./remote-
|
|
24
|
+
const enabled = await (await import("./remote-NTHRHVEP.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
|
|
25
25
|
if (enabled !== 0) return enabled;
|
|
26
|
-
const setup = (await import("./setup-
|
|
26
|
+
const setup = (await import("./setup-RO7356M4.js")).relaySetupCommand(["--write"], cwd, out);
|
|
27
27
|
if (setup !== 0) return setup;
|
|
28
28
|
const broker = await brokerStatus();
|
|
29
29
|
if (!broker.enabled) out(`the broker is still local-only; run \`${CLI_NAME} daemon restart\` to link it to ${url}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scp-dev/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "The swarm command line tool.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://scp.dev",
|
|
@@ -24,14 +24,15 @@
|
|
|
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.
|
|
30
|
-
"@scp-dev/mcp": "0.1.
|
|
27
|
+
"@scp-dev/core": "0.1.4",
|
|
28
|
+
"@scp-dev/broker": "0.1.5",
|
|
29
|
+
"@scp-dev/hub": "0.3.1",
|
|
30
|
+
"@scp-dev/mcp": "0.1.5",
|
|
31
31
|
"smol-toml": "^1.9.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@openai/codex": "0.159.0",
|
|
35
|
+
"@scp-dev/mesh": "0.0.2",
|
|
35
36
|
"opencode-ai": "1.18.34",
|
|
36
37
|
"tsup": "^8.5.1"
|
|
37
38
|
},
|