sessionpipe-core 0.9.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/control.js +180 -13
- package/dist/control.js.map +2 -2
- package/dist/index.js +2133 -965
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +443 -34
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +201 -34
- package/dist/schema.js.map +2 -2
- package/dist/service.js +155 -0
- package/dist/service.js.map +7 -0
- package/dist/src/adapters/acp-control.d.ts +87 -0
- package/dist/src/adapters/acp-control.d.ts.map +1 -0
- package/dist/src/adapters/antigravity-control.d.ts +15 -5
- package/dist/src/adapters/antigravity-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +39 -4
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-pool.d.ts +116 -0
- package/dist/src/adapters/claude-code-pool.d.ts.map +1 -0
- package/dist/src/adapters/claude-code.d.ts +20 -1
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/claude-shaped.d.ts +3 -0
- package/dist/src/adapters/claude-shaped.d.ts.map +1 -1
- package/dist/src/adapters/codex-config.d.ts +2 -1
- package/dist/src/adapters/codex-config.d.ts.map +1 -1
- package/dist/src/adapters/codex-control.d.ts +23 -7
- package/dist/src/adapters/codex-control.d.ts.map +1 -1
- package/dist/src/adapters/codex.d.ts +9 -0
- package/dist/src/adapters/codex.d.ts.map +1 -1
- package/dist/src/adapters/gemini-cli.d.ts +3 -0
- package/dist/src/adapters/gemini-cli.d.ts.map +1 -1
- package/dist/src/adapters/headless.d.ts +4 -1
- package/dist/src/adapters/headless.d.ts.map +1 -1
- package/dist/src/adapters/index.d.ts +7 -1
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/adapters/types.d.ts +18 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/control/verify.d.ts +99 -17
- package/dist/src/control/verify.d.ts.map +1 -1
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/launched-by.d.ts +12 -0
- package/dist/src/launched-by.d.ts.map +1 -0
- package/dist/src/paths.d.ts +4 -0
- package/dist/src/paths.d.ts.map +1 -1
- package/dist/src/privacy/filter.d.ts.map +1 -1
- package/dist/src/service/index.d.ts +83 -0
- package/dist/src/service/index.d.ts.map +1 -0
- package/dist/src/types.d.ts +4 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/version.d.ts +39 -0
- package/dist/src/version.d.ts.map +1 -0
- package/dist/version.js +75 -0
- package/dist/version.js.map +7 -0
- package/package.json +10 -2
package/dist/index.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
1
|
+
// sessionpipe-core 0.11.0 · Apache-2.0 · https://sessionpipe.org
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __export = (target, all) => {
|
|
4
4
|
for (var name in all)
|
|
5
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
-
// src/adapters/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
// src/adapters/claude-code-pool.ts
|
|
9
|
+
var claude_code_pool_exports = {};
|
|
10
|
+
__export(claude_code_pool_exports, {
|
|
11
|
+
POOL_ENDPOINTS: () => POOL_ENDPOINTS,
|
|
12
|
+
PoolAuthError: () => PoolAuthError,
|
|
13
|
+
PoolUpstreamError: () => PoolUpstreamError,
|
|
14
|
+
beginLogin: () => beginLogin,
|
|
15
|
+
completeLogin: () => completeLogin,
|
|
16
|
+
fetchProfile: () => fetchProfile,
|
|
17
|
+
fetchUsage: () => fetchUsage,
|
|
18
|
+
importableLogins: () => importableLogins,
|
|
19
|
+
keychainService: () => keychainService,
|
|
20
|
+
parseUserId: () => parseUserId,
|
|
21
|
+
readLimits: () => readLimits,
|
|
22
|
+
refreshCredential: () => refreshCredential,
|
|
23
|
+
upstreamHeaders: () => upstreamHeaders,
|
|
24
|
+
usageLimits: () => usageLimits,
|
|
25
|
+
withAccount: () => withAccount
|
|
26
|
+
});
|
|
27
|
+
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
28
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
29
|
+
import os2 from "node:os";
|
|
30
|
+
import path5 from "node:path";
|
|
20
31
|
|
|
21
32
|
// src/paths.ts
|
|
22
33
|
import os from "node:os";
|
|
@@ -39,8 +50,19 @@ function tilde(p, home = homeDir()) {
|
|
|
39
50
|
if (p === home) return "~";
|
|
40
51
|
return p.startsWith(home + path.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;
|
|
41
52
|
}
|
|
53
|
+
function untilde(p, home = homeDir()) {
|
|
54
|
+
if (p === "~") return home;
|
|
55
|
+
return /^~[\\/]/.test(p) ? path.join(home, p.slice(2)) : p;
|
|
56
|
+
}
|
|
42
57
|
var safeId = (s) => String(s).replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 160);
|
|
43
58
|
|
|
59
|
+
// src/adapters/claude-code.ts
|
|
60
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
61
|
+
import { createHash } from "node:crypto";
|
|
62
|
+
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
63
|
+
import { userInfo } from "node:os";
|
|
64
|
+
import path4 from "node:path";
|
|
65
|
+
|
|
44
66
|
// src/readers/files.ts
|
|
45
67
|
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
46
68
|
import path2 from "node:path";
|
|
@@ -131,6 +153,38 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
131
153
|
return out;
|
|
132
154
|
}
|
|
133
155
|
|
|
156
|
+
// src/readers/git.ts
|
|
157
|
+
import { execFileSync } from "node:child_process";
|
|
158
|
+
var cache = /* @__PURE__ */ new Map();
|
|
159
|
+
function gitFacts(cwd) {
|
|
160
|
+
if (!cwd) return {};
|
|
161
|
+
const hit = cache.get(cwd);
|
|
162
|
+
if (hit) return hit;
|
|
163
|
+
const run = (args) => {
|
|
164
|
+
try {
|
|
165
|
+
return execFileSync("git", ["-C", cwd, ...args], {
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
timeout: 1500,
|
|
168
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
169
|
+
}).trim();
|
|
170
|
+
} catch {
|
|
171
|
+
return "";
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const out = {};
|
|
175
|
+
const repo = run(["remote", "get-url", "origin"]);
|
|
176
|
+
if (repo) out.repo = stripCredentials(repo);
|
|
177
|
+
const branch = run(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
178
|
+
if (branch && branch !== "HEAD") out.branch = branch;
|
|
179
|
+
cache.set(cwd, out);
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
function stripCredentials(url) {
|
|
183
|
+
const ssh = /^(?:ssh:\/\/)?git@([^:/]+)[:/](.+?)(?:\.git)?$/.exec(url);
|
|
184
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
185
|
+
return url.replace(/\/\/[^/@]*@/, "//").replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
186
|
+
}
|
|
187
|
+
|
|
134
188
|
// src/adapters/codex-rollout.ts
|
|
135
189
|
function codexSubagent(meta) {
|
|
136
190
|
if (meta?.type !== "session_meta") return false;
|
|
@@ -290,8 +344,10 @@ function readJsonFile(file) {
|
|
|
290
344
|
try {
|
|
291
345
|
raw = readFileSync(file, "utf8");
|
|
292
346
|
} catch {
|
|
293
|
-
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true };
|
|
347
|
+
return { ok: true, existed: false, value: {}, raw: "", indent: " ", eol: "\n", finalNewline: true, bom: false };
|
|
294
348
|
}
|
|
349
|
+
const bom = raw.charCodeAt(0) === 65279;
|
|
350
|
+
if (bom) raw = raw.slice(1);
|
|
295
351
|
let value;
|
|
296
352
|
try {
|
|
297
353
|
value = JSON.parse(raw);
|
|
@@ -311,12 +367,13 @@ function readJsonFile(file) {
|
|
|
311
367
|
raw,
|
|
312
368
|
indent,
|
|
313
369
|
eol,
|
|
314
|
-
finalNewline: /\r?\n$/.test(raw)
|
|
370
|
+
finalNewline: /\r?\n$/.test(raw),
|
|
371
|
+
bom
|
|
315
372
|
};
|
|
316
373
|
}
|
|
317
374
|
function formatJson(value, f) {
|
|
318
375
|
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
319
|
-
return f.finalNewline ? text + f.eol : text;
|
|
376
|
+
return (f.bom ? "\uFEFF" : "") + (f.finalNewline ? text + f.eol : text);
|
|
320
377
|
}
|
|
321
378
|
function readJson(file) {
|
|
322
379
|
const f = readJsonFile(file);
|
|
@@ -412,74 +469,79 @@ function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
|
412
469
|
return current === events.length ? "current" : "stale";
|
|
413
470
|
}
|
|
414
471
|
|
|
415
|
-
// src/adapters/
|
|
416
|
-
var NAME = "
|
|
417
|
-
var
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
472
|
+
// src/adapters/claude-code.ts
|
|
473
|
+
var NAME = "claude-code";
|
|
474
|
+
var CLAUDE_EVENTS = [
|
|
475
|
+
"SessionStart",
|
|
476
|
+
"UserPromptSubmit",
|
|
477
|
+
"PreToolUse",
|
|
478
|
+
"PostToolUse",
|
|
479
|
+
"PostToolUseFailure",
|
|
480
|
+
"PermissionRequest",
|
|
481
|
+
"Notification",
|
|
482
|
+
"Stop",
|
|
483
|
+
"SubagentStart",
|
|
484
|
+
"SubagentStop",
|
|
485
|
+
"PreCompact",
|
|
486
|
+
"PostCompact",
|
|
487
|
+
"SessionEnd",
|
|
488
|
+
"StopFailure"
|
|
489
|
+
];
|
|
490
|
+
var LEAN_SKIP = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUseFailure"]);
|
|
491
|
+
var eventsFor = (lean) => lean ? CLAUDE_EVENTS.filter((e) => !LEAN_SKIP.has(e)) : [...CLAUDE_EVENTS];
|
|
492
|
+
var OPTS = {
|
|
493
|
+
matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0,
|
|
494
|
+
timeout: 5,
|
|
495
|
+
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
496
|
+
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
497
|
+
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
498
|
+
};
|
|
499
|
+
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
500
|
+
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
501
|
+
const out = [];
|
|
502
|
+
const add = (d) => {
|
|
503
|
+
if (!d) return;
|
|
504
|
+
const r = path4.resolve(d);
|
|
505
|
+
if (!out.includes(r)) out.push(r);
|
|
506
|
+
};
|
|
507
|
+
add(path4.join(home, ".claude"));
|
|
508
|
+
add(env.CLAUDE_CONFIG_DIR);
|
|
509
|
+
try {
|
|
510
|
+
for (const e of readdirSync2(home, { withFileTypes: true })) {
|
|
511
|
+
if (!e.isDirectory() || !/^\.claude[-_.]/.test(e.name)) continue;
|
|
512
|
+
const d = path4.join(home, e.name);
|
|
513
|
+
if (existsSync2(path4.join(d, "projects")) || existsSync2(path4.join(d, "settings.json"))) add(d);
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
return out.filter((d, i) => i === 0 || existsSync2(d));
|
|
432
518
|
}
|
|
433
|
-
var
|
|
519
|
+
var settingsIn = (dir) => path4.join(dir, "settings.json");
|
|
520
|
+
var configDirOf = (transcript) => path4.dirname(path4.dirname(path4.dirname(transcript)));
|
|
521
|
+
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
522
|
+
var endReason = (r) => r === "clear" || r === "logout" || r === "exit" ? r : r === "prompt_input_exit" ? "exit" : "other";
|
|
523
|
+
var notifKind = (t) => t === "permission_prompt" ? "permission" : t === "idle_prompt" ? "idle" : t === "elicitation_dialog" ? "elicitation" : "question";
|
|
524
|
+
var claudeCode = {
|
|
434
525
|
name: NAME,
|
|
435
|
-
events:
|
|
436
|
-
detect: (env = process.env) =>
|
|
437
|
-
configFiles: (env = process.env) =>
|
|
438
|
-
install(cmd, env = process.env) {
|
|
439
|
-
const
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
hooks
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
return
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
}
|
|
455
|
-
];
|
|
456
|
-
},
|
|
457
|
-
uninstall(env = process.env, opts = {}) {
|
|
458
|
-
const file = hooksFile(env);
|
|
459
|
-
if (!existsSync2(file)) return [{ file, changed: false }];
|
|
460
|
-
const f = readJsonFile(file);
|
|
461
|
-
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
462
|
-
const hooks = f.value;
|
|
463
|
-
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
464
|
-
delete hooks[HOOK_NAME];
|
|
465
|
-
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
466
|
-
unlinkSync2(file);
|
|
467
|
-
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
468
|
-
}
|
|
469
|
-
writeFileSync2(file, formatJson(hooks, f));
|
|
470
|
-
return [{ file, changed: true }];
|
|
471
|
-
},
|
|
472
|
-
installed(cmd, env = process.env) {
|
|
473
|
-
const file = hooksFile(env);
|
|
474
|
-
const f = readJsonFile(file);
|
|
475
|
-
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
476
|
-
return [
|
|
477
|
-
{
|
|
478
|
-
file,
|
|
479
|
-
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? "current" : "stale"
|
|
480
|
-
}
|
|
481
|
-
];
|
|
482
|
-
},
|
|
526
|
+
events: CLAUDE_EVENTS,
|
|
527
|
+
detect: (env = process.env) => claudeDirs(env).some((d) => existsSync2(d)),
|
|
528
|
+
configFiles: (env = process.env) => claudeDirs(env).map(settingsIn),
|
|
529
|
+
install: (cmd, env = process.env, opts = {}) => claudeDirs(env).flatMap((d) => {
|
|
530
|
+
const hooks = installClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS);
|
|
531
|
+
const line = opts.statusLine === void 0 ? null : statusLine(settingsIn(d), opts.statusLine, cmd);
|
|
532
|
+
return line ? [hooks, line] : [hooks];
|
|
533
|
+
}),
|
|
534
|
+
uninstall: (env = process.env, opts = {}) => claudeDirs(env).map((d) => {
|
|
535
|
+
const line = statusLine(settingsIn(d), false);
|
|
536
|
+
const hooks = uninstallClaudeShaped(settingsIn(d), CLAUDE_EVENTS, {
|
|
537
|
+
created: opts.created?.includes(settingsIn(d))
|
|
538
|
+
});
|
|
539
|
+
return line && !hooks.changed ? line : hooks;
|
|
540
|
+
}),
|
|
541
|
+
installed: (cmd, env = process.env, opts = {}) => claudeDirs(env).map((d) => ({
|
|
542
|
+
file: settingsIn(d),
|
|
543
|
+
state: installedClaudeShaped(settingsIn(d), NAME, eventsFor(opts.lean), cmd, OPTS)
|
|
544
|
+
})),
|
|
483
545
|
fromHook(input) {
|
|
484
546
|
const event = input.argv[1] ?? "";
|
|
485
547
|
let s = {};
|
|
@@ -488,226 +550,1087 @@ var antigravity = {
|
|
|
488
550
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
489
551
|
} catch {
|
|
490
552
|
}
|
|
491
|
-
const id = s.
|
|
492
|
-
|
|
493
|
-
if (
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
if (typeof s.
|
|
497
|
-
|
|
498
|
-
const
|
|
553
|
+
const id = s.session_id ?? s.sessionId;
|
|
554
|
+
if (!id) return null;
|
|
555
|
+
if (notClaudeCode(String(id), s)) return null;
|
|
556
|
+
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
557
|
+
if (typeof s.model === "string") session.model = s.model;
|
|
558
|
+
if (typeof s.agent_id === "string" && event.startsWith("Subagent") === false && s.agent_id)
|
|
559
|
+
session.parent_id = null;
|
|
560
|
+
const turn_id = typeof s.prompt_id === "string" ? s.prompt_id : void 0;
|
|
561
|
+
const tool = validTool(s.tool_name) ? s.tool_name : void 0;
|
|
562
|
+
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
563
|
+
const ms = typeof s.duration_ms === "number" ? Math.round(s.duration_ms) : void 0;
|
|
564
|
+
const agent_id = typeof s.agent_id === "string" ? s.agent_id : void 0;
|
|
565
|
+
const permissionId = () => `perm-${createHash("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
499
566
|
const ev = (type, data) => ({ type, data: prune(data), harnessEvent: event });
|
|
500
567
|
const events = [];
|
|
501
568
|
switch (event) {
|
|
502
|
-
case "
|
|
503
|
-
|
|
504
|
-
events.push(ev("session.started", { source: "unknown" }));
|
|
505
|
-
events.push(ev("turn.started", {}));
|
|
506
|
-
} else events.push(ev("session.heartbeat", {}));
|
|
569
|
+
case "SessionStart":
|
|
570
|
+
events.push(ev("session.started", { source: pickSource(s.source ?? s.trigger) }));
|
|
507
571
|
break;
|
|
508
|
-
case "
|
|
572
|
+
case "UserPromptSubmit": {
|
|
573
|
+
const prompt = typeof s.prompt === "string" ? s.prompt : void 0;
|
|
574
|
+
events.push(
|
|
575
|
+
ev("turn.started", {
|
|
576
|
+
turn_id,
|
|
577
|
+
prompt_chars: prompt === void 0 ? void 0 : [...prompt].length,
|
|
578
|
+
// Claude Code's hook schema documents `source`
|
|
579
|
+
// (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a
|
|
580
|
+
// payload may omit it while the field rolls out; 2.1.304's payload has no such
|
|
581
|
+
// key at all. Forwarded verbatim, shape-guarded only: the list is the harness's
|
|
582
|
+
// to extend.
|
|
583
|
+
submitted_by: harnessWord(s.source),
|
|
584
|
+
// One of Claude Code's own prompt wrappers: the prompt is a block the harness
|
|
585
|
+
// wrote, so it is not the person's words. Absent is unknown, never "the person" —
|
|
586
|
+
// and `true` is never "no person caused this turn", because a Talk or
|
|
587
|
+
// `sessionpipe wait` delivery hands the person's own message over inside a
|
|
588
|
+
// <task-notification> (PROTOCOL.md §4).
|
|
589
|
+
injected: prompt !== void 0 && injectedBlock(prompt) ? true : void 0
|
|
590
|
+
})
|
|
591
|
+
);
|
|
509
592
|
break;
|
|
593
|
+
}
|
|
510
594
|
case "PreToolUse":
|
|
511
|
-
if (tool) events.push(ev("tool.started", { tool, input:
|
|
595
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
512
596
|
break;
|
|
513
597
|
case "PostToolUse":
|
|
514
|
-
if (
|
|
515
|
-
else
|
|
598
|
+
if (tool)
|
|
516
599
|
events.push(
|
|
517
600
|
ev("tool.ended", {
|
|
518
601
|
tool,
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
602
|
+
call_id,
|
|
603
|
+
turn_id,
|
|
604
|
+
agent_id,
|
|
605
|
+
ms,
|
|
606
|
+
ok: true,
|
|
607
|
+
input: s.tool_input,
|
|
608
|
+
output: s.tool_response
|
|
609
|
+
})
|
|
610
|
+
);
|
|
611
|
+
break;
|
|
612
|
+
case "PostToolUseFailure":
|
|
613
|
+
if (tool)
|
|
614
|
+
events.push(
|
|
615
|
+
ev("tool.ended", {
|
|
616
|
+
tool,
|
|
617
|
+
call_id,
|
|
618
|
+
turn_id,
|
|
619
|
+
agent_id,
|
|
620
|
+
ms,
|
|
621
|
+
ok: false,
|
|
622
|
+
error: errText(s.error ?? s.tool_response),
|
|
623
|
+
input: s.tool_input,
|
|
624
|
+
output: s.tool_response
|
|
522
625
|
})
|
|
523
626
|
);
|
|
524
627
|
break;
|
|
628
|
+
case "PermissionRequest":
|
|
629
|
+
events.push(
|
|
630
|
+
ev("attention.needed", {
|
|
631
|
+
attention_id: call_id ?? permissionId(),
|
|
632
|
+
kind: "permission",
|
|
633
|
+
tool,
|
|
634
|
+
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
635
|
+
})
|
|
636
|
+
);
|
|
637
|
+
break;
|
|
638
|
+
case "Notification":
|
|
639
|
+
events.push(
|
|
640
|
+
ev("attention.needed", {
|
|
641
|
+
attention_id: `notif-${Date.now()}`,
|
|
642
|
+
kind: notifKind(s.notification_type),
|
|
643
|
+
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
644
|
+
})
|
|
645
|
+
);
|
|
646
|
+
break;
|
|
525
647
|
case "Stop":
|
|
526
|
-
events.push(
|
|
648
|
+
events.push(
|
|
649
|
+
ev("turn.ended", {
|
|
650
|
+
turn_id,
|
|
651
|
+
reason: "stop",
|
|
652
|
+
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
653
|
+
})
|
|
654
|
+
);
|
|
527
655
|
break;
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
656
|
+
case "StopFailure":
|
|
657
|
+
events.push(
|
|
658
|
+
ev("turn.ended", {
|
|
659
|
+
turn_id,
|
|
660
|
+
reason: "error",
|
|
661
|
+
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
662
|
+
})
|
|
663
|
+
);
|
|
664
|
+
break;
|
|
665
|
+
case "SubagentStart":
|
|
666
|
+
events.push(
|
|
667
|
+
ev("subagent.started", {
|
|
668
|
+
agent_id: String(s.agent_id ?? ""),
|
|
669
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
670
|
+
})
|
|
671
|
+
);
|
|
672
|
+
break;
|
|
673
|
+
case "SubagentStop":
|
|
674
|
+
events.push(
|
|
675
|
+
ev("subagent.ended", {
|
|
676
|
+
agent_id: String(s.agent_id ?? ""),
|
|
677
|
+
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
678
|
+
})
|
|
679
|
+
);
|
|
680
|
+
break;
|
|
681
|
+
case "PreCompact":
|
|
682
|
+
events.push(ev("context.compacted", { trigger: s.trigger === "manual" ? "manual" : "auto" }));
|
|
683
|
+
break;
|
|
684
|
+
case "PostCompact":
|
|
685
|
+
break;
|
|
686
|
+
// PreCompact already said it; a second event would double-count
|
|
687
|
+
case "SessionEnd":
|
|
688
|
+
events.push(ev("session.ended", { reason: endReason(s.reason) }));
|
|
689
|
+
break;
|
|
690
|
+
case "StatusLine": {
|
|
691
|
+
const limits = claudeLimits(s.rate_limits);
|
|
692
|
+
if (!limits) return null;
|
|
693
|
+
events.push(ev("limits.observed", limits));
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
default:
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
const hints = {};
|
|
700
|
+
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
701
|
+
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
702
|
+
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
703
|
+
const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
|
|
704
|
+
if (attended === "1" || attended === "0") hints.attended = attended === "1" || !!input.env.SESSIONPIPE_CONTROL_JOB;
|
|
705
|
+
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
706
|
+
const r = { session, events, hints };
|
|
707
|
+
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
708
|
+
return r;
|
|
709
|
+
},
|
|
710
|
+
readTranscript(file, fromLine) {
|
|
711
|
+
let text = "";
|
|
712
|
+
try {
|
|
713
|
+
text = readFileSync2(file, "utf8");
|
|
714
|
+
} catch {
|
|
715
|
+
return { turns: [], line: fromLine };
|
|
716
|
+
}
|
|
717
|
+
return readClaude(text.split("\n"), fromLine);
|
|
718
|
+
},
|
|
719
|
+
facts(session, transcript, hints, state) {
|
|
720
|
+
const out = {};
|
|
721
|
+
if (transcript) Object.assign(out, claudeFacts(transcript, session.id, hints, state));
|
|
722
|
+
const cwd = out.cwd ?? session.cwd;
|
|
723
|
+
Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});
|
|
724
|
+
if (!out.url && typeof hints.desktop_id === "string") out.url = `claude://code/continue/${hints.desktop_id}`;
|
|
725
|
+
if (typeof hints.attended === "boolean") out.attended = hints.attended || !!out.url;
|
|
726
|
+
return out;
|
|
551
727
|
},
|
|
728
|
+
remoteFacts: (session, local, transcript, state, env = process.env) => claudeRemoteFacts(session.id, local, transcript, state, env),
|
|
552
729
|
backfill(sinceMs, env = process.env) {
|
|
553
730
|
const rows2 = [];
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
}
|
|
731
|
+
const files = [];
|
|
732
|
+
for (const d of claudeDirs(env))
|
|
733
|
+
files.push(...recentFiles(path4.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
734
|
+
const noState = { get: () => ({}), save: () => {
|
|
735
|
+
} };
|
|
736
|
+
for (const f of files) {
|
|
737
|
+
const id = path4.basename(f, ".jsonl");
|
|
738
|
+
const facts = claudeFacts(f, id, {}, noState);
|
|
739
|
+
if (!facts.started_at || !facts.last_at) continue;
|
|
740
|
+
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
741
|
+
const cwd = rest.cwd;
|
|
742
|
+
rows2.push({
|
|
743
|
+
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
744
|
+
started_at,
|
|
745
|
+
last_at,
|
|
746
|
+
transcript: f
|
|
747
|
+
});
|
|
572
748
|
}
|
|
573
749
|
return rows2;
|
|
574
750
|
}
|
|
575
751
|
};
|
|
752
|
+
var LINE_REFRESH_S = 60;
|
|
753
|
+
function statusLine(file, want, cmd) {
|
|
754
|
+
const f = readJsonFile(file);
|
|
755
|
+
if (!f.ok) return null;
|
|
756
|
+
const cur = f.value.statusLine;
|
|
757
|
+
const ours = typeof cur?.command === "string" && OURS.test(cur.command);
|
|
758
|
+
const command = cmd?.([NAME, "StatusLine"]).command;
|
|
759
|
+
if (want && cur && !ours)
|
|
760
|
+
return { file, changed: false, skipped: true, note: "a status line of your own is set; limits are not read" };
|
|
761
|
+
if (want ? ours && cur?.command === command && cur?.refreshInterval === LINE_REFRESH_S : !ours) return null;
|
|
762
|
+
if (want) f.value.statusLine = { type: "command", command, refreshInterval: LINE_REFRESH_S };
|
|
763
|
+
else delete f.value.statusLine;
|
|
764
|
+
writeFileSync2(file, formatJson(f.value, f));
|
|
765
|
+
return { file, changed: true, note: want ? "status line written (account limits)" : "status line removed" };
|
|
766
|
+
}
|
|
767
|
+
var epochIso = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
768
|
+
var WINDOW_MINUTES = { five_hour: 300, seven_day: 10080 };
|
|
769
|
+
function claudeLimits(rl) {
|
|
770
|
+
if (!rl || typeof rl !== "object" || Array.isArray(rl)) return null;
|
|
771
|
+
const windows = [];
|
|
772
|
+
for (const [id, w] of Object.entries(rl)) {
|
|
773
|
+
if (!/^[a-z0-9_]{1,64}$/.test(id) || typeof w?.used_percentage !== "number" || w.used_percentage < 0) continue;
|
|
774
|
+
windows.push(
|
|
775
|
+
prune({
|
|
776
|
+
id,
|
|
777
|
+
used_percent: w.used_percentage,
|
|
778
|
+
window_minutes: WINDOW_MINUTES[id],
|
|
779
|
+
resets_at: epochIso(w.resets_at)
|
|
780
|
+
})
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
return windows.length ? { windows: windows.slice(0, 8) } : null;
|
|
784
|
+
}
|
|
576
785
|
function prune(o) {
|
|
577
786
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
578
787
|
return o;
|
|
579
788
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
789
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
790
|
+
function notClaudeCode(id, s) {
|
|
791
|
+
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
792
|
+
}
|
|
793
|
+
function pickSource(v) {
|
|
794
|
+
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
795
|
+
}
|
|
796
|
+
var harnessWord = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : void 0;
|
|
797
|
+
var WRAPPER = /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\s/>]/i;
|
|
798
|
+
var injectedBlock = (prompt) => WRAPPER.test(prompt.trim());
|
|
799
|
+
function errText(v) {
|
|
800
|
+
if (typeof v === "string") return v.slice(0, 2e3);
|
|
801
|
+
if (v && typeof v === "object") {
|
|
802
|
+
const e = v;
|
|
803
|
+
if (typeof e.error === "string") return e.error.slice(0, 2e3);
|
|
804
|
+
if (typeof e.message === "string") return e.message.slice(0, 2e3);
|
|
805
|
+
}
|
|
806
|
+
return void 0;
|
|
807
|
+
}
|
|
808
|
+
function describe(input) {
|
|
809
|
+
if (!input || typeof input !== "object") return "";
|
|
810
|
+
const i = input;
|
|
811
|
+
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;
|
|
812
|
+
return typeof v === "string" ? ` ${v.slice(0, 120)}` : "";
|
|
813
|
+
}
|
|
814
|
+
var NAME_RANK = {
|
|
815
|
+
user: "custom",
|
|
816
|
+
auto: "harness",
|
|
817
|
+
hook: "harness",
|
|
818
|
+
peer: "harness"
|
|
819
|
+
};
|
|
820
|
+
function registryEntry(j) {
|
|
821
|
+
if (!j || typeof j !== "object") return null;
|
|
822
|
+
const r = j;
|
|
823
|
+
const source = r.nameSource ? NAME_RANK[r.nameSource] : void 0;
|
|
824
|
+
return {
|
|
825
|
+
sessionId: r.sessionId ? String(r.sessionId) : null,
|
|
826
|
+
name: source && typeof r.name === "string" && r.name.trim() ? r.name.trim() : null,
|
|
827
|
+
name_source: source ?? null,
|
|
828
|
+
bridge: typeof r.bridgeSessionId === "string" ? r.bridgeSessionId : null
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
var liveCache = /* @__PURE__ */ new Map();
|
|
832
|
+
function claudeLive(dir) {
|
|
833
|
+
const hit = liveCache.get(dir);
|
|
834
|
+
if (hit) return hit;
|
|
835
|
+
const map = /* @__PURE__ */ new Map();
|
|
836
|
+
let names = [];
|
|
837
|
+
try {
|
|
838
|
+
names = readdirSync2(path4.join(dir, "sessions")).filter((n) => n.endsWith(".json"));
|
|
839
|
+
} catch {
|
|
840
|
+
}
|
|
841
|
+
for (const n of names) {
|
|
584
842
|
try {
|
|
585
|
-
|
|
843
|
+
const e = registryEntry(JSON.parse(readFileSync2(path4.join(dir, "sessions", n), "utf8")));
|
|
844
|
+
if (e?.sessionId) map.set(e.sessionId, e);
|
|
586
845
|
} catch {
|
|
587
|
-
return null;
|
|
588
846
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return path4.resolve(s);
|
|
847
|
+
}
|
|
848
|
+
liveCache.set(dir, map);
|
|
849
|
+
return map;
|
|
593
850
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const out = [p];
|
|
851
|
+
function claudeLiveByPid(dir, pid) {
|
|
852
|
+
if (!pid) return null;
|
|
597
853
|
try {
|
|
598
|
-
|
|
599
|
-
if (r !== p) out.push(r);
|
|
854
|
+
return registryEntry(JSON.parse(readFileSync2(path4.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
600
855
|
} catch {
|
|
856
|
+
return null;
|
|
601
857
|
}
|
|
602
|
-
return out;
|
|
603
858
|
}
|
|
604
|
-
function
|
|
859
|
+
function claudePid(env = process.env) {
|
|
860
|
+
try {
|
|
861
|
+
const dirs = claudeDirs(env);
|
|
862
|
+
const has = (pid2) => dirs.some((d) => existsSync2(path4.join(d, "sessions", `${pid2}.json`)));
|
|
863
|
+
let pid = process.ppid;
|
|
864
|
+
if (has(pid)) return pid;
|
|
865
|
+
const parent = parentOf();
|
|
866
|
+
for (let hop = 0; hop < 6 && pid > 1; hop++) {
|
|
867
|
+
pid = parent(pid);
|
|
868
|
+
if (pid > 1 && has(pid)) return pid;
|
|
869
|
+
}
|
|
870
|
+
} catch {
|
|
871
|
+
}
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
function parentOf() {
|
|
875
|
+
if (process.platform === "linux") {
|
|
876
|
+
return (pid) => {
|
|
877
|
+
try {
|
|
878
|
+
const st = readFileSync2(`/proc/${pid}/stat`, "utf8");
|
|
879
|
+
return Number(st.slice(st.lastIndexOf(")") + 2).split(" ")[1]) || 0;
|
|
880
|
+
} catch {
|
|
881
|
+
return 0;
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
const table = /* @__PURE__ */ new Map();
|
|
886
|
+
try {
|
|
887
|
+
const out = execFileSync2("ps", ["-axo", "pid=,ppid="], {
|
|
888
|
+
encoding: "utf8",
|
|
889
|
+
timeout: 2e3,
|
|
890
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
891
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
892
|
+
});
|
|
893
|
+
for (const l of out.split("\n")) {
|
|
894
|
+
const m = /^\s*(\d+)\s+(\d+)/.exec(l);
|
|
895
|
+
if (m) table.set(Number(m[1]), Number(m[2]));
|
|
896
|
+
}
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
return (pid) => table.get(pid) || 0;
|
|
900
|
+
}
|
|
901
|
+
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
902
|
+
function claudeLogin(dir, home = homeDir()) {
|
|
903
|
+
const files = [path4.join(dir, ".claude.json")];
|
|
904
|
+
if (path4.resolve(dir) === path4.join(home, ".claude")) files.unshift(path4.join(home, ".claude.json"));
|
|
905
|
+
for (const f of files) {
|
|
906
|
+
try {
|
|
907
|
+
const a = JSON.parse(readFileSync2(f, "utf8")).oauthAccount;
|
|
908
|
+
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
909
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
910
|
+
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
911
|
+
}
|
|
912
|
+
} catch {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
return apiAccount(dir);
|
|
916
|
+
}
|
|
917
|
+
var ANTHROPIC_HOST = /(^|\.)anthropic\.com$/i;
|
|
918
|
+
function apiAccount(dir) {
|
|
919
|
+
const base = readJson(settingsIn(dir)).env?.ANTHROPIC_BASE_URL;
|
|
920
|
+
if (typeof base !== "string") return null;
|
|
921
|
+
let u;
|
|
922
|
+
try {
|
|
923
|
+
u = new URL(base.trim());
|
|
924
|
+
} catch {
|
|
925
|
+
return null;
|
|
926
|
+
}
|
|
927
|
+
if (u.protocol !== "https:" && u.protocol !== "http:" || !u.hostname || ANTHROPIC_HOST.test(u.hostname))
|
|
928
|
+
return null;
|
|
929
|
+
const origin = `${u.protocol}//${u.host}`;
|
|
930
|
+
return { id: `api-${createHash("sha256").update(origin).digest("hex").slice(0, 16)}`, name: u.host };
|
|
931
|
+
}
|
|
932
|
+
var accountCache = /* @__PURE__ */ new Map();
|
|
933
|
+
function claudeAccount(dir) {
|
|
934
|
+
const hit = accountCache.get(dir);
|
|
935
|
+
if (hit !== void 0) return hit;
|
|
936
|
+
const acct = claudeLogin(dir)?.id ?? null;
|
|
937
|
+
accountCache.set(dir, acct);
|
|
938
|
+
return acct;
|
|
939
|
+
}
|
|
940
|
+
function claudeAccounts(dirs, home = homeDir()) {
|
|
605
941
|
const out = [];
|
|
606
|
-
|
|
942
|
+
for (const d of dirs) {
|
|
943
|
+
const l = claudeLogin(d, home);
|
|
944
|
+
if (!l) continue;
|
|
945
|
+
const seen = out.find((a) => a.id === l.id);
|
|
946
|
+
if (!seen) out.push({ harness: NAME, ...l });
|
|
947
|
+
else if (!seen.email && l.email) seen.email = l.email;
|
|
948
|
+
}
|
|
949
|
+
return out;
|
|
950
|
+
}
|
|
951
|
+
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
952
|
+
function transcriptLink(records) {
|
|
953
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
954
|
+
const j = records[i];
|
|
955
|
+
if (j.type === "system" && j.subtype === "bridge_status" && typeof j.url === "string" && LINK_RE.test(j.url))
|
|
956
|
+
return j.url;
|
|
957
|
+
if (j.type === "bridge-session" && typeof j.bridgeSessionId === "string") {
|
|
958
|
+
const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);
|
|
959
|
+
if (m) return `https://claude.ai/code/session_${m[1]}`;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
function appTitle(text) {
|
|
965
|
+
const t = String(text).replace(/\s+/g, " ").trim();
|
|
966
|
+
return t.length > 50 ? `${t.slice(0, 50)}...` : t;
|
|
967
|
+
}
|
|
968
|
+
function askText(j) {
|
|
969
|
+
if (!j || j.type !== "user" || j.isMeta || j.isSidechain) return null;
|
|
970
|
+
const origin = j.origin;
|
|
971
|
+
if (origin?.kind && origin.kind !== "human") return null;
|
|
972
|
+
const c = j.message?.content;
|
|
973
|
+
const text = (typeof c === "string" ? c : Array.isArray(c) ? c.filter((b) => b && b.type === "text" && typeof b.text === "string").map((b) => b.text).join("\n") : "").trim();
|
|
974
|
+
if (!text || isInjected(text)) return null;
|
|
975
|
+
return text;
|
|
976
|
+
}
|
|
977
|
+
function firstAsk(records) {
|
|
978
|
+
for (const j of records) {
|
|
979
|
+
const t = askText(j);
|
|
980
|
+
if (t) return appTitle(t);
|
|
981
|
+
}
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
984
|
+
function scanTitles(file, from, found) {
|
|
985
|
+
const out = { custom: found.custom ?? null, ai: found.ai ?? null, upto: from };
|
|
986
|
+
let size = 0;
|
|
607
987
|
try {
|
|
608
|
-
|
|
988
|
+
size = statSync2(file).size;
|
|
609
989
|
} catch {
|
|
610
990
|
return out;
|
|
611
991
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
992
|
+
if (size < from) {
|
|
993
|
+
out.upto = 0;
|
|
994
|
+
out.custom = out.ai = null;
|
|
995
|
+
from = 0;
|
|
996
|
+
}
|
|
997
|
+
let pos = 0;
|
|
998
|
+
scanLines(
|
|
999
|
+
file,
|
|
1000
|
+
(l) => {
|
|
1001
|
+
pos += Buffer.byteLength(l, "utf8") + 1;
|
|
1002
|
+
if (pos <= from || l.indexOf('-title"') < 0) return null;
|
|
1003
|
+
try {
|
|
1004
|
+
const j = JSON.parse(l);
|
|
1005
|
+
if (j.type === "custom-title" && j.customTitle) out.custom = j.customTitle;
|
|
1006
|
+
if (j.type === "ai-title" && j.aiTitle) out.ai = j.aiTitle;
|
|
1007
|
+
} catch {
|
|
1008
|
+
}
|
|
1009
|
+
return null;
|
|
1010
|
+
},
|
|
1011
|
+
512 * 1024 * 1024
|
|
1012
|
+
);
|
|
1013
|
+
out.upto = size;
|
|
1014
|
+
return out;
|
|
1015
|
+
}
|
|
1016
|
+
function claudeFacts(file, sessionId, hints, state) {
|
|
1017
|
+
const { head, tail } = ends(file, 64 * 1024, 256 * 1024);
|
|
1018
|
+
const H = parseLines(head);
|
|
1019
|
+
const T = parseLines(tail);
|
|
1020
|
+
const out = {};
|
|
1021
|
+
const id = sessionId || path4.basename(file, ".jsonl");
|
|
1022
|
+
const mem = state.get(NAME, id);
|
|
1023
|
+
let custom = null;
|
|
1024
|
+
let ai = null;
|
|
1025
|
+
for (const j of H.concat(T)) {
|
|
1026
|
+
if (j.type === "custom-title" && typeof j.customTitle === "string") custom = j.customTitle;
|
|
1027
|
+
if (j.type === "ai-title" && typeof j.aiTitle === "string") ai = j.aiTitle;
|
|
1028
|
+
}
|
|
1029
|
+
let size = 0;
|
|
1030
|
+
try {
|
|
1031
|
+
size = statSync2(file).size;
|
|
1032
|
+
} catch {
|
|
1033
|
+
}
|
|
1034
|
+
if (!custom && !ai && size > 64 * 1024 + 256 * 1024) {
|
|
1035
|
+
const s = scanTitles(file, Number(mem.scan_upto) || 0, {
|
|
1036
|
+
custom: mem.scan_custom,
|
|
1037
|
+
ai: mem.scan_ai
|
|
1038
|
+
});
|
|
1039
|
+
custom = s.custom;
|
|
1040
|
+
ai = s.ai;
|
|
1041
|
+
state.save(NAME, id, { scan_upto: s.upto, scan_custom: s.custom, scan_ai: s.ai });
|
|
1042
|
+
}
|
|
1043
|
+
const dir = configDirOf(file);
|
|
1044
|
+
const live = claudeLive(dir).get(id) ?? claudeLiveByPid(dir, hints.claude_pid);
|
|
1045
|
+
if (live?.name) state.save(NAME, id, { name: live.name, name_source: live.name_source });
|
|
1046
|
+
const bridge = live?.bridge && /^session_[A-Za-z0-9]+$/.test(live.bridge) ? `https://claude.ai/code/${live.bridge}` : null;
|
|
1047
|
+
const url = bridge ?? transcriptLink(T) ?? transcriptLink(H) ?? (typeof mem.url === "string" ? mem.url : null);
|
|
1048
|
+
if (url) {
|
|
1049
|
+
out.url = url;
|
|
1050
|
+
state.save(NAME, id, { url });
|
|
1051
|
+
}
|
|
1052
|
+
const name = live?.name ? { title: live.name, source: live.name_source } : typeof mem.name === "string" ? { title: mem.name, source: mem.name_source } : null;
|
|
1053
|
+
const pick = name && name.source === "custom" && name || custom && { title: custom, source: "custom" } || name || ai && { title: ai, source: "harness" };
|
|
1054
|
+
let ask = typeof mem.first_ask === "string" ? mem.first_ask : firstAsk(H);
|
|
1055
|
+
if (!ask && size > 64 * 1024 && !mem.first_ask_none) {
|
|
1056
|
+
ask = scanLines(file, (l) => {
|
|
1057
|
+
try {
|
|
1058
|
+
const t = askText(JSON.parse(l));
|
|
1059
|
+
return t ? appTitle(t) : null;
|
|
1060
|
+
} catch {
|
|
1061
|
+
return null;
|
|
1062
|
+
}
|
|
1063
|
+
});
|
|
1064
|
+
if (!ask && size > 16 * 1024 * 1024) state.save(NAME, id, { first_ask_none: true });
|
|
1065
|
+
}
|
|
1066
|
+
if (ask) {
|
|
1067
|
+
out.first_ask = ask;
|
|
1068
|
+
if (!mem.first_ask) state.save(NAME, id, { first_ask: ask });
|
|
1069
|
+
}
|
|
1070
|
+
if (pick) {
|
|
1071
|
+
out.title = String(pick.title).slice(0, 120);
|
|
1072
|
+
out.title_source = pick.source;
|
|
1073
|
+
} else if (ask) {
|
|
1074
|
+
out.title = ask;
|
|
1075
|
+
out.title_source = "first-ask";
|
|
1076
|
+
}
|
|
1077
|
+
const acct = claudeAccount(dir);
|
|
1078
|
+
if (acct) out.account_id = acct;
|
|
1079
|
+
for (let i = T.length - 1; i >= 0; i--) {
|
|
1080
|
+
const m = T[i].message?.model;
|
|
1081
|
+
if (T[i]?.type === "assistant" && m && m !== "<synthetic>") {
|
|
1082
|
+
out.model = m;
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
const meta = [...T].reverse().find((j) => j.version) ?? H.find((j) => j.version);
|
|
1087
|
+
if (meta?.version) out.harness_version = String(meta.version);
|
|
1088
|
+
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1089
|
+
if (times.length) {
|
|
1090
|
+
out.started_at = Math.min(...times);
|
|
1091
|
+
out.last_at = Math.max(...times);
|
|
1092
|
+
}
|
|
1093
|
+
const withCwd = H.find((j) => typeof j.cwd === "string");
|
|
1094
|
+
if (withCwd) {
|
|
1095
|
+
out.cwd = withCwd.cwd;
|
|
1096
|
+
if (typeof withCwd.gitBranch === "string" && withCwd.gitBranch !== "HEAD") out.branch = withCwd.gitBranch;
|
|
1097
|
+
}
|
|
1098
|
+
return out;
|
|
1099
|
+
}
|
|
1100
|
+
var REMOTE_RETRY_MS = 6e4;
|
|
1101
|
+
var REMOTE_REFRESH_MS = 30 * 6e4;
|
|
1102
|
+
var CLAUDE_API = "https://api.anthropic.com";
|
|
1103
|
+
var KEYCHAIN = "Claude Code-credentials";
|
|
1104
|
+
var REMOTE_TIMEOUT_MS = 3e3;
|
|
1105
|
+
var KEYCHAIN_TIMEOUT_MS = 2e3;
|
|
1106
|
+
var REMOTE_MAX_BYTES = 1 << 20;
|
|
1107
|
+
var cleanName = (s) => s.replace(/[\p{Cc}\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/gu, " ").replace(/\s+/g, " ").trim();
|
|
1108
|
+
async function claudeRemoteFacts(id, local, transcript, state, env, now = Date.now()) {
|
|
1109
|
+
if (!transcript || !local.url || !LINK_RE.test(local.url) || local.title_source === "custom") return {};
|
|
1110
|
+
const mem = state.get(NAME, id);
|
|
1111
|
+
let title = typeof mem.remote_title === "string" ? mem.remote_title : null;
|
|
1112
|
+
if (now - (Number(mem.remote_at) || 0) >= (title ? REMOTE_REFRESH_MS : REMOTE_RETRY_MS)) {
|
|
1113
|
+
state.save(NAME, id, { remote_at: now });
|
|
1114
|
+
const token = claudeAiToken(configDirOf(transcript), env, now);
|
|
1115
|
+
const got = token ? await claudeAiTitle(local.url.slice(local.url.lastIndexOf("/") + 1), token) : void 0;
|
|
1116
|
+
if (got !== void 0) {
|
|
1117
|
+
title = got?.slice(0, 200) ?? null;
|
|
1118
|
+
state.save(NAME, id, { remote_title: title });
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
return title ? { title: title.slice(0, 120), title_source: "harness" } : {};
|
|
1122
|
+
}
|
|
1123
|
+
function keychainServices(dir, env) {
|
|
1124
|
+
const named = (p) => `${KEYCHAIN}-${createHash("sha256").update(p.normalize("NFC")).digest("hex").slice(0, 8)}`;
|
|
1125
|
+
const out = /* @__PURE__ */ new Set();
|
|
1126
|
+
if (env.CLAUDE_CONFIG_DIR && path4.resolve(env.CLAUDE_CONFIG_DIR) === dir) out.add(named(env.CLAUDE_CONFIG_DIR));
|
|
1127
|
+
if (dir === path4.join(env.HOME || homeDir(), ".claude")) out.add(KEYCHAIN);
|
|
1128
|
+
out.add(named(dir));
|
|
1129
|
+
return [...out];
|
|
1130
|
+
}
|
|
1131
|
+
var tokenCache = /* @__PURE__ */ new Map();
|
|
1132
|
+
function claudeAiToken(dir, env, now) {
|
|
1133
|
+
const hit = tokenCache.get(dir);
|
|
1134
|
+
if (hit && hit.expires > now + 6e4) return hit.token;
|
|
1135
|
+
const raw = [];
|
|
1136
|
+
if (process.platform === "darwin")
|
|
1137
|
+
for (const service of keychainServices(dir, env)) {
|
|
1138
|
+
try {
|
|
1139
|
+
const user = env.USER || userInfo().username;
|
|
1140
|
+
const account = /^[\w.-]+$/.test(user) ? user : "claude-code-user";
|
|
1141
|
+
raw.push(
|
|
1142
|
+
execFileSync2("security", ["find-generic-password", "-a", account, "-w", "-s", service], {
|
|
1143
|
+
encoding: "utf8",
|
|
1144
|
+
timeout: KEYCHAIN_TIMEOUT_MS,
|
|
1145
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1146
|
+
})
|
|
1147
|
+
);
|
|
1148
|
+
break;
|
|
1149
|
+
} catch {
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
try {
|
|
1153
|
+
raw.push(readFileSync2(path4.join(dir, ".credentials.json"), "utf8"));
|
|
1154
|
+
} catch {
|
|
1155
|
+
}
|
|
1156
|
+
for (const r of raw) {
|
|
616
1157
|
try {
|
|
617
|
-
|
|
1158
|
+
const t = r.trim();
|
|
1159
|
+
const o = JSON.parse(/^(?:[0-9a-f]{2})+$/i.test(t) ? Buffer.from(t, "hex").toString("utf8") : t).claudeAiOauth;
|
|
1160
|
+
if (typeof o?.accessToken !== "string" || !o.accessToken) continue;
|
|
1161
|
+
const expires = typeof o.expiresAt === "number" ? o.expiresAt : Number.POSITIVE_INFINITY;
|
|
1162
|
+
if (expires <= now + 6e4) continue;
|
|
1163
|
+
tokenCache.set(dir, { token: o.accessToken, expires });
|
|
1164
|
+
return o.accessToken;
|
|
618
1165
|
} catch {
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return null;
|
|
1169
|
+
}
|
|
1170
|
+
async function claudeAiTitle(bridge, token) {
|
|
1171
|
+
try {
|
|
1172
|
+
const r = await fetch(`${CLAUDE_API}/v1/code/sessions/${bridge}`, {
|
|
1173
|
+
headers: { Authorization: `Bearer ${token}`, "anthropic-version": "2023-06-01", "User-Agent": "sessionpipe" },
|
|
1174
|
+
redirect: "error",
|
|
1175
|
+
signal: AbortSignal.timeout(REMOTE_TIMEOUT_MS)
|
|
1176
|
+
});
|
|
1177
|
+
if (r.status !== 200 || Number(r.headers.get("content-length")) > REMOTE_MAX_BYTES) return void 0;
|
|
1178
|
+
const text = await r.text();
|
|
1179
|
+
if (text.length > REMOTE_MAX_BYTES) return void 0;
|
|
1180
|
+
const j = JSON.parse(text);
|
|
1181
|
+
const s = j?.response_shape ?? j?.session ?? j;
|
|
1182
|
+
const bare = (x) => x.replace(/^(?:session|cse)_/, "");
|
|
1183
|
+
if (typeof s?.id !== "string" || bare(s.id) !== bare(bridge)) return void 0;
|
|
1184
|
+
return typeof s.title === "string" && cleanName(s.title) || null;
|
|
1185
|
+
} catch {
|
|
1186
|
+
return void 0;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// src/adapters/claude-code-pool.ts
|
|
1191
|
+
var POOL_ENDPOINTS = {
|
|
1192
|
+
authorize: "https://claude.com/cai/oauth/authorize",
|
|
1193
|
+
token: "https://platform.claude.com/v1/oauth/token",
|
|
1194
|
+
callback: "https://platform.claude.com/oauth/code/callback",
|
|
1195
|
+
profile: "https://api.anthropic.com/api/oauth/profile",
|
|
1196
|
+
usage: "https://api.anthropic.com/api/oauth/usage",
|
|
1197
|
+
api: "https://api.anthropic.com",
|
|
1198
|
+
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
1199
|
+
// The six Claude Code itself asks for with this client id: a token minted with fewer
|
|
1200
|
+
// than the client's usual set is the kind of thing that fails only live.
|
|
1201
|
+
scopes: [
|
|
1202
|
+
"user:inference",
|
|
1203
|
+
"user:profile",
|
|
1204
|
+
"org:create_api_key",
|
|
1205
|
+
"user:sessions:claude_code",
|
|
1206
|
+
"user:mcp_servers",
|
|
1207
|
+
"user:file_upload"
|
|
1208
|
+
],
|
|
1209
|
+
beta: "oauth-2025-04-20"
|
|
1210
|
+
};
|
|
1211
|
+
var PoolAuthError = class extends Error {
|
|
1212
|
+
};
|
|
1213
|
+
var PoolUpstreamError = class extends Error {
|
|
1214
|
+
};
|
|
1215
|
+
var PREFIX = "anthropic-ratelimit-unified-";
|
|
1216
|
+
var WINDOW_IDS = { "5h": "five_hour", "7d": "seven_day" };
|
|
1217
|
+
var WINDOW_MINUTES2 = { five_hour: 300, seven_day: 10080 };
|
|
1218
|
+
var STATUSES = /* @__PURE__ */ new Set(["allowed", "allowed_warning", "rejected"]);
|
|
1219
|
+
var ID_RE = /^[a-z0-9_]{1,64}$/;
|
|
1220
|
+
function entries(h) {
|
|
1221
|
+
const out = [];
|
|
1222
|
+
if (h instanceof Headers) {
|
|
1223
|
+
h.forEach((v, k) => {
|
|
1224
|
+
out.push([k.toLowerCase(), v]);
|
|
1225
|
+
});
|
|
1226
|
+
return out;
|
|
1227
|
+
}
|
|
1228
|
+
for (const [k, v] of Object.entries(h)) {
|
|
1229
|
+
const s = Array.isArray(v) ? v[0] : v;
|
|
1230
|
+
if (typeof s === "string") out.push([k.toLowerCase(), s]);
|
|
1231
|
+
}
|
|
1232
|
+
return out;
|
|
1233
|
+
}
|
|
1234
|
+
function isoTime(v) {
|
|
1235
|
+
if (typeof v === "number") return v > 0 && v < 1e11 ? new Date(v * 1e3).toISOString() : void 0;
|
|
1236
|
+
if (typeof v !== "string") return void 0;
|
|
1237
|
+
const s = v.trim();
|
|
1238
|
+
if (/^\d+(\.\d+)?$/.test(s)) return isoTime(Number(s));
|
|
1239
|
+
if (!/^\d{4}-\d{2}-\d{2}T/.test(s)) return void 0;
|
|
1240
|
+
const t = Date.parse(s);
|
|
1241
|
+
return Number.isFinite(t) ? new Date(t).toISOString() : void 0;
|
|
1242
|
+
}
|
|
1243
|
+
var percentOf = (n) => Math.round(Math.max(0, n) * 10) / 10;
|
|
1244
|
+
var windowId = (part) => WINDOW_IDS[part] ?? `seven_day_${part.slice(3)}`;
|
|
1245
|
+
function readLimits(headers, now) {
|
|
1246
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1247
|
+
const out = { windows: [], observed_at: now };
|
|
1248
|
+
let any = false;
|
|
1249
|
+
for (const [name, value] of entries(headers)) {
|
|
1250
|
+
if (!name.startsWith(PREFIX)) continue;
|
|
1251
|
+
any = true;
|
|
1252
|
+
const rest = name.slice(PREFIX.length);
|
|
1253
|
+
if (rest === "status") {
|
|
1254
|
+
if (STATUSES.has(value)) out.status = value;
|
|
619
1255
|
continue;
|
|
620
1256
|
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
1257
|
+
if (rest === "representative-claim") {
|
|
1258
|
+
if (ID_RE.test(value)) out.claim = value;
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (rest === "reset") {
|
|
1262
|
+
const t = isoTime(value);
|
|
1263
|
+
if (t) out.resets_at = t;
|
|
1264
|
+
continue;
|
|
1265
|
+
}
|
|
1266
|
+
const m = /^(5h|7d|7d_[a-z0-9_]+)-(utilization|reset)$/.exec(rest);
|
|
1267
|
+
if (!m) continue;
|
|
1268
|
+
const id = windowId(m[1]);
|
|
1269
|
+
if (!ID_RE.test(id)) continue;
|
|
1270
|
+
const w = seen.get(id) ?? {};
|
|
1271
|
+
seen.set(id, w);
|
|
1272
|
+
if (m[2] === "utilization") {
|
|
1273
|
+
const n = Number(value);
|
|
1274
|
+
if (value.trim() && Number.isFinite(n)) w.used = percentOf(n * 100);
|
|
1275
|
+
} else {
|
|
1276
|
+
const t = isoTime(value);
|
|
1277
|
+
if (t) w.resets_at = t;
|
|
625
1278
|
}
|
|
626
|
-
|
|
1279
|
+
}
|
|
1280
|
+
if (!any) return null;
|
|
1281
|
+
for (const [id, w] of seen) {
|
|
1282
|
+
if (w.used === void 0) continue;
|
|
1283
|
+
const minutes = WINDOW_MINUTES2[id];
|
|
1284
|
+
out.windows.push({
|
|
1285
|
+
id,
|
|
1286
|
+
used_percent: w.used,
|
|
1287
|
+
...w.resets_at ? { resets_at: w.resets_at } : {},
|
|
1288
|
+
...minutes ? { window_minutes: minutes } : {}
|
|
1289
|
+
});
|
|
627
1290
|
}
|
|
628
1291
|
return out;
|
|
629
1292
|
}
|
|
630
|
-
function
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
if (
|
|
1293
|
+
function usageLimits(json, now) {
|
|
1294
|
+
if (!json || typeof json !== "object" || Array.isArray(json)) return null;
|
|
1295
|
+
const windows = [];
|
|
1296
|
+
for (const [id, v] of Object.entries(json)) {
|
|
1297
|
+
if (!ID_RE.test(id) || !v || typeof v !== "object" || Array.isArray(v)) continue;
|
|
1298
|
+
const w = v;
|
|
1299
|
+
if (typeof w.utilization !== "number" || !Number.isFinite(w.utilization)) continue;
|
|
1300
|
+
const resets_at = isoTime(w.resets_at);
|
|
1301
|
+
const minutes = WINDOW_MINUTES2[id];
|
|
1302
|
+
windows.push({
|
|
1303
|
+
id,
|
|
1304
|
+
used_percent: percentOf(w.utilization),
|
|
1305
|
+
...resets_at ? { resets_at } : {},
|
|
1306
|
+
...minutes ? { window_minutes: minutes } : {}
|
|
1307
|
+
});
|
|
635
1308
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1309
|
+
return windows.length ? { windows: windows.slice(0, 8), observed_at: now } : null;
|
|
1310
|
+
}
|
|
1311
|
+
var UUID2 = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
1312
|
+
var UUID_RE = new RegExp(`^${UUID2}$`, "i");
|
|
1313
|
+
var LEGACY_ACCOUNT = new RegExp(`(?:^|_)account_(${UUID2})(?=_|$)`, "i");
|
|
1314
|
+
var LEGACY_SESSION = new RegExp(`(?:^|_)session_(${UUID2})$`, "i");
|
|
1315
|
+
function userIdOf(body) {
|
|
1316
|
+
let v = body;
|
|
1317
|
+
if (Buffer.isBuffer(v) || typeof v === "string") {
|
|
1318
|
+
try {
|
|
1319
|
+
v = JSON.parse(v.toString());
|
|
1320
|
+
} catch {
|
|
1321
|
+
return null;
|
|
645
1322
|
}
|
|
646
|
-
|
|
1323
|
+
}
|
|
1324
|
+
const id = v?.metadata?.user_id;
|
|
1325
|
+
return typeof id === "string" && id.length <= 4096 ? id : null;
|
|
1326
|
+
}
|
|
1327
|
+
function partsOf(id) {
|
|
1328
|
+
if (id.startsWith("{")) {
|
|
1329
|
+
try {
|
|
1330
|
+
const j = JSON.parse(id);
|
|
1331
|
+
return {
|
|
1332
|
+
...typeof j.session_id === "string" && j.session_id ? { session: j.session_id } : {},
|
|
1333
|
+
...typeof j.account_uuid === "string" && UUID_RE.test(j.account_uuid) ? { account: j.account_uuid } : {}
|
|
1334
|
+
};
|
|
1335
|
+
} catch {
|
|
1336
|
+
return {};
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
const s = LEGACY_SESSION.exec(id)?.[1];
|
|
1340
|
+
const a = LEGACY_ACCOUNT.exec(id)?.[1];
|
|
1341
|
+
return { ...s ? { session: s } : {}, ...a ? { account: a } : {} };
|
|
1342
|
+
}
|
|
1343
|
+
function parseUserId(body) {
|
|
1344
|
+
const id = userIdOf(body);
|
|
1345
|
+
return id === null ? null : partsOf(id);
|
|
1346
|
+
}
|
|
1347
|
+
function withAccount(body, accountUuid) {
|
|
1348
|
+
if (!UUID_RE.test(accountUuid)) return body;
|
|
1349
|
+
const id = userIdOf(body);
|
|
1350
|
+
if (id === null) return body;
|
|
1351
|
+
const current = partsOf(id).account;
|
|
1352
|
+
if (current && current.toLowerCase() === accountUuid.toLowerCase()) return body;
|
|
1353
|
+
const empty = !current && id.startsWith("{") && emptyAccountSlot(id);
|
|
1354
|
+
if (!current && !empty) return body;
|
|
1355
|
+
const text = body.toString("utf8");
|
|
1356
|
+
const literal = JSON.stringify(id).slice(1, -1);
|
|
1357
|
+
const was = current ?? "";
|
|
1358
|
+
const at = current ? literal.indexOf(current) : literal.indexOf(EMPTY_SLOT) + EMPTY_SLOT.length;
|
|
1359
|
+
const ms = [...text.matchAll(/"user_id"\s*:\s*"((?:[^"\\]|\\.)*)"/g)].filter((x) => x[1] === literal);
|
|
1360
|
+
if (!ms.length) return body;
|
|
1361
|
+
if (ms.length === 1) {
|
|
1362
|
+
const m = ms[0];
|
|
1363
|
+
const start = m.index + m[0].length - 1 - literal.length + at;
|
|
1364
|
+
return Buffer.from(text.slice(0, start) + accountUuid + text.slice(start + was.length), "utf8");
|
|
1365
|
+
}
|
|
1366
|
+
const j = JSON.parse(text);
|
|
1367
|
+
if (current) j.metadata.user_id = id.replace(current, accountUuid);
|
|
1368
|
+
else
|
|
1369
|
+
j.metadata.user_id = JSON.stringify({ ...JSON.parse(id), account_uuid: accountUuid });
|
|
1370
|
+
return Buffer.from(JSON.stringify(j), "utf8");
|
|
1371
|
+
}
|
|
1372
|
+
var EMPTY_SLOT = '\\"account_uuid\\":\\"';
|
|
1373
|
+
function emptyAccountSlot(id) {
|
|
1374
|
+
try {
|
|
1375
|
+
return JSON.parse(id).account_uuid === "";
|
|
1376
|
+
} catch {
|
|
1377
|
+
return false;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
var DROP = /* @__PURE__ */ new Set([
|
|
1381
|
+
"connection",
|
|
1382
|
+
"keep-alive",
|
|
1383
|
+
"transfer-encoding",
|
|
1384
|
+
"te",
|
|
1385
|
+
"trailer",
|
|
1386
|
+
"upgrade",
|
|
1387
|
+
"host",
|
|
1388
|
+
"content-length",
|
|
1389
|
+
"authorization",
|
|
1390
|
+
"x-api-key"
|
|
1391
|
+
]);
|
|
1392
|
+
function upstreamHeaders(req, access) {
|
|
1393
|
+
const out = {};
|
|
1394
|
+
for (const [k, v] of Object.entries(req)) {
|
|
1395
|
+
const name = k.toLowerCase();
|
|
1396
|
+
if (v === void 0 || DROP.has(name) || name.startsWith("proxy-")) continue;
|
|
1397
|
+
out[name] = Array.isArray(v) ? v.join(", ") : v;
|
|
1398
|
+
}
|
|
1399
|
+
out.authorization = `Bearer ${access}`;
|
|
1400
|
+
const betas = (out["anthropic-beta"] ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
1401
|
+
if (!betas.includes(POOL_ENDPOINTS.beta)) betas.push(POOL_ENDPOINTS.beta);
|
|
1402
|
+
out["anthropic-beta"] = betas.join(",");
|
|
1403
|
+
return out;
|
|
1404
|
+
}
|
|
1405
|
+
function beginLogin(random = randomBytes) {
|
|
1406
|
+
const verifier = random(32).toString("base64url");
|
|
1407
|
+
const state = random(32).toString("base64url");
|
|
1408
|
+
const challenge = createHash2("sha256").update(verifier).digest("base64url");
|
|
1409
|
+
const u = new URL(POOL_ENDPOINTS.authorize);
|
|
1410
|
+
u.searchParams.append("code", "true");
|
|
1411
|
+
u.searchParams.append("client_id", POOL_ENDPOINTS.clientId);
|
|
1412
|
+
u.searchParams.append("response_type", "code");
|
|
1413
|
+
u.searchParams.append("redirect_uri", POOL_ENDPOINTS.callback);
|
|
1414
|
+
u.searchParams.append("scope", POOL_ENDPOINTS.scopes.join(" "));
|
|
1415
|
+
u.searchParams.append("code_challenge", challenge);
|
|
1416
|
+
u.searchParams.append("code_challenge_method", "S256");
|
|
1417
|
+
u.searchParams.append("state", state);
|
|
1418
|
+
return { url: u.toString(), verifier, state };
|
|
1419
|
+
}
|
|
1420
|
+
function pastedCode(pasted, state) {
|
|
1421
|
+
const s = pasted.trim();
|
|
1422
|
+
if (!s) throw new PoolAuthError("Paste the code the sign-in page shows.");
|
|
1423
|
+
if (/^https?:\/\//i.test(s)) {
|
|
1424
|
+
let u;
|
|
1425
|
+
try {
|
|
1426
|
+
u = new URL(s);
|
|
1427
|
+
} catch {
|
|
1428
|
+
throw new PoolAuthError("That is not the sign-in page's code.");
|
|
1429
|
+
}
|
|
1430
|
+
const code2 = u.searchParams.get("code");
|
|
1431
|
+
const st2 = u.searchParams.get("state");
|
|
1432
|
+
if (!code2) throw new PoolAuthError("That URL carries no code.");
|
|
1433
|
+
if (st2 !== null && st2 !== state) throw new PoolAuthError("That code belongs to another sign-in; start again.");
|
|
1434
|
+
return code2;
|
|
1435
|
+
}
|
|
1436
|
+
const hash = s.indexOf("#");
|
|
1437
|
+
if (hash < 0) return s;
|
|
1438
|
+
const code = s.slice(0, hash).trim();
|
|
1439
|
+
const st = s.slice(hash + 1).trim();
|
|
1440
|
+
if (!code) throw new PoolAuthError("Paste the code the sign-in page shows.");
|
|
1441
|
+
if (st && st !== state) throw new PoolAuthError("That code belongs to another sign-in; start again.");
|
|
1442
|
+
return code;
|
|
1443
|
+
}
|
|
1444
|
+
var JSON_HEADERS = { "content-type": "application/json", "anthropic-beta": POOL_ENDPOINTS.beta };
|
|
1445
|
+
async function post(fetch2, url, body, what) {
|
|
1446
|
+
try {
|
|
1447
|
+
return await fetch2(url, { method: "POST", headers: JSON_HEADERS, body: JSON.stringify(body) });
|
|
1448
|
+
} catch (e) {
|
|
1449
|
+
throw new PoolUpstreamError(`${what}: ${e.message || "no answer from Anthropic"}`);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
async function get(fetch2, url, access, what) {
|
|
1453
|
+
try {
|
|
1454
|
+
return await fetch2(url, { headers: { authorization: `Bearer ${access}`, "anthropic-beta": POOL_ENDPOINTS.beta } });
|
|
1455
|
+
} catch (e) {
|
|
1456
|
+
throw new PoolUpstreamError(`${what}: ${e.message || "no answer from Anthropic"}`);
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
async function jsonOf(r) {
|
|
1460
|
+
try {
|
|
1461
|
+
return await r.json();
|
|
1462
|
+
} catch {
|
|
1463
|
+
return null;
|
|
1464
|
+
}
|
|
647
1465
|
}
|
|
648
|
-
function
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
|
|
1466
|
+
function credentialOf(j, now, keepRefresh, what) {
|
|
1467
|
+
const t = j;
|
|
1468
|
+
const refresh = typeof t?.refresh_token === "string" && t.refresh_token ? t.refresh_token : keepRefresh;
|
|
1469
|
+
if (!t || typeof t.access_token !== "string" || !t.access_token || !refresh || typeof t.expires_in !== "number")
|
|
1470
|
+
throw new PoolUpstreamError(`${what}: Anthropic's answer had no token in it`);
|
|
1471
|
+
return { access: t.access_token, refresh, expires_at: now + t.expires_in * 1e3 };
|
|
1472
|
+
}
|
|
1473
|
+
async function completeLogin(code, pending, fetch2, now) {
|
|
1474
|
+
const c = pastedCode(code, pending.state);
|
|
1475
|
+
const r = await post(
|
|
1476
|
+
fetch2,
|
|
1477
|
+
POOL_ENDPOINTS.token,
|
|
1478
|
+
{
|
|
1479
|
+
grant_type: "authorization_code",
|
|
1480
|
+
code: c,
|
|
1481
|
+
state: pending.state,
|
|
1482
|
+
client_id: POOL_ENDPOINTS.clientId,
|
|
1483
|
+
redirect_uri: POOL_ENDPOINTS.callback,
|
|
1484
|
+
code_verifier: pending.verifier
|
|
1485
|
+
},
|
|
1486
|
+
"sign-in"
|
|
1487
|
+
);
|
|
1488
|
+
if (r.status >= 400 && r.status < 500)
|
|
1489
|
+
throw new PoolAuthError(`Anthropic did not accept the code (HTTP ${r.status}); start the sign-in again.`);
|
|
1490
|
+
if (!r.ok) throw new PoolUpstreamError(`sign-in: Anthropic answered HTTP ${r.status}; try again in a minute`);
|
|
1491
|
+
const credential = credentialOf(await jsonOf(r), now(), void 0, "sign-in");
|
|
1492
|
+
const profile = await fetchProfile(credential.access, fetch2);
|
|
1493
|
+
return { credential, profile };
|
|
1494
|
+
}
|
|
1495
|
+
async function refreshCredential(cred, fetch2, now) {
|
|
1496
|
+
const r = await post(
|
|
1497
|
+
fetch2,
|
|
1498
|
+
POOL_ENDPOINTS.token,
|
|
1499
|
+
{ grant_type: "refresh_token", refresh_token: cred.refresh, client_id: POOL_ENDPOINTS.clientId },
|
|
1500
|
+
"refresh"
|
|
1501
|
+
);
|
|
1502
|
+
if (r.status === 400 || r.status === 401)
|
|
1503
|
+
throw new PoolAuthError("This account's sign-in has expired; sign in again.");
|
|
1504
|
+
if (!r.ok) throw new PoolUpstreamError(`refresh: Anthropic answered HTTP ${r.status}; try again in a minute`);
|
|
1505
|
+
return credentialOf(await jsonOf(r), now(), cred.refresh, "refresh");
|
|
1506
|
+
}
|
|
1507
|
+
async function fetchProfile(access, fetch2) {
|
|
1508
|
+
const r = await get(fetch2, POOL_ENDPOINTS.profile, access, "profile");
|
|
1509
|
+
if (r.status === 401 || r.status === 403)
|
|
1510
|
+
throw new PoolAuthError("Anthropic does not know this token; sign in again.");
|
|
1511
|
+
if (!r.ok) throw new PoolUpstreamError(`profile: Anthropic answered HTTP ${r.status}; try again in a minute`);
|
|
1512
|
+
const j = await jsonOf(r);
|
|
1513
|
+
const a = j?.account;
|
|
1514
|
+
if (typeof a?.uuid !== "string" || !UUID_RE.test(a.uuid) || typeof a.email !== "string" || !a.email)
|
|
1515
|
+
throw new PoolUpstreamError("profile: Anthropic's answer did not say whose token this is");
|
|
1516
|
+
const org = j?.organization?.name;
|
|
1517
|
+
const plan = a.subscription_type;
|
|
1518
|
+
return {
|
|
1519
|
+
account_uuid: a.uuid.toLowerCase(),
|
|
1520
|
+
email: a.email,
|
|
1521
|
+
...typeof org === "string" && org ? { org } : {},
|
|
1522
|
+
...typeof plan === "string" && plan ? { plan } : {}
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
async function fetchUsage(access, fetch2, now) {
|
|
1526
|
+
const r = await get(fetch2, POOL_ENDPOINTS.usage, access, "usage");
|
|
1527
|
+
if (r.status === 401 || r.status === 403)
|
|
1528
|
+
throw new PoolAuthError("Anthropic does not know this token; sign in again.");
|
|
1529
|
+
if (!r.ok) throw new PoolUpstreamError(`usage: Anthropic answered HTTP ${r.status}; try again in a minute`);
|
|
1530
|
+
return usageLimits(await jsonOf(r), now());
|
|
1531
|
+
}
|
|
1532
|
+
var SERVICE = "Claude Code-credentials";
|
|
1533
|
+
function keychainService(dir, home, platform = process.platform) {
|
|
1534
|
+
const norm = (x) => platform === "win32" ? path5.resolve(x).toLowerCase() : path5.resolve(x);
|
|
1535
|
+
if (norm(dir) === norm(path5.join(home, ".claude"))) return SERVICE;
|
|
1536
|
+
return `${SERVICE}-${createHash2("sha256").update(dir).digest("hex").slice(0, 8)}`;
|
|
1537
|
+
}
|
|
1538
|
+
function keychainUser(env) {
|
|
1539
|
+
let u = env.USER;
|
|
1540
|
+
if (!u) {
|
|
652
1541
|
try {
|
|
653
|
-
|
|
654
|
-
readFileSync2(path4.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
655
|
-
);
|
|
656
|
-
if (!m) continue;
|
|
657
|
-
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
658
|
-
const conv = opts.conversation ?? "";
|
|
659
|
-
if (!ID_RE.test(conv) || !opts.workspaces?.length) return base;
|
|
660
|
-
const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);
|
|
661
|
-
return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;
|
|
1542
|
+
u = os2.userInfo().username;
|
|
662
1543
|
} catch {
|
|
1544
|
+
u = "";
|
|
663
1545
|
}
|
|
664
1546
|
}
|
|
665
|
-
return
|
|
1547
|
+
return /^[a-zA-Z0-9._-]+$/.test(u) ? u : "claude-code-user";
|
|
666
1548
|
}
|
|
667
|
-
function
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
680
|
-
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
681
|
-
out.title_source = "first-ask";
|
|
682
|
-
break;
|
|
1549
|
+
function credentialIn(text) {
|
|
1550
|
+
try {
|
|
1551
|
+
const o = JSON.parse(text).claudeAiOauth;
|
|
1552
|
+
if (typeof o?.accessToken !== "string" || !o.accessToken || typeof o.refreshToken !== "string" || !o.refreshToken)
|
|
1553
|
+
return null;
|
|
1554
|
+
return {
|
|
1555
|
+
access: o.accessToken,
|
|
1556
|
+
refresh: o.refreshToken,
|
|
1557
|
+
expires_at: typeof o.expiresAt === "number" ? o.expiresAt : 0
|
|
1558
|
+
};
|
|
1559
|
+
} catch {
|
|
1560
|
+
return null;
|
|
683
1561
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
1562
|
+
}
|
|
1563
|
+
var EMAIL_RE2 = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
1564
|
+
var readText = (f) => readFileSync3(f, "utf8");
|
|
1565
|
+
function importableLogins(env, exec, readFile = readText, platform = process.platform) {
|
|
1566
|
+
const out = [];
|
|
1567
|
+
const home = env.HOME || homeDir();
|
|
1568
|
+
const user = keychainUser(env);
|
|
1569
|
+
for (const dir of claudeDirs(env, home)) {
|
|
1570
|
+
let credential = null;
|
|
1571
|
+
let source = "file";
|
|
1572
|
+
if (platform === "darwin") {
|
|
1573
|
+
const service = keychainService(dir, home, platform);
|
|
1574
|
+
for (const args of [
|
|
1575
|
+
["find-generic-password", "-s", service, "-a", user, "-w"],
|
|
1576
|
+
["find-generic-password", "-s", service, "-w"]
|
|
1577
|
+
]) {
|
|
1578
|
+
try {
|
|
1579
|
+
credential = credentialIn(exec("security", args).trim());
|
|
1580
|
+
} catch {
|
|
1581
|
+
}
|
|
1582
|
+
if (credential) break;
|
|
1583
|
+
}
|
|
1584
|
+
if (credential) source = "keychain";
|
|
1585
|
+
}
|
|
1586
|
+
if (!credential) {
|
|
1587
|
+
try {
|
|
1588
|
+
credential = credentialIn(readFile(path5.join(dir, ".credentials.json")));
|
|
1589
|
+
} catch {
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
if (!credential) continue;
|
|
1593
|
+
out.push({ dir, credential, ...whoIs(dir, home, readFile), source });
|
|
688
1594
|
}
|
|
689
1595
|
return out;
|
|
690
1596
|
}
|
|
1597
|
+
function whoIs(dir, home, readFile) {
|
|
1598
|
+
const files = [path5.join(dir, ".claude.json")];
|
|
1599
|
+
if (path5.resolve(dir) === path5.join(home, ".claude")) files.unshift(path5.join(home, ".claude.json"));
|
|
1600
|
+
for (const f of files) {
|
|
1601
|
+
try {
|
|
1602
|
+
const a = JSON.parse(readFile(f)).oauthAccount;
|
|
1603
|
+
if (typeof a?.accountUuid === "string" && UUID_RE.test(a.accountUuid)) {
|
|
1604
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE2.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
1605
|
+
return { account_uuid: a.accountUuid.toLowerCase(), ...email ? { email } : {} };
|
|
1606
|
+
}
|
|
1607
|
+
} catch {
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return {};
|
|
1611
|
+
}
|
|
691
1612
|
|
|
692
|
-
// src/adapters/
|
|
693
|
-
var
|
|
694
|
-
__export(
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
1613
|
+
// src/adapters/acp-control.ts
|
|
1614
|
+
var acp_control_exports = {};
|
|
1615
|
+
__export(acp_control_exports, {
|
|
1616
|
+
ACP_AGENTS: () => ACP_AGENTS,
|
|
1617
|
+
ACP_HARNESSES: () => ACP_HARNESSES,
|
|
1618
|
+
acpAgent: () => acpAgent,
|
|
1619
|
+
acpHarnesses: () => acpHarnesses,
|
|
1620
|
+
acpOverride: () => acpOverride,
|
|
1621
|
+
errorWords: () => errorWords,
|
|
1622
|
+
findAcpAgent: () => findAcpAgent,
|
|
1623
|
+
permissionChoice: () => permissionChoice,
|
|
1624
|
+
startAcp: () => startAcp
|
|
703
1625
|
});
|
|
704
|
-
import
|
|
705
|
-
import
|
|
1626
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
1627
|
+
import os3 from "node:os";
|
|
1628
|
+
import path7 from "node:path";
|
|
706
1629
|
|
|
707
1630
|
// src/adapters/headless.ts
|
|
708
1631
|
import { spawn } from "node:child_process";
|
|
709
1632
|
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
710
|
-
import
|
|
1633
|
+
import path6 from "node:path";
|
|
711
1634
|
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
712
1635
|
var OUT_CAP = 1024 * 1024;
|
|
713
1636
|
function isExe(p) {
|
|
@@ -721,11 +1644,11 @@ function isExe(p) {
|
|
|
721
1644
|
}
|
|
722
1645
|
function findBinary(o) {
|
|
723
1646
|
if (o.override && isExe(o.override)) return o.override;
|
|
724
|
-
for (const d of String(o.env?.PATH || "").split(
|
|
725
|
-
if (!d || !
|
|
726
|
-
for (const n of o.names) if (isExe(
|
|
1647
|
+
for (const d of String(o.env?.PATH || "").split(path6.delimiter)) {
|
|
1648
|
+
if (!d || !path6.isAbsolute(d)) continue;
|
|
1649
|
+
for (const n of o.names) if (isExe(path6.join(d, n))) return path6.join(d, n);
|
|
727
1650
|
}
|
|
728
|
-
for (const d of o.dirs) for (const n of o.names) if (isExe(
|
|
1651
|
+
for (const d of o.dirs) for (const n of o.names) if (isExe(path6.join(d, n))) return path6.join(d, n);
|
|
729
1652
|
return null;
|
|
730
1653
|
}
|
|
731
1654
|
function jobEnv(env = process.env) {
|
|
@@ -846,229 +1769,441 @@ function runFailure(label, res) {
|
|
|
846
1769
|
return null;
|
|
847
1770
|
}
|
|
848
1771
|
|
|
849
|
-
// src/adapters/
|
|
850
|
-
var
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1772
|
+
// src/adapters/acp-control.ts
|
|
1773
|
+
var ACP_AGENTS = [
|
|
1774
|
+
{ harness: "gemini-cli", registry: "gemini", label: "Gemini CLI", bin: "gemini", args: ["--acp"] },
|
|
1775
|
+
{ harness: "devin", registry: "devin", label: "Devin", bin: "devin", args: ["acp"] },
|
|
1776
|
+
{ harness: "opencode", registry: "opencode", label: "OpenCode", bin: "opencode", args: ["acp"] },
|
|
1777
|
+
{ harness: "cursor", registry: "cursor", label: "Cursor", bin: "cursor-agent", args: ["acp"] },
|
|
1778
|
+
{ harness: "copilot-cli", registry: "github-copilot-cli", label: "Copilot CLI", bin: "copilot", args: ["--acp"] },
|
|
1779
|
+
{
|
|
1780
|
+
harness: "droid",
|
|
1781
|
+
registry: "factory-droid",
|
|
1782
|
+
label: "Droid",
|
|
1783
|
+
bin: "droid",
|
|
1784
|
+
args: ["exec", "--output-format", "acp-daemon"]
|
|
1785
|
+
},
|
|
1786
|
+
{ harness: "goose", registry: "goose", label: "goose", bin: "goose", args: ["acp"] },
|
|
1787
|
+
{ harness: "kilo", registry: "kilo", label: "Kilo", bin: "kilo", args: ["acp"] },
|
|
1788
|
+
{
|
|
1789
|
+
harness: "qwen-code",
|
|
1790
|
+
registry: "qwen-code",
|
|
1791
|
+
label: "Qwen Code",
|
|
1792
|
+
bin: "qwen",
|
|
1793
|
+
args: ["--acp", "--experimental-skills"]
|
|
1794
|
+
},
|
|
1795
|
+
{ harness: "cline", registry: "cline", label: "Cline", bin: "cline", args: ["--acp"] },
|
|
1796
|
+
{ harness: "auggie", registry: "auggie", label: "Auggie", bin: "auggie", args: ["--acp"] },
|
|
1797
|
+
{ harness: "kimi", registry: "kimi", label: "Kimi CLI", bin: "kimi", args: ["acp"] },
|
|
1798
|
+
{ harness: "amp", registry: "amp-acp", label: "Amp", bin: "amp-acp", args: [] },
|
|
1799
|
+
{ harness: "mistral-vibe", registry: "mistral-vibe", label: "Mistral Vibe", bin: "vibe-acp", args: [] },
|
|
1800
|
+
{ harness: "bob", registry: "bob", label: "Bob", bin: "bob", args: ["acp"] },
|
|
1801
|
+
{ harness: "grok-build", registry: "grok-build", label: "Grok Build", bin: "grok", args: ["agent", "stdio"] },
|
|
1802
|
+
{ harness: "qoder", registry: "qoder", label: "Qoder CLI", bin: "qodercli", args: ["--acp"] },
|
|
1803
|
+
{ harness: "minimax-code", registry: "minimax-code", label: "MiniMax Code", bin: "mcode", args: ["acp"] },
|
|
1804
|
+
{ harness: "codebuddy-code", registry: "codebuddy-code", label: "Codebuddy Code", bin: "codebuddy", args: ["--acp"] },
|
|
1805
|
+
{ harness: "nova", registry: "nova", label: "Nova", bin: "nova", args: ["acp"] },
|
|
1806
|
+
{ harness: "dimcode", registry: "dimcode", label: "DimCode", bin: "dim", args: ["acp"] },
|
|
1807
|
+
{ harness: "dirac", registry: "dirac", label: "Dirac", bin: "dirac", args: ["--acp"] },
|
|
1808
|
+
{ harness: "harn", registry: "harn", label: "Harn", bin: "harn", args: ["serve", "acp"] },
|
|
1809
|
+
{ harness: "vtcode", registry: "vtcode", label: "VT Code", bin: "vtcode", args: ["acp"] },
|
|
1810
|
+
{ harness: "stakpak", registry: "stakpak", label: "Stakpak", bin: "stakpak", args: ["acp"] },
|
|
1811
|
+
{ harness: "crow-cli", registry: "crow-cli", label: "crow-cli", bin: "crow-cli", args: ["acp"] },
|
|
1812
|
+
{ harness: "corust-agent", registry: "corust-agent", label: "Corust Agent", bin: "corust-agent-acp", args: [] },
|
|
1813
|
+
{ harness: "cortex-code", registry: "cortex-code", label: "Cortex Code", bin: "cortex", args: ["acp", "serve"] },
|
|
1814
|
+
{ harness: "kimchi", registry: "kimchi", label: "Kimchi", bin: "kimchi", args: ["--mode", "acp"] }
|
|
1815
|
+
];
|
|
1816
|
+
var ACP_HARNESSES = ACP_AGENTS.map((a) => a.harness);
|
|
1817
|
+
var acpAgent = (harness) => ACP_AGENTS.find((a) => a.harness === harness);
|
|
1818
|
+
var acpOverride = (harness) => `SESSIONPIPE_ACP_${harness.toUpperCase().replace(/-/g, "_")}`;
|
|
1819
|
+
function findAcpAgent(harness, env = process.env, home = os3.homedir(), nodeDir = path7.dirname(process.execPath)) {
|
|
1820
|
+
const a = acpAgent(harness);
|
|
1821
|
+
if (!a) return null;
|
|
1822
|
+
const bin = findBinary({
|
|
1823
|
+
override: env[acpOverride(harness)],
|
|
1824
|
+
names: process.platform === "win32" ? [`${a.bin}.exe`, `${a.bin}.cmd`] : [a.bin],
|
|
855
1825
|
env,
|
|
856
|
-
dirs: [
|
|
1826
|
+
dirs: [
|
|
1827
|
+
nodeDir,
|
|
1828
|
+
path7.join(home, ".local", "bin"),
|
|
1829
|
+
path7.join(home, ".cargo", "bin"),
|
|
1830
|
+
"/opt/homebrew/bin",
|
|
1831
|
+
"/usr/local/bin"
|
|
1832
|
+
]
|
|
857
1833
|
});
|
|
1834
|
+
return bin ? { bin, args: a.args } : null;
|
|
858
1835
|
}
|
|
859
|
-
function
|
|
860
|
-
return
|
|
861
|
-
}
|
|
862
|
-
function agyArgs(o) {
|
|
863
|
-
const prompt = o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt;
|
|
864
|
-
return [
|
|
865
|
-
...o.resume ? ["--conversation", o.resume] : [],
|
|
866
|
-
"-p",
|
|
867
|
-
prompt,
|
|
868
|
-
"--output-format",
|
|
869
|
-
"stream-json",
|
|
870
|
-
...agyModeFlags(o.mode)
|
|
871
|
-
];
|
|
1836
|
+
function acpHarnesses(env = process.env) {
|
|
1837
|
+
return ACP_HARNESSES.filter((h) => findAcpAgent(h, env));
|
|
872
1838
|
}
|
|
873
|
-
var
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1839
|
+
var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
1840
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
1841
|
+
function permissionChoice(options, mode) {
|
|
1842
|
+
const list = (Array.isArray(options) ? options : []).map(obj).filter((o) => !!o && !!str(o.optionId)).map((o) => ({ optionId: str(o.optionId), kind: str(o.kind) }));
|
|
1843
|
+
const want = mode === "auto" ? ["allow_once", "allow_always"] : ["reject_once", "reject_always"];
|
|
1844
|
+
for (const k of want) {
|
|
1845
|
+
const hit = list.find((o) => o.kind === k);
|
|
1846
|
+
if (hit) return hit;
|
|
880
1847
|
}
|
|
881
|
-
if (!j || typeof j !== "object") return null;
|
|
882
|
-
const str = (v) => typeof v === "string" ? v : "";
|
|
883
|
-
const out = {};
|
|
884
|
-
if (j.event === "init") {
|
|
885
|
-
if (str(j.conversation_id)) out.session = str(j.conversation_id);
|
|
886
|
-
} else if (j.event === "step_update") {
|
|
887
|
-
const s = j.step_update ?? {};
|
|
888
|
-
if (str(s.conversation_id)) out.session = str(s.conversation_id);
|
|
889
|
-
if (s.step_type === "agent_response" && str(s.text_delta))
|
|
890
|
-
out.delta = { step: Number(s.step_index ?? 0), text: str(s.text_delta) };
|
|
891
|
-
if (s.step_type === "error_message") {
|
|
892
|
-
const why = str(s.text_delta) || str(s.error) || str(s.message);
|
|
893
|
-
if (why) out.note = why;
|
|
894
|
-
}
|
|
895
|
-
} else if (j.event === "result") {
|
|
896
|
-
const r = j.result ?? {};
|
|
897
|
-
if (str(r.conversation_id)) out.session = str(r.conversation_id);
|
|
898
|
-
out.result = {
|
|
899
|
-
status: str(r.status),
|
|
900
|
-
response: str(r.response),
|
|
901
|
-
error: str(r.error),
|
|
902
|
-
denied: Array.isArray(r.denied_actions) ? r.denied_actions : []
|
|
903
|
-
};
|
|
904
|
-
} else return null;
|
|
905
|
-
return Object.keys(out).length ? out : null;
|
|
906
|
-
}
|
|
907
|
-
function deniedName(d) {
|
|
908
|
-
if (typeof d === "string") return d.slice(0, 80);
|
|
909
|
-
if (!d || typeof d !== "object") return null;
|
|
910
|
-
const o = d;
|
|
911
|
-
for (const k of ["tool_name", "toolName", "name", "tool", "action", "type"])
|
|
912
|
-
if (typeof o[k] === "string" && o[k]) return o[k].slice(0, 80);
|
|
913
1848
|
return null;
|
|
914
1849
|
}
|
|
915
|
-
function
|
|
916
|
-
const
|
|
917
|
-
const
|
|
918
|
-
|
|
919
|
-
const raw = s.slice(i + "AGY_ERROR:".length).trim();
|
|
920
|
-
try {
|
|
921
|
-
const j = JSON.parse(raw);
|
|
922
|
-
for (const k of ["message", "error", "detail"]) if (typeof j[k] === "string" && j[k]) return j[k];
|
|
923
|
-
} catch {
|
|
924
|
-
}
|
|
925
|
-
return raw.split("\n")[0].slice(0, 300);
|
|
1850
|
+
function errorWords(err) {
|
|
1851
|
+
const message = str(err.message);
|
|
1852
|
+
const data = str(err.data) || str(obj(err.data)?.message);
|
|
1853
|
+
return data && data !== message ? message ? `${message}: ${data}` : data : message;
|
|
926
1854
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
return
|
|
1855
|
+
function toolName(call, titles) {
|
|
1856
|
+
if (!call) return null;
|
|
1857
|
+
const t = str(call.title) || titles.get(str(call.toolCallId)) || str(call.name) || str(call.toolCallId);
|
|
1858
|
+
return t ? t.slice(0, 80) : null;
|
|
1859
|
+
}
|
|
1860
|
+
var CLIENT_INFO = { name: "sessionpipe", title: "sessionpipe control", version: "1" };
|
|
1861
|
+
function startAcp(o) {
|
|
1862
|
+
const now = o.now ?? Date.now;
|
|
1863
|
+
const log = o.log ?? (() => {
|
|
1864
|
+
});
|
|
1865
|
+
const label = o.label;
|
|
1866
|
+
let child;
|
|
1867
|
+
try {
|
|
1868
|
+
child = spawn2(o.bin, o.args, {
|
|
1869
|
+
cwd: o.cwd,
|
|
1870
|
+
env: jobEnv(o.env),
|
|
1871
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1872
|
+
detached: process.platform !== "win32",
|
|
1873
|
+
windowsHide: true
|
|
1874
|
+
});
|
|
1875
|
+
} catch (e) {
|
|
1876
|
+
return Promise.resolve({ failed: `couldn't start ${label}: ${e.message}` });
|
|
949
1877
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
const why = agyError(res.stderr) || this.note || "";
|
|
956
|
-
return {
|
|
957
|
-
failed: `Antigravity exited (${res.signal || `code ${res.code}`}) without an answer${why ? `: ${why}` : ""}`
|
|
958
|
-
};
|
|
1878
|
+
const kill = (sig) => {
|
|
1879
|
+
try {
|
|
1880
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
1881
|
+
else child.kill(sig);
|
|
1882
|
+
} catch {
|
|
959
1883
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
var reading = () => new AgyReading();
|
|
968
|
-
|
|
969
|
-
// src/adapters/claude-code.ts
|
|
970
|
-
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
971
|
-
import { createHash } from "node:crypto";
|
|
972
|
-
import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
|
|
973
|
-
import path7 from "node:path";
|
|
974
|
-
|
|
975
|
-
// src/readers/git.ts
|
|
976
|
-
import { execFileSync } from "node:child_process";
|
|
977
|
-
var cache = /* @__PURE__ */ new Map();
|
|
978
|
-
function gitFacts(cwd) {
|
|
979
|
-
if (!cwd) return {};
|
|
980
|
-
const hit = cache.get(cwd);
|
|
981
|
-
if (hit) return hit;
|
|
982
|
-
const run = (args) => {
|
|
1884
|
+
};
|
|
1885
|
+
let gone = null;
|
|
1886
|
+
let errTail = "";
|
|
1887
|
+
let nextId = 1;
|
|
1888
|
+
const pending = /* @__PURE__ */ new Map();
|
|
1889
|
+
const write = (m) => {
|
|
1890
|
+
if (gone) return;
|
|
983
1891
|
try {
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
timeout: 1500,
|
|
987
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
988
|
-
}).trim();
|
|
1892
|
+
child.stdin?.write(`${JSON.stringify(m)}
|
|
1893
|
+
`);
|
|
989
1894
|
} catch {
|
|
990
|
-
return "";
|
|
991
1895
|
}
|
|
992
1896
|
};
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1897
|
+
const request = (method, params, timeoutMs) => new Promise((resolve, reject) => {
|
|
1898
|
+
if (gone) return reject(new Error(gone));
|
|
1899
|
+
const id = nextId++;
|
|
1900
|
+
const t = setTimeout(() => {
|
|
1901
|
+
pending.delete(id);
|
|
1902
|
+
reject(new Error(`${label} didn't answer ${method} in ${Math.round(timeoutMs / 1e3)} s`));
|
|
1903
|
+
}, timeoutMs);
|
|
1904
|
+
t.unref();
|
|
1905
|
+
pending.set(id, {
|
|
1906
|
+
resolve: (r) => {
|
|
1907
|
+
clearTimeout(t);
|
|
1908
|
+
resolve(r);
|
|
1909
|
+
},
|
|
1910
|
+
reject: (e) => {
|
|
1911
|
+
clearTimeout(t);
|
|
1912
|
+
reject(e);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
write({ jsonrpc: "2.0", id, method, params });
|
|
1916
|
+
});
|
|
1917
|
+
const notify = (method, params) => write({ jsonrpc: "2.0", method, params });
|
|
1918
|
+
let turn = null;
|
|
1919
|
+
const titles = /* @__PURE__ */ new Map();
|
|
1920
|
+
const onUpdate = (u) => {
|
|
1921
|
+
if (!u) return;
|
|
1922
|
+
const kind = str(u.sessionUpdate);
|
|
1923
|
+
if (kind === "tool_call" && str(u.toolCallId) && str(u.title)) {
|
|
1924
|
+
if (titles.size >= 200) titles.clear();
|
|
1925
|
+
titles.set(str(u.toolCallId), str(u.title));
|
|
1926
|
+
return;
|
|
1927
|
+
}
|
|
1928
|
+
if (kind !== "agent_message_chunk" || !turn) return;
|
|
1929
|
+
const c = obj(u.content);
|
|
1930
|
+
if (c?.type !== "text" || !str(c.text)) return;
|
|
1931
|
+
const mid = str(u.messageId) || null;
|
|
1932
|
+
if (turn.message !== void 0 && mid !== turn.message && turn.text.trim())
|
|
1933
|
+
turn.text = `${turn.text.trimEnd()}
|
|
1006
1934
|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
// The hook waits up to 125 s for a verified control answer, and only when control
|
|
1031
|
-
// is paired on this machine; everywhere else it exits in milliseconds as before.
|
|
1032
|
-
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
1033
|
-
};
|
|
1034
|
-
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
1035
|
-
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
1036
|
-
const out = [];
|
|
1037
|
-
const add = (d) => {
|
|
1038
|
-
if (!d) return;
|
|
1039
|
-
const r = path7.resolve(d);
|
|
1040
|
-
if (!out.includes(r)) out.push(r);
|
|
1935
|
+
`;
|
|
1936
|
+
turn.message = mid;
|
|
1937
|
+
turn.text += str(c.text);
|
|
1938
|
+
o.onText?.(turn.text);
|
|
1939
|
+
};
|
|
1940
|
+
const onRequest = (id, method, params) => {
|
|
1941
|
+
if (method !== "session/request_permission") {
|
|
1942
|
+
write({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } });
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
const pick = permissionChoice(params?.options, o.mode);
|
|
1946
|
+
if (!pick?.kind.startsWith("allow")) {
|
|
1947
|
+
const name = toolName(obj(params?.toolCall), titles);
|
|
1948
|
+
if (turn) {
|
|
1949
|
+
turn.count++;
|
|
1950
|
+
if (name && !turn.denied.includes(name) && turn.denied.length < 20) turn.denied.push(name);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
write({
|
|
1954
|
+
jsonrpc: "2.0",
|
|
1955
|
+
id,
|
|
1956
|
+
result: { outcome: pick ? { outcome: "selected", optionId: pick.optionId } : { outcome: "cancelled" } }
|
|
1957
|
+
});
|
|
1041
1958
|
};
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
if (existsSync3(path7.join(d, "projects")) || existsSync3(path7.join(d, "settings.json"))) add(d);
|
|
1959
|
+
const onLine = (line) => {
|
|
1960
|
+
let m;
|
|
1961
|
+
try {
|
|
1962
|
+
m = obj(JSON.parse(line));
|
|
1963
|
+
} catch {
|
|
1964
|
+
return;
|
|
1049
1965
|
}
|
|
1050
|
-
|
|
1966
|
+
if (!m) return;
|
|
1967
|
+
const method = str(m.method);
|
|
1968
|
+
if (method) {
|
|
1969
|
+
if (m.id !== void 0) onRequest(m.id, method, obj(m.params));
|
|
1970
|
+
else if (method === "session/update") onUpdate(obj(obj(m.params)?.update));
|
|
1971
|
+
return;
|
|
1972
|
+
}
|
|
1973
|
+
const p = typeof m.id === "number" ? pending.get(m.id) : void 0;
|
|
1974
|
+
if (!p) return;
|
|
1975
|
+
pending.delete(m.id);
|
|
1976
|
+
const err = obj(m.error);
|
|
1977
|
+
if (err) p.reject(new Error(errorWords(err) || `${label} returned error ${String(err.code ?? "?")}`));
|
|
1978
|
+
else p.resolve(m.result);
|
|
1979
|
+
};
|
|
1980
|
+
let partial = "";
|
|
1981
|
+
child.stdout?.on("data", (b) => {
|
|
1982
|
+
const lines = (partial + b.toString("utf8")).split("\n");
|
|
1983
|
+
partial = lines.pop();
|
|
1984
|
+
for (const l of lines) if (l.trim()) onLine(l);
|
|
1985
|
+
if (partial.length > 4 * 1024 * 1024) partial = "";
|
|
1986
|
+
});
|
|
1987
|
+
child.stderr?.on("data", (b) => {
|
|
1988
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
1989
|
+
});
|
|
1990
|
+
const left = (why) => {
|
|
1991
|
+
if (gone) return;
|
|
1992
|
+
gone = why;
|
|
1993
|
+
for (const p of pending.values()) p.reject(new Error(why));
|
|
1994
|
+
pending.clear();
|
|
1995
|
+
};
|
|
1996
|
+
child.on("error", (e) => left(`couldn't start ${label}: ${e.message}`));
|
|
1997
|
+
const exited = (code, signal) => {
|
|
1998
|
+
const tail = errTail.trim().split("\n").slice(-3).join(" ").trim();
|
|
1999
|
+
left(`${label} exited (${signal || `code ${code}`})${tail ? `: ${tail}` : ""}`);
|
|
2000
|
+
};
|
|
2001
|
+
child.on("exit", exited);
|
|
2002
|
+
child.on("close", exited);
|
|
2003
|
+
let closed = false;
|
|
2004
|
+
const close = () => {
|
|
2005
|
+
if (closed) return;
|
|
2006
|
+
closed = true;
|
|
2007
|
+
try {
|
|
2008
|
+
child.stdin?.end();
|
|
2009
|
+
} catch {
|
|
2010
|
+
}
|
|
2011
|
+
const grace = o.closeGraceMs ?? 2e3;
|
|
2012
|
+
const t = setTimeout(() => kill("SIGTERM"), grace);
|
|
2013
|
+
t.unref();
|
|
2014
|
+
const k = setTimeout(() => kill("SIGKILL"), grace * 6);
|
|
2015
|
+
k.unref();
|
|
2016
|
+
child.once("exit", () => {
|
|
2017
|
+
clearTimeout(t);
|
|
2018
|
+
clearTimeout(k);
|
|
2019
|
+
});
|
|
2020
|
+
};
|
|
2021
|
+
const open = o.openTimeoutMs ?? 6e4;
|
|
2022
|
+
function held(session) {
|
|
2023
|
+
let busy = false;
|
|
2024
|
+
let lastUsed = now();
|
|
2025
|
+
return {
|
|
2026
|
+
session,
|
|
2027
|
+
mode: "api",
|
|
2028
|
+
get busy() {
|
|
2029
|
+
return busy;
|
|
2030
|
+
},
|
|
2031
|
+
get lastUsed() {
|
|
2032
|
+
return lastUsed;
|
|
2033
|
+
},
|
|
2034
|
+
get closed() {
|
|
2035
|
+
return closed || !!gone;
|
|
2036
|
+
},
|
|
2037
|
+
async send(text) {
|
|
2038
|
+
if (closed) return { reply: "", error: `${label} was closed` };
|
|
2039
|
+
busy = true;
|
|
2040
|
+
lastUsed = now();
|
|
2041
|
+
const t = { text: "", denied: [], count: 0 };
|
|
2042
|
+
turn = t;
|
|
2043
|
+
const limit = o.timeoutMs ?? JOB_TIMEOUT_MS;
|
|
2044
|
+
let timedOut = false;
|
|
2045
|
+
const timer = setTimeout(() => {
|
|
2046
|
+
timedOut = true;
|
|
2047
|
+
notify("session/cancel", { sessionId: session });
|
|
2048
|
+
setTimeout(close, 5e3).unref();
|
|
2049
|
+
}, limit);
|
|
2050
|
+
timer.unref();
|
|
2051
|
+
try {
|
|
2052
|
+
const res = obj(
|
|
2053
|
+
await request("session/prompt", { sessionId: session, prompt: [{ type: "text", text }] }, limit + 1e4)
|
|
2054
|
+
);
|
|
2055
|
+
const stop = str(res?.stopReason);
|
|
2056
|
+
const words = t.text.trim() || `(${label} finished without a written reply.)`;
|
|
2057
|
+
const refused = deniedNote(t.denied, t.count);
|
|
2058
|
+
if (stop === "end_turn" || stop === "max_tokens") return { reply: words + refused };
|
|
2059
|
+
if (stop === "refusal") return { reply: `${words}
|
|
2060
|
+
|
|
2061
|
+
(${label} refused the request.)${refused}` };
|
|
2062
|
+
if (stop === "cancelled")
|
|
2063
|
+
return timedOut ? {
|
|
2064
|
+
reply: "",
|
|
2065
|
+
error: `${label} was still working after ${Math.round(limit / 6e4)} minutes, so it was stopped`
|
|
2066
|
+
} : { reply: `${words}
|
|
2067
|
+
|
|
2068
|
+
(The turn was cancelled.)${refused}` };
|
|
2069
|
+
return { reply: "", error: `${label} ended the turn with ${stop || "no stop reason"}` };
|
|
2070
|
+
} catch (e) {
|
|
2071
|
+
return { reply: "", error: e.message };
|
|
2072
|
+
} finally {
|
|
2073
|
+
clearTimeout(timer);
|
|
2074
|
+
turn = null;
|
|
2075
|
+
busy = false;
|
|
2076
|
+
lastUsed = now();
|
|
2077
|
+
}
|
|
2078
|
+
},
|
|
2079
|
+
async interrupt() {
|
|
2080
|
+
notify("session/cancel", { sessionId: session });
|
|
2081
|
+
},
|
|
2082
|
+
close
|
|
2083
|
+
};
|
|
1051
2084
|
}
|
|
1052
|
-
return
|
|
2085
|
+
return (async () => {
|
|
2086
|
+
let signIn = "";
|
|
2087
|
+
try {
|
|
2088
|
+
const init = obj(
|
|
2089
|
+
await request(
|
|
2090
|
+
"initialize",
|
|
2091
|
+
{
|
|
2092
|
+
protocolVersion: 1,
|
|
2093
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
|
|
2094
|
+
clientInfo: CLIENT_INFO
|
|
2095
|
+
},
|
|
2096
|
+
open
|
|
2097
|
+
)
|
|
2098
|
+
);
|
|
2099
|
+
const caps = obj(init?.agentCapabilities);
|
|
2100
|
+
const info = obj(init?.agentInfo);
|
|
2101
|
+
log(
|
|
2102
|
+
`${label}: ${str(info?.name) || "agent"} ${str(info?.version)} (ACP ${String(init?.protocolVersion ?? "?")})`
|
|
2103
|
+
);
|
|
2104
|
+
const methods = Array.isArray(init?.authMethods) ? init.authMethods.map(obj) : [];
|
|
2105
|
+
if (methods.length) {
|
|
2106
|
+
const how = str(methods[0]?.description) || str(methods[0]?.name);
|
|
2107
|
+
signIn = ` (if ${label} wants a sign-in, do it at the machine's terminal${how ? `: ${how}` : ""})`;
|
|
2108
|
+
}
|
|
2109
|
+
if (o.load) {
|
|
2110
|
+
if (!caps?.loadSession) {
|
|
2111
|
+
close();
|
|
2112
|
+
return { unsupported: `${label} can't reopen a session once it has been closed (no session/load)` };
|
|
2113
|
+
}
|
|
2114
|
+
await request("session/load", { sessionId: o.load, cwd: o.cwd, mcpServers: [] }, open);
|
|
2115
|
+
return { held: held(o.load) };
|
|
2116
|
+
}
|
|
2117
|
+
const res = obj(await request("session/new", { cwd: o.cwd, mcpServers: [] }, open));
|
|
2118
|
+
const session = str(res?.sessionId);
|
|
2119
|
+
if (!session) throw new Error(`${label} opened a session without naming it`);
|
|
2120
|
+
return { held: held(session) };
|
|
2121
|
+
} catch (e) {
|
|
2122
|
+
close();
|
|
2123
|
+
return { failed: `${e.message}${signIn}` };
|
|
2124
|
+
}
|
|
2125
|
+
})();
|
|
1053
2126
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
2127
|
+
|
|
2128
|
+
// src/adapters/antigravity.ts
|
|
2129
|
+
import {
|
|
2130
|
+
existsSync as existsSync3,
|
|
2131
|
+
mkdirSync as mkdirSync2,
|
|
2132
|
+
readdirSync as readdirSync3,
|
|
2133
|
+
readFileSync as readFileSync4,
|
|
2134
|
+
realpathSync,
|
|
2135
|
+
statSync as statSync4,
|
|
2136
|
+
unlinkSync as unlinkSync2,
|
|
2137
|
+
writeFileSync as writeFileSync3
|
|
2138
|
+
} from "node:fs";
|
|
2139
|
+
import path8 from "node:path";
|
|
2140
|
+
var NAME2 = "antigravity";
|
|
2141
|
+
var HOOK_NAME = "sessionpipe";
|
|
2142
|
+
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
2143
|
+
var AG_STALE_PRETOOL = '{"decision":"ask","reason":"sessionpipe: an outdated PreToolUse hook is still loaded. Restart this conversation (or run sessionpipe install) to drop it."}\n';
|
|
2144
|
+
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
2145
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path8.join(homeDir(), ".gemini");
|
|
2146
|
+
var hooksFile = (env) => path8.join(gemini(env), "config", "hooks.json");
|
|
2147
|
+
var projectsDir = (env) => path8.join(gemini(env), "config", "projects");
|
|
2148
|
+
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path8.join(gemini(env), d));
|
|
2149
|
+
var ID_RE2 = /^[A-Za-z0-9_-]{1,128}$/;
|
|
1056
2150
|
var validTool2 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
2151
|
+
function entry(cmd) {
|
|
2152
|
+
const handler = (e) => ({ type: "command", command: cmd([NAME2, e]).command, timeout: 5 });
|
|
2153
|
+
const out = { enabled: true };
|
|
2154
|
+
for (const e of AG_EVENTS) out[e] = /Tool/.test(e) ? [{ matcher: "*", hooks: [handler(e)] }] : [handler(e)];
|
|
2155
|
+
return out;
|
|
2156
|
+
}
|
|
2157
|
+
var antigravity = {
|
|
1060
2158
|
name: NAME2,
|
|
1061
|
-
events:
|
|
1062
|
-
detect: (env = process.env) =>
|
|
1063
|
-
configFiles: (env = process.env) =>
|
|
1064
|
-
install
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
2159
|
+
events: AG_EVENTS,
|
|
2160
|
+
detect: (env = process.env) => existsSync3(path8.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync3(d)),
|
|
2161
|
+
configFiles: (env = process.env) => [hooksFile(env)],
|
|
2162
|
+
install(cmd, env = process.env) {
|
|
2163
|
+
const file = hooksFile(env);
|
|
2164
|
+
const f = readJsonFile(file);
|
|
2165
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
2166
|
+
const hooks = f.value;
|
|
2167
|
+
const e = entry(cmd);
|
|
2168
|
+
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
2169
|
+
hooks[HOOK_NAME] = e;
|
|
2170
|
+
mkdirSync2(path8.dirname(file), { recursive: true });
|
|
2171
|
+
writeFileSync3(file, formatJson(hooks, f));
|
|
2172
|
+
return [
|
|
2173
|
+
{
|
|
2174
|
+
file,
|
|
2175
|
+
changed: true,
|
|
2176
|
+
created: !f.existed,
|
|
2177
|
+
note: "hooks.json is read when a conversation starts; open ones report after a restart"
|
|
2178
|
+
}
|
|
2179
|
+
];
|
|
2180
|
+
},
|
|
2181
|
+
uninstall(env = process.env, opts = {}) {
|
|
2182
|
+
const file = hooksFile(env);
|
|
2183
|
+
if (!existsSync3(file)) return [{ file, changed: false }];
|
|
2184
|
+
const f = readJsonFile(file);
|
|
2185
|
+
if (!f.ok) return [{ file, changed: false, skipped: true, note: `${f.reason}; not touched` }];
|
|
2186
|
+
const hooks = f.value;
|
|
2187
|
+
if (!(HOOK_NAME in hooks)) return [{ file, changed: false }];
|
|
2188
|
+
delete hooks[HOOK_NAME];
|
|
2189
|
+
if (opts.created?.includes(file) && !Object.keys(hooks).length) {
|
|
2190
|
+
unlinkSync2(file);
|
|
2191
|
+
return [{ file, changed: true, note: "removed (sessionpipe created it)" }];
|
|
2192
|
+
}
|
|
2193
|
+
writeFileSync3(file, formatJson(hooks, f));
|
|
2194
|
+
return [{ file, changed: true }];
|
|
2195
|
+
},
|
|
2196
|
+
installed(cmd, env = process.env) {
|
|
2197
|
+
const file = hooksFile(env);
|
|
2198
|
+
const f = readJsonFile(file);
|
|
2199
|
+
const have = f.ok ? f.value[HOOK_NAME] : void 0;
|
|
2200
|
+
return [
|
|
2201
|
+
{
|
|
2202
|
+
file,
|
|
2203
|
+
state: !have ? "missing" : JSON.stringify(have) === JSON.stringify(entry(cmd)) ? "current" : "stale"
|
|
2204
|
+
}
|
|
2205
|
+
];
|
|
2206
|
+
},
|
|
1072
2207
|
fromHook(input) {
|
|
1073
2208
|
const event = input.argv[1] ?? "";
|
|
1074
2209
|
let s = {};
|
|
@@ -1077,170 +2212,87 @@ var claudeCode = {
|
|
|
1077
2212
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1078
2213
|
} catch {
|
|
1079
2214
|
}
|
|
1080
|
-
const id = s.session_id ?? s.sessionId;
|
|
1081
|
-
|
|
1082
|
-
if (
|
|
1083
|
-
const
|
|
1084
|
-
|
|
1085
|
-
if (typeof s.
|
|
1086
|
-
|
|
1087
|
-
const
|
|
1088
|
-
const tool = validTool2(s.tool_name) ? s.tool_name : void 0;
|
|
1089
|
-
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
1090
|
-
const ms = typeof s.duration_ms === "number" ? Math.round(s.duration_ms) : void 0;
|
|
1091
|
-
const agent_id = typeof s.agent_id === "string" ? s.agent_id : void 0;
|
|
1092
|
-
const permissionId = () => `perm-${createHash("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
2215
|
+
const id = s.conversationId ?? s.session_id ?? s.sessionId;
|
|
2216
|
+
const stdout = antigravityAnswer(event);
|
|
2217
|
+
if (!id) return { session: { id: "" }, events: [], stdout };
|
|
2218
|
+
const workspaces = agWorkspaces(s.workspacePaths);
|
|
2219
|
+
const session = { id: String(id), cwd: workspaces[0] ?? input.cwd };
|
|
2220
|
+
if (typeof s.modelName === "string" && s.modelName !== "auto") session.model = s.modelName;
|
|
2221
|
+
const toolCall = s.toolCall;
|
|
2222
|
+
const tool = validTool2(toolCall?.name) ? toolCall?.name : validTool2(s.toolName) ? s.toolName : void 0;
|
|
1093
2223
|
const ev = (type, data) => ({ type, data: prune2(data), harnessEvent: event });
|
|
1094
2224
|
const events = [];
|
|
1095
2225
|
switch (event) {
|
|
1096
|
-
case "
|
|
1097
|
-
|
|
2226
|
+
case "PreInvocation":
|
|
2227
|
+
if (Number(s.invocationNum) === 0 || s.invocationNum === void 0) {
|
|
2228
|
+
events.push(ev("session.started", { source: "unknown" }));
|
|
2229
|
+
events.push(ev("turn.started", {}));
|
|
2230
|
+
} else events.push(ev("session.heartbeat", {}));
|
|
1098
2231
|
break;
|
|
1099
|
-
case "
|
|
1100
|
-
events.push(
|
|
1101
|
-
ev("turn.started", {
|
|
1102
|
-
turn_id,
|
|
1103
|
-
prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
|
|
1104
|
-
})
|
|
1105
|
-
);
|
|
2232
|
+
case "PostInvocation":
|
|
1106
2233
|
break;
|
|
1107
2234
|
case "PreToolUse":
|
|
1108
|
-
if (tool) events.push(ev("tool.started", { tool,
|
|
2235
|
+
if (tool) events.push(ev("tool.started", { tool, input: toolCall?.args }));
|
|
1109
2236
|
break;
|
|
1110
2237
|
case "PostToolUse":
|
|
1111
|
-
if (tool)
|
|
1112
|
-
|
|
1113
|
-
ev("tool.ended", {
|
|
1114
|
-
tool,
|
|
1115
|
-
call_id,
|
|
1116
|
-
turn_id,
|
|
1117
|
-
agent_id,
|
|
1118
|
-
ms,
|
|
1119
|
-
ok: true,
|
|
1120
|
-
input: s.tool_input,
|
|
1121
|
-
output: s.tool_response
|
|
1122
|
-
})
|
|
1123
|
-
);
|
|
1124
|
-
break;
|
|
1125
|
-
case "PostToolUseFailure":
|
|
1126
|
-
if (tool)
|
|
2238
|
+
if (!tool) events.push(ev("session.heartbeat", {}));
|
|
2239
|
+
else
|
|
1127
2240
|
events.push(
|
|
1128
2241
|
ev("tool.ended", {
|
|
1129
2242
|
tool,
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
ms,
|
|
1134
|
-
ok: false,
|
|
1135
|
-
error: errText(s.error ?? s.tool_response),
|
|
1136
|
-
input: s.tool_input,
|
|
1137
|
-
output: s.tool_response
|
|
2243
|
+
ok: !s.error,
|
|
2244
|
+
error: typeof s.error === "string" ? s.error.slice(0, 2e3) : void 0,
|
|
2245
|
+
input: toolCall?.args
|
|
1138
2246
|
})
|
|
1139
2247
|
);
|
|
1140
2248
|
break;
|
|
1141
|
-
case "PermissionRequest":
|
|
1142
|
-
events.push(
|
|
1143
|
-
ev("attention.needed", {
|
|
1144
|
-
attention_id: call_id ?? permissionId(),
|
|
1145
|
-
kind: "permission",
|
|
1146
|
-
tool,
|
|
1147
|
-
message: tool ? `${tool}${describe(s.tool_input)}` : void 0
|
|
1148
|
-
})
|
|
1149
|
-
);
|
|
1150
|
-
break;
|
|
1151
|
-
case "Notification":
|
|
1152
|
-
events.push(
|
|
1153
|
-
ev("attention.needed", {
|
|
1154
|
-
attention_id: `notif-${Date.now()}`,
|
|
1155
|
-
kind: notifKind(s.notification_type),
|
|
1156
|
-
message: typeof s.message === "string" ? s.message.slice(0, 200) : void 0
|
|
1157
|
-
})
|
|
1158
|
-
);
|
|
1159
|
-
break;
|
|
1160
2249
|
case "Stop":
|
|
1161
|
-
events.push(ev("turn.ended", {
|
|
1162
|
-
break;
|
|
1163
|
-
case "StopFailure":
|
|
1164
|
-
events.push(
|
|
1165
|
-
ev("turn.ended", {
|
|
1166
|
-
turn_id,
|
|
1167
|
-
reason: "error",
|
|
1168
|
-
error: typeof s.error === "string" ? s.error.slice(0, 200) : void 0
|
|
1169
|
-
})
|
|
1170
|
-
);
|
|
1171
|
-
break;
|
|
1172
|
-
case "SubagentStart":
|
|
1173
|
-
events.push(
|
|
1174
|
-
ev("subagent.started", {
|
|
1175
|
-
agent_id: String(s.agent_id ?? ""),
|
|
1176
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
1177
|
-
})
|
|
1178
|
-
);
|
|
1179
|
-
break;
|
|
1180
|
-
case "SubagentStop":
|
|
1181
|
-
events.push(
|
|
1182
|
-
ev("subagent.ended", {
|
|
1183
|
-
agent_id: String(s.agent_id ?? ""),
|
|
1184
|
-
agent_type: typeof s.agent_type === "string" ? s.agent_type : void 0
|
|
1185
|
-
})
|
|
1186
|
-
);
|
|
1187
|
-
break;
|
|
1188
|
-
case "PreCompact":
|
|
1189
|
-
events.push(ev("context.compacted", { trigger: s.trigger === "manual" ? "manual" : "auto" }));
|
|
1190
|
-
break;
|
|
1191
|
-
case "PostCompact":
|
|
1192
|
-
break;
|
|
1193
|
-
// PreCompact already said it; a second event would double-count
|
|
1194
|
-
case "SessionEnd":
|
|
1195
|
-
events.push(ev("session.ended", { reason: endReason(s.reason) }));
|
|
2250
|
+
events.push(ev("turn.ended", { reason: s.error ? "error" : "stop" }));
|
|
1196
2251
|
break;
|
|
1197
2252
|
default:
|
|
1198
|
-
return
|
|
2253
|
+
return { session, events: [], stdout };
|
|
1199
2254
|
}
|
|
1200
|
-
const
|
|
1201
|
-
if (
|
|
1202
|
-
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
1203
|
-
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
1204
|
-
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
1205
|
-
const r = { session, events, hints };
|
|
1206
|
-
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
2255
|
+
const r = { session, events, stdout, hints: { workspaces } };
|
|
2256
|
+
if (typeof s.transcriptPath === "string") r.transcript = s.transcriptPath;
|
|
1207
2257
|
return r;
|
|
1208
2258
|
},
|
|
1209
2259
|
readTranscript(file, fromLine) {
|
|
1210
2260
|
let text = "";
|
|
1211
2261
|
try {
|
|
1212
|
-
text =
|
|
2262
|
+
text = readFileSync4(file, "utf8");
|
|
1213
2263
|
} catch {
|
|
1214
2264
|
return { turns: [], line: fromLine };
|
|
1215
2265
|
}
|
|
1216
|
-
return
|
|
2266
|
+
return readAntigravity(text.split("\n"), fromLine);
|
|
1217
2267
|
},
|
|
1218
|
-
facts(session, transcript, hints
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
2268
|
+
facts(session, transcript, hints) {
|
|
2269
|
+
if (!transcript) return {};
|
|
2270
|
+
return antigravityFacts(transcript, {
|
|
2271
|
+
conversation: session.id,
|
|
2272
|
+
workspaces: hints.workspaces ?? (session.cwd ? [session.cwd] : []),
|
|
2273
|
+
env: process.env
|
|
2274
|
+
});
|
|
1225
2275
|
},
|
|
1226
2276
|
backfill(sinceMs, env = process.env) {
|
|
1227
2277
|
const rows2 = [];
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
const id
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
last_at
|
|
1243
|
-
|
|
2278
|
+
for (const data of dataDirs(env)) {
|
|
2279
|
+
let ids = [];
|
|
2280
|
+
try {
|
|
2281
|
+
ids = readdirSync3(path8.join(data, "brain")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));
|
|
2282
|
+
} catch {
|
|
2283
|
+
}
|
|
2284
|
+
for (const id of ids) {
|
|
2285
|
+
const f = path8.join(data, "brain", id, ".system_generated", "logs", "transcript.jsonl");
|
|
2286
|
+
try {
|
|
2287
|
+
if (statSync4(f).mtimeMs < sinceMs) continue;
|
|
2288
|
+
} catch {
|
|
2289
|
+
continue;
|
|
2290
|
+
}
|
|
2291
|
+
const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });
|
|
2292
|
+
if (!facts.started_at || !facts.last_at) continue;
|
|
2293
|
+
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
2294
|
+
rows2.push({ session: { id, ...rest }, started_at, last_at });
|
|
2295
|
+
}
|
|
1244
2296
|
}
|
|
1245
2297
|
return rows2;
|
|
1246
2298
|
}
|
|
@@ -1249,307 +2301,266 @@ function prune2(o) {
|
|
|
1249
2301
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1250
2302
|
return o;
|
|
1251
2303
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
2304
|
+
function agPath(p) {
|
|
2305
|
+
if (typeof p !== "string" || !p) return null;
|
|
2306
|
+
let s = p;
|
|
2307
|
+
if (/^file:/i.test(s)) {
|
|
2308
|
+
try {
|
|
2309
|
+
s = decodeURIComponent(new URL(s).pathname);
|
|
2310
|
+
} catch {
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(s)) s = s.slice(1);
|
|
2314
|
+
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) return null;
|
|
2315
|
+
if (!path8.isAbsolute(s)) return null;
|
|
2316
|
+
return path8.resolve(s);
|
|
1258
2317
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
if (
|
|
2318
|
+
var agWorkspaces = (paths) => (Array.isArray(paths) ? paths : []).map(agPath).filter((p) => !!p).filter((p, i, a) => a.indexOf(p) === i);
|
|
2319
|
+
function agVariants(p) {
|
|
2320
|
+
const out = [p];
|
|
2321
|
+
try {
|
|
2322
|
+
const r = realpathSync.native(p);
|
|
2323
|
+
if (r !== p) out.push(r);
|
|
2324
|
+
} catch {
|
|
1265
2325
|
}
|
|
1266
|
-
return
|
|
1267
|
-
}
|
|
1268
|
-
function describe(input) {
|
|
1269
|
-
if (!input || typeof input !== "object") return "";
|
|
1270
|
-
const i = input;
|
|
1271
|
-
const v = i.command ?? i.file_path ?? i.path ?? i.url ?? i.pattern;
|
|
1272
|
-
return typeof v === "string" ? ` ${v.slice(0, 120)}` : "";
|
|
1273
|
-
}
|
|
1274
|
-
var NAME_RANK = {
|
|
1275
|
-
user: "custom",
|
|
1276
|
-
auto: "harness",
|
|
1277
|
-
hook: "harness",
|
|
1278
|
-
peer: "harness"
|
|
1279
|
-
};
|
|
1280
|
-
function registryEntry(j) {
|
|
1281
|
-
if (!j || typeof j !== "object") return null;
|
|
1282
|
-
const r = j;
|
|
1283
|
-
const source = r.nameSource ? NAME_RANK[r.nameSource] : void 0;
|
|
1284
|
-
return {
|
|
1285
|
-
sessionId: r.sessionId ? String(r.sessionId) : null,
|
|
1286
|
-
name: source && typeof r.name === "string" && r.name.trim() ? r.name.trim() : null,
|
|
1287
|
-
name_source: source ?? null,
|
|
1288
|
-
bridge: typeof r.bridgeSessionId === "string" ? r.bridgeSessionId : null
|
|
1289
|
-
};
|
|
2326
|
+
return out;
|
|
1290
2327
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
const hit = liveCache.get(dir);
|
|
1294
|
-
if (hit) return hit;
|
|
1295
|
-
const map = /* @__PURE__ */ new Map();
|
|
2328
|
+
function agProjects(dir) {
|
|
2329
|
+
const out = [];
|
|
1296
2330
|
let names = [];
|
|
1297
2331
|
try {
|
|
1298
|
-
names = readdirSync3(
|
|
2332
|
+
names = readdirSync3(dir).filter((n) => n.endsWith(".json"));
|
|
1299
2333
|
} catch {
|
|
2334
|
+
return out;
|
|
1300
2335
|
}
|
|
1301
2336
|
for (const n of names) {
|
|
2337
|
+
const id = n.slice(0, -5);
|
|
2338
|
+
if (!ID_RE2.test(id)) continue;
|
|
2339
|
+
let j;
|
|
1302
2340
|
try {
|
|
1303
|
-
|
|
1304
|
-
if (e?.sessionId) map.set(e.sessionId, e);
|
|
2341
|
+
j = JSON.parse(readFileSync4(path8.join(dir, n), "utf8"));
|
|
1305
2342
|
} catch {
|
|
2343
|
+
continue;
|
|
1306
2344
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
function claudeLiveByPid(dir, pid) {
|
|
1312
|
-
if (!pid) return null;
|
|
1313
|
-
try {
|
|
1314
|
-
return registryEntry(JSON.parse(readFileSync3(path7.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
1315
|
-
} catch {
|
|
1316
|
-
return null;
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
function claudePid(env = process.env) {
|
|
1320
|
-
try {
|
|
1321
|
-
const dirs = claudeDirs(env);
|
|
1322
|
-
const has = (pid2) => dirs.some((d) => existsSync3(path7.join(d, "sessions", `${pid2}.json`)));
|
|
1323
|
-
let pid = process.ppid;
|
|
1324
|
-
if (has(pid)) return pid;
|
|
1325
|
-
const parent = parentOf();
|
|
1326
|
-
for (let hop = 0; hop < 6 && pid > 1; hop++) {
|
|
1327
|
-
pid = parent(pid);
|
|
1328
|
-
if (pid > 1 && has(pid)) return pid;
|
|
2345
|
+
const folders = [];
|
|
2346
|
+
for (const r of j?.projectResources?.resources ?? []) {
|
|
2347
|
+
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
2348
|
+
if (f) folders.push(...agVariants(f));
|
|
1329
2349
|
}
|
|
1330
|
-
|
|
2350
|
+
if (folders.length) out.push({ id, folders });
|
|
1331
2351
|
}
|
|
1332
|
-
return
|
|
2352
|
+
return out;
|
|
1333
2353
|
}
|
|
1334
|
-
function
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
return Number(st.slice(st.lastIndexOf(")") + 2).split(" ")[1]) || 0;
|
|
1340
|
-
} catch {
|
|
1341
|
-
return 0;
|
|
1342
|
-
}
|
|
1343
|
-
};
|
|
2354
|
+
function agMatchProject(projects, workspaces) {
|
|
2355
|
+
const ws = [];
|
|
2356
|
+
for (const w of workspaces) {
|
|
2357
|
+
const p = agPath(w);
|
|
2358
|
+
if (p) ws.push(...agVariants(p));
|
|
1344
2359
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const m = /^\s*(\d+)\s+(\d+)/.exec(l);
|
|
1355
|
-
if (m) table.set(Number(m[1]), Number(m[2]));
|
|
2360
|
+
let best = null;
|
|
2361
|
+
let bestLen = -1;
|
|
2362
|
+
for (const pr of projects)
|
|
2363
|
+
for (const f of pr.folders) {
|
|
2364
|
+
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path8.sep) ? f : f + path8.sep));
|
|
2365
|
+
if (within && f.length > bestLen) {
|
|
2366
|
+
best = pr.id;
|
|
2367
|
+
bestLen = f.length;
|
|
2368
|
+
}
|
|
1356
2369
|
}
|
|
1357
|
-
|
|
1358
|
-
}
|
|
1359
|
-
return (pid) => table.get(pid) || 0;
|
|
2370
|
+
return best;
|
|
1360
2371
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
for (const f of files) {
|
|
2372
|
+
function antigravityRemoteUrl(transcript, opts) {
|
|
2373
|
+
const i = transcript.indexOf(`${path8.sep}brain${path8.sep}`);
|
|
2374
|
+
const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));
|
|
2375
|
+
for (const d of dirs) {
|
|
1366
2376
|
try {
|
|
1367
|
-
const
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
2377
|
+
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
2378
|
+
readFileSync4(path8.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
2379
|
+
);
|
|
2380
|
+
if (!m) continue;
|
|
2381
|
+
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
2382
|
+
const conv = opts.conversation ?? "";
|
|
2383
|
+
if (!ID_RE2.test(conv) || !opts.workspaces?.length) return base;
|
|
2384
|
+
const project = agMatchProject(agProjects(opts.projectsDir ?? projectsDir(opts.env)), opts.workspaces);
|
|
2385
|
+
return project ? `${base}?p=${encodeURIComponent(`c/${conv}?section=${project}`)}` : base;
|
|
1372
2386
|
} catch {
|
|
1373
2387
|
}
|
|
1374
2388
|
}
|
|
1375
2389
|
return null;
|
|
1376
2390
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
const
|
|
1389
|
-
if (!
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
2391
|
+
function antigravityFacts(file, opts) {
|
|
2392
|
+
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
2393
|
+
const H = parseLines(head);
|
|
2394
|
+
const T = parseLines(tail);
|
|
2395
|
+
const out = {};
|
|
2396
|
+
const url = antigravityRemoteUrl(file, opts);
|
|
2397
|
+
if (url) out.url = url;
|
|
2398
|
+
for (const j of H) {
|
|
2399
|
+
if (j.type !== "USER_INPUT" || typeof j.content !== "string") continue;
|
|
2400
|
+
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
2401
|
+
const text = (m ? m[1] : j.content).trim();
|
|
2402
|
+
const line = (text.split("\n").find((l) => l.trim()) ?? "").trim();
|
|
2403
|
+
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
2404
|
+
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
2405
|
+
out.title_source = "first-ask";
|
|
2406
|
+
break;
|
|
1393
2407
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
for (let i = records.length - 1; i >= 0; i--) {
|
|
1399
|
-
const j = records[i];
|
|
1400
|
-
if (j.type === "system" && j.subtype === "bridge_status" && typeof j.url === "string" && LINK_RE.test(j.url))
|
|
1401
|
-
return j.url;
|
|
1402
|
-
if (j.type === "bridge-session" && typeof j.bridgeSessionId === "string") {
|
|
1403
|
-
const m = /^(?:cse|session)_([A-Za-z0-9]+)$/.exec(j.bridgeSessionId);
|
|
1404
|
-
if (m) return `https://claude.ai/code/session_${m[1]}`;
|
|
1405
|
-
}
|
|
2408
|
+
const times = H.concat(T).map((j) => Date.parse(String(j.created_at ?? j.timestamp ?? ""))).filter((n) => n > 0);
|
|
2409
|
+
if (times.length) {
|
|
2410
|
+
out.started_at = Math.min(...times);
|
|
2411
|
+
out.last_at = Math.max(...times);
|
|
1406
2412
|
}
|
|
1407
|
-
return
|
|
1408
|
-
}
|
|
1409
|
-
function appTitle(text) {
|
|
1410
|
-
const t = String(text).replace(/\s+/g, " ").trim();
|
|
1411
|
-
return t.length > 50 ? `${t.slice(0, 50)}...` : t;
|
|
2413
|
+
return out;
|
|
1412
2414
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
2415
|
+
|
|
2416
|
+
// src/adapters/antigravity-control.ts
|
|
2417
|
+
var antigravity_control_exports = {};
|
|
2418
|
+
__export(antigravity_control_exports, {
|
|
2419
|
+
AgyReading: () => AgyReading,
|
|
2420
|
+
agyArgs: () => agyArgs,
|
|
2421
|
+
agyEnv: () => agyEnv,
|
|
2422
|
+
agyError: () => agyError,
|
|
2423
|
+
agyLine: () => agyLine,
|
|
2424
|
+
agyModeFlags: () => agyModeFlags,
|
|
2425
|
+
defaultLevel: () => defaultLevel,
|
|
2426
|
+
findAgy: () => findAgy,
|
|
2427
|
+
offeredLevels: () => offeredLevels,
|
|
2428
|
+
reading: () => reading
|
|
2429
|
+
});
|
|
2430
|
+
import os4 from "node:os";
|
|
2431
|
+
import path9 from "node:path";
|
|
2432
|
+
var LABEL = "Antigravity";
|
|
2433
|
+
function findAgy(env = process.env, home = os4.homedir()) {
|
|
2434
|
+
return findBinary({
|
|
2435
|
+
override: env.SESSIONPIPE_AGY,
|
|
2436
|
+
names: process.platform === "win32" ? ["agy.exe", "agy.cmd"] : ["agy"],
|
|
2437
|
+
env,
|
|
2438
|
+
dirs: [path9.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
2439
|
+
});
|
|
1421
2440
|
}
|
|
1422
|
-
function
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
if (t) return appTitle(t);
|
|
1426
|
-
}
|
|
1427
|
-
return null;
|
|
2441
|
+
function agyModeFlags(level) {
|
|
2442
|
+
if (level === "read") throw new Error("Antigravity has no read-only mode");
|
|
2443
|
+
return level === "safe" || level === "inherit" ? [] : ["--mode", "accept-edits"];
|
|
1428
2444
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
2445
|
+
var offeredLevels = (mode) => mode === "auto" ? ["safe", "edits"] : ["safe"];
|
|
2446
|
+
var defaultLevel = (mode) => mode === "auto" ? "edits" : mode;
|
|
2447
|
+
function agyArgs(o) {
|
|
2448
|
+
const prompt = o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt;
|
|
2449
|
+
return [
|
|
2450
|
+
...o.resume ? ["--conversation", o.resume] : [],
|
|
2451
|
+
"-p",
|
|
2452
|
+
prompt,
|
|
2453
|
+
"--output-format",
|
|
2454
|
+
"stream-json",
|
|
2455
|
+
...agyModeFlags(o.permission)
|
|
2456
|
+
];
|
|
2457
|
+
}
|
|
2458
|
+
var agyEnv = (env = process.env) => jobEnv(env);
|
|
2459
|
+
function agyLine(line) {
|
|
2460
|
+
let j;
|
|
1432
2461
|
try {
|
|
1433
|
-
|
|
2462
|
+
j = JSON.parse(line);
|
|
1434
2463
|
} catch {
|
|
1435
|
-
return
|
|
1436
|
-
}
|
|
1437
|
-
if (size < from) {
|
|
1438
|
-
out.upto = 0;
|
|
1439
|
-
out.custom = out.ai = null;
|
|
1440
|
-
from = 0;
|
|
2464
|
+
return null;
|
|
1441
2465
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
file,
|
|
1445
|
-
(l) => {
|
|
1446
|
-
pos += Buffer.byteLength(l, "utf8") + 1;
|
|
1447
|
-
if (pos <= from || l.indexOf('-title"') < 0) return null;
|
|
1448
|
-
try {
|
|
1449
|
-
const j = JSON.parse(l);
|
|
1450
|
-
if (j.type === "custom-title" && j.customTitle) out.custom = j.customTitle;
|
|
1451
|
-
if (j.type === "ai-title" && j.aiTitle) out.ai = j.aiTitle;
|
|
1452
|
-
} catch {
|
|
1453
|
-
}
|
|
1454
|
-
return null;
|
|
1455
|
-
},
|
|
1456
|
-
512 * 1024 * 1024
|
|
1457
|
-
);
|
|
1458
|
-
out.upto = size;
|
|
1459
|
-
return out;
|
|
1460
|
-
}
|
|
1461
|
-
function claudeFacts(file, sessionId, hints, state) {
|
|
1462
|
-
const { head, tail } = ends(file, 64 * 1024, 256 * 1024);
|
|
1463
|
-
const H = parseLines(head);
|
|
1464
|
-
const T = parseLines(tail);
|
|
2466
|
+
if (!j || typeof j !== "object") return null;
|
|
2467
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
1465
2468
|
const out = {};
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
if (
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
2469
|
+
if (j.event === "init") {
|
|
2470
|
+
if (str2(j.conversation_id)) out.session = str2(j.conversation_id);
|
|
2471
|
+
} else if (j.event === "step_update") {
|
|
2472
|
+
const s = j.step_update ?? {};
|
|
2473
|
+
if (str2(s.conversation_id)) out.session = str2(s.conversation_id);
|
|
2474
|
+
if (s.step_type === "agent_response" && str2(s.text_delta))
|
|
2475
|
+
out.delta = { step: Number(s.step_index ?? 0), text: str2(s.text_delta) };
|
|
2476
|
+
if (s.step_type === "error_message") {
|
|
2477
|
+
const why = str2(s.text_delta) || str2(s.error) || str2(s.message);
|
|
2478
|
+
if (why) out.note = why;
|
|
2479
|
+
}
|
|
2480
|
+
} else if (j.event === "result") {
|
|
2481
|
+
const r = j.result ?? {};
|
|
2482
|
+
if (str2(r.conversation_id)) out.session = str2(r.conversation_id);
|
|
2483
|
+
out.result = {
|
|
2484
|
+
status: str2(r.status),
|
|
2485
|
+
response: str2(r.response),
|
|
2486
|
+
error: str2(r.error),
|
|
2487
|
+
denied: Array.isArray(r.denied_actions) ? r.denied_actions : []
|
|
2488
|
+
};
|
|
2489
|
+
} else return null;
|
|
2490
|
+
return Object.keys(out).length ? out : null;
|
|
2491
|
+
}
|
|
2492
|
+
function deniedName(d) {
|
|
2493
|
+
if (typeof d === "string") return d.slice(0, 80);
|
|
2494
|
+
if (!d || typeof d !== "object") return null;
|
|
2495
|
+
const o = d;
|
|
2496
|
+
for (const k of ["tool_name", "toolName", "name", "tool", "action", "type"])
|
|
2497
|
+
if (typeof o[k] === "string" && o[k]) return o[k].slice(0, 80);
|
|
2498
|
+
return null;
|
|
2499
|
+
}
|
|
2500
|
+
function agyError(stderr) {
|
|
2501
|
+
const s = String(stderr || "");
|
|
2502
|
+
const i = s.lastIndexOf("AGY_ERROR:");
|
|
2503
|
+
if (i < 0) return "";
|
|
2504
|
+
const raw = s.slice(i + "AGY_ERROR:".length).trim();
|
|
1475
2505
|
try {
|
|
1476
|
-
|
|
2506
|
+
const j = JSON.parse(raw);
|
|
2507
|
+
for (const k of ["message", "error", "detail"]) if (typeof j[k] === "string" && j[k]) return j[k];
|
|
1477
2508
|
} catch {
|
|
1478
2509
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
if (live?.name) state.save(NAME2, id, { name: live.name, name_source: live.name_source });
|
|
1491
|
-
const bridge = live?.bridge && /^session_[A-Za-z0-9]+$/.test(live.bridge) ? `https://claude.ai/code/${live.bridge}` : null;
|
|
1492
|
-
const url = bridge ?? transcriptLink(T) ?? transcriptLink(H) ?? (typeof mem.url === "string" ? mem.url : null);
|
|
1493
|
-
if (url) {
|
|
1494
|
-
out.url = url;
|
|
1495
|
-
state.save(NAME2, id, { url });
|
|
1496
|
-
}
|
|
1497
|
-
const name = live?.name ? { title: live.name, source: live.name_source } : typeof mem.name === "string" ? { title: mem.name, source: mem.name_source } : null;
|
|
1498
|
-
const pick = name && name.source === "custom" && name || custom && { title: custom, source: "custom" } || name || ai && { title: ai, source: "harness" };
|
|
1499
|
-
let ask = typeof mem.first_ask === "string" ? mem.first_ask : firstAsk(H);
|
|
1500
|
-
if (!ask && size > 64 * 1024 && !mem.first_ask_none) {
|
|
1501
|
-
ask = scanLines(file, (l) => {
|
|
1502
|
-
try {
|
|
1503
|
-
const t = askText(JSON.parse(l));
|
|
1504
|
-
return t ? appTitle(t) : null;
|
|
1505
|
-
} catch {
|
|
1506
|
-
return null;
|
|
1507
|
-
}
|
|
1508
|
-
});
|
|
1509
|
-
if (!ask && size > 16 * 1024 * 1024) state.save(NAME2, id, { first_ask_none: true });
|
|
1510
|
-
}
|
|
1511
|
-
if (ask) {
|
|
1512
|
-
out.first_ask = ask;
|
|
1513
|
-
if (!mem.first_ask) state.save(NAME2, id, { first_ask: ask });
|
|
2510
|
+
return raw.split("\n")[0].slice(0, 300);
|
|
2511
|
+
}
|
|
2512
|
+
var AgyReading = class {
|
|
2513
|
+
session = null;
|
|
2514
|
+
lines = 0;
|
|
2515
|
+
buf = "";
|
|
2516
|
+
step = null;
|
|
2517
|
+
result = null;
|
|
2518
|
+
note = null;
|
|
2519
|
+
get text() {
|
|
2520
|
+
return this.buf;
|
|
1514
2521
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
2522
|
+
feed(line) {
|
|
2523
|
+
this.lines++;
|
|
2524
|
+
const p = agyLine(line);
|
|
2525
|
+
if (!p) return false;
|
|
2526
|
+
if (p.session && !this.session) this.session = p.session;
|
|
2527
|
+
if (p.note) this.note = p.note;
|
|
2528
|
+
if (p.result) this.result = p.result;
|
|
2529
|
+
if (!p.delta) return false;
|
|
2530
|
+
if (this.step !== null && p.delta.step !== this.step && this.buf && !this.buf.endsWith("\n\n")) this.buf += "\n\n";
|
|
2531
|
+
this.step = p.delta.step;
|
|
2532
|
+
this.buf += p.delta.text;
|
|
2533
|
+
return true;
|
|
1521
2534
|
}
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
const
|
|
1526
|
-
if (
|
|
1527
|
-
|
|
1528
|
-
|
|
2535
|
+
outcome(res) {
|
|
2536
|
+
const broke = runFailure(LABEL, res);
|
|
2537
|
+
if (broke) return { failed: broke };
|
|
2538
|
+
const r = this.result;
|
|
2539
|
+
if (!r) {
|
|
2540
|
+
const why = agyError(res.stderr) || this.note || "";
|
|
2541
|
+
return {
|
|
2542
|
+
failed: `Antigravity exited (${res.signal || `code ${res.code}`}) without an answer${why ? `: ${why}` : ""}`
|
|
2543
|
+
};
|
|
1529
2544
|
}
|
|
2545
|
+
if (r.status !== "SUCCESS")
|
|
2546
|
+
return { failed: r.error || agyError(res.stderr) || this.note || "Antigravity reported an error" };
|
|
2547
|
+
const names = [...new Set(r.denied.map(deniedName).filter((n) => !!n))];
|
|
2548
|
+
const text = r.response.trim() ? r.response.trim() : this.buf.trim() || "(Antigravity finished without a written reply.)";
|
|
2549
|
+
return { reply: text + deniedNote(names, r.denied.length) };
|
|
1530
2550
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
1534
|
-
if (times.length) {
|
|
1535
|
-
out.started_at = Math.min(...times);
|
|
1536
|
-
out.last_at = Math.max(...times);
|
|
1537
|
-
}
|
|
1538
|
-
const withCwd = H.find((j) => typeof j.cwd === "string");
|
|
1539
|
-
if (withCwd) {
|
|
1540
|
-
out.cwd = withCwd.cwd;
|
|
1541
|
-
if (typeof withCwd.gitBranch === "string" && withCwd.gitBranch !== "HEAD") out.branch = withCwd.gitBranch;
|
|
1542
|
-
}
|
|
1543
|
-
return out;
|
|
1544
|
-
}
|
|
2551
|
+
};
|
|
2552
|
+
var reading = () => new AgyReading();
|
|
1545
2553
|
|
|
1546
2554
|
// src/adapters/claude-code-control.ts
|
|
1547
2555
|
var claude_code_control_exports = {};
|
|
1548
2556
|
__export(claude_code_control_exports, {
|
|
1549
2557
|
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
2558
|
+
ceilingLevel: () => ceilingLevel,
|
|
2559
|
+
claudeArgs: () => claudeArgs,
|
|
1550
2560
|
claudeEnv: () => claudeEnv,
|
|
1551
2561
|
claudeLogin: () => claudeLogin2,
|
|
1552
2562
|
cmpVersion: () => cmpVersion,
|
|
2563
|
+
defaultLevel: () => defaultLevel2,
|
|
1553
2564
|
detectCaps: () => detectCaps,
|
|
1554
2565
|
findClaude: () => findClaude,
|
|
1555
2566
|
findTranscript: () => findTranscript,
|
|
@@ -1558,6 +2569,8 @@ __export(claude_code_control_exports, {
|
|
|
1558
2569
|
loginCommand: () => loginCommand,
|
|
1559
2570
|
loginHint: () => loginHint,
|
|
1560
2571
|
modeFlags: () => modeFlags,
|
|
2572
|
+
offeredLevels: () => offeredLevels2,
|
|
2573
|
+
outputArgs: () => outputArgs,
|
|
1561
2574
|
parseAuthStatus: () => parseAuthStatus,
|
|
1562
2575
|
parseCaps: () => parseCaps,
|
|
1563
2576
|
parseResult: () => parseResult,
|
|
@@ -1566,10 +2579,33 @@ __export(claude_code_control_exports, {
|
|
|
1566
2579
|
sessionFolder: () => sessionFolder
|
|
1567
2580
|
});
|
|
1568
2581
|
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1569
|
-
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as
|
|
1570
|
-
import
|
|
1571
|
-
import
|
|
1572
|
-
|
|
2582
|
+
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync5, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
2583
|
+
import os5 from "node:os";
|
|
2584
|
+
import path10 from "node:path";
|
|
2585
|
+
|
|
2586
|
+
// src/control/verify.ts
|
|
2587
|
+
var GRANT_MAX_MS = 24 * 36e5;
|
|
2588
|
+
var SKEW_MS = 5 * 6e4;
|
|
2589
|
+
var MACHINE_MAX_AGE_MS = 24 * 36e5;
|
|
2590
|
+
var RECEIVER_MAX_AGE_MS = 10 * 6e4;
|
|
2591
|
+
var NONCE_TTL_MS = 25 * 36e5;
|
|
2592
|
+
var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
2593
|
+
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
2594
|
+
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
2595
|
+
var FIELDS = {
|
|
2596
|
+
prompt: ["text", "files"],
|
|
2597
|
+
"permission.answer": ["for", "decision"],
|
|
2598
|
+
cancel: [],
|
|
2599
|
+
start: ["text", "cwd", "files", "permission", "model"],
|
|
2600
|
+
"key.add": ["key", "name"],
|
|
2601
|
+
pool: ["pool_action", "pool_account", "pool_order", "pool_code"]
|
|
2602
|
+
};
|
|
2603
|
+
var POOL_FIELDS = FIELDS.pool;
|
|
2604
|
+
var enc = new TextEncoder();
|
|
2605
|
+
var dec = new TextDecoder();
|
|
2606
|
+
|
|
2607
|
+
// src/adapters/claude-code-control.ts
|
|
2608
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1573
2609
|
var LIVE_WRITE_MS = 9e4;
|
|
1574
2610
|
var cmpVersion = (a, b) => {
|
|
1575
2611
|
const pa = a.split(".").map(Number);
|
|
@@ -1578,28 +2614,28 @@ var cmpVersion = (a, b) => {
|
|
|
1578
2614
|
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1579
2615
|
return 0;
|
|
1580
2616
|
};
|
|
1581
|
-
function findClaude(env = process.env, home =
|
|
2617
|
+
function findClaude(env = process.env, home = os5.homedir()) {
|
|
1582
2618
|
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1583
2619
|
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1584
|
-
for (const d of String(env.PATH || "").split(
|
|
1585
|
-
if (!d || !
|
|
1586
|
-
for (const n of names) if (isExe(
|
|
2620
|
+
for (const d of String(env.PATH || "").split(path10.delimiter)) {
|
|
2621
|
+
if (!d || !path10.isAbsolute(d)) continue;
|
|
2622
|
+
for (const n of names) if (isExe(path10.join(d, n))) return path10.join(d, n);
|
|
1587
2623
|
}
|
|
1588
2624
|
for (const p of [
|
|
1589
|
-
|
|
1590
|
-
|
|
2625
|
+
path10.join(home, ".claude", "local", "claude"),
|
|
2626
|
+
path10.join(home, ".local", "bin", "claude"),
|
|
1591
2627
|
"/opt/homebrew/bin/claude",
|
|
1592
2628
|
"/usr/local/bin/claude"
|
|
1593
2629
|
])
|
|
1594
2630
|
if (isExe(p)) return p;
|
|
1595
|
-
const app =
|
|
2631
|
+
const app = path10.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1596
2632
|
let versions = [];
|
|
1597
2633
|
try {
|
|
1598
2634
|
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1599
2635
|
} catch {
|
|
1600
2636
|
}
|
|
1601
2637
|
for (const v of versions) {
|
|
1602
|
-
const p =
|
|
2638
|
+
const p = path10.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1603
2639
|
if (isExe(p)) return p;
|
|
1604
2640
|
}
|
|
1605
2641
|
return null;
|
|
@@ -1625,11 +2661,19 @@ function parseCaps(help) {
|
|
|
1625
2661
|
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1626
2662
|
};
|
|
1627
2663
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
2664
|
+
var LEVEL_MODE = {
|
|
2665
|
+
read: "plan",
|
|
2666
|
+
edits: "acceptEdits",
|
|
2667
|
+
auto: "auto"
|
|
2668
|
+
};
|
|
2669
|
+
function modeFlags(level, caps) {
|
|
2670
|
+
if (level === "inherit") return { args: [] };
|
|
2671
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
2672
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
2673
|
+
if (level !== "safe") {
|
|
2674
|
+
const m = LEVEL_MODE[level];
|
|
2675
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
2676
|
+
return { args: ["--permission-mode", m] };
|
|
1633
2677
|
}
|
|
1634
2678
|
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1635
2679
|
const args = [];
|
|
@@ -1641,6 +2685,32 @@ function modeFlags(mode, caps) {
|
|
|
1641
2685
|
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1642
2686
|
return { args };
|
|
1643
2687
|
}
|
|
2688
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
2689
|
+
function offeredLevels2(mode, caps) {
|
|
2690
|
+
return START_PERMISSIONS.filter(
|
|
2691
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2694
|
+
var defaultLevel2 = (mode) => mode;
|
|
2695
|
+
function outputArgs(caps) {
|
|
2696
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
2697
|
+
}
|
|
2698
|
+
function claudeArgs(o) {
|
|
2699
|
+
const mf = modeFlags(o.level, o.caps);
|
|
2700
|
+
if ("error" in mf) return mf;
|
|
2701
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
2702
|
+
return {
|
|
2703
|
+
args: [
|
|
2704
|
+
"-p",
|
|
2705
|
+
...outputArgs(o.caps),
|
|
2706
|
+
...mf.args,
|
|
2707
|
+
...o.model ? ["--model", o.model] : [],
|
|
2708
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
2709
|
+
...where,
|
|
2710
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
2711
|
+
]
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
1644
2714
|
function claudeEnv(configDir, env = process.env) {
|
|
1645
2715
|
const e = { ...env };
|
|
1646
2716
|
for (const k of [
|
|
@@ -1654,7 +2724,7 @@ function claudeEnv(configDir, env = process.env) {
|
|
|
1654
2724
|
delete e[k];
|
|
1655
2725
|
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1656
2726
|
if (configDir !== void 0) {
|
|
1657
|
-
if (
|
|
2727
|
+
if (path10.resolve(configDir) === path10.resolve(os5.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1658
2728
|
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1659
2729
|
}
|
|
1660
2730
|
return e;
|
|
@@ -1687,12 +2757,12 @@ function claudeLogin2(bin, configDir, env = process.env) {
|
|
|
1687
2757
|
});
|
|
1688
2758
|
}
|
|
1689
2759
|
function loginCommand(configDir) {
|
|
1690
|
-
const home =
|
|
1691
|
-
const isDefault = !configDir ||
|
|
2760
|
+
const home = os5.homedir();
|
|
2761
|
+
const isDefault = !configDir || path10.resolve(configDir) === path10.resolve(home, ".claude");
|
|
1692
2762
|
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
1693
2763
|
}
|
|
1694
2764
|
function loginHint(configDir, platform = process.platform) {
|
|
1695
|
-
const shown = configDir ? configDir.replace(
|
|
2765
|
+
const shown = configDir ? configDir.replace(os5.homedir(), "~") : "~/.claude";
|
|
1696
2766
|
const login = loginCommand(configDir);
|
|
1697
2767
|
if (platform === "darwin")
|
|
1698
2768
|
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.`;
|
|
@@ -1714,17 +2784,17 @@ function detectCaps(bin) {
|
|
|
1714
2784
|
return parseCaps(help);
|
|
1715
2785
|
}
|
|
1716
2786
|
function findTranscript(session, dirs) {
|
|
1717
|
-
if (!
|
|
2787
|
+
if (!UUID_RE2.test(session)) return null;
|
|
1718
2788
|
for (const dir of dirs) {
|
|
1719
2789
|
let projects = [];
|
|
1720
2790
|
try {
|
|
1721
|
-
projects = readdirSync4(
|
|
2791
|
+
projects = readdirSync4(path10.join(dir, "projects"), { withFileTypes: true });
|
|
1722
2792
|
} catch {
|
|
1723
2793
|
continue;
|
|
1724
2794
|
}
|
|
1725
2795
|
for (const p of projects) {
|
|
1726
2796
|
if (!p.isDirectory()) continue;
|
|
1727
|
-
const file =
|
|
2797
|
+
const file = path10.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
1728
2798
|
try {
|
|
1729
2799
|
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
1730
2800
|
} catch {
|
|
@@ -1746,7 +2816,7 @@ function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
|
1746
2816
|
} catch {
|
|
1747
2817
|
return null;
|
|
1748
2818
|
}
|
|
1749
|
-
if (!j || typeof j.cwd !== "string" || !
|
|
2819
|
+
if (!j || typeof j.cwd !== "string" || !path10.isAbsolute(j.cwd)) return null;
|
|
1750
2820
|
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
1751
2821
|
first ??= j.cwd;
|
|
1752
2822
|
count++;
|
|
@@ -1807,13 +2877,13 @@ function pidAlive(pid) {
|
|
|
1807
2877
|
function liveProcess(session, configDir) {
|
|
1808
2878
|
let names = [];
|
|
1809
2879
|
try {
|
|
1810
|
-
names = readdirSync4(
|
|
2880
|
+
names = readdirSync4(path10.join(configDir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
1811
2881
|
} catch {
|
|
1812
2882
|
return false;
|
|
1813
2883
|
}
|
|
1814
2884
|
for (const n of names) {
|
|
1815
2885
|
try {
|
|
1816
|
-
const j = JSON.parse(
|
|
2886
|
+
const j = JSON.parse(readFileSync5(path10.join(configDir, "sessions", n), "utf8"));
|
|
1817
2887
|
if (String(j.sessionId) !== session) continue;
|
|
1818
2888
|
if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
|
|
1819
2889
|
} catch {
|
|
@@ -1858,14 +2928,14 @@ function parseResult(stdout) {
|
|
|
1858
2928
|
}
|
|
1859
2929
|
return null;
|
|
1860
2930
|
}
|
|
1861
|
-
var isUuid = (s) => typeof s === "string" &&
|
|
2931
|
+
var isUuid = (s) => typeof s === "string" && UUID_RE2.test(s);
|
|
1862
2932
|
|
|
1863
2933
|
// src/adapters/codex.ts
|
|
1864
|
-
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as
|
|
1865
|
-
import
|
|
2934
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2935
|
+
import path11 from "node:path";
|
|
1866
2936
|
|
|
1867
2937
|
// src/adapters/codex-config.ts
|
|
1868
|
-
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)
|
|
2938
|
+
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?(?=\r?$)/m;
|
|
1869
2939
|
var NOTIFY_KEY = /^[ \t]*notify[ \t]*=/m;
|
|
1870
2940
|
var OURS2 = /(?:hook\.js|sessionpipe-hook)",\s*"codex",\s*"notify"/;
|
|
1871
2941
|
function scan(line, st) {
|
|
@@ -1955,10 +3025,11 @@ function dropLine(text, index, length) {
|
|
|
1955
3025
|
}
|
|
1956
3026
|
function insertTopLevel(text, line) {
|
|
1957
3027
|
const { head, tail } = split(text);
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
3028
|
+
const body = head.replace(/\s*$/, "");
|
|
3029
|
+
const eol = (body.includes("\n") ? body : text).includes("\r\n") ? "\r\n" : "\n";
|
|
3030
|
+
const top = head.trim() ? head.replace(/\s*$/, eol) : "";
|
|
3031
|
+
if (!tail) return top + line + eol;
|
|
3032
|
+
return `${top + line}${eol}${eol}${tail}`;
|
|
1962
3033
|
}
|
|
1963
3034
|
function withNotify(text, line) {
|
|
1964
3035
|
const n = readNotify(text);
|
|
@@ -2007,25 +3078,25 @@ var CODEX_EVENTS = [
|
|
|
2007
3078
|
];
|
|
2008
3079
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
2009
3080
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
2010
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
2011
|
-
var hooksFile2 = (env) =>
|
|
2012
|
-
var configFile2 = (env) =>
|
|
2013
|
-
var sessionsDir = (env) =>
|
|
3081
|
+
var codexHome = (env) => env.CODEX_HOME || path11.join(homeDir(), ".codex");
|
|
3082
|
+
var hooksFile2 = (env) => path11.join(codexHome(env), "hooks.json");
|
|
3083
|
+
var configFile2 = (env) => path11.join(codexHome(env), "config.toml");
|
|
3084
|
+
var sessionsDir = (env) => path11.join(codexHome(env), "sessions");
|
|
2014
3085
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2015
3086
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
2016
3087
|
function installNotify(cmd, env) {
|
|
2017
3088
|
const file = configFile2(env);
|
|
2018
3089
|
let text = "";
|
|
2019
3090
|
try {
|
|
2020
|
-
text =
|
|
3091
|
+
text = readFileSync6(file, "utf8");
|
|
2021
3092
|
} catch {
|
|
2022
3093
|
}
|
|
2023
3094
|
const argv = cmd([NAME3, "notify"]).argv;
|
|
2024
3095
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
2025
3096
|
const n = readNotify(text);
|
|
2026
3097
|
const write = (next) => {
|
|
2027
|
-
mkdirSync3(
|
|
2028
|
-
|
|
3098
|
+
mkdirSync3(path11.dirname(file), { recursive: true });
|
|
3099
|
+
writeFileSync4(file, next);
|
|
2029
3100
|
};
|
|
2030
3101
|
if (n && !n.other) {
|
|
2031
3102
|
if (!n.misplaced && JSON.stringify(n.argv) === JSON.stringify(argv))
|
|
@@ -2063,19 +3134,19 @@ function uninstallNotify(env) {
|
|
|
2063
3134
|
const file = configFile2(env);
|
|
2064
3135
|
let text;
|
|
2065
3136
|
try {
|
|
2066
|
-
text =
|
|
3137
|
+
text = readFileSync6(file, "utf8");
|
|
2067
3138
|
} catch {
|
|
2068
3139
|
return { file, changed: false };
|
|
2069
3140
|
}
|
|
2070
3141
|
const n = readNotify(text);
|
|
2071
3142
|
if (!n) return { file, changed: false };
|
|
2072
3143
|
if (!n.other) {
|
|
2073
|
-
|
|
3144
|
+
writeFileSync4(file, withoutNotify(text));
|
|
2074
3145
|
return { file, changed: true };
|
|
2075
3146
|
}
|
|
2076
3147
|
const prev = previousNotify(n.argv);
|
|
2077
3148
|
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev))) {
|
|
2078
|
-
|
|
3149
|
+
writeFileSync4(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`));
|
|
2079
3150
|
return { file, changed: true };
|
|
2080
3151
|
}
|
|
2081
3152
|
return { file, changed: false };
|
|
@@ -2091,7 +3162,7 @@ function findRollout(threadId, env = process.env) {
|
|
|
2091
3162
|
return;
|
|
2092
3163
|
}
|
|
2093
3164
|
for (const n of names.sort().reverse()) {
|
|
2094
|
-
const p =
|
|
3165
|
+
const p = path11.join(dir, n);
|
|
2095
3166
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
2096
3167
|
best = p;
|
|
2097
3168
|
return;
|
|
@@ -2135,7 +3206,7 @@ var codex = {
|
|
|
2135
3206
|
];
|
|
2136
3207
|
let text = "";
|
|
2137
3208
|
try {
|
|
2138
|
-
text =
|
|
3209
|
+
text = readFileSync6(configFile2(env), "utf8");
|
|
2139
3210
|
} catch {
|
|
2140
3211
|
}
|
|
2141
3212
|
const n = readNotify(text);
|
|
@@ -2181,7 +3252,7 @@ var codex = {
|
|
|
2181
3252
|
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2182
3253
|
} catch {
|
|
2183
3254
|
}
|
|
2184
|
-
const id = s.session_id;
|
|
3255
|
+
const id = s.session_id ?? s.thread_id ?? s.conversation_id ?? s["thread-id"] ?? s["session-id"];
|
|
2185
3256
|
if (!id) return null;
|
|
2186
3257
|
if (s.transcript_path === null) return null;
|
|
2187
3258
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
@@ -2262,7 +3333,7 @@ var codex = {
|
|
|
2262
3333
|
readTranscript(file, fromLine) {
|
|
2263
3334
|
let text = "";
|
|
2264
3335
|
try {
|
|
2265
|
-
text =
|
|
3336
|
+
text = readFileSync6(file, "utf8");
|
|
2266
3337
|
} catch {
|
|
2267
3338
|
return { turns: [], line: fromLine };
|
|
2268
3339
|
}
|
|
@@ -2276,7 +3347,7 @@ var codex = {
|
|
|
2276
3347
|
backfill(sinceMs, env = process.env) {
|
|
2277
3348
|
const rows2 = [];
|
|
2278
3349
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2279
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
3350
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path11.basename(f));
|
|
2280
3351
|
if (!m?.[1]) continue;
|
|
2281
3352
|
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2282
3353
|
const facts = codexFacts(f);
|
|
@@ -2285,12 +3356,40 @@ var codex = {
|
|
|
2285
3356
|
rows2.push({ session: { id: m[1], ...rest }, started_at, last_at });
|
|
2286
3357
|
}
|
|
2287
3358
|
return rows2;
|
|
2288
|
-
}
|
|
3359
|
+
},
|
|
3360
|
+
limits: (transcript) => codexLimits(transcript)
|
|
2289
3361
|
};
|
|
3362
|
+
var epochIso2 = (t) => typeof t === "number" && t > 0 && t < 1e11 ? new Date(t * 1e3).toISOString() : void 0;
|
|
2290
3363
|
function prune3(o) {
|
|
2291
3364
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
2292
3365
|
return o;
|
|
2293
3366
|
}
|
|
3367
|
+
function codexLimits(file) {
|
|
3368
|
+
const { tail } = ends(file, 0, 256 * 1024);
|
|
3369
|
+
for (const r of parseLines(tail).reverse()) {
|
|
3370
|
+
const p = r.payload;
|
|
3371
|
+
if (r.type !== "event_msg" || p?.type !== "token_count") continue;
|
|
3372
|
+
if (!p.rate_limits || typeof p.rate_limits !== "object") continue;
|
|
3373
|
+
const rl = p.rate_limits;
|
|
3374
|
+
if (rl.limit_id != null && rl.limit_id !== "codex") continue;
|
|
3375
|
+
const windows = [];
|
|
3376
|
+
for (const id of ["primary", "secondary"]) {
|
|
3377
|
+
const w = rl[id];
|
|
3378
|
+
if (typeof w?.used_percent !== "number" || w.used_percent < 0) continue;
|
|
3379
|
+
windows.push(
|
|
3380
|
+
prune3({
|
|
3381
|
+
id,
|
|
3382
|
+
used_percent: w.used_percent,
|
|
3383
|
+
window_minutes: Number.isInteger(w.window_minutes) && Number(w.window_minutes) > 0 ? Number(w.window_minutes) : void 0,
|
|
3384
|
+
resets_at: epochIso2(w.resets_at)
|
|
3385
|
+
})
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
if (!windows.length) return null;
|
|
3389
|
+
return prune3({ windows, plan: typeof rl.plan_type === "string" ? rl.plan_type.slice(0, 64) : void 0 });
|
|
3390
|
+
}
|
|
3391
|
+
return null;
|
|
3392
|
+
}
|
|
2294
3393
|
function codexFacts(file) {
|
|
2295
3394
|
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
2296
3395
|
let H = parseLines(head);
|
|
@@ -2343,38 +3442,71 @@ __export(codex_control_exports, {
|
|
|
2343
3442
|
codexLine: () => codexLine,
|
|
2344
3443
|
codexLogin: () => codexLogin,
|
|
2345
3444
|
codexModeFlags: () => codexModeFlags,
|
|
3445
|
+
codexModels: () => codexModels,
|
|
3446
|
+
defaultLevel: () => defaultLevel3,
|
|
2346
3447
|
findCodex: () => findCodex,
|
|
2347
3448
|
findRollout: () => findRollout,
|
|
3449
|
+
offeredLevels: () => offeredLevels3,
|
|
2348
3450
|
reading: () => reading2
|
|
2349
3451
|
});
|
|
2350
3452
|
import { execFile as execFile2 } from "node:child_process";
|
|
2351
|
-
import
|
|
2352
|
-
import
|
|
3453
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
3454
|
+
import os6 from "node:os";
|
|
3455
|
+
import path12 from "node:path";
|
|
2353
3456
|
var LABEL2 = "Codex";
|
|
2354
|
-
function findCodex(env = process.env, home =
|
|
3457
|
+
function findCodex(env = process.env, home = os6.homedir(), nodeDir = path12.dirname(process.execPath)) {
|
|
2355
3458
|
return findBinary({
|
|
2356
3459
|
override: env.SESSIONPIPE_CODEX,
|
|
2357
3460
|
names: process.platform === "win32" ? ["codex.exe", "codex.cmd"] : ["codex"],
|
|
2358
3461
|
env,
|
|
2359
|
-
dirs: [nodeDir,
|
|
3462
|
+
dirs: [nodeDir, path12.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
2360
3463
|
});
|
|
2361
3464
|
}
|
|
2362
|
-
function codexModeFlags(
|
|
3465
|
+
function codexModeFlags(level, resume = false) {
|
|
3466
|
+
if (level === "inherit") return [];
|
|
3467
|
+
const sandbox = level === "read" ? "read-only" : "workspace-write";
|
|
2363
3468
|
if (resume)
|
|
2364
|
-
return
|
|
3469
|
+
return level === "auto" ? [
|
|
2365
3470
|
"-c",
|
|
2366
3471
|
'sandbox_mode="workspace-write"',
|
|
2367
3472
|
"-c",
|
|
2368
3473
|
'approval_policy="on-request"',
|
|
2369
3474
|
"-c",
|
|
2370
3475
|
'approvals_reviewer="auto_review"'
|
|
2371
|
-
] : ["-c",
|
|
2372
|
-
return
|
|
3476
|
+
] : ["-c", `sandbox_mode="${sandbox}"`];
|
|
3477
|
+
return level === "auto" ? ["--approve-for-me"] : ["-s", sandbox];
|
|
2373
3478
|
}
|
|
3479
|
+
var offeredLevels3 = (mode) => mode === "auto" ? ["read", "safe", "auto"] : ["read", "safe"];
|
|
3480
|
+
var defaultLevel3 = (mode) => mode;
|
|
2374
3481
|
function codexArgs(o) {
|
|
2375
3482
|
const common = ["--json", "--skip-git-repo-check"];
|
|
2376
|
-
|
|
2377
|
-
|
|
3483
|
+
const model = o.model ? ["-m", o.model] : [];
|
|
3484
|
+
if (o.resume)
|
|
3485
|
+
return ["exec", "resume", ...common, ...codexModeFlags(o.permission, true), ...model, "--", o.resume, o.prompt];
|
|
3486
|
+
return [
|
|
3487
|
+
"exec",
|
|
3488
|
+
...common,
|
|
3489
|
+
...o.cwd ? ["-C", o.cwd] : [],
|
|
3490
|
+
...codexModeFlags(o.permission),
|
|
3491
|
+
...model,
|
|
3492
|
+
"--",
|
|
3493
|
+
o.prompt
|
|
3494
|
+
];
|
|
3495
|
+
}
|
|
3496
|
+
function codexModels(env = process.env, home = os6.homedir()) {
|
|
3497
|
+
try {
|
|
3498
|
+
const file = path12.join(env.CODEX_HOME || path12.join(home, ".codex"), "models_cache.json");
|
|
3499
|
+
const j = JSON.parse(readFileSync7(file, "utf8"));
|
|
3500
|
+
if (!Array.isArray(j.models)) return [];
|
|
3501
|
+
const out = [];
|
|
3502
|
+
for (const m of j.models)
|
|
3503
|
+
if (m && m.visibility === "list" && typeof m.slug === "string" && MODEL_RE.test(m.slug) && m.slug.length <= 100) {
|
|
3504
|
+
if (!out.includes(m.slug)) out.push(m.slug);
|
|
3505
|
+
}
|
|
3506
|
+
return out.slice(0, 50);
|
|
3507
|
+
} catch {
|
|
3508
|
+
return [];
|
|
3509
|
+
}
|
|
2378
3510
|
}
|
|
2379
3511
|
var codexEnv = (env = process.env) => jobEnv(env);
|
|
2380
3512
|
var LOGIN_HINT = "Codex isn't signed in on this machine: run `codex login --device-auth` there.";
|
|
@@ -2401,22 +3533,22 @@ function codexLine(line) {
|
|
|
2401
3533
|
return null;
|
|
2402
3534
|
}
|
|
2403
3535
|
if (!j || typeof j !== "object") return null;
|
|
2404
|
-
const
|
|
3536
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
2405
3537
|
switch (j.type) {
|
|
2406
3538
|
case "thread.started":
|
|
2407
|
-
return
|
|
3539
|
+
return str2(j.thread_id) ? { session: str2(j.thread_id) } : null;
|
|
2408
3540
|
case "item.completed": {
|
|
2409
3541
|
const it = j.item;
|
|
2410
|
-
if (it?.type === "agent_message" &&
|
|
2411
|
-
if (it?.type === "error" &&
|
|
3542
|
+
if (it?.type === "agent_message" && str2(it.text)) return { message: str2(it.text) };
|
|
3543
|
+
if (it?.type === "error" && str2(it.message)) return { warning: str2(it.message) };
|
|
2412
3544
|
return null;
|
|
2413
3545
|
}
|
|
2414
3546
|
case "turn.completed":
|
|
2415
3547
|
return { done: true };
|
|
2416
3548
|
case "turn.failed":
|
|
2417
|
-
return { done: true, failed:
|
|
3549
|
+
return { done: true, failed: str2(j.error?.message) || "Codex reported an error" };
|
|
2418
3550
|
case "error":
|
|
2419
|
-
return
|
|
3551
|
+
return str2(j.message) ? { note: str2(j.message) } : null;
|
|
2420
3552
|
default:
|
|
2421
3553
|
return null;
|
|
2422
3554
|
}
|
|
@@ -2467,9 +3599,9 @@ function codexStderr(stderr) {
|
|
|
2467
3599
|
var reading2 = () => new CodexReading();
|
|
2468
3600
|
|
|
2469
3601
|
// src/adapters/devin.ts
|
|
2470
|
-
import { createHash as
|
|
3602
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2471
3603
|
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2472
|
-
import
|
|
3604
|
+
import path13 from "node:path";
|
|
2473
3605
|
|
|
2474
3606
|
// src/readers/sqlite.ts
|
|
2475
3607
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
@@ -2653,11 +3785,11 @@ function foreignEvent(file) {
|
|
|
2653
3785
|
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
2654
3786
|
}
|
|
2655
3787
|
var homeOf = (env) => env.HOME || homeDir();
|
|
2656
|
-
var appData = (env) => env.APPDATA ||
|
|
2657
|
-
var devinConfigDir = (env = process.env) => process.platform === "win32" ?
|
|
2658
|
-
var configFile3 = (env) =>
|
|
2659
|
-
var devinDataDir = (env = process.env) => process.platform === "win32" ?
|
|
2660
|
-
var sessionsDb = (env) =>
|
|
3788
|
+
var appData = (env) => env.APPDATA || path13.join(homeOf(env), "AppData", "Roaming");
|
|
3789
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path13.join(appData(env), "devin") : path13.join(env.XDG_CONFIG_HOME || path13.join(homeOf(env), ".config"), "devin");
|
|
3790
|
+
var configFile3 = (env) => path13.join(devinConfigDir(env), "config.json");
|
|
3791
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path13.join(appData(env), "devin", "cli") : path13.join(env.XDG_DATA_HOME || path13.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
3792
|
+
var sessionsDb = (env) => path13.join(devinDataDir(env), "sessions.db");
|
|
2661
3793
|
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
2662
3794
|
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2663
3795
|
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
@@ -2708,7 +3840,7 @@ var devin = {
|
|
|
2708
3840
|
const tool = validTool4(s.tool_name) ? s.tool_name : void 0;
|
|
2709
3841
|
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
2710
3842
|
const response = s.tool_response && typeof s.tool_response === "object" && !Array.isArray(s.tool_response) ? s.tool_response : void 0;
|
|
2711
|
-
const permissionId = () => `perm-${
|
|
3843
|
+
const permissionId = () => `perm-${createHash3("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
2712
3844
|
const ev = (type, data) => ({ type, data: prune4(data), harnessEvent: event });
|
|
2713
3845
|
const events = [];
|
|
2714
3846
|
switch (event) {
|
|
@@ -2829,7 +3961,7 @@ var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
|
2829
3961
|
function sessionFacts(row) {
|
|
2830
3962
|
const out = {};
|
|
2831
3963
|
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
2832
|
-
if (typeof row.working_directory === "string" &&
|
|
3964
|
+
if (typeof row.working_directory === "string" && path13.isAbsolute(row.working_directory))
|
|
2833
3965
|
out.cwd = row.working_directory;
|
|
2834
3966
|
const started = Number(row.created_at);
|
|
2835
3967
|
const last = Number(row.last_activity_at);
|
|
@@ -2874,8 +4006,8 @@ function readDevinChain(db, session, fromLine) {
|
|
|
2874
4006
|
}
|
|
2875
4007
|
function devinVersion(env = process.env) {
|
|
2876
4008
|
try {
|
|
2877
|
-
const target = readlinkSync(
|
|
2878
|
-
const name =
|
|
4009
|
+
const target = readlinkSync(path13.join(devinDataDir(env), "_versions", "current"));
|
|
4010
|
+
const name = path13.basename(target);
|
|
2879
4011
|
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
2880
4012
|
} catch {
|
|
2881
4013
|
return void 0;
|
|
@@ -2889,8 +4021,8 @@ function devinAccount(env = process.env) {
|
|
|
2889
4021
|
|
|
2890
4022
|
// src/adapters/gemini-cli.ts
|
|
2891
4023
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
2892
|
-
import { existsSync as existsSync7, readFileSync as
|
|
2893
|
-
import
|
|
4024
|
+
import { existsSync as existsSync7, readFileSync as readFileSync8 } from "node:fs";
|
|
4025
|
+
import path14 from "node:path";
|
|
2894
4026
|
var NAME5 = "gemini-cli";
|
|
2895
4027
|
var GEMINI_EVENTS = [
|
|
2896
4028
|
"SessionStart",
|
|
@@ -2903,26 +4035,31 @@ var GEMINI_EVENTS = [
|
|
|
2903
4035
|
"SessionEnd"
|
|
2904
4036
|
];
|
|
2905
4037
|
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
2906
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2907
|
-
var settings = (env) =>
|
|
4038
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path14.join(homeDir(), ".gemini");
|
|
4039
|
+
var settings = (env) => path14.join(geminiDir(env), "settings.json");
|
|
2908
4040
|
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
2909
4041
|
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
4042
|
+
function geminiVersion(platform = process.platform, env = process.env) {
|
|
4043
|
+
const win = platform === "win32";
|
|
4044
|
+
const bin = win ? findBinary({ names: ["gemini.exe", "gemini.cmd"], dirs: [path14.dirname(process.execPath)], env }) : "gemini";
|
|
4045
|
+
if (!bin) throw new Error("no gemini on PATH");
|
|
4046
|
+
const o = {
|
|
4047
|
+
encoding: "utf8",
|
|
4048
|
+
timeout: 4e3,
|
|
4049
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4050
|
+
env
|
|
4051
|
+
};
|
|
4052
|
+
const out = /\.cmd$/i.test(bin) ? execFileSync5(`"${bin}" --version`, { ...o, shell: true }) : execFileSync5(bin, ["--version"], o);
|
|
4053
|
+
return out.trim();
|
|
4054
|
+
}
|
|
2910
4055
|
function geminiHooksActive(settingsFile) {
|
|
2911
|
-
|
|
2912
|
-
try {
|
|
2913
|
-
tools = JSON.parse(readFileSync6(settingsFile, "utf8")).tools;
|
|
2914
|
-
} catch {
|
|
2915
|
-
}
|
|
4056
|
+
const tools = readJson(settingsFile).tools;
|
|
2916
4057
|
if (tools?.enableHooks === true) return { active: true };
|
|
2917
4058
|
if (tools?.enableHooks === false)
|
|
2918
4059
|
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
2919
4060
|
let version = "";
|
|
2920
4061
|
try {
|
|
2921
|
-
version =
|
|
2922
|
-
encoding: "utf8",
|
|
2923
|
-
timeout: 4e3,
|
|
2924
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
2925
|
-
}).trim();
|
|
4062
|
+
version = geminiVersion();
|
|
2926
4063
|
} catch {
|
|
2927
4064
|
return {
|
|
2928
4065
|
active: true,
|
|
@@ -2940,7 +4077,7 @@ var geminiCli = {
|
|
|
2940
4077
|
name: NAME5,
|
|
2941
4078
|
events: GEMINI_EVENTS,
|
|
2942
4079
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2943
|
-
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(
|
|
4080
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path14.join(geminiDir(env), "tmp")),
|
|
2944
4081
|
configFiles: (env = process.env) => [settings(env)],
|
|
2945
4082
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2946
4083
|
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
@@ -3038,7 +4175,7 @@ var geminiCli = {
|
|
|
3038
4175
|
readTranscript(file, fromLine) {
|
|
3039
4176
|
let text = "";
|
|
3040
4177
|
try {
|
|
3041
|
-
text =
|
|
4178
|
+
text = readFileSync8(file, "utf8");
|
|
3042
4179
|
} catch {
|
|
3043
4180
|
return { turns: [], line: fromLine };
|
|
3044
4181
|
}
|
|
@@ -3076,21 +4213,23 @@ function prune5(o) {
|
|
|
3076
4213
|
// src/adapters/index.ts
|
|
3077
4214
|
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
3078
4215
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
4216
|
+
var HELLO_HARNESSES_MAX = 20;
|
|
3079
4217
|
function drivableHarnesses(env = process.env) {
|
|
3080
4218
|
return [
|
|
3081
4219
|
...findClaude(env) ? ["claude-code"] : [],
|
|
3082
4220
|
...findCodex(env) ? ["codex"] : [],
|
|
3083
|
-
...findAgy(env) ? ["antigravity"] : []
|
|
3084
|
-
|
|
4221
|
+
...findAgy(env) ? ["antigravity"] : [],
|
|
4222
|
+
...acpHarnesses(env)
|
|
4223
|
+
].slice(0, HELLO_HARNESSES_MAX);
|
|
3085
4224
|
}
|
|
3086
4225
|
|
|
3087
4226
|
// src/config.ts
|
|
3088
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as
|
|
3089
|
-
import
|
|
4227
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
4228
|
+
import path15 from "node:path";
|
|
3090
4229
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
3091
4230
|
function readConfig(file = configFile()) {
|
|
3092
4231
|
try {
|
|
3093
|
-
const j = JSON.parse(
|
|
4232
|
+
const j = JSON.parse(readFileSync9(file, "utf8"));
|
|
3094
4233
|
return {
|
|
3095
4234
|
...DEFAULT_CONFIG,
|
|
3096
4235
|
...j,
|
|
@@ -3102,8 +4241,8 @@ function readConfig(file = configFile()) {
|
|
|
3102
4241
|
}
|
|
3103
4242
|
}
|
|
3104
4243
|
function writeConfig(c, file = configFile()) {
|
|
3105
|
-
mkdirSync4(
|
|
3106
|
-
|
|
4244
|
+
mkdirSync4(path15.dirname(file), { recursive: true, mode: 448 });
|
|
4245
|
+
writeFileSync5(file, `${JSON.stringify(c, null, 2)}
|
|
3107
4246
|
`, { mode: 384 });
|
|
3108
4247
|
if (process.platform !== "win32" && existsSync8(file)) {
|
|
3109
4248
|
try {
|
|
@@ -3118,7 +4257,7 @@ function machineName(c, hostname) {
|
|
|
3118
4257
|
}
|
|
3119
4258
|
|
|
3120
4259
|
// src/ulid.ts
|
|
3121
|
-
import { randomBytes } from "node:crypto";
|
|
4260
|
+
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
3122
4261
|
var ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
3123
4262
|
var lastTime = 0;
|
|
3124
4263
|
var lastRand = [];
|
|
@@ -3143,7 +4282,7 @@ function ulid(now = Date.now()) {
|
|
|
3143
4282
|
}
|
|
3144
4283
|
} else {
|
|
3145
4284
|
lastTime = now;
|
|
3146
|
-
lastRand = Array.from(
|
|
4285
|
+
lastRand = Array.from(randomBytes2(16), (b) => b % 32);
|
|
3147
4286
|
}
|
|
3148
4287
|
return encodeTime(now) + lastRand.map((v) => ALPHABET[v]).join("");
|
|
3149
4288
|
}
|
|
@@ -3167,6 +4306,22 @@ function makeEvent(a, ctx) {
|
|
|
3167
4306
|
};
|
|
3168
4307
|
}
|
|
3169
4308
|
|
|
4309
|
+
// src/launched-by.ts
|
|
4310
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
4311
|
+
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
4312
|
+
function launchedBy(env) {
|
|
4313
|
+
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
4314
|
+
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
4315
|
+
}
|
|
4316
|
+
function readHook(adapter, input) {
|
|
4317
|
+
const r = adapter.fromHook(input);
|
|
4318
|
+
if (r && !r.session.parent_id) {
|
|
4319
|
+
const launcher = launchedBy(input.env);
|
|
4320
|
+
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
4321
|
+
}
|
|
4322
|
+
return r;
|
|
4323
|
+
}
|
|
4324
|
+
|
|
3170
4325
|
// src/outbox.ts
|
|
3171
4326
|
import {
|
|
3172
4327
|
appendFileSync,
|
|
@@ -3175,14 +4330,14 @@ import {
|
|
|
3175
4330
|
mkdirSync as mkdirSync5,
|
|
3176
4331
|
openSync as openSync3,
|
|
3177
4332
|
readdirSync as readdirSync6,
|
|
3178
|
-
readFileSync as
|
|
4333
|
+
readFileSync as readFileSync10,
|
|
3179
4334
|
readSync as readSync3,
|
|
3180
4335
|
renameSync,
|
|
3181
4336
|
statSync as statSync6,
|
|
3182
4337
|
unlinkSync as unlinkSync3,
|
|
3183
|
-
writeFileSync as
|
|
4338
|
+
writeFileSync as writeFileSync6
|
|
3184
4339
|
} from "node:fs";
|
|
3185
|
-
import
|
|
4340
|
+
import path16 from "node:path";
|
|
3186
4341
|
var READ_CHUNK = 1024 * 1024;
|
|
3187
4342
|
var READ_MAX = 8 * 1024 * 1024;
|
|
3188
4343
|
var Outbox = class {
|
|
@@ -3191,13 +4346,13 @@ var Outbox = class {
|
|
|
3191
4346
|
}
|
|
3192
4347
|
root;
|
|
3193
4348
|
dir(harness) {
|
|
3194
|
-
return
|
|
4349
|
+
return path16.join(this.root, "outbox", safeId(harness));
|
|
3195
4350
|
}
|
|
3196
4351
|
file(ref) {
|
|
3197
|
-
return
|
|
4352
|
+
return path16.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
3198
4353
|
}
|
|
3199
4354
|
seqFile(ref) {
|
|
3200
|
-
return
|
|
4355
|
+
return path16.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
3201
4356
|
}
|
|
3202
4357
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
3203
4358
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
@@ -3205,19 +4360,19 @@ var Outbox = class {
|
|
|
3205
4360
|
const f = this.seqFile(ref);
|
|
3206
4361
|
let n = -1;
|
|
3207
4362
|
try {
|
|
3208
|
-
const t =
|
|
4363
|
+
const t = readFileSync10(f, "utf8").trim();
|
|
3209
4364
|
if (/^\d+$/.test(t)) n = Number(t);
|
|
3210
4365
|
} catch {
|
|
3211
4366
|
}
|
|
3212
4367
|
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
3213
|
-
mkdirSync5(
|
|
3214
|
-
|
|
4368
|
+
mkdirSync5(path16.dirname(f), { recursive: true, mode: 448 });
|
|
4369
|
+
writeFileSync6(f, String(n + 1), { mode: 384 });
|
|
3215
4370
|
return n;
|
|
3216
4371
|
}
|
|
3217
4372
|
lastSeqInFile(ref) {
|
|
3218
4373
|
const f = this.file(ref);
|
|
3219
4374
|
if (!existsSync9(f)) return -1;
|
|
3220
|
-
const lines =
|
|
4375
|
+
const lines = readFileSync10(f, "utf8").trim().split("\n");
|
|
3221
4376
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3222
4377
|
try {
|
|
3223
4378
|
const j = JSON.parse(lines[i]);
|
|
@@ -3236,10 +4391,10 @@ var Outbox = class {
|
|
|
3236
4391
|
/** Every session file, newest first. */
|
|
3237
4392
|
sessions() {
|
|
3238
4393
|
const out = [];
|
|
3239
|
-
const base =
|
|
4394
|
+
const base = path16.join(this.root, "outbox");
|
|
3240
4395
|
if (!existsSync9(base)) return [];
|
|
3241
4396
|
for (const h of readdirSync6(base)) {
|
|
3242
|
-
const d =
|
|
4397
|
+
const d = path16.join(base, h);
|
|
3243
4398
|
let names = [];
|
|
3244
4399
|
try {
|
|
3245
4400
|
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
@@ -3248,7 +4403,7 @@ var Outbox = class {
|
|
|
3248
4403
|
}
|
|
3249
4404
|
for (const n of names) {
|
|
3250
4405
|
try {
|
|
3251
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(
|
|
4406
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path16.join(d, n)).mtimeMs });
|
|
3252
4407
|
} catch {
|
|
3253
4408
|
}
|
|
3254
4409
|
}
|
|
@@ -3336,20 +4491,20 @@ var Cursors = class {
|
|
|
3336
4491
|
root;
|
|
3337
4492
|
sink;
|
|
3338
4493
|
file() {
|
|
3339
|
-
return
|
|
4494
|
+
return path16.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
3340
4495
|
}
|
|
3341
4496
|
read() {
|
|
3342
4497
|
try {
|
|
3343
|
-
return JSON.parse(
|
|
4498
|
+
return JSON.parse(readFileSync10(this.file(), "utf8"));
|
|
3344
4499
|
} catch {
|
|
3345
4500
|
return {};
|
|
3346
4501
|
}
|
|
3347
4502
|
}
|
|
3348
4503
|
write(c) {
|
|
3349
4504
|
const f = this.file();
|
|
3350
|
-
mkdirSync5(
|
|
4505
|
+
mkdirSync5(path16.dirname(f), { recursive: true, mode: 448 });
|
|
3351
4506
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
3352
|
-
|
|
4507
|
+
writeFileSync6(tmp, JSON.stringify(c), { mode: 384 });
|
|
3353
4508
|
renameSync(tmp, f);
|
|
3354
4509
|
}
|
|
3355
4510
|
key(ref) {
|
|
@@ -3360,16 +4515,16 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
3360
4515
|
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
3361
4516
|
}
|
|
3362
4517
|
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
3363
|
-
const dir =
|
|
4518
|
+
const dir = path16.join(root, "locks");
|
|
3364
4519
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
3365
|
-
const f =
|
|
4520
|
+
const f = path16.join(dir, `${name}.lock`);
|
|
3366
4521
|
try {
|
|
3367
|
-
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(
|
|
4522
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync10(f, "utf8")))) unlinkSync3(f);
|
|
3368
4523
|
else return null;
|
|
3369
4524
|
} catch {
|
|
3370
4525
|
}
|
|
3371
4526
|
try {
|
|
3372
|
-
|
|
4527
|
+
writeFileSync6(f, String(process.pid), { flag: "wx" });
|
|
3373
4528
|
} catch {
|
|
3374
4529
|
return null;
|
|
3375
4530
|
}
|
|
@@ -3391,7 +4546,7 @@ function processAlive(pid) {
|
|
|
3391
4546
|
}
|
|
3392
4547
|
|
|
3393
4548
|
// src/privacy/pii.ts
|
|
3394
|
-
import { createHash as
|
|
4549
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3395
4550
|
var RULESET_PII = "pii@1";
|
|
3396
4551
|
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
3397
4552
|
var PHONE = /(?<![\w.])(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w)/g;
|
|
@@ -3413,7 +4568,7 @@ function reducePii(text, home) {
|
|
|
3413
4568
|
return out;
|
|
3414
4569
|
}
|
|
3415
4570
|
function hashMachine(name) {
|
|
3416
|
-
return
|
|
4571
|
+
return createHash4("sha256").update(name).digest("hex").slice(0, 8);
|
|
3417
4572
|
}
|
|
3418
4573
|
function reducePiiDeep(v, home) {
|
|
3419
4574
|
if (typeof v === "string") return reducePii(v, home);
|
|
@@ -3604,6 +4759,7 @@ var TYPE_TIER = {
|
|
|
3604
4759
|
"subagent.started": 1,
|
|
3605
4760
|
"subagent.ended": 1,
|
|
3606
4761
|
"context.compacted": 1,
|
|
4762
|
+
"limits.observed": 1,
|
|
3607
4763
|
native: 3
|
|
3608
4764
|
};
|
|
3609
4765
|
var DATA_ALLOW = {
|
|
@@ -3612,8 +4768,8 @@ var DATA_ALLOW = {
|
|
|
3612
4768
|
"session.ended": { 0: ["reason"] },
|
|
3613
4769
|
"session.backfill": { 0: ["started_at", "last_at", "turns"] },
|
|
3614
4770
|
"session.forgotten": { 0: [] },
|
|
3615
|
-
"turn.started": { 0: ["turn_id"], 1: ["prompt_chars"] },
|
|
3616
|
-
"turn.ended": { 0: ["turn_id", "reason", "ms"], 1: ["error"] },
|
|
4771
|
+
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
4772
|
+
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3617
4773
|
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
3618
4774
|
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3619
4775
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
@@ -3623,7 +4779,8 @@ var DATA_ALLOW = {
|
|
|
3623
4779
|
"attention.cleared": { 0: ["attention_id", "how"] },
|
|
3624
4780
|
"subagent.started": { 1: ["agent_id", "agent_type"] },
|
|
3625
4781
|
"subagent.ended": { 1: ["agent_id", "agent_type"] },
|
|
3626
|
-
"context.compacted": { 1: ["trigger"] }
|
|
4782
|
+
"context.compacted": { 1: ["trigger"] },
|
|
4783
|
+
"limits.observed": { 1: ["windows", "plan", "account_id"] }
|
|
3627
4784
|
};
|
|
3628
4785
|
var SESSION_ALLOW = [
|
|
3629
4786
|
"id",
|
|
@@ -3637,7 +4794,9 @@ var SESSION_ALLOW = [
|
|
|
3637
4794
|
"title_source",
|
|
3638
4795
|
"url",
|
|
3639
4796
|
"account_id",
|
|
3640
|
-
"
|
|
4797
|
+
"attended",
|
|
4798
|
+
"parent_id",
|
|
4799
|
+
"launched_by"
|
|
3641
4800
|
];
|
|
3642
4801
|
var MAX_SIDE = 2e4;
|
|
3643
4802
|
var MAX_IO = 64 * 1024;
|
|
@@ -3686,7 +4845,9 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3686
4845
|
const s = reducePiiDeep(out.session, opts.home);
|
|
3687
4846
|
if (typeof s.machine === "string") s.machine = hashMachine(s.machine);
|
|
3688
4847
|
delete s.account_id;
|
|
3689
|
-
|
|
4848
|
+
const d = reducePiiDeep(out.data, opts.home);
|
|
4849
|
+
delete d.account_id;
|
|
4850
|
+
out = { ...out, session: s, data: d };
|
|
3690
4851
|
rulesets.push(RULESET_PII);
|
|
3691
4852
|
}
|
|
3692
4853
|
out.privacy = { rulesets, pii };
|
|
@@ -3694,14 +4855,14 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3694
4855
|
}
|
|
3695
4856
|
|
|
3696
4857
|
// src/short-id.ts
|
|
3697
|
-
import { createHash as
|
|
4858
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
3698
4859
|
function shortId(harness, id) {
|
|
3699
|
-
return
|
|
4860
|
+
return createHash5("sha256").update(`sessionpipe.session:${harness}:${id}`).digest("hex").slice(0, 8);
|
|
3700
4861
|
}
|
|
3701
4862
|
|
|
3702
4863
|
// src/sinks/file.ts
|
|
3703
4864
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3704
|
-
import
|
|
4865
|
+
import path17 from "node:path";
|
|
3705
4866
|
var FileSink = class {
|
|
3706
4867
|
constructor(name, file, tier, pii) {
|
|
3707
4868
|
this.name = name;
|
|
@@ -3715,7 +4876,7 @@ var FileSink = class {
|
|
|
3715
4876
|
pii;
|
|
3716
4877
|
async send(events) {
|
|
3717
4878
|
try {
|
|
3718
|
-
mkdirSync6(
|
|
4879
|
+
mkdirSync6(path17.dirname(this.file), { recursive: true });
|
|
3719
4880
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3720
4881
|
`);
|
|
3721
4882
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3757,7 +4918,9 @@ export {
|
|
|
3757
4918
|
DEFAULT_CONFIG,
|
|
3758
4919
|
DEVIN_EVENTS,
|
|
3759
4920
|
FileSink,
|
|
4921
|
+
HELLO_HARNESSES_MAX,
|
|
3760
4922
|
JOB_TIMEOUT_MS,
|
|
4923
|
+
LAUNCHED_BY_ENV,
|
|
3761
4924
|
Outbox,
|
|
3762
4925
|
PROTOCOL_VERSION,
|
|
3763
4926
|
REDACTED,
|
|
@@ -3765,6 +4928,7 @@ export {
|
|
|
3765
4928
|
RULESET_SECRETS,
|
|
3766
4929
|
StdoutSink,
|
|
3767
4930
|
TYPE_TIER,
|
|
4931
|
+
acp_control_exports as acpControl,
|
|
3768
4932
|
adapterByName,
|
|
3769
4933
|
antigravity,
|
|
3770
4934
|
antigravityAnswer,
|
|
@@ -3774,6 +4938,7 @@ export {
|
|
|
3774
4938
|
claudeCode,
|
|
3775
4939
|
claude_code_control_exports as claudeControl,
|
|
3776
4940
|
claudeDirs,
|
|
4941
|
+
claude_code_pool_exports as claudePool,
|
|
3777
4942
|
codex,
|
|
3778
4943
|
codex_control_exports as codexControl,
|
|
3779
4944
|
configFile,
|
|
@@ -3792,6 +4957,7 @@ export {
|
|
|
3792
4957
|
isSecretName,
|
|
3793
4958
|
isUlid,
|
|
3794
4959
|
jobEnv,
|
|
4960
|
+
launchedBy,
|
|
3795
4961
|
machineName,
|
|
3796
4962
|
makeEvent,
|
|
3797
4963
|
notClaudeCode,
|
|
@@ -3803,6 +4969,7 @@ export {
|
|
|
3803
4969
|
readConfig,
|
|
3804
4970
|
readDevinChain,
|
|
3805
4971
|
readGemini,
|
|
4972
|
+
readHook,
|
|
3806
4973
|
redactDeep,
|
|
3807
4974
|
redactSecrets,
|
|
3808
4975
|
reducePii,
|
|
@@ -3816,6 +4983,7 @@ export {
|
|
|
3816
4983
|
takeNamedLock,
|
|
3817
4984
|
tilde,
|
|
3818
4985
|
ulid,
|
|
4986
|
+
untilde,
|
|
3819
4987
|
writeConfig
|
|
3820
4988
|
};
|
|
3821
4989
|
//# sourceMappingURL=index.js.map
|