sessionpipe-core 0.13.2 → 0.13.4
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/control.js +101 -1
- package/dist/control.js.map +4 -4
- package/dist/index.js +459 -228
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +5 -0
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +7 -2
- package/dist/schema.js.map +2 -2
- package/dist/service.js +1 -1
- package/dist/src/adapters/acp-control.d.ts +2 -2
- package/dist/src/adapters/acp-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +33 -3
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code.d.ts +7 -0
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/headless.d.ts +22 -1
- package/dist/src/adapters/headless.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +1 -1
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/adapters/types.d.ts +20 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/control/frames.d.ts +87 -0
- package/dist/src/control/frames.d.ts.map +1 -0
- package/dist/src/control/index.d.ts +3 -2
- package/dist/src/control/index.d.ts.map +1 -1
- package/dist/src/readers/transcripts.d.ts +7 -2
- package/dist/src/readers/transcripts.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// sessionpipe-core 0.13.
|
|
1
|
+
// sessionpipe-core 0.13.4 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __export = (target, all) => {
|
|
4
4
|
for (var name in all)
|
|
@@ -63,6 +63,82 @@ import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as
|
|
|
63
63
|
import { userInfo } from "node:os";
|
|
64
64
|
import path4 from "node:path";
|
|
65
65
|
|
|
66
|
+
// src/control/frames.ts
|
|
67
|
+
function hostOf(receiver) {
|
|
68
|
+
try {
|
|
69
|
+
return new URL(receiver).host;
|
|
70
|
+
} catch {
|
|
71
|
+
return receiver;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
var IN_PLACE_ANSWER = "Answer it right here, as you would answer the person at the keyboard: your reply is sent back to them automatically. Don't run a command or call a tool just to send it.";
|
|
75
|
+
var COPY_WHYS = ["open", "running", "recent"];
|
|
76
|
+
var copyNote = (why) => `You are a copy of that session, started because the original is ${why === "open" ? "open in a window" : "in use by another process"}, and you have its conversation, folder and tools. Do what the message asks the way the original would: edits, commands and publishing all take effect for real. Only this conversation stays here, so the person reads your reply and not the original's${why === "open" ? " window" : ""}: say what you did, then stop.`;
|
|
77
|
+
var RE_ARM = "(Answer it right here, as you would answer the person at the keyboard: your reply is sent back to them automatically, so don't run a command or call a tool just to send it. Then, without mentioning it, start `sessionpipe wait` again as a background command, with the longest timeout your harness allows, to keep receiving messages.)";
|
|
78
|
+
function frame(text, receiver) {
|
|
79
|
+
return `Message via ${hostOf(receiver)} (sessionpipe control, signed on the sender's device):
|
|
80
|
+
|
|
81
|
+
${text}`;
|
|
82
|
+
}
|
|
83
|
+
var marked = (text, host, mark) => `Message via ${host} (sessionpipe control, signed on the sender's device; ${mark}):
|
|
84
|
+
|
|
85
|
+
${text}`;
|
|
86
|
+
function frameStart(text, receiver) {
|
|
87
|
+
return `${text}
|
|
88
|
+
|
|
89
|
+
(Sent via ${hostOf(receiver)} with sessionpipe control, signed on the sender's device.)`;
|
|
90
|
+
}
|
|
91
|
+
function frameAsk(text, from, mark) {
|
|
92
|
+
return `${text}
|
|
93
|
+
|
|
94
|
+
(Asked by ${from ?? "another session"} on this machine with sessionpipe ask${mark ? `, ${mark}` : ""}. Answer here in full: your reply is returned to that session automatically. Don't run a command or call a tool just to send it.)`;
|
|
95
|
+
}
|
|
96
|
+
function frameHandoff(from, receiver, reply) {
|
|
97
|
+
return `Answer from ${from} (sessionpipe handoff, asked via ${hostOf(receiver)}):
|
|
98
|
+
|
|
99
|
+
${reply}`;
|
|
100
|
+
}
|
|
101
|
+
var HOLE = "\0";
|
|
102
|
+
var MARK = "";
|
|
103
|
+
var lineOf = (built, hole = "(\\S+)") => new RegExp(
|
|
104
|
+
`^${built.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replaceAll(HOLE, hole).replaceAll(MARK, "message \\S+")}$`
|
|
105
|
+
);
|
|
106
|
+
var HEAD_LINES = [lineOf(frame("", HOLE)), lineOf(marked("", HOLE, MARK))];
|
|
107
|
+
var START_LINES = [lineOf(frameStart("", HOLE))];
|
|
108
|
+
var ASK_LINES = [lineOf(frameAsk("", HOLE), "[^\\n]{1,300}")];
|
|
109
|
+
var MARKED_ASK_LINES = [lineOf(frameAsk("", HOLE, MARK), "([^\\n]{1,300}?)")];
|
|
110
|
+
var HANDOFF_LINES = [lineOf(frameHandoff(HOLE, HOLE, ""))];
|
|
111
|
+
var COPY_NOTE_0_13_0 = "You are a copy of that session, started to answer this message: the conversation above is a replay, the original session is somewhere else, and nothing you do here appears in it. Answer the message and stop; don't carry on the original's work.";
|
|
112
|
+
var COPY_NOTES = [...new Set(COPY_WHYS.map(copyNote)), COPY_NOTE_0_13_0];
|
|
113
|
+
var NOTES = [IN_PLACE_ANSWER, ...COPY_NOTES, RE_ARM];
|
|
114
|
+
var ASK_NOTES = [...COPY_NOTES, RE_ARM];
|
|
115
|
+
var lineIn = (lines, line) => {
|
|
116
|
+
for (const l of lines) {
|
|
117
|
+
const m = l.exec(line);
|
|
118
|
+
if (m) return m;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
};
|
|
122
|
+
function deliveredWords(text) {
|
|
123
|
+
const paragraphs = text.split("\n\n");
|
|
124
|
+
const at = (i) => paragraphs[i].trim();
|
|
125
|
+
const n = paragraphs.length;
|
|
126
|
+
const note = n > 1 && NOTES.includes(at(n - 1)) ? at(n - 1) : null;
|
|
127
|
+
const end = note === null ? n : n - 1;
|
|
128
|
+
const words = (from, to) => paragraphs.slice(from, to).join("\n\n").trim();
|
|
129
|
+
const head = lineIn(HEAD_LINES, at(0));
|
|
130
|
+
if (head && words(1, end)) return { text: words(1, end), via: head[1], delivered: true };
|
|
131
|
+
const start = note === null ? lineIn(START_LINES, at(n - 1)) : null;
|
|
132
|
+
if (start && words(0, n - 1)) return { text: words(0, n - 1), via: start[1], delivered: true };
|
|
133
|
+
const asks = note === null || ASK_NOTES.includes(note);
|
|
134
|
+
const theirs = asks && (lineIn(ASK_LINES, at(end - 1)) && words(0, end - 1) || lineIn(HANDOFF_LINES, at(0)) && words(1, end));
|
|
135
|
+
if (theirs) return { text: note === null ? text : words(0, end), via: null, delivered: true };
|
|
136
|
+
const marked2 = asks ? lineIn(MARKED_ASK_LINES, at(end - 1)) : null;
|
|
137
|
+
if (marked2 && words(0, end - 1))
|
|
138
|
+
return { text: `${words(0, end - 1)}${frameAsk("", marked2[1])}`, via: null, delivered: true };
|
|
139
|
+
return { text, via: null, delivered: false };
|
|
140
|
+
}
|
|
141
|
+
|
|
66
142
|
// src/readers/files.ts
|
|
67
143
|
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
68
144
|
import path2 from "node:path";
|
|
@@ -199,15 +275,23 @@ function codexReplayBoundary(meta) {
|
|
|
199
275
|
}
|
|
200
276
|
|
|
201
277
|
// src/readers/transcripts.ts
|
|
202
|
-
function pairTurns(msgs, endLine) {
|
|
278
|
+
function pairTurns(msgs, endLine, tail = false) {
|
|
203
279
|
const turns = [];
|
|
204
280
|
let cur = null;
|
|
205
281
|
for (const m of msgs) {
|
|
206
|
-
if (m.role === "
|
|
282
|
+
if (m.role === "interrupt") {
|
|
283
|
+
if (cur) {
|
|
284
|
+
cur.interrupted = true;
|
|
285
|
+
cur.endLine = m.line;
|
|
286
|
+
turns.push(cur);
|
|
287
|
+
cur = null;
|
|
288
|
+
}
|
|
289
|
+
} else if (m.role === "user") {
|
|
207
290
|
if (cur && !cur.assistant) {
|
|
208
291
|
cur.user += `
|
|
209
292
|
|
|
210
293
|
${m.text}`;
|
|
294
|
+
if (m.id) cur.id = m.id;
|
|
211
295
|
continue;
|
|
212
296
|
}
|
|
213
297
|
if (cur) turns.push(cur);
|
|
@@ -217,12 +301,14 @@ ${m.text}`;
|
|
|
217
301
|
at: m.at,
|
|
218
302
|
startLine: m.line,
|
|
219
303
|
endLine: m.line,
|
|
220
|
-
...m.injected ? { injected: true } : {}
|
|
304
|
+
...m.injected ? { injected: true } : {},
|
|
305
|
+
...m.id ? { id: m.id } : {}
|
|
221
306
|
};
|
|
222
307
|
} else {
|
|
223
308
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
224
309
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
225
310
|
cur.endLine = m.line;
|
|
311
|
+
cur.said = m.text;
|
|
226
312
|
}
|
|
227
313
|
}
|
|
228
314
|
if (cur?.assistant) turns.push(cur);
|
|
@@ -233,12 +319,22 @@ ${m.text}`;
|
|
|
233
319
|
assistant: t.assistant,
|
|
234
320
|
at: t.at,
|
|
235
321
|
endLine: t.endLine,
|
|
236
|
-
...t.injected ? { injected: true } : {}
|
|
322
|
+
...t.injected ? { injected: true } : {},
|
|
323
|
+
...t.id ? { id: t.id } : {},
|
|
324
|
+
...t.interrupted ? { interrupted: true } : {}
|
|
237
325
|
})),
|
|
238
326
|
line
|
|
239
327
|
};
|
|
240
328
|
if (cur && !cur.assistant && cur.user)
|
|
241
|
-
out.open = {
|
|
329
|
+
out.open = {
|
|
330
|
+
user: cur.user,
|
|
331
|
+
at: cur.at,
|
|
332
|
+
...cur.injected ? { injected: true } : {},
|
|
333
|
+
...cur.id ? { id: cur.id } : {}
|
|
334
|
+
};
|
|
335
|
+
const last = turns[turns.length - 1];
|
|
336
|
+
if (tail && last)
|
|
337
|
+
out.tail = { start: last.startLine, ...last.said !== void 0 && !last.interrupted ? { said: last.said } : {} };
|
|
242
338
|
return out;
|
|
243
339
|
}
|
|
244
340
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -262,6 +358,10 @@ var textParts = (c, kinds) => {
|
|
|
262
358
|
).map((b) => b.text).join("\n");
|
|
263
359
|
};
|
|
264
360
|
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
361
|
+
var fromAgent = (origin) => {
|
|
362
|
+
const o = origin;
|
|
363
|
+
return o?.handback === true || typeof o?.senderTaskId === "string";
|
|
364
|
+
};
|
|
265
365
|
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
266
366
|
var PEER_OUTRO = "\n\nThis came from another ";
|
|
267
367
|
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
@@ -281,40 +381,68 @@ var queuedPrompt = (a) => {
|
|
|
281
381
|
const q = a;
|
|
282
382
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
283
383
|
const kind = q.origin?.kind ?? "human";
|
|
284
|
-
if (!SPOKEN.has(kind)) return "";
|
|
384
|
+
if (!SPOKEN.has(kind) || fromAgent(q.origin)) return "";
|
|
285
385
|
const text = textParts(q.prompt, ["text"]).trim();
|
|
286
386
|
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
287
387
|
};
|
|
288
388
|
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
389
|
+
var INTERRUPTED = /^\[Request interrupted by user[^\]]*\]$/;
|
|
390
|
+
var HOOK_FEEDBACK = /^[A-Za-z]+ hook feedback:\n/;
|
|
391
|
+
var hookDelivery = (text) => {
|
|
392
|
+
const head = HOOK_FEEDBACK.exec(text);
|
|
393
|
+
if (!head) return "";
|
|
394
|
+
const said = deliveredWords(text.slice(head[0].length));
|
|
395
|
+
return said.delivered ? said.text : "";
|
|
396
|
+
};
|
|
397
|
+
var hasBlock = (c, type) => Array.isArray(c) && c.some((b) => b && typeof b === "object" && b.type === type);
|
|
289
398
|
function readClaude(lines, from) {
|
|
290
399
|
const msgs = [];
|
|
400
|
+
let saidLine = -1;
|
|
401
|
+
let turnOver = false;
|
|
291
402
|
for (let i = from; i < lines.length; i++) {
|
|
292
403
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
293
404
|
if (!j || j.isSidechain) continue;
|
|
294
405
|
const origin = j.origin;
|
|
295
406
|
const channel = j.type === "user" && origin?.kind === "channel";
|
|
296
407
|
const peer = origin?.kind === "peer";
|
|
408
|
+
const c = j.message?.content;
|
|
409
|
+
if (!turnOver && (j.type === "assistant" && hasBlock(c, "tool_use") || j.type === "user" && hasBlock(c, "tool_result")))
|
|
410
|
+
saidLine = -1;
|
|
297
411
|
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
298
|
-
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
299
412
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
300
|
-
const
|
|
413
|
+
const id = typeof j.promptId === "string" && j.promptId ? j.promptId : void 0;
|
|
414
|
+
const ask = (said, m = {}) => {
|
|
415
|
+
const text = deliveredWords(said).text;
|
|
416
|
+
if (!text || isInjected(text)) return;
|
|
417
|
+
msgs.push({ role: "user", text, at, line: i, ...m });
|
|
418
|
+
turnOver = true;
|
|
419
|
+
};
|
|
420
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) {
|
|
421
|
+
if (j.isMeta && !origin?.kind)
|
|
422
|
+
ask(hookDelivery(textParts(c, ["text"]).trim()), { injected: true, ...id ? { id } : {} });
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
301
425
|
if (channel) {
|
|
302
|
-
|
|
303
|
-
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
426
|
+
ask(channelText(textParts(c, ["text"]).trim()), { injected: true });
|
|
304
427
|
} else if (j.type === "user") {
|
|
305
428
|
const raw = textParts(c, ["text"]).trim();
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
429
|
+
if (!peer && INTERRUPTED.test(raw)) {
|
|
430
|
+
msgs.push({ role: "interrupt", text: "", at, line: i });
|
|
431
|
+
turnOver = true;
|
|
432
|
+
} else if (!peer) ask(raw, id ? { id } : {});
|
|
433
|
+
else if (!fromAgent(origin)) ask(peerAsk(origin, raw), { injected: true, ...id ? { id } : {} });
|
|
309
434
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
310
435
|
const text = textParts(c, ["text"]).trim();
|
|
311
|
-
if (text)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
436
|
+
if (text) {
|
|
437
|
+
msgs.push({ role: "assistant", text, at, line: i });
|
|
438
|
+
saidLine = i;
|
|
439
|
+
turnOver = false;
|
|
440
|
+
}
|
|
441
|
+
} else if (j.type === "attachment") ask(queuedPrompt(j.attachment));
|
|
316
442
|
}
|
|
317
|
-
|
|
443
|
+
const out = pairTurns(msgs, endOf(lines), true);
|
|
444
|
+
if (out.tail && saidLine !== out.turns[out.turns.length - 1].endLine) delete out.tail.said;
|
|
445
|
+
return out;
|
|
318
446
|
}
|
|
319
447
|
function codexInjected(text) {
|
|
320
448
|
return /^<[a-z_-]+>/i.test(text) || /^# AGENTS\.md instructions\b/i.test(text);
|
|
@@ -766,6 +894,8 @@ var claudeCode = {
|
|
|
766
894
|
}
|
|
767
895
|
const hints = {};
|
|
768
896
|
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
897
|
+
if (event === "Stop" && typeof s.last_assistant_message === "string" && s.last_assistant_message.trim())
|
|
898
|
+
hints.final = s.last_assistant_message;
|
|
769
899
|
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
770
900
|
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
771
901
|
const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
|
|
@@ -1028,6 +1158,14 @@ function claudeAccounts(dirs, home = homeDir()) {
|
|
|
1028
1158
|
}
|
|
1029
1159
|
return out;
|
|
1030
1160
|
}
|
|
1161
|
+
function claudeAccountDirs(dirs, home = homeDir()) {
|
|
1162
|
+
const out = [];
|
|
1163
|
+
for (const d of dirs) {
|
|
1164
|
+
const l = claudeLogin(d, home);
|
|
1165
|
+
if (l) out.push({ id: l.id, dir: d });
|
|
1166
|
+
}
|
|
1167
|
+
return out;
|
|
1168
|
+
}
|
|
1031
1169
|
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
1032
1170
|
function transcriptLink(records) {
|
|
1033
1171
|
for (let i = records.length - 1; i >= 0; i--) {
|
|
@@ -1050,7 +1188,9 @@ function askText(j) {
|
|
|
1050
1188
|
const origin = j.origin;
|
|
1051
1189
|
if (origin?.kind && origin.kind !== "human") return null;
|
|
1052
1190
|
const c = j.message?.content;
|
|
1053
|
-
const text = (
|
|
1191
|
+
const text = deliveredWords(
|
|
1192
|
+
(typeof c === "string" ? c : Array.isArray(c) ? c.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n") : "").trim()
|
|
1193
|
+
).text;
|
|
1054
1194
|
if (!text || isInjected(text)) return null;
|
|
1055
1195
|
return text;
|
|
1056
1196
|
}
|
|
@@ -1695,6 +1835,7 @@ var acp_control_exports = {};
|
|
|
1695
1835
|
__export(acp_control_exports, {
|
|
1696
1836
|
ACP_AGENTS: () => ACP_AGENTS,
|
|
1697
1837
|
ACP_HARNESSES: () => ACP_HARNESSES,
|
|
1838
|
+
CANCELLED_NOTE: () => CANCELLED_NOTE,
|
|
1698
1839
|
acpAgent: () => acpAgent,
|
|
1699
1840
|
acpHarnesses: () => acpHarnesses,
|
|
1700
1841
|
acpOverride: () => acpOverride,
|
|
@@ -1802,8 +1943,13 @@ function runHeadless(bin, args, o) {
|
|
|
1802
1943
|
let tooBig = false;
|
|
1803
1944
|
let errTail = "";
|
|
1804
1945
|
let timedOut = false;
|
|
1946
|
+
let stopped = false;
|
|
1805
1947
|
let settled = false;
|
|
1806
1948
|
let child;
|
|
1949
|
+
if (o.signal?.aborted) {
|
|
1950
|
+
resolve({ stopped: true });
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1807
1953
|
const c = spawnable(bin, args);
|
|
1808
1954
|
if (!c) {
|
|
1809
1955
|
resolve({ error: shimNote(bin) });
|
|
@@ -1866,27 +2012,53 @@ function runHeadless(bin, args, o) {
|
|
|
1866
2012
|
child.stderr?.on("data", (b) => {
|
|
1867
2013
|
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
1868
2014
|
});
|
|
2015
|
+
let ending = null;
|
|
2016
|
+
let gone = false;
|
|
2017
|
+
const end = () => {
|
|
2018
|
+
kill("SIGTERM");
|
|
2019
|
+
ending ??= setTimeout(() => {
|
|
2020
|
+
ending = null;
|
|
2021
|
+
kill("SIGKILL");
|
|
2022
|
+
}, 1e4);
|
|
2023
|
+
ending.unref();
|
|
2024
|
+
};
|
|
1869
2025
|
const timer = setTimeout(() => {
|
|
2026
|
+
if (gone) return;
|
|
1870
2027
|
timedOut = true;
|
|
1871
|
-
|
|
1872
|
-
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
2028
|
+
end();
|
|
1873
2029
|
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
2030
|
+
const stop = () => {
|
|
2031
|
+
if (gone) return;
|
|
2032
|
+
stopped = true;
|
|
2033
|
+
end();
|
|
2034
|
+
};
|
|
2035
|
+
o.signal?.addEventListener("abort", stop, { once: true });
|
|
1874
2036
|
const result = (code, signal) => ({
|
|
1875
2037
|
code,
|
|
1876
2038
|
signal,
|
|
1877
2039
|
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
1878
2040
|
tooBig,
|
|
1879
2041
|
stderr: errTail,
|
|
1880
|
-
timedOut
|
|
2042
|
+
timedOut,
|
|
2043
|
+
...stopped ? { stopped } : {}
|
|
1881
2044
|
});
|
|
1882
2045
|
const finish = (r) => {
|
|
1883
2046
|
if (settled) return;
|
|
1884
2047
|
settled = true;
|
|
1885
2048
|
clearTimeout(timer);
|
|
2049
|
+
o.signal?.removeEventListener("abort", stop);
|
|
1886
2050
|
resolve(r);
|
|
1887
2051
|
};
|
|
1888
2052
|
child.on("error", (e) => finish({ error: e.message }));
|
|
1889
|
-
child.on("exit", (code, signal) =>
|
|
2053
|
+
child.on("exit", (code, signal) => {
|
|
2054
|
+
if (ending) {
|
|
2055
|
+
clearTimeout(ending);
|
|
2056
|
+
ending = null;
|
|
2057
|
+
kill("SIGKILL");
|
|
2058
|
+
}
|
|
2059
|
+
gone = true;
|
|
2060
|
+
setTimeout(() => finish(result(code, signal)), 2e3).unref();
|
|
2061
|
+
});
|
|
1890
2062
|
child.on("close", (code, signal) => {
|
|
1891
2063
|
if (o.onLine && partial.trim()) {
|
|
1892
2064
|
try {
|
|
@@ -1903,6 +2075,7 @@ function runHeadless(bin, args, o) {
|
|
|
1903
2075
|
function stderrTail(stderr, n = 3) {
|
|
1904
2076
|
return String(stderr || "").trim().split("\n").slice(-n).join(" ").trim();
|
|
1905
2077
|
}
|
|
2078
|
+
var CANCELLED_NOTE = "(The turn was cancelled.)";
|
|
1906
2079
|
function deniedNote(names, count) {
|
|
1907
2080
|
if (!count) return "";
|
|
1908
2081
|
return `
|
|
@@ -2144,7 +2317,9 @@ function startAcp(o) {
|
|
|
2144
2317
|
pending.clear();
|
|
2145
2318
|
};
|
|
2146
2319
|
child.on("error", (e) => left(`couldn't start ${label}: ${e.message}`));
|
|
2320
|
+
let dead = false;
|
|
2147
2321
|
const exited = (code, signal) => {
|
|
2322
|
+
dead = true;
|
|
2148
2323
|
const tail = errTail.trim().split("\n").slice(-3).join(" ").trim();
|
|
2149
2324
|
left(`${label} exited (${signal || `code ${code}`})${tail ? `: ${tail}` : ""}`);
|
|
2150
2325
|
};
|
|
@@ -2158,6 +2333,7 @@ function startAcp(o) {
|
|
|
2158
2333
|
child.stdin?.end();
|
|
2159
2334
|
} catch {
|
|
2160
2335
|
}
|
|
2336
|
+
if (dead) return;
|
|
2161
2337
|
const grace = o.closeGraceMs ?? 2e3;
|
|
2162
2338
|
const t = setTimeout(() => kill("SIGTERM"), grace);
|
|
2163
2339
|
t.unref();
|
|
@@ -2215,7 +2391,7 @@ function startAcp(o) {
|
|
|
2215
2391
|
error: `${label} was still working after ${Math.round(limit / 6e4)} minutes, so it was stopped`
|
|
2216
2392
|
} : { reply: `${words}
|
|
2217
2393
|
|
|
2218
|
-
|
|
2394
|
+
${CANCELLED_NOTE}${refused2}` };
|
|
2219
2395
|
return { reply: "", error: `${label} ended the turn with ${stop || "no stop reason"}` };
|
|
2220
2396
|
} catch (e) {
|
|
2221
2397
|
return { reply: "", error: e.message };
|
|
@@ -2703,8 +2879,10 @@ __export(claude_code_control_exports, {
|
|
|
2703
2879
|
findClaude: () => findClaude,
|
|
2704
2880
|
findTranscript: () => findTranscript,
|
|
2705
2881
|
findTranscripts: () => findTranscripts,
|
|
2882
|
+
helpFailure: () => helpFailure,
|
|
2706
2883
|
homeSettings: () => homeSettings,
|
|
2707
2884
|
hookMode: () => hookMode,
|
|
2885
|
+
initSession: () => initSession,
|
|
2708
2886
|
isUuid: () => isUuid,
|
|
2709
2887
|
liveProcess: () => liveProcess,
|
|
2710
2888
|
liveRecord: () => liveRecord,
|
|
@@ -2719,11 +2897,13 @@ __export(claude_code_control_exports, {
|
|
|
2719
2897
|
parseCaps: () => parseCaps,
|
|
2720
2898
|
parseResult: () => parseResult,
|
|
2721
2899
|
peerClass: () => peerClass,
|
|
2900
|
+
recentWrite: () => recentWrite,
|
|
2722
2901
|
recentlyWritten: () => recentlyWritten,
|
|
2723
2902
|
relaunchMode: () => relaunchMode,
|
|
2724
2903
|
resolveHome: () => resolveHome,
|
|
2725
2904
|
runClaude: () => runClaude,
|
|
2726
|
-
sessionFolder: () => sessionFolder
|
|
2905
|
+
sessionFolder: () => sessionFolder,
|
|
2906
|
+
versionManagerBins: () => versionManagerBins
|
|
2727
2907
|
});
|
|
2728
2908
|
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
2729
2909
|
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync6, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
@@ -2751,6 +2931,201 @@ var POOL_FIELDS = FIELDS.pool;
|
|
|
2751
2931
|
var enc = new TextEncoder();
|
|
2752
2932
|
var dec = new TextDecoder();
|
|
2753
2933
|
|
|
2934
|
+
// src/privacy/pii.ts
|
|
2935
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2936
|
+
var RULESET_PII = "pii@1";
|
|
2937
|
+
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
2938
|
+
var PHONE = /(?<![\w.])(?:(?<!\)[ .-]?|\+\d{1,3}[ .-]?)(\+?(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))(?:(?= (?:[01]?\d|2[0-3]):)|(?!\w|[ .-]\d\d))|(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w))/g;
|
|
2939
|
+
function looksPhone(s) {
|
|
2940
|
+
const digits = s.replace(/\D/g, "");
|
|
2941
|
+
if (digits.length < 7 || digits.length > 15) return false;
|
|
2942
|
+
if (!/[+() .-]/.test(s)) return false;
|
|
2943
|
+
if (!/[+() -]/.test(s)) return false;
|
|
2944
|
+
return true;
|
|
2945
|
+
}
|
|
2946
|
+
function reducePii(text, home) {
|
|
2947
|
+
if (!text) return text;
|
|
2948
|
+
let out = text.replace(EMAIL, "[email]");
|
|
2949
|
+
out = out.replace(PHONE, (m, date) => !date && looksPhone(m) ? "[phone]" : m);
|
|
2950
|
+
if (home) {
|
|
2951
|
+
const h = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2952
|
+
out = out.replace(new RegExp(`(?<![A-Za-z0-9_])${h}(?=/|$|\\s|["'\`])`, "g"), "~");
|
|
2953
|
+
}
|
|
2954
|
+
return out;
|
|
2955
|
+
}
|
|
2956
|
+
function hashMachine(name) {
|
|
2957
|
+
return createHash3("sha256").update(name).digest("hex").slice(0, 8);
|
|
2958
|
+
}
|
|
2959
|
+
function reducePiiDeep(v, home) {
|
|
2960
|
+
if (typeof v === "string") return reducePii(v, home);
|
|
2961
|
+
if (Array.isArray(v)) return v.map((x) => reducePiiDeep(x, home));
|
|
2962
|
+
if (v && typeof v === "object") {
|
|
2963
|
+
const out = {};
|
|
2964
|
+
for (const [k, x] of Object.entries(v)) out[k] = reducePiiDeep(x, home);
|
|
2965
|
+
return out;
|
|
2966
|
+
}
|
|
2967
|
+
return v;
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
// src/privacy/secrets.ts
|
|
2971
|
+
var RULESET_SECRETS = "secrets@1";
|
|
2972
|
+
var REDACTED = "[redacted]";
|
|
2973
|
+
var TOKEN_PATTERNS = [
|
|
2974
|
+
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g,
|
|
2975
|
+
/\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g,
|
|
2976
|
+
// AWS access key id
|
|
2977
|
+
/\bgh[pousr]_[A-Za-z0-9]{30,}\b/g,
|
|
2978
|
+
// GitHub
|
|
2979
|
+
/\bgithub_pat_[A-Za-z0-9_]{30,}\b/g,
|
|
2980
|
+
/\bglpat-[A-Za-z0-9_-]{20,}\b/g,
|
|
2981
|
+
// GitLab
|
|
2982
|
+
/\bxox[abposre]-[A-Za-z0-9-]{10,}\b/g,
|
|
2983
|
+
// Slack
|
|
2984
|
+
/\bhttps:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/_-]+/g,
|
|
2985
|
+
/\bhttps:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[A-Za-z0-9_-]+/g,
|
|
2986
|
+
/\bsk-(?:ant-|proj-|svcacct-|admin-|or-v1-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
2987
|
+
// Anthropic, OpenAI, DeepSeek, OpenRouter
|
|
2988
|
+
/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
|
|
2989
|
+
// Stripe secret / restricted
|
|
2990
|
+
/\bwhsec_[A-Za-z0-9+/=]{20,}/g,
|
|
2991
|
+
// Stripe / Svix / Standard Webhooks secret
|
|
2992
|
+
/\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
2993
|
+
// Google API key
|
|
2994
|
+
/\bya29\.[0-9A-Za-z_-]{20,}/g,
|
|
2995
|
+
// Google OAuth access token
|
|
2996
|
+
/\b1\/\/0[0-9A-Za-z_-]{30,}/g,
|
|
2997
|
+
// Google OAuth refresh token
|
|
2998
|
+
/\bGOCSPX-[0-9A-Za-z_-]{20,}/g,
|
|
2999
|
+
// Google OAuth client secret
|
|
3000
|
+
/\bxai-[A-Za-z0-9]{30,}\b/g,
|
|
3001
|
+
/\bgsk_[A-Za-z0-9]{30,}\b/g,
|
|
3002
|
+
// Groq
|
|
3003
|
+
/\bpplx-[A-Za-z0-9]{30,}\b/g,
|
|
3004
|
+
// Perplexity
|
|
3005
|
+
/\bhf_[A-Za-z0-9]{30,}\b/g,
|
|
3006
|
+
// Hugging Face
|
|
3007
|
+
/\bpcsk_[A-Za-z0-9_]{30,}\b/g,
|
|
3008
|
+
// Pinecone
|
|
3009
|
+
/\bnpm_[A-Za-z0-9]{30,}\b/g,
|
|
3010
|
+
/\bpypi-[A-Za-z0-9_-]{50,}/g,
|
|
3011
|
+
/\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
|
|
3012
|
+
// SendGrid
|
|
3013
|
+
/\bshp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}\b/g,
|
|
3014
|
+
// Shopify
|
|
3015
|
+
/\bdop_v1_[a-f0-9]{64}\b/g,
|
|
3016
|
+
// DigitalOcean
|
|
3017
|
+
/\b(?:lin_api|lin_oauth)_[A-Za-z0-9]{30,}\b/g,
|
|
3018
|
+
// Linear
|
|
3019
|
+
/\b(?:ntn|secret)_[A-Za-z0-9]{40,}\b/g,
|
|
3020
|
+
// Notion
|
|
3021
|
+
/\bsbp_[a-f0-9]{40}\b/g,
|
|
3022
|
+
// Supabase
|
|
3023
|
+
/\bfigd_[A-Za-z0-9_-]{30,}/g,
|
|
3024
|
+
// Figma
|
|
3025
|
+
/\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9]{30,}/g,
|
|
3026
|
+
// Doppler
|
|
3027
|
+
/\bgrn_[A-Za-z0-9_-]{20,}/g,
|
|
3028
|
+
// Granola
|
|
3029
|
+
/\bss_[a-f0-9]{32,}\b/g,
|
|
3030
|
+
// spacesheep API keys
|
|
3031
|
+
/\b[0-9]{8,10}:AA[A-Za-z0-9_-]{33}\b/g,
|
|
3032
|
+
// Telegram bot token
|
|
3033
|
+
/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
3034
|
+
// JWT
|
|
3035
|
+
];
|
|
3036
|
+
var URL_CREDENTIALS = /\b([a-z][a-z0-9+.-]{1,20}:\/\/[^\s:/@'"`<>]{1,100}:)([^\s@/'"`<>]{1,200})(@)/gi;
|
|
3037
|
+
var AUTH_HEADER = /\b((?:Bearer|Basic|Token)\s+)([A-Za-z0-9._~+/=-]{16,})/g;
|
|
3038
|
+
var SECRET_WORD = "(?:pass(?:word|wd|phrase|code)?s?|pwd|secret(?:[ _-]?key)?s?|client[ _-]?secret|api[ _-]?keys?|apikeys?|access[ _-]?keys?|access[ _-]?tokens?|auth[ _-]?tokens?|refresh[ _-]?tokens?|private[ _-]?keys?|tokens?|credentials?|creds)";
|
|
3039
|
+
var NAME_SEP = /(?<![A-Za-z0-9_])([A-Za-z][A-Za-z0-9_-]{1,79})(["']?\s{0,3}(?::=|=>|=|:)\s{0,3})/g;
|
|
3040
|
+
var VALUE = /"([^"\n]{1,300})"|'([^'\n]{1,300})'|`([^`\n]{1,300})`|([^\s"'`,;&)}\]]{1,300})/y;
|
|
3041
|
+
function redactAssignments(text) {
|
|
3042
|
+
let out = "";
|
|
3043
|
+
let last = 0;
|
|
3044
|
+
NAME_SEP.lastIndex = 0;
|
|
3045
|
+
for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {
|
|
3046
|
+
const name = m[1];
|
|
3047
|
+
if (!isSecretName(name)) continue;
|
|
3048
|
+
VALUE.lastIndex = NAME_SEP.lastIndex;
|
|
3049
|
+
const v = VALUE.exec(text);
|
|
3050
|
+
if (!v) continue;
|
|
3051
|
+
const value = v[1] ?? v[2] ?? v[3] ?? v[4];
|
|
3052
|
+
NAME_SEP.lastIndex = VALUE.lastIndex;
|
|
3053
|
+
if (isPlaceholder(value) || value.includes(REDACTED)) continue;
|
|
3054
|
+
if (/tokens?$/i.test(name) && /^\d+(?:[.,]\d+)*[km]?$/i.test(value)) continue;
|
|
3055
|
+
const q = v[1] != null ? '"' : v[2] != null ? "'" : v[3] != null ? "`" : "";
|
|
3056
|
+
out += text.slice(last, v.index) + q + REDACTED + q;
|
|
3057
|
+
last = VALUE.lastIndex;
|
|
3058
|
+
}
|
|
3059
|
+
return last ? out + text.slice(last) : text;
|
|
3060
|
+
}
|
|
3061
|
+
function isSecretName(name) {
|
|
3062
|
+
if (/^[A-Z0-9_]+_KEY$/.test(name)) return true;
|
|
3063
|
+
const flat = name.toLowerCase().replace(/[_-]/g, "");
|
|
3064
|
+
return /(?:pass(?:word|wd|phrase|code)|pwd|secret|secretkey|secretaccesskey|clientsecret|apikey|accesskey|accesstoken|authtoken|refreshtoken|privatekey|token|credential|creds)s?$/.test(
|
|
3065
|
+
flat
|
|
3066
|
+
);
|
|
3067
|
+
}
|
|
3068
|
+
var PROSE = new RegExp(
|
|
3069
|
+
`(\\b${SECRET_WORD}(?:[ \\t]{1,3}(?:is|was|=|:|to|as|of|for[ \\t]{1,3}\\S{1,40}[ \\t]{1,3}is))?[ \\t]{1,3})(["'\`]?)([^\\s"'\`]{6,200})\\2`,
|
|
3070
|
+
"gi"
|
|
3071
|
+
);
|
|
3072
|
+
function isPlaceholder(v) {
|
|
3073
|
+
const s = v.replace(/[.,;:!?]+$/, "");
|
|
3074
|
+
if (!s) return true;
|
|
3075
|
+
if (/^(?:string|number|boolean|bool|str|int|any|unknown|null|undefined|none|nil|true|false|optional|required|text|varchar|password|secret|token|env|string\[\]|object|bytes)$/i.test(
|
|
3076
|
+
s
|
|
3077
|
+
))
|
|
3078
|
+
return true;
|
|
3079
|
+
if (/^\[?redacted\]?$/i.test(s) || /^\*+$/.test(s) || /^x{3,}$/i.test(s) || /^\.{3}$|^…$/.test(s)) return true;
|
|
3080
|
+
if (/^[<{$%]/.test(s)) return true;
|
|
3081
|
+
if (/^(?:process\.env|env|os\.environ|c\.env|this\.env|import\.meta\.env|getenv|config|settings|secrets|args|opts|options|req|request|body|params|input|data|user|self|this)\b/i.test(
|
|
3082
|
+
s
|
|
3083
|
+
))
|
|
3084
|
+
return true;
|
|
3085
|
+
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(s)) return true;
|
|
3086
|
+
if (/[(]/.test(s)) return true;
|
|
3087
|
+
if (/^(?:your|my|the|a|an|some|new|old)[-_ ]/i.test(s)) return true;
|
|
3088
|
+
return false;
|
|
3089
|
+
}
|
|
3090
|
+
function looksSecret(v) {
|
|
3091
|
+
const s = v.replace(/[.,;:!?)]+$/, "");
|
|
3092
|
+
if (s.length < 6) return false;
|
|
3093
|
+
if (isPlaceholder(s)) return false;
|
|
3094
|
+
if (/^https?:\/\//i.test(s)) return false;
|
|
3095
|
+
if (/^[a-z]+(?:[-'][a-z]+)*$/.test(s)) return false;
|
|
3096
|
+
if (/^[A-Z][a-z]+$/.test(s)) return false;
|
|
3097
|
+
if (/^\d+(?:[.,]\d+)*[a-z%]*$/i.test(s)) return false;
|
|
3098
|
+
if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(s)) return false;
|
|
3099
|
+
const kinds = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((r) => r.test(s)).length;
|
|
3100
|
+
return kinds >= 3 || /[0-9]/.test(s) && /[A-Za-z]/.test(s);
|
|
3101
|
+
}
|
|
3102
|
+
function redactSecrets(text) {
|
|
3103
|
+
if (!text) return text;
|
|
3104
|
+
let out = text;
|
|
3105
|
+
for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);
|
|
3106
|
+
out = out.replace(URL_CREDENTIALS, (_m, head, _pw, at) => `${head}${REDACTED}${at}`);
|
|
3107
|
+
out = out.replace(AUTH_HEADER, (_m, head) => `${head}${REDACTED}`);
|
|
3108
|
+
out = redactAssignments(out);
|
|
3109
|
+
out = out.replace(
|
|
3110
|
+
PROSE,
|
|
3111
|
+
(m, head, q, v) => !looksSecret(v) || v.includes(REDACTED) ? m : `${head}${q}${REDACTED}${q}`
|
|
3112
|
+
);
|
|
3113
|
+
return out;
|
|
3114
|
+
}
|
|
3115
|
+
function hasSecret(text) {
|
|
3116
|
+
return !!text && redactSecrets(text) !== text;
|
|
3117
|
+
}
|
|
3118
|
+
function redactDeep(v) {
|
|
3119
|
+
if (typeof v === "string") return redactSecrets(v);
|
|
3120
|
+
if (Array.isArray(v)) return v.map(redactDeep);
|
|
3121
|
+
if (v && typeof v === "object") {
|
|
3122
|
+
const out = {};
|
|
3123
|
+
for (const [k, x] of Object.entries(v)) out[k] = redactDeep(x);
|
|
3124
|
+
return out;
|
|
3125
|
+
}
|
|
3126
|
+
return v;
|
|
3127
|
+
}
|
|
3128
|
+
|
|
2754
3129
|
// src/adapters/claude-code-control.ts
|
|
2755
3130
|
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2756
3131
|
var LIVE_WRITE_MS = 9e4;
|
|
@@ -2761,6 +3136,29 @@ var cmpVersion = (a, b) => {
|
|
|
2761
3136
|
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
2762
3137
|
return 0;
|
|
2763
3138
|
};
|
|
3139
|
+
function versionManagerBins(env = process.env, home = os5.homedir()) {
|
|
3140
|
+
const out = [];
|
|
3141
|
+
const versions = (dir, tail) => {
|
|
3142
|
+
let names = [];
|
|
3143
|
+
try {
|
|
3144
|
+
names = readdirSync4(dir).filter((n) => /^v?\d+(\.\d+)*$/.test(n));
|
|
3145
|
+
} catch {
|
|
3146
|
+
return;
|
|
3147
|
+
}
|
|
3148
|
+
for (const n of names.sort((a, b) => cmpVersion(a.replace(/^v/, ""), b.replace(/^v/, ""))).reverse())
|
|
3149
|
+
out.push(path10.join(dir, n, ...tail));
|
|
3150
|
+
};
|
|
3151
|
+
versions(path10.join(env.NVM_DIR || path10.join(home, ".nvm"), "versions", "node"), ["bin"]);
|
|
3152
|
+
const fnmDirs = env.FNM_DIR ? [env.FNM_DIR] : [
|
|
3153
|
+
path10.join(home, "Library", "Application Support", "fnm"),
|
|
3154
|
+
path10.join(home, ".local", "share", "fnm"),
|
|
3155
|
+
path10.join(home, ".fnm")
|
|
3156
|
+
];
|
|
3157
|
+
for (const d of fnmDirs) versions(path10.join(d, "node-versions"), ["installation", "bin"]);
|
|
3158
|
+
out.push(path10.join(env.VOLTA_HOME || path10.join(home, ".volta"), "bin"));
|
|
3159
|
+
versions(path10.join(env.ASDF_DATA_DIR || path10.join(home, ".asdf"), "installs", "nodejs"), ["bin"]);
|
|
3160
|
+
return out;
|
|
3161
|
+
}
|
|
2764
3162
|
function findClaude(env = process.env, home = os5.homedir()) {
|
|
2765
3163
|
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
2766
3164
|
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
@@ -2775,6 +3173,7 @@ function findClaude(env = process.env, home = os5.homedir()) {
|
|
|
2775
3173
|
"/usr/local/bin/claude"
|
|
2776
3174
|
])
|
|
2777
3175
|
if (isExe(p)) return p;
|
|
3176
|
+
for (const d of versionManagerBins(env, home)) if (isExe(path10.join(d, "claude"))) return path10.join(d, "claude");
|
|
2778
3177
|
const app = path10.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
2779
3178
|
let versions = [];
|
|
2780
3179
|
try {
|
|
@@ -2919,6 +3318,23 @@ function loginHint(configDir, platform = process.platform) {
|
|
|
2919
3318
|
return `Claude Code (${shown}) isn't signed in as this machine's background service sees it. On a Mac its login lives in the login keychain, which a background service can read only while someone is logged in at the screen: log in at the Mac, or run ${login} there.`;
|
|
2920
3319
|
return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
|
|
2921
3320
|
}
|
|
3321
|
+
var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`, "g");
|
|
3322
|
+
function helpFailure(err, home = os5.homedir()) {
|
|
3323
|
+
if (err?.code === "ENOENT") return "failed: no such file";
|
|
3324
|
+
if (err?.code === "EACCES") return "failed: the file can't be run (permission denied)";
|
|
3325
|
+
const plain2 = Array.from(String(err?.stderr ?? "").replace(ANSI, "")).filter((ch) => {
|
|
3326
|
+
const c = ch.charCodeAt(0);
|
|
3327
|
+
return c === 10 || c >= 32 && c !== 127;
|
|
3328
|
+
}).join("");
|
|
3329
|
+
const line = plain2.split("\n").map((l) => l.replace(/\s+/g, " ").trim()).find((l) => l.length > 0);
|
|
3330
|
+
if (line) {
|
|
3331
|
+
const said = redactSecrets(reducePii(line, home));
|
|
3332
|
+
return `failed: ${said.length > 200 ? `${said.slice(0, 199)}\u2026` : said}`;
|
|
3333
|
+
}
|
|
3334
|
+
if (err?.signal) return `failed: killed by ${err.signal}`;
|
|
3335
|
+
if (typeof err?.status === "number") return `failed with exit code ${err.status}`;
|
|
3336
|
+
return "failed";
|
|
3337
|
+
}
|
|
2922
3338
|
function detectCaps(bin, timeoutMs = 3e4) {
|
|
2923
3339
|
const c = probe(bin, ["--help"]);
|
|
2924
3340
|
try {
|
|
@@ -2936,7 +3352,7 @@ function detectCaps(bin, timeoutMs = 3e4) {
|
|
|
2936
3352
|
const err = e;
|
|
2937
3353
|
const caps = parseCaps(String(err?.stdout || ""));
|
|
2938
3354
|
if (caps.hasMode) return caps;
|
|
2939
|
-
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` :
|
|
3355
|
+
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : helpFailure(err);
|
|
2940
3356
|
return { ...caps, unread: `\`${path10.basename(bin)} --help\` ${why}` };
|
|
2941
3357
|
}
|
|
2942
3358
|
}
|
|
@@ -3157,16 +3573,16 @@ function relaunchMode(recorded, home) {
|
|
|
3157
3573
|
return recorded;
|
|
3158
3574
|
return null;
|
|
3159
3575
|
}
|
|
3160
|
-
function recentlyWritten(file,
|
|
3576
|
+
function recentlyWritten(file, knownEnd, now = Date.now()) {
|
|
3161
3577
|
let m;
|
|
3162
3578
|
try {
|
|
3163
3579
|
m = statSync5(file).mtimeMs;
|
|
3164
3580
|
} catch {
|
|
3165
3581
|
return false;
|
|
3166
3582
|
}
|
|
3167
|
-
|
|
3168
|
-
return !(ourLastEnd && m <= ourLastEnd + 2e3);
|
|
3583
|
+
return recentWrite(m, knownEnd, now);
|
|
3169
3584
|
}
|
|
3585
|
+
var recentWrite = (written, knownEnd, now) => now - written <= LIVE_WRITE_MS && !(knownEnd && written <= knownEnd + 2e3);
|
|
3170
3586
|
function runClaude(bin, args, o) {
|
|
3171
3587
|
return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('"type":"result"')) });
|
|
3172
3588
|
}
|
|
@@ -3195,6 +3611,15 @@ function parseResult(stdout) {
|
|
|
3195
3611
|
return null;
|
|
3196
3612
|
}
|
|
3197
3613
|
var isUuid = (s) => typeof s === "string" && UUID_RE2.test(s);
|
|
3614
|
+
function initSession(line) {
|
|
3615
|
+
if (!line.includes('"init"')) return null;
|
|
3616
|
+
try {
|
|
3617
|
+
const j = JSON.parse(line);
|
|
3618
|
+
return j.type === "system" && j.subtype === "init" && isUuid(j.session_id) ? j.session_id : null;
|
|
3619
|
+
} catch {
|
|
3620
|
+
return null;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3198
3623
|
|
|
3199
3624
|
// src/adapters/codex.ts
|
|
3200
3625
|
import { existsSync as existsSync4, readdirSync as readdirSync5, readFileSync as readFileSync7 } from "node:fs";
|
|
@@ -3884,7 +4309,7 @@ function codexStderr(stderr) {
|
|
|
3884
4309
|
var reading2 = () => new CodexReading();
|
|
3885
4310
|
|
|
3886
4311
|
// src/adapters/devin.ts
|
|
3887
|
-
import { createHash as
|
|
4312
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3888
4313
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
3889
4314
|
import path13 from "node:path";
|
|
3890
4315
|
|
|
@@ -4125,7 +4550,7 @@ var devin = {
|
|
|
4125
4550
|
const tool = validTool4(s.tool_name) ? s.tool_name : void 0;
|
|
4126
4551
|
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
4127
4552
|
const response = s.tool_response && typeof s.tool_response === "object" && !Array.isArray(s.tool_response) ? s.tool_response : void 0;
|
|
4128
|
-
const permissionId = () => `perm-${
|
|
4553
|
+
const permissionId = () => `perm-${createHash4("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
4129
4554
|
const ev = (type, data) => ({ type, data: prune4(data), harnessEvent: event });
|
|
4130
4555
|
const events = [];
|
|
4131
4556
|
switch (event) {
|
|
@@ -4946,201 +5371,6 @@ function processAlive(pid) {
|
|
|
4946
5371
|
}
|
|
4947
5372
|
}
|
|
4948
5373
|
|
|
4949
|
-
// src/privacy/pii.ts
|
|
4950
|
-
import { createHash as createHash4 } from "node:crypto";
|
|
4951
|
-
var RULESET_PII = "pii@1";
|
|
4952
|
-
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
4953
|
-
var PHONE = /(?<![\w.])(?:(?<!\)[ .-]?|\+\d{1,3}[ .-]?)(\+?(?:19|20)\d{2}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))(?:(?= (?:[01]?\d|2[0-3]):)|(?!\w|[ .-]\d\d))|(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w))/g;
|
|
4954
|
-
function looksPhone(s) {
|
|
4955
|
-
const digits = s.replace(/\D/g, "");
|
|
4956
|
-
if (digits.length < 7 || digits.length > 15) return false;
|
|
4957
|
-
if (!/[+() .-]/.test(s)) return false;
|
|
4958
|
-
if (!/[+() -]/.test(s)) return false;
|
|
4959
|
-
return true;
|
|
4960
|
-
}
|
|
4961
|
-
function reducePii(text, home) {
|
|
4962
|
-
if (!text) return text;
|
|
4963
|
-
let out = text.replace(EMAIL, "[email]");
|
|
4964
|
-
out = out.replace(PHONE, (m, date) => !date && looksPhone(m) ? "[phone]" : m);
|
|
4965
|
-
if (home) {
|
|
4966
|
-
const h = home.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4967
|
-
out = out.replace(new RegExp(`(?<![A-Za-z0-9_])${h}(?=/|$|\\s|["'\`])`, "g"), "~");
|
|
4968
|
-
}
|
|
4969
|
-
return out;
|
|
4970
|
-
}
|
|
4971
|
-
function hashMachine(name) {
|
|
4972
|
-
return createHash4("sha256").update(name).digest("hex").slice(0, 8);
|
|
4973
|
-
}
|
|
4974
|
-
function reducePiiDeep(v, home) {
|
|
4975
|
-
if (typeof v === "string") return reducePii(v, home);
|
|
4976
|
-
if (Array.isArray(v)) return v.map((x) => reducePiiDeep(x, home));
|
|
4977
|
-
if (v && typeof v === "object") {
|
|
4978
|
-
const out = {};
|
|
4979
|
-
for (const [k, x] of Object.entries(v)) out[k] = reducePiiDeep(x, home);
|
|
4980
|
-
return out;
|
|
4981
|
-
}
|
|
4982
|
-
return v;
|
|
4983
|
-
}
|
|
4984
|
-
|
|
4985
|
-
// src/privacy/secrets.ts
|
|
4986
|
-
var RULESET_SECRETS = "secrets@1";
|
|
4987
|
-
var REDACTED = "[redacted]";
|
|
4988
|
-
var TOKEN_PATTERNS = [
|
|
4989
|
-
/-----BEGIN [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END [A-Z0-9 ]*PRIVATE KEY(?: BLOCK)?-----|$)/g,
|
|
4990
|
-
/\b(?:AKIA|ASIA|ABIA|ACCA)[0-9A-Z]{16}\b/g,
|
|
4991
|
-
// AWS access key id
|
|
4992
|
-
/\bgh[pousr]_[A-Za-z0-9]{30,}\b/g,
|
|
4993
|
-
// GitHub
|
|
4994
|
-
/\bgithub_pat_[A-Za-z0-9_]{30,}\b/g,
|
|
4995
|
-
/\bglpat-[A-Za-z0-9_-]{20,}\b/g,
|
|
4996
|
-
// GitLab
|
|
4997
|
-
/\bxox[abposre]-[A-Za-z0-9-]{10,}\b/g,
|
|
4998
|
-
// Slack
|
|
4999
|
-
/\bhttps:\/\/hooks\.slack\.com\/services\/[A-Za-z0-9/_-]+/g,
|
|
5000
|
-
/\bhttps:\/\/(?:ptb\.|canary\.)?discord(?:app)?\.com\/api\/webhooks\/[0-9]+\/[A-Za-z0-9_-]+/g,
|
|
5001
|
-
/\bsk-(?:ant-|proj-|svcacct-|admin-|or-v1-)?[A-Za-z0-9_-]{20,}\b/g,
|
|
5002
|
-
// Anthropic, OpenAI, DeepSeek, OpenRouter
|
|
5003
|
-
/\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/g,
|
|
5004
|
-
// Stripe secret / restricted
|
|
5005
|
-
/\bwhsec_[A-Za-z0-9+/=]{20,}/g,
|
|
5006
|
-
// Stripe / Svix / Standard Webhooks secret
|
|
5007
|
-
/\bAIza[0-9A-Za-z_-]{35}\b/g,
|
|
5008
|
-
// Google API key
|
|
5009
|
-
/\bya29\.[0-9A-Za-z_-]{20,}/g,
|
|
5010
|
-
// Google OAuth access token
|
|
5011
|
-
/\b1\/\/0[0-9A-Za-z_-]{30,}/g,
|
|
5012
|
-
// Google OAuth refresh token
|
|
5013
|
-
/\bGOCSPX-[0-9A-Za-z_-]{20,}/g,
|
|
5014
|
-
// Google OAuth client secret
|
|
5015
|
-
/\bxai-[A-Za-z0-9]{30,}\b/g,
|
|
5016
|
-
/\bgsk_[A-Za-z0-9]{30,}\b/g,
|
|
5017
|
-
// Groq
|
|
5018
|
-
/\bpplx-[A-Za-z0-9]{30,}\b/g,
|
|
5019
|
-
// Perplexity
|
|
5020
|
-
/\bhf_[A-Za-z0-9]{30,}\b/g,
|
|
5021
|
-
// Hugging Face
|
|
5022
|
-
/\bpcsk_[A-Za-z0-9_]{30,}\b/g,
|
|
5023
|
-
// Pinecone
|
|
5024
|
-
/\bnpm_[A-Za-z0-9]{30,}\b/g,
|
|
5025
|
-
/\bpypi-[A-Za-z0-9_-]{50,}/g,
|
|
5026
|
-
/\bSG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
|
|
5027
|
-
// SendGrid
|
|
5028
|
-
/\bshp(?:at|ss|ca|pa)_[a-fA-F0-9]{32}\b/g,
|
|
5029
|
-
// Shopify
|
|
5030
|
-
/\bdop_v1_[a-f0-9]{64}\b/g,
|
|
5031
|
-
// DigitalOcean
|
|
5032
|
-
/\b(?:lin_api|lin_oauth)_[A-Za-z0-9]{30,}\b/g,
|
|
5033
|
-
// Linear
|
|
5034
|
-
/\b(?:ntn|secret)_[A-Za-z0-9]{40,}\b/g,
|
|
5035
|
-
// Notion
|
|
5036
|
-
/\bsbp_[a-f0-9]{40}\b/g,
|
|
5037
|
-
// Supabase
|
|
5038
|
-
/\bfigd_[A-Za-z0-9_-]{30,}/g,
|
|
5039
|
-
// Figma
|
|
5040
|
-
/\bdp\.(?:pt|st|sa|ct)\.[A-Za-z0-9]{30,}/g,
|
|
5041
|
-
// Doppler
|
|
5042
|
-
/\bgrn_[A-Za-z0-9_-]{20,}/g,
|
|
5043
|
-
// Granola
|
|
5044
|
-
/\bss_[a-f0-9]{32,}\b/g,
|
|
5045
|
-
// spacesheep API keys
|
|
5046
|
-
/\b[0-9]{8,10}:AA[A-Za-z0-9_-]{33}\b/g,
|
|
5047
|
-
// Telegram bot token
|
|
5048
|
-
/\beyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
5049
|
-
// JWT
|
|
5050
|
-
];
|
|
5051
|
-
var URL_CREDENTIALS = /\b([a-z][a-z0-9+.-]{1,20}:\/\/[^\s:/@'"`<>]{1,100}:)([^\s@/'"`<>]{1,200})(@)/gi;
|
|
5052
|
-
var AUTH_HEADER = /\b((?:Bearer|Basic|Token)\s+)([A-Za-z0-9._~+/=-]{16,})/g;
|
|
5053
|
-
var SECRET_WORD = "(?:pass(?:word|wd|phrase|code)?s?|pwd|secret(?:[ _-]?key)?s?|client[ _-]?secret|api[ _-]?keys?|apikeys?|access[ _-]?keys?|access[ _-]?tokens?|auth[ _-]?tokens?|refresh[ _-]?tokens?|private[ _-]?keys?|tokens?|credentials?|creds)";
|
|
5054
|
-
var NAME_SEP = /(?<![A-Za-z0-9_])([A-Za-z][A-Za-z0-9_-]{1,79})(["']?\s{0,3}(?::=|=>|=|:)\s{0,3})/g;
|
|
5055
|
-
var VALUE = /"([^"\n]{1,300})"|'([^'\n]{1,300})'|`([^`\n]{1,300})`|([^\s"'`,;&)}\]]{1,300})/y;
|
|
5056
|
-
function redactAssignments(text) {
|
|
5057
|
-
let out = "";
|
|
5058
|
-
let last = 0;
|
|
5059
|
-
NAME_SEP.lastIndex = 0;
|
|
5060
|
-
for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {
|
|
5061
|
-
const name = m[1];
|
|
5062
|
-
if (!isSecretName(name)) continue;
|
|
5063
|
-
VALUE.lastIndex = NAME_SEP.lastIndex;
|
|
5064
|
-
const v = VALUE.exec(text);
|
|
5065
|
-
if (!v) continue;
|
|
5066
|
-
const value = v[1] ?? v[2] ?? v[3] ?? v[4];
|
|
5067
|
-
NAME_SEP.lastIndex = VALUE.lastIndex;
|
|
5068
|
-
if (isPlaceholder(value) || value.includes(REDACTED)) continue;
|
|
5069
|
-
if (/tokens?$/i.test(name) && /^\d+(?:[.,]\d+)*[km]?$/i.test(value)) continue;
|
|
5070
|
-
const q = v[1] != null ? '"' : v[2] != null ? "'" : v[3] != null ? "`" : "";
|
|
5071
|
-
out += text.slice(last, v.index) + q + REDACTED + q;
|
|
5072
|
-
last = VALUE.lastIndex;
|
|
5073
|
-
}
|
|
5074
|
-
return last ? out + text.slice(last) : text;
|
|
5075
|
-
}
|
|
5076
|
-
function isSecretName(name) {
|
|
5077
|
-
if (/^[A-Z0-9_]+_KEY$/.test(name)) return true;
|
|
5078
|
-
const flat = name.toLowerCase().replace(/[_-]/g, "");
|
|
5079
|
-
return /(?:pass(?:word|wd|phrase|code)|pwd|secret|secretkey|secretaccesskey|clientsecret|apikey|accesskey|accesstoken|authtoken|refreshtoken|privatekey|token|credential|creds)s?$/.test(
|
|
5080
|
-
flat
|
|
5081
|
-
);
|
|
5082
|
-
}
|
|
5083
|
-
var PROSE = new RegExp(
|
|
5084
|
-
`(\\b${SECRET_WORD}(?:[ \\t]{1,3}(?:is|was|=|:|to|as|of|for[ \\t]{1,3}\\S{1,40}[ \\t]{1,3}is))?[ \\t]{1,3})(["'\`]?)([^\\s"'\`]{6,200})\\2`,
|
|
5085
|
-
"gi"
|
|
5086
|
-
);
|
|
5087
|
-
function isPlaceholder(v) {
|
|
5088
|
-
const s = v.replace(/[.,;:!?]+$/, "");
|
|
5089
|
-
if (!s) return true;
|
|
5090
|
-
if (/^(?:string|number|boolean|bool|str|int|any|unknown|null|undefined|none|nil|true|false|optional|required|text|varchar|password|secret|token|env|string\[\]|object|bytes)$/i.test(
|
|
5091
|
-
s
|
|
5092
|
-
))
|
|
5093
|
-
return true;
|
|
5094
|
-
if (/^\[?redacted\]?$/i.test(s) || /^\*+$/.test(s) || /^x{3,}$/i.test(s) || /^\.{3}$|^…$/.test(s)) return true;
|
|
5095
|
-
if (/^[<{$%]/.test(s)) return true;
|
|
5096
|
-
if (/^(?:process\.env|env|os\.environ|c\.env|this\.env|import\.meta\.env|getenv|config|settings|secrets|args|opts|options|req|request|body|params|input|data|user|self|this)\b/i.test(
|
|
5097
|
-
s
|
|
5098
|
-
))
|
|
5099
|
-
return true;
|
|
5100
|
-
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(s)) return true;
|
|
5101
|
-
if (/[(]/.test(s)) return true;
|
|
5102
|
-
if (/^(?:your|my|the|a|an|some|new|old)[-_ ]/i.test(s)) return true;
|
|
5103
|
-
return false;
|
|
5104
|
-
}
|
|
5105
|
-
function looksSecret(v) {
|
|
5106
|
-
const s = v.replace(/[.,;:!?)]+$/, "");
|
|
5107
|
-
if (s.length < 6) return false;
|
|
5108
|
-
if (isPlaceholder(s)) return false;
|
|
5109
|
-
if (/^https?:\/\//i.test(s)) return false;
|
|
5110
|
-
if (/^[a-z]+(?:[-'][a-z]+)*$/.test(s)) return false;
|
|
5111
|
-
if (/^[A-Z][a-z]+$/.test(s)) return false;
|
|
5112
|
-
if (/^\d+(?:[.,]\d+)*[a-z%]*$/i.test(s)) return false;
|
|
5113
|
-
if (/^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+$/.test(s)) return false;
|
|
5114
|
-
const kinds = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/].filter((r) => r.test(s)).length;
|
|
5115
|
-
return kinds >= 3 || /[0-9]/.test(s) && /[A-Za-z]/.test(s);
|
|
5116
|
-
}
|
|
5117
|
-
function redactSecrets(text) {
|
|
5118
|
-
if (!text) return text;
|
|
5119
|
-
let out = text;
|
|
5120
|
-
for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);
|
|
5121
|
-
out = out.replace(URL_CREDENTIALS, (_m, head, _pw, at) => `${head}${REDACTED}${at}`);
|
|
5122
|
-
out = out.replace(AUTH_HEADER, (_m, head) => `${head}${REDACTED}`);
|
|
5123
|
-
out = redactAssignments(out);
|
|
5124
|
-
out = out.replace(
|
|
5125
|
-
PROSE,
|
|
5126
|
-
(m, head, q, v) => !looksSecret(v) || v.includes(REDACTED) ? m : `${head}${q}${REDACTED}${q}`
|
|
5127
|
-
);
|
|
5128
|
-
return out;
|
|
5129
|
-
}
|
|
5130
|
-
function hasSecret(text) {
|
|
5131
|
-
return !!text && redactSecrets(text) !== text;
|
|
5132
|
-
}
|
|
5133
|
-
function redactDeep(v) {
|
|
5134
|
-
if (typeof v === "string") return redactSecrets(v);
|
|
5135
|
-
if (Array.isArray(v)) return v.map(redactDeep);
|
|
5136
|
-
if (v && typeof v === "object") {
|
|
5137
|
-
const out = {};
|
|
5138
|
-
for (const [k, x] of Object.entries(v)) out[k] = redactDeep(x);
|
|
5139
|
-
return out;
|
|
5140
|
-
}
|
|
5141
|
-
return v;
|
|
5142
|
-
}
|
|
5143
|
-
|
|
5144
5374
|
// src/privacy/filter.ts
|
|
5145
5375
|
var TYPE_TIER = {
|
|
5146
5376
|
"session.started": 0,
|
|
@@ -5346,6 +5576,7 @@ export {
|
|
|
5346
5576
|
antigravity_control_exports as antigravityControl,
|
|
5347
5577
|
assertConfigWritable,
|
|
5348
5578
|
capTail,
|
|
5579
|
+
claudeAccountDirs,
|
|
5349
5580
|
claudeAccounts,
|
|
5350
5581
|
claudeCode,
|
|
5351
5582
|
claude_code_control_exports as claudeControl,
|