sessionpipe-core 0.7.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/control.js +172 -12
- package/dist/control.js.map +2 -2
- package/dist/index.js +1508 -431
- package/dist/index.js.map +4 -4
- package/dist/schema/v1.d.ts +405 -104
- package/dist/schema/v1.d.ts.map +1 -1
- package/dist/schema.js +174 -28
- 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/antigravity.d.ts +1 -2
- package/dist/src/adapters/antigravity.d.ts.map +1 -1
- package/dist/src/adapters/claude-code-control.d.ts +41 -4
- package/dist/src/adapters/claude-code-control.d.ts.map +1 -1
- package/dist/src/adapters/claude-code.d.ts +10 -0
- package/dist/src/adapters/claude-code.d.ts.map +1 -1
- package/dist/src/adapters/claude-shaped.d.ts.map +1 -1
- package/dist/src/adapters/codex-config.d.ts +2 -2
- 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-rollout.d.ts +8 -0
- package/dist/src/adapters/codex-rollout.d.ts.map +1 -0
- package/dist/src/adapters/codex.d.ts +1 -1
- package/dist/src/adapters/codex.d.ts.map +1 -1
- package/dist/src/adapters/devin.d.ts +32 -0
- package/dist/src/adapters/devin.d.ts.map +1 -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 +11 -3
- package/dist/src/adapters/index.d.ts.map +1 -1
- package/dist/src/adapters/types.d.ts +9 -1
- package/dist/src/adapters/types.d.ts.map +1 -1
- package/dist/src/control/verify.d.ts +96 -13
- package/dist/src/control/verify.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -2
- 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/outbox.d.ts +13 -2
- package/dist/src/outbox.d.ts.map +1 -1
- package/dist/src/paths.d.ts +7 -1
- package/dist/src/paths.d.ts.map +1 -1
- package/dist/src/privacy/filter.d.ts +2 -0
- package/dist/src/privacy/filter.d.ts.map +1 -1
- package/dist/src/privacy/index.d.ts +1 -1
- package/dist/src/privacy/index.d.ts.map +1 -1
- package/dist/src/readers/files.d.ts.map +1 -1
- package/dist/src/readers/git.d.ts.map +1 -1
- package/dist/src/readers/sqlite.d.ts +23 -0
- package/dist/src/readers/sqlite.d.ts.map +1 -0
- package/dist/src/readers/transcripts.d.ts +5 -2
- package/dist/src/readers/transcripts.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/short-id.d.ts +2 -0
- package/dist/src/short-id.d.ts.map +1 -0
- package/dist/src/sinks/file.d.ts.map +1 -1
- package/dist/src/sinks/stdout.d.ts.map +1 -1
- package/dist/src/types.d.ts +1 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/ulid.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 +11 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,525 @@
|
|
|
1
|
-
// sessionpipe-core 0.
|
|
1
|
+
// sessionpipe-core 0.10.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/acp-control.ts
|
|
9
|
+
var acp_control_exports = {};
|
|
10
|
+
__export(acp_control_exports, {
|
|
11
|
+
ACP_AGENTS: () => ACP_AGENTS,
|
|
12
|
+
ACP_HARNESSES: () => ACP_HARNESSES,
|
|
13
|
+
acpAgent: () => acpAgent,
|
|
14
|
+
acpHarnesses: () => acpHarnesses,
|
|
15
|
+
acpOverride: () => acpOverride,
|
|
16
|
+
errorWords: () => errorWords,
|
|
17
|
+
findAcpAgent: () => findAcpAgent,
|
|
18
|
+
permissionChoice: () => permissionChoice,
|
|
19
|
+
startAcp: () => startAcp
|
|
20
|
+
});
|
|
21
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
22
|
+
import os from "node:os";
|
|
23
|
+
import path2 from "node:path";
|
|
24
|
+
|
|
25
|
+
// src/adapters/headless.ts
|
|
26
|
+
import { spawn } from "node:child_process";
|
|
27
|
+
import { accessSync, constants, statSync } from "node:fs";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
30
|
+
var OUT_CAP = 1024 * 1024;
|
|
31
|
+
function isExe(p) {
|
|
32
|
+
try {
|
|
33
|
+
if (!statSync(p).isFile()) return false;
|
|
34
|
+
accessSync(p, constants.X_OK);
|
|
35
|
+
return true;
|
|
36
|
+
} catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function findBinary(o) {
|
|
41
|
+
if (o.override && isExe(o.override)) return o.override;
|
|
42
|
+
for (const d of String(o.env?.PATH || "").split(path.delimiter)) {
|
|
43
|
+
if (!d || !path.isAbsolute(d)) continue;
|
|
44
|
+
for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);
|
|
45
|
+
}
|
|
46
|
+
for (const d of o.dirs) for (const n of o.names) if (isExe(path.join(d, n))) return path.join(d, n);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function jobEnv(env = process.env) {
|
|
50
|
+
return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
|
|
51
|
+
}
|
|
52
|
+
function runHeadless(bin, args, o) {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
let out = [];
|
|
55
|
+
let outLen = 0;
|
|
56
|
+
let tooBig = false;
|
|
57
|
+
let errTail = "";
|
|
58
|
+
let timedOut = false;
|
|
59
|
+
let settled = false;
|
|
60
|
+
let child;
|
|
61
|
+
try {
|
|
62
|
+
child = spawn(bin, args, {
|
|
63
|
+
cwd: o.cwd,
|
|
64
|
+
env: o.env,
|
|
65
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
66
|
+
detached: process.platform !== "win32",
|
|
67
|
+
windowsHide: true
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
resolve({ error: e.message });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const kill = (sig) => {
|
|
74
|
+
try {
|
|
75
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
76
|
+
else child.kill(sig);
|
|
77
|
+
} catch {
|
|
78
|
+
try {
|
|
79
|
+
child.kill(sig);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const keep = (line) => {
|
|
85
|
+
if (!o.keep?.(line)) return;
|
|
86
|
+
out = [Buffer.from(line)];
|
|
87
|
+
outLen = line.length;
|
|
88
|
+
};
|
|
89
|
+
let partial = "";
|
|
90
|
+
child.stdout?.on("data", (b) => {
|
|
91
|
+
if (o.onLine) {
|
|
92
|
+
const chunk = partial + b.toString("utf8");
|
|
93
|
+
const lines = chunk.split("\n");
|
|
94
|
+
partial = lines.pop();
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
if (!line.trim()) continue;
|
|
97
|
+
try {
|
|
98
|
+
o.onLine(line);
|
|
99
|
+
} catch {
|
|
100
|
+
}
|
|
101
|
+
keep(line);
|
|
102
|
+
}
|
|
103
|
+
if (partial.length > OUT_CAP) partial = "";
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (tooBig) return;
|
|
107
|
+
if (outLen + b.length > OUT_CAP) {
|
|
108
|
+
tooBig = true;
|
|
109
|
+
out = [];
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
out.push(b);
|
|
113
|
+
outLen += b.length;
|
|
114
|
+
});
|
|
115
|
+
child.stderr?.on("data", (b) => {
|
|
116
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
117
|
+
});
|
|
118
|
+
const timer = setTimeout(() => {
|
|
119
|
+
timedOut = true;
|
|
120
|
+
kill("SIGTERM");
|
|
121
|
+
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
122
|
+
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
123
|
+
const result = (code, signal) => ({
|
|
124
|
+
code,
|
|
125
|
+
signal,
|
|
126
|
+
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
127
|
+
tooBig,
|
|
128
|
+
stderr: errTail,
|
|
129
|
+
timedOut
|
|
130
|
+
});
|
|
131
|
+
const finish = (r) => {
|
|
132
|
+
if (settled) return;
|
|
133
|
+
settled = true;
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
resolve(r);
|
|
136
|
+
};
|
|
137
|
+
child.on("error", (e) => finish({ error: e.message }));
|
|
138
|
+
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
139
|
+
child.on("close", (code, signal) => {
|
|
140
|
+
if (o.onLine && partial.trim()) {
|
|
141
|
+
try {
|
|
142
|
+
o.onLine(partial);
|
|
143
|
+
} catch {
|
|
144
|
+
}
|
|
145
|
+
keep(partial);
|
|
146
|
+
partial = "";
|
|
147
|
+
}
|
|
148
|
+
finish(result(code, signal));
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
function stderrTail(stderr, n = 3) {
|
|
153
|
+
return String(stderr || "").trim().split("\n").slice(-n).join(" ").trim();
|
|
154
|
+
}
|
|
155
|
+
function deniedNote(names, count) {
|
|
156
|
+
if (!count) return "";
|
|
157
|
+
return `
|
|
158
|
+
|
|
159
|
+
(Not allowed on this machine, since nobody was there to approve: ${names.join(", ") || `${count} action(s)`}.)`;
|
|
160
|
+
}
|
|
161
|
+
function runFailure(label, res) {
|
|
162
|
+
if (res.error) return `couldn't start ${label}: ${res.error}`;
|
|
163
|
+
if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/adapters/acp-control.ts
|
|
168
|
+
var ACP_AGENTS = [
|
|
169
|
+
{ harness: "gemini-cli", registry: "gemini", label: "Gemini CLI", bin: "gemini", args: ["--acp"] },
|
|
170
|
+
{ harness: "devin", registry: "devin", label: "Devin", bin: "devin", args: ["acp"] },
|
|
171
|
+
{ harness: "opencode", registry: "opencode", label: "OpenCode", bin: "opencode", args: ["acp"] },
|
|
172
|
+
{ harness: "cursor", registry: "cursor", label: "Cursor", bin: "cursor-agent", args: ["acp"] },
|
|
173
|
+
{ harness: "copilot-cli", registry: "github-copilot-cli", label: "Copilot CLI", bin: "copilot", args: ["--acp"] },
|
|
174
|
+
{
|
|
175
|
+
harness: "droid",
|
|
176
|
+
registry: "factory-droid",
|
|
177
|
+
label: "Droid",
|
|
178
|
+
bin: "droid",
|
|
179
|
+
args: ["exec", "--output-format", "acp-daemon"]
|
|
180
|
+
},
|
|
181
|
+
{ harness: "goose", registry: "goose", label: "goose", bin: "goose", args: ["acp"] },
|
|
182
|
+
{ harness: "kilo", registry: "kilo", label: "Kilo", bin: "kilo", args: ["acp"] },
|
|
183
|
+
{
|
|
184
|
+
harness: "qwen-code",
|
|
185
|
+
registry: "qwen-code",
|
|
186
|
+
label: "Qwen Code",
|
|
187
|
+
bin: "qwen",
|
|
188
|
+
args: ["--acp", "--experimental-skills"]
|
|
189
|
+
},
|
|
190
|
+
{ harness: "cline", registry: "cline", label: "Cline", bin: "cline", args: ["--acp"] },
|
|
191
|
+
{ harness: "auggie", registry: "auggie", label: "Auggie", bin: "auggie", args: ["--acp"] },
|
|
192
|
+
{ harness: "kimi", registry: "kimi", label: "Kimi CLI", bin: "kimi", args: ["acp"] },
|
|
193
|
+
{ harness: "amp", registry: "amp-acp", label: "Amp", bin: "amp-acp", args: [] },
|
|
194
|
+
{ harness: "mistral-vibe", registry: "mistral-vibe", label: "Mistral Vibe", bin: "vibe-acp", args: [] },
|
|
195
|
+
{ harness: "bob", registry: "bob", label: "Bob", bin: "bob", args: ["acp"] },
|
|
196
|
+
{ harness: "grok-build", registry: "grok-build", label: "Grok Build", bin: "grok", args: ["agent", "stdio"] },
|
|
197
|
+
{ harness: "qoder", registry: "qoder", label: "Qoder CLI", bin: "qodercli", args: ["--acp"] },
|
|
198
|
+
{ harness: "minimax-code", registry: "minimax-code", label: "MiniMax Code", bin: "mcode", args: ["acp"] },
|
|
199
|
+
{ harness: "codebuddy-code", registry: "codebuddy-code", label: "Codebuddy Code", bin: "codebuddy", args: ["--acp"] },
|
|
200
|
+
{ harness: "nova", registry: "nova", label: "Nova", bin: "nova", args: ["acp"] },
|
|
201
|
+
{ harness: "dimcode", registry: "dimcode", label: "DimCode", bin: "dim", args: ["acp"] },
|
|
202
|
+
{ harness: "dirac", registry: "dirac", label: "Dirac", bin: "dirac", args: ["--acp"] },
|
|
203
|
+
{ harness: "harn", registry: "harn", label: "Harn", bin: "harn", args: ["serve", "acp"] },
|
|
204
|
+
{ harness: "vtcode", registry: "vtcode", label: "VT Code", bin: "vtcode", args: ["acp"] },
|
|
205
|
+
{ harness: "stakpak", registry: "stakpak", label: "Stakpak", bin: "stakpak", args: ["acp"] },
|
|
206
|
+
{ harness: "crow-cli", registry: "crow-cli", label: "crow-cli", bin: "crow-cli", args: ["acp"] },
|
|
207
|
+
{ harness: "corust-agent", registry: "corust-agent", label: "Corust Agent", bin: "corust-agent-acp", args: [] },
|
|
208
|
+
{ harness: "cortex-code", registry: "cortex-code", label: "Cortex Code", bin: "cortex", args: ["acp", "serve"] },
|
|
209
|
+
{ harness: "kimchi", registry: "kimchi", label: "Kimchi", bin: "kimchi", args: ["--mode", "acp"] }
|
|
210
|
+
];
|
|
211
|
+
var ACP_HARNESSES = ACP_AGENTS.map((a) => a.harness);
|
|
212
|
+
var acpAgent = (harness) => ACP_AGENTS.find((a) => a.harness === harness);
|
|
213
|
+
var acpOverride = (harness) => `SESSIONPIPE_ACP_${harness.toUpperCase().replace(/-/g, "_")}`;
|
|
214
|
+
function findAcpAgent(harness, env = process.env, home = os.homedir(), nodeDir = path2.dirname(process.execPath)) {
|
|
215
|
+
const a = acpAgent(harness);
|
|
216
|
+
if (!a) return null;
|
|
217
|
+
const bin = findBinary({
|
|
218
|
+
override: env[acpOverride(harness)],
|
|
219
|
+
names: process.platform === "win32" ? [`${a.bin}.exe`, `${a.bin}.cmd`] : [a.bin],
|
|
220
|
+
env,
|
|
221
|
+
dirs: [
|
|
222
|
+
nodeDir,
|
|
223
|
+
path2.join(home, ".local", "bin"),
|
|
224
|
+
path2.join(home, ".cargo", "bin"),
|
|
225
|
+
"/opt/homebrew/bin",
|
|
226
|
+
"/usr/local/bin"
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
return bin ? { bin, args: a.args } : null;
|
|
230
|
+
}
|
|
231
|
+
function acpHarnesses(env = process.env) {
|
|
232
|
+
return ACP_HARNESSES.filter((h) => findAcpAgent(h, env));
|
|
233
|
+
}
|
|
234
|
+
var obj = (v) => v && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
235
|
+
var str = (v) => typeof v === "string" ? v : "";
|
|
236
|
+
function permissionChoice(options, mode) {
|
|
237
|
+
const list = (Array.isArray(options) ? options : []).map(obj).filter((o) => !!o && !!str(o.optionId)).map((o) => ({ optionId: str(o.optionId), kind: str(o.kind) }));
|
|
238
|
+
const want = mode === "auto" ? ["allow_once", "allow_always"] : ["reject_once", "reject_always"];
|
|
239
|
+
for (const k of want) {
|
|
240
|
+
const hit = list.find((o) => o.kind === k);
|
|
241
|
+
if (hit) return hit;
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
function errorWords(err) {
|
|
246
|
+
const message = str(err.message);
|
|
247
|
+
const data = str(err.data) || str(obj(err.data)?.message);
|
|
248
|
+
return data && data !== message ? message ? `${message}: ${data}` : data : message;
|
|
249
|
+
}
|
|
250
|
+
function toolName(call, titles) {
|
|
251
|
+
if (!call) return null;
|
|
252
|
+
const t = str(call.title) || titles.get(str(call.toolCallId)) || str(call.name) || str(call.toolCallId);
|
|
253
|
+
return t ? t.slice(0, 80) : null;
|
|
254
|
+
}
|
|
255
|
+
var CLIENT_INFO = { name: "sessionpipe", title: "sessionpipe control", version: "1" };
|
|
256
|
+
function startAcp(o) {
|
|
257
|
+
const now = o.now ?? Date.now;
|
|
258
|
+
const log = o.log ?? (() => {
|
|
259
|
+
});
|
|
260
|
+
const label = o.label;
|
|
261
|
+
let child;
|
|
262
|
+
try {
|
|
263
|
+
child = spawn2(o.bin, o.args, {
|
|
264
|
+
cwd: o.cwd,
|
|
265
|
+
env: jobEnv(o.env),
|
|
266
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
267
|
+
detached: process.platform !== "win32",
|
|
268
|
+
windowsHide: true
|
|
269
|
+
});
|
|
270
|
+
} catch (e) {
|
|
271
|
+
return Promise.resolve({ failed: `couldn't start ${label}: ${e.message}` });
|
|
272
|
+
}
|
|
273
|
+
const kill = (sig) => {
|
|
274
|
+
try {
|
|
275
|
+
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
276
|
+
else child.kill(sig);
|
|
277
|
+
} catch {
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
let gone = null;
|
|
281
|
+
let errTail = "";
|
|
282
|
+
let nextId = 1;
|
|
283
|
+
const pending = /* @__PURE__ */ new Map();
|
|
284
|
+
const write = (m) => {
|
|
285
|
+
if (gone) return;
|
|
286
|
+
try {
|
|
287
|
+
child.stdin?.write(`${JSON.stringify(m)}
|
|
288
|
+
`);
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
const request = (method, params, timeoutMs) => new Promise((resolve, reject) => {
|
|
293
|
+
if (gone) return reject(new Error(gone));
|
|
294
|
+
const id = nextId++;
|
|
295
|
+
const t = setTimeout(() => {
|
|
296
|
+
pending.delete(id);
|
|
297
|
+
reject(new Error(`${label} didn't answer ${method} in ${Math.round(timeoutMs / 1e3)} s`));
|
|
298
|
+
}, timeoutMs);
|
|
299
|
+
t.unref();
|
|
300
|
+
pending.set(id, {
|
|
301
|
+
resolve: (r) => {
|
|
302
|
+
clearTimeout(t);
|
|
303
|
+
resolve(r);
|
|
304
|
+
},
|
|
305
|
+
reject: (e) => {
|
|
306
|
+
clearTimeout(t);
|
|
307
|
+
reject(e);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
write({ jsonrpc: "2.0", id, method, params });
|
|
311
|
+
});
|
|
312
|
+
const notify = (method, params) => write({ jsonrpc: "2.0", method, params });
|
|
313
|
+
let turn = null;
|
|
314
|
+
const titles = /* @__PURE__ */ new Map();
|
|
315
|
+
const onUpdate = (u) => {
|
|
316
|
+
if (!u) return;
|
|
317
|
+
const kind = str(u.sessionUpdate);
|
|
318
|
+
if (kind === "tool_call" && str(u.toolCallId) && str(u.title)) {
|
|
319
|
+
if (titles.size >= 200) titles.clear();
|
|
320
|
+
titles.set(str(u.toolCallId), str(u.title));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (kind !== "agent_message_chunk" || !turn) return;
|
|
324
|
+
const c = obj(u.content);
|
|
325
|
+
if (c?.type !== "text" || !str(c.text)) return;
|
|
326
|
+
const mid = str(u.messageId) || null;
|
|
327
|
+
if (turn.message !== void 0 && mid !== turn.message && turn.text.trim())
|
|
328
|
+
turn.text = `${turn.text.trimEnd()}
|
|
329
|
+
|
|
330
|
+
`;
|
|
331
|
+
turn.message = mid;
|
|
332
|
+
turn.text += str(c.text);
|
|
333
|
+
o.onText?.(turn.text);
|
|
334
|
+
};
|
|
335
|
+
const onRequest = (id, method, params) => {
|
|
336
|
+
if (method !== "session/request_permission") {
|
|
337
|
+
write({ jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${method}` } });
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const pick = permissionChoice(params?.options, o.mode);
|
|
341
|
+
if (!pick?.kind.startsWith("allow")) {
|
|
342
|
+
const name = toolName(obj(params?.toolCall), titles);
|
|
343
|
+
if (turn) {
|
|
344
|
+
turn.count++;
|
|
345
|
+
if (name && !turn.denied.includes(name) && turn.denied.length < 20) turn.denied.push(name);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
write({
|
|
349
|
+
jsonrpc: "2.0",
|
|
350
|
+
id,
|
|
351
|
+
result: { outcome: pick ? { outcome: "selected", optionId: pick.optionId } : { outcome: "cancelled" } }
|
|
352
|
+
});
|
|
353
|
+
};
|
|
354
|
+
const onLine = (line) => {
|
|
355
|
+
let m;
|
|
356
|
+
try {
|
|
357
|
+
m = obj(JSON.parse(line));
|
|
358
|
+
} catch {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
if (!m) return;
|
|
362
|
+
const method = str(m.method);
|
|
363
|
+
if (method) {
|
|
364
|
+
if (m.id !== void 0) onRequest(m.id, method, obj(m.params));
|
|
365
|
+
else if (method === "session/update") onUpdate(obj(obj(m.params)?.update));
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const p = typeof m.id === "number" ? pending.get(m.id) : void 0;
|
|
369
|
+
if (!p) return;
|
|
370
|
+
pending.delete(m.id);
|
|
371
|
+
const err = obj(m.error);
|
|
372
|
+
if (err) p.reject(new Error(errorWords(err) || `${label} returned error ${String(err.code ?? "?")}`));
|
|
373
|
+
else p.resolve(m.result);
|
|
374
|
+
};
|
|
375
|
+
let partial = "";
|
|
376
|
+
child.stdout?.on("data", (b) => {
|
|
377
|
+
const lines = (partial + b.toString("utf8")).split("\n");
|
|
378
|
+
partial = lines.pop();
|
|
379
|
+
for (const l of lines) if (l.trim()) onLine(l);
|
|
380
|
+
if (partial.length > 4 * 1024 * 1024) partial = "";
|
|
381
|
+
});
|
|
382
|
+
child.stderr?.on("data", (b) => {
|
|
383
|
+
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
384
|
+
});
|
|
385
|
+
const left = (why) => {
|
|
386
|
+
if (gone) return;
|
|
387
|
+
gone = why;
|
|
388
|
+
for (const p of pending.values()) p.reject(new Error(why));
|
|
389
|
+
pending.clear();
|
|
390
|
+
};
|
|
391
|
+
child.on("error", (e) => left(`couldn't start ${label}: ${e.message}`));
|
|
392
|
+
const exited = (code, signal) => {
|
|
393
|
+
const tail = errTail.trim().split("\n").slice(-3).join(" ").trim();
|
|
394
|
+
left(`${label} exited (${signal || `code ${code}`})${tail ? `: ${tail}` : ""}`);
|
|
395
|
+
};
|
|
396
|
+
child.on("exit", exited);
|
|
397
|
+
child.on("close", exited);
|
|
398
|
+
let closed = false;
|
|
399
|
+
const close = () => {
|
|
400
|
+
if (closed) return;
|
|
401
|
+
closed = true;
|
|
402
|
+
try {
|
|
403
|
+
child.stdin?.end();
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
const grace = o.closeGraceMs ?? 2e3;
|
|
407
|
+
const t = setTimeout(() => kill("SIGTERM"), grace);
|
|
408
|
+
t.unref();
|
|
409
|
+
const k = setTimeout(() => kill("SIGKILL"), grace * 6);
|
|
410
|
+
k.unref();
|
|
411
|
+
child.once("exit", () => {
|
|
412
|
+
clearTimeout(t);
|
|
413
|
+
clearTimeout(k);
|
|
414
|
+
});
|
|
415
|
+
};
|
|
416
|
+
const open = o.openTimeoutMs ?? 6e4;
|
|
417
|
+
function held(session) {
|
|
418
|
+
let busy = false;
|
|
419
|
+
let lastUsed = now();
|
|
420
|
+
return {
|
|
421
|
+
session,
|
|
422
|
+
mode: "api",
|
|
423
|
+
get busy() {
|
|
424
|
+
return busy;
|
|
425
|
+
},
|
|
426
|
+
get lastUsed() {
|
|
427
|
+
return lastUsed;
|
|
428
|
+
},
|
|
429
|
+
get closed() {
|
|
430
|
+
return closed || !!gone;
|
|
431
|
+
},
|
|
432
|
+
async send(text) {
|
|
433
|
+
if (closed) return { reply: "", error: `${label} was closed` };
|
|
434
|
+
busy = true;
|
|
435
|
+
lastUsed = now();
|
|
436
|
+
const t = { text: "", denied: [], count: 0 };
|
|
437
|
+
turn = t;
|
|
438
|
+
const limit = o.timeoutMs ?? JOB_TIMEOUT_MS;
|
|
439
|
+
let timedOut = false;
|
|
440
|
+
const timer = setTimeout(() => {
|
|
441
|
+
timedOut = true;
|
|
442
|
+
notify("session/cancel", { sessionId: session });
|
|
443
|
+
setTimeout(close, 5e3).unref();
|
|
444
|
+
}, limit);
|
|
445
|
+
timer.unref();
|
|
446
|
+
try {
|
|
447
|
+
const res = obj(
|
|
448
|
+
await request("session/prompt", { sessionId: session, prompt: [{ type: "text", text }] }, limit + 1e4)
|
|
449
|
+
);
|
|
450
|
+
const stop = str(res?.stopReason);
|
|
451
|
+
const words = t.text.trim() || `(${label} finished without a written reply.)`;
|
|
452
|
+
const refused = deniedNote(t.denied, t.count);
|
|
453
|
+
if (stop === "end_turn" || stop === "max_tokens") return { reply: words + refused };
|
|
454
|
+
if (stop === "refusal") return { reply: `${words}
|
|
455
|
+
|
|
456
|
+
(${label} refused the request.)${refused}` };
|
|
457
|
+
if (stop === "cancelled")
|
|
458
|
+
return timedOut ? {
|
|
459
|
+
reply: "",
|
|
460
|
+
error: `${label} was still working after ${Math.round(limit / 6e4)} minutes, so it was stopped`
|
|
461
|
+
} : { reply: `${words}
|
|
462
|
+
|
|
463
|
+
(The turn was cancelled.)${refused}` };
|
|
464
|
+
return { reply: "", error: `${label} ended the turn with ${stop || "no stop reason"}` };
|
|
465
|
+
} catch (e) {
|
|
466
|
+
return { reply: "", error: e.message };
|
|
467
|
+
} finally {
|
|
468
|
+
clearTimeout(timer);
|
|
469
|
+
turn = null;
|
|
470
|
+
busy = false;
|
|
471
|
+
lastUsed = now();
|
|
472
|
+
}
|
|
473
|
+
},
|
|
474
|
+
async interrupt() {
|
|
475
|
+
notify("session/cancel", { sessionId: session });
|
|
476
|
+
},
|
|
477
|
+
close
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
return (async () => {
|
|
481
|
+
let signIn = "";
|
|
482
|
+
try {
|
|
483
|
+
const init = obj(
|
|
484
|
+
await request(
|
|
485
|
+
"initialize",
|
|
486
|
+
{
|
|
487
|
+
protocolVersion: 1,
|
|
488
|
+
clientCapabilities: { fs: { readTextFile: false, writeTextFile: false }, terminal: false },
|
|
489
|
+
clientInfo: CLIENT_INFO
|
|
490
|
+
},
|
|
491
|
+
open
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
const caps = obj(init?.agentCapabilities);
|
|
495
|
+
const info = obj(init?.agentInfo);
|
|
496
|
+
log(
|
|
497
|
+
`${label}: ${str(info?.name) || "agent"} ${str(info?.version)} (ACP ${String(init?.protocolVersion ?? "?")})`
|
|
498
|
+
);
|
|
499
|
+
const methods = Array.isArray(init?.authMethods) ? init.authMethods.map(obj) : [];
|
|
500
|
+
if (methods.length) {
|
|
501
|
+
const how = str(methods[0]?.description) || str(methods[0]?.name);
|
|
502
|
+
signIn = ` (if ${label} wants a sign-in, do it at the machine's terminal${how ? `: ${how}` : ""})`;
|
|
503
|
+
}
|
|
504
|
+
if (o.load) {
|
|
505
|
+
if (!caps?.loadSession) {
|
|
506
|
+
close();
|
|
507
|
+
return { unsupported: `${label} can't reopen a session once it has been closed (no session/load)` };
|
|
508
|
+
}
|
|
509
|
+
await request("session/load", { sessionId: o.load, cwd: o.cwd, mcpServers: [] }, open);
|
|
510
|
+
return { held: held(o.load) };
|
|
511
|
+
}
|
|
512
|
+
const res = obj(await request("session/new", { cwd: o.cwd, mcpServers: [] }, open));
|
|
513
|
+
const session = str(res?.sessionId);
|
|
514
|
+
if (!session) throw new Error(`${label} opened a session without naming it`);
|
|
515
|
+
return { held: held(session) };
|
|
516
|
+
} catch (e) {
|
|
517
|
+
close();
|
|
518
|
+
return { failed: `${e.message}${signIn}` };
|
|
519
|
+
}
|
|
520
|
+
})();
|
|
521
|
+
}
|
|
522
|
+
|
|
8
523
|
// src/adapters/antigravity.ts
|
|
9
524
|
import {
|
|
10
525
|
existsSync as existsSync2,
|
|
@@ -12,38 +527,42 @@ import {
|
|
|
12
527
|
readdirSync as readdirSync2,
|
|
13
528
|
readFileSync as readFileSync2,
|
|
14
529
|
realpathSync,
|
|
15
|
-
statSync as
|
|
530
|
+
statSync as statSync3,
|
|
16
531
|
unlinkSync as unlinkSync2,
|
|
17
532
|
writeFileSync as writeFileSync2
|
|
18
533
|
} from "node:fs";
|
|
19
|
-
import
|
|
534
|
+
import path6 from "node:path";
|
|
20
535
|
|
|
21
536
|
// src/paths.ts
|
|
22
|
-
import
|
|
23
|
-
import
|
|
24
|
-
var
|
|
537
|
+
import os2 from "node:os";
|
|
538
|
+
import path3 from "node:path";
|
|
539
|
+
var homeDir = () => os2.homedir();
|
|
25
540
|
function configFile(env = process.env) {
|
|
26
|
-
if (env.SESSIONPIPE_CONFIG) return
|
|
541
|
+
if (env.SESSIONPIPE_CONFIG) return path3.resolve(env.SESSIONPIPE_CONFIG);
|
|
27
542
|
if (process.platform === "win32")
|
|
28
|
-
return
|
|
29
|
-
return
|
|
543
|
+
return path3.join(env.APPDATA || path3.join(homeDir(), "AppData", "Roaming"), "sessionpipe", "config.json");
|
|
544
|
+
return path3.join(env.XDG_CONFIG_HOME || path3.join(homeDir(), ".config"), "sessionpipe", "config.json");
|
|
30
545
|
}
|
|
31
546
|
function stateDir(env = process.env) {
|
|
32
|
-
if (env.SESSIONPIPE_STATE) return
|
|
547
|
+
if (env.SESSIONPIPE_STATE) return path3.resolve(env.SESSIONPIPE_STATE);
|
|
33
548
|
if (process.platform === "win32")
|
|
34
|
-
return
|
|
35
|
-
return
|
|
549
|
+
return path3.join(env.LOCALAPPDATA || path3.join(homeDir(), "AppData", "Local"), "sessionpipe");
|
|
550
|
+
return path3.join(env.XDG_STATE_HOME || path3.join(homeDir(), ".local", "state"), "sessionpipe");
|
|
36
551
|
}
|
|
37
|
-
function tilde(p, home =
|
|
552
|
+
function tilde(p, home = homeDir()) {
|
|
38
553
|
if (!p) return p;
|
|
39
554
|
if (p === home) return "~";
|
|
40
|
-
return p.startsWith(home +
|
|
555
|
+
return p.startsWith(home + path3.sep) || p.startsWith(`${home}/`) ? `~${p.slice(home.length)}` : p;
|
|
556
|
+
}
|
|
557
|
+
function untilde(p, home = homeDir()) {
|
|
558
|
+
if (p === "~") return home;
|
|
559
|
+
return /^~[\\/]/.test(p) ? path3.join(home, p.slice(2)) : p;
|
|
41
560
|
}
|
|
42
561
|
var safeId = (s) => String(s).replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 160);
|
|
43
562
|
|
|
44
563
|
// src/readers/files.ts
|
|
45
|
-
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync } from "node:fs";
|
|
46
|
-
import
|
|
564
|
+
import { closeSync, fstatSync, openSync, readdirSync, readSync, statSync as statSync2 } from "node:fs";
|
|
565
|
+
import path4 from "node:path";
|
|
47
566
|
function ends(file, headBytes, tailBytes) {
|
|
48
567
|
let fd;
|
|
49
568
|
try {
|
|
@@ -115,14 +634,14 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
115
634
|
return;
|
|
116
635
|
}
|
|
117
636
|
for (const e of names) {
|
|
118
|
-
const p =
|
|
637
|
+
const p = path4.join(dir, e.name);
|
|
119
638
|
if (e.isDirectory()) {
|
|
120
639
|
if (d < depth) walk(p, d + 1);
|
|
121
640
|
continue;
|
|
122
641
|
}
|
|
123
642
|
if (!match(e.name)) continue;
|
|
124
643
|
try {
|
|
125
|
-
if (
|
|
644
|
+
if (statSync2(p).mtimeMs >= sinceMs) out.push(p);
|
|
126
645
|
} catch {
|
|
127
646
|
}
|
|
128
647
|
}
|
|
@@ -131,6 +650,19 @@ function recentFiles(root, depth, sinceMs, match) {
|
|
|
131
650
|
return out;
|
|
132
651
|
}
|
|
133
652
|
|
|
653
|
+
// src/adapters/codex-rollout.ts
|
|
654
|
+
function codexSubagent(meta) {
|
|
655
|
+
if (meta?.type !== "session_meta") return false;
|
|
656
|
+
const p = meta.payload;
|
|
657
|
+
return !!p?.source?.subagent;
|
|
658
|
+
}
|
|
659
|
+
function codexReplayBoundary(meta) {
|
|
660
|
+
if (meta?.type !== "session_meta") return void 0;
|
|
661
|
+
const p = meta.payload;
|
|
662
|
+
if (!p || !p.forked_from_id && !p.source?.subagent) return void 0;
|
|
663
|
+
return (record) => typeof p.id === "string" && !!p.id && record?.type === "event_msg" && record.payload?.thread_id === p.id;
|
|
664
|
+
}
|
|
665
|
+
|
|
134
666
|
// src/readers/transcripts.ts
|
|
135
667
|
function pairTurns(msgs, endLine) {
|
|
136
668
|
const turns = [];
|
|
@@ -153,10 +685,14 @@ ${m.text}`;
|
|
|
153
685
|
}
|
|
154
686
|
if (cur?.assistant) turns.push(cur);
|
|
155
687
|
const line = cur && !cur.assistant ? cur.startLine : endLine;
|
|
156
|
-
|
|
688
|
+
const out = {
|
|
689
|
+
turns: turns.map((t) => ({ user: t.user, assistant: t.assistant, at: t.at, endLine: t.endLine })),
|
|
690
|
+
line
|
|
691
|
+
};
|
|
692
|
+
if (cur && !cur.assistant && cur.user) out.open = { user: cur.user, at: cur.at };
|
|
693
|
+
return out;
|
|
157
694
|
}
|
|
158
|
-
var isInjected = (text) => /^<[a-z_-]
|
|
159
|
-
var endOf = (lines) => lines[lines.length - 1] === "" ? lines.length - 1 : lines.length;
|
|
695
|
+
var isInjected = (text) => /^<[a-z_-]+(?:\s[^>]*)?>/i.test(text) && /<\/[a-z_-]+>\s*$/i.test(text) || /^<!--\s/.test(text) || /^<command-/.test(text);
|
|
160
696
|
var parse = (raw) => {
|
|
161
697
|
try {
|
|
162
698
|
return JSON.parse(raw);
|
|
@@ -164,6 +700,11 @@ var parse = (raw) => {
|
|
|
164
700
|
return null;
|
|
165
701
|
}
|
|
166
702
|
};
|
|
703
|
+
var endOf = (lines) => {
|
|
704
|
+
const last = lines[lines.length - 1];
|
|
705
|
+
if (last === void 0) return 0;
|
|
706
|
+
return last === "" || parse(last) === null ? lines.length - 1 : lines.length;
|
|
707
|
+
};
|
|
167
708
|
var textParts = (c, kinds) => {
|
|
168
709
|
if (typeof c === "string") return c;
|
|
169
710
|
if (!Array.isArray(c)) return "";
|
|
@@ -171,13 +712,21 @@ var textParts = (c, kinds) => {
|
|
|
171
712
|
(b) => b && typeof b === "object" && kinds.includes(b.type ?? "") && typeof b.text === "string"
|
|
172
713
|
).map((b) => b.text).join("\n");
|
|
173
714
|
};
|
|
715
|
+
var queuedPrompt = (a) => {
|
|
716
|
+
if (!a || typeof a !== "object") return "";
|
|
717
|
+
const q = a;
|
|
718
|
+
if (q.type !== "queued_command" || (q.commandMode ?? "prompt") !== "prompt") return "";
|
|
719
|
+
if (q.origin?.kind && q.origin.kind !== "human") return "";
|
|
720
|
+
return textParts(q.prompt, ["text"]).trim();
|
|
721
|
+
};
|
|
174
722
|
function readClaude(lines, from) {
|
|
175
723
|
const msgs = [];
|
|
176
724
|
for (let i = from; i < lines.length; i++) {
|
|
177
725
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
178
726
|
if (!j || j.isSidechain) continue;
|
|
179
727
|
const origin = j.origin;
|
|
180
|
-
if (j.type === "user" && (j.isMeta || origin?.kind && origin.kind !== "human"))
|
|
728
|
+
if (j.type === "user" && (j.isMeta || j.isCompactSummary || j.repeatsCompactSummary || origin?.kind && origin.kind !== "human"))
|
|
729
|
+
continue;
|
|
181
730
|
const at = Date.parse(String(j.timestamp ?? "")) || Date.now();
|
|
182
731
|
const c = j.message?.content;
|
|
183
732
|
if (j.type === "user") {
|
|
@@ -187,6 +736,9 @@ function readClaude(lines, from) {
|
|
|
187
736
|
} else if (j.type === "assistant" && Array.isArray(c)) {
|
|
188
737
|
const text = textParts(c, ["text"]).trim();
|
|
189
738
|
if (text) msgs.push({ role: "assistant", text, at, line: i });
|
|
739
|
+
} else if (j.type === "attachment") {
|
|
740
|
+
const text = queuedPrompt(j.attachment);
|
|
741
|
+
if (text && !isInjected(text)) msgs.push({ role: "user", text, at, line: i });
|
|
190
742
|
}
|
|
191
743
|
}
|
|
192
744
|
return pairTurns(msgs, endOf(lines));
|
|
@@ -196,7 +748,10 @@ function codexInjected(text) {
|
|
|
196
748
|
}
|
|
197
749
|
function readCodex(lines, from) {
|
|
198
750
|
const msgs = [];
|
|
199
|
-
|
|
751
|
+
const boundary = codexReplayBoundary(parse(lines.find((line) => line.trim()) ?? ""));
|
|
752
|
+
const start = boundary ? lines.findIndex((line) => boundary(parse(line))) : 0;
|
|
753
|
+
if (start < 0) return pairTurns(msgs, endOf(lines));
|
|
754
|
+
for (let i = Math.max(from, start); i < lines.length; i++) {
|
|
200
755
|
const j = lines[i] ? parse(lines[i]) : null;
|
|
201
756
|
if (!j || j.type !== "response_item") continue;
|
|
202
757
|
const p = j.payload;
|
|
@@ -234,7 +789,7 @@ function readAntigravity(lines, from) {
|
|
|
234
789
|
const at = Date.parse(String(j.created_at ?? j.timestamp ?? "")) || Date.now();
|
|
235
790
|
if (j.type === "USER_INPUT") {
|
|
236
791
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
237
|
-
const text = (m ? m[1]
|
|
792
|
+
const text = (m ? m[1] : j.content).trim();
|
|
238
793
|
if (text) msgs.push({ role: "user", text, at, line: i });
|
|
239
794
|
} else if (j.type === "PLANNER_RESPONSE") {
|
|
240
795
|
const text = j.content.trim();
|
|
@@ -246,7 +801,7 @@ function readAntigravity(lines, from) {
|
|
|
246
801
|
|
|
247
802
|
// src/adapters/claude-shaped.ts
|
|
248
803
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
249
|
-
import
|
|
804
|
+
import path5 from "node:path";
|
|
250
805
|
var OURS = /(?:sessionpipe[^"\n]*[\\/]hook\.js|[\\/]sessionpipe-hook)"?\s+[a-z][a-z-]*\s+[A-Za-z]+\s*$/;
|
|
251
806
|
var isOurs = (h) => Array.isArray(h?.hooks) && h.hooks.some((x) => typeof x.command === "string" && OURS.test(x.command));
|
|
252
807
|
function readJsonFile(file) {
|
|
@@ -282,6 +837,10 @@ function formatJson(value, f) {
|
|
|
282
837
|
const text = JSON.stringify(value, null, f.indent).replace(/\n/g, f.eol);
|
|
283
838
|
return f.finalNewline ? text + f.eol : text;
|
|
284
839
|
}
|
|
840
|
+
function readJson(file) {
|
|
841
|
+
const f = readJsonFile(file);
|
|
842
|
+
return f.ok ? f.value : {};
|
|
843
|
+
}
|
|
285
844
|
function entryFor(event, harness, cmd, o) {
|
|
286
845
|
const matcher = o.matcherFor(event);
|
|
287
846
|
return {
|
|
@@ -323,7 +882,7 @@ function installClaudeShaped(file, harness, events, cmd, o) {
|
|
|
323
882
|
}
|
|
324
883
|
if (changed) {
|
|
325
884
|
settings2.hooks = hooks;
|
|
326
|
-
mkdirSync(
|
|
885
|
+
mkdirSync(path5.dirname(file), { recursive: true });
|
|
327
886
|
writeFileSync(file, formatJson(settings2, f));
|
|
328
887
|
}
|
|
329
888
|
return { file, changed, created: !f.existed && changed };
|
|
@@ -351,8 +910,7 @@ function uninstallClaudeShaped(file, events, opts = {}) {
|
|
|
351
910
|
unlinkSync(file);
|
|
352
911
|
return { file, changed: true, note: "removed (sessionpipe created it)" };
|
|
353
912
|
}
|
|
354
|
-
|
|
355
|
-
if (next !== f.raw) writeFileSync(file, next);
|
|
913
|
+
writeFileSync(file, formatJson(settings2, f));
|
|
356
914
|
return { file, changed: true };
|
|
357
915
|
}
|
|
358
916
|
function installedClaudeShaped(file, harness, events, cmd, o) {
|
|
@@ -379,10 +937,10 @@ var HOOK_NAME = "sessionpipe";
|
|
|
379
937
|
var AG_EVENTS = ["PreInvocation", "PostInvocation", "PostToolUse", "Stop"];
|
|
380
938
|
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';
|
|
381
939
|
var antigravityAnswer = (event) => event === "PreToolUse" ? AG_STALE_PRETOOL : "{}\n";
|
|
382
|
-
var gemini = (env) => env.GEMINI_CLI_HOME ||
|
|
383
|
-
var hooksFile = (env) =>
|
|
384
|
-
var projectsDir = (env) =>
|
|
385
|
-
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) =>
|
|
940
|
+
var gemini = (env) => env.GEMINI_CLI_HOME || path6.join(homeDir(), ".gemini");
|
|
941
|
+
var hooksFile = (env) => path6.join(gemini(env), "config", "hooks.json");
|
|
942
|
+
var projectsDir = (env) => path6.join(gemini(env), "config", "projects");
|
|
943
|
+
var dataDirs = (env) => ["antigravity", "antigravity-cli", "antigravity-ide"].map((d) => path6.join(gemini(env), d));
|
|
386
944
|
var ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
387
945
|
var validTool = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
388
946
|
function entry(cmd) {
|
|
@@ -394,7 +952,7 @@ function entry(cmd) {
|
|
|
394
952
|
var antigravity = {
|
|
395
953
|
name: NAME,
|
|
396
954
|
events: AG_EVENTS,
|
|
397
|
-
detect: (env = process.env) => existsSync2(
|
|
955
|
+
detect: (env = process.env) => existsSync2(path6.join(gemini(env), "config")) || dataDirs(env).some((d) => existsSync2(d)),
|
|
398
956
|
configFiles: (env = process.env) => [hooksFile(env)],
|
|
399
957
|
install(cmd, env = process.env) {
|
|
400
958
|
const file = hooksFile(env);
|
|
@@ -404,7 +962,7 @@ var antigravity = {
|
|
|
404
962
|
const e = entry(cmd);
|
|
405
963
|
if (JSON.stringify(hooks[HOOK_NAME]) === JSON.stringify(e)) return [{ file, changed: false }];
|
|
406
964
|
hooks[HOOK_NAME] = e;
|
|
407
|
-
mkdirSync2(
|
|
965
|
+
mkdirSync2(path6.dirname(file), { recursive: true });
|
|
408
966
|
writeFileSync2(file, formatJson(hooks, f));
|
|
409
967
|
return [
|
|
410
968
|
{
|
|
@@ -445,7 +1003,8 @@ var antigravity = {
|
|
|
445
1003
|
const event = input.argv[1] ?? "";
|
|
446
1004
|
let s = {};
|
|
447
1005
|
try {
|
|
448
|
-
|
|
1006
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
1007
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
449
1008
|
} catch {
|
|
450
1009
|
}
|
|
451
1010
|
const id = s.conversationId ?? s.session_id ?? s.sessionId;
|
|
@@ -501,39 +1060,36 @@ var antigravity = {
|
|
|
501
1060
|
}
|
|
502
1061
|
return readAntigravity(text.split("\n"), fromLine);
|
|
503
1062
|
},
|
|
504
|
-
facts(session, transcript, hints
|
|
1063
|
+
facts(session, transcript, hints) {
|
|
505
1064
|
if (!transcript) return {};
|
|
506
1065
|
return antigravityFacts(transcript, {
|
|
507
1066
|
conversation: session.id,
|
|
508
1067
|
workspaces: hints.workspaces ?? (session.cwd ? [session.cwd] : []),
|
|
509
|
-
state,
|
|
510
1068
|
env: process.env
|
|
511
1069
|
});
|
|
512
1070
|
},
|
|
513
1071
|
backfill(sinceMs, env = process.env) {
|
|
514
|
-
const
|
|
515
|
-
const noState = { get: () => ({}), save: () => {
|
|
516
|
-
} };
|
|
1072
|
+
const rows2 = [];
|
|
517
1073
|
for (const data of dataDirs(env)) {
|
|
518
1074
|
let ids = [];
|
|
519
1075
|
try {
|
|
520
|
-
ids = readdirSync2(
|
|
1076
|
+
ids = readdirSync2(path6.join(data, "brain")).filter((n) => /^[0-9a-f-]{36}$/i.test(n));
|
|
521
1077
|
} catch {
|
|
522
1078
|
}
|
|
523
1079
|
for (const id of ids) {
|
|
524
|
-
const f =
|
|
1080
|
+
const f = path6.join(data, "brain", id, ".system_generated", "logs", "transcript.jsonl");
|
|
525
1081
|
try {
|
|
526
|
-
if (
|
|
1082
|
+
if (statSync3(f).mtimeMs < sinceMs) continue;
|
|
527
1083
|
} catch {
|
|
528
1084
|
continue;
|
|
529
1085
|
}
|
|
530
|
-
const facts = antigravityFacts(f, { conversation: id, workspaces: [],
|
|
1086
|
+
const facts = antigravityFacts(f, { conversation: id, workspaces: [], env });
|
|
531
1087
|
if (!facts.started_at || !facts.last_at) continue;
|
|
532
1088
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
533
|
-
|
|
1089
|
+
rows2.push({ session: { id, ...rest }, started_at, last_at });
|
|
534
1090
|
}
|
|
535
1091
|
}
|
|
536
|
-
return
|
|
1092
|
+
return rows2;
|
|
537
1093
|
}
|
|
538
1094
|
};
|
|
539
1095
|
function prune(o) {
|
|
@@ -551,8 +1107,8 @@ function agPath(p) {
|
|
|
551
1107
|
}
|
|
552
1108
|
if (process.platform === "win32" && /^\/[A-Za-z]:/.test(s)) s = s.slice(1);
|
|
553
1109
|
} else if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s)) return null;
|
|
554
|
-
if (!
|
|
555
|
-
return
|
|
1110
|
+
if (!path6.isAbsolute(s)) return null;
|
|
1111
|
+
return path6.resolve(s);
|
|
556
1112
|
}
|
|
557
1113
|
var agWorkspaces = (paths) => (Array.isArray(paths) ? paths : []).map(agPath).filter((p) => !!p).filter((p, i, a) => a.indexOf(p) === i);
|
|
558
1114
|
function agVariants(p) {
|
|
@@ -577,13 +1133,13 @@ function agProjects(dir) {
|
|
|
577
1133
|
if (!ID_RE.test(id)) continue;
|
|
578
1134
|
let j;
|
|
579
1135
|
try {
|
|
580
|
-
j = JSON.parse(readFileSync2(
|
|
1136
|
+
j = JSON.parse(readFileSync2(path6.join(dir, n), "utf8"));
|
|
581
1137
|
} catch {
|
|
582
1138
|
continue;
|
|
583
1139
|
}
|
|
584
1140
|
const folders = [];
|
|
585
1141
|
for (const r of j?.projectResources?.resources ?? []) {
|
|
586
|
-
const f = agPath(r?.folderUri);
|
|
1142
|
+
const f = agPath(r?.folderUri ?? r?.gitFolder?.folderUri);
|
|
587
1143
|
if (f) folders.push(...agVariants(f));
|
|
588
1144
|
}
|
|
589
1145
|
if (folders.length) out.push({ id, folders });
|
|
@@ -600,7 +1156,7 @@ function agMatchProject(projects, workspaces) {
|
|
|
600
1156
|
let bestLen = -1;
|
|
601
1157
|
for (const pr of projects)
|
|
602
1158
|
for (const f of pr.folders) {
|
|
603
|
-
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(
|
|
1159
|
+
const within = ws.some((w) => w === f || w.startsWith(f.endsWith(path6.sep) ? f : f + path6.sep));
|
|
604
1160
|
if (within && f.length > bestLen) {
|
|
605
1161
|
best = pr.id;
|
|
606
1162
|
bestLen = f.length;
|
|
@@ -609,12 +1165,12 @@ function agMatchProject(projects, workspaces) {
|
|
|
609
1165
|
return best;
|
|
610
1166
|
}
|
|
611
1167
|
function antigravityRemoteUrl(transcript, opts) {
|
|
612
|
-
const i = transcript.indexOf(`${
|
|
1168
|
+
const i = transcript.indexOf(`${path6.sep}brain${path6.sep}`);
|
|
613
1169
|
const dirs = (i > 0 ? [transcript.slice(0, i)] : []).concat(dataDirs(opts.env));
|
|
614
1170
|
for (const d of dirs) {
|
|
615
1171
|
try {
|
|
616
1172
|
const m = /installation_uuid:\s*"([0-9a-f-]{36})"/i.exec(
|
|
617
|
-
readFileSync2(
|
|
1173
|
+
readFileSync2(path6.join(d, "antigravity_state.pbtxt"), "utf8")
|
|
618
1174
|
);
|
|
619
1175
|
if (!m) continue;
|
|
620
1176
|
const base = `https://antigravity.google.com/r/${m[1]}-v2`;
|
|
@@ -637,7 +1193,7 @@ function antigravityFacts(file, opts) {
|
|
|
637
1193
|
for (const j of H) {
|
|
638
1194
|
if (j.type !== "USER_INPUT" || typeof j.content !== "string") continue;
|
|
639
1195
|
const m = /<USER_REQUEST>([\s\S]*?)(?:<\/USER_REQUEST>|$)/.exec(j.content);
|
|
640
|
-
const text = (m ? m[1]
|
|
1196
|
+
const text = (m ? m[1] : j.content).trim();
|
|
641
1197
|
const line = (text.split("\n").find((l) => l.trim()) ?? "").trim();
|
|
642
1198
|
if (!line || /^<[a-z_-]+>/i.test(line)) continue;
|
|
643
1199
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
@@ -661,167 +1217,28 @@ __export(antigravity_control_exports, {
|
|
|
661
1217
|
agyError: () => agyError,
|
|
662
1218
|
agyLine: () => agyLine,
|
|
663
1219
|
agyModeFlags: () => agyModeFlags,
|
|
1220
|
+
defaultLevel: () => defaultLevel,
|
|
664
1221
|
findAgy: () => findAgy,
|
|
1222
|
+
offeredLevels: () => offeredLevels,
|
|
665
1223
|
reading: () => reading
|
|
666
1224
|
});
|
|
667
|
-
import
|
|
668
|
-
import
|
|
669
|
-
|
|
670
|
-
// src/adapters/headless.ts
|
|
671
|
-
import { spawn } from "node:child_process";
|
|
672
|
-
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
673
|
-
import path5 from "node:path";
|
|
674
|
-
var JOB_TIMEOUT_MS = 30 * 6e4;
|
|
675
|
-
var OUT_CAP = 1024 * 1024;
|
|
676
|
-
function isExe(p) {
|
|
677
|
-
try {
|
|
678
|
-
if (!statSync3(p).isFile()) return false;
|
|
679
|
-
accessSync(p, constants.X_OK);
|
|
680
|
-
return true;
|
|
681
|
-
} catch {
|
|
682
|
-
return false;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
function findBinary(o) {
|
|
686
|
-
if (o.override && isExe(o.override)) return o.override;
|
|
687
|
-
for (const d of String(o.env?.PATH || "").split(path5.delimiter)) {
|
|
688
|
-
if (!d || !path5.isAbsolute(d)) continue;
|
|
689
|
-
for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
690
|
-
}
|
|
691
|
-
for (const d of o.dirs) for (const n of o.names) if (isExe(path5.join(d, n))) return path5.join(d, n);
|
|
692
|
-
return null;
|
|
693
|
-
}
|
|
694
|
-
function jobEnv(env = process.env) {
|
|
695
|
-
return { ...env, SESSIONPIPE_CONTROL_JOB: "1" };
|
|
696
|
-
}
|
|
697
|
-
function runHeadless(bin, args, o) {
|
|
698
|
-
return new Promise((resolve) => {
|
|
699
|
-
let out = [];
|
|
700
|
-
let outLen = 0;
|
|
701
|
-
let tooBig = false;
|
|
702
|
-
let errTail = "";
|
|
703
|
-
let timedOut = false;
|
|
704
|
-
let settled = false;
|
|
705
|
-
let child;
|
|
706
|
-
try {
|
|
707
|
-
child = spawn(bin, args, {
|
|
708
|
-
cwd: o.cwd,
|
|
709
|
-
env: o.env,
|
|
710
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
711
|
-
detached: process.platform !== "win32",
|
|
712
|
-
windowsHide: true
|
|
713
|
-
});
|
|
714
|
-
} catch (e) {
|
|
715
|
-
resolve({ error: e.message });
|
|
716
|
-
return;
|
|
717
|
-
}
|
|
718
|
-
const kill = (sig) => {
|
|
719
|
-
try {
|
|
720
|
-
if (process.platform !== "win32" && child.pid) process.kill(-child.pid, sig);
|
|
721
|
-
else child.kill(sig);
|
|
722
|
-
} catch {
|
|
723
|
-
try {
|
|
724
|
-
child.kill(sig);
|
|
725
|
-
} catch {
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
const keep = (line) => {
|
|
730
|
-
if (!o.keep?.(line)) return;
|
|
731
|
-
out = [Buffer.from(line)];
|
|
732
|
-
outLen = line.length;
|
|
733
|
-
};
|
|
734
|
-
let partial = "";
|
|
735
|
-
child.stdout?.on("data", (b) => {
|
|
736
|
-
if (o.onLine) {
|
|
737
|
-
const chunk = partial + b.toString("utf8");
|
|
738
|
-
const lines = chunk.split("\n");
|
|
739
|
-
partial = lines.pop() ?? "";
|
|
740
|
-
for (const line of lines) {
|
|
741
|
-
if (!line.trim()) continue;
|
|
742
|
-
try {
|
|
743
|
-
o.onLine(line);
|
|
744
|
-
} catch {
|
|
745
|
-
}
|
|
746
|
-
keep(line);
|
|
747
|
-
}
|
|
748
|
-
if (partial.length > OUT_CAP) partial = "";
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (tooBig) return;
|
|
752
|
-
if (outLen + b.length > OUT_CAP) {
|
|
753
|
-
tooBig = true;
|
|
754
|
-
out = [];
|
|
755
|
-
return;
|
|
756
|
-
}
|
|
757
|
-
out.push(b);
|
|
758
|
-
outLen += b.length;
|
|
759
|
-
});
|
|
760
|
-
child.stderr?.on("data", (b) => {
|
|
761
|
-
errTail = (errTail + b.toString("utf8")).slice(-4e3);
|
|
762
|
-
});
|
|
763
|
-
const timer = setTimeout(() => {
|
|
764
|
-
timedOut = true;
|
|
765
|
-
kill("SIGTERM");
|
|
766
|
-
setTimeout(() => kill("SIGKILL"), 1e4).unref();
|
|
767
|
-
}, o.timeoutMs ?? JOB_TIMEOUT_MS);
|
|
768
|
-
const result = (code, signal) => ({
|
|
769
|
-
code,
|
|
770
|
-
signal,
|
|
771
|
-
stdout: tooBig ? null : Buffer.concat(out).toString("utf8"),
|
|
772
|
-
tooBig,
|
|
773
|
-
stderr: errTail,
|
|
774
|
-
timedOut
|
|
775
|
-
});
|
|
776
|
-
const finish = (r) => {
|
|
777
|
-
if (settled) return;
|
|
778
|
-
settled = true;
|
|
779
|
-
clearTimeout(timer);
|
|
780
|
-
resolve(r);
|
|
781
|
-
};
|
|
782
|
-
child.on("error", (e) => finish({ error: e.message }));
|
|
783
|
-
child.on("exit", (code, signal) => setTimeout(() => finish(result(code, signal)), 2e3).unref());
|
|
784
|
-
child.on("close", (code, signal) => {
|
|
785
|
-
if (o.onLine && partial.trim()) {
|
|
786
|
-
try {
|
|
787
|
-
o.onLine(partial);
|
|
788
|
-
} catch {
|
|
789
|
-
}
|
|
790
|
-
keep(partial);
|
|
791
|
-
partial = "";
|
|
792
|
-
}
|
|
793
|
-
finish(result(code, signal));
|
|
794
|
-
});
|
|
795
|
-
});
|
|
796
|
-
}
|
|
797
|
-
function stderrTail(stderr, n = 3) {
|
|
798
|
-
return String(stderr || "").trim().split("\n").slice(-n).join(" ").trim();
|
|
799
|
-
}
|
|
800
|
-
function deniedNote(names, count) {
|
|
801
|
-
if (!count) return "";
|
|
802
|
-
return `
|
|
803
|
-
|
|
804
|
-
(Not allowed on this machine, since nobody was there to approve: ${names.join(", ") || `${count} action(s)`}.)`;
|
|
805
|
-
}
|
|
806
|
-
function runFailure(label, res) {
|
|
807
|
-
if (res.error) return `couldn't start ${label}: ${res.error}`;
|
|
808
|
-
if (res.timedOut) return `${label} was still working after 30 minutes, so it was stopped`;
|
|
809
|
-
return null;
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
// src/adapters/antigravity-control.ts
|
|
1225
|
+
import os3 from "node:os";
|
|
1226
|
+
import path7 from "node:path";
|
|
813
1227
|
var LABEL = "Antigravity";
|
|
814
|
-
function findAgy(env = process.env, home =
|
|
1228
|
+
function findAgy(env = process.env, home = os3.homedir()) {
|
|
815
1229
|
return findBinary({
|
|
816
1230
|
override: env.SESSIONPIPE_AGY,
|
|
817
1231
|
names: process.platform === "win32" ? ["agy.exe", "agy.cmd"] : ["agy"],
|
|
818
1232
|
env,
|
|
819
|
-
dirs: [
|
|
1233
|
+
dirs: [path7.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
820
1234
|
});
|
|
821
1235
|
}
|
|
822
|
-
function agyModeFlags(
|
|
823
|
-
|
|
1236
|
+
function agyModeFlags(level) {
|
|
1237
|
+
if (level === "read") throw new Error("Antigravity has no read-only mode");
|
|
1238
|
+
return level === "safe" || level === "inherit" ? [] : ["--mode", "accept-edits"];
|
|
824
1239
|
}
|
|
1240
|
+
var offeredLevels = (mode) => mode === "auto" ? ["safe", "edits"] : ["safe"];
|
|
1241
|
+
var defaultLevel = (mode) => mode === "auto" ? "edits" : mode;
|
|
825
1242
|
function agyArgs(o) {
|
|
826
1243
|
const prompt = o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt;
|
|
827
1244
|
return [
|
|
@@ -830,7 +1247,7 @@ function agyArgs(o) {
|
|
|
830
1247
|
prompt,
|
|
831
1248
|
"--output-format",
|
|
832
1249
|
"stream-json",
|
|
833
|
-
...agyModeFlags(o.
|
|
1250
|
+
...agyModeFlags(o.permission)
|
|
834
1251
|
];
|
|
835
1252
|
}
|
|
836
1253
|
var agyEnv = (env = process.env) => jobEnv(env);
|
|
@@ -842,26 +1259,26 @@ function agyLine(line) {
|
|
|
842
1259
|
return null;
|
|
843
1260
|
}
|
|
844
1261
|
if (!j || typeof j !== "object") return null;
|
|
845
|
-
const
|
|
1262
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
846
1263
|
const out = {};
|
|
847
1264
|
if (j.event === "init") {
|
|
848
|
-
if (
|
|
1265
|
+
if (str2(j.conversation_id)) out.session = str2(j.conversation_id);
|
|
849
1266
|
} else if (j.event === "step_update") {
|
|
850
1267
|
const s = j.step_update ?? {};
|
|
851
|
-
if (
|
|
852
|
-
if (s.step_type === "agent_response" &&
|
|
853
|
-
out.delta = { step: Number(s.step_index ?? 0), text:
|
|
1268
|
+
if (str2(s.conversation_id)) out.session = str2(s.conversation_id);
|
|
1269
|
+
if (s.step_type === "agent_response" && str2(s.text_delta))
|
|
1270
|
+
out.delta = { step: Number(s.step_index ?? 0), text: str2(s.text_delta) };
|
|
854
1271
|
if (s.step_type === "error_message") {
|
|
855
|
-
const why =
|
|
1272
|
+
const why = str2(s.text_delta) || str2(s.error) || str2(s.message);
|
|
856
1273
|
if (why) out.note = why;
|
|
857
1274
|
}
|
|
858
1275
|
} else if (j.event === "result") {
|
|
859
1276
|
const r = j.result ?? {};
|
|
860
|
-
if (
|
|
1277
|
+
if (str2(r.conversation_id)) out.session = str2(r.conversation_id);
|
|
861
1278
|
out.result = {
|
|
862
|
-
status:
|
|
863
|
-
response:
|
|
864
|
-
error:
|
|
1279
|
+
status: str2(r.status),
|
|
1280
|
+
response: str2(r.response),
|
|
1281
|
+
error: str2(r.error),
|
|
865
1282
|
denied: Array.isArray(r.denied_actions) ? r.denied_actions : []
|
|
866
1283
|
};
|
|
867
1284
|
} else return null;
|
|
@@ -885,7 +1302,7 @@ function agyError(stderr) {
|
|
|
885
1302
|
for (const k of ["message", "error", "detail"]) if (typeof j[k] === "string" && j[k]) return j[k];
|
|
886
1303
|
} catch {
|
|
887
1304
|
}
|
|
888
|
-
return raw.split("\n")[0]
|
|
1305
|
+
return raw.split("\n")[0].slice(0, 300);
|
|
889
1306
|
}
|
|
890
1307
|
var AgyReading = class {
|
|
891
1308
|
session = null;
|
|
@@ -933,7 +1350,7 @@ var reading = () => new AgyReading();
|
|
|
933
1350
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
934
1351
|
import { createHash } from "node:crypto";
|
|
935
1352
|
import { existsSync as existsSync3, readdirSync as readdirSync3, readFileSync as readFileSync3, statSync as statSync4 } from "node:fs";
|
|
936
|
-
import
|
|
1353
|
+
import path8 from "node:path";
|
|
937
1354
|
|
|
938
1355
|
// src/readers/git.ts
|
|
939
1356
|
import { execFileSync } from "node:child_process";
|
|
@@ -962,10 +1379,9 @@ function gitFacts(cwd) {
|
|
|
962
1379
|
return out;
|
|
963
1380
|
}
|
|
964
1381
|
function stripCredentials(url) {
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
return u.replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
1382
|
+
const ssh = /^(?:ssh:\/\/)?git@([^:/]+)[:/](.+?)(?:\.git)?$/.exec(url);
|
|
1383
|
+
if (ssh) return `${ssh[1]}/${ssh[2]}`;
|
|
1384
|
+
return url.replace(/\/\/[^/@]*@/, "//").replace(/^https?:\/\//, "").replace(/\.git$/, "");
|
|
969
1385
|
}
|
|
970
1386
|
|
|
971
1387
|
// src/adapters/claude-code.ts
|
|
@@ -996,27 +1412,27 @@ var OPTS = {
|
|
|
996
1412
|
timeoutFor: (e) => e === "PermissionRequest" ? 130 : void 0
|
|
997
1413
|
};
|
|
998
1414
|
var DESKTOP_ID = /^local_[A-Za-z0-9-]{1,64}$/;
|
|
999
|
-
function claudeDirs(env = process.env, home = env.HOME ||
|
|
1415
|
+
function claudeDirs(env = process.env, home = env.HOME || homeDir()) {
|
|
1000
1416
|
const out = [];
|
|
1001
1417
|
const add = (d) => {
|
|
1002
1418
|
if (!d) return;
|
|
1003
|
-
const r =
|
|
1419
|
+
const r = path8.resolve(d);
|
|
1004
1420
|
if (!out.includes(r)) out.push(r);
|
|
1005
1421
|
};
|
|
1006
|
-
add(
|
|
1422
|
+
add(path8.join(home, ".claude"));
|
|
1007
1423
|
add(env.CLAUDE_CONFIG_DIR);
|
|
1008
1424
|
try {
|
|
1009
1425
|
for (const e of readdirSync3(home, { withFileTypes: true })) {
|
|
1010
1426
|
if (!e.isDirectory() || !/^\.claude[-_.]/.test(e.name)) continue;
|
|
1011
|
-
const d =
|
|
1012
|
-
if (existsSync3(
|
|
1427
|
+
const d = path8.join(home, e.name);
|
|
1428
|
+
if (existsSync3(path8.join(d, "projects")) || existsSync3(path8.join(d, "settings.json"))) add(d);
|
|
1013
1429
|
}
|
|
1014
1430
|
} catch {
|
|
1015
1431
|
}
|
|
1016
1432
|
return out.filter((d, i) => i === 0 || existsSync3(d));
|
|
1017
1433
|
}
|
|
1018
|
-
var settingsIn = (dir) =>
|
|
1019
|
-
var configDirOf = (transcript) =>
|
|
1434
|
+
var settingsIn = (dir) => path8.join(dir, "settings.json");
|
|
1435
|
+
var configDirOf = (transcript) => path8.dirname(path8.dirname(path8.dirname(transcript)));
|
|
1020
1436
|
var validTool2 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1021
1437
|
var endReason = (r) => r === "clear" || r === "logout" || r === "exit" ? r : r === "prompt_input_exit" ? "exit" : "other";
|
|
1022
1438
|
var notifKind = (t) => t === "permission_prompt" ? "permission" : t === "idle_prompt" ? "idle" : t === "elicitation_dialog" ? "elicitation" : "question";
|
|
@@ -1037,11 +1453,13 @@ var claudeCode = {
|
|
|
1037
1453
|
const event = input.argv[1] ?? "";
|
|
1038
1454
|
let s = {};
|
|
1039
1455
|
try {
|
|
1040
|
-
|
|
1456
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
1457
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
1041
1458
|
} catch {
|
|
1042
1459
|
}
|
|
1043
1460
|
const id = s.session_id ?? s.sessionId;
|
|
1044
1461
|
if (!id) return null;
|
|
1462
|
+
if (notClaudeCode(String(id), s)) return null;
|
|
1045
1463
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
1046
1464
|
if (typeof s.model === "string") session.model = s.model;
|
|
1047
1465
|
if (typeof s.agent_id === "string" && event.startsWith("Subagent") === false && s.agent_id)
|
|
@@ -1058,14 +1476,28 @@ var claudeCode = {
|
|
|
1058
1476
|
case "SessionStart":
|
|
1059
1477
|
events.push(ev("session.started", { source: pickSource(s.source ?? s.trigger) }));
|
|
1060
1478
|
break;
|
|
1061
|
-
case "UserPromptSubmit":
|
|
1479
|
+
case "UserPromptSubmit": {
|
|
1480
|
+
const prompt = typeof s.prompt === "string" ? s.prompt : void 0;
|
|
1062
1481
|
events.push(
|
|
1063
1482
|
ev("turn.started", {
|
|
1064
1483
|
turn_id,
|
|
1065
|
-
prompt_chars:
|
|
1484
|
+
prompt_chars: prompt === void 0 ? void 0 : [...prompt].length,
|
|
1485
|
+
// Claude Code's hook schema documents `source`
|
|
1486
|
+
// (user | sdk | system | loop_wakeup | schedule_wakeup | poll_event) and says a
|
|
1487
|
+
// payload may omit it while the field rolls out; 2.1.304's payload has no such
|
|
1488
|
+
// key at all. Forwarded verbatim, shape-guarded only: the list is the harness's
|
|
1489
|
+
// to extend.
|
|
1490
|
+
submitted_by: harnessWord(s.source),
|
|
1491
|
+
// One of Claude Code's own prompt wrappers: the prompt is a block the harness
|
|
1492
|
+
// wrote, so it is not the person's words. Absent is unknown, never "the person" —
|
|
1493
|
+
// and `true` is never "no person caused this turn", because a Talk or
|
|
1494
|
+
// `sessionpipe wait` delivery hands the person's own message over inside a
|
|
1495
|
+
// <task-notification> (PROTOCOL.md §4).
|
|
1496
|
+
injected: prompt !== void 0 && injectedBlock(prompt) ? true : void 0
|
|
1066
1497
|
})
|
|
1067
1498
|
);
|
|
1068
1499
|
break;
|
|
1500
|
+
}
|
|
1069
1501
|
case "PreToolUse":
|
|
1070
1502
|
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, agent_id, input: s.tool_input }));
|
|
1071
1503
|
break;
|
|
@@ -1120,7 +1552,13 @@ var claudeCode = {
|
|
|
1120
1552
|
);
|
|
1121
1553
|
break;
|
|
1122
1554
|
case "Stop":
|
|
1123
|
-
events.push(
|
|
1555
|
+
events.push(
|
|
1556
|
+
ev("turn.ended", {
|
|
1557
|
+
turn_id,
|
|
1558
|
+
reason: "stop",
|
|
1559
|
+
background: Array.isArray(s.background_tasks) ? s.background_tasks.length : void 0
|
|
1560
|
+
})
|
|
1561
|
+
);
|
|
1124
1562
|
break;
|
|
1125
1563
|
case "StopFailure":
|
|
1126
1564
|
events.push(
|
|
@@ -1160,8 +1598,11 @@ var claudeCode = {
|
|
|
1160
1598
|
return null;
|
|
1161
1599
|
}
|
|
1162
1600
|
const hints = {};
|
|
1601
|
+
if (event === "UserPromptSubmit" && typeof s.prompt === "string") hints.prompt = s.prompt;
|
|
1163
1602
|
const host = input.env.CLAUDE_CODE_HOST_SESSION_ID;
|
|
1164
1603
|
if (host && DESKTOP_ID.test(host)) hints.desktop_id = host;
|
|
1604
|
+
const attended = input.env.CLAUDE_CODE_SESSION_ATTENDED;
|
|
1605
|
+
if (attended === "1" || attended === "0") hints.attended = attended === "1" || !!input.env.SESSIONPIPE_CONTROL_JOB;
|
|
1165
1606
|
if (event !== "PreToolUse" && event !== "PostToolUse") hints.claude_pid = claudePid(input.env);
|
|
1166
1607
|
const r = { session, events, hints };
|
|
1167
1608
|
if (typeof s.transcript_path === "string") r.transcript = s.transcript_path;
|
|
@@ -1182,37 +1623,45 @@ var claudeCode = {
|
|
|
1182
1623
|
const cwd = out.cwd ?? session.cwd;
|
|
1183
1624
|
Object.assign(out, gitFacts(cwd), out.branch ? { branch: out.branch } : {});
|
|
1184
1625
|
if (!out.url && typeof hints.desktop_id === "string") out.url = `claude://code/continue/${hints.desktop_id}`;
|
|
1626
|
+
if (typeof hints.attended === "boolean") out.attended = hints.attended || !!out.url;
|
|
1185
1627
|
return out;
|
|
1186
1628
|
},
|
|
1187
1629
|
backfill(sinceMs, env = process.env) {
|
|
1188
|
-
const
|
|
1630
|
+
const rows2 = [];
|
|
1189
1631
|
const files = [];
|
|
1190
1632
|
for (const d of claudeDirs(env))
|
|
1191
|
-
files.push(...recentFiles(
|
|
1633
|
+
files.push(...recentFiles(path8.join(d, "projects"), 1, sinceMs, (n) => /^[0-9a-f-]{36}\.jsonl$/.test(n)));
|
|
1192
1634
|
const noState = { get: () => ({}), save: () => {
|
|
1193
1635
|
} };
|
|
1194
1636
|
for (const f of files) {
|
|
1195
|
-
const id =
|
|
1637
|
+
const id = path8.basename(f, ".jsonl");
|
|
1196
1638
|
const facts = claudeFacts(f, id, {}, noState);
|
|
1197
1639
|
if (!facts.started_at || !facts.last_at) continue;
|
|
1198
1640
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
1199
1641
|
const cwd = rest.cwd;
|
|
1200
|
-
|
|
1642
|
+
rows2.push({
|
|
1201
1643
|
session: { id, ...rest, ...gitFacts(cwd), ...rest.branch ? { branch: rest.branch } : {} },
|
|
1202
1644
|
started_at,
|
|
1203
1645
|
last_at
|
|
1204
1646
|
});
|
|
1205
1647
|
}
|
|
1206
|
-
return
|
|
1648
|
+
return rows2;
|
|
1207
1649
|
}
|
|
1208
1650
|
};
|
|
1209
1651
|
function prune2(o) {
|
|
1210
1652
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
1211
1653
|
return o;
|
|
1212
1654
|
}
|
|
1655
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1656
|
+
function notClaudeCode(id, s) {
|
|
1657
|
+
return typeof s.transcript_path !== "string" && !UUID.test(id);
|
|
1658
|
+
}
|
|
1213
1659
|
function pickSource(v) {
|
|
1214
1660
|
return typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
1215
1661
|
}
|
|
1662
|
+
var harnessWord = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_.-]{0,39}$/.test(v) ? v : void 0;
|
|
1663
|
+
var WRAPPER = /^<(?:task-notification|cross-session-message|system-reminder|local-command-stdout|command-[a-z-]+)[\s/>]/i;
|
|
1664
|
+
var injectedBlock = (prompt) => WRAPPER.test(prompt.trim());
|
|
1216
1665
|
function errText(v) {
|
|
1217
1666
|
if (typeof v === "string") return v.slice(0, 2e3);
|
|
1218
1667
|
if (v && typeof v === "object") {
|
|
@@ -1252,12 +1701,12 @@ function claudeLive(dir) {
|
|
|
1252
1701
|
const map = /* @__PURE__ */ new Map();
|
|
1253
1702
|
let names = [];
|
|
1254
1703
|
try {
|
|
1255
|
-
names = readdirSync3(
|
|
1704
|
+
names = readdirSync3(path8.join(dir, "sessions")).filter((n) => n.endsWith(".json"));
|
|
1256
1705
|
} catch {
|
|
1257
1706
|
}
|
|
1258
1707
|
for (const n of names) {
|
|
1259
1708
|
try {
|
|
1260
|
-
const e = registryEntry(JSON.parse(readFileSync3(
|
|
1709
|
+
const e = registryEntry(JSON.parse(readFileSync3(path8.join(dir, "sessions", n), "utf8")));
|
|
1261
1710
|
if (e?.sessionId) map.set(e.sessionId, e);
|
|
1262
1711
|
} catch {
|
|
1263
1712
|
}
|
|
@@ -1268,7 +1717,7 @@ function claudeLive(dir) {
|
|
|
1268
1717
|
function claudeLiveByPid(dir, pid) {
|
|
1269
1718
|
if (!pid) return null;
|
|
1270
1719
|
try {
|
|
1271
|
-
return registryEntry(JSON.parse(readFileSync3(
|
|
1720
|
+
return registryEntry(JSON.parse(readFileSync3(path8.join(dir, "sessions", `${pid}.json`), "utf8")));
|
|
1272
1721
|
} catch {
|
|
1273
1722
|
return null;
|
|
1274
1723
|
}
|
|
@@ -1276,7 +1725,7 @@ function claudeLiveByPid(dir, pid) {
|
|
|
1276
1725
|
function claudePid(env = process.env) {
|
|
1277
1726
|
try {
|
|
1278
1727
|
const dirs = claudeDirs(env);
|
|
1279
|
-
const has = (pid2) => dirs.some((d) => existsSync3(
|
|
1728
|
+
const has = (pid2) => dirs.some((d) => existsSync3(path8.join(d, "sessions", `${pid2}.json`)));
|
|
1280
1729
|
let pid = process.ppid;
|
|
1281
1730
|
if (has(pid)) return pid;
|
|
1282
1731
|
const parent = parentOf();
|
|
@@ -1315,26 +1764,61 @@ function parentOf() {
|
|
|
1315
1764
|
}
|
|
1316
1765
|
return (pid) => table.get(pid) || 0;
|
|
1317
1766
|
}
|
|
1318
|
-
var
|
|
1319
|
-
function
|
|
1320
|
-
const
|
|
1321
|
-
if (
|
|
1322
|
-
const files = [path7.join(dir, ".claude.json")];
|
|
1323
|
-
if (path7.resolve(dir) === path7.join(HOME, ".claude")) files.unshift(path7.join(HOME, ".claude.json"));
|
|
1324
|
-
let acct = null;
|
|
1767
|
+
var EMAIL_RE = /^[^@\s]{1,64}@[^@\s]{1,189}$/;
|
|
1768
|
+
function claudeLogin(dir, home = homeDir()) {
|
|
1769
|
+
const files = [path8.join(dir, ".claude.json")];
|
|
1770
|
+
if (path8.resolve(dir) === path8.join(home, ".claude")) files.unshift(path8.join(home, ".claude.json"));
|
|
1325
1771
|
for (const f of files) {
|
|
1326
1772
|
try {
|
|
1327
1773
|
const a = JSON.parse(readFileSync3(f, "utf8")).oauthAccount;
|
|
1328
|
-
if (a?.accountUuid) {
|
|
1329
|
-
|
|
1330
|
-
|
|
1774
|
+
if (typeof a?.accountUuid === "string" && a.accountUuid) {
|
|
1775
|
+
const email = typeof a.emailAddress === "string" && EMAIL_RE.test(a.emailAddress) ? a.emailAddress : void 0;
|
|
1776
|
+
return { id: a.accountUuid.slice(0, 128), ...email ? { email } : {} };
|
|
1331
1777
|
}
|
|
1332
1778
|
} catch {
|
|
1333
1779
|
}
|
|
1334
1780
|
}
|
|
1781
|
+
return apiAccount(dir);
|
|
1782
|
+
}
|
|
1783
|
+
var ANTHROPIC_HOST = /(^|\.)anthropic\.com$/i;
|
|
1784
|
+
function apiAccount(dir) {
|
|
1785
|
+
let base;
|
|
1786
|
+
try {
|
|
1787
|
+
base = JSON.parse(readFileSync3(settingsIn(dir), "utf8")).env?.ANTHROPIC_BASE_URL;
|
|
1788
|
+
} catch {
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1791
|
+
if (typeof base !== "string") return null;
|
|
1792
|
+
let u;
|
|
1793
|
+
try {
|
|
1794
|
+
u = new URL(base.trim());
|
|
1795
|
+
} catch {
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
if (u.protocol !== "https:" && u.protocol !== "http:" || !u.hostname || ANTHROPIC_HOST.test(u.hostname))
|
|
1799
|
+
return null;
|
|
1800
|
+
const origin = `${u.protocol}//${u.host}`;
|
|
1801
|
+
return { id: `api-${createHash("sha256").update(origin).digest("hex").slice(0, 16)}`, name: u.host };
|
|
1802
|
+
}
|
|
1803
|
+
var accountCache = /* @__PURE__ */ new Map();
|
|
1804
|
+
function claudeAccount(dir) {
|
|
1805
|
+
const hit = accountCache.get(dir);
|
|
1806
|
+
if (hit !== void 0) return hit;
|
|
1807
|
+
const acct = claudeLogin(dir)?.id ?? null;
|
|
1335
1808
|
accountCache.set(dir, acct);
|
|
1336
1809
|
return acct;
|
|
1337
1810
|
}
|
|
1811
|
+
function claudeAccounts(dirs, home = homeDir()) {
|
|
1812
|
+
const out = [];
|
|
1813
|
+
for (const d of dirs) {
|
|
1814
|
+
const l = claudeLogin(d, home);
|
|
1815
|
+
if (!l) continue;
|
|
1816
|
+
const seen = out.find((a) => a.id === l.id);
|
|
1817
|
+
if (!seen) out.push({ harness: NAME2, ...l });
|
|
1818
|
+
else if (!seen.email && l.email) seen.email = l.email;
|
|
1819
|
+
}
|
|
1820
|
+
return out;
|
|
1821
|
+
}
|
|
1338
1822
|
var LINK_RE = /^https:\/\/claude\.ai\/code\/session_[A-Za-z0-9]+$/;
|
|
1339
1823
|
function transcriptLink(records) {
|
|
1340
1824
|
for (let i = records.length - 1; i >= 0; i--) {
|
|
@@ -1405,7 +1889,7 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1405
1889
|
const H = parseLines(head);
|
|
1406
1890
|
const T = parseLines(tail);
|
|
1407
1891
|
const out = {};
|
|
1408
|
-
const id = sessionId ||
|
|
1892
|
+
const id = sessionId || path8.basename(file, ".jsonl");
|
|
1409
1893
|
const mem = state.get(NAME2, id);
|
|
1410
1894
|
let custom = null;
|
|
1411
1895
|
let ai = null;
|
|
@@ -1489,8 +1973,12 @@ function claudeFacts(file, sessionId, hints, state) {
|
|
|
1489
1973
|
var claude_code_control_exports = {};
|
|
1490
1974
|
__export(claude_code_control_exports, {
|
|
1491
1975
|
JOB_TIMEOUT_MS: () => JOB_TIMEOUT_MS,
|
|
1976
|
+
ceilingLevel: () => ceilingLevel,
|
|
1977
|
+
claudeArgs: () => claudeArgs,
|
|
1492
1978
|
claudeEnv: () => claudeEnv,
|
|
1493
|
-
claudeLogin: () =>
|
|
1979
|
+
claudeLogin: () => claudeLogin2,
|
|
1980
|
+
cmpVersion: () => cmpVersion,
|
|
1981
|
+
defaultLevel: () => defaultLevel2,
|
|
1494
1982
|
detectCaps: () => detectCaps,
|
|
1495
1983
|
findClaude: () => findClaude,
|
|
1496
1984
|
findTranscript: () => findTranscript,
|
|
@@ -1499,6 +1987,8 @@ __export(claude_code_control_exports, {
|
|
|
1499
1987
|
loginCommand: () => loginCommand,
|
|
1500
1988
|
loginHint: () => loginHint,
|
|
1501
1989
|
modeFlags: () => modeFlags,
|
|
1990
|
+
offeredLevels: () => offeredLevels2,
|
|
1991
|
+
outputArgs: () => outputArgs,
|
|
1502
1992
|
parseAuthStatus: () => parseAuthStatus,
|
|
1503
1993
|
parseCaps: () => parseCaps,
|
|
1504
1994
|
parseResult: () => parseResult,
|
|
@@ -1508,8 +1998,22 @@ __export(claude_code_control_exports, {
|
|
|
1508
1998
|
});
|
|
1509
1999
|
import { execFile, execFileSync as execFileSync3 } from "node:child_process";
|
|
1510
2000
|
import { closeSync as closeSync2, openSync as openSync2, readdirSync as readdirSync4, readFileSync as readFileSync4, readSync as readSync2, statSync as statSync5 } from "node:fs";
|
|
1511
|
-
import
|
|
1512
|
-
import
|
|
2001
|
+
import os4 from "node:os";
|
|
2002
|
+
import path9 from "node:path";
|
|
2003
|
+
|
|
2004
|
+
// src/control/verify.ts
|
|
2005
|
+
var GRANT_MAX_MS = 24 * 36e5;
|
|
2006
|
+
var SKEW_MS = 5 * 6e4;
|
|
2007
|
+
var MACHINE_MAX_AGE_MS = 24 * 36e5;
|
|
2008
|
+
var RECEIVER_MAX_AGE_MS = 10 * 6e4;
|
|
2009
|
+
var NONCE_TTL_MS = 25 * 36e5;
|
|
2010
|
+
var MODEL_RE = /^[A-Za-z0-9][A-Za-z0-9._:/@[\]-]*$/;
|
|
2011
|
+
var START_PERMISSIONS = ["read", "safe", "edits", "auto"];
|
|
2012
|
+
var levelRank = (l) => START_PERMISSIONS.indexOf(l);
|
|
2013
|
+
var enc = new TextEncoder();
|
|
2014
|
+
var dec = new TextDecoder();
|
|
2015
|
+
|
|
2016
|
+
// src/adapters/claude-code-control.ts
|
|
1513
2017
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1514
2018
|
var LIVE_WRITE_MS = 9e4;
|
|
1515
2019
|
var cmpVersion = (a, b) => {
|
|
@@ -1519,28 +2023,28 @@ var cmpVersion = (a, b) => {
|
|
|
1519
2023
|
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
1520
2024
|
return 0;
|
|
1521
2025
|
};
|
|
1522
|
-
function findClaude(env = process.env, home =
|
|
2026
|
+
function findClaude(env = process.env, home = os4.homedir()) {
|
|
1523
2027
|
if (env.SESSIONPIPE_CLAUDE && isExe(env.SESSIONPIPE_CLAUDE)) return env.SESSIONPIPE_CLAUDE;
|
|
1524
2028
|
const names = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
1525
|
-
for (const d of String(env.PATH || "").split(
|
|
1526
|
-
if (!d || !
|
|
1527
|
-
for (const n of names) if (isExe(
|
|
2029
|
+
for (const d of String(env.PATH || "").split(path9.delimiter)) {
|
|
2030
|
+
if (!d || !path9.isAbsolute(d)) continue;
|
|
2031
|
+
for (const n of names) if (isExe(path9.join(d, n))) return path9.join(d, n);
|
|
1528
2032
|
}
|
|
1529
2033
|
for (const p of [
|
|
1530
|
-
|
|
1531
|
-
|
|
2034
|
+
path9.join(home, ".claude", "local", "claude"),
|
|
2035
|
+
path9.join(home, ".local", "bin", "claude"),
|
|
1532
2036
|
"/opt/homebrew/bin/claude",
|
|
1533
2037
|
"/usr/local/bin/claude"
|
|
1534
2038
|
])
|
|
1535
2039
|
if (isExe(p)) return p;
|
|
1536
|
-
const app =
|
|
2040
|
+
const app = path9.join(home, "Library", "Application Support", "Claude", "claude-code");
|
|
1537
2041
|
let versions = [];
|
|
1538
2042
|
try {
|
|
1539
2043
|
versions = readdirSync4(app).filter((n) => /^\d+(\.\d+)*$/.test(n)).sort(cmpVersion).reverse();
|
|
1540
2044
|
} catch {
|
|
1541
2045
|
}
|
|
1542
2046
|
for (const v of versions) {
|
|
1543
|
-
const p =
|
|
2047
|
+
const p = path9.join(app, v, "claude.app", "Contents", "MacOS", "claude");
|
|
1544
2048
|
if (isExe(p)) return p;
|
|
1545
2049
|
}
|
|
1546
2050
|
return null;
|
|
@@ -1566,11 +2070,19 @@ function parseCaps(help) {
|
|
|
1566
2070
|
stream: !!optionBlock(help, "--include-partial-messages")
|
|
1567
2071
|
};
|
|
1568
2072
|
}
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
2073
|
+
var LEVEL_MODE = {
|
|
2074
|
+
read: "plan",
|
|
2075
|
+
edits: "acceptEdits",
|
|
2076
|
+
auto: "auto"
|
|
2077
|
+
};
|
|
2078
|
+
function modeFlags(level, caps) {
|
|
2079
|
+
if (level === "inherit") return { args: [] };
|
|
2080
|
+
if (level === "auto" && !caps.modes.includes("auto"))
|
|
2081
|
+
return { error: "this machine is in auto mode, but its Claude Code has no auto permission mode" };
|
|
2082
|
+
if (level !== "safe") {
|
|
2083
|
+
const m = LEVEL_MODE[level];
|
|
2084
|
+
if (!caps.modes.includes(m)) return { error: `this machine's Claude Code has no ${m} permission mode` };
|
|
2085
|
+
return { args: ["--permission-mode", m] };
|
|
1574
2086
|
}
|
|
1575
2087
|
if (caps.modes.includes("dontAsk")) return { args: ["--permission-mode", "dontAsk"] };
|
|
1576
2088
|
const args = [];
|
|
@@ -1582,6 +2094,32 @@ function modeFlags(mode, caps) {
|
|
|
1582
2094
|
if (caps.promptsNone) args.push("--permission-prompts", "none");
|
|
1583
2095
|
return { args };
|
|
1584
2096
|
}
|
|
2097
|
+
var ceilingLevel = (mode) => mode === "inherit" ? "safe" : mode;
|
|
2098
|
+
function offeredLevels2(mode, caps) {
|
|
2099
|
+
return START_PERMISSIONS.filter(
|
|
2100
|
+
(l) => levelRank(l) <= levelRank(ceilingLevel(mode)) && (l === "safe" || caps.modes.includes(LEVEL_MODE[l]))
|
|
2101
|
+
);
|
|
2102
|
+
}
|
|
2103
|
+
var defaultLevel2 = (mode) => mode;
|
|
2104
|
+
function outputArgs(caps) {
|
|
2105
|
+
return caps.stream ? ["--output-format", "stream-json", "--verbose", "--include-partial-messages"] : ["--output-format", "json"];
|
|
2106
|
+
}
|
|
2107
|
+
function claudeArgs(o) {
|
|
2108
|
+
const mf = modeFlags(o.level, o.caps);
|
|
2109
|
+
if ("error" in mf) return mf;
|
|
2110
|
+
const where = o.resume ? ["--resume", o.resume, ...o.fork ? ["--fork-session"] : []] : ["--session-id", o.session];
|
|
2111
|
+
return {
|
|
2112
|
+
args: [
|
|
2113
|
+
"-p",
|
|
2114
|
+
...outputArgs(o.caps),
|
|
2115
|
+
...mf.args,
|
|
2116
|
+
...o.model ? ["--model", o.model] : [],
|
|
2117
|
+
...o.allowedTools?.length ? [`--allowedTools=${o.allowedTools.join(",")}`] : [],
|
|
2118
|
+
...where,
|
|
2119
|
+
o.prompt.startsWith("-") ? ` ${o.prompt}` : o.prompt
|
|
2120
|
+
]
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
1585
2123
|
function claudeEnv(configDir, env = process.env) {
|
|
1586
2124
|
const e = { ...env };
|
|
1587
2125
|
for (const k of [
|
|
@@ -1595,7 +2133,7 @@ function claudeEnv(configDir, env = process.env) {
|
|
|
1595
2133
|
delete e[k];
|
|
1596
2134
|
e.SESSIONPIPE_CONTROL_JOB = "1";
|
|
1597
2135
|
if (configDir !== void 0) {
|
|
1598
|
-
if (
|
|
2136
|
+
if (path9.resolve(configDir) === path9.resolve(os4.homedir(), ".claude")) delete e.CLAUDE_CONFIG_DIR;
|
|
1599
2137
|
else e.CLAUDE_CONFIG_DIR = configDir;
|
|
1600
2138
|
}
|
|
1601
2139
|
return e;
|
|
@@ -1616,7 +2154,7 @@ function parseAuthStatus(stdout) {
|
|
|
1616
2154
|
return { loggedIn: null };
|
|
1617
2155
|
}
|
|
1618
2156
|
}
|
|
1619
|
-
function
|
|
2157
|
+
function claudeLogin2(bin, configDir, env = process.env) {
|
|
1620
2158
|
return new Promise((resolve) => {
|
|
1621
2159
|
execFile(
|
|
1622
2160
|
bin,
|
|
@@ -1628,12 +2166,12 @@ function claudeLogin(bin, configDir, env = process.env) {
|
|
|
1628
2166
|
});
|
|
1629
2167
|
}
|
|
1630
2168
|
function loginCommand(configDir) {
|
|
1631
|
-
const home =
|
|
1632
|
-
const isDefault = !configDir ||
|
|
2169
|
+
const home = os4.homedir();
|
|
2170
|
+
const isDefault = !configDir || path9.resolve(configDir) === path9.resolve(home, ".claude");
|
|
1633
2171
|
return isDefault ? "`claude auth login`" : `\`CLAUDE_CONFIG_DIR=${configDir.replace(home, "~")} claude auth login\``;
|
|
1634
2172
|
}
|
|
1635
2173
|
function loginHint(configDir, platform = process.platform) {
|
|
1636
|
-
const shown = configDir ? configDir.replace(
|
|
2174
|
+
const shown = configDir ? configDir.replace(os4.homedir(), "~") : "~/.claude";
|
|
1637
2175
|
const login = loginCommand(configDir);
|
|
1638
2176
|
if (platform === "darwin")
|
|
1639
2177
|
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.`;
|
|
@@ -1659,13 +2197,13 @@ function findTranscript(session, dirs) {
|
|
|
1659
2197
|
for (const dir of dirs) {
|
|
1660
2198
|
let projects = [];
|
|
1661
2199
|
try {
|
|
1662
|
-
projects = readdirSync4(
|
|
2200
|
+
projects = readdirSync4(path9.join(dir, "projects"), { withFileTypes: true });
|
|
1663
2201
|
} catch {
|
|
1664
2202
|
continue;
|
|
1665
2203
|
}
|
|
1666
2204
|
for (const p of projects) {
|
|
1667
2205
|
if (!p.isDirectory()) continue;
|
|
1668
|
-
const file =
|
|
2206
|
+
const file = path9.join(dir, "projects", p.name, `${session}.jsonl`);
|
|
1669
2207
|
try {
|
|
1670
2208
|
if (statSync5(file).isFile()) return { file, configDir: dir, project: p.name };
|
|
1671
2209
|
} catch {
|
|
@@ -1687,7 +2225,7 @@ function sessionFolder(t, cap2 = 8 * 1024 * 1024) {
|
|
|
1687
2225
|
} catch {
|
|
1688
2226
|
return null;
|
|
1689
2227
|
}
|
|
1690
|
-
if (!j || typeof j.cwd !== "string" || !
|
|
2228
|
+
if (!j || typeof j.cwd !== "string" || !path9.isAbsolute(j.cwd)) return null;
|
|
1691
2229
|
if (projectName(j.cwd) === t.project) return j.cwd;
|
|
1692
2230
|
first ??= j.cwd;
|
|
1693
2231
|
count++;
|
|
@@ -1748,13 +2286,13 @@ function pidAlive(pid) {
|
|
|
1748
2286
|
function liveProcess(session, configDir) {
|
|
1749
2287
|
let names = [];
|
|
1750
2288
|
try {
|
|
1751
|
-
names = readdirSync4(
|
|
2289
|
+
names = readdirSync4(path9.join(configDir, "sessions")).filter((n) => /^\d+\.json$/.test(n));
|
|
1752
2290
|
} catch {
|
|
1753
2291
|
return false;
|
|
1754
2292
|
}
|
|
1755
2293
|
for (const n of names) {
|
|
1756
2294
|
try {
|
|
1757
|
-
const j = JSON.parse(readFileSync4(
|
|
2295
|
+
const j = JSON.parse(readFileSync4(path9.join(configDir, "sessions", n), "utf8"));
|
|
1758
2296
|
if (String(j.sessionId) !== session) continue;
|
|
1759
2297
|
if (pidAlive(Number(j.pid ?? n.slice(0, -5)))) return true;
|
|
1760
2298
|
} catch {
|
|
@@ -1803,7 +2341,7 @@ var isUuid = (s) => typeof s === "string" && UUID_RE.test(s);
|
|
|
1803
2341
|
|
|
1804
2342
|
// src/adapters/codex.ts
|
|
1805
2343
|
import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1806
|
-
import
|
|
2344
|
+
import path10 from "node:path";
|
|
1807
2345
|
|
|
1808
2346
|
// src/adapters/codex-config.ts
|
|
1809
2347
|
var NOTIFY_LINE = /^[ \t]*notify[ \t]*=[ \t]*(\[.*\])[ \t]*(?:#.*)?$/m;
|
|
@@ -1867,18 +2405,13 @@ function parseArgv(raw) {
|
|
|
1867
2405
|
function readNotify(text) {
|
|
1868
2406
|
const { head, tail, at } = split(text);
|
|
1869
2407
|
const m = NOTIFY_LINE.exec(head);
|
|
1870
|
-
if (m)
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
index: m.index,
|
|
1875
|
-
length: m[0].length,
|
|
1876
|
-
misplaced: false,
|
|
1877
|
-
other: !OURS2.test(m[1] ?? "")
|
|
1878
|
-
};
|
|
2408
|
+
if (m) {
|
|
2409
|
+
const raw = m[1];
|
|
2410
|
+
return { raw, argv: parseArgv(raw), index: m.index, length: m[0].length, misplaced: false, other: !OURS2.test(raw) };
|
|
2411
|
+
}
|
|
1879
2412
|
const k = NOTIFY_KEY.exec(head);
|
|
1880
2413
|
if (k) {
|
|
1881
|
-
const rest = head.slice(k.index).split("\n")[0]
|
|
2414
|
+
const rest = head.slice(k.index).split("\n")[0];
|
|
1882
2415
|
return {
|
|
1883
2416
|
raw: rest.replace(NOTIFY_KEY, "").trim(),
|
|
1884
2417
|
argv: null,
|
|
@@ -1889,15 +2422,11 @@ function readNotify(text) {
|
|
|
1889
2422
|
};
|
|
1890
2423
|
}
|
|
1891
2424
|
const t = NOTIFY_LINE.exec(tail);
|
|
1892
|
-
if (t
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
argv: parseArgv(t[
|
|
1896
|
-
|
|
1897
|
-
length: t[0].length,
|
|
1898
|
-
misplaced: true,
|
|
1899
|
-
other: false
|
|
1900
|
-
};
|
|
2425
|
+
if (t) {
|
|
2426
|
+
const raw = t[1];
|
|
2427
|
+
if (OURS2.test(raw))
|
|
2428
|
+
return { raw, argv: parseArgv(raw), index: at + t.index, length: t[0].length, misplaced: true, other: false };
|
|
2429
|
+
}
|
|
1901
2430
|
return null;
|
|
1902
2431
|
}
|
|
1903
2432
|
function dropLine(text, index, length) {
|
|
@@ -1916,9 +2445,7 @@ function withNotify(text, line) {
|
|
|
1916
2445
|
if (n?.misplaced) return insertTopLevel(dropLine(text, n.index, n.length), line);
|
|
1917
2446
|
return insertTopLevel(text, line);
|
|
1918
2447
|
}
|
|
1919
|
-
function replaceNotify(text, line) {
|
|
1920
|
-
const n = readNotify(text);
|
|
1921
|
-
if (!n || n.misplaced || !n.length) return null;
|
|
2448
|
+
function replaceNotify(text, n, line) {
|
|
1922
2449
|
return text.slice(0, n.index) + line + text.slice(n.index + n.length);
|
|
1923
2450
|
}
|
|
1924
2451
|
function withoutNotify(text) {
|
|
@@ -1959,10 +2486,10 @@ var CODEX_EVENTS = [
|
|
|
1959
2486
|
];
|
|
1960
2487
|
var OPTS2 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
1961
2488
|
var eventsFor2 = (lean) => lean ? CODEX_EVENTS.filter((e) => e !== "PreToolUse") : [...CODEX_EVENTS];
|
|
1962
|
-
var codexHome = (env) => env.CODEX_HOME ||
|
|
1963
|
-
var hooksFile2 = (env) =>
|
|
1964
|
-
var configFile2 = (env) =>
|
|
1965
|
-
var sessionsDir = (env) =>
|
|
2489
|
+
var codexHome = (env) => env.CODEX_HOME || path10.join(homeDir(), ".codex");
|
|
2490
|
+
var hooksFile2 = (env) => path10.join(codexHome(env), "hooks.json");
|
|
2491
|
+
var configFile2 = (env) => path10.join(codexHome(env), "config.toml");
|
|
2492
|
+
var sessionsDir = (env) => path10.join(codexHome(env), "sessions");
|
|
1966
2493
|
var validTool3 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
1967
2494
|
var endReason2 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
1968
2495
|
function installNotify(cmd, env) {
|
|
@@ -1976,7 +2503,7 @@ function installNotify(cmd, env) {
|
|
|
1976
2503
|
const line = `notify = ${JSON.stringify(argv)}`;
|
|
1977
2504
|
const n = readNotify(text);
|
|
1978
2505
|
const write = (next) => {
|
|
1979
|
-
mkdirSync3(
|
|
2506
|
+
mkdirSync3(path10.dirname(file), { recursive: true });
|
|
1980
2507
|
writeFileSync3(file, next);
|
|
1981
2508
|
};
|
|
1982
2509
|
if (n && !n.other) {
|
|
@@ -1997,11 +2524,8 @@ function installNotify(cmd, env) {
|
|
|
1997
2524
|
return { file, changed: false, note: "notify current (after Codex Computer Use)" };
|
|
1998
2525
|
}
|
|
1999
2526
|
if (!prev || OURS2.test(JSON.stringify(prev))) {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
write(next);
|
|
2003
|
-
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
2004
|
-
}
|
|
2527
|
+
write(replaceNotify(text, n, `notify = ${withPreviousRaw(n.raw, argv)}`));
|
|
2528
|
+
return { file, changed: true, note: "chained after Codex Computer Use (--previous-notify)" };
|
|
2005
2529
|
}
|
|
2006
2530
|
}
|
|
2007
2531
|
return {
|
|
@@ -2030,8 +2554,7 @@ function uninstallNotify(env) {
|
|
|
2030
2554
|
}
|
|
2031
2555
|
const prev = previousNotify(n.argv);
|
|
2032
2556
|
if (isComputerUse(n.argv) && prev && OURS2.test(JSON.stringify(prev))) {
|
|
2033
|
-
|
|
2034
|
-
if (next) writeFileSync3(file, next);
|
|
2557
|
+
writeFileSync3(file, replaceNotify(text, n, `notify = ${stripPreviousRaw(n.raw)}`));
|
|
2035
2558
|
return { file, changed: true };
|
|
2036
2559
|
}
|
|
2037
2560
|
return { file, changed: false };
|
|
@@ -2047,7 +2570,7 @@ function findRollout(threadId, env = process.env) {
|
|
|
2047
2570
|
return;
|
|
2048
2571
|
}
|
|
2049
2572
|
for (const n of names.sort().reverse()) {
|
|
2050
|
-
const p =
|
|
2573
|
+
const p = path10.join(dir, n);
|
|
2051
2574
|
if (n.endsWith(`-${threadId}.jsonl`)) {
|
|
2052
2575
|
best = p;
|
|
2053
2576
|
return;
|
|
@@ -2061,6 +2584,13 @@ function findRollout(threadId, env = process.env) {
|
|
|
2061
2584
|
walk(sessionsDir(env), 0);
|
|
2062
2585
|
return best;
|
|
2063
2586
|
}
|
|
2587
|
+
function rolloutMeta(file) {
|
|
2588
|
+
return scanLines(file, (line) => parseLines([line])[0] ?? {});
|
|
2589
|
+
}
|
|
2590
|
+
function ofAnotherThread(file, id) {
|
|
2591
|
+
const m = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl(?:\.zst)?$/i.exec(file);
|
|
2592
|
+
return !!m?.[1] && m[1].toLowerCase() !== id.toLowerCase();
|
|
2593
|
+
}
|
|
2064
2594
|
var codex = {
|
|
2065
2595
|
name: NAME3,
|
|
2066
2596
|
events: CODEX_EVENTS,
|
|
@@ -2103,11 +2633,14 @@ var codex = {
|
|
|
2103
2633
|
if (event === "notify") {
|
|
2104
2634
|
const raw = input.argv.find((a) => a.startsWith("{")) ?? input.stdin;
|
|
2105
2635
|
try {
|
|
2106
|
-
|
|
2636
|
+
const j = raw ? JSON.parse(raw) : {};
|
|
2637
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2107
2638
|
} catch {
|
|
2108
2639
|
}
|
|
2109
2640
|
const id2 = s["thread-id"] ?? s.thread_id ?? s["session-id"] ?? s.session_id;
|
|
2110
2641
|
if (!id2) return null;
|
|
2642
|
+
const file = findRollout(String(id2), input.env);
|
|
2643
|
+
if (!file || codexSubagent(rolloutMeta(file))) return null;
|
|
2111
2644
|
const r2 = {
|
|
2112
2645
|
session: { id: String(id2), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd },
|
|
2113
2646
|
events: [
|
|
@@ -2117,18 +2650,19 @@ var codex = {
|
|
|
2117
2650
|
harnessEvent: "notify"
|
|
2118
2651
|
}
|
|
2119
2652
|
],
|
|
2120
|
-
hints: {}
|
|
2653
|
+
hints: {},
|
|
2654
|
+
transcript: file
|
|
2121
2655
|
};
|
|
2122
|
-
const file = findRollout(String(id2), input.env);
|
|
2123
|
-
if (file) r2.transcript = file;
|
|
2124
2656
|
return r2;
|
|
2125
2657
|
}
|
|
2126
2658
|
try {
|
|
2127
|
-
|
|
2659
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
2660
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2128
2661
|
} catch {
|
|
2129
2662
|
}
|
|
2130
|
-
const id = s.session_id;
|
|
2663
|
+
const id = s.session_id ?? s.thread_id ?? s.conversation_id ?? s["thread-id"] ?? s["session-id"];
|
|
2131
2664
|
if (!id) return null;
|
|
2665
|
+
if (s.transcript_path === null) return null;
|
|
2132
2666
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
2133
2667
|
if (typeof s.model === "string") session.model = s.model;
|
|
2134
2668
|
const turn_id = typeof s.turn_id === "string" ? s.turn_id : void 0;
|
|
@@ -2200,7 +2734,8 @@ var codex = {
|
|
|
2200
2734
|
return null;
|
|
2201
2735
|
}
|
|
2202
2736
|
const r = { session, events, hints: {} };
|
|
2203
|
-
if (typeof s.transcript_path === "string"
|
|
2737
|
+
if (typeof s.transcript_path === "string" && !ofAnotherThread(s.transcript_path, String(id)))
|
|
2738
|
+
r.transcript = s.transcript_path;
|
|
2204
2739
|
return r;
|
|
2205
2740
|
},
|
|
2206
2741
|
readTranscript(file, fromLine) {
|
|
@@ -2218,16 +2753,17 @@ var codex = {
|
|
|
2218
2753
|
return out;
|
|
2219
2754
|
},
|
|
2220
2755
|
backfill(sinceMs, env = process.env) {
|
|
2221
|
-
const
|
|
2756
|
+
const rows2 = [];
|
|
2222
2757
|
for (const f of recentFiles(sessionsDir(env), 3, sinceMs, (n) => /^rollout-.*\.jsonl$/.test(n))) {
|
|
2223
|
-
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(
|
|
2758
|
+
const m = /([0-9a-f]{8}-[0-9a-f-]{27,})\.jsonl$/i.exec(path10.basename(f));
|
|
2224
2759
|
if (!m?.[1]) continue;
|
|
2760
|
+
if (codexSubagent(rolloutMeta(f))) continue;
|
|
2225
2761
|
const facts = codexFacts(f);
|
|
2226
2762
|
if (!facts.started_at || !facts.last_at) continue;
|
|
2227
2763
|
const { started_at, last_at, first_ask: _fa, harness_version: _hv, ...rest } = facts;
|
|
2228
|
-
|
|
2764
|
+
rows2.push({ session: { id: m[1], ...rest }, started_at, last_at });
|
|
2229
2765
|
}
|
|
2230
|
-
return
|
|
2766
|
+
return rows2;
|
|
2231
2767
|
}
|
|
2232
2768
|
};
|
|
2233
2769
|
function prune3(o) {
|
|
@@ -2236,10 +2772,11 @@ function prune3(o) {
|
|
|
2236
2772
|
}
|
|
2237
2773
|
function codexFacts(file) {
|
|
2238
2774
|
const { head, tail } = ends(file, 128 * 1024, 64 * 1024);
|
|
2239
|
-
|
|
2240
|
-
|
|
2775
|
+
let H = parseLines(head);
|
|
2776
|
+
let T = parseLines(tail);
|
|
2241
2777
|
const out = {};
|
|
2242
2778
|
const meta = H.find((j) => j.type === "session_meta" && j.payload);
|
|
2779
|
+
const boundary = codexReplayBoundary(meta);
|
|
2243
2780
|
if (meta?.payload) {
|
|
2244
2781
|
const p = meta.payload;
|
|
2245
2782
|
if (p.cwd) out.cwd = p.cwd;
|
|
@@ -2247,6 +2784,13 @@ function codexFacts(file) {
|
|
|
2247
2784
|
if (p.git?.branch) out.branch = p.git.branch;
|
|
2248
2785
|
if (p.git?.repository_url) out.repo = String(p.git.repository_url).replace(/\/\/[^/@]*@/, "//");
|
|
2249
2786
|
}
|
|
2787
|
+
if (boundary) {
|
|
2788
|
+
const h = H.findIndex(boundary);
|
|
2789
|
+
const t = T.findIndex(boundary);
|
|
2790
|
+
H = h < 0 ? [] : H.slice(h);
|
|
2791
|
+
const found = h >= 0 || t >= 0 || scanLines(file, (line) => boundary(parseLines([line])[0] ?? null) ? true : null, Number.POSITIVE_INFINITY);
|
|
2792
|
+
T = t >= 0 ? T.slice(t) : found ? T : [];
|
|
2793
|
+
}
|
|
2250
2794
|
const ctx = H.concat(T).reverse().find((j) => j.type === "turn_context" && j.payload?.model);
|
|
2251
2795
|
if (ctx?.payload?.model) out.model = ctx.payload.model;
|
|
2252
2796
|
for (const j of H) {
|
|
@@ -2254,12 +2798,12 @@ function codexFacts(file) {
|
|
|
2254
2798
|
if (j.type !== "response_item" || !p || p.type !== "message" || p.role !== "user") continue;
|
|
2255
2799
|
const text = (p.content ?? []).filter((b) => b && b.type === "input_text" && typeof b.text === "string").map((b) => b.text).join("\n").trim();
|
|
2256
2800
|
if (!text || codexInjected(text)) continue;
|
|
2257
|
-
const line = text.split("\n")
|
|
2801
|
+
const line = text.split("\n")[0];
|
|
2258
2802
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
2259
2803
|
out.title_source = "first-ask";
|
|
2260
2804
|
break;
|
|
2261
2805
|
}
|
|
2262
|
-
const times = H.concat(T).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
2806
|
+
const times = (boundary && meta ? [meta, ...H, ...T] : H.concat(T)).map((j) => Date.parse(String(j.timestamp ?? ""))).filter((n) => n > 0);
|
|
2263
2807
|
if (times.length) {
|
|
2264
2808
|
out.started_at = Math.min(...times);
|
|
2265
2809
|
out.last_at = Math.max(...times);
|
|
@@ -2278,38 +2822,71 @@ __export(codex_control_exports, {
|
|
|
2278
2822
|
codexLine: () => codexLine,
|
|
2279
2823
|
codexLogin: () => codexLogin,
|
|
2280
2824
|
codexModeFlags: () => codexModeFlags,
|
|
2825
|
+
codexModels: () => codexModels,
|
|
2826
|
+
defaultLevel: () => defaultLevel3,
|
|
2281
2827
|
findCodex: () => findCodex,
|
|
2282
2828
|
findRollout: () => findRollout,
|
|
2829
|
+
offeredLevels: () => offeredLevels3,
|
|
2283
2830
|
reading: () => reading2
|
|
2284
2831
|
});
|
|
2285
2832
|
import { execFile as execFile2 } from "node:child_process";
|
|
2286
|
-
import
|
|
2287
|
-
import
|
|
2833
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2834
|
+
import os5 from "node:os";
|
|
2835
|
+
import path11 from "node:path";
|
|
2288
2836
|
var LABEL2 = "Codex";
|
|
2289
|
-
function findCodex(env = process.env, home =
|
|
2837
|
+
function findCodex(env = process.env, home = os5.homedir(), nodeDir = path11.dirname(process.execPath)) {
|
|
2290
2838
|
return findBinary({
|
|
2291
2839
|
override: env.SESSIONPIPE_CODEX,
|
|
2292
2840
|
names: process.platform === "win32" ? ["codex.exe", "codex.cmd"] : ["codex"],
|
|
2293
2841
|
env,
|
|
2294
|
-
dirs: [nodeDir,
|
|
2842
|
+
dirs: [nodeDir, path11.join(home, ".local", "bin"), "/opt/homebrew/bin", "/usr/local/bin"]
|
|
2295
2843
|
});
|
|
2296
2844
|
}
|
|
2297
|
-
function codexModeFlags(
|
|
2845
|
+
function codexModeFlags(level, resume = false) {
|
|
2846
|
+
if (level === "inherit") return [];
|
|
2847
|
+
const sandbox = level === "read" ? "read-only" : "workspace-write";
|
|
2298
2848
|
if (resume)
|
|
2299
|
-
return
|
|
2849
|
+
return level === "auto" ? [
|
|
2300
2850
|
"-c",
|
|
2301
2851
|
'sandbox_mode="workspace-write"',
|
|
2302
2852
|
"-c",
|
|
2303
2853
|
'approval_policy="on-request"',
|
|
2304
2854
|
"-c",
|
|
2305
2855
|
'approvals_reviewer="auto_review"'
|
|
2306
|
-
] : ["-c",
|
|
2307
|
-
return
|
|
2856
|
+
] : ["-c", `sandbox_mode="${sandbox}"`];
|
|
2857
|
+
return level === "auto" ? ["--approve-for-me"] : ["-s", sandbox];
|
|
2308
2858
|
}
|
|
2859
|
+
var offeredLevels3 = (mode) => mode === "auto" ? ["read", "safe", "auto"] : ["read", "safe"];
|
|
2860
|
+
var defaultLevel3 = (mode) => mode;
|
|
2309
2861
|
function codexArgs(o) {
|
|
2310
2862
|
const common = ["--json", "--skip-git-repo-check"];
|
|
2311
|
-
|
|
2312
|
-
|
|
2863
|
+
const model = o.model ? ["-m", o.model] : [];
|
|
2864
|
+
if (o.resume)
|
|
2865
|
+
return ["exec", "resume", ...common, ...codexModeFlags(o.permission, true), ...model, "--", o.resume, o.prompt];
|
|
2866
|
+
return [
|
|
2867
|
+
"exec",
|
|
2868
|
+
...common,
|
|
2869
|
+
...o.cwd ? ["-C", o.cwd] : [],
|
|
2870
|
+
...codexModeFlags(o.permission),
|
|
2871
|
+
...model,
|
|
2872
|
+
"--",
|
|
2873
|
+
o.prompt
|
|
2874
|
+
];
|
|
2875
|
+
}
|
|
2876
|
+
function codexModels(env = process.env, home = os5.homedir()) {
|
|
2877
|
+
try {
|
|
2878
|
+
const file = path11.join(env.CODEX_HOME || path11.join(home, ".codex"), "models_cache.json");
|
|
2879
|
+
const j = JSON.parse(readFileSync6(file, "utf8"));
|
|
2880
|
+
if (!Array.isArray(j.models)) return [];
|
|
2881
|
+
const out = [];
|
|
2882
|
+
for (const m of j.models)
|
|
2883
|
+
if (m && m.visibility === "list" && typeof m.slug === "string" && MODEL_RE.test(m.slug) && m.slug.length <= 100) {
|
|
2884
|
+
if (!out.includes(m.slug)) out.push(m.slug);
|
|
2885
|
+
}
|
|
2886
|
+
return out.slice(0, 50);
|
|
2887
|
+
} catch {
|
|
2888
|
+
return [];
|
|
2889
|
+
}
|
|
2313
2890
|
}
|
|
2314
2891
|
var codexEnv = (env = process.env) => jobEnv(env);
|
|
2315
2892
|
var LOGIN_HINT = "Codex isn't signed in on this machine: run `codex login --device-auth` there.";
|
|
@@ -2336,22 +2913,22 @@ function codexLine(line) {
|
|
|
2336
2913
|
return null;
|
|
2337
2914
|
}
|
|
2338
2915
|
if (!j || typeof j !== "object") return null;
|
|
2339
|
-
const
|
|
2916
|
+
const str2 = (v) => typeof v === "string" ? v : "";
|
|
2340
2917
|
switch (j.type) {
|
|
2341
2918
|
case "thread.started":
|
|
2342
|
-
return
|
|
2919
|
+
return str2(j.thread_id) ? { session: str2(j.thread_id) } : null;
|
|
2343
2920
|
case "item.completed": {
|
|
2344
2921
|
const it = j.item;
|
|
2345
|
-
if (it?.type === "agent_message" &&
|
|
2346
|
-
if (it?.type === "error" &&
|
|
2922
|
+
if (it?.type === "agent_message" && str2(it.text)) return { message: str2(it.text) };
|
|
2923
|
+
if (it?.type === "error" && str2(it.message)) return { warning: str2(it.message) };
|
|
2347
2924
|
return null;
|
|
2348
2925
|
}
|
|
2349
2926
|
case "turn.completed":
|
|
2350
2927
|
return { done: true };
|
|
2351
2928
|
case "turn.failed":
|
|
2352
|
-
return { done: true, failed:
|
|
2929
|
+
return { done: true, failed: str2(j.error?.message) || "Codex reported an error" };
|
|
2353
2930
|
case "error":
|
|
2354
|
-
return
|
|
2931
|
+
return str2(j.message) ? { note: str2(j.message) } : null;
|
|
2355
2932
|
default:
|
|
2356
2933
|
return null;
|
|
2357
2934
|
}
|
|
@@ -2401,11 +2978,432 @@ function codexStderr(stderr) {
|
|
|
2401
2978
|
}
|
|
2402
2979
|
var reading2 = () => new CodexReading();
|
|
2403
2980
|
|
|
2404
|
-
// src/adapters/
|
|
2981
|
+
// src/adapters/devin.ts
|
|
2982
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
2983
|
+
import { existsSync as existsSync6, readlinkSync } from "node:fs";
|
|
2984
|
+
import path12 from "node:path";
|
|
2985
|
+
|
|
2986
|
+
// src/readers/sqlite.ts
|
|
2405
2987
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2406
|
-
import { existsSync as existsSync5
|
|
2407
|
-
import
|
|
2408
|
-
|
|
2988
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
2989
|
+
import { createRequire } from "node:module";
|
|
2990
|
+
function quiet(fn) {
|
|
2991
|
+
const original = process.emitWarning;
|
|
2992
|
+
try {
|
|
2993
|
+
process.emitWarning = ((warning, ...rest) => {
|
|
2994
|
+
const kind = typeof rest[0] === "string" ? rest[0] : rest[0]?.type;
|
|
2995
|
+
if (kind === "ExperimentalWarning" && /sqlite/i.test(String(warning))) return;
|
|
2996
|
+
original.call(process, warning, ...rest);
|
|
2997
|
+
});
|
|
2998
|
+
return fn();
|
|
2999
|
+
} finally {
|
|
3000
|
+
process.emitWarning = original;
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
var ctorCache;
|
|
3004
|
+
function dbCtor() {
|
|
3005
|
+
if (ctorCache !== void 0) return ctorCache;
|
|
3006
|
+
ctorCache = null;
|
|
3007
|
+
try {
|
|
3008
|
+
const req = createRequire(import.meta.url);
|
|
3009
|
+
const m = quiet(() => req("node:sqlite"));
|
|
3010
|
+
if (typeof m?.DatabaseSync === "function") ctorCache = m.DatabaseSync;
|
|
3011
|
+
} catch {
|
|
3012
|
+
}
|
|
3013
|
+
return ctorCache;
|
|
3014
|
+
}
|
|
3015
|
+
function plain(row) {
|
|
3016
|
+
if (!row || typeof row !== "object") return null;
|
|
3017
|
+
const out = {};
|
|
3018
|
+
for (const [k, v] of Object.entries(row)) {
|
|
3019
|
+
if (typeof v === "bigint") out[k] = Number(v);
|
|
3020
|
+
else if (v instanceof Uint8Array) out[k] = null;
|
|
3021
|
+
else out[k] = v;
|
|
3022
|
+
}
|
|
3023
|
+
return out;
|
|
3024
|
+
}
|
|
3025
|
+
var rows = (v) => {
|
|
3026
|
+
if (!Array.isArray(v)) return null;
|
|
3027
|
+
const out = [];
|
|
3028
|
+
for (const r of v) {
|
|
3029
|
+
const p = plain(r);
|
|
3030
|
+
if (p) out.push(p);
|
|
3031
|
+
}
|
|
3032
|
+
return out;
|
|
3033
|
+
};
|
|
3034
|
+
function viaNode(file, sql, params, o) {
|
|
3035
|
+
const C = dbCtor();
|
|
3036
|
+
if (!C) return null;
|
|
3037
|
+
let db;
|
|
3038
|
+
try {
|
|
3039
|
+
db = quiet(() => new C(file, { readOnly: true, timeout: o.timeoutMs }));
|
|
3040
|
+
} catch {
|
|
3041
|
+
return null;
|
|
3042
|
+
}
|
|
3043
|
+
try {
|
|
3044
|
+
try {
|
|
3045
|
+
db.exec(`PRAGMA busy_timeout = ${Math.round(o.timeoutMs)}`);
|
|
3046
|
+
} catch {
|
|
3047
|
+
}
|
|
3048
|
+
return quiet(() => rows(db.prepare(sql).all(...params)));
|
|
3049
|
+
} catch {
|
|
3050
|
+
return null;
|
|
3051
|
+
} finally {
|
|
3052
|
+
try {
|
|
3053
|
+
db.close();
|
|
3054
|
+
} catch {
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
var CHILD = `const { DatabaseSync } = require("node:sqlite");
|
|
3059
|
+
const [, file, sql, params, ms] = process.argv;
|
|
3060
|
+
const db = new DatabaseSync(file, { readOnly: true, timeout: Number(ms) });
|
|
3061
|
+
try { db.exec("PRAGMA busy_timeout = " + Number(ms)); } catch {}
|
|
3062
|
+
const out = db.prepare(sql).all(...JSON.parse(params));
|
|
3063
|
+
db.close();
|
|
3064
|
+
process.stdout.write(JSON.stringify(out));`;
|
|
3065
|
+
function viaChild(file, sql, params, o) {
|
|
3066
|
+
try {
|
|
3067
|
+
const out = execFileSync4(
|
|
3068
|
+
process.execPath,
|
|
3069
|
+
["--experimental-sqlite", "--no-warnings", "-e", CHILD, file, sql, JSON.stringify(params), String(o.timeoutMs)],
|
|
3070
|
+
{
|
|
3071
|
+
encoding: "utf8",
|
|
3072
|
+
timeout: o.timeoutMs + 3e3,
|
|
3073
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3074
|
+
maxBuffer: o.maxBuffer,
|
|
3075
|
+
windowsHide: true
|
|
3076
|
+
}
|
|
3077
|
+
);
|
|
3078
|
+
return rows(JSON.parse(out || "null"));
|
|
3079
|
+
} catch {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
var SAFE = /^[A-Za-z0-9 ._:@/+-]{0,256}$/;
|
|
3084
|
+
function inlineParams(sql, params) {
|
|
3085
|
+
if (sql.includes("'")) return null;
|
|
3086
|
+
const holes = sql.split("?").length - 1;
|
|
3087
|
+
if (holes !== params.length) return null;
|
|
3088
|
+
let i = 0;
|
|
3089
|
+
let bad = false;
|
|
3090
|
+
const out = sql.replace(/\?/g, () => {
|
|
3091
|
+
const p = params[i++];
|
|
3092
|
+
if (typeof p === "number") {
|
|
3093
|
+
if (!Number.isFinite(p)) {
|
|
3094
|
+
bad = true;
|
|
3095
|
+
return "NULL";
|
|
3096
|
+
}
|
|
3097
|
+
return String(p);
|
|
3098
|
+
}
|
|
3099
|
+
if (typeof p !== "string" || !SAFE.test(p)) {
|
|
3100
|
+
bad = true;
|
|
3101
|
+
return "NULL";
|
|
3102
|
+
}
|
|
3103
|
+
return `'${p}'`;
|
|
3104
|
+
});
|
|
3105
|
+
return bad ? null : out;
|
|
3106
|
+
}
|
|
3107
|
+
function viaCli(file, sql, params, o) {
|
|
3108
|
+
const text = inlineParams(sql, params);
|
|
3109
|
+
if (!text) return null;
|
|
3110
|
+
try {
|
|
3111
|
+
const out = execFileSync4("sqlite3", ["-readonly", "-json", file, text], {
|
|
3112
|
+
encoding: "utf8",
|
|
3113
|
+
timeout: o.timeoutMs + 3e3,
|
|
3114
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
3115
|
+
maxBuffer: o.maxBuffer,
|
|
3116
|
+
windowsHide: true
|
|
3117
|
+
}).trim();
|
|
3118
|
+
if (!out) return [];
|
|
3119
|
+
return rows(JSON.parse(out));
|
|
3120
|
+
} catch {
|
|
3121
|
+
return null;
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
function querySqlite(file, sql, params = [], opts = {}) {
|
|
3125
|
+
const o = {
|
|
3126
|
+
timeoutMs: opts.timeoutMs ?? 2e3,
|
|
3127
|
+
engine: opts.engine ?? "auto",
|
|
3128
|
+
maxBuffer: opts.maxBuffer ?? 32 * 1024 * 1024
|
|
3129
|
+
};
|
|
3130
|
+
if (!file) return null;
|
|
3131
|
+
try {
|
|
3132
|
+
if (!existsSync5(file)) return null;
|
|
3133
|
+
} catch {
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
const ways = o.engine === "auto" ? [viaNode, viaChild, viaCli] : o.engine === "node" ? [viaNode] : o.engine === "child" ? [viaChild] : [viaCli];
|
|
3137
|
+
for (const way of ways) {
|
|
3138
|
+
const r = way(file, sql, params, o);
|
|
3139
|
+
if (r) return r;
|
|
3140
|
+
}
|
|
3141
|
+
return null;
|
|
3142
|
+
}
|
|
3143
|
+
|
|
3144
|
+
// src/adapters/devin.ts
|
|
3145
|
+
var NAME4 = "devin";
|
|
3146
|
+
var DEVIN_EVENTS = [
|
|
3147
|
+
"SessionStart",
|
|
3148
|
+
"UserPromptSubmit",
|
|
3149
|
+
"PreToolUse",
|
|
3150
|
+
"PostToolUse",
|
|
3151
|
+
"PermissionRequest",
|
|
3152
|
+
"Stop",
|
|
3153
|
+
"PostCompaction",
|
|
3154
|
+
"SessionEnd"
|
|
3155
|
+
];
|
|
3156
|
+
var OPTS3 = { matcherFor: (e) => /Tool|Permission/.test(e) ? "" : void 0, timeout: 5 };
|
|
3157
|
+
var eventsFor3 = (lean) => lean ? DEVIN_EVENTS.filter((e) => e !== "PreToolUse") : [...DEVIN_EVENTS];
|
|
3158
|
+
var INSTALL_NOTE = "Devin drops a whole hooks file on one event name it doesn't know; `/hooks` in `devin` lists what loaded";
|
|
3159
|
+
var CLAUDE_NOTE = "Devin also reads ~/.claude/settings.json hooks (read_config_from.claude); a Claude-only event name there makes it ignore that file whole";
|
|
3160
|
+
var deadFileNote = (bad) => `Devin loads NO hooks from this file: "${bad}" is not one of its event names and it rejects the file whole \u2014 remove that key (\`/hooks\` in \`devin\` lists what loaded)`;
|
|
3161
|
+
function foreignEvent(file) {
|
|
3162
|
+
const hooks = readJson(file).hooks;
|
|
3163
|
+
if (!hooks || typeof hooks !== "object" || Array.isArray(hooks)) return void 0;
|
|
3164
|
+
const ok = DEVIN_EVENTS;
|
|
3165
|
+
return Object.keys(hooks).find((k) => !ok.includes(k));
|
|
3166
|
+
}
|
|
3167
|
+
var homeOf = (env) => env.HOME || homeDir();
|
|
3168
|
+
var appData = (env) => env.APPDATA || path12.join(homeOf(env), "AppData", "Roaming");
|
|
3169
|
+
var devinConfigDir = (env = process.env) => process.platform === "win32" ? path12.join(appData(env), "devin") : path12.join(env.XDG_CONFIG_HOME || path12.join(homeOf(env), ".config"), "devin");
|
|
3170
|
+
var configFile3 = (env) => path12.join(devinConfigDir(env), "config.json");
|
|
3171
|
+
var devinDataDir = (env = process.env) => process.platform === "win32" ? path12.join(appData(env), "devin", "cli") : path12.join(env.XDG_DATA_HOME || path12.join(homeOf(env), ".local", "share"), "devin", "cli");
|
|
3172
|
+
var sessionsDb = (env) => path12.join(devinDataDir(env), "sessions.db");
|
|
3173
|
+
var SESSION_ID = /^[A-Za-z0-9._:-]{1,128}$/;
|
|
3174
|
+
var validTool4 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
3175
|
+
var endReason3 = (r) => r === "clear" || r === "logout" || r === "exit" ? r : "other";
|
|
3176
|
+
var pickSource2 = (v) => typeof v === "string" && ["startup", "resume", "clear", "compact", "fork"].includes(v) ? v : "unknown";
|
|
3177
|
+
function errText2(v) {
|
|
3178
|
+
if (typeof v === "string" && v) return v.slice(0, 2e3);
|
|
3179
|
+
if (v && typeof v === "object") {
|
|
3180
|
+
const e = v;
|
|
3181
|
+
if (typeof e.message === "string" && e.message) return e.message.slice(0, 2e3);
|
|
3182
|
+
if (typeof e.error === "string" && e.error) return e.error.slice(0, 2e3);
|
|
3183
|
+
}
|
|
3184
|
+
return void 0;
|
|
3185
|
+
}
|
|
3186
|
+
var cut = (s, n) => s.length > n ? `${s.slice(0, n).replace(/\s+\S*$/, "")}\u2026` : s;
|
|
3187
|
+
var devin = {
|
|
3188
|
+
name: NAME4,
|
|
3189
|
+
events: DEVIN_EVENTS,
|
|
3190
|
+
detect: (env = process.env) => existsSync6(devinConfigDir(env)) || existsSync6(devinDataDir(env)),
|
|
3191
|
+
configFiles: (env = process.env) => [configFile3(env)],
|
|
3192
|
+
install: (cmd, env = process.env, opts = {}) => {
|
|
3193
|
+
const file = configFile3(env);
|
|
3194
|
+
const r = installClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
3195
|
+
if (r.skipped) return [r];
|
|
3196
|
+
const bad = foreignEvent(file);
|
|
3197
|
+
return [{ ...r, note: bad ? deadFileNote(bad) : INSTALL_NOTE }];
|
|
3198
|
+
},
|
|
3199
|
+
uninstall: (env = process.env, opts = {}) => [
|
|
3200
|
+
uninstallClaudeShaped(configFile3(env), DEVIN_EVENTS, { created: opts.created?.includes(configFile3(env)) })
|
|
3201
|
+
],
|
|
3202
|
+
installed: (cmd, env = process.env, opts = {}) => {
|
|
3203
|
+
const file = configFile3(env);
|
|
3204
|
+
const state = installedClaudeShaped(file, NAME4, eventsFor3(opts.lean), cmd, OPTS3);
|
|
3205
|
+
const bad = state === "missing" ? void 0 : foreignEvent(file);
|
|
3206
|
+
return [{ file, state: bad ? "inactive" : state, note: bad ? deadFileNote(bad) : CLAUDE_NOTE }];
|
|
3207
|
+
},
|
|
3208
|
+
fromHook(input) {
|
|
3209
|
+
const event = input.argv[1] ?? "";
|
|
3210
|
+
let s = {};
|
|
3211
|
+
try {
|
|
3212
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
3213
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
3214
|
+
} catch {
|
|
3215
|
+
}
|
|
3216
|
+
const id = typeof s.session_id === "string" ? s.session_id : "";
|
|
3217
|
+
if (!id) return null;
|
|
3218
|
+
const session = { id, cwd: input.env.DEVIN_PROJECT_DIR ?? input.cwd };
|
|
3219
|
+
const turn_id = typeof s.prompt_id === "string" ? s.prompt_id : void 0;
|
|
3220
|
+
const tool = validTool4(s.tool_name) ? s.tool_name : void 0;
|
|
3221
|
+
const call_id = typeof s.tool_use_id === "string" ? s.tool_use_id : void 0;
|
|
3222
|
+
const response = s.tool_response && typeof s.tool_response === "object" && !Array.isArray(s.tool_response) ? s.tool_response : void 0;
|
|
3223
|
+
const permissionId = () => `perm-${createHash2("sha256").update(`${turn_id ?? ""}|${tool ?? ""}|${JSON.stringify(s.tool_input ?? null)}`).digest("hex").slice(0, 16)}`;
|
|
3224
|
+
const ev = (type, data) => ({ type, data: prune4(data), harnessEvent: event });
|
|
3225
|
+
const events = [];
|
|
3226
|
+
switch (event) {
|
|
3227
|
+
case "SessionStart":
|
|
3228
|
+
events.push(ev("session.started", { source: pickSource2(s.source) }));
|
|
3229
|
+
break;
|
|
3230
|
+
case "UserPromptSubmit":
|
|
3231
|
+
events.push(
|
|
3232
|
+
ev("turn.started", {
|
|
3233
|
+
turn_id,
|
|
3234
|
+
prompt_chars: typeof s.prompt === "string" ? [...s.prompt].length : void 0
|
|
3235
|
+
})
|
|
3236
|
+
);
|
|
3237
|
+
break;
|
|
3238
|
+
case "PreToolUse":
|
|
3239
|
+
if (tool) events.push(ev("tool.started", { tool, call_id, turn_id, input: s.tool_input }));
|
|
3240
|
+
break;
|
|
3241
|
+
case "PostToolUse":
|
|
3242
|
+
if (tool)
|
|
3243
|
+
events.push(
|
|
3244
|
+
ev("tool.ended", {
|
|
3245
|
+
tool,
|
|
3246
|
+
call_id,
|
|
3247
|
+
turn_id,
|
|
3248
|
+
// `ok` is required on tool.ended (PROTOCOL.md §events), so a response
|
|
3249
|
+
// that carries no success flag at all reads as "nothing said it failed".
|
|
3250
|
+
ok: response?.success !== false,
|
|
3251
|
+
error: errText2(response?.error),
|
|
3252
|
+
input: s.tool_input,
|
|
3253
|
+
output: response ? response.output : s.tool_response
|
|
3254
|
+
})
|
|
3255
|
+
);
|
|
3256
|
+
break;
|
|
3257
|
+
case "PermissionRequest":
|
|
3258
|
+
events.push(ev("attention.needed", { attention_id: call_id ?? permissionId(), kind: "permission", tool }));
|
|
3259
|
+
break;
|
|
3260
|
+
case "Stop":
|
|
3261
|
+
events.push(ev("turn.ended", { turn_id, reason: "stop" }));
|
|
3262
|
+
break;
|
|
3263
|
+
case "PostCompaction":
|
|
3264
|
+
events.push(ev("context.compacted", { trigger: "auto" }));
|
|
3265
|
+
break;
|
|
3266
|
+
case "SessionEnd":
|
|
3267
|
+
events.push(ev("session.ended", { reason: endReason3(s.reason) }));
|
|
3268
|
+
break;
|
|
3269
|
+
default:
|
|
3270
|
+
return null;
|
|
3271
|
+
}
|
|
3272
|
+
return {
|
|
3273
|
+
session,
|
|
3274
|
+
events,
|
|
3275
|
+
hints: {},
|
|
3276
|
+
transcript: transcriptRef(sessionsDb(input.env), id)
|
|
3277
|
+
};
|
|
3278
|
+
},
|
|
3279
|
+
readTranscript(file, fromLine) {
|
|
3280
|
+
const ref = splitRef(file);
|
|
3281
|
+
if (!ref) return { turns: [], line: fromLine };
|
|
3282
|
+
return readDevinChain(ref.db, ref.session, fromLine);
|
|
3283
|
+
},
|
|
3284
|
+
facts(session, transcript, _hints, _state, env = process.env) {
|
|
3285
|
+
const out = {};
|
|
3286
|
+
const version = devinVersion(env);
|
|
3287
|
+
if (version) out.harness_version = version;
|
|
3288
|
+
const account = devinAccount(env);
|
|
3289
|
+
if (account) out.account_id = account;
|
|
3290
|
+
const ref = splitRef(transcript) ?? { db: sessionsDb(env), session: session.id };
|
|
3291
|
+
if (!SESSION_ID.test(ref.session)) return out;
|
|
3292
|
+
const rows2 = querySqlite(ref.db, SESSION_SQL, [ref.session]);
|
|
3293
|
+
const row = rows2?.[0];
|
|
3294
|
+
if (!row) return out;
|
|
3295
|
+
Object.assign(out, sessionFacts(row));
|
|
3296
|
+
return out;
|
|
3297
|
+
},
|
|
3298
|
+
backfill(sinceMs, env = process.env) {
|
|
3299
|
+
const rows2 = querySqlite(sessionsDb(env), BACKFILL_SQL, ["text", Math.floor(sinceMs / 1e3)]);
|
|
3300
|
+
if (!rows2) return [];
|
|
3301
|
+
const out = [];
|
|
3302
|
+
for (const row of rows2) {
|
|
3303
|
+
const id = String(row.id);
|
|
3304
|
+
if (!SESSION_ID.test(id)) continue;
|
|
3305
|
+
const { started_at, last_at, ...rest } = sessionFacts(row);
|
|
3306
|
+
if (!started_at || !last_at) continue;
|
|
3307
|
+
out.push({ session: { id, ...rest }, started_at, last_at });
|
|
3308
|
+
}
|
|
3309
|
+
return out;
|
|
3310
|
+
}
|
|
3311
|
+
};
|
|
3312
|
+
function prune4(o) {
|
|
3313
|
+
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
3314
|
+
return o;
|
|
3315
|
+
}
|
|
3316
|
+
var transcriptRef = (db, session) => `${db}#${session}`;
|
|
3317
|
+
function splitRef(ref) {
|
|
3318
|
+
if (!ref) return null;
|
|
3319
|
+
const at = ref.lastIndexOf("#");
|
|
3320
|
+
if (at <= 0) return null;
|
|
3321
|
+
const db = ref.slice(0, at);
|
|
3322
|
+
const session = ref.slice(at + 1);
|
|
3323
|
+
return db && SESSION_ID.test(session) ? { db, session } : null;
|
|
3324
|
+
}
|
|
3325
|
+
var CHAIN_LIMIT = 4e3;
|
|
3326
|
+
var CHAIN_SQL = `WITH RECURSIVE chain(node_id, parent_node_id, chat_message, created_at) AS (
|
|
3327
|
+
SELECT node_id, parent_node_id, chat_message, created_at FROM message_nodes
|
|
3328
|
+
WHERE session_id = ? AND node_id = (SELECT main_chain_id FROM sessions WHERE id = ?)
|
|
3329
|
+
UNION ALL
|
|
3330
|
+
SELECT m.node_id, m.parent_node_id, m.chat_message, m.created_at FROM message_nodes m
|
|
3331
|
+
JOIN chain c ON m.node_id = c.parent_node_id AND m.session_id = ? AND m.node_id >= ?
|
|
3332
|
+
)
|
|
3333
|
+
SELECT node_id, chat_message, created_at FROM chain ORDER BY node_id DESC LIMIT ${CHAIN_LIMIT}`;
|
|
3334
|
+
var SESSION_FIELDS = `s.model, s.working_directory, s.created_at, s.last_activity_at,
|
|
3335
|
+
(SELECT p.content FROM prompt_history p WHERE p.session_id = s.id AND p.is_shell = 0
|
|
3336
|
+
ORDER BY p.timestamp ASC, p.id ASC LIMIT 1) AS first_ask`;
|
|
3337
|
+
var SESSION_SQL = `SELECT ${SESSION_FIELDS} FROM sessions s WHERE s.id = ? LIMIT 1`;
|
|
3338
|
+
var BACKFILL_SQL = `SELECT s.id, ${SESSION_FIELDS} FROM sessions s
|
|
3339
|
+
WHERE s.hidden = 0 AND typeof(s.id) = ? AND s.last_activity_at >= ?
|
|
3340
|
+
ORDER BY s.last_activity_at DESC LIMIT 500`;
|
|
3341
|
+
function sessionFacts(row) {
|
|
3342
|
+
const out = {};
|
|
3343
|
+
if (typeof row.model === "string" && row.model) out.model = row.model;
|
|
3344
|
+
if (typeof row.working_directory === "string" && path12.isAbsolute(row.working_directory))
|
|
3345
|
+
out.cwd = row.working_directory;
|
|
3346
|
+
const started = Number(row.created_at);
|
|
3347
|
+
const last = Number(row.last_activity_at);
|
|
3348
|
+
if (Number.isFinite(started) && started > 0) out.started_at = Math.round(started * 1e3);
|
|
3349
|
+
if (Number.isFinite(last) && last > 0) out.last_at = Math.round(last * 1e3);
|
|
3350
|
+
if (typeof row.first_ask === "string") {
|
|
3351
|
+
const line = row.first_ask.split("\n").find((l) => l.trim()) ?? "";
|
|
3352
|
+
if (line.trim()) {
|
|
3353
|
+
out.title = cut(line.trim(), 80);
|
|
3354
|
+
out.title_source = "first-ask";
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
return out;
|
|
3358
|
+
}
|
|
3359
|
+
function readDevinChain(db, session, fromLine) {
|
|
3360
|
+
if (!SESSION_ID.test(session)) return { turns: [], line: fromLine };
|
|
3361
|
+
const floor = Number.isFinite(fromLine) && fromLine > 0 ? Math.floor(fromLine) : 0;
|
|
3362
|
+
const rows2 = querySqlite(db, CHAIN_SQL, [session, session, session, floor]);
|
|
3363
|
+
if (!rows2?.length) return { turns: [], line: fromLine };
|
|
3364
|
+
const msgs = [];
|
|
3365
|
+
let end = fromLine;
|
|
3366
|
+
for (const row of rows2) {
|
|
3367
|
+
const node = Number(row.node_id);
|
|
3368
|
+
if (!Number.isFinite(node)) continue;
|
|
3369
|
+
if (node + 1 > end) end = node + 1;
|
|
3370
|
+
if (node < fromLine) continue;
|
|
3371
|
+
let j;
|
|
3372
|
+
try {
|
|
3373
|
+
j = JSON.parse(String(row.chat_message ?? ""));
|
|
3374
|
+
} catch {
|
|
3375
|
+
continue;
|
|
3376
|
+
}
|
|
3377
|
+
const text = typeof j.content === "string" ? j.content.trim() : "";
|
|
3378
|
+
if (!text) continue;
|
|
3379
|
+
const secs = Number(row.created_at);
|
|
3380
|
+
const at = Date.parse(String(j.metadata?.created_at ?? "")) || (Number.isFinite(secs) && secs > 0 ? Math.round(secs * 1e3) : 0) || Date.now();
|
|
3381
|
+
if (j.role === "user" && j.metadata?.is_user_input === true) msgs.push({ role: "user", text, at, line: node });
|
|
3382
|
+
else if (j.role === "assistant") msgs.push({ role: "assistant", text, at, line: node });
|
|
3383
|
+
}
|
|
3384
|
+
msgs.sort((a, b) => a.line - b.line);
|
|
3385
|
+
return pairTurns(msgs, end);
|
|
3386
|
+
}
|
|
3387
|
+
function devinVersion(env = process.env) {
|
|
3388
|
+
try {
|
|
3389
|
+
const target = readlinkSync(path12.join(devinDataDir(env), "_versions", "current"));
|
|
3390
|
+
const name = path12.basename(target);
|
|
3391
|
+
return /^[0-9][0-9A-Za-z.+-]{0,63}$/.test(name) ? name : void 0;
|
|
3392
|
+
} catch {
|
|
3393
|
+
return void 0;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
function devinAccount(env = process.env) {
|
|
3397
|
+
const d = readJson(configFile3(env)).devin;
|
|
3398
|
+
const id = d && typeof d === "object" ? d.org_id : void 0;
|
|
3399
|
+
return typeof id === "string" && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : void 0;
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
// src/adapters/gemini-cli.ts
|
|
3403
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
3404
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
|
|
3405
|
+
import path13 from "node:path";
|
|
3406
|
+
var NAME5 = "gemini-cli";
|
|
2409
3407
|
var GEMINI_EVENTS = [
|
|
2410
3408
|
"SessionStart",
|
|
2411
3409
|
"BeforeAgent",
|
|
@@ -2416,15 +3414,15 @@ var GEMINI_EVENTS = [
|
|
|
2416
3414
|
"PreCompress",
|
|
2417
3415
|
"SessionEnd"
|
|
2418
3416
|
];
|
|
2419
|
-
var
|
|
2420
|
-
var geminiDir = (env) => env.GEMINI_CLI_HOME ||
|
|
2421
|
-
var settings = (env) =>
|
|
2422
|
-
var
|
|
2423
|
-
var
|
|
3417
|
+
var OPTS4 = { matcherFor: (e) => /Tool/.test(e) ? ".*" : void 0, timeout: 5e3 };
|
|
3418
|
+
var geminiDir = (env) => env.GEMINI_CLI_HOME || path13.join(homeDir(), ".gemini");
|
|
3419
|
+
var settings = (env) => path13.join(geminiDir(env), "settings.json");
|
|
3420
|
+
var validTool5 = (n) => typeof n === "string" && /^[A-Za-z0-9_.:-]{1,120}$/.test(n);
|
|
3421
|
+
var eventsFor4 = (lean) => lean ? GEMINI_EVENTS.filter((e) => e !== "BeforeTool") : [...GEMINI_EVENTS];
|
|
2424
3422
|
function geminiHooksActive(settingsFile) {
|
|
2425
3423
|
let tools;
|
|
2426
3424
|
try {
|
|
2427
|
-
tools = JSON.parse(
|
|
3425
|
+
tools = JSON.parse(readFileSync7(settingsFile, "utf8")).tools;
|
|
2428
3426
|
} catch {
|
|
2429
3427
|
}
|
|
2430
3428
|
if (tools?.enableHooks === true) return { active: true };
|
|
@@ -2432,7 +3430,7 @@ function geminiHooksActive(settingsFile) {
|
|
|
2432
3430
|
return { active: false, note: "inactive: tools.enableHooks is false in settings.json" };
|
|
2433
3431
|
let version = "";
|
|
2434
3432
|
try {
|
|
2435
|
-
version =
|
|
3433
|
+
version = execFileSync5("gemini", ["--version"], {
|
|
2436
3434
|
encoding: "utf8",
|
|
2437
3435
|
timeout: 4e3,
|
|
2438
3436
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -2451,13 +3449,13 @@ function geminiHooksActive(settingsFile) {
|
|
|
2451
3449
|
};
|
|
2452
3450
|
}
|
|
2453
3451
|
var geminiCli = {
|
|
2454
|
-
name:
|
|
3452
|
+
name: NAME5,
|
|
2455
3453
|
events: GEMINI_EVENTS,
|
|
2456
3454
|
// ~/.gemini also belongs to Antigravity; Gemini CLI is here when its settings or its chat logs exist.
|
|
2457
|
-
detect: (env = process.env) =>
|
|
3455
|
+
detect: (env = process.env) => existsSync7(settings(env)) || existsSync7(path13.join(geminiDir(env), "tmp")),
|
|
2458
3456
|
configFiles: (env = process.env) => [settings(env)],
|
|
2459
3457
|
install: (cmd, env = process.env, opts = {}) => {
|
|
2460
|
-
const r = installClaudeShaped(settings(env),
|
|
3458
|
+
const r = installClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
2461
3459
|
const a = geminiHooksActive(settings(env));
|
|
2462
3460
|
return [a.note && !r.skipped ? { ...r, note: [r.note, a.note].filter(Boolean).join("; ") } : r];
|
|
2463
3461
|
},
|
|
@@ -2465,7 +3463,7 @@ var geminiCli = {
|
|
|
2465
3463
|
uninstallClaudeShaped(settings(env), GEMINI_EVENTS, { created: opts.created?.includes(settings(env)) })
|
|
2466
3464
|
],
|
|
2467
3465
|
installed: (cmd, env = process.env, opts = {}) => {
|
|
2468
|
-
const state = installedClaudeShaped(settings(env),
|
|
3466
|
+
const state = installedClaudeShaped(settings(env), NAME5, eventsFor4(opts.lean), cmd, OPTS4);
|
|
2469
3467
|
const a = geminiHooksActive(settings(env));
|
|
2470
3468
|
return [
|
|
2471
3469
|
{
|
|
@@ -2479,14 +3477,15 @@ var geminiCli = {
|
|
|
2479
3477
|
const event = input.argv[1] ?? "";
|
|
2480
3478
|
let s = {};
|
|
2481
3479
|
try {
|
|
2482
|
-
|
|
3480
|
+
const j = input.stdin ? JSON.parse(input.stdin) : {};
|
|
3481
|
+
if (j && typeof j === "object" && !Array.isArray(j)) s = j;
|
|
2483
3482
|
} catch {
|
|
2484
3483
|
}
|
|
2485
3484
|
const id = s.session_id;
|
|
2486
3485
|
if (!id) return null;
|
|
2487
3486
|
const session = { id: String(id), cwd: typeof s.cwd === "string" ? s.cwd : input.cwd };
|
|
2488
|
-
const tool =
|
|
2489
|
-
const ev = (type, data) => ({ type, data:
|
|
3487
|
+
const tool = validTool5(s.tool_name) ? s.tool_name : void 0;
|
|
3488
|
+
const ev = (type, data) => ({ type, data: prune5(data), harnessEvent: event });
|
|
2490
3489
|
const events = [];
|
|
2491
3490
|
switch (event) {
|
|
2492
3491
|
case "SessionStart":
|
|
@@ -2551,7 +3550,7 @@ var geminiCli = {
|
|
|
2551
3550
|
readTranscript(file, fromLine) {
|
|
2552
3551
|
let text = "";
|
|
2553
3552
|
try {
|
|
2554
|
-
text =
|
|
3553
|
+
text = readFileSync7(file, "utf8");
|
|
2555
3554
|
} catch {
|
|
2556
3555
|
return { turns: [], line: fromLine };
|
|
2557
3556
|
}
|
|
@@ -2567,7 +3566,7 @@ var geminiCli = {
|
|
|
2567
3566
|
const raw = first ? first.content ?? first.message ?? first.text : void 0;
|
|
2568
3567
|
const text = typeof raw === "string" ? raw.trim() : "";
|
|
2569
3568
|
if (text) {
|
|
2570
|
-
const line = text.split("\n")
|
|
3569
|
+
const line = text.split("\n")[0];
|
|
2571
3570
|
out.title = line.length > 80 ? `${line.slice(0, 80).replace(/\s+\S*$/, "")}\u2026` : line;
|
|
2572
3571
|
out.title_source = "first-ask";
|
|
2573
3572
|
}
|
|
@@ -2581,29 +3580,31 @@ var geminiCli = {
|
|
|
2581
3580
|
backfill: () => []
|
|
2582
3581
|
// chat logs are keyed by a project hash, not a session id we can name; the first hook of a resumed session reports it
|
|
2583
3582
|
};
|
|
2584
|
-
function
|
|
3583
|
+
function prune5(o) {
|
|
2585
3584
|
for (const k of Object.keys(o)) if (o[k] === void 0) delete o[k];
|
|
2586
3585
|
return o;
|
|
2587
3586
|
}
|
|
2588
3587
|
|
|
2589
3588
|
// src/adapters/index.ts
|
|
2590
|
-
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity];
|
|
3589
|
+
var ADAPTERS = [claudeCode, codex, geminiCli, antigravity, devin];
|
|
2591
3590
|
var adapterByName = (name) => ADAPTERS.find((a) => a.name === name);
|
|
3591
|
+
var HELLO_HARNESSES_MAX = 20;
|
|
2592
3592
|
function drivableHarnesses(env = process.env) {
|
|
2593
3593
|
return [
|
|
2594
3594
|
...findClaude(env) ? ["claude-code"] : [],
|
|
2595
3595
|
...findCodex(env) ? ["codex"] : [],
|
|
2596
|
-
...findAgy(env) ? ["antigravity"] : []
|
|
2597
|
-
|
|
3596
|
+
...findAgy(env) ? ["antigravity"] : [],
|
|
3597
|
+
...acpHarnesses(env)
|
|
3598
|
+
].slice(0, HELLO_HARNESSES_MAX);
|
|
2598
3599
|
}
|
|
2599
3600
|
|
|
2600
3601
|
// src/config.ts
|
|
2601
|
-
import { chmodSync, existsSync as
|
|
2602
|
-
import
|
|
3602
|
+
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
3603
|
+
import path14 from "node:path";
|
|
2603
3604
|
var DEFAULT_CONFIG = { sinks: [], harnesses: {}, update_check: true, keep_days: 30 };
|
|
2604
3605
|
function readConfig(file = configFile()) {
|
|
2605
3606
|
try {
|
|
2606
|
-
const j = JSON.parse(
|
|
3607
|
+
const j = JSON.parse(readFileSync8(file, "utf8"));
|
|
2607
3608
|
return {
|
|
2608
3609
|
...DEFAULT_CONFIG,
|
|
2609
3610
|
...j,
|
|
@@ -2615,10 +3616,10 @@ function readConfig(file = configFile()) {
|
|
|
2615
3616
|
}
|
|
2616
3617
|
}
|
|
2617
3618
|
function writeConfig(c, file = configFile()) {
|
|
2618
|
-
mkdirSync4(
|
|
3619
|
+
mkdirSync4(path14.dirname(file), { recursive: true, mode: 448 });
|
|
2619
3620
|
writeFileSync4(file, `${JSON.stringify(c, null, 2)}
|
|
2620
3621
|
`, { mode: 384 });
|
|
2621
|
-
if (process.platform !== "win32" &&
|
|
3622
|
+
if (process.platform !== "win32" && existsSync8(file)) {
|
|
2622
3623
|
try {
|
|
2623
3624
|
chmodSync(file, 384);
|
|
2624
3625
|
} catch {
|
|
@@ -2647,7 +3648,7 @@ function encodeTime(t) {
|
|
|
2647
3648
|
function ulid(now = Date.now()) {
|
|
2648
3649
|
if (now === lastTime) {
|
|
2649
3650
|
for (let i = lastRand.length - 1; i >= 0; i--) {
|
|
2650
|
-
const v =
|
|
3651
|
+
const v = lastRand[i] + 1;
|
|
2651
3652
|
if (v < 32) {
|
|
2652
3653
|
lastRand[i] = v;
|
|
2653
3654
|
break;
|
|
@@ -2656,8 +3657,7 @@ function ulid(now = Date.now()) {
|
|
|
2656
3657
|
}
|
|
2657
3658
|
} else {
|
|
2658
3659
|
lastTime = now;
|
|
2659
|
-
|
|
2660
|
-
lastRand = Array.from({ length: 16 }, (_, i) => (b[i] ?? 0) % 32);
|
|
3660
|
+
lastRand = Array.from(randomBytes(16), (b) => b % 32);
|
|
2661
3661
|
}
|
|
2662
3662
|
return encodeTime(now) + lastRand.map((v) => ALPHABET[v]).join("");
|
|
2663
3663
|
}
|
|
@@ -2681,57 +3681,76 @@ function makeEvent(a, ctx) {
|
|
|
2681
3681
|
};
|
|
2682
3682
|
}
|
|
2683
3683
|
|
|
3684
|
+
// src/launched-by.ts
|
|
3685
|
+
var LAUNCHED_BY_ENV = "SESSIONPIPE_LAUNCHED_BY";
|
|
3686
|
+
var LAUNCHED_BY = /^[a-z][a-z0-9-]{0,39}:[A-Za-z0-9._:-]{1,128}$/;
|
|
3687
|
+
function launchedBy(env) {
|
|
3688
|
+
const v = env[LAUNCHED_BY_ENV]?.trim();
|
|
3689
|
+
return v && LAUNCHED_BY.test(v) ? v : void 0;
|
|
3690
|
+
}
|
|
3691
|
+
function readHook(adapter, input) {
|
|
3692
|
+
const r = adapter.fromHook(input);
|
|
3693
|
+
if (r && !r.session.parent_id) {
|
|
3694
|
+
const launcher = launchedBy(input.env);
|
|
3695
|
+
if (launcher && launcher !== `${adapter.name}:${r.session.id}`) r.session.launched_by = launcher;
|
|
3696
|
+
}
|
|
3697
|
+
return r;
|
|
3698
|
+
}
|
|
3699
|
+
|
|
2684
3700
|
// src/outbox.ts
|
|
2685
3701
|
import {
|
|
2686
3702
|
appendFileSync,
|
|
2687
3703
|
closeSync as closeSync3,
|
|
2688
|
-
existsSync as
|
|
3704
|
+
existsSync as existsSync9,
|
|
2689
3705
|
mkdirSync as mkdirSync5,
|
|
2690
3706
|
openSync as openSync3,
|
|
2691
3707
|
readdirSync as readdirSync6,
|
|
2692
|
-
readFileSync as
|
|
3708
|
+
readFileSync as readFileSync9,
|
|
2693
3709
|
readSync as readSync3,
|
|
2694
3710
|
renameSync,
|
|
2695
3711
|
statSync as statSync6,
|
|
2696
3712
|
unlinkSync as unlinkSync3,
|
|
2697
3713
|
writeFileSync as writeFileSync5
|
|
2698
3714
|
} from "node:fs";
|
|
2699
|
-
import
|
|
3715
|
+
import path15 from "node:path";
|
|
3716
|
+
var READ_CHUNK = 1024 * 1024;
|
|
3717
|
+
var READ_MAX = 8 * 1024 * 1024;
|
|
2700
3718
|
var Outbox = class {
|
|
2701
3719
|
constructor(root) {
|
|
2702
3720
|
this.root = root;
|
|
2703
3721
|
}
|
|
2704
3722
|
root;
|
|
2705
3723
|
dir(harness) {
|
|
2706
|
-
return
|
|
3724
|
+
return path15.join(this.root, "outbox", safeId(harness));
|
|
2707
3725
|
}
|
|
2708
3726
|
file(ref) {
|
|
2709
|
-
return
|
|
3727
|
+
return path15.join(this.dir(ref.harness), `${safeId(ref.session)}.jsonl`);
|
|
2710
3728
|
}
|
|
2711
3729
|
seqFile(ref) {
|
|
2712
|
-
return
|
|
3730
|
+
return path15.join(this.dir(ref.harness), `${safeId(ref.session)}.seq`);
|
|
2713
3731
|
}
|
|
2714
3732
|
/** The next seq for a session, persisted (survives a hibernating machine and a
|
|
2715
3733
|
* restart; an in-memory counter would restart at 0 and collide). */
|
|
2716
3734
|
nextSeq(ref) {
|
|
2717
3735
|
const f = this.seqFile(ref);
|
|
2718
|
-
let n =
|
|
3736
|
+
let n = -1;
|
|
2719
3737
|
try {
|
|
2720
|
-
|
|
3738
|
+
const t = readFileSync9(f, "utf8").trim();
|
|
3739
|
+
if (/^\d+$/.test(t)) n = Number(t);
|
|
2721
3740
|
} catch {
|
|
2722
|
-
n = this.lastSeqInFile(ref) + 1;
|
|
2723
3741
|
}
|
|
2724
|
-
|
|
3742
|
+
if (n < 0) n = this.lastSeqInFile(ref) + 1;
|
|
3743
|
+
mkdirSync5(path15.dirname(f), { recursive: true, mode: 448 });
|
|
2725
3744
|
writeFileSync5(f, String(n + 1), { mode: 384 });
|
|
2726
3745
|
return n;
|
|
2727
3746
|
}
|
|
2728
3747
|
lastSeqInFile(ref) {
|
|
2729
3748
|
const f = this.file(ref);
|
|
2730
|
-
if (!
|
|
2731
|
-
const lines =
|
|
3749
|
+
if (!existsSync9(f)) return -1;
|
|
3750
|
+
const lines = readFileSync9(f, "utf8").trim().split("\n");
|
|
2732
3751
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
2733
3752
|
try {
|
|
2734
|
-
const j = JSON.parse(lines[i]
|
|
3753
|
+
const j = JSON.parse(lines[i]);
|
|
2735
3754
|
if (typeof j.session?.seq === "number") return j.session.seq;
|
|
2736
3755
|
} catch {
|
|
2737
3756
|
}
|
|
@@ -2747,10 +3766,10 @@ var Outbox = class {
|
|
|
2747
3766
|
/** Every session file, newest first. */
|
|
2748
3767
|
sessions() {
|
|
2749
3768
|
const out = [];
|
|
2750
|
-
const base =
|
|
2751
|
-
if (!
|
|
3769
|
+
const base = path15.join(this.root, "outbox");
|
|
3770
|
+
if (!existsSync9(base)) return [];
|
|
2752
3771
|
for (const h of readdirSync6(base)) {
|
|
2753
|
-
const d =
|
|
3772
|
+
const d = path15.join(base, h);
|
|
2754
3773
|
let names = [];
|
|
2755
3774
|
try {
|
|
2756
3775
|
names = readdirSync6(d).filter((n) => n.endsWith(".jsonl"));
|
|
@@ -2759,7 +3778,7 @@ var Outbox = class {
|
|
|
2759
3778
|
}
|
|
2760
3779
|
for (const n of names) {
|
|
2761
3780
|
try {
|
|
2762
|
-
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(
|
|
3781
|
+
out.push({ ref: { harness: h, session: n.slice(0, -6) }, mtime: statSync6(path15.join(d, n)).mtimeMs });
|
|
2763
3782
|
} catch {
|
|
2764
3783
|
}
|
|
2765
3784
|
}
|
|
@@ -2767,31 +3786,43 @@ var Outbox = class {
|
|
|
2767
3786
|
return out.sort((a, b) => b.mtime - a.mtime).map((x) => x.ref);
|
|
2768
3787
|
}
|
|
2769
3788
|
/** Read events after byte offset `from`; returns them and the new offset. Lines
|
|
2770
|
-
* are read whole; a partial trailing line (a write in progress) is left.
|
|
3789
|
+
* are read whole; a partial trailing line (a write in progress) is left. A read
|
|
3790
|
+
* takes READ_CHUNK bytes at a time, not the rest of the file: a session file grows
|
|
3791
|
+
* to tens of MB and a drain takes it in batches of 50. It reads on until the batch
|
|
3792
|
+
* is full or the file ends, so lines that parse to nothing never stop a drain; one
|
|
3793
|
+
* line longer than the chunk gets a larger one, up to READ_MAX. */
|
|
2771
3794
|
read(ref, from, maxEvents = 500) {
|
|
2772
3795
|
const f = this.file(ref);
|
|
2773
|
-
if (!
|
|
3796
|
+
if (!existsSync9(f)) return { events: [], offset: from };
|
|
2774
3797
|
const size = statSync6(f).size;
|
|
2775
3798
|
if (from >= size) return { events: [], offset: from };
|
|
2776
3799
|
const fd = openSync3(f, "r");
|
|
2777
3800
|
try {
|
|
2778
|
-
const buf = Buffer.alloc(Math.min(size - from, 8 * 1024 * 1024));
|
|
2779
|
-
const n = readSync3(fd, buf, 0, buf.length, from);
|
|
2780
|
-
const text = buf.subarray(0, n).toString("utf8");
|
|
2781
3801
|
const events = [];
|
|
2782
3802
|
let offset = from;
|
|
2783
|
-
let
|
|
2784
|
-
while (events.length < maxEvents) {
|
|
2785
|
-
const
|
|
2786
|
-
|
|
2787
|
-
const
|
|
2788
|
-
pos =
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
3803
|
+
let chunk = READ_CHUNK;
|
|
3804
|
+
while (events.length < maxEvents && offset < size) {
|
|
3805
|
+
const buf = Buffer.alloc(Math.min(size - offset, chunk));
|
|
3806
|
+
const n = readSync3(fd, buf, 0, buf.length, offset);
|
|
3807
|
+
const text = buf.subarray(0, n).toString("utf8");
|
|
3808
|
+
let pos = 0;
|
|
3809
|
+
let used = 0;
|
|
3810
|
+
while (events.length < maxEvents) {
|
|
3811
|
+
const nl = text.indexOf("\n", pos);
|
|
3812
|
+
if (nl < 0) break;
|
|
3813
|
+
const line = text.slice(pos, nl);
|
|
3814
|
+
pos = nl + 1;
|
|
3815
|
+
used += Buffer.byteLength(line, "utf8") + 1;
|
|
3816
|
+
if (!line.trim()) continue;
|
|
3817
|
+
try {
|
|
3818
|
+
events.push(JSON.parse(line));
|
|
3819
|
+
} catch {
|
|
3820
|
+
}
|
|
2794
3821
|
}
|
|
3822
|
+
offset += used;
|
|
3823
|
+
if (used) continue;
|
|
3824
|
+
if (offset + n >= size || chunk >= READ_MAX) break;
|
|
3825
|
+
chunk *= 2;
|
|
2795
3826
|
}
|
|
2796
3827
|
return { events, offset };
|
|
2797
3828
|
} finally {
|
|
@@ -2835,18 +3866,18 @@ var Cursors = class {
|
|
|
2835
3866
|
root;
|
|
2836
3867
|
sink;
|
|
2837
3868
|
file() {
|
|
2838
|
-
return
|
|
3869
|
+
return path15.join(this.root, "cursors", `${safeId(this.sink)}.json`);
|
|
2839
3870
|
}
|
|
2840
3871
|
read() {
|
|
2841
3872
|
try {
|
|
2842
|
-
return JSON.parse(
|
|
3873
|
+
return JSON.parse(readFileSync9(this.file(), "utf8"));
|
|
2843
3874
|
} catch {
|
|
2844
3875
|
return {};
|
|
2845
3876
|
}
|
|
2846
3877
|
}
|
|
2847
3878
|
write(c) {
|
|
2848
3879
|
const f = this.file();
|
|
2849
|
-
mkdirSync5(
|
|
3880
|
+
mkdirSync5(path15.dirname(f), { recursive: true, mode: 448 });
|
|
2850
3881
|
const tmp = `${f}.${process.pid}.tmp`;
|
|
2851
3882
|
writeFileSync5(tmp, JSON.stringify(c), { mode: 384 });
|
|
2852
3883
|
renameSync(tmp, f);
|
|
@@ -2856,11 +3887,14 @@ var Cursors = class {
|
|
|
2856
3887
|
}
|
|
2857
3888
|
};
|
|
2858
3889
|
function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
2859
|
-
|
|
3890
|
+
return takeNamedLock(root, `${safeId(ref.harness)}-${safeId(ref.session)}`, staleMs);
|
|
3891
|
+
}
|
|
3892
|
+
function takeNamedLock(root, name, staleMs = 5 * 6e4) {
|
|
3893
|
+
const dir = path15.join(root, "locks");
|
|
2860
3894
|
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2861
|
-
const f =
|
|
3895
|
+
const f = path15.join(dir, `${name}.lock`);
|
|
2862
3896
|
try {
|
|
2863
|
-
if (Date.now() - statSync6(f).mtimeMs > staleMs) unlinkSync3(f);
|
|
3897
|
+
if (Date.now() - statSync6(f).mtimeMs > staleMs || !processAlive(Number(readFileSync9(f, "utf8")))) unlinkSync3(f);
|
|
2864
3898
|
else return null;
|
|
2865
3899
|
} catch {
|
|
2866
3900
|
}
|
|
@@ -2876,9 +3910,18 @@ function takeLock(root, ref, staleMs = 5 * 6e4) {
|
|
|
2876
3910
|
}
|
|
2877
3911
|
};
|
|
2878
3912
|
}
|
|
3913
|
+
function processAlive(pid) {
|
|
3914
|
+
if (!Number.isInteger(pid) || pid <= 0) return true;
|
|
3915
|
+
try {
|
|
3916
|
+
process.kill(pid, 0);
|
|
3917
|
+
return true;
|
|
3918
|
+
} catch (e) {
|
|
3919
|
+
return e.code === "EPERM";
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
2879
3922
|
|
|
2880
3923
|
// src/privacy/pii.ts
|
|
2881
|
-
import { createHash as
|
|
3924
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2882
3925
|
var RULESET_PII = "pii@1";
|
|
2883
3926
|
var EMAIL = /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g;
|
|
2884
3927
|
var PHONE = /(?<![\w.])(?:\+\d{1,3}[ .-]?)?(?:\(\d{2,4}\)[ .-]?)?\d{2,4}(?:[ .-]?\d{2,4}){1,3}(?!\w)/g;
|
|
@@ -2900,7 +3943,7 @@ function reducePii(text, home) {
|
|
|
2900
3943
|
return out;
|
|
2901
3944
|
}
|
|
2902
3945
|
function hashMachine(name) {
|
|
2903
|
-
return
|
|
3946
|
+
return createHash3("sha256").update(name).digest("hex").slice(0, 8);
|
|
2904
3947
|
}
|
|
2905
3948
|
function reducePiiDeep(v, home) {
|
|
2906
3949
|
if (typeof v === "string") return reducePii(v, home);
|
|
@@ -2989,12 +4032,12 @@ function redactAssignments(text) {
|
|
|
2989
4032
|
let last = 0;
|
|
2990
4033
|
NAME_SEP.lastIndex = 0;
|
|
2991
4034
|
for (let m = NAME_SEP.exec(text); m; m = NAME_SEP.exec(text)) {
|
|
2992
|
-
const name = m[1]
|
|
4035
|
+
const name = m[1];
|
|
2993
4036
|
if (!isSecretName(name)) continue;
|
|
2994
4037
|
VALUE.lastIndex = NAME_SEP.lastIndex;
|
|
2995
4038
|
const v = VALUE.exec(text);
|
|
2996
4039
|
if (!v) continue;
|
|
2997
|
-
const value = v[1] ?? v[2] ?? v[3] ?? v[4]
|
|
4040
|
+
const value = v[1] ?? v[2] ?? v[3] ?? v[4];
|
|
2998
4041
|
NAME_SEP.lastIndex = VALUE.lastIndex;
|
|
2999
4042
|
if (isPlaceholder(value) || value.includes(REDACTED)) continue;
|
|
3000
4043
|
if (/tokens?$/i.test(name) && /^\d+(?:[.,]\d+)*[km]?$/i.test(value)) continue;
|
|
@@ -3050,7 +4093,7 @@ function redactSecrets(text) {
|
|
|
3050
4093
|
let out = text;
|
|
3051
4094
|
for (const re of TOKEN_PATTERNS) out = out.replace(re, REDACTED);
|
|
3052
4095
|
out = out.replace(URL_CREDENTIALS, (_m, head, _pw, at) => `${head}${REDACTED}${at}`);
|
|
3053
|
-
out = out.replace(AUTH_HEADER, (
|
|
4096
|
+
out = out.replace(AUTH_HEADER, (_m, head) => `${head}${REDACTED}`);
|
|
3054
4097
|
out = redactAssignments(out);
|
|
3055
4098
|
out = out.replace(
|
|
3056
4099
|
PROSE,
|
|
@@ -3082,6 +4125,7 @@ var TYPE_TIER = {
|
|
|
3082
4125
|
"turn.started": 0,
|
|
3083
4126
|
"turn.ended": 0,
|
|
3084
4127
|
"turn.transcript": 2,
|
|
4128
|
+
"turn.progress": 2,
|
|
3085
4129
|
"tool.started": 1,
|
|
3086
4130
|
"tool.ended": 1,
|
|
3087
4131
|
"files.changed": 1,
|
|
@@ -3098,9 +4142,10 @@ var DATA_ALLOW = {
|
|
|
3098
4142
|
"session.ended": { 0: ["reason"] },
|
|
3099
4143
|
"session.backfill": { 0: ["started_at", "last_at", "turns"] },
|
|
3100
4144
|
"session.forgotten": { 0: [] },
|
|
3101
|
-
"turn.started": { 0: ["turn_id"], 1: ["prompt_chars"] },
|
|
3102
|
-
"turn.ended": { 0: ["turn_id", "reason", "ms"], 1: ["error"] },
|
|
4145
|
+
"turn.started": { 0: ["turn_id", "submitted_by", "injected"], 1: ["prompt_chars"] },
|
|
4146
|
+
"turn.ended": { 0: ["turn_id", "reason", "ms", "background"], 1: ["error"] },
|
|
3103
4147
|
"turn.transcript": { 2: ["turn", "user", "assistant", "at"] },
|
|
4148
|
+
"turn.progress": { 2: ["turn", "user", "assistant", "at"] },
|
|
3104
4149
|
"tool.started": { 1: ["tool", "call_id", "turn_id", "agent_id"], 3: ["input"] },
|
|
3105
4150
|
"tool.ended": { 1: ["tool", "call_id", "turn_id", "agent_id", "ms", "ok", "error"], 3: ["input", "output"] },
|
|
3106
4151
|
"files.changed": { 1: ["paths"] },
|
|
@@ -3122,7 +4167,9 @@ var SESSION_ALLOW = [
|
|
|
3122
4167
|
"title_source",
|
|
3123
4168
|
"url",
|
|
3124
4169
|
"account_id",
|
|
3125
|
-
"
|
|
4170
|
+
"attended",
|
|
4171
|
+
"parent_id",
|
|
4172
|
+
"launched_by"
|
|
3126
4173
|
];
|
|
3127
4174
|
var MAX_SIDE = 2e4;
|
|
3128
4175
|
var MAX_IO = 64 * 1024;
|
|
@@ -3141,10 +4188,12 @@ function cap(v, max) {
|
|
|
3141
4188
|
}
|
|
3142
4189
|
return v;
|
|
3143
4190
|
}
|
|
4191
|
+
function capTail(v, max) {
|
|
4192
|
+
return v.length > max ? `\u2026${v.slice(v.length - max + 1)}` : v;
|
|
4193
|
+
}
|
|
3144
4194
|
function filterEvent(event, tier, pii, opts = {}) {
|
|
3145
4195
|
const minTier = TYPE_TIER[event.type];
|
|
3146
4196
|
if (minTier !== void 0 && minTier > tier) return null;
|
|
3147
|
-
if (event.type === "native" && tier < 3) return null;
|
|
3148
4197
|
const session = {};
|
|
3149
4198
|
for (const k of SESSION_ALLOW)
|
|
3150
4199
|
if (event.session[k] !== void 0)
|
|
@@ -3154,7 +4203,8 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3154
4203
|
if (keys === null) data = { ...event.data };
|
|
3155
4204
|
else for (const k of keys) if (event.data[k] !== void 0) data[k] = event.data[k];
|
|
3156
4205
|
if (typeof data.user === "string") data.user = cap(data.user, MAX_SIDE);
|
|
3157
|
-
if (typeof data.assistant === "string")
|
|
4206
|
+
if (typeof data.assistant === "string")
|
|
4207
|
+
data.assistant = event.type === "turn.progress" ? capTail(data.assistant, MAX_SIDE) : cap(data.assistant, MAX_SIDE);
|
|
3158
4208
|
if (data.input !== void 0) data.input = cap(data.input, MAX_IO);
|
|
3159
4209
|
if (data.output !== void 0) data.output = cap(data.output, MAX_IO);
|
|
3160
4210
|
if (typeof data.message === "string") data.message = cap(data.message, 200);
|
|
@@ -3175,9 +4225,15 @@ function filterEvent(event, tier, pii, opts = {}) {
|
|
|
3175
4225
|
return out;
|
|
3176
4226
|
}
|
|
3177
4227
|
|
|
4228
|
+
// src/short-id.ts
|
|
4229
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
4230
|
+
function shortId(harness, id) {
|
|
4231
|
+
return createHash4("sha256").update(`sessionpipe.session:${harness}:${id}`).digest("hex").slice(0, 8);
|
|
4232
|
+
}
|
|
4233
|
+
|
|
3178
4234
|
// src/sinks/file.ts
|
|
3179
4235
|
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
3180
|
-
import
|
|
4236
|
+
import path16 from "node:path";
|
|
3181
4237
|
var FileSink = class {
|
|
3182
4238
|
constructor(name, file, tier, pii) {
|
|
3183
4239
|
this.name = name;
|
|
@@ -3191,7 +4247,7 @@ var FileSink = class {
|
|
|
3191
4247
|
pii;
|
|
3192
4248
|
async send(events) {
|
|
3193
4249
|
try {
|
|
3194
|
-
mkdirSync6(
|
|
4250
|
+
mkdirSync6(path16.dirname(this.file), { recursive: true });
|
|
3195
4251
|
appendFileSync2(this.file, `${events.map((e) => JSON.stringify(e)).join("\n")}
|
|
3196
4252
|
`);
|
|
3197
4253
|
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
@@ -3215,8 +4271,12 @@ var StdoutSink = class {
|
|
|
3215
4271
|
pii;
|
|
3216
4272
|
write;
|
|
3217
4273
|
async send(events) {
|
|
3218
|
-
|
|
3219
|
-
|
|
4274
|
+
try {
|
|
4275
|
+
for (const e of events) this.write(JSON.stringify(e));
|
|
4276
|
+
return { ok: true, acknowledged: events.length, accepted: events.length, duplicates: 0, rejected: [] };
|
|
4277
|
+
} catch (e) {
|
|
4278
|
+
return { ok: false, acknowledged: 0, error: String(e.message ?? e), action: "retry" };
|
|
4279
|
+
}
|
|
3220
4280
|
}
|
|
3221
4281
|
};
|
|
3222
4282
|
|
|
@@ -3227,9 +4287,11 @@ export {
|
|
|
3227
4287
|
AG_STALE_PRETOOL,
|
|
3228
4288
|
Cursors,
|
|
3229
4289
|
DEFAULT_CONFIG,
|
|
4290
|
+
DEVIN_EVENTS,
|
|
3230
4291
|
FileSink,
|
|
3231
|
-
|
|
4292
|
+
HELLO_HARNESSES_MAX,
|
|
3232
4293
|
JOB_TIMEOUT_MS,
|
|
4294
|
+
LAUNCHED_BY_ENV,
|
|
3233
4295
|
Outbox,
|
|
3234
4296
|
PROTOCOL_VERSION,
|
|
3235
4297
|
REDACTED,
|
|
@@ -3237,10 +4299,13 @@ export {
|
|
|
3237
4299
|
RULESET_SECRETS,
|
|
3238
4300
|
StdoutSink,
|
|
3239
4301
|
TYPE_TIER,
|
|
4302
|
+
acp_control_exports as acpControl,
|
|
3240
4303
|
adapterByName,
|
|
3241
4304
|
antigravity,
|
|
3242
4305
|
antigravityAnswer,
|
|
3243
4306
|
antigravity_control_exports as antigravityControl,
|
|
4307
|
+
capTail,
|
|
4308
|
+
claudeAccounts,
|
|
3244
4309
|
claudeCode,
|
|
3245
4310
|
claude_code_control_exports as claudeControl,
|
|
3246
4311
|
claudeDirs,
|
|
@@ -3248,35 +4313,47 @@ export {
|
|
|
3248
4313
|
codex_control_exports as codexControl,
|
|
3249
4314
|
configFile,
|
|
3250
4315
|
deniedNote,
|
|
4316
|
+
devin,
|
|
4317
|
+
devinConfigDir,
|
|
4318
|
+
devinDataDir,
|
|
3251
4319
|
drivableHarnesses,
|
|
3252
4320
|
filterEvent,
|
|
3253
4321
|
geminiCli,
|
|
3254
4322
|
gitFacts,
|
|
3255
4323
|
hasSecret,
|
|
3256
4324
|
hashMachine,
|
|
4325
|
+
homeDir,
|
|
3257
4326
|
isInjected,
|
|
3258
4327
|
isSecretName,
|
|
3259
4328
|
isUlid,
|
|
3260
4329
|
jobEnv,
|
|
4330
|
+
launchedBy,
|
|
3261
4331
|
machineName,
|
|
3262
4332
|
makeEvent,
|
|
4333
|
+
notClaudeCode,
|
|
3263
4334
|
pairTurns,
|
|
4335
|
+
processAlive,
|
|
3264
4336
|
readAntigravity,
|
|
3265
4337
|
readClaude,
|
|
3266
4338
|
readCodex,
|
|
3267
4339
|
readConfig,
|
|
4340
|
+
readDevinChain,
|
|
3268
4341
|
readGemini,
|
|
4342
|
+
readHook,
|
|
3269
4343
|
redactDeep,
|
|
3270
4344
|
redactSecrets,
|
|
3271
4345
|
reducePii,
|
|
3272
4346
|
reducePiiDeep,
|
|
3273
4347
|
runHeadless,
|
|
3274
4348
|
safeId,
|
|
4349
|
+
shortId,
|
|
3275
4350
|
stateDir,
|
|
3276
4351
|
stripCredentials,
|
|
3277
4352
|
takeLock,
|
|
4353
|
+
takeNamedLock,
|
|
3278
4354
|
tilde,
|
|
3279
4355
|
ulid,
|
|
4356
|
+
untilde,
|
|
3280
4357
|
writeConfig
|
|
3281
4358
|
};
|
|
3282
4359
|
//# sourceMappingURL=index.js.map
|