@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
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import {
|
|
2
|
+
agyNote,
|
|
3
|
+
messagingAllow
|
|
4
|
+
} from "./chunk-RZUQ422C.js";
|
|
5
|
+
import "./chunk-KN7WEIE4.js";
|
|
6
|
+
import {
|
|
7
|
+
whichOnPath
|
|
8
|
+
} from "./chunk-SNW53KT3.js";
|
|
9
|
+
import {
|
|
10
|
+
hooksTarget
|
|
11
|
+
} from "./chunk-LRQP25RI.js";
|
|
12
|
+
import {
|
|
13
|
+
AGY_CONVERSATION,
|
|
14
|
+
AGY_HOOK_SET
|
|
15
|
+
} from "./chunk-277BAY7J.js";
|
|
16
|
+
import "./chunk-YYSLYVNZ.js";
|
|
17
|
+
import {
|
|
18
|
+
RelayUsage,
|
|
19
|
+
relayIdentity,
|
|
20
|
+
startDaemon
|
|
21
|
+
} from "./chunk-3SOIVANO.js";
|
|
22
|
+
import "./chunk-MNTIEWKF.js";
|
|
23
|
+
|
|
24
|
+
// src/resume.ts
|
|
25
|
+
import { spawn } from "child_process";
|
|
26
|
+
import { randomBytes, randomUUID } from "crypto";
|
|
27
|
+
import { mkdtempSync, readFileSync, rmSync } from "fs";
|
|
28
|
+
import { tmpdir } from "os";
|
|
29
|
+
import { join } from "path";
|
|
30
|
+
import { PassThrough } from "stream";
|
|
31
|
+
import { connectBroker, relayDir } from "@scp-dev/broker";
|
|
32
|
+
import { CLI_NAME, validHandle } from "@scp-dev/core";
|
|
33
|
+
import { MCP_PROTOCOLS, runRelayMcp } from "@scp-dev/mcp";
|
|
34
|
+
var SPECS = {
|
|
35
|
+
agy: {
|
|
36
|
+
bin: "SCP_AGY_BIN",
|
|
37
|
+
refused: ["-i", "--prompt-interactive", "-c", "--continue", "--input-format", "--remote-control"],
|
|
38
|
+
prompt: ["-p", "--print", "--prompt"],
|
|
39
|
+
ids: ["--conversation"]
|
|
40
|
+
},
|
|
41
|
+
grok: {
|
|
42
|
+
bin: "SCP_GROK_BIN",
|
|
43
|
+
refused: ["-c", "--continue", "--fork-session"],
|
|
44
|
+
prompt: ["-p", "--single", "--prompt-file", "--prompt-json"],
|
|
45
|
+
ids: ["-s", "--session-id", "-r", "--resume"]
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var AGY_HELP = `${CLI_NAME} agy [--name N | --as N] [--] [agy args...]
|
|
49
|
+
runs agy headless, wake on: a -p prompt is its first turn, and each new message resumes the same conversation as a
|
|
50
|
+
turn (agy --conversation <id> -p <message>); without a prompt the first message starts it. Refuses -i, -c,
|
|
51
|
+
--input-format and --remote-control: nothing local starts a turn in an interactive agy, so run plain agy for that
|
|
52
|
+
(its hooks still deliver before each model call). Refuses to start without the ${CLI_NAME} hooks, which report the
|
|
53
|
+
conversation; ${CLI_NAME} setup --write installs them.
|
|
54
|
+
$SCP_AGY_BIN picks the agy binary (default agy on PATH).`;
|
|
55
|
+
var GROK_HELP = `${CLI_NAME} grok [--name N | --as N] [--] [grok args...]
|
|
56
|
+
runs grok headless, wake on: a -p prompt is its first turn, and each new message resumes the same session as a turn
|
|
57
|
+
(grok --resume <id> -p <message>); without a prompt the first message starts it. The session id is yours (-s, -r)
|
|
58
|
+
or a new one. Refuses -c and --fork-session: nothing local starts a turn in an interactive grok, so run plain grok
|
|
59
|
+
for that. Adds the --allow rules for the relay tools unless you pass your own.
|
|
60
|
+
$SCP_GROK_BIN picks the grok binary (default grok on PATH).`;
|
|
61
|
+
function insert(args, extra) {
|
|
62
|
+
const end = args.indexOf("--");
|
|
63
|
+
return end < 0 ? [...args, ...extra] : [...args.slice(0, end), ...extra, ...args.slice(end)];
|
|
64
|
+
}
|
|
65
|
+
function resumeLaunch(harness, args, env, uuid = randomUUID) {
|
|
66
|
+
let name;
|
|
67
|
+
let i = 0;
|
|
68
|
+
for (; i < args.length; i++) {
|
|
69
|
+
const arg = args[i];
|
|
70
|
+
if (arg === "--name" || arg === "--as") {
|
|
71
|
+
name = args[++i];
|
|
72
|
+
if (name === void 0) throw new RelayUsage(`${CLI_NAME} ${harness} ${arg} takes a name`);
|
|
73
|
+
} else if (arg.startsWith("--name=") || arg.startsWith("--as=")) name = arg.slice(arg.indexOf("=") + 1);
|
|
74
|
+
else break;
|
|
75
|
+
}
|
|
76
|
+
if (args[i] === "--") i++;
|
|
77
|
+
const rest = args.slice(i);
|
|
78
|
+
name = name?.replace(/^me\//, "");
|
|
79
|
+
if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`${JSON.stringify(name)} is not a valid agent handle`);
|
|
80
|
+
const spec = SPECS[harness];
|
|
81
|
+
const refusal = (flag) => new RelayUsage(
|
|
82
|
+
`${CLI_NAME} ${harness}: ${flag} is not supported: nothing local starts a turn in an interactive ${harness} session; run plain ${harness} for that`
|
|
83
|
+
);
|
|
84
|
+
if (harness === "agy" && rest[0] === "remote-control") throw refusal("remote-control");
|
|
85
|
+
const base = [];
|
|
86
|
+
const ids = /* @__PURE__ */ new Map();
|
|
87
|
+
let prompt = false;
|
|
88
|
+
for (let j = 0; j < rest.length; j++) {
|
|
89
|
+
const arg = rest[j];
|
|
90
|
+
if (arg === "--") {
|
|
91
|
+
base.push(...rest.slice(j));
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
const refused = spec.refused.find((flag) => arg === flag || arg.startsWith(`${flag}=`));
|
|
95
|
+
if (refused) throw refusal(refused);
|
|
96
|
+
const valued = [...spec.prompt, ...spec.ids].find((flag) => arg === flag || arg.startsWith(`${flag}=`));
|
|
97
|
+
if (!valued) {
|
|
98
|
+
base.push(arg);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const value = arg === valued ? rest[++j] : arg.slice(valued.length + 1);
|
|
102
|
+
if (value === void 0) throw new RelayUsage(`${CLI_NAME} ${harness} ${valued} takes a value`);
|
|
103
|
+
if (spec.prompt.includes(valued)) prompt = true;
|
|
104
|
+
else ids.set(valued, value);
|
|
105
|
+
}
|
|
106
|
+
const { SCP_SESSION: _session, SCP_PARENT: _parent, SCP_HARNESS_PID: _pid, ...clean } = env;
|
|
107
|
+
const launchEnv = { ...clean, SCP_HARNESS: harness, SCP_SESSION: randomBytes(16).toString("hex"), ...name !== void 0 ? { SCP_NAME: name } : {} };
|
|
108
|
+
const named = name !== void 0 ? { name } : {};
|
|
109
|
+
if (harness === "agy") {
|
|
110
|
+
const id2 = ids.get("--conversation");
|
|
111
|
+
return {
|
|
112
|
+
harness,
|
|
113
|
+
...named,
|
|
114
|
+
...prompt ? { first: rest } : {},
|
|
115
|
+
base,
|
|
116
|
+
opening: id2 === void 0 ? [] : ["--conversation", id2],
|
|
117
|
+
...id2 !== void 0 ? { id: id2 } : {},
|
|
118
|
+
extra: [],
|
|
119
|
+
env: launchEnv
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const resumed = ids.get("-r") ?? ids.get("--resume");
|
|
123
|
+
const given = ids.get("-s") ?? ids.get("--session-id");
|
|
124
|
+
const id = resumed ?? given ?? uuid();
|
|
125
|
+
const extra = messagingAllow("grok", rest);
|
|
126
|
+
return {
|
|
127
|
+
harness,
|
|
128
|
+
...named,
|
|
129
|
+
...prompt ? { first: insert(rest, [...resumed ?? given ? [] : ["-s", id], ...extra]) } : {},
|
|
130
|
+
base,
|
|
131
|
+
opening: resumed !== void 0 ? ["--resume", id] : ["-s", id],
|
|
132
|
+
id,
|
|
133
|
+
extra,
|
|
134
|
+
env: launchEnv
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function turnArgv(launch, text, id) {
|
|
138
|
+
const conversation = id === void 0 ? launch.opening : launch.harness === "agy" ? ["--conversation", id] : ["--resume", id];
|
|
139
|
+
return insert(launch.base, [...conversation, "-p", text, ...launch.extra]);
|
|
140
|
+
}
|
|
141
|
+
function agyHooksProblem(env, cwd) {
|
|
142
|
+
for (const scope of ["project", "user"]) {
|
|
143
|
+
try {
|
|
144
|
+
const set = JSON.parse(readFileSync(hooksTarget("agy", scope, env, cwd), "utf8"))[AGY_HOOK_SET];
|
|
145
|
+
if (set && set.enabled !== false && Array.isArray(set.SessionStart) && set.SessionStart.length) return void 0;
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return `${CLI_NAME} agy: agy has no ${CLI_NAME} hooks, so it cannot report the conversation to resume; run ${CLI_NAME} setup --write`;
|
|
150
|
+
}
|
|
151
|
+
function readConversation(file) {
|
|
152
|
+
if (!file) return void 0;
|
|
153
|
+
try {
|
|
154
|
+
const id = readFileSync(file, "utf8").trim();
|
|
155
|
+
return AGY_CONVERSATION.test(id) ? id : void 0;
|
|
156
|
+
} catch {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function resumeCommand(harness, args, env = process.env, cwd = process.cwd(), err = (line) => void process.stderr.write(`${line}
|
|
161
|
+
`)) {
|
|
162
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
163
|
+
process.stdout.write(`${harness === "agy" ? AGY_HELP : GROK_HELP}
|
|
164
|
+
`);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
const launch = resumeLaunch(harness, args, env);
|
|
168
|
+
const spec = SPECS[harness];
|
|
169
|
+
const bin = env[spec.bin] || whichOnPath(harness, env);
|
|
170
|
+
if (!bin) throw new RelayUsage(`${harness} is not on PATH: install it, or set $${spec.bin} to its binary`);
|
|
171
|
+
if (harness === "agy") {
|
|
172
|
+
const problem = agyHooksProblem(env, cwd);
|
|
173
|
+
if (problem) throw new RelayUsage(problem);
|
|
174
|
+
const note = agyNote(["agy", "-p", "", ...launch.base], env, "agy");
|
|
175
|
+
if (note) err(note);
|
|
176
|
+
}
|
|
177
|
+
const runEnv = { ...launch.env, SCP_HARNESS_PID: String(process.pid) };
|
|
178
|
+
const claim = relayIdentity({ harness, fallbackHarness: harness, cwd, env: runEnv, harnessPid: () => process.pid });
|
|
179
|
+
if (!validHandle(claim.handle)) throw new RelayUsage(`${JSON.stringify(claim.handle)} is not a valid agent handle`);
|
|
180
|
+
const dir = harness === "agy" ? mkdtempSync(join(tmpdir(), "scp-agy-")) : void 0;
|
|
181
|
+
const file = dir === void 0 ? void 0 : join(dir, "conversation");
|
|
182
|
+
if (file) runEnv.SCP_AGY_CONVERSATION_FILE = file;
|
|
183
|
+
const broker = relayDir(runEnv);
|
|
184
|
+
const input = new PassThrough();
|
|
185
|
+
const queue = [];
|
|
186
|
+
let id = launch.id;
|
|
187
|
+
let opened = false;
|
|
188
|
+
let ready = false;
|
|
189
|
+
let stopping = false;
|
|
190
|
+
let running;
|
|
191
|
+
let finish;
|
|
192
|
+
const done = new Promise((resolve) => {
|
|
193
|
+
finish = resolve;
|
|
194
|
+
});
|
|
195
|
+
let joined;
|
|
196
|
+
const whoami = new Promise((resolve) => {
|
|
197
|
+
joined = resolve;
|
|
198
|
+
});
|
|
199
|
+
const turn = (argv) => {
|
|
200
|
+
const child = spawn(bin, argv, { env: runEnv, stdio: ["ignore", "inherit", "inherit"] });
|
|
201
|
+
running = child;
|
|
202
|
+
let settled = false;
|
|
203
|
+
child.once("error", (error) => {
|
|
204
|
+
if (settled) return;
|
|
205
|
+
settled = true;
|
|
206
|
+
running = void 0;
|
|
207
|
+
err(`${CLI_NAME} ${harness}: could not start ${bin}: ${error.message}`);
|
|
208
|
+
finish(1);
|
|
209
|
+
});
|
|
210
|
+
child.once("exit", (code, signal) => {
|
|
211
|
+
if (settled) return;
|
|
212
|
+
settled = true;
|
|
213
|
+
running = void 0;
|
|
214
|
+
if (stopping) return finish(0);
|
|
215
|
+
if (code !== 0) {
|
|
216
|
+
err(`${CLI_NAME} ${harness}: turn failed (exit ${code ?? signal}); undelivered messages stay unread in relay_inbox`);
|
|
217
|
+
return finish(code ?? 1);
|
|
218
|
+
}
|
|
219
|
+
opened = true;
|
|
220
|
+
if (harness === "agy") id = readConversation(file) ?? id;
|
|
221
|
+
if (id === void 0) {
|
|
222
|
+
err(`${CLI_NAME} agy: agy's ${CLI_NAME} hooks did not report the conversation, so it cannot be resumed; run ${CLI_NAME} setup --write`);
|
|
223
|
+
return finish(1);
|
|
224
|
+
}
|
|
225
|
+
next();
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
const next = () => {
|
|
229
|
+
if (!ready || running || stopping || !queue.length) return;
|
|
230
|
+
turn(turnArgv(launch, queue.splice(0).join("\n\n"), opened ? id : void 0));
|
|
231
|
+
};
|
|
232
|
+
let lines = "";
|
|
233
|
+
const channel = runRelayMcp({
|
|
234
|
+
connect: () => connectBroker(broker, () => startDaemon(broker)),
|
|
235
|
+
claim,
|
|
236
|
+
channels: true,
|
|
237
|
+
wake: harness === "agy" ? "agy-resume" : "grok-resume",
|
|
238
|
+
input,
|
|
239
|
+
env: runEnv,
|
|
240
|
+
output: (chunk) => {
|
|
241
|
+
lines += chunk;
|
|
242
|
+
for (; ; ) {
|
|
243
|
+
const end = lines.indexOf("\n");
|
|
244
|
+
if (end < 0) break;
|
|
245
|
+
const message = JSON.parse(lines.slice(0, end));
|
|
246
|
+
lines = lines.slice(end + 1);
|
|
247
|
+
if (message.id === 2) joined(message.result ?? {});
|
|
248
|
+
if (message.method === "notifications/claude/channel") {
|
|
249
|
+
const content = message.params.content;
|
|
250
|
+
if (typeof content === "string") {
|
|
251
|
+
queue.push(content);
|
|
252
|
+
next();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
log: err
|
|
258
|
+
});
|
|
259
|
+
void channel.catch((error) => {
|
|
260
|
+
err(`${CLI_NAME} ${harness}: ${String(error)}`);
|
|
261
|
+
finish(1);
|
|
262
|
+
});
|
|
263
|
+
const stop = (signal) => {
|
|
264
|
+
stopping = true;
|
|
265
|
+
if (running) running.kill(signal);
|
|
266
|
+
else finish(0);
|
|
267
|
+
};
|
|
268
|
+
const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
269
|
+
for (const signal of signals) process.on(signal, stop);
|
|
270
|
+
try {
|
|
271
|
+
input.write(
|
|
272
|
+
`${JSON.stringify({ id: 1, method: "initialize", params: { protocolVersion: MCP_PROTOCOLS[0], capabilities: { experimental: { "claude/channel": {} } }, clientInfo: { name: `scp-${harness}`, version: "0.1.0" } } })}
|
|
273
|
+
`
|
|
274
|
+
);
|
|
275
|
+
input.write(`${JSON.stringify({ method: "notifications/initialized" })}
|
|
276
|
+
`);
|
|
277
|
+
input.write(`${JSON.stringify({ id: 2, method: "tools/call", params: { name: "relay_whoami", arguments: {} } })}
|
|
278
|
+
`);
|
|
279
|
+
const result = await Promise.race([whoami, done.then(() => void 0)]);
|
|
280
|
+
if (result === void 0) return await done;
|
|
281
|
+
const text = (result.content ?? []).map((part) => part.text ?? "").join("\n");
|
|
282
|
+
const address = /^address: (\S+)$/m.exec(text)?.[1];
|
|
283
|
+
if (result.isError || !address) throw new RelayUsage(`${CLI_NAME} ${harness}: could not join the relay: ${text || "no answer"}`);
|
|
284
|
+
runEnv.SCP_NAME = address.slice(address.lastIndexOf("/") + 1);
|
|
285
|
+
ready = true;
|
|
286
|
+
if (launch.first) turn(launch.first);
|
|
287
|
+
else {
|
|
288
|
+
err(`${CLI_NAME} ${harness}: ${address} waiting for messages`);
|
|
289
|
+
next();
|
|
290
|
+
}
|
|
291
|
+
return await done;
|
|
292
|
+
} finally {
|
|
293
|
+
for (const signal of signals) process.off(signal, stop);
|
|
294
|
+
input.end();
|
|
295
|
+
await channel.catch(() => {
|
|
296
|
+
});
|
|
297
|
+
if (running?.exitCode === null && running.signalCode === null) running.kill("SIGTERM");
|
|
298
|
+
if (dir) rmSync(dir, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
export {
|
|
302
|
+
AGY_HELP,
|
|
303
|
+
GROK_HELP,
|
|
304
|
+
agyHooksProblem,
|
|
305
|
+
resumeCommand,
|
|
306
|
+
resumeLaunch,
|
|
307
|
+
turnArgv
|
|
308
|
+
};
|
|
@@ -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-SNW53KT3.js";
|
|
19
|
+
import "./chunk-LRQP25RI.js";
|
|
20
|
+
import "./chunk-277BAY7J.js";
|
|
21
21
|
import "./chunk-YYSLYVNZ.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-3SOIVANO.js";
|
|
23
23
|
import "./chunk-MNTIEWKF.js";
|
|
24
24
|
export {
|
|
25
25
|
SETUP_HARNESSES,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CODEX_VALUED,
|
|
3
3
|
MESSAGING,
|
|
4
|
+
MUSE_MODE,
|
|
5
|
+
MUSE_MODES_ALLOWED,
|
|
4
6
|
SPAWN_RUNNER,
|
|
5
7
|
STDIN_WAIT_MS,
|
|
6
8
|
agentShell,
|
|
@@ -15,6 +17,8 @@ import {
|
|
|
15
17
|
harnessArgv,
|
|
16
18
|
harnessOutput,
|
|
17
19
|
harnessStdin,
|
|
20
|
+
messagingAllow,
|
|
21
|
+
museModes,
|
|
18
22
|
programAt,
|
|
19
23
|
repliesOutput,
|
|
20
24
|
resumeArgv,
|
|
@@ -31,19 +35,21 @@ import {
|
|
|
31
35
|
toml,
|
|
32
36
|
transcriptReplies,
|
|
33
37
|
workerName
|
|
34
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-RZUQ422C.js";
|
|
35
39
|
import {
|
|
36
40
|
SPAWN_HELP
|
|
37
41
|
} from "./chunk-KN7WEIE4.js";
|
|
38
|
-
import "./chunk-
|
|
39
|
-
import "./chunk-
|
|
40
|
-
import "./chunk-
|
|
42
|
+
import "./chunk-SNW53KT3.js";
|
|
43
|
+
import "./chunk-LRQP25RI.js";
|
|
44
|
+
import "./chunk-277BAY7J.js";
|
|
41
45
|
import "./chunk-YYSLYVNZ.js";
|
|
42
|
-
import "./chunk-
|
|
46
|
+
import "./chunk-3SOIVANO.js";
|
|
43
47
|
import "./chunk-MNTIEWKF.js";
|
|
44
48
|
export {
|
|
45
49
|
CODEX_VALUED,
|
|
46
50
|
MESSAGING,
|
|
51
|
+
MUSE_MODE,
|
|
52
|
+
MUSE_MODES_ALLOWED,
|
|
47
53
|
SPAWN_HELP,
|
|
48
54
|
SPAWN_RUNNER,
|
|
49
55
|
STDIN_WAIT_MS,
|
|
@@ -59,6 +65,8 @@ export {
|
|
|
59
65
|
harnessArgv,
|
|
60
66
|
harnessOutput,
|
|
61
67
|
harnessStdin,
|
|
68
|
+
messagingAllow,
|
|
69
|
+
museModes,
|
|
62
70
|
programAt,
|
|
63
71
|
repliesOutput,
|
|
64
72
|
resumeArgv,
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
RelayUsage,
|
|
3
3
|
activeRelay,
|
|
4
4
|
relayCredential
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-3SOIVANO.js";
|
|
6
6
|
import "./chunk-MNTIEWKF.js";
|
|
7
7
|
|
|
8
8
|
// src/up.ts
|
|
@@ -11,7 +11,9 @@ var WAKE_LAUNCH = [
|
|
|
11
11
|
["claude", `${CLI_NAME} claude --name <name> Claude Code with the scp channel: messages wake it`],
|
|
12
12
|
["codex", `${CLI_NAME} codex --name <name> messages start or steer its turns`],
|
|
13
13
|
["pi", `${CLI_NAME} pi --name <name> messages arrive as turns`],
|
|
14
|
-
["opencode", "opencode its scp plugin wakes an idle session (on by default)"]
|
|
14
|
+
["opencode", "opencode its scp plugin wakes an idle session (on by default)"],
|
|
15
|
+
["agy", `${CLI_NAME} agy --name <name> headless: each message resumes its conversation`],
|
|
16
|
+
["grok", `${CLI_NAME} grok --name <name> headless: each message resumes its session`]
|
|
15
17
|
];
|
|
16
18
|
function nextSteps(installed) {
|
|
17
19
|
const launches = WAKE_LAUNCH.filter(([harness]) => installed.has(harness)).map(([, line]) => ` ${line}`);
|
|
@@ -29,21 +31,21 @@ async function up(positionals, values, cwd, brokerStatus) {
|
|
|
29
31
|
const { url } = activeRelay(process.env);
|
|
30
32
|
const headless = values.token !== void 0 || Boolean(process.env.SCP_TOKEN?.trim());
|
|
31
33
|
if (headless || !relayCredential(process.env, url).token) {
|
|
32
|
-
const code = await (await import("./login-
|
|
34
|
+
const code = await (await import("./login-DCNCC3RS.js")).login({
|
|
33
35
|
...values.token !== void 0 ? { token: values.token } : {},
|
|
34
36
|
...values.label !== void 0 ? { label: values.label } : {},
|
|
35
37
|
...values["no-browser"] ? { "no-browser": true } : {}
|
|
36
38
|
});
|
|
37
39
|
if (code !== 0) return code;
|
|
38
40
|
} else out(`already logged in to ${url}`);
|
|
39
|
-
const enabled = await (await import("./remote-
|
|
41
|
+
const enabled = await (await import("./remote-YO5B2HQI.js")).cloudMain(["enable"], values.machine !== void 0 ? { machine: values.machine } : {}, brokerStatus);
|
|
40
42
|
if (enabled !== 0) return enabled;
|
|
41
|
-
const setup = (await import("./setup-
|
|
43
|
+
const setup = (await import("./setup-U7CWN5QP.js")).relaySetupCommand(["--write"], cwd, out);
|
|
42
44
|
if (setup !== 0) return setup;
|
|
43
45
|
const broker = await brokerStatus();
|
|
44
46
|
if (!broker.enabled) out(`the broker is still local-only; run \`${CLI_NAME} daemon restart\` to link it to ${url}`);
|
|
45
47
|
else out(`broker linked to ${broker.url ?? url}${broker.connected ? "" : " (connecting)"}`);
|
|
46
|
-
const { detectHarnesses, whichOnPath } = await import("./setup-
|
|
48
|
+
const { detectHarnesses, whichOnPath } = await import("./setup-U7CWN5QP.js");
|
|
47
49
|
const which = (name) => whichOnPath(name, process.env);
|
|
48
50
|
const installed = new Set(detectHarnesses(process.env, which).keys());
|
|
49
51
|
if (which("pi")) installed.add("pi");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scp-dev/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "The swarm command line tool.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://scp.dev",
|
|
@@ -24,15 +24,15 @@
|
|
|
24
24
|
"typecheck": "tsc -p ."
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@scp-dev/core": "0.
|
|
28
|
-
"@scp-dev/broker": "0.4.
|
|
29
|
-
"@scp-dev/hub": "0.6.
|
|
30
|
-
"@scp-dev/mcp": "0.2.
|
|
27
|
+
"@scp-dev/core": "0.4.0",
|
|
28
|
+
"@scp-dev/broker": "0.4.2",
|
|
29
|
+
"@scp-dev/hub": "0.6.3",
|
|
30
|
+
"@scp-dev/mcp": "0.2.2",
|
|
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.
|
|
35
|
+
"@scp-dev/mesh": "0.0.9",
|
|
36
36
|
"opencode-ai": "1.18.34",
|
|
37
37
|
"tsup": "^8.5.1"
|
|
38
38
|
},
|
package/dist/claude-RNH2E4GU.js
DELETED
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
entryArgv,
|
|
3
|
-
mcpFile,
|
|
4
|
-
mountedEntry,
|
|
5
|
-
whichOnPath
|
|
6
|
-
} from "./chunk-UKQBH4AQ.js";
|
|
7
|
-
import "./chunk-2342GTME.js";
|
|
8
|
-
import "./chunk-XQHPDNVG.js";
|
|
9
|
-
import "./chunk-YYSLYVNZ.js";
|
|
10
|
-
import {
|
|
11
|
-
CHANNELS_ENV,
|
|
12
|
-
RelayUsage,
|
|
13
|
-
deriveHandle,
|
|
14
|
-
joinMode
|
|
15
|
-
} from "./chunk-AAFHLHXG.js";
|
|
16
|
-
import "./chunk-MNTIEWKF.js";
|
|
17
|
-
|
|
18
|
-
// src/claude.ts
|
|
19
|
-
import { spawn } from "child_process";
|
|
20
|
-
import { existsSync, readFileSync } from "fs";
|
|
21
|
-
import { join } from "path";
|
|
22
|
-
import { CLI_NAME, validHandle } from "@scp-dev/core";
|
|
23
|
-
var CHANNEL_FLAGS = ["--dangerously-load-development-channels", "server:scp"];
|
|
24
|
-
function claudeArgv(argv) {
|
|
25
|
-
let name;
|
|
26
|
-
let i = 0;
|
|
27
|
-
for (; ; ) {
|
|
28
|
-
const arg = argv[i];
|
|
29
|
-
if (arg === "--name" || arg === "--as") {
|
|
30
|
-
if (argv[i + 1] === void 0) throw new RelayUsage(`${CLI_NAME} claude ${arg} takes a handle`);
|
|
31
|
-
name = argv[i + 1];
|
|
32
|
-
i += 2;
|
|
33
|
-
} else if (arg?.startsWith("--name=") || arg?.startsWith("--as=")) {
|
|
34
|
-
name = arg.slice(arg.indexOf("=") + 1);
|
|
35
|
-
i++;
|
|
36
|
-
} else break;
|
|
37
|
-
}
|
|
38
|
-
if (argv[i] === "--") i++;
|
|
39
|
-
name = name?.replace(/^me\//, "");
|
|
40
|
-
if (name !== void 0 && !validHandle(name)) throw new RelayUsage(`--name ${JSON.stringify(name)} is not a valid agent handle`);
|
|
41
|
-
return { ...name !== void 0 ? { name } : {}, args: argv.slice(i) };
|
|
42
|
-
}
|
|
43
|
-
function claudeLaunchArgs(args, added) {
|
|
44
|
-
const end = args.indexOf("--");
|
|
45
|
-
const at = end < 0 ? args.length : end;
|
|
46
|
-
const channels = args.some((arg, i) => i < at && (arg === "server:scp" || arg === `${CHANNEL_FLAGS[0]}=server:scp`)) ? [] : CHANNEL_FLAGS;
|
|
47
|
-
return [...args.slice(0, at), ...channels, ...added, ...args.slice(at)];
|
|
48
|
-
}
|
|
49
|
-
var STRICT_HINT = `${CLI_NAME} claude: --strict-mcp-config without --mcp-config drops the scp mount, so messages cannot wake this session`;
|
|
50
|
-
function externalMcp(args) {
|
|
51
|
-
return args.some((arg) => arg === "--mcp-config" || arg.startsWith("--mcp-config="));
|
|
52
|
-
}
|
|
53
|
-
function namePrompt(name, args, env, cwd) {
|
|
54
|
-
if (name !== void 0 || joinMode(env) === "manual" || args.some((arg) => /^--(append-)?system-prompt/.test(arg))) return [];
|
|
55
|
-
const handle = env.SCP_NAME || deriveHandle("claude-code", cwd);
|
|
56
|
-
return [
|
|
57
|
-
"--append-system-prompt",
|
|
58
|
-
`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.`
|
|
59
|
-
];
|
|
60
|
-
}
|
|
61
|
-
function mountHint(env, cwd = process.cwd()) {
|
|
62
|
-
const file = mcpFile("claude", env);
|
|
63
|
-
const entries = [];
|
|
64
|
-
try {
|
|
65
|
-
const user = mountedEntry("claude", file);
|
|
66
|
-
if (user) entries.push(user);
|
|
67
|
-
const config = existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : {};
|
|
68
|
-
const local = config.projects?.[cwd]?.mcpServers?.scp;
|
|
69
|
-
if (local && typeof local === "object") entries.push(local);
|
|
70
|
-
} catch {
|
|
71
|
-
return `${CLI_NAME} claude: ${file} is unreadable, so the scp mount is unchecked; fix it or run ${CLI_NAME} setup --write`;
|
|
72
|
-
}
|
|
73
|
-
const project = join(cwd, ".mcp.json");
|
|
74
|
-
try {
|
|
75
|
-
const entry = existsSync(project) ? mountedEntry("claude", project) : void 0;
|
|
76
|
-
if (entry) entries.push(entry);
|
|
77
|
-
} catch {
|
|
78
|
-
}
|
|
79
|
-
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`;
|
|
80
|
-
if (!entries.some((entry) => entryArgv("claude", entry).includes("--channels")))
|
|
81
|
-
return `${CLI_NAME} claude: the scp mount lacks --channels, so messages cannot wake this session; run ${CLI_NAME} setup --write`;
|
|
82
|
-
return void 0;
|
|
83
|
-
}
|
|
84
|
-
async function claudeCommand(argv, env = process.env, err = (line) => void process.stderr.write(`${line}
|
|
85
|
-
`), cwd = process.cwd()) {
|
|
86
|
-
if (argv[0] === "--help" || argv[0] === "-h") {
|
|
87
|
-
process.stdout.write(
|
|
88
|
-
`${CLI_NAME} claude [--name N | --as N] [--] [claude args...] start Claude Code with the scp channel loaded, so peer messages wake it
|
|
89
|
-
`
|
|
90
|
-
);
|
|
91
|
-
return 0;
|
|
92
|
-
}
|
|
93
|
-
const { name, args } = claudeArgv(argv);
|
|
94
|
-
const bin = whichOnPath("claude", env);
|
|
95
|
-
if (!bin) throw new RelayUsage("claude is not on PATH");
|
|
96
|
-
const external = externalMcp(args);
|
|
97
|
-
const hint = external ? void 0 : args.includes("--strict-mcp-config") ? STRICT_HINT : mountHint(env, cwd);
|
|
98
|
-
if (hint) throw new RelayUsage(hint);
|
|
99
|
-
if (external) err(`${CLI_NAME} claude: using your --mcp-config; it needs the scp server with --channels for messages to wake this session`);
|
|
100
|
-
const child = spawn(bin, claudeLaunchArgs(args, namePrompt(name, args, env, cwd)), {
|
|
101
|
-
cwd,
|
|
102
|
-
stdio: "inherit",
|
|
103
|
-
env: { ...env, ...name !== void 0 ? { SCP_NAME: name } : {}, [CHANNELS_ENV]: String(process.pid) }
|
|
104
|
-
});
|
|
105
|
-
const forward = (signal) => () => void child.kill(signal);
|
|
106
|
-
const handlers = ["SIGTERM", "SIGHUP"].map((signal) => [signal, forward(signal)]);
|
|
107
|
-
const ignore = () => {
|
|
108
|
-
};
|
|
109
|
-
process.on("SIGINT", ignore);
|
|
110
|
-
for (const [signal, handler] of handlers) process.on(signal, handler);
|
|
111
|
-
try {
|
|
112
|
-
return await new Promise((resolve, reject) => {
|
|
113
|
-
child.once("error", reject);
|
|
114
|
-
child.once("exit", (code, signal) => resolve(code ?? (signal ? 128 + (signalNumber(signal) ?? 1) : 1)));
|
|
115
|
-
});
|
|
116
|
-
} finally {
|
|
117
|
-
process.off("SIGINT", ignore);
|
|
118
|
-
for (const [signal, handler] of handlers) process.off(signal, handler);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
var SIGNALS = { SIGHUP: 1, SIGINT: 2, SIGKILL: 9, SIGTERM: 15 };
|
|
122
|
-
var signalNumber = (signal) => SIGNALS[signal];
|
|
123
|
-
export {
|
|
124
|
-
CHANNEL_FLAGS,
|
|
125
|
-
claudeArgv,
|
|
126
|
-
claudeCommand,
|
|
127
|
-
claudeLaunchArgs,
|
|
128
|
-
mountHint,
|
|
129
|
-
namePrompt
|
|
130
|
-
};
|