sessionpipe 0.12.0 → 0.13.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/README.md +48 -6
- package/dist/ask.d.ts +40 -0
- package/dist/ask.d.ts.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +3098 -1166
- package/dist/cli.js.map +4 -4
- package/dist/control/channel.d.ts +43 -0
- package/dist/control/channel.d.ts.map +1 -0
- package/dist/control/cli.d.ts +13 -1
- package/dist/control/cli.d.ts.map +1 -1
- package/dist/control/daemon.d.ts +189 -2
- package/dist/control/daemon.d.ts.map +1 -1
- package/dist/control/inbox.d.ts +119 -0
- package/dist/control/inbox.d.ts.map +1 -0
- package/dist/control/local.d.ts +69 -4
- package/dist/control/local.d.ts.map +1 -1
- package/dist/control/pair.d.ts.map +1 -1
- package/dist/control/reply.d.ts +25 -1
- package/dist/control/reply.d.ts.map +1 -1
- package/dist/control/route.d.ts +50 -2
- package/dist/control/route.d.ts.map +1 -1
- package/dist/control/sdk.d.ts.map +1 -1
- package/dist/control/store.d.ts +49 -4
- package/dist/control/store.d.ts.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/hook.d.ts.map +1 -1
- package/dist/hook.js +14 -4
- package/dist/hook.js.map +2 -2
- package/dist/index.js +819 -138
- package/dist/index.js.map +4 -4
- package/dist/run.d.ts +1 -0
- package/dist/run.d.ts.map +1 -1
- package/dist/worker.js +828 -147
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
// sessionpipe 0.
|
|
1
|
+
// sessionpipe 0.13.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
2
7
|
|
|
3
8
|
// src/http-sink.ts
|
|
4
9
|
import { createHash, createHmac } from "node:crypto";
|
|
@@ -100,15 +105,15 @@ import {
|
|
|
100
105
|
appendFileSync as appendFileSync3,
|
|
101
106
|
existsSync as existsSync10,
|
|
102
107
|
mkdirSync as mkdirSync8,
|
|
103
|
-
readdirSync as
|
|
104
|
-
readFileSync as
|
|
108
|
+
readdirSync as readdirSync7,
|
|
109
|
+
readFileSync as readFileSync11,
|
|
105
110
|
renameSync as renameSync3,
|
|
106
|
-
statSync as
|
|
111
|
+
statSync as statSync7,
|
|
107
112
|
unlinkSync as unlinkSync4,
|
|
108
113
|
writeFileSync as writeFileSync8
|
|
109
114
|
} from "node:fs";
|
|
110
|
-
import
|
|
111
|
-
import
|
|
115
|
+
import os4 from "node:os";
|
|
116
|
+
import path16 from "node:path";
|
|
112
117
|
|
|
113
118
|
// ../core/src/paths.ts
|
|
114
119
|
import os from "node:os";
|
|
@@ -288,7 +293,14 @@ ${m.text}`;
|
|
|
288
293
|
continue;
|
|
289
294
|
}
|
|
290
295
|
if (cur) turns.push(cur);
|
|
291
|
-
cur = {
|
|
296
|
+
cur = {
|
|
297
|
+
user: m.text,
|
|
298
|
+
assistant: "",
|
|
299
|
+
at: m.at,
|
|
300
|
+
startLine: m.line,
|
|
301
|
+
endLine: m.line,
|
|
302
|
+
...m.injected ? { injected: true } : {}
|
|
303
|
+
};
|
|
292
304
|
} else {
|
|
293
305
|
if (!cur) cur = { user: "", assistant: "", at: m.at, startLine: m.line, endLine: m.line };
|
|
294
306
|
cur.assistant += (cur.assistant ? "\n\n" : "") + m.text;
|
|
@@ -298,10 +310,17 @@ ${m.text}`;
|
|
|
298
310
|
if (cur?.assistant) turns.push(cur);
|
|
299
311
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
300
312
|
const out = {
|
|
301
|
-
turns: turns.map((t) => ({
|
|
313
|
+
turns: turns.map((t) => ({
|
|
314
|
+
user: t.user,
|
|
315
|
+
assistant: t.assistant,
|
|
316
|
+
at: t.at,
|
|
317
|
+
endLine: t.endLine,
|
|
318
|
+
...t.injected ? { injected: true } : {}
|
|
319
|
+
})),
|
|
302
320
|
line
|
|
303
321
|
};
|
|
304
|
-
if (cur && !cur.assistant && cur.user)
|
|
322
|
+
if (cur && !cur.assistant && cur.user)
|
|
323
|
+
out.open = { user: cur.user, at: cur.at, ...cur.injected ? { injected: true } : {} };
|
|
305
324
|
return out;
|
|
306
325
|
}
|
|
307
326
|
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
@@ -324,27 +343,51 @@ var textParts = (c, kinds) => {
|
|
|
324
343
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
325
344
|
).map((b) => b.text).join("\n");
|
|
326
345
|
};
|
|
346
|
+
var SPOKEN = /* @__PURE__ */ new Set(["human", "peer"]);
|
|
347
|
+
var PEER_INTRO = /sent a message[^\n]*:$/;
|
|
348
|
+
var PEER_OUTRO = "\n\nThis came from another ";
|
|
349
|
+
var ENVELOPE = /^<cross-session-message\b[^>]*>\n([\s\S]*)\n<\/cross-session-message>$/;
|
|
350
|
+
function peerAsk(origin, text) {
|
|
351
|
+
const body = origin?.body;
|
|
352
|
+
let t = typeof body === "string" && body.trim() ? body : text;
|
|
353
|
+
if (t !== body) {
|
|
354
|
+
const nl = t.indexOf("\n");
|
|
355
|
+
if (nl > 0 && PEER_INTRO.test(t.slice(0, nl))) t = t.slice(nl + 1);
|
|
356
|
+
const outro = t.indexOf(PEER_OUTRO);
|
|
357
|
+
if (outro > 0) t = t.slice(0, outro);
|
|
358
|
+
}
|
|
359
|
+
return (ENVELOPE.exec(t.trim())?.[1] ?? t).trim();
|
|
360
|
+
}
|
|
327
361
|
var queuedPrompt = (a) => {
|
|
328
362
|
if (!a || typeof a !== "object") return "";
|
|
329
363
|
const q = a;
|
|
330
364
|
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
331
|
-
|
|
332
|
-
|
|
365
|
+
const kind = q.origin?.kind ?? "human";
|
|
366
|
+
if (!SPOKEN.has(kind)) return "";
|
|
367
|
+
const text = textParts(q.prompt, ["text"]).trim();
|
|
368
|
+
return kind === "peer" ? peerAsk(q.origin, text) : text;
|
|
333
369
|
};
|
|
370
|
+
var channelText = (text) => /^<channel\b[^>]*>([\s\S]*)<\/channel>$/.exec(text)?.[1]?.trim() ?? "";
|
|
334
371
|
function readClaude(lines, from) {
|
|
335
372
|
const msgs = [];
|
|
336
373
|
for (let i = from; i < lines.length; i++) {
|
|
337
374
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
338
375
|
if (!j || j.isSidechain) continue;
|
|
339
376
|
const origin = j.origin;
|
|
340
|
-
|
|
341
|
-
|
|
377
|
+
const channel = j.type === "user" && origin?.kind === "channel";
|
|
378
|
+
const peer = origin?.kind === "peer";
|
|
379
|
+
if (j.type === "user" && (j.isCompactSummary || j.repeatsCompactSummary)) continue;
|
|
380
|
+
if (j.type === "user" && !channel && !peer && (j.isMeta || origin?.kind && origin.kind !== "human")) continue;
|
|
342
381
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
343
382
|
const c = j.message?.content;
|
|
344
|
-
if (
|
|
345
|
-
const text = textParts(c, ["text"]).trim();
|
|
383
|
+
if (channel) {
|
|
384
|
+
const text = channelText(textParts(c, ["text"]).trim());
|
|
385
|
+
if (text) msgs.push({ role: "user", text, at, line: i, injected: true });
|
|
386
|
+
} else if (j.type === "user") {
|
|
387
|
+
const raw = textParts(c, ["text"]).trim();
|
|
388
|
+
const text = peer ? peerAsk(origin, raw) : raw;
|
|
346
389
|
if (!text || isInjected(text)) continue;
|
|
347
|
-
msgs.push({ role: "user", text, at, line: i });
|
|
390
|
+
msgs.push({ role: "user", text, at, line: i, ...peer ? { injected: true } : {} });
|
|
348
391
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
349
392
|
const text = textParts(c, ["text"]).trim();
|
|
350
393
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
@@ -1258,8 +1301,44 @@ async function claudeAiTitle(bridge, token) {
|
|
|
1258
1301
|
}
|
|
1259
1302
|
|
|
1260
1303
|
// ../core/src/adapters/headless.ts
|
|
1304
|
+
import { spawn } from "node:child_process";
|
|
1261
1305
|
import { accessSync, constants, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
1262
1306
|
import path5 from "node:path";
|
|
1307
|
+
|
|
1308
|
+
// ../core/src/launched-by.ts
|
|
1309
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
1310
|
+
var DELEGATED_BY_ENV = "SESSIONPIPE_ASKED_BY";
|
|
1311
|
+
var ASK_CHAIN_ENV = "SESSIONPIPE_ASK_CHAIN";
|
|
1312
|
+
var LINEAGE_ENV = [LAUNCHED_BY_ENV, DELEGATED_BY_ENV, ASK_CHAIN_ENV];
|
|
1313
|
+
var SESSION_REF = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
1314
|
+
function withoutLineage(env) {
|
|
1315
|
+
const e = { ...env };
|
|
1316
|
+
for (const k of LINEAGE_ENV) delete e[k];
|
|
1317
|
+
return e;
|
|
1318
|
+
}
|
|
1319
|
+
function refIn(env, name) {
|
|
1320
|
+
const v = env[name]?.trim();
|
|
1321
|
+
return v && SESSION_REF.test(v) ? v : void 0;
|
|
1322
|
+
}
|
|
1323
|
+
function launchedBy(env) {
|
|
1324
|
+
return refIn(env, LAUNCHED_BY_ENV);
|
|
1325
|
+
}
|
|
1326
|
+
function delegatedBy(env) {
|
|
1327
|
+
return refIn(env, DELEGATED_BY_ENV);
|
|
1328
|
+
}
|
|
1329
|
+
function readHook(adapter, input) {
|
|
1330
|
+
const r = adapter.fromHook(input);
|
|
1331
|
+
if (r && !r.session.parent_id) {
|
|
1332
|
+
const self = `${adapter.name}:${r.session.id}`;
|
|
1333
|
+
const launcher = launchedBy(input.env);
|
|
1334
|
+
if (launcher && launcher !== self) r.session.launched_by = launcher;
|
|
1335
|
+
const asker = delegatedBy(input.env);
|
|
1336
|
+
if (asker && asker !== self) r.session.delegated_by = asker;
|
|
1337
|
+
}
|
|
1338
|
+
return r;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// ../core/src/adapters/headless.ts
|
|
1263
1342
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
1264
1343
|
var OUT_CAP = 1024 * 1024;
|
|
1265
1344
|
function isExe(p) {
|
|
@@ -1301,10 +1380,116 @@ function spawnable(bin, args) {
|
|
|
1301
1380
|
const target = shimTarget(bin);
|
|
1302
1381
|
return target ? { file: process.execPath, args: [target, ...args] } : null;
|
|
1303
1382
|
}
|
|
1383
|
+
var shimNote = (bin) => `${path5.basename(bin)} is a launcher script that names no program to run; point the harness's override (SESSIONPIPE_CLAUDE, SESSIONPIPE_CODEX, \u2026) at the program itself`;
|
|
1304
1384
|
function probe(bin, args) {
|
|
1305
1385
|
const s = spawnable(bin, args);
|
|
1306
1386
|
return s ? { ...s, shell: false } : { file: [`"${bin}"`, ...args].join(" "), args: [], shell: true };
|
|
1307
1387
|
}
|
|
1388
|
+
function runHeadless(bin, args, o) {
|
|
1389
|
+
return new Promise((resolve) => {
|
|
1390
|
+
let out = [];
|
|
1391
|
+
let outLen = 0;
|
|
1392
|
+
let tooBig = false;
|
|
1393
|
+
let errTail = "";
|
|
1394
|
+
let timedOut = false;
|
|
1395
|
+
let settled = false;
|
|
1396
|
+
let child;
|
|
1397
|
+
const c = spawnable(bin, args);
|
|
1398
|
+
if (!c) {
|
|
1399
|
+
resolve({ error: shimNote(bin) });
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
try {
|
|
1403
|
+
child = spawn(c.file, c.args, {
|
|
1404
|
+
cwd: o.cwd,
|
|
1405
|
+
env: o.env,
|
|
1406
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1407
|
+
detached: process.platform !== "win32",
|
|
1408
|
+
windowsHide: true
|
|
1409
|
+
});
|
|
1410
|
+
} catch (e) {
|
|
1411
|
+
resolve({ error: e.message });
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
const kill = (sig) => {
|
|
1415
|
+
try {
|
|
1416
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
1417
|
+
else child.kill(sig);
|
|
1418
|
+
} catch {
|
|
1419
|
+
try {
|
|
1420
|
+
child.kill(sig);
|
|
1421
|
+
} catch {
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
};
|
|
1425
|
+
const keep = (line) => {
|
|
1426
|
+
if (!o.keep?.(line)) return;
|
|
1427
|
+
out = [Buffer.from(line)];
|
|
1428
|
+
outLen = line.length;
|
|
1429
|
+
};
|
|
1430
|
+
let partial = "";
|
|
1431
|
+
child.stdout?.on("data", (b) => {
|
|
1432
|
+
if (o.onLine) {
|
|
1433
|
+
const chunk = partial + b.toString("utf8");
|
|
1434
|
+
const lines = chunk.split("\n");
|
|
1435
|
+
partial = lines.pop();
|
|
1436
|
+
for (const line of lines) {
|
|
1437
|
+
if (!line.trim()) continue;
|
|
1438
|
+
try {
|
|
1439
|
+
o.onLine(line);
|
|
1440
|
+
} catch {
|
|
1441
|
+
}
|
|
1442
|
+
keep(line);
|
|
1443
|
+
}
|
|
1444
|
+
if (partial.length > OUT_CAP) partial = "";
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
if (tooBig) return;
|
|
1448
|
+
if (outLen + b.length > OUT_CAP) {
|
|
1449
|
+
tooBig = true;
|
|
1450
|
+
out = [];
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
out.push(b);
|
|
1454
|
+
outLen += b.length;
|
|
1455
|
+
});
|
|
1456
|
+
child.stderr?.on("data", (b) => {
|
|
1457
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
1458
|
+
});
|
|
1459
|
+
const timer = setTimeout(() => {
|
|
1460
|
+
timedOut = true;
|
|
1461
|
+
kill("SIGTERM");
|
|
1462
|
+
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
1463
|
+
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
1464
|
+
const result = (code, signal) => ({
|
|
1465
|
+
code,
|
|
1466
|
+
signal,
|
|
1467
|
+
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
1468
|
+
tooBig,
|
|
1469
|
+
stderr: errTail,
|
|
1470
|
+
timedOut
|
|
1471
|
+
});
|
|
1472
|
+
const finish = (r) => {
|
|
1473
|
+
if (settled) return;
|
|
1474
|
+
settled = true;
|
|
1475
|
+
clearTimeout(timer);
|
|
1476
|
+
resolve(r);
|
|
1477
|
+
};
|
|
1478
|
+
child.on("error", (e) => finish({ error: e.message }));
|
|
1479
|
+
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
1480
|
+
child.on("close", (code, signal) => {
|
|
1481
|
+
if (o.onLine && partial.trim()) {
|
|
1482
|
+
try {
|
|
1483
|
+
o.onLine(partial);
|
|
1484
|
+
} catch {
|
|
1485
|
+
}
|
|
1486
|
+
keep(partial);
|
|
1487
|
+
partial = "";
|
|
1488
|
+
}
|
|
1489
|
+
finish(result(code, signal));
|
|
1490
|
+
});
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1308
1493
|
|
|
1309
1494
|
// ../core/src/adapters/antigravity.ts
|
|
1310
1495
|
import {
|
|
@@ -1594,6 +1779,50 @@ function antigravityFacts(file, opts) {
|
|
|
1594
1779
|
return out;
|
|
1595
1780
|
}
|
|
1596
1781
|
|
|
1782
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1783
|
+
var claude_code_control_exports = {};
|
|
1784
|
+
__export(claude_code_control_exports, {
|
|
1785
|
+
CLAUDE_MODES: () => CLAUDE_MODES,
|
|
1786
|
+
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1787
|
+
MODE_RANK: () => MODE_RANK,
|
|
1788
|
+
NO_HOME_SETTINGS: () => NO_HOME_SETTINGS,
|
|
1789
|
+
ceilingLevel: () => ceilingLevel,
|
|
1790
|
+
claudeArgs: () => claudeArgs,
|
|
1791
|
+
claudeEnv: () => claudeEnv,
|
|
1792
|
+
claudeLogin: () => claudeLogin2,
|
|
1793
|
+
cmpVersion: () => cmpVersion,
|
|
1794
|
+
defaultLevel: () => defaultLevel,
|
|
1795
|
+
detectCaps: () => detectCaps,
|
|
1796
|
+
findClaude: () => findClaude,
|
|
1797
|
+
findTranscript: () => findTranscript,
|
|
1798
|
+
findTranscripts: () => findTranscripts,
|
|
1799
|
+
homeSettings: () => homeSettings,
|
|
1800
|
+
hookMode: () => hookMode,
|
|
1801
|
+
isUuid: () => isUuid,
|
|
1802
|
+
liveProcess: () => liveProcess,
|
|
1803
|
+
liveRecord: () => liveRecord,
|
|
1804
|
+
liveRecords: () => liveRecords,
|
|
1805
|
+
loginCommand: () => loginCommand,
|
|
1806
|
+
loginHint: () => loginHint,
|
|
1807
|
+
modeFlags: () => modeFlags,
|
|
1808
|
+
modeRank: () => modeRank,
|
|
1809
|
+
offeredLevels: () => offeredLevels,
|
|
1810
|
+
outputArgs: () => outputArgs,
|
|
1811
|
+
parseAuthStatus: () => parseAuthStatus,
|
|
1812
|
+
parseCaps: () => parseCaps,
|
|
1813
|
+
parseResult: () => parseResult,
|
|
1814
|
+
peerClass: () => peerClass,
|
|
1815
|
+
recentlyWritten: () => recentlyWritten,
|
|
1816
|
+
relaunchMode: () => relaunchMode,
|
|
1817
|
+
resolveHome: () => resolveHome,
|
|
1818
|
+
runClaude: () => runClaude,
|
|
1819
|
+
sessionFolder: () => sessionFolder
|
|
1820
|
+
});
|
|
1821
|
+
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1822
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
1823
|
+
import os2 from "node:os";
|
|
1824
|
+
import path7 from "node:path";
|
|
1825
|
+
|
|
1597
1826
|
// ../core/src/control/verify.ts
|
|
1598
1827
|
var GRANT_MAX_MS = 24 * 36e5;
|
|
1599
1828
|
var SKEW_MS = 5 * 6e4;
|
|
@@ -1604,10 +1833,10 @@ var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
|
1604
1833
|
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
1605
1834
|
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
1606
1835
|
var FIELDS = {
|
|
1607
|
-
prompt: ["text", "files"],
|
|
1836
|
+
prompt: ["text", "files", "reply_to"],
|
|
1608
1837
|
"permission.answer": ["for", "decision"],
|
|
1609
1838
|
cancel: [],
|
|
1610
|
-
start: ["text", "cwd", "files", "permission", "model"],
|
|
1839
|
+
start: ["text", "cwd", "files", "permission", "model", "reply_to"],
|
|
1611
1840
|
"key.add": ["key", "name"],
|
|
1612
1841
|
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
1613
1842
|
};
|
|
@@ -1615,9 +1844,454 @@ var POOL_FIELDS = FIELDS.pool;
|
|
|
1615
1844
|
var enc = new TextEncoder();
|
|
1616
1845
|
var dec = new TextDecoder();
|
|
1617
1846
|
|
|
1847
|
+
// ../core/src/adapters/claude-code-control.ts
|
|
1848
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1849
|
+
var LIVE_WRITE_MS = 9e4;
|
|
1850
|
+
var cmpVersion = (a, b) => {
|
|
1851
|
+
const pa = a.split(".").map(Number);
|
|
1852
|
+
const pb = b.split(".").map(Number);
|
|
1853
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++)
|
|
1854
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1855
|
+
return 0;
|
|
1856
|
+
};
|
|
1857
|
+
function findClaude(env = process.env, home = os2.homedir()) {
|
|
1858
|
+
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1859
|
+
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1860
|
+
for (const d of String(env.PATH || "").split(path7.delimiter)) {
|
|
1861
|
+
if (!d || !path7.isAbsolute(d)) continue;
|
|
1862
|
+
for (const n of names) if (isExe(path7.join(d, n))) return path7.join(d, n);
|
|
1863
|
+
}
|
|
1864
|
+
for (const p of [
|
|
1865
|
+
path7.join(home, ".claude", "local", "claude"),
|
|
1866
|
+
path7.join(home, ".local", "bin", "claude"),
|
|
1867
|
+
"/opt/homebrew/bin/claude",
|
|
1868
|
+
"/usr/local/bin/claude"
|
|
1869
|
+
])
|
|
1870
|
+
if (isExe(p)) return p;
|
|
1871
|
+
const app = path7.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1872
|
+
let versions = [];
|
|
1873
|
+
try {
|
|
1874
|
+
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1875
|
+
} catch {
|
|
1876
|
+
}
|
|
1877
|
+
for (const v of versions) {
|
|
1878
|
+
const p = path7.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1879
|
+
if (isExe(p)) return p;
|
|
1880
|
+
}
|
|
1881
|
+
return null;
|
|
1882
|
+
}
|
|
1883
|
+
function optionBlock(help, flag) {
|
|
1884
|
+
const lines = help.split("\n");
|
|
1885
|
+
const re = new RegExp(`^\\s{0,4}(?:-\\w, )?${flag.replace(/-/g, "\\-")}(?![\\w-])`);
|
|
1886
|
+
const start = lines.findIndex((l) => re.test(l));
|
|
1887
|
+
if (start < 0) return null;
|
|
1888
|
+
const out = [lines[start]];
|
|
1889
|
+
for (let i = start + 1; i < lines.length && lines[i]?.trim() && !/^\s{0,4}-/.test(lines[i]); i++)
|
|
1890
|
+
out.push(lines[i]);
|
|
1891
|
+
return out.join(" ");
|
|
1892
|
+
}
|
|
1893
|
+
function parseCaps(help) {
|
|
1894
|
+
const pm = optionBlock(help, "--permission-mode");
|
|
1895
|
+
const pp = optionBlock(help, "--permission-prompts");
|
|
1896
|
+
return {
|
|
1897
|
+
hasMode: !!pm,
|
|
1898
|
+
modes: pm ? [...pm.matchAll(/"([A-Za-z]+)"/g)].map((m) => m[1]) : [],
|
|
1899
|
+
promptsNone: !!pp && /"none"/.test(pp),
|
|
1900
|
+
fork: !!optionBlock(help, "--fork-session"),
|
|
1901
|
+
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
var LEVEL_MODE = {
|
|
1905
|
+
read: "plan",
|
|
1906
|
+
edits: "acceptEdits",
|
|
1907
|
+
auto: "auto"
|
|
1908
|
+
};
|
|
1909
|
+
function modeFlags(level, caps) {
|
|
1910
|
+
if (level === "inherit") return { args: [] };
|
|
1911
|
+
if (caps.unread)
|
|
1912
|
+
return { error: `this machine couldn't read its Claude Code's options (${caps.unread}); try again in a minute` };
|
|
1913
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
1914
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
1915
|
+
if (level !== "safe") {
|
|
1916
|
+
const m = LEVEL_MODE[level];
|
|
1917
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
1918
|
+
return { args: ["--permission-mode", m] };
|
|
1919
|
+
}
|
|
1920
|
+
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1921
|
+
const args = [];
|
|
1922
|
+
if (caps.hasMode)
|
|
1923
|
+
args.push(
|
|
1924
|
+
"--permission-mode",
|
|
1925
|
+
caps.modes.includes("manual") && !caps.modes.includes("default") ? "manual" : "default"
|
|
1926
|
+
);
|
|
1927
|
+
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1928
|
+
return { args };
|
|
1929
|
+
}
|
|
1930
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
1931
|
+
function offeredLevels(mode, caps) {
|
|
1932
|
+
if (caps.unread) return [];
|
|
1933
|
+
return START_PERMISSIONS.filter(
|
|
1934
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
var defaultLevel = (mode) => mode;
|
|
1938
|
+
function outputArgs(caps) {
|
|
1939
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
1940
|
+
}
|
|
1941
|
+
function claudeArgs(o) {
|
|
1942
|
+
const mf = modeFlags(o.level, o.caps);
|
|
1943
|
+
if ("error" in mf) return mf;
|
|
1944
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
1945
|
+
return {
|
|
1946
|
+
args: [
|
|
1947
|
+
"-p",
|
|
1948
|
+
...outputArgs(o.caps),
|
|
1949
|
+
...mf.args,
|
|
1950
|
+
...o.model ? ["--model", o.model] : [],
|
|
1951
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
1952
|
+
...where,
|
|
1953
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
1954
|
+
]
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
function claudeEnv(configDir, env = process.env) {
|
|
1958
|
+
const e = withoutLineage(env);
|
|
1959
|
+
for (const k of [
|
|
1960
|
+
"CLAUDECODE",
|
|
1961
|
+
"CLAUDE_CODE_SESSION_ID",
|
|
1962
|
+
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
1963
|
+
"CLAUDE_CODE_ENTRYPOINT",
|
|
1964
|
+
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
1965
|
+
"CLAUDE_CODE_SSE_PORT"
|
|
1966
|
+
])
|
|
1967
|
+
delete e[k];
|
|
1968
|
+
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1969
|
+
if (configDir !== void 0) {
|
|
1970
|
+
if (path7.resolve(configDir) === path7.resolve(os2.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1971
|
+
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1972
|
+
}
|
|
1973
|
+
return e;
|
|
1974
|
+
}
|
|
1975
|
+
function parseAuthStatus(stdout) {
|
|
1976
|
+
const text = String(stdout || "").trim();
|
|
1977
|
+
const start = text.indexOf("{");
|
|
1978
|
+
if (start < 0) return { loggedIn: null };
|
|
1979
|
+
try {
|
|
1980
|
+
const j = JSON.parse(text.slice(start));
|
|
1981
|
+
if (typeof j.loggedIn !== "boolean") return { loggedIn: null };
|
|
1982
|
+
return {
|
|
1983
|
+
loggedIn: j.loggedIn,
|
|
1984
|
+
...typeof j.authMethod === "string" ? { method: j.authMethod } : {},
|
|
1985
|
+
...typeof j.subscriptionType === "string" ? { subscription: j.subscriptionType } : {}
|
|
1986
|
+
};
|
|
1987
|
+
} catch {
|
|
1988
|
+
return { loggedIn: null };
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function claudeLogin2(bin, configDir, env = process.env) {
|
|
1992
|
+
const c = probe(bin, ["auth", "status", "--json"]);
|
|
1993
|
+
return new Promise((resolve) => {
|
|
1994
|
+
execFile(
|
|
1995
|
+
c.file,
|
|
1996
|
+
c.args,
|
|
1997
|
+
{ encoding: "utf8", timeout: 2e4, env: claudeEnv(configDir, env), maxBuffer: 256 * 1024, shell: c.shell },
|
|
1998
|
+
// `auth status` may exit non-zero when signed out; its JSON still says so.
|
|
1999
|
+
(_err, stdout) => resolve(parseAuthStatus(stdout))
|
|
2000
|
+
);
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
function loginCommand(configDir) {
|
|
2004
|
+
const home = os2.homedir();
|
|
2005
|
+
const isDefault = !configDir || path7.resolve(configDir) === path7.resolve(home, ".claude");
|
|
2006
|
+
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
2007
|
+
}
|
|
2008
|
+
function loginHint(configDir, platform = process.platform) {
|
|
2009
|
+
const shown = configDir ? configDir.replace(os2.homedir(), "~") : "~/.claude";
|
|
2010
|
+
const login = loginCommand(configDir);
|
|
2011
|
+
if (platform === "darwin")
|
|
2012
|
+
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.`;
|
|
2013
|
+
return `Claude Code (${shown}) isn't signed in on this machine: run ${login} there.`;
|
|
2014
|
+
}
|
|
2015
|
+
function detectCaps(bin, timeoutMs = 3e4) {
|
|
2016
|
+
const c = probe(bin, ["--help"]);
|
|
2017
|
+
try {
|
|
2018
|
+
return parseCaps(
|
|
2019
|
+
execFileSync3(c.file, c.args, {
|
|
2020
|
+
encoding: "utf8",
|
|
2021
|
+
timeout: timeoutMs,
|
|
2022
|
+
env: claudeEnv(),
|
|
2023
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2024
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
2025
|
+
shell: c.shell
|
|
2026
|
+
})
|
|
2027
|
+
);
|
|
2028
|
+
} catch (e) {
|
|
2029
|
+
const err = e;
|
|
2030
|
+
const caps = parseCaps(String(err?.stdout || ""));
|
|
2031
|
+
if (caps.hasMode) return caps;
|
|
2032
|
+
const why = err?.code === "ETIMEDOUT" ? `timed out after ${timeoutMs / 1e3} s` : "failed";
|
|
2033
|
+
return { ...caps, unread: `\`${path7.basename(bin)} --help\` ${why}` };
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
function transcriptIn(session, dir) {
|
|
2037
|
+
let projects = [];
|
|
2038
|
+
try {
|
|
2039
|
+
projects = readdirSync4(path7.join(dir, "projects"), { withFileTypes: true });
|
|
2040
|
+
} catch {
|
|
2041
|
+
return null;
|
|
2042
|
+
}
|
|
2043
|
+
for (const p of projects) {
|
|
2044
|
+
if (!p.isDirectory()) continue;
|
|
2045
|
+
const file = path7.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
2046
|
+
try {
|
|
2047
|
+
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
2048
|
+
} catch {
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
return null;
|
|
2052
|
+
}
|
|
2053
|
+
function findTranscript(session, dirs) {
|
|
2054
|
+
if (!UUID_RE.test(session)) return null;
|
|
2055
|
+
for (const dir of dirs) {
|
|
2056
|
+
const t = transcriptIn(session, dir);
|
|
2057
|
+
if (t) return t;
|
|
2058
|
+
}
|
|
2059
|
+
return null;
|
|
2060
|
+
}
|
|
2061
|
+
function findTranscripts(session, dirs) {
|
|
2062
|
+
if (!UUID_RE.test(session)) return [];
|
|
2063
|
+
const out = [];
|
|
2064
|
+
for (const dir of dirs) {
|
|
2065
|
+
const t = transcriptIn(session, dir);
|
|
2066
|
+
if (t) out.push(t);
|
|
2067
|
+
}
|
|
2068
|
+
return out;
|
|
2069
|
+
}
|
|
2070
|
+
var homeName = (t) => path7.basename(t.configDir);
|
|
2071
|
+
function resolveHome(session, dirs) {
|
|
2072
|
+
const all = findTranscripts(session, dirs);
|
|
2073
|
+
if (!all.length) return null;
|
|
2074
|
+
if (all.length === 1) return { transcript: all[0] };
|
|
2075
|
+
const live = all.filter((t) => liveRecords(session, t.configDir).length > 0);
|
|
2076
|
+
if (live.length === 1) return { transcript: live[0] };
|
|
2077
|
+
return {
|
|
2078
|
+
ambiguous: [...new Set(all.map(homeName))].sort(),
|
|
2079
|
+
live: [...new Set(live.map(homeName))].sort()
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
var projectName = (cwd) => cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
2083
|
+
function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
2084
|
+
let first = null;
|
|
2085
|
+
let count = 0;
|
|
2086
|
+
let fd;
|
|
2087
|
+
const look = (line) => {
|
|
2088
|
+
if (line.indexOf('"cwd"') < 0) return null;
|
|
2089
|
+
let j;
|
|
2090
|
+
try {
|
|
2091
|
+
j = JSON.parse(line.toString("utf8"));
|
|
2092
|
+
} catch {
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
2095
|
+
if (!j || typeof j.cwd !== "string" || !path7.isAbsolute(j.cwd)) return null;
|
|
2096
|
+
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
2097
|
+
first ??= j.cwd;
|
|
2098
|
+
count++;
|
|
2099
|
+
return null;
|
|
2100
|
+
};
|
|
2101
|
+
try {
|
|
2102
|
+
fd = openSync2(t.file, "r");
|
|
2103
|
+
const buf = Buffer.alloc(256 * 1024);
|
|
2104
|
+
let pos = 0;
|
|
2105
|
+
let rest = Buffer.alloc(0);
|
|
2106
|
+
let skipping = false;
|
|
2107
|
+
while (pos < cap2 && count < 50) {
|
|
2108
|
+
const n = readSync2(fd, buf, 0, buf.length, pos);
|
|
2109
|
+
if (!n) {
|
|
2110
|
+
if (!skipping && rest.length) {
|
|
2111
|
+
const hit = look(rest);
|
|
2112
|
+
if (hit) return hit;
|
|
2113
|
+
}
|
|
2114
|
+
break;
|
|
2115
|
+
}
|
|
2116
|
+
pos += n;
|
|
2117
|
+
let chunk = rest.length ? Buffer.concat([rest, buf.subarray(0, n)]) : buf.subarray(0, n);
|
|
2118
|
+
let nl = chunk.indexOf(10);
|
|
2119
|
+
while (nl >= 0) {
|
|
2120
|
+
const line = chunk.subarray(0, nl);
|
|
2121
|
+
chunk = chunk.subarray(nl + 1);
|
|
2122
|
+
if (skipping) skipping = false;
|
|
2123
|
+
else {
|
|
2124
|
+
const hit = look(line);
|
|
2125
|
+
if (hit) return hit;
|
|
2126
|
+
}
|
|
2127
|
+
nl = chunk.indexOf(10);
|
|
2128
|
+
}
|
|
2129
|
+
if (chunk.length > 2 * 1024 * 1024) {
|
|
2130
|
+
skipping = true;
|
|
2131
|
+
rest = Buffer.alloc(0);
|
|
2132
|
+
} else rest = skipping ? Buffer.alloc(0) : Buffer.from(chunk);
|
|
2133
|
+
}
|
|
2134
|
+
} catch {
|
|
2135
|
+
} finally {
|
|
2136
|
+
if (fd !== void 0)
|
|
2137
|
+
try {
|
|
2138
|
+
closeSync2(fd);
|
|
2139
|
+
} catch {
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return first;
|
|
2143
|
+
}
|
|
2144
|
+
function pidAlive(pid) {
|
|
2145
|
+
if (!Number.isSafeInteger(pid) || pid <= 1) return false;
|
|
2146
|
+
try {
|
|
2147
|
+
process.kill(pid, 0);
|
|
2148
|
+
return true;
|
|
2149
|
+
} catch (e) {
|
|
2150
|
+
return e.code === "EPERM";
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
var registryRow = (file, configDir, j) => {
|
|
2154
|
+
const pid = Number(j.pid ?? path7.basename(file).slice(0, -5));
|
|
2155
|
+
const str = (v) => typeof v === "string" && v ? v : null;
|
|
2156
|
+
let keyFile = null;
|
|
2157
|
+
try {
|
|
2158
|
+
const want = new RegExp(`^${pid}\\.[0-9a-f]{64}\\.key$`);
|
|
2159
|
+
const hit = readdirSync4(path7.dirname(file)).find((n) => want.test(n));
|
|
2160
|
+
if (hit) keyFile = path7.join(path7.dirname(file), hit);
|
|
2161
|
+
} catch {
|
|
2162
|
+
}
|
|
2163
|
+
return {
|
|
2164
|
+
configDir,
|
|
2165
|
+
file,
|
|
2166
|
+
pid,
|
|
2167
|
+
socket: str(j.messagingSocketPath),
|
|
2168
|
+
peerProtocol: typeof j.peerProtocol === "number" ? j.peerProtocol : null,
|
|
2169
|
+
procStart: str(j.procStart),
|
|
2170
|
+
status: str(j.status),
|
|
2171
|
+
kind: str(j.kind),
|
|
2172
|
+
entrypoint: str(j.entrypoint),
|
|
2173
|
+
keyFile
|
|
2174
|
+
};
|
|
2175
|
+
};
|
|
2176
|
+
function liveRecords(session, dirs) {
|
|
2177
|
+
const out = [];
|
|
2178
|
+
for (const dir of typeof dirs === "string" ? [dirs] : dirs) {
|
|
2179
|
+
let names = [];
|
|
2180
|
+
try {
|
|
2181
|
+
names = readdirSync4(path7.join(dir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
2182
|
+
} catch {
|
|
2183
|
+
continue;
|
|
2184
|
+
}
|
|
2185
|
+
for (const n of names) {
|
|
2186
|
+
const file = path7.join(dir, "sessions", n);
|
|
2187
|
+
let j;
|
|
2188
|
+
try {
|
|
2189
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2190
|
+
} catch {
|
|
2191
|
+
continue;
|
|
2192
|
+
}
|
|
2193
|
+
if (String(j.sessionId) !== session) continue;
|
|
2194
|
+
const row = registryRow(file, dir, j);
|
|
2195
|
+
if (pidAlive(row.pid)) out.push(row);
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
return out;
|
|
2199
|
+
}
|
|
2200
|
+
function liveRecord(session, dirs) {
|
|
2201
|
+
const all = liveRecords(session, dirs);
|
|
2202
|
+
return all.length === 1 ? all[0] : null;
|
|
2203
|
+
}
|
|
2204
|
+
function liveProcess(session, configDir) {
|
|
2205
|
+
return liveRecords(session, configDir).length > 0;
|
|
2206
|
+
}
|
|
2207
|
+
var MODE_RANK = {
|
|
2208
|
+
plan: 0,
|
|
2209
|
+
default: 1,
|
|
2210
|
+
manual: 1,
|
|
2211
|
+
dontAsk: 1,
|
|
2212
|
+
acceptEdits: 2,
|
|
2213
|
+
auto: 3,
|
|
2214
|
+
bypassPermissions: 4
|
|
2215
|
+
};
|
|
2216
|
+
var CLAUDE_MODES = Object.keys(MODE_RANK);
|
|
2217
|
+
var modeRank = (m) => m && m in MODE_RANK ? MODE_RANK[m] : -1;
|
|
2218
|
+
function hookMode(payload) {
|
|
2219
|
+
if (!payload || typeof payload !== "object") return null;
|
|
2220
|
+
const p = payload;
|
|
2221
|
+
const v = typeof p.permission_mode === "string" ? p.permission_mode : p.permissionMode;
|
|
2222
|
+
return typeof v === "string" && CLAUDE_MODES.includes(v) ? v : null;
|
|
2223
|
+
}
|
|
2224
|
+
function peerClass(mode) {
|
|
2225
|
+
if (mode === "bypassPermissions") return "bypass";
|
|
2226
|
+
if (mode === "default" || mode === "manual" || mode === "dontAsk" || mode === "acceptEdits") return "prompting";
|
|
2227
|
+
return null;
|
|
2228
|
+
}
|
|
2229
|
+
var NO_HOME_SETTINGS = { defaultMode: null, disableAutoMode: false, disableBypass: false };
|
|
2230
|
+
function homeSettings(configDir, home = os2.homedir()) {
|
|
2231
|
+
const file = path7.join(configDir ?? path7.join(home, ".claude"), "settings.json");
|
|
2232
|
+
let j;
|
|
2233
|
+
try {
|
|
2234
|
+
j = JSON.parse(readFileSync5(file, "utf8"));
|
|
2235
|
+
} catch {
|
|
2236
|
+
return { ...NO_HOME_SETTINGS };
|
|
2237
|
+
}
|
|
2238
|
+
if (!j || typeof j !== "object" || Array.isArray(j)) return { ...NO_HOME_SETTINGS };
|
|
2239
|
+
const p = j.permissions && typeof j.permissions === "object" ? j.permissions : {};
|
|
2240
|
+
const off = (k) => j[k] === "disable" || p[k] === "disable";
|
|
2241
|
+
return {
|
|
2242
|
+
defaultMode: typeof p.defaultMode === "string" && CLAUDE_MODES.includes(p.defaultMode) ? p.defaultMode : null,
|
|
2243
|
+
disableAutoMode: off("disableAutoMode"),
|
|
2244
|
+
disableBypass: off("disableBypassPermissionsMode")
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
function relaunchMode(recorded, home) {
|
|
2248
|
+
if (recorded === "plan") return recorded;
|
|
2249
|
+
if ((recorded === "dontAsk" || recorded === "acceptEdits") && modeRank(home.defaultMode) >= modeRank(recorded))
|
|
2250
|
+
return recorded;
|
|
2251
|
+
return null;
|
|
2252
|
+
}
|
|
2253
|
+
function recentlyWritten(file, ourLastEnd, now = Date.now()) {
|
|
2254
|
+
let m;
|
|
2255
|
+
try {
|
|
2256
|
+
m = statSync5(file).mtimeMs;
|
|
2257
|
+
} catch {
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2260
|
+
if (now - m > LIVE_WRITE_MS) return false;
|
|
2261
|
+
return !(ourLastEnd && m <= ourLastEnd + 2e3);
|
|
2262
|
+
}
|
|
2263
|
+
function runClaude(bin, args, o) {
|
|
2264
|
+
return runHeadless(bin, args, { ...o, keep: o.keep ?? ((line) => line.includes('"type":"result"')) });
|
|
2265
|
+
}
|
|
2266
|
+
function parseResult(stdout) {
|
|
2267
|
+
const s = String(stdout || "").trim();
|
|
2268
|
+
if (!s) return null;
|
|
2269
|
+
const pick = (j) => {
|
|
2270
|
+
if (Array.isArray(j)) {
|
|
2271
|
+
for (let i = j.length - 1; i >= 0; i--) if (j[i]?.type === "result") return j[i];
|
|
2272
|
+
return null;
|
|
2273
|
+
}
|
|
2274
|
+
return j && typeof j === "object" ? j : null;
|
|
2275
|
+
};
|
|
2276
|
+
try {
|
|
2277
|
+
return pick(JSON.parse(s));
|
|
2278
|
+
} catch {
|
|
2279
|
+
}
|
|
2280
|
+
const lines = s.split("\n");
|
|
2281
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2282
|
+
try {
|
|
2283
|
+
const j = pick(JSON.parse(lines[i]));
|
|
2284
|
+
if (j && (j.type === "result" || "result" in j)) return j;
|
|
2285
|
+
} catch {
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
return null;
|
|
2289
|
+
}
|
|
2290
|
+
var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
|
|
2291
|
+
|
|
1618
2292
|
// ../core/src/adapters/codex.ts
|
|
1619
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as
|
|
1620
|
-
import
|
|
2293
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2294
|
+
import path8 from "node:path";
|
|
1621
2295
|
|
|
1622
2296
|
// ../core/src/adapters/codex-config.ts
|
|
1623
2297
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
@@ -1763,24 +2437,24 @@ var CODEX_EVENTS = [
|
|
|
1763
2437
|
];
|
|
1764
2438
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1765
2439
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1766
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1767
|
-
var hooksFile2 = (env) =>
|
|
1768
|
-
var configFile2 = (env) =>
|
|
1769
|
-
var sessionsDir = (env) =>
|
|
2440
|
+
var codexHome = (env) => env.CODEX_HOME || path8.join(homeDir(), ".codex");
|
|
2441
|
+
var hooksFile2 = (env) => path8.join(codexHome(env), "hooks.json");
|
|
2442
|
+
var configFile2 = (env) => path8.join(codexHome(env), "config.toml");
|
|
2443
|
+
var sessionsDir = (env) => path8.join(codexHome(env), "sessions");
|
|
1770
2444
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1771
2445
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1772
2446
|
function installNotify(cmd, env) {
|
|
1773
2447
|
const file = configFile2(env);
|
|
1774
2448
|
let text = "";
|
|
1775
2449
|
try {
|
|
1776
|
-
text =
|
|
2450
|
+
text = readFileSync6(file, "utf8");
|
|
1777
2451
|
} catch {
|
|
1778
2452
|
}
|
|
1779
2453
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
1780
2454
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1781
2455
|
const n = readNotify(text);
|
|
1782
2456
|
const write = (next) => {
|
|
1783
|
-
mkdirSync3(
|
|
2457
|
+
mkdirSync3(path8.dirname(file), { recursive: true });
|
|
1784
2458
|
writeFileSync4(file, next);
|
|
1785
2459
|
};
|
|
1786
2460
|
if (n && !n.other) {
|
|
@@ -1819,7 +2493,7 @@ function uninstallNotify(env) {
|
|
|
1819
2493
|
const file = configFile2(env);
|
|
1820
2494
|
let text;
|
|
1821
2495
|
try {
|
|
1822
|
-
text =
|
|
2496
|
+
text = readFileSync6(file, "utf8");
|
|
1823
2497
|
} catch {
|
|
1824
2498
|
return { file, changed: false };
|
|
1825
2499
|
}
|
|
@@ -1842,12 +2516,12 @@ function findRollout(threadId, env = process.env) {
|
|
|
1842
2516
|
const walk = (dir, depth) => {
|
|
1843
2517
|
let names = [];
|
|
1844
2518
|
try {
|
|
1845
|
-
names =
|
|
2519
|
+
names = readdirSync5(dir);
|
|
1846
2520
|
} catch {
|
|
1847
2521
|
return;
|
|
1848
2522
|
}
|
|
1849
2523
|
for (const n of names.sort().reverse()) {
|
|
1850
|
-
const p =
|
|
2524
|
+
const p = path8.join(dir, n);
|
|
1851
2525
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
1852
2526
|
best = p;
|
|
1853
2527
|
return;
|
|
@@ -1891,7 +2565,7 @@ var codex = {
|
|
|
1891
2565
|
];
|
|
1892
2566
|
let text = "";
|
|
1893
2567
|
try {
|
|
1894
|
-
text =
|
|
2568
|
+
text = readFileSync6(configFile2(env), "utf8");
|
|
1895
2569
|
} catch {
|
|
1896
2570
|
}
|
|
1897
2571
|
const n = readNotify(text);
|
|
@@ -2018,7 +2692,7 @@ var codex = {
|
|
|
2018
2692
|
readTranscript(file, fromLine) {
|
|
2019
2693
|
let text = "";
|
|
2020
2694
|
try {
|
|
2021
|
-
text =
|
|
2695
|
+
text = readFileSync6(file, "utf8");
|
|
2022
2696
|
} catch {
|
|
2023
2697
|
return { turns: [], line: fromLine };
|
|
2024
2698
|
}
|
|
@@ -2032,7 +2706,7 @@ var codex = {
|
|
|
2032
2706
|
backfill(sinceMs, env = process.env) {
|
|
2033
2707
|
const rows2 = [];
|
|
2034
2708
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2035
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
2709
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path8.basename(f));
|
|
2036
2710
|
if (!m?.[1]) continue;
|
|
2037
2711
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2038
2712
|
const facts = codexFacts(f);
|
|
@@ -2119,10 +2793,10 @@ function codexFacts(file) {
|
|
|
2119
2793
|
// ../core/src/adapters/devin.ts
|
|
2120
2794
|
import { createHash as createHash3 } from "node:crypto";
|
|
2121
2795
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2122
|
-
import
|
|
2796
|
+
import path9 from "node:path";
|
|
2123
2797
|
|
|
2124
2798
|
// ../core/src/readers/sqlite.ts
|
|
2125
|
-
import { execFileSync as
|
|
2799
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2126
2800
|
import { existsSync as existsSync5 } from "node:fs";
|
|
2127
2801
|
import { createRequire } from "node:module";
|
|
2128
2802
|
function quiet(fn) {
|
|
@@ -2202,7 +2876,7 @@ db.close();
|
|
|
2202
2876
|
process.stdout.write(JSON.stringify(out));`;
|
|
2203
2877
|
function viaChild(file, sql, params, o) {
|
|
2204
2878
|
try {
|
|
2205
|
-
const out =
|
|
2879
|
+
const out = execFileSync4(
|
|
2206
2880
|
process.execPath,
|
|
2207
2881
|
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
2208
2882
|
{
|
|
@@ -2246,7 +2920,7 @@ function viaCli(file, sql, params, o) {
|
|
|
2246
2920
|
const text = inlineParams(sql, params);
|
|
2247
2921
|
if (!text) return null;
|
|
2248
2922
|
try {
|
|
2249
|
-
const out =
|
|
2923
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
2250
2924
|
encoding: "utf8",
|
|
2251
2925
|
timeout: o.timeoutMs + 3e3,
|
|
2252
2926
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2303,11 +2977,11 @@ function foreignEvent(file) {
|
|
|
2303
2977
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2304
2978
|
}
|
|
2305
2979
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2306
|
-
var appData = (env) => env.APPDATA ||
|
|
2307
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2308
|
-
var configFile3 = (env) =>
|
|
2309
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2310
|
-
var sessionsDb = (env) =>
|
|
2980
|
+
var appData = (env) => env.APPDATA || path9.join(homeOf(env), "AppData", "Roaming");
|
|
2981
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin") : path9.join(env.XDG_CONFIG_HOME || path9.join(homeOf(env), ".config"), "devin");
|
|
2982
|
+
var configFile3 = (env) => path9.join(devinConfigDir(env), "config.json");
|
|
2983
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path9.join(appData(env), "devin", "cli") : path9.join(env.XDG_DATA_HOME || path9.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
2984
|
+
var sessionsDb = (env) => path9.join(devinDataDir(env), "sessions.db");
|
|
2311
2985
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2312
2986
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2313
2987
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2479,7 +3153,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2479
3153
|
function sessionFacts(row) {
|
|
2480
3154
|
const out = {};
|
|
2481
3155
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2482
|
-
if (typeof row.working_directory === "string" &&
|
|
3156
|
+
if (typeof row.working_directory === "string" && path9.isAbsolute(row.working_directory))
|
|
2483
3157
|
out.cwd = row.working_directory;
|
|
2484
3158
|
const started = Number(row.created_at);
|
|
2485
3159
|
const last = Number(row.last_activity_at);
|
|
@@ -2524,8 +3198,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2524
3198
|
}
|
|
2525
3199
|
function devinVersion(env = process.env) {
|
|
2526
3200
|
try {
|
|
2527
|
-
const target = readlinkSync(
|
|
2528
|
-
const name =
|
|
3201
|
+
const target = readlinkSync(path9.join(devinDataDir(env), "_versions", "current"));
|
|
3202
|
+
const name = path9.basename(target);
|
|
2529
3203
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2530
3204
|
} catch {
|
|
2531
3205
|
return void 0;
|
|
@@ -2538,9 +3212,9 @@ function devinAccount(env = process.env) {
|
|
|
2538
3212
|
}
|
|
2539
3213
|
|
|
2540
3214
|
// ../core/src/adapters/gemini-cli.ts
|
|
2541
|
-
import { execFileSync as
|
|
2542
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2543
|
-
import
|
|
3215
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3216
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
3217
|
+
import path10 from "node:path";
|
|
2544
3218
|
var NAME5 = "gemini-cli";
|
|
2545
3219
|
var GEMINI_EVENTS = [
|
|
2546
3220
|
"SessionStart",
|
|
@@ -2553,16 +3227,16 @@ var GEMINI_EVENTS = [
|
|
|
2553
3227
|
"SessionEnd"
|
|
2554
3228
|
];
|
|
2555
3229
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2556
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2557
|
-
var settings = (env) =>
|
|
3230
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path10.join(homeDir(), ".gemini");
|
|
3231
|
+
var settings = (env) => path10.join(geminiDir(env), "settings.json");
|
|
2558
3232
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2559
3233
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2560
3234
|
function geminiVersion(platform = process.platform, env = process.env) {
|
|
2561
3235
|
const win = platform === "win32";
|
|
2562
|
-
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [
|
|
3236
|
+
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path10.dirname(process.execPath)], env }) : "gemini";
|
|
2563
3237
|
if (!bin) throw new Error("no gemini on PATH");
|
|
2564
3238
|
const c = probe(bin, ["--version"]);
|
|
2565
|
-
return
|
|
3239
|
+
return execFileSync5(c.file, c.args, {
|
|
2566
3240
|
encoding: "utf8",
|
|
2567
3241
|
timeout: 4e3,
|
|
2568
3242
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -2595,7 +3269,7 @@ var geminiCli = {
|
|
|
2595
3269
|
name: NAME5,
|
|
2596
3270
|
events: GEMINI_EVENTS,
|
|
2597
3271
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2598
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
3272
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path10.join(geminiDir(env), "tmp")),
|
|
2599
3273
|
configFiles: (env = process.env) => [settings(env)],
|
|
2600
3274
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2601
3275
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -2693,7 +3367,7 @@ var geminiCli = {
|
|
|
2693
3367
|
readTranscript(file, fromLine) {
|
|
2694
3368
|
let text = "";
|
|
2695
3369
|
try {
|
|
2696
|
-
text =
|
|
3370
|
+
text = readFileSync7(file, "utf8");
|
|
2697
3371
|
} catch {
|
|
2698
3372
|
return { turns: [], line: fromLine };
|
|
2699
3373
|
}
|
|
@@ -2733,12 +3407,12 @@ var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
|
2733
3407
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
2734
3408
|
|
|
2735
3409
|
// ../core/src/config.ts
|
|
2736
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
2737
|
-
import
|
|
3410
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3411
|
+
import path11 from "node:path";
|
|
2738
3412
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2739
3413
|
function readConfig(file = configFile()) {
|
|
2740
3414
|
try {
|
|
2741
|
-
const j = JSON.parse(
|
|
3415
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
2742
3416
|
return {
|
|
2743
3417
|
...DEFAULT_CONFIG,
|
|
2744
3418
|
...j,
|
|
@@ -2750,7 +3424,7 @@ function readConfig(file = configFile()) {
|
|
|
2750
3424
|
}
|
|
2751
3425
|
}
|
|
2752
3426
|
function writeConfig(c, file = configFile()) {
|
|
2753
|
-
mkdirSync4(
|
|
3427
|
+
mkdirSync4(path11.dirname(file), { recursive: true, mode: 448 });
|
|
2754
3428
|
writeFileSync5(file, `${JSON.stringify(c, null, 2)}
|
|
2755
3429
|
`, { mode: 384 });
|
|
2756
3430
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
@@ -2814,38 +3488,22 @@ function makeEvent(a, ctx) {
|
|
|
2814
3488
|
};
|
|
2815
3489
|
}
|
|
2816
3490
|
|
|
2817
|
-
// ../core/src/launched-by.ts
|
|
2818
|
-
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
2819
|
-
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
2820
|
-
function launchedBy(env) {
|
|
2821
|
-
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
2822
|
-
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
2823
|
-
}
|
|
2824
|
-
function readHook(adapter, input) {
|
|
2825
|
-
const r = adapter.fromHook(input);
|
|
2826
|
-
if (r && !r.session.parent_id) {
|
|
2827
|
-
const launcher = launchedBy(input.env);
|
|
2828
|
-
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
2829
|
-
}
|
|
2830
|
-
return r;
|
|
2831
|
-
}
|
|
2832
|
-
|
|
2833
3491
|
// ../core/src/outbox.ts
|
|
2834
3492
|
import {
|
|
2835
3493
|
appendFileSync,
|
|
2836
|
-
closeSync as
|
|
3494
|
+
closeSync as closeSync3,
|
|
2837
3495
|
existsSync as existsSync9,
|
|
2838
3496
|
mkdirSync as mkdirSync5,
|
|
2839
|
-
openSync as
|
|
2840
|
-
readdirSync as
|
|
2841
|
-
readFileSync as
|
|
2842
|
-
readSync as
|
|
3497
|
+
openSync as openSync3,
|
|
3498
|
+
readdirSync as readdirSync6,
|
|
3499
|
+
readFileSync as readFileSync9,
|
|
3500
|
+
readSync as readSync3,
|
|
2843
3501
|
renameSync,
|
|
2844
|
-
statSync as
|
|
3502
|
+
statSync as statSync6,
|
|
2845
3503
|
unlinkSync as unlinkSync3,
|
|
2846
3504
|
writeFileSync as writeFileSync6
|
|
2847
3505
|
} from "node:fs";
|
|
2848
|
-
import
|
|
3506
|
+
import path12 from "node:path";
|
|
2849
3507
|
var READ_CHUNK = 1024 * 1024;
|
|
2850
3508
|
var READ_MAX = 8 * 1024 * 1024;
|
|
2851
3509
|
var Outbox = class {
|
|
@@ -2854,13 +3512,13 @@ var Outbox = class {
|
|
|
2854
3512
|
}
|
|
2855
3513
|
root;
|
|
2856
3514
|
dir(harness) {
|
|
2857
|
-
return
|
|
3515
|
+
return path12.join(this.root, "outbox", safeId(harness));
|
|
2858
3516
|
}
|
|
2859
3517
|
file(ref) {
|
|
2860
|
-
return
|
|
3518
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2861
3519
|
}
|
|
2862
3520
|
seqFile(ref) {
|
|
2863
|
-
return
|
|
3521
|
+
return path12.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2864
3522
|
}
|
|
2865
3523
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2866
3524
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -2868,19 +3526,19 @@ var Outbox = class {
|
|
|
2868
3526
|
const f = this.seqFile(ref);
|
|
2869
3527
|
let n = -1;
|
|
2870
3528
|
try {
|
|
2871
|
-
const t =
|
|
3529
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
2872
3530
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
2873
3531
|
} catch {
|
|
2874
3532
|
}
|
|
2875
3533
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
2876
|
-
mkdirSync5(
|
|
3534
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
2877
3535
|
writeFileSync6(f, String(n + 1), { mode: 384 });
|
|
2878
3536
|
return n;
|
|
2879
3537
|
}
|
|
2880
3538
|
lastSeqInFile(ref) {
|
|
2881
3539
|
const f = this.file(ref);
|
|
2882
3540
|
if (!existsSync9(f)) return -1;
|
|
2883
|
-
const lines =
|
|
3541
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
2884
3542
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2885
3543
|
try {
|
|
2886
3544
|
const j = JSON.parse(lines[i]);
|
|
@@ -2899,19 +3557,19 @@ var Outbox = class {
|
|
|
2899
3557
|
/** Every session file, newest first. */
|
|
2900
3558
|
sessions() {
|
|
2901
3559
|
const out = [];
|
|
2902
|
-
const base =
|
|
3560
|
+
const base = path12.join(this.root, "outbox");
|
|
2903
3561
|
if (!existsSync9(base)) return [];
|
|
2904
|
-
for (const h of
|
|
2905
|
-
const d =
|
|
3562
|
+
for (const h of readdirSync6(base)) {
|
|
3563
|
+
const d = path12.join(base, h);
|
|
2906
3564
|
let names = [];
|
|
2907
3565
|
try {
|
|
2908
|
-
names =
|
|
3566
|
+
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
2909
3567
|
} catch {
|
|
2910
3568
|
continue;
|
|
2911
3569
|
}
|
|
2912
3570
|
for (const n of names) {
|
|
2913
3571
|
try {
|
|
2914
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime:
|
|
3572
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path12.join(d, n)).mtimeMs });
|
|
2915
3573
|
} catch {
|
|
2916
3574
|
}
|
|
2917
3575
|
}
|
|
@@ -2927,16 +3585,16 @@ var Outbox = class {
|
|
|
2927
3585
|
read(ref, from, maxEvents = 500) {
|
|
2928
3586
|
const f = this.file(ref);
|
|
2929
3587
|
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2930
|
-
const size =
|
|
3588
|
+
const size = statSync6(f).size;
|
|
2931
3589
|
if (from >= size) return { events: [], offset: from };
|
|
2932
|
-
const fd =
|
|
3590
|
+
const fd = openSync3(f, "r");
|
|
2933
3591
|
try {
|
|
2934
3592
|
const events = [];
|
|
2935
3593
|
let offset = from;
|
|
2936
3594
|
let chunk = READ_CHUNK;
|
|
2937
3595
|
while (events.length < maxEvents && offset < size) {
|
|
2938
3596
|
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
2939
|
-
const n =
|
|
3597
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
2940
3598
|
const text = buf.subarray(0, n).toString("utf8");
|
|
2941
3599
|
let pos = 0;
|
|
2942
3600
|
let used = 0;
|
|
@@ -2959,7 +3617,7 @@ var Outbox = class {
|
|
|
2959
3617
|
}
|
|
2960
3618
|
return { events, offset };
|
|
2961
3619
|
} finally {
|
|
2962
|
-
|
|
3620
|
+
closeSync3(fd);
|
|
2963
3621
|
}
|
|
2964
3622
|
}
|
|
2965
3623
|
/** Files older than `days` are pruned; the cursor files with them. */
|
|
@@ -2969,7 +3627,7 @@ var Outbox = class {
|
|
|
2969
3627
|
for (const ref of this.sessions()) {
|
|
2970
3628
|
const f = this.file(ref);
|
|
2971
3629
|
try {
|
|
2972
|
-
if (
|
|
3630
|
+
if (statSync6(f).mtimeMs < cutoff) {
|
|
2973
3631
|
unlinkSync3(f);
|
|
2974
3632
|
try {
|
|
2975
3633
|
unlinkSync3(this.seqFile(ref));
|
|
@@ -2999,18 +3657,18 @@ var Cursors = class {
|
|
|
2999
3657
|
root;
|
|
3000
3658
|
sink;
|
|
3001
3659
|
file() {
|
|
3002
|
-
return
|
|
3660
|
+
return path12.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
3003
3661
|
}
|
|
3004
3662
|
read() {
|
|
3005
3663
|
try {
|
|
3006
|
-
return JSON.parse(
|
|
3664
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
3007
3665
|
} catch {
|
|
3008
3666
|
return {};
|
|
3009
3667
|
}
|
|
3010
3668
|
}
|
|
3011
3669
|
write(c) {
|
|
3012
3670
|
const f = this.file();
|
|
3013
|
-
mkdirSync5(
|
|
3671
|
+
mkdirSync5(path12.dirname(f), { recursive: true, mode: 448 });
|
|
3014
3672
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
3015
3673
|
writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
|
|
3016
3674
|
renameSync(tmp, f);
|
|
@@ -3023,11 +3681,11 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
3023
3681
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
3024
3682
|
}
|
|
3025
3683
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
3026
|
-
const dir =
|
|
3684
|
+
const dir = path12.join(root, "locks");
|
|
3027
3685
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
3028
|
-
const f =
|
|
3686
|
+
const f = path12.join(dir, `${name}.lock`);
|
|
3029
3687
|
try {
|
|
3030
|
-
if (Date.now() -
|
|
3688
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
3031
3689
|
else return null;
|
|
3032
3690
|
} catch {
|
|
3033
3691
|
}
|
|
@@ -3265,6 +3923,8 @@ var TYPE_TIER = {
|
|
|
3265
3923
|
"subagent.ended": 1,
|
|
3266
3924
|
"context.compacted": 1,
|
|
3267
3925
|
"limits.observed": 1,
|
|
3926
|
+
"delegation.started": 0,
|
|
3927
|
+
"delegation.ended": 0,
|
|
3268
3928
|
native: 3
|
|
3269
3929
|
};
|
|
3270
3930
|
var DATA_ALLOW = {
|
|
@@ -3275,8 +3935,8 @@ var DATA_ALLOW = {
|
|
|
3275
3935
|
"session.forgotten": { 0: [] },
|
|
3276
3936
|
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
3277
3937
|
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3278
|
-
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3279
|
-
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3938
|
+
"turn.transcript": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3939
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at", "injected"] },
|
|
3280
3940
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3281
3941
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3282
3942
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3285,7 +3945,9 @@ var DATA_ALLOW = {
|
|
|
3285
3945
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
3286
3946
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
3287
3947
|
"context.compacted": { 1: ["trigger"] },
|
|
3288
|
-
"limits.observed": { 1: ["windows", "plan", "account_id"] }
|
|
3948
|
+
"limits.observed": { 1: ["windows", "plan", "account_id"] },
|
|
3949
|
+
"delegation.started": { 0: ["ask_id", "via", "harness", "to"], 1: ["text"] },
|
|
3950
|
+
"delegation.ended": { 0: ["ask_id", "to", "ok", "ms", "error", "relayed"], 2: ["reply"] }
|
|
3289
3951
|
};
|
|
3290
3952
|
var SESSION_ALLOW = [
|
|
3291
3953
|
"id",
|
|
@@ -3301,7 +3963,8 @@ var SESSION_ALLOW = [
|
|
|
3301
3963
|
"account_id",
|
|
3302
3964
|
"attended",
|
|
3303
3965
|
"parent_id",
|
|
3304
|
-
"launched_by"
|
|
3966
|
+
"launched_by",
|
|
3967
|
+
"delegated_by"
|
|
3305
3968
|
];
|
|
3306
3969
|
var MAX_SIDE = 2e4;
|
|
3307
3970
|
var MAX_IO = 64 * 1024;
|
|
@@ -3340,6 +4003,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3340
4003
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3341
4004
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3342
4005
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
4006
|
+
if (event.type === "delegation.started" && typeof data.text === "string") data.text = cap(data.text, 200);
|
|
4007
|
+
if (event.type === "delegation.ended" && typeof data.reply === "string") data.reply = cap(data.reply, MAX_SIDE);
|
|
3343
4008
|
const rulesets = [];
|
|
3344
4009
|
let out = { ...event, tier, session, data, privacy: { rulesets, pii } };
|
|
3345
4010
|
if (tier > 0) {
|
|
@@ -3367,7 +4032,7 @@ function shortId(harness, id) {
|
|
|
3367
4032
|
|
|
3368
4033
|
// ../core/src/sinks/file.ts
|
|
3369
4034
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3370
|
-
import
|
|
4035
|
+
import path13 from "node:path";
|
|
3371
4036
|
var FileSink = class {
|
|
3372
4037
|
constructor(name, file, tier, pii) {
|
|
3373
4038
|
this.name = name;
|
|
@@ -3381,7 +4046,7 @@ var FileSink = class {
|
|
|
3381
4046
|
pii;
|
|
3382
4047
|
async send(events) {
|
|
3383
4048
|
try {
|
|
3384
|
-
mkdirSync6(
|
|
4049
|
+
mkdirSync6(path13.dirname(this.file), { recursive: true });
|
|
3385
4050
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3386
4051
|
`);
|
|
3387
4052
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3415,8 +4080,8 @@ var StdoutSink = class {
|
|
|
3415
4080
|
};
|
|
3416
4081
|
|
|
3417
4082
|
// src/control/aliases.ts
|
|
3418
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as
|
|
3419
|
-
import
|
|
4083
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync7, readFileSync as readFileSync10, renameSync as renameSync2, writeFileSync as writeFileSync7 } from "node:fs";
|
|
4084
|
+
import path14 from "node:path";
|
|
3420
4085
|
|
|
3421
4086
|
// src/secrets.ts
|
|
3422
4087
|
import { spawnSync } from "node:child_process";
|
|
@@ -3460,10 +4125,10 @@ function startOptionsOf(v) {
|
|
|
3460
4125
|
}
|
|
3461
4126
|
|
|
3462
4127
|
// src/control/aliases.ts
|
|
3463
|
-
var aliasFile = (controlDir) =>
|
|
4128
|
+
var aliasFile = (controlDir) => path14.join(controlDir, "aliases.json");
|
|
3464
4129
|
function readAliases(file) {
|
|
3465
4130
|
try {
|
|
3466
|
-
const j = JSON.parse(
|
|
4131
|
+
const j = JSON.parse(readFileSync10(file, "utf8"));
|
|
3467
4132
|
if (!j || typeof j !== "object" || Array.isArray(j)) return {};
|
|
3468
4133
|
const out = {};
|
|
3469
4134
|
for (const [k, v] of Object.entries(j)) {
|
|
@@ -3484,12 +4149,12 @@ function receiverIdOf(all, harness, id) {
|
|
|
3484
4149
|
|
|
3485
4150
|
// src/control/local.ts
|
|
3486
4151
|
import net from "node:net";
|
|
3487
|
-
import
|
|
3488
|
-
import
|
|
4152
|
+
import os3 from "node:os";
|
|
4153
|
+
import path15 from "node:path";
|
|
3489
4154
|
function socketPath(state, env = process.env) {
|
|
3490
4155
|
if (env.SESSIONPIPE_SOCKET) return env.SESSIONPIPE_SOCKET;
|
|
3491
|
-
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${
|
|
3492
|
-
return
|
|
4156
|
+
if (process.platform === "win32") return `\\\\.\\pipe\\sessionpipe-control-${os3.userInfo().username}`;
|
|
4157
|
+
return path15.join(state, "control.sock");
|
|
3493
4158
|
}
|
|
3494
4159
|
function ask(sock, req, timeoutMs) {
|
|
3495
4160
|
return new Promise((resolve) => {
|
|
@@ -3525,6 +4190,11 @@ function ask(sock, req, timeoutMs) {
|
|
|
3525
4190
|
async function tellDaemon(state, job, session, cwd, transcript) {
|
|
3526
4191
|
const sock = socketPath(state);
|
|
3527
4192
|
if (process.platform !== "win32" && !existsSync10(sock)) return;
|
|
4193
|
+
let mode = null;
|
|
4194
|
+
try {
|
|
4195
|
+
mode = claude_code_control_exports.hookMode(JSON.parse(job.stdin));
|
|
4196
|
+
} catch {
|
|
4197
|
+
}
|
|
3528
4198
|
await ask(
|
|
3529
4199
|
sock,
|
|
3530
4200
|
{
|
|
@@ -3532,7 +4202,8 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3532
4202
|
session: `${job.harness}:${session}`,
|
|
3533
4203
|
event: job.event,
|
|
3534
4204
|
...cwd ? { cwd } : {},
|
|
3535
|
-
...transcript ? { transcript } : {}
|
|
4205
|
+
...transcript ? { transcript } : {},
|
|
4206
|
+
...mode ? { mode } : {}
|
|
3536
4207
|
},
|
|
3537
4208
|
300
|
|
3538
4209
|
);
|
|
@@ -3540,7 +4211,7 @@ async function tellDaemon(state, job, session, cwd, transcript) {
|
|
|
3540
4211
|
function enqueueJob(state, job) {
|
|
3541
4212
|
const jobs = jobsDir(state);
|
|
3542
4213
|
mkdirSync8(jobs, { recursive: true, mode: 448 });
|
|
3543
|
-
const f =
|
|
4214
|
+
const f = path16.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
3544
4215
|
writeFileSync8(f, JSON.stringify(job), { mode: 384 });
|
|
3545
4216
|
return f;
|
|
3546
4217
|
}
|
|
@@ -3554,9 +4225,9 @@ function releaseJob(claimed) {
|
|
|
3554
4225
|
function log(state, line) {
|
|
3555
4226
|
try {
|
|
3556
4227
|
mkdirSync8(state, { recursive: true, mode: 448 });
|
|
3557
|
-
const f =
|
|
4228
|
+
const f = path16.join(state, "log");
|
|
3558
4229
|
try {
|
|
3559
|
-
if (
|
|
4230
|
+
if (statSync7(f).size > 1024 * 1024) renameSync3(f, `${f}.1`);
|
|
3560
4231
|
} catch {
|
|
3561
4232
|
}
|
|
3562
4233
|
appendFileSync3(f, `${(/* @__PURE__ */ new Date()).toISOString()} ${line}
|
|
@@ -3565,11 +4236,11 @@ function log(state, line) {
|
|
|
3565
4236
|
}
|
|
3566
4237
|
}
|
|
3567
4238
|
function factsState(state) {
|
|
3568
|
-
const file = (h, s) =>
|
|
4239
|
+
const file = (h, s) => path16.join(state, "facts", safeId(h), `${safeId(s)}.json`);
|
|
3569
4240
|
return {
|
|
3570
4241
|
get(h, s) {
|
|
3571
4242
|
try {
|
|
3572
|
-
return JSON.parse(
|
|
4243
|
+
return JSON.parse(readFileSync11(file(h, s), "utf8"));
|
|
3573
4244
|
} catch {
|
|
3574
4245
|
return {};
|
|
3575
4246
|
}
|
|
@@ -3578,7 +4249,7 @@ function factsState(state) {
|
|
|
3578
4249
|
const cur = this.get(h, s);
|
|
3579
4250
|
const next = { ...cur, ...patch };
|
|
3580
4251
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
3581
|
-
mkdirSync8(
|
|
4252
|
+
mkdirSync8(path16.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
3582
4253
|
writeFileSync8(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
3583
4254
|
}
|
|
3584
4255
|
};
|
|
@@ -3595,7 +4266,7 @@ function buildSinks(sinks, o = {}) {
|
|
|
3595
4266
|
const tier = Math.min(s.tier, s.max_tier ?? 3);
|
|
3596
4267
|
if (s.url === "stdout") out.push(new StdoutSink(s.name, tier, !!s.pii));
|
|
3597
4268
|
else if (s.url.startsWith("file:"))
|
|
3598
|
-
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/,
|
|
4269
|
+
out.push(new FileSink(s.name, s.url.slice(5).replace(/^~(?=\/|$)/, os4.homedir()), tier, !!s.pii));
|
|
3599
4270
|
else if (/^https?:\/\//.test(s.url)) out.push(new HttpSink(s, tier));
|
|
3600
4271
|
}
|
|
3601
4272
|
return out;
|
|
@@ -3603,13 +4274,14 @@ function buildSinks(sinks, o = {}) {
|
|
|
3603
4274
|
var PROGRESS_MS = 2e4;
|
|
3604
4275
|
function openTurn(read, o) {
|
|
3605
4276
|
const last = read.turns[read.turns.length - 1];
|
|
3606
|
-
const fresh = (user, at) => ({
|
|
4277
|
+
const fresh = (user, at, injected) => ({
|
|
3607
4278
|
turn: o.base + read.turns.length,
|
|
3608
4279
|
user: user.slice(0, MAX_TURN_SIDE),
|
|
3609
4280
|
assistant: "",
|
|
3610
|
-
at
|
|
4281
|
+
at,
|
|
4282
|
+
...injected ? { injected: true } : {}
|
|
3611
4283
|
});
|
|
3612
|
-
if (read.open) return fresh(read.open.user, read.open.at);
|
|
4284
|
+
if (read.open) return fresh(read.open.user, read.open.at, read.open.injected);
|
|
3613
4285
|
const prompt = o.prompt?.trim();
|
|
3614
4286
|
if (prompt && !isInjected(prompt) && !(last && last.user.includes(prompt.slice(0, 200)))) return fresh(prompt, o.at);
|
|
3615
4287
|
if (!last) return null;
|
|
@@ -3617,6 +4289,7 @@ function openTurn(read, o) {
|
|
|
3617
4289
|
turn: o.base + read.turns.length - 1,
|
|
3618
4290
|
user: last.user.slice(0, MAX_TURN_SIDE),
|
|
3619
4291
|
assistant: capTail(last.assistant, MAX_TURN_SIDE),
|
|
4292
|
+
...last.injected ? { injected: true } : {},
|
|
3620
4293
|
at: last.at
|
|
3621
4294
|
};
|
|
3622
4295
|
}
|
|
@@ -3624,6 +4297,7 @@ var MAX_TURN_SIDE = 2e4;
|
|
|
3624
4297
|
var JOB_OWN_ENV = [
|
|
3625
4298
|
"CLAUDE_CODE_SESSION_ATTENDED",
|
|
3626
4299
|
"CLAUDE_CODE_HOST_SESSION_ID",
|
|
4300
|
+
"SESSIONPIPE_ASKED_BY",
|
|
3627
4301
|
"SESSIONPIPE_CONTROL_JOB",
|
|
3628
4302
|
"SESSIONPIPE_LAUNCHED_BY"
|
|
3629
4303
|
];
|
|
@@ -3651,7 +4325,7 @@ async function runJob(job, opts = {}) {
|
|
|
3651
4325
|
);
|
|
3652
4326
|
return { events: [], delivered: {} };
|
|
3653
4327
|
}
|
|
3654
|
-
const id = receiverIdOf(readAliases(aliasFile(
|
|
4328
|
+
const id = receiverIdOf(readAliases(aliasFile(path16.join(state, "control"))), job.harness, r.session.id) ?? r.session.id;
|
|
3655
4329
|
const claudeEnd = job.harness === "claude-code" && r.events.some((e) => e.type === "session.ended");
|
|
3656
4330
|
if (!claudeEnd) await tellDaemon(state, job, id, r.session.cwd, r.transcript);
|
|
3657
4331
|
const outbox = new Outbox(state);
|
|
@@ -3688,7 +4362,7 @@ async function runJob(job, opts = {}) {
|
|
|
3688
4362
|
learned = rest;
|
|
3689
4363
|
if (harness_version) facts.save(job.harness, id, { harness_version });
|
|
3690
4364
|
facts.save(job.harness, id, { session: { ...mem.session, ...learned } });
|
|
3691
|
-
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd &&
|
|
4365
|
+
if (CWD_FROM_HOOKS.has(job.harness) && r.session.cwd && path16.isAbsolute(r.session.cwd))
|
|
3692
4366
|
facts.save(job.harness, id, { cwd: r.session.cwd });
|
|
3693
4367
|
}
|
|
3694
4368
|
const known = mem.session ?? {};
|
|
@@ -3701,10 +4375,11 @@ async function runJob(job, opts = {}) {
|
|
|
3701
4375
|
...learned.branch ? { branch: learned.branch } : {},
|
|
3702
4376
|
id,
|
|
3703
4377
|
seq: 0,
|
|
3704
|
-
machine: machineName(cfg,
|
|
4378
|
+
machine: machineName(cfg, os4.hostname()),
|
|
3705
4379
|
...cwdRaw ? { cwd: tilde(cwdRaw) } : {},
|
|
3706
4380
|
...r.session.model ? { model: r.session.model } : {},
|
|
3707
|
-
...r.session.launched_by ? { launched_by: r.session.launched_by } : {}
|
|
4381
|
+
...r.session.launched_by ? { launched_by: r.session.launched_by } : {},
|
|
4382
|
+
...r.session.delegated_by ? { delegated_by: r.session.delegated_by } : {}
|
|
3708
4383
|
};
|
|
3709
4384
|
const version = facts.get(job.harness, id).harness_version ?? void 0;
|
|
3710
4385
|
for (const a of r.events) {
|
|
@@ -3758,7 +4433,13 @@ async function runJob(job, opts = {}) {
|
|
|
3758
4433
|
{
|
|
3759
4434
|
type: "turn.transcript",
|
|
3760
4435
|
harnessEvent: job.event,
|
|
3761
|
-
data: {
|
|
4436
|
+
data: {
|
|
4437
|
+
turn: turnNo++,
|
|
4438
|
+
user: t.user,
|
|
4439
|
+
assistant: t.assistant,
|
|
4440
|
+
at: new Date(t.at).toISOString(),
|
|
4441
|
+
...t.injected ? { injected: true } : {}
|
|
4442
|
+
}
|
|
3762
4443
|
},
|
|
3763
4444
|
{
|
|
3764
4445
|
harness: { name: job.harness, ...version ? { version } : {} },
|
|
@@ -3804,7 +4485,7 @@ async function runJob(job, opts = {}) {
|
|
|
3804
4485
|
return { events, delivered };
|
|
3805
4486
|
}
|
|
3806
4487
|
async function drain(state, sinkConfigs, outbox, first) {
|
|
3807
|
-
const more =
|
|
4488
|
+
const more = path16.join(state, "locks", "flush.more");
|
|
3808
4489
|
let release = takeNamedLock(state, "flush");
|
|
3809
4490
|
if (!release) {
|
|
3810
4491
|
try {
|
|
@@ -3832,10 +4513,10 @@ async function drain(state, sinkConfigs, outbox, first) {
|
|
|
3832
4513
|
}
|
|
3833
4514
|
}
|
|
3834
4515
|
var BACKOFF_MS = [2e3, 1e4, 6e4, 5 * 6e4, 30 * 6e4];
|
|
3835
|
-
var backoffFile = (state, sink) =>
|
|
4516
|
+
var backoffFile = (state, sink) => path16.join(state, "backoff", `${safeId(sink)}.json`);
|
|
3836
4517
|
function readBackoff(state, sink) {
|
|
3837
4518
|
try {
|
|
3838
|
-
const b = JSON.parse(
|
|
4519
|
+
const b = JSON.parse(readFileSync11(backoffFile(state, sink), "utf8"));
|
|
3839
4520
|
return typeof b.until === "number" ? b : null;
|
|
3840
4521
|
} catch {
|
|
3841
4522
|
return null;
|
|
@@ -3848,7 +4529,7 @@ function noteFailure(state, sink, res, prev) {
|
|
|
3848
4529
|
const b = { until: Date.now() + wait, failures, why: `${res.status ?? ""} ${res.error ?? ""}`.trim() };
|
|
3849
4530
|
const f = backoffFile(state, sink);
|
|
3850
4531
|
try {
|
|
3851
|
-
mkdirSync8(
|
|
4532
|
+
mkdirSync8(path16.dirname(f), { recursive: true, mode: 448 });
|
|
3852
4533
|
writeFileSync8(f, JSON.stringify(b), { mode: 384 });
|
|
3853
4534
|
} catch {
|
|
3854
4535
|
}
|
|
@@ -3948,7 +4629,7 @@ async function flush(state, sinkConfigs, outbox, first, o = {}) {
|
|
|
3948
4629
|
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
3949
4630
|
if (!events.length) break;
|
|
3950
4631
|
for (const e of events) {
|
|
3951
|
-
const f = filterEvent(e, sink.tier, sink.pii, { home:
|
|
4632
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os4.homedir() });
|
|
3952
4633
|
if (f) batch.push(f);
|
|
3953
4634
|
}
|
|
3954
4635
|
offset = next;
|
|
@@ -3981,7 +4662,7 @@ function applySinkVerdict(name, res) {
|
|
|
3981
4662
|
} catch {
|
|
3982
4663
|
}
|
|
3983
4664
|
}
|
|
3984
|
-
var jobsDir = (state) =>
|
|
4665
|
+
var jobsDir = (state) => path16.join(state, "jobs");
|
|
3985
4666
|
export {
|
|
3986
4667
|
HttpSink,
|
|
3987
4668
|
buildSinks,
|