sessionpipe 0.1.0 → 0.1.1
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/cli.js +417 -140
- package/dist/cli.js.map +3 -3
- package/dist/hook.js +5 -5
- package/dist/hook.js.map +2 -2
- package/dist/index.js +283 -96
- package/dist/index.js.map +3 -3
- package/dist/run.d.ts +13 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/worker.js +315 -102
- package/dist/worker.js.map +4 -4
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// sessionpipe 0.1.
|
|
2
|
+
// sessionpipe 0.1.1 · Apache-2.0 · https://sessionpipe.org
|
|
3
3
|
|
|
4
4
|
// src/worker.ts
|
|
5
|
-
import { readFileSync as readFileSync9, unlinkSync as
|
|
6
|
-
|
|
7
|
-
// src/run.ts
|
|
8
|
-
import { appendFileSync as appendFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync8, renameSync as renameSync2, statSync as statSync5, writeFileSync as writeFileSync6 } from "node:fs";
|
|
9
|
-
import os2 from "node:os";
|
|
10
|
-
import path11 from "node:path";
|
|
5
|
+
import { readFileSync as readFileSync9, unlinkSync as unlinkSync5 } from "node:fs";
|
|
11
6
|
|
|
12
7
|
// ../core/src/adapters/antigravity.ts
|
|
13
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
existsSync as existsSync2,
|
|
10
|
+
mkdirSync as mkdirSync2,
|
|
11
|
+
readdirSync as readdirSync2,
|
|
12
|
+
readFileSync as readFileSync2,
|
|
13
|
+
realpathSync,
|
|
14
|
+
statSync as statSync2,
|
|
15
|
+
unlinkSync as unlinkSync2,
|
|
16
|
+
writeFileSync as writeFileSync2
|
|
17
|
+
} from "node:fs";
|
|
14
18
|
import path4 from "node:path";
|
|
15
19
|
|
|
16
20
|
// ../core/src/paths.ts
|
|
@@ -237,16 +241,42 @@ function readAntigravity(lines, from) {
|
|
|
237
241
|
}
|
|
238
242
|
|
|
239
243
|
// ../core/src/adapters/claude-shaped.ts
|
|
240
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
244
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
241
245
|
import path3 from "node:path";
|
|
242
246
|
var OURS = /sessionpipe[^"\n]*[\\/]hook\.js"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
|
|
243
247
|
var isOurs = (h) => Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === "string" && OURS.test(x.command));
|
|
244
|
-
function
|
|
248
|
+
function readJsonFile(file) {
|
|
249
|
+
let raw;
|
|
245
250
|
try {
|
|
246
|
-
|
|
251
|
+
raw = readFileSync(file, "utf8");
|
|
247
252
|
} catch {
|
|
248
|
-
return {};
|
|
253
|
+
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true };
|
|
249
254
|
}
|
|
255
|
+
let value;
|
|
256
|
+
try {
|
|
257
|
+
value = JSON.parse(raw);
|
|
258
|
+
} catch {
|
|
259
|
+
const hint = /^\s*\/\/|\/\*/m.test(raw) ? "not plain JSON (it has comments)" : "not plain JSON";
|
|
260
|
+
return { ok: false, existed: true, reason: hint };
|
|
261
|
+
}
|
|
262
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
263
|
+
return { ok: false, existed: true, reason: "not a JSON object" };
|
|
264
|
+
const eol = raw.includes("\r\n") ? "\r\n" : "\n";
|
|
265
|
+
const m = /^( +|\t+)\S/m.exec(raw);
|
|
266
|
+
const indent = m?.[1] ?? " ";
|
|
267
|
+
return {
|
|
268
|
+
ok: true,
|
|
269
|
+
existed: true,
|
|
270
|
+
value,
|
|
271
|
+
raw,
|
|
272
|
+
indent,
|
|
273
|
+
eol,
|
|
274
|
+
finalNewline: /\r?\n$/.test(raw)
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function formatJson(value, f) {
|
|
278
|
+
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
279
|
+
return f.finalNewline ? text + f.eol : text;
|
|
250
280
|
}
|
|
251
281
|
function entryFor(event, harness, cmd, o) {
|
|
252
282
|
const matcher = o.matcherFor(event);
|
|
@@ -256,9 +286,22 @@ function entryFor(event, harness, cmd, o) {
|
|
|
256
286
|
};
|
|
257
287
|
}
|
|
258
288
|
function installClaudeShaped(file, harness, events, cmd, o) {
|
|
259
|
-
const
|
|
289
|
+
const f = readJsonFile(file);
|
|
290
|
+
if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };
|
|
291
|
+
const settings2 = f.value;
|
|
260
292
|
const hooks = settings2.hooks && typeof settings2.hooks === "object" ? settings2.hooks : {};
|
|
261
293
|
let changed = false;
|
|
294
|
+
const want = new Set(events);
|
|
295
|
+
for (const ev of Object.keys(hooks)) {
|
|
296
|
+
if (want.has(ev)) continue;
|
|
297
|
+
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
298
|
+
const kept = list.filter((h) => !isOurs(h));
|
|
299
|
+
if (kept.length !== list.length) {
|
|
300
|
+
changed = true;
|
|
301
|
+
if (kept.length) hooks[ev] = kept;
|
|
302
|
+
else delete hooks[ev];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
262
305
|
for (const ev of events) {
|
|
263
306
|
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
264
307
|
const entry2 = entryFor(ev, harness, cmd, o);
|
|
@@ -277,19 +320,19 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
277
320
|
if (changed) {
|
|
278
321
|
settings2.hooks = hooks;
|
|
279
322
|
mkdirSync(path3.dirname(file), { recursive: true });
|
|
280
|
-
writeFileSync(file,
|
|
281
|
-
`);
|
|
323
|
+
writeFileSync(file, formatJson(settings2, f));
|
|
282
324
|
}
|
|
283
|
-
return { file, changed };
|
|
325
|
+
return { file, changed, created: !f.existed && changed };
|
|
284
326
|
}
|
|
285
|
-
function uninstallClaudeShaped(file, events) {
|
|
327
|
+
function uninstallClaudeShaped(file, events, opts = {}) {
|
|
286
328
|
if (!existsSync(file)) return { file, changed: false };
|
|
287
|
-
const
|
|
288
|
-
|
|
329
|
+
const f = readJsonFile(file);
|
|
330
|
+
if (!f.ok) return { file, changed: false, skipped: true, note: `${f.reason}; not touched` };
|
|
331
|
+
const settings2 = f.value;
|
|
289
332
|
const hooks = settings2.hooks;
|
|
290
333
|
if (!hooks || typeof hooks !== "object") return { file, changed: false };
|
|
291
334
|
let changed = false;
|
|
292
|
-
for (const ev of events) {
|
|
335
|
+
for (const ev of [...events, ...Object.keys(hooks)]) {
|
|
293
336
|
const list = Array.isArray(hooks[ev]) ? hooks[ev] : [];
|
|
294
337
|
const kept = list.filter((h) => !isOurs(h));
|
|
295
338
|
if (kept.length !== list.length) {
|
|
@@ -300,13 +343,17 @@ function uninstallClaudeShaped(file, events) {
|
|
|
300
343
|
}
|
|
301
344
|
if (!changed) return { file, changed: false };
|
|
302
345
|
if (!Object.keys(hooks).length) delete settings2.hooks;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
346
|
+
if (opts.created && !Object.keys(settings2).length) {
|
|
347
|
+
unlinkSync(file);
|
|
348
|
+
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
349
|
+
}
|
|
350
|
+
const next = formatJson(settings2, f);
|
|
351
|
+
if (next !== f.raw) writeFileSync(file, next);
|
|
306
352
|
return { file, changed: true };
|
|
307
353
|
}
|
|
308
354
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
309
|
-
const
|
|
355
|
+
const f = readJsonFile(file);
|
|
356
|
+
const hooks = f.ok ? f.value.hooks ?? {} : {};
|
|
310
357
|
let present = 0;
|
|
311
358
|
let current = 0;
|
|
312
359
|
for (const ev of events) {
|
|
@@ -315,6 +362,9 @@ function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
|
315
362
|
if (mine.length) present++;
|
|
316
363
|
if (mine.length === 1 && JSON.stringify(mine[0]) === JSON.stringify(entryFor(ev, harness, cmd, o))) current++;
|
|
317
364
|
}
|
|
365
|
+
for (const ev of Object.keys(hooks))
|
|
366
|
+
if (!events.includes(ev) && (Array.isArray(hooks[ev]) ? hooks[ev] : []).some((h) => isOurs(h)))
|
|
367
|
+
return "stale";
|
|
318
368
|
if (!present) return "missing";
|
|
319
369
|
return current === events.length ? "current" : "stale";
|
|
320
370
|
}
|
|
@@ -329,10 +379,13 @@ var projectsDir = (env) => path4.join(gemini(env), "config", "projects");
|
|
|
329
379
|
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path4.join(gemini(env), d));
|
|
330
380
|
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
331
381
|
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
332
|
-
function entry(cmd) {
|
|
382
|
+
function entry(cmd, lean) {
|
|
333
383
|
const handler = (e) => ({ type: "command", command: cmd([NAME, e]).command, timeout: 5 });
|
|
334
384
|
const out = { enabled: true };
|
|
335
|
-
for (const e of AG_EVENTS)
|
|
385
|
+
for (const e of AG_EVENTS) {
|
|
386
|
+
if (lean && e === "PreToolUse") continue;
|
|
387
|
+
out[e] = /Tool/.test(e) ? [{ matcher: "*", hooks: [handler(e)] }] : [handler(e)];
|
|
388
|
+
}
|
|
336
389
|
return out;
|
|
337
390
|
}
|
|
338
391
|
var antigravity = {
|
|
@@ -340,34 +393,49 @@ var antigravity = {
|
|
|
340
393
|
events: AG_EVENTS,
|
|
341
394
|
detect: (env = process.env) => existsSync2(path4.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync2(d)),
|
|
342
395
|
configFiles: (env = process.env) => [hooksFile(env)],
|
|
343
|
-
install(cmd, env = process.env) {
|
|
396
|
+
install(cmd, env = process.env, opts = {}) {
|
|
344
397
|
const file = hooksFile(env);
|
|
345
|
-
const
|
|
346
|
-
|
|
398
|
+
const f = readJsonFile(file);
|
|
399
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
400
|
+
const hooks = f.value;
|
|
401
|
+
const e = entry(cmd, opts.lean);
|
|
347
402
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
348
403
|
hooks[HOOK_NAME] = e;
|
|
349
404
|
mkdirSync2(path4.dirname(file), { recursive: true });
|
|
350
|
-
writeFileSync2(file,
|
|
351
|
-
`);
|
|
405
|
+
writeFileSync2(file, formatJson(hooks, f));
|
|
352
406
|
return [
|
|
353
|
-
{
|
|
407
|
+
{
|
|
408
|
+
file,
|
|
409
|
+
changed: true,
|
|
410
|
+
created: !f.existed,
|
|
411
|
+
note: "hooks.json is read when a conversation starts; open ones report after a restart"
|
|
412
|
+
}
|
|
354
413
|
];
|
|
355
414
|
},
|
|
356
|
-
uninstall(env = process.env) {
|
|
415
|
+
uninstall(env = process.env, opts = {}) {
|
|
357
416
|
const file = hooksFile(env);
|
|
358
417
|
if (!existsSync2(file)) return [{ file, changed: false }];
|
|
359
|
-
const
|
|
418
|
+
const f = readJsonFile(file);
|
|
419
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
420
|
+
const hooks = f.value;
|
|
360
421
|
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
361
422
|
delete hooks[HOOK_NAME];
|
|
362
|
-
|
|
363
|
-
|
|
423
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
424
|
+
unlinkSync2(file);
|
|
425
|
+
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
426
|
+
}
|
|
427
|
+
writeFileSync2(file, formatJson(hooks, f));
|
|
364
428
|
return [{ file, changed: true }];
|
|
365
429
|
},
|
|
366
|
-
installed(cmd, env = process.env) {
|
|
430
|
+
installed(cmd, env = process.env, opts = {}) {
|
|
367
431
|
const file = hooksFile(env);
|
|
368
|
-
const
|
|
432
|
+
const f = readJsonFile(file);
|
|
433
|
+
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
369
434
|
return [
|
|
370
|
-
{
|
|
435
|
+
{
|
|
436
|
+
file,
|
|
437
|
+
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd, opts.lean)) ? "current" : "stale"
|
|
438
|
+
}
|
|
371
439
|
];
|
|
372
440
|
},
|
|
373
441
|
fromHook(input) {
|
|
@@ -633,8 +701,11 @@ var CLAUDE_EVENTS = [
|
|
|
633
701
|
"SubagentStop",
|
|
634
702
|
"PreCompact",
|
|
635
703
|
"PostCompact",
|
|
636
|
-
"SessionEnd"
|
|
704
|
+
"SessionEnd",
|
|
705
|
+
"StopFailure"
|
|
637
706
|
];
|
|
707
|
+
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
708
|
+
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
638
709
|
var OPTS = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
639
710
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
640
711
|
function claudeDirs(env = process.env, home = HOME) {
|
|
@@ -666,11 +737,13 @@ var claudeCode = {
|
|
|
666
737
|
events: CLAUDE_EVENTS,
|
|
667
738
|
detect: (env = process.env) => claudeDirs(env).some((d) => existsSync3(d)),
|
|
668
739
|
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
669
|
-
install: (cmd, env = process.env) => claudeDirs(env).map((d) => installClaudeShaped(settingsIn(d), NAME2,
|
|
670
|
-
uninstall: (env = process.env) => claudeDirs(env).map(
|
|
671
|
-
|
|
740
|
+
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => installClaudeShaped(settingsIn(d), NAME2, eventsFor(opts.lean), cmd, OPTS)),
|
|
741
|
+
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map(
|
|
742
|
+
(d) => uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, { created: opts.created?.includes(settingsIn(d)) })
|
|
743
|
+
),
|
|
744
|
+
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
672
745
|
file: settingsIn(d),
|
|
673
|
-
state: installedClaudeShaped(settingsIn(d), NAME2,
|
|
746
|
+
state: installedClaudeShaped(settingsIn(d), NAME2, eventsFor(opts.lean), cmd, OPTS)
|
|
674
747
|
})),
|
|
675
748
|
fromHook(input) {
|
|
676
749
|
const event = input.argv[1] ?? "";
|
|
@@ -761,6 +834,15 @@ var claudeCode = {
|
|
|
761
834
|
case "Stop":
|
|
762
835
|
events.push(ev("turn.ended", { turn_id, reason: "stop" }));
|
|
763
836
|
break;
|
|
837
|
+
case "StopFailure":
|
|
838
|
+
events.push(
|
|
839
|
+
ev("turn.ended", {
|
|
840
|
+
turn_id,
|
|
841
|
+
reason: "error",
|
|
842
|
+
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
break;
|
|
764
846
|
case "SubagentStart":
|
|
765
847
|
events.push(
|
|
766
848
|
ev("subagent.started", {
|
|
@@ -1272,6 +1354,7 @@ var CODEX_EVENTS = [
|
|
|
1272
1354
|
"SessionEnd"
|
|
1273
1355
|
];
|
|
1274
1356
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1357
|
+
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1275
1358
|
var codexHome = (env) => env.CODEX_HOME || path6.join(HOME, ".codex");
|
|
1276
1359
|
var hooksFile2 = (env) => path6.join(codexHome(env), "hooks.json");
|
|
1277
1360
|
var configFile2 = (env) => path6.join(codexHome(env), "config.toml");
|
|
@@ -1320,7 +1403,8 @@ function installNotify(cmd, env) {
|
|
|
1320
1403
|
return {
|
|
1321
1404
|
file,
|
|
1322
1405
|
changed: false,
|
|
1323
|
-
|
|
1406
|
+
skipped: true,
|
|
1407
|
+
note: `notify belongs to ${(n.argv?.[0] ?? n.raw).replace(/^.*\//, "").slice(0, 40)}; left alone. Codex will report nothing until you approve the hooks: start \`codex\` and accept the hook review`
|
|
1324
1408
|
};
|
|
1325
1409
|
}
|
|
1326
1410
|
write(withNotify(text, line));
|
|
@@ -1377,14 +1461,21 @@ var codex = {
|
|
|
1377
1461
|
events: CODEX_EVENTS,
|
|
1378
1462
|
detect: (env = process.env) => existsSync4(codexHome(env)),
|
|
1379
1463
|
configFiles: (env = process.env) => [hooksFile2(env), configFile2(env)],
|
|
1380
|
-
install: (cmd, env = process.env) => [
|
|
1381
|
-
installClaudeShaped(hooksFile2(env), NAME3,
|
|
1464
|
+
install: (cmd, env = process.env, opts = {}) => [
|
|
1465
|
+
installClaudeShaped(hooksFile2(env), NAME3, eventsFor2(opts.lean), cmd, OPTS2),
|
|
1382
1466
|
installNotify(cmd, env)
|
|
1383
1467
|
],
|
|
1384
|
-
uninstall: (env = process.env) => [
|
|
1385
|
-
|
|
1468
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
1469
|
+
uninstallClaudeShaped(hooksFile2(env), CODEX_EVENTS, { created: opts.created?.includes(hooksFile2(env)) }),
|
|
1470
|
+
uninstallNotify(env)
|
|
1471
|
+
],
|
|
1472
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
1386
1473
|
const out = [
|
|
1387
|
-
{
|
|
1474
|
+
{
|
|
1475
|
+
file: hooksFile2(env),
|
|
1476
|
+
state: installedClaudeShaped(hooksFile2(env), NAME3, eventsFor2(opts.lean), cmd, OPTS2),
|
|
1477
|
+
note: "Codex runs non-managed hooks only after the review in `codex` (/hooks); until then nothing is reported unless the notify fallback is ours"
|
|
1478
|
+
}
|
|
1388
1479
|
];
|
|
1389
1480
|
let text = "";
|
|
1390
1481
|
try {
|
|
@@ -1572,6 +1663,7 @@ function codexFacts(file) {
|
|
|
1572
1663
|
}
|
|
1573
1664
|
|
|
1574
1665
|
// ../core/src/adapters/gemini-cli.ts
|
|
1666
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
1575
1667
|
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
1576
1668
|
import path7 from "node:path";
|
|
1577
1669
|
var NAME4 = "gemini-cli";
|
|
@@ -1589,17 +1681,61 @@ var OPTS3 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
|
1589
1681
|
var geminiDir = (env) => env.GEMINI_CLI_HOME || path7.join(HOME, ".gemini");
|
|
1590
1682
|
var settings = (env) => path7.join(geminiDir(env), "settings.json");
|
|
1591
1683
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1684
|
+
var eventsFor3 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
1685
|
+
function geminiHooksActive(settingsFile) {
|
|
1686
|
+
let tools;
|
|
1687
|
+
try {
|
|
1688
|
+
tools = JSON.parse(readFileSync5(settingsFile, "utf8")).tools;
|
|
1689
|
+
} catch {
|
|
1690
|
+
}
|
|
1691
|
+
if (tools?.enableHooks === true) return { active: true };
|
|
1692
|
+
if (tools?.enableHooks === false)
|
|
1693
|
+
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
1694
|
+
let version = "";
|
|
1695
|
+
try {
|
|
1696
|
+
version = execFileSync3("gemini", ["--version"], {
|
|
1697
|
+
encoding: "utf8",
|
|
1698
|
+
timeout: 4e3,
|
|
1699
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1700
|
+
}).trim();
|
|
1701
|
+
} catch {
|
|
1702
|
+
return {
|
|
1703
|
+
active: true,
|
|
1704
|
+
note: "could not run `gemini --version`; hooks need tools.enableHooks on versions before 0.46"
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
const m = /(\d+)\.(\d+)/.exec(version);
|
|
1708
|
+
if (m && (Number(m[1]) > 0 || Number(m[2]) >= 46)) return { active: true };
|
|
1709
|
+
return {
|
|
1710
|
+
active: false,
|
|
1711
|
+
note: `inactive: Gemini CLI ${version || "?"} runs hooks only with "tools": {"enableHooks": true} in settings.json`
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1592
1714
|
var geminiCli = {
|
|
1593
1715
|
name: NAME4,
|
|
1594
1716
|
events: GEMINI_EVENTS,
|
|
1595
1717
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
1596
1718
|
detect: (env = process.env) => existsSync5(settings(env)) || existsSync5(path7.join(geminiDir(env), "tmp")),
|
|
1597
1719
|
configFiles: (env = process.env) => [settings(env)],
|
|
1598
|
-
install: (cmd, env = process.env
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1720
|
+
install: (cmd, env = process.env, opts = {}) => {
|
|
1721
|
+
const r = installClaudeShaped(settings(env), NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1722
|
+
const a = geminiHooksActive(settings(env));
|
|
1723
|
+
return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join("; ") } : r];
|
|
1724
|
+
},
|
|
1725
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
1726
|
+
uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) })
|
|
1602
1727
|
],
|
|
1728
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
1729
|
+
const state = installedClaudeShaped(settings(env), NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
1730
|
+
const a = geminiHooksActive(settings(env));
|
|
1731
|
+
return [
|
|
1732
|
+
{
|
|
1733
|
+
file: settings(env),
|
|
1734
|
+
state: state === "current" && !a.active ? "inactive" : state,
|
|
1735
|
+
...a.note ? { note: a.note } : {}
|
|
1736
|
+
}
|
|
1737
|
+
];
|
|
1738
|
+
},
|
|
1603
1739
|
fromHook(input) {
|
|
1604
1740
|
const event = input.argv[1] ?? "";
|
|
1605
1741
|
let s = {};
|
|
@@ -1810,7 +1946,7 @@ import {
|
|
|
1810
1946
|
readSync as readSync2,
|
|
1811
1947
|
renameSync,
|
|
1812
1948
|
statSync as statSync4,
|
|
1813
|
-
unlinkSync,
|
|
1949
|
+
unlinkSync as unlinkSync3,
|
|
1814
1950
|
writeFileSync as writeFileSync5
|
|
1815
1951
|
} from "node:fs";
|
|
1816
1952
|
import path9 from "node:path";
|
|
@@ -1838,8 +1974,8 @@ var Outbox = class {
|
|
|
1838
1974
|
} catch {
|
|
1839
1975
|
n = this.lastSeqInFile(ref) + 1;
|
|
1840
1976
|
}
|
|
1841
|
-
mkdirSync5(path9.dirname(f), { recursive: true });
|
|
1842
|
-
writeFileSync5(f, String(n + 1));
|
|
1977
|
+
mkdirSync5(path9.dirname(f), { recursive: true, mode: 448 });
|
|
1978
|
+
writeFileSync5(f, String(n + 1), { mode: 384 });
|
|
1843
1979
|
return n;
|
|
1844
1980
|
}
|
|
1845
1981
|
lastSeqInFile(ref) {
|
|
@@ -1857,9 +1993,9 @@ var Outbox = class {
|
|
|
1857
1993
|
}
|
|
1858
1994
|
append(ref, events) {
|
|
1859
1995
|
if (!events.length) return;
|
|
1860
|
-
mkdirSync5(this.dir(ref.harness), { recursive: true });
|
|
1996
|
+
mkdirSync5(this.dir(ref.harness), { recursive: true, mode: 448 });
|
|
1861
1997
|
appendFileSync(this.file(ref), `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
1862
|
-
|
|
1998
|
+
`, { mode: 384 });
|
|
1863
1999
|
}
|
|
1864
2000
|
/** Every session file, newest first. */
|
|
1865
2001
|
sessions() {
|
|
@@ -1923,9 +2059,9 @@ var Outbox = class {
|
|
|
1923
2059
|
const f = this.file(ref);
|
|
1924
2060
|
try {
|
|
1925
2061
|
if (statSync4(f).mtimeMs < cutoff) {
|
|
1926
|
-
|
|
2062
|
+
unlinkSync3(f);
|
|
1927
2063
|
try {
|
|
1928
|
-
|
|
2064
|
+
unlinkSync3(this.seqFile(ref));
|
|
1929
2065
|
} catch {
|
|
1930
2066
|
}
|
|
1931
2067
|
n++;
|
|
@@ -1938,7 +2074,7 @@ var Outbox = class {
|
|
|
1938
2074
|
delete(ref) {
|
|
1939
2075
|
for (const f of [this.file(ref), this.seqFile(ref)]) {
|
|
1940
2076
|
try {
|
|
1941
|
-
|
|
2077
|
+
unlinkSync3(f);
|
|
1942
2078
|
} catch {
|
|
1943
2079
|
}
|
|
1944
2080
|
}
|
|
@@ -1963,9 +2099,9 @@ var Cursors = class {
|
|
|
1963
2099
|
}
|
|
1964
2100
|
write(c) {
|
|
1965
2101
|
const f = this.file();
|
|
1966
|
-
mkdirSync5(path9.dirname(f), { recursive: true });
|
|
2102
|
+
mkdirSync5(path9.dirname(f), { recursive: true, mode: 448 });
|
|
1967
2103
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
1968
|
-
writeFileSync5(tmp, JSON.stringify(c));
|
|
2104
|
+
writeFileSync5(tmp, JSON.stringify(c), { mode: 384 });
|
|
1969
2105
|
renameSync(tmp, f);
|
|
1970
2106
|
}
|
|
1971
2107
|
key(ref) {
|
|
@@ -1974,10 +2110,10 @@ var Cursors = class {
|
|
|
1974
2110
|
};
|
|
1975
2111
|
function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
1976
2112
|
const dir = path9.join(root, "locks");
|
|
1977
|
-
mkdirSync5(dir, { recursive: true });
|
|
2113
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
1978
2114
|
const f = path9.join(dir, `${safeId(ref.harness)}-${safeId(ref.session)}.lock`);
|
|
1979
2115
|
try {
|
|
1980
|
-
if (Date.now() - statSync4(f).mtimeMs > staleMs)
|
|
2116
|
+
if (Date.now() - statSync4(f).mtimeMs > staleMs) unlinkSync3(f);
|
|
1981
2117
|
else return null;
|
|
1982
2118
|
} catch {
|
|
1983
2119
|
}
|
|
@@ -1988,7 +2124,7 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
1988
2124
|
}
|
|
1989
2125
|
return () => {
|
|
1990
2126
|
try {
|
|
1991
|
-
|
|
2127
|
+
unlinkSync3(f);
|
|
1992
2128
|
} catch {
|
|
1993
2129
|
}
|
|
1994
2130
|
};
|
|
@@ -2124,7 +2260,7 @@ function redactAssignments(text) {
|
|
|
2124
2260
|
function isSecretName(name) {
|
|
2125
2261
|
if (/^[A-Z0-9_]+_KEY$/.test(name)) return true;
|
|
2126
2262
|
const flat = name.toLowerCase().replace(/[_-]/g, "");
|
|
2127
|
-
return /(?:pass(?:word|wd|phrase|code)
|
|
2263
|
+
return /(?:pass(?:word|wd|phrase|code)|pwd|secret|secretkey|secretaccesskey|clientsecret|apikey|accesskey|accesstoken|authtoken|refreshtoken|privatekey|token|credential|creds)s?$/.test(
|
|
2128
2264
|
flat
|
|
2129
2265
|
);
|
|
2130
2266
|
}
|
|
@@ -2213,7 +2349,7 @@ var DATA_ALLOW = {
|
|
|
2213
2349
|
"session.backfill": { 0: ["started_at", "last_at", "turns"] },
|
|
2214
2350
|
"session.forgotten": { 0: [] },
|
|
2215
2351
|
"turn.started": { 0: ["turn_id"], 1: ["prompt_chars"] },
|
|
2216
|
-
"turn.ended": { 0: ["turn_id", "reason", "ms"] },
|
|
2352
|
+
"turn.ended": { 0: ["turn_id", "reason", "ms"], 1: ["error"] },
|
|
2217
2353
|
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
2218
2354
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
2219
2355
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
@@ -2334,6 +2470,21 @@ var StdoutSink = class {
|
|
|
2334
2470
|
}
|
|
2335
2471
|
};
|
|
2336
2472
|
|
|
2473
|
+
// src/run.ts
|
|
2474
|
+
import {
|
|
2475
|
+
appendFileSync as appendFileSync3,
|
|
2476
|
+
existsSync as existsSync8,
|
|
2477
|
+
mkdirSync as mkdirSync7,
|
|
2478
|
+
readdirSync as readdirSync6,
|
|
2479
|
+
readFileSync as readFileSync8,
|
|
2480
|
+
renameSync as renameSync2,
|
|
2481
|
+
statSync as statSync5,
|
|
2482
|
+
unlinkSync as unlinkSync4,
|
|
2483
|
+
writeFileSync as writeFileSync6
|
|
2484
|
+
} from "node:fs";
|
|
2485
|
+
import os2 from "node:os";
|
|
2486
|
+
import path11 from "node:path";
|
|
2487
|
+
|
|
2337
2488
|
// src/http-sink.ts
|
|
2338
2489
|
import { createHmac, randomUUID } from "node:crypto";
|
|
2339
2490
|
var TIMEOUT_MS = 15e3;
|
|
@@ -2420,9 +2571,41 @@ var HttpSink = class {
|
|
|
2420
2571
|
};
|
|
2421
2572
|
|
|
2422
2573
|
// src/run.ts
|
|
2574
|
+
function enqueueJob(state, job2) {
|
|
2575
|
+
const jobs = jobsDir(state);
|
|
2576
|
+
mkdirSync7(jobs, { recursive: true, mode: 448 });
|
|
2577
|
+
const f = path11.join(jobs, `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
2578
|
+
writeFileSync6(f, JSON.stringify(job2), { mode: 384 });
|
|
2579
|
+
return f;
|
|
2580
|
+
}
|
|
2581
|
+
async function sweepJobs(state, olderThanMs = 3e3, limit = 20) {
|
|
2582
|
+
const jobs = jobsDir(state);
|
|
2583
|
+
let names = [];
|
|
2584
|
+
try {
|
|
2585
|
+
names = readdirSync6(jobs).filter((n) => n.endsWith(".json"));
|
|
2586
|
+
} catch {
|
|
2587
|
+
return 0;
|
|
2588
|
+
}
|
|
2589
|
+
let ran = 0;
|
|
2590
|
+
for (const n of names.sort().slice(0, limit)) {
|
|
2591
|
+
const f = path11.join(jobs, n);
|
|
2592
|
+
let job2 = null;
|
|
2593
|
+
try {
|
|
2594
|
+
if (Date.now() - statSync5(f).mtimeMs < olderThanMs) continue;
|
|
2595
|
+
job2 = JSON.parse(readFileSync8(f, "utf8"));
|
|
2596
|
+
unlinkSync4(f);
|
|
2597
|
+
} catch {
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
await runJob(job2, { state }).catch(() => {
|
|
2601
|
+
});
|
|
2602
|
+
ran++;
|
|
2603
|
+
}
|
|
2604
|
+
return ran;
|
|
2605
|
+
}
|
|
2423
2606
|
function log(state, line) {
|
|
2424
2607
|
try {
|
|
2425
|
-
mkdirSync7(state, { recursive: true });
|
|
2608
|
+
mkdirSync7(state, { recursive: true, mode: 448 });
|
|
2426
2609
|
const f = path11.join(state, "log");
|
|
2427
2610
|
try {
|
|
2428
2611
|
if (statSync5(f).size > 1024 * 1024) renameSync2(f, `${f}.1`);
|
|
@@ -2447,8 +2630,8 @@ function factsState(state) {
|
|
|
2447
2630
|
const cur = this.get(h, s);
|
|
2448
2631
|
const next = { ...cur, ...patch };
|
|
2449
2632
|
if (JSON.stringify(next) === JSON.stringify(cur)) return;
|
|
2450
|
-
mkdirSync7(path11.dirname(file(h, s)), { recursive: true });
|
|
2451
|
-
writeFileSync6(file(h, s), JSON.stringify(next));
|
|
2633
|
+
mkdirSync7(path11.dirname(file(h, s)), { recursive: true, mode: 448 });
|
|
2634
|
+
writeFileSync6(file(h, s), JSON.stringify(next), { mode: 384 });
|
|
2452
2635
|
}
|
|
2453
2636
|
};
|
|
2454
2637
|
}
|
|
@@ -2473,12 +2656,14 @@ async function runJob(job2, opts = {}) {
|
|
|
2473
2656
|
if (!r || !r.session.id) return { events: [], delivered: {} };
|
|
2474
2657
|
const outbox = new Outbox(state);
|
|
2475
2658
|
const ref = { harness: job2.harness, session: r.session.id };
|
|
2476
|
-
|
|
2659
|
+
let release = takeLock(state, ref);
|
|
2660
|
+
for (let wait = 50; !release && wait <= 3200; wait *= 2) {
|
|
2661
|
+
await new Promise((res) => setTimeout(res, wait));
|
|
2662
|
+
release = takeLock(state, ref);
|
|
2663
|
+
}
|
|
2477
2664
|
if (!release) {
|
|
2478
|
-
log(state, `${job2.harness} ${job2.event}: session ${r.session.id} locked, re-queued`);
|
|
2479
|
-
|
|
2480
|
-
mkdirSync7(jobs, { recursive: true });
|
|
2481
|
-
writeFileSync6(path11.join(jobs, `${Date.now().toString(36)}-requeue.json`), JSON.stringify(job2));
|
|
2665
|
+
log(state, `${job2.harness} ${job2.event}: session ${r.session.id} locked for 6 s, re-queued`);
|
|
2666
|
+
enqueueJob(state, job2);
|
|
2482
2667
|
return { events: [], delivered: {} };
|
|
2483
2668
|
}
|
|
2484
2669
|
try {
|
|
@@ -2542,7 +2727,10 @@ async function runJob(job2, opts = {}) {
|
|
|
2542
2727
|
if (read.turns.length)
|
|
2543
2728
|
facts.save(job2.harness, r.session.id, { transcript_line: read.line, transcript_turns: turnNo });
|
|
2544
2729
|
}
|
|
2545
|
-
outbox.append(
|
|
2730
|
+
outbox.append(
|
|
2731
|
+
ref,
|
|
2732
|
+
events.map((e) => ({ ...e, session: redactDeep(e.session), data: redactDeep(e.data) }))
|
|
2733
|
+
);
|
|
2546
2734
|
log(
|
|
2547
2735
|
state,
|
|
2548
2736
|
`${job2.harness} ${job2.event}: ${events.map((e) => e.type).join(",") || "nothing"} (${r.session.id.slice(0, 8)})`
|
|
@@ -2568,34 +2756,57 @@ async function flush(state, sinkConfigs, outbox, first) {
|
|
|
2568
2756
|
const cursors = new Cursors(state, sink.name);
|
|
2569
2757
|
const c = cursors.read();
|
|
2570
2758
|
let sent = 0;
|
|
2759
|
+
let stalled = false;
|
|
2760
|
+
let batch = [];
|
|
2761
|
+
let pending = {};
|
|
2762
|
+
const send = async () => {
|
|
2763
|
+
if (!batch.length) {
|
|
2764
|
+
Object.assign(c, pending);
|
|
2765
|
+
pending = {};
|
|
2766
|
+
cursors.write(c);
|
|
2767
|
+
return true;
|
|
2768
|
+
}
|
|
2769
|
+
const res = await sink.send(batch);
|
|
2770
|
+
if (!res.ok) {
|
|
2771
|
+
log(state, `sink ${sink.name}: ${res.status ?? ""} ${res.error ?? ""} \u2192 ${res.action ?? "retry"}`);
|
|
2772
|
+
if (res.action === "pause" || res.action === "lower-tier") applySinkVerdict(sink.name, res);
|
|
2773
|
+
if (res.action !== "drop") {
|
|
2774
|
+
batch = [];
|
|
2775
|
+
pending = {};
|
|
2776
|
+
return false;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
sent += batch.length;
|
|
2780
|
+
Object.assign(c, pending);
|
|
2781
|
+
cursors.write(c);
|
|
2782
|
+
batch = [];
|
|
2783
|
+
pending = {};
|
|
2784
|
+
return true;
|
|
2785
|
+
};
|
|
2571
2786
|
for (const ref of refs) {
|
|
2572
|
-
if (sent >= MAX_PER_RUN) break;
|
|
2573
|
-
let offset = c[cursors.key(ref)] ?? 0;
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
const { events, offset: next } = outbox.read(ref, offset,
|
|
2787
|
+
if (stalled || sent >= MAX_PER_RUN) break;
|
|
2788
|
+
let offset = pending[cursors.key(ref)] ?? c[cursors.key(ref)] ?? 0;
|
|
2789
|
+
for (; ; ) {
|
|
2790
|
+
const room = BATCH - batch.length;
|
|
2791
|
+
const { events, offset: next } = outbox.read(ref, offset, room);
|
|
2577
2792
|
if (!events.length) break;
|
|
2578
|
-
const
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
const res = await sink.send(filtered);
|
|
2582
|
-
ok = res.ok;
|
|
2583
|
-
if (!ok) {
|
|
2584
|
-
log(state, `sink ${sink.name}: ${res.status ?? ""} ${res.error ?? ""} \u2192 ${res.action ?? "retry"}`);
|
|
2585
|
-
if (res.action === "pause" || res.action === "lower-tier") applySinkVerdict(sink.name, res);
|
|
2586
|
-
if (res.action === "drop") ok = true;
|
|
2587
|
-
}
|
|
2588
|
-
if (ok) sent += filtered.length;
|
|
2589
|
-
}
|
|
2590
|
-
if (!ok) {
|
|
2591
|
-
stalled = true;
|
|
2592
|
-
break;
|
|
2793
|
+
for (const e of events) {
|
|
2794
|
+
const f = filterEvent(e, sink.tier, sink.pii, { home: os2.homedir() });
|
|
2795
|
+
if (f) batch.push(f);
|
|
2593
2796
|
}
|
|
2594
2797
|
offset = next;
|
|
2595
|
-
|
|
2596
|
-
|
|
2798
|
+
pending[cursors.key(ref)] = offset;
|
|
2799
|
+
if (batch.length >= BATCH) {
|
|
2800
|
+
if (!await send()) {
|
|
2801
|
+
stalled = true;
|
|
2802
|
+
break;
|
|
2803
|
+
}
|
|
2804
|
+
if (sent >= MAX_PER_RUN) break;
|
|
2805
|
+
}
|
|
2597
2806
|
}
|
|
2598
|
-
|
|
2807
|
+
}
|
|
2808
|
+
if (!stalled && (batch.length || Object.keys(pending).length)) {
|
|
2809
|
+
if (!await send()) stalled = true;
|
|
2599
2810
|
}
|
|
2600
2811
|
delivered[sink.name] = sent;
|
|
2601
2812
|
}
|
|
@@ -2612,6 +2823,7 @@ function applySinkVerdict(name, res) {
|
|
|
2612
2823
|
} catch {
|
|
2613
2824
|
}
|
|
2614
2825
|
}
|
|
2826
|
+
var jobsDir = (state) => path11.join(state, "jobs");
|
|
2615
2827
|
|
|
2616
2828
|
// src/worker.ts
|
|
2617
2829
|
var jobFile = process.argv[2];
|
|
@@ -2622,12 +2834,13 @@ try {
|
|
|
2622
2834
|
} catch {
|
|
2623
2835
|
}
|
|
2624
2836
|
try {
|
|
2625
|
-
|
|
2837
|
+
unlinkSync5(jobFile);
|
|
2626
2838
|
} catch {
|
|
2627
2839
|
}
|
|
2628
2840
|
if (!job) process.exit(0);
|
|
2629
2841
|
var deadline = setTimeout(() => process.exit(0), 15e3);
|
|
2630
2842
|
deadline.unref();
|
|
2631
2843
|
runJob(job).catch(() => {
|
|
2844
|
+
}).then(() => sweepJobs(stateDir())).catch(() => {
|
|
2632
2845
|
}).finally(() => process.exit(0));
|
|
2633
2846
|
//# sourceMappingURL=worker.js.map
|