@fml-inc/fml 0.8.0-rc.1 → 0.8.0-rc.3
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/.claude-plugin/plugin.json +1 -1
- package/README.md +29 -16
- package/dist/{chunk-CK5MEL5J.js → chunk-3KISRT57.js} +1 -1
- package/dist/{chunk-HUR2HCUG.js → chunk-7DWFJENY.js} +2 -2
- package/dist/{chunk-T5YVLSUC.js → chunk-7V7G7NVF.js} +4 -0
- package/dist/{chunk-7DR5X7XI.js → chunk-A6HAEVJX.js} +136 -52
- package/dist/{chunk-F66IBM3Z.js → chunk-DZKYW327.js} +2 -2
- package/dist/{chunk-I4GZPTBM.js → chunk-ENW2GJNO.js} +1 -1
- package/dist/{chunk-DEMVOVVY.js → chunk-KBT5RMAU.js} +263 -32
- package/dist/{chunk-P3QLARXE.js → chunk-R45CWWZO.js} +3 -3
- package/dist/{chunk-JXSSPQ5B.js → chunk-YSU5CS2V.js} +1 -1
- package/dist/cli.js +1708 -1351
- package/dist/hook-handler.js +299 -42
- package/dist/mcp/server.js +7 -7
- package/dist/observer-server.js +8 -8
- package/dist/pi/extension.js +350 -0
- package/dist/sync-cycle.js +3 -3
- package/dist/{token-store-R7WXEPLI.js → token-store-BT5THIAJ.js} +1 -1
- package/package.json +6 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FML_HOOK_INBOX_DIR
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-A6HAEVJX.js";
|
|
4
4
|
|
|
5
5
|
// ../../packages/panopticon/src/observer/auth.ts
|
|
6
6
|
import crypto from "crypto";
|
|
@@ -71,7 +71,8 @@ var HOOK_TARGETS = Object.freeze([
|
|
|
71
71
|
"claude",
|
|
72
72
|
"codex",
|
|
73
73
|
"gemini",
|
|
74
|
-
"grok"
|
|
74
|
+
"grok",
|
|
75
|
+
"pi"
|
|
75
76
|
]);
|
|
76
77
|
function isHookTarget(value) {
|
|
77
78
|
return typeof value === "string" && HOOK_TARGETS.some((targetId) => targetId === value);
|
|
@@ -137,6 +138,20 @@ var hook_events_default = {
|
|
|
137
138
|
"PreCompact",
|
|
138
139
|
"PostCompact",
|
|
139
140
|
"SessionEnd"
|
|
141
|
+
],
|
|
142
|
+
pi: [
|
|
143
|
+
"SessionStart",
|
|
144
|
+
"UserPromptSubmit",
|
|
145
|
+
"TurnStart",
|
|
146
|
+
"PreToolUse",
|
|
147
|
+
"PostToolUse",
|
|
148
|
+
"PostToolUseFailure",
|
|
149
|
+
"Stop",
|
|
150
|
+
"Notification",
|
|
151
|
+
"ConfigChange",
|
|
152
|
+
"PreCompact",
|
|
153
|
+
"PostCompact",
|
|
154
|
+
"SessionEnd"
|
|
140
155
|
]
|
|
141
156
|
};
|
|
142
157
|
|
|
@@ -153,6 +168,221 @@ var GEMINI_HOOK_EVENTS = Object.freeze([
|
|
|
153
168
|
var GROK_HOOK_EVENTS = Object.freeze([
|
|
154
169
|
...hook_events_default.grok
|
|
155
170
|
]);
|
|
171
|
+
var PI_HOOK_EVENTS = Object.freeze([...hook_events_default.pi]);
|
|
172
|
+
|
|
173
|
+
// ../../packages/agent-targets/src/hooks/read-command.ts
|
|
174
|
+
import path2 from "path";
|
|
175
|
+
var SHELL_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
176
|
+
"bash",
|
|
177
|
+
"exec_command",
|
|
178
|
+
"shell"
|
|
179
|
+
]);
|
|
180
|
+
var COMMAND_SEPARATORS = /* @__PURE__ */ new Set([
|
|
181
|
+
";",
|
|
182
|
+
"&",
|
|
183
|
+
"&&",
|
|
184
|
+
"|",
|
|
185
|
+
"||",
|
|
186
|
+
"<",
|
|
187
|
+
"<<",
|
|
188
|
+
">",
|
|
189
|
+
">>",
|
|
190
|
+
">&1",
|
|
191
|
+
">&2",
|
|
192
|
+
"1>",
|
|
193
|
+
"1>>",
|
|
194
|
+
"1>&1",
|
|
195
|
+
"1>&2",
|
|
196
|
+
"2>",
|
|
197
|
+
"2>>",
|
|
198
|
+
"2>&1",
|
|
199
|
+
"2>&2"
|
|
200
|
+
]);
|
|
201
|
+
function tokenizeShellLikeCommand(command) {
|
|
202
|
+
const tokens = [];
|
|
203
|
+
let current = "";
|
|
204
|
+
let quote = null;
|
|
205
|
+
let escaping = false;
|
|
206
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
207
|
+
const character = command[index];
|
|
208
|
+
if (escaping) {
|
|
209
|
+
current += character;
|
|
210
|
+
escaping = false;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (character === "\\") {
|
|
214
|
+
escaping = true;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (quote) {
|
|
218
|
+
if (character === quote) quote = null;
|
|
219
|
+
else current += character;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (character === "'" || character === '"') {
|
|
223
|
+
quote = character;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (current === "" && (character === "1" || character === "2") && command[index + 1] === ">") {
|
|
227
|
+
if (command[index + 2] === ">") {
|
|
228
|
+
tokens.push(`${character}>>`);
|
|
229
|
+
index += 2;
|
|
230
|
+
} else if (command[index + 2] === "&" && (command[index + 3] === "1" || command[index + 3] === "2")) {
|
|
231
|
+
tokens.push(`${character}>&${command[index + 3]}`);
|
|
232
|
+
index += 3;
|
|
233
|
+
} else {
|
|
234
|
+
tokens.push(`${character}>`);
|
|
235
|
+
index += 1;
|
|
236
|
+
}
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
if (/[;&|<>]/.test(character)) {
|
|
240
|
+
if (current) {
|
|
241
|
+
tokens.push(current);
|
|
242
|
+
current = "";
|
|
243
|
+
}
|
|
244
|
+
const next = command[index + 1];
|
|
245
|
+
if (character === ">" && next === "&" && (command[index + 2] === "1" || command[index + 2] === "2")) {
|
|
246
|
+
tokens.push(`>&${command[index + 2]}`);
|
|
247
|
+
index += 2;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (character === "&" && next === "&" || character === "|" && next === "|" || character === "<" && next === "<" || character === ">" && next === ">") {
|
|
251
|
+
tokens.push(`${character}${next}`);
|
|
252
|
+
index += 1;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
tokens.push(character);
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (/\s/.test(character)) {
|
|
259
|
+
if (current) {
|
|
260
|
+
tokens.push(current);
|
|
261
|
+
current = "";
|
|
262
|
+
}
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
current += character;
|
|
266
|
+
}
|
|
267
|
+
if (quote || escaping) return [];
|
|
268
|
+
if (current) tokens.push(current);
|
|
269
|
+
return tokens;
|
|
270
|
+
}
|
|
271
|
+
function splitCommandSegments(tokens) {
|
|
272
|
+
const segments = [];
|
|
273
|
+
let current = [];
|
|
274
|
+
for (let index = 0; index < tokens.length; index += 1) {
|
|
275
|
+
const token = tokens[index];
|
|
276
|
+
if (COMMAND_SEPARATORS.has(token)) {
|
|
277
|
+
if (current.length > 0) {
|
|
278
|
+
segments.push(current);
|
|
279
|
+
current = [];
|
|
280
|
+
}
|
|
281
|
+
if (token === "<" || token === "<<") index += 1;
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
current.push(token);
|
|
285
|
+
}
|
|
286
|
+
if (current.length > 0) segments.push(current);
|
|
287
|
+
return segments;
|
|
288
|
+
}
|
|
289
|
+
function stripSimpleOptions(args, flagsWithValues = /* @__PURE__ */ new Set(), patternFlags = /* @__PURE__ */ new Set()) {
|
|
290
|
+
const remaining = [];
|
|
291
|
+
let consumedPatternFlag = false;
|
|
292
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
293
|
+
const argument = args[index];
|
|
294
|
+
if (argument === "--") {
|
|
295
|
+
remaining.push(...args.slice(index + 1));
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
if (argument.startsWith("-") && argument.length > 1) {
|
|
299
|
+
if (flagsWithValues.has(argument)) {
|
|
300
|
+
if (patternFlags.has(argument)) consumedPatternFlag = true;
|
|
301
|
+
if (!argument.includes("=")) index += 1;
|
|
302
|
+
}
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
remaining.push(argument);
|
|
306
|
+
}
|
|
307
|
+
return { args: remaining, consumedPatternFlag };
|
|
308
|
+
}
|
|
309
|
+
function readCommandFileArgs(args, flagsWithValues, patternFlags = flagsWithValues) {
|
|
310
|
+
const stripped = stripSimpleOptions(args, flagsWithValues, patternFlags);
|
|
311
|
+
return stripped.consumedPatternFlag ? stripped.args : stripped.args.slice(1);
|
|
312
|
+
}
|
|
313
|
+
function isPlausibleReadPath(argument) {
|
|
314
|
+
if (!argument || argument === "-" || argument.startsWith("-")) return false;
|
|
315
|
+
if (COMMAND_SEPARATORS.has(argument)) return false;
|
|
316
|
+
if (/[\0*?[{}`$]/.test(argument)) return false;
|
|
317
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(argument)) return false;
|
|
318
|
+
return path2.posix.isAbsolute(argument) || path2.win32.isAbsolute(argument) || argument.startsWith("~/") || argument.startsWith("./") || argument.startsWith("../") || argument.includes("/") || /\.[A-Za-z0-9][A-Za-z0-9_-]*$/.test(argument) || /^[A-Z][A-Za-z0-9_.-]*$/.test(argument);
|
|
319
|
+
}
|
|
320
|
+
function filePathFromSegment(tokens) {
|
|
321
|
+
const commandTokens = [...tokens];
|
|
322
|
+
while (commandTokens[0]?.includes("=") && /^[A-Za-z_][A-Za-z0-9_]*=/.test(commandTokens[0])) {
|
|
323
|
+
commandTokens.shift();
|
|
324
|
+
}
|
|
325
|
+
const commandName = commandTokens.shift()?.split("/").pop();
|
|
326
|
+
if (!commandName) return null;
|
|
327
|
+
let args;
|
|
328
|
+
switch (commandName) {
|
|
329
|
+
case "bat":
|
|
330
|
+
case "batcat":
|
|
331
|
+
case "cat":
|
|
332
|
+
case "file":
|
|
333
|
+
case "less":
|
|
334
|
+
case "more":
|
|
335
|
+
case "nl":
|
|
336
|
+
case "stat":
|
|
337
|
+
case "wc":
|
|
338
|
+
args = stripSimpleOptions(commandTokens).args;
|
|
339
|
+
break;
|
|
340
|
+
case "head":
|
|
341
|
+
case "tail":
|
|
342
|
+
args = stripSimpleOptions(commandTokens, /* @__PURE__ */ new Set(["-n", "-c"])).args;
|
|
343
|
+
break;
|
|
344
|
+
case "sed":
|
|
345
|
+
if (commandTokens.some(
|
|
346
|
+
(argument) => argument.startsWith("-i") || argument === "--in-place" || argument.startsWith("--in-place=")
|
|
347
|
+
)) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
args = readCommandFileArgs(commandTokens, /* @__PURE__ */ new Set(["-e", "-f"]));
|
|
351
|
+
break;
|
|
352
|
+
case "awk":
|
|
353
|
+
args = readCommandFileArgs(
|
|
354
|
+
commandTokens,
|
|
355
|
+
/* @__PURE__ */ new Set(["-f", "-v"]),
|
|
356
|
+
/* @__PURE__ */ new Set(["-f"])
|
|
357
|
+
);
|
|
358
|
+
break;
|
|
359
|
+
case "grep":
|
|
360
|
+
case "rg":
|
|
361
|
+
args = readCommandFileArgs(
|
|
362
|
+
commandTokens,
|
|
363
|
+
/* @__PURE__ */ new Set(["-A", "-B", "-C", "-e", "-f", "-g", "-m", "-t"]),
|
|
364
|
+
/* @__PURE__ */ new Set(["-e", "-f"])
|
|
365
|
+
);
|
|
366
|
+
break;
|
|
367
|
+
default:
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
return args.find(isPlausibleReadPath) ?? null;
|
|
371
|
+
}
|
|
372
|
+
function extractReadFilePathFromCommand(command) {
|
|
373
|
+
const tokens = tokenizeShellLikeCommand(command);
|
|
374
|
+
if (tokens.length === 0) return null;
|
|
375
|
+
for (let index = 0; index < tokens.length - 1; index += 1) {
|
|
376
|
+
if (tokens[index] === "<" && isPlausibleReadPath(tokens[index + 1])) {
|
|
377
|
+
return tokens[index + 1];
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const segment of splitCommandSegments(tokens)) {
|
|
381
|
+
const filePath = filePathFromSegment(segment);
|
|
382
|
+
if (filePath) return filePath;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
156
386
|
|
|
157
387
|
// ../../packages/agent-targets/src/hooks/normalize.ts
|
|
158
388
|
var MAX_EXTRACTED_TEXT_BYTES = 64 * 1024;
|
|
@@ -309,12 +539,12 @@ function normalizedEvent(target, payload, context) {
|
|
|
309
539
|
"workingDirectory",
|
|
310
540
|
"workspaceRoot"
|
|
311
541
|
]) ?? context.cwd;
|
|
312
|
-
const filePath = firstString(payload, ["file_path", "filePath", "path"]) ?? (toolInput ? firstString(toolInput, ["file_path", "filePath", "path"]) : void 0);
|
|
313
542
|
const command = boundedExtractedText(
|
|
314
543
|
firstText(payload, ["command"]) ?? (toolInput ? firstText(toolInput, ["command", "cmd"]) : void 0),
|
|
315
544
|
"command"
|
|
316
545
|
);
|
|
317
546
|
const toolName = firstString(payload, ["tool_name", "toolName", "name"]);
|
|
547
|
+
const filePath = firstString(payload, ["file_path", "filePath", "path"]) ?? (toolInput ? firstString(toolInput, ["file_path", "filePath", "path"]) : void 0) ?? (command && toolName && SHELL_TOOL_NAMES.has(toolName.toLowerCase()) ? extractReadFilePathFromCommand(command) ?? void 0 : void 0);
|
|
318
548
|
const userPrompt = boundedExtractedText(
|
|
319
549
|
firstText(payload, ["prompt", "user_prompt", "userPrompt"]) ?? (target === "gemini" ? geminiUserPrompt(payload) : void 0),
|
|
320
550
|
"user_prompt"
|
|
@@ -371,7 +601,8 @@ var HOOK_NORMALIZATION_CAPABILITIES = Object.freeze([
|
|
|
371
601
|
capability("claude", CLAUDE_HOOK_EVENTS),
|
|
372
602
|
capability("codex", CODEX_HOOK_EVENTS),
|
|
373
603
|
capability("gemini", GEMINI_HOOK_EVENTS),
|
|
374
|
-
capability("grok", GROK_HOOK_EVENTS)
|
|
604
|
+
capability("grok", GROK_HOOK_EVENTS),
|
|
605
|
+
capability("pi", PI_HOOK_EVENTS)
|
|
375
606
|
]);
|
|
376
607
|
function parseHookCaptureRequest(value) {
|
|
377
608
|
if (!isRecord(value) || !isHookTarget(value.target)) return null;
|
|
@@ -434,7 +665,7 @@ function normalizeObserverHookCapture(request, options = {}) {
|
|
|
434
665
|
// ../../packages/panopticon/src/observer/database.ts
|
|
435
666
|
import crypto2 from "crypto";
|
|
436
667
|
import fs2 from "fs";
|
|
437
|
-
import
|
|
668
|
+
import path3 from "path";
|
|
438
669
|
var ObserverHookSpoolEntryTooLargeError = class extends Error {
|
|
439
670
|
constructor(logicalBytes, maxBytes) {
|
|
440
671
|
super(
|
|
@@ -789,7 +1020,7 @@ function openObserverDatabase(databaseFile, options = {}) {
|
|
|
789
1020
|
if (!sqlite?.DatabaseSync) {
|
|
790
1021
|
throw new Error("FML observer requires the Node.js built-in SQLite module");
|
|
791
1022
|
}
|
|
792
|
-
const databaseDirectory =
|
|
1023
|
+
const databaseDirectory = path3.dirname(databaseFile);
|
|
793
1024
|
fs2.mkdirSync(databaseDirectory, { recursive: true, mode: 448 });
|
|
794
1025
|
if (options.restrictDatabaseDirectory !== false) {
|
|
795
1026
|
enforceOwnerOnlyMode(databaseDirectory, 448);
|
|
@@ -3068,7 +3299,7 @@ function createObserverHttpServer(opts) {
|
|
|
3068
3299
|
// src/hooks/inbox.ts
|
|
3069
3300
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3070
3301
|
import fs3 from "fs";
|
|
3071
|
-
import
|
|
3302
|
+
import path4 from "path";
|
|
3072
3303
|
|
|
3073
3304
|
// src/hooks/inbox-config.ts
|
|
3074
3305
|
var BYTE_SUFFIXES = {
|
|
@@ -3210,7 +3441,7 @@ function entryFileName(id) {
|
|
|
3210
3441
|
return `${id}.json`;
|
|
3211
3442
|
}
|
|
3212
3443
|
function entryPath(inboxDir, id) {
|
|
3213
|
-
return
|
|
3444
|
+
return path4.join(inboxDir, entryFileName(id));
|
|
3214
3445
|
}
|
|
3215
3446
|
function enforceOwnerOnlyDirectory(inboxDir) {
|
|
3216
3447
|
fs3.mkdirSync(inboxDir, { recursive: true, mode: 448 });
|
|
@@ -3320,7 +3551,7 @@ function parseCapacityState(value) {
|
|
|
3320
3551
|
};
|
|
3321
3552
|
}
|
|
3322
3553
|
function capacityStatePath(inboxDir) {
|
|
3323
|
-
return
|
|
3554
|
+
return path4.join(inboxDir, CAPACITY_STATE_FILE);
|
|
3324
3555
|
}
|
|
3325
3556
|
function readCapacityStateFile(inboxDir) {
|
|
3326
3557
|
try {
|
|
@@ -3348,7 +3579,7 @@ function writeCapacityState(inboxDir, state, capacityLock) {
|
|
|
3348
3579
|
enforceOwnerOnlyDirectory(inboxDir);
|
|
3349
3580
|
capacityLock.assertOwned();
|
|
3350
3581
|
const destination = capacityStatePath(inboxDir);
|
|
3351
|
-
const temporary =
|
|
3582
|
+
const temporary = path4.join(
|
|
3352
3583
|
inboxDir,
|
|
3353
3584
|
`${CAPACITY_STATE_FILE}.${process.pid}.${randomUUID2()}.tmp`
|
|
3354
3585
|
);
|
|
@@ -3457,7 +3688,7 @@ function recoverStaleCapacityLock(lockPath, nowMs) {
|
|
|
3457
3688
|
return;
|
|
3458
3689
|
}
|
|
3459
3690
|
fs3.rmSync(abandoned, { force: true });
|
|
3460
|
-
fsyncDirectory(
|
|
3691
|
+
fsyncDirectory(path4.dirname(lockPath));
|
|
3461
3692
|
} catch (error) {
|
|
3462
3693
|
if (!capacityLockRaceError(error)) throw error;
|
|
3463
3694
|
}
|
|
@@ -3468,7 +3699,7 @@ function sleepSync(milliseconds) {
|
|
|
3468
3699
|
}
|
|
3469
3700
|
function acquireCapacityLock(inboxDir, waitMs = CAPACITY_LOCK_WAIT_MS) {
|
|
3470
3701
|
enforceOwnerOnlyDirectory(inboxDir);
|
|
3471
|
-
const lockPath =
|
|
3702
|
+
const lockPath = path4.join(inboxDir, CAPACITY_LOCK_FILE);
|
|
3472
3703
|
const deadlineMs = Date.now() + Math.max(0, waitMs);
|
|
3473
3704
|
while (true) {
|
|
3474
3705
|
let descriptor;
|
|
@@ -3541,19 +3772,19 @@ function capacityMutationEvidence(inboxDir, pending, includeTemporary) {
|
|
|
3541
3772
|
const prefix = `.${id}.`;
|
|
3542
3773
|
const candidates = [];
|
|
3543
3774
|
if (includeTemporary && pending.temporaryFileName) {
|
|
3544
|
-
candidates.push(
|
|
3775
|
+
candidates.push(path4.join(inboxDir, pending.temporaryFileName));
|
|
3545
3776
|
} else if (includeTemporary) {
|
|
3546
3777
|
try {
|
|
3547
3778
|
for (const name of fs3.readdirSync(inboxDir)) {
|
|
3548
3779
|
if (name.startsWith(prefix) && name.endsWith(".tmp")) {
|
|
3549
|
-
candidates.push(
|
|
3780
|
+
candidates.push(path4.join(inboxDir, name));
|
|
3550
3781
|
}
|
|
3551
3782
|
}
|
|
3552
3783
|
} catch (error) {
|
|
3553
3784
|
return error.code === "ENOENT" ? { kind: "missing" } : { kind: "unavailable" };
|
|
3554
3785
|
}
|
|
3555
3786
|
}
|
|
3556
|
-
candidates.push(
|
|
3787
|
+
candidates.push(path4.join(inboxDir, fileName));
|
|
3557
3788
|
let presentBytes;
|
|
3558
3789
|
for (const candidate of candidates) {
|
|
3559
3790
|
try {
|
|
@@ -3665,7 +3896,7 @@ function removeCapacityTrackedEntry(filePath, inboxDir, expectedBytes, missingIs
|
|
|
3665
3896
|
}
|
|
3666
3897
|
if (bytes === void 0) return false;
|
|
3667
3898
|
const pendingRemoval = {
|
|
3668
|
-
fileName:
|
|
3899
|
+
fileName: path4.basename(filePath),
|
|
3669
3900
|
bytes,
|
|
3670
3901
|
startedAtMs: Date.now()
|
|
3671
3902
|
};
|
|
@@ -3746,7 +3977,7 @@ function readRetirementState(inboxDir) {
|
|
|
3746
3977
|
try {
|
|
3747
3978
|
return parseRetirementState(
|
|
3748
3979
|
JSON.parse(
|
|
3749
|
-
fs3.readFileSync(
|
|
3980
|
+
fs3.readFileSync(path4.join(inboxDir, RETIREMENT_STATE_FILE), "utf8")
|
|
3750
3981
|
)
|
|
3751
3982
|
) ?? defaultRetirementState();
|
|
3752
3983
|
} catch {
|
|
@@ -3755,8 +3986,8 @@ function readRetirementState(inboxDir) {
|
|
|
3755
3986
|
}
|
|
3756
3987
|
function writeRetirementState(inboxDir, state) {
|
|
3757
3988
|
enforceOwnerOnlyDirectory(inboxDir);
|
|
3758
|
-
const destination =
|
|
3759
|
-
const temporary =
|
|
3989
|
+
const destination = path4.join(inboxDir, RETIREMENT_STATE_FILE);
|
|
3990
|
+
const temporary = path4.join(
|
|
3760
3991
|
inboxDir,
|
|
3761
3992
|
`${RETIREMENT_STATE_FILE}.${process.pid}.${randomUUID2()}.tmp`
|
|
3762
3993
|
);
|
|
@@ -3783,7 +4014,7 @@ function completePendingRetirement(inboxDir, state) {
|
|
|
3783
4014
|
const pending = state.pending;
|
|
3784
4015
|
if (!pending) return state;
|
|
3785
4016
|
if (!removeCapacityTrackedEntry(
|
|
3786
|
-
|
|
4017
|
+
path4.join(inboxDir, pending.fileName),
|
|
3787
4018
|
inboxDir,
|
|
3788
4019
|
pending.bytes,
|
|
3789
4020
|
true
|
|
@@ -3819,7 +4050,7 @@ function retireEntry(filePath, reason, inboxDir) {
|
|
|
3819
4050
|
const pending = {
|
|
3820
4051
|
...current,
|
|
3821
4052
|
pending: {
|
|
3822
|
-
fileName:
|
|
4053
|
+
fileName: path4.basename(filePath),
|
|
3823
4054
|
bytes,
|
|
3824
4055
|
reason,
|
|
3825
4056
|
retiredAtMs: Date.now()
|
|
@@ -3870,7 +4101,7 @@ function persistHookInboxEntry(entry, inboxDir = FML_HOOK_INBOX_DIR, limits = ho
|
|
|
3870
4101
|
throw new Error("FML hook inbox admission is busy");
|
|
3871
4102
|
}
|
|
3872
4103
|
const destination = entryPath(inboxDir, entry.id);
|
|
3873
|
-
const temporary =
|
|
4104
|
+
const temporary = path4.join(
|
|
3874
4105
|
inboxDir,
|
|
3875
4106
|
`.${entry.id}.${process.pid}.${randomUUID2()}.tmp`
|
|
3876
4107
|
);
|
|
@@ -3900,8 +4131,8 @@ function persistHookInboxEntry(entry, inboxDir = FML_HOOK_INBOX_DIR, limits = ho
|
|
|
3900
4131
|
}
|
|
3901
4132
|
try {
|
|
3902
4133
|
const pendingPersist = {
|
|
3903
|
-
fileName:
|
|
3904
|
-
temporaryFileName:
|
|
4134
|
+
fileName: path4.basename(destination),
|
|
4135
|
+
temporaryFileName: path4.basename(temporary),
|
|
3905
4136
|
bytes: serializedBytes,
|
|
3906
4137
|
startedAtMs: Date.now()
|
|
3907
4138
|
};
|
|
@@ -4059,7 +4290,7 @@ async function scanDrainEntryNames(inboxDir, maximumEntries, skipThrough, exclud
|
|
|
4059
4290
|
const abandonedLockMatch = ABANDONED_CAPACITY_LOCK_PATTERN.exec(name);
|
|
4060
4291
|
if (INBOX_STATE_TEMP_PATTERN.test(name) || abandonedLockMatch !== null) {
|
|
4061
4292
|
try {
|
|
4062
|
-
const orphanPath =
|
|
4293
|
+
const orphanPath = path4.join(inboxDir, name);
|
|
4063
4294
|
const stat = await fs3.promises.lstat(orphanPath);
|
|
4064
4295
|
const abandonedAtMs = abandonedLockMatch ? Number(abandonedLockMatch[1]) : void 0;
|
|
4065
4296
|
const isStale = abandonedLockMatch ? validTimestamp(abandonedAtMs) && abandonedAtMs <= cutoffMs : stat.mtimeMs <= cutoffMs;
|
|
@@ -4082,8 +4313,8 @@ async function scanDrainEntryNames(inboxDir, maximumEntries, skipThrough, exclud
|
|
|
4082
4313
|
const id = match?.[1];
|
|
4083
4314
|
if (!id) continue;
|
|
4084
4315
|
const destination = entryPath(inboxDir, id);
|
|
4085
|
-
const destinationName =
|
|
4086
|
-
const temporary =
|
|
4316
|
+
const destinationName = path4.basename(destination);
|
|
4317
|
+
const temporary = path4.join(inboxDir, name);
|
|
4087
4318
|
try {
|
|
4088
4319
|
const stat = await fs3.promises.lstat(temporary);
|
|
4089
4320
|
if (!stat.isFile()) continue;
|
|
@@ -4136,7 +4367,7 @@ async function scanDrainEntryNames(inboxDir, maximumEntries, skipThrough, exclud
|
|
|
4136
4367
|
return { names, failed, resetCursor, blockCursorAdvance };
|
|
4137
4368
|
}
|
|
4138
4369
|
function drainCursor(inboxDir) {
|
|
4139
|
-
const key =
|
|
4370
|
+
const key = path4.resolve(inboxDir);
|
|
4140
4371
|
const existing = HOOK_INBOX_DRAIN_CURSORS.get(key);
|
|
4141
4372
|
if (existing) {
|
|
4142
4373
|
HOOK_INBOX_DRAIN_CURSORS.delete(key);
|
|
@@ -4173,7 +4404,7 @@ async function pendingInboxSizeAsync(inboxDir) {
|
|
|
4173
4404
|
for (let dirent = await directory.read(); dirent !== null; dirent = await directory.read()) {
|
|
4174
4405
|
if (!isPendingEntryName(dirent.name)) continue;
|
|
4175
4406
|
try {
|
|
4176
|
-
const stat = await fs3.promises.lstat(
|
|
4407
|
+
const stat = await fs3.promises.lstat(path4.join(inboxDir, dirent.name));
|
|
4177
4408
|
if (stat.isFile()) {
|
|
4178
4409
|
pendingEntries += 1;
|
|
4179
4410
|
pendingBytes += stat.size;
|
|
@@ -4197,7 +4428,7 @@ function pendingInboxSize(inboxDir, includeTemporary = false) {
|
|
|
4197
4428
|
continue;
|
|
4198
4429
|
}
|
|
4199
4430
|
try {
|
|
4200
|
-
const stat = fs3.lstatSync(
|
|
4431
|
+
const stat = fs3.lstatSync(path4.join(inboxDir, dirent.name));
|
|
4201
4432
|
if (stat.isFile()) {
|
|
4202
4433
|
pendingEntries += 1;
|
|
4203
4434
|
pendingBytes += stat.size;
|
|
@@ -4352,7 +4583,7 @@ async function drainHookInbox(capture, inboxDir = FML_HOOK_INBOX_DIR) {
|
|
|
4352
4583
|
safeThrough = name;
|
|
4353
4584
|
};
|
|
4354
4585
|
for (const name of names) {
|
|
4355
|
-
const filePath =
|
|
4586
|
+
const filePath = path4.join(inboxDir, name);
|
|
4356
4587
|
const readResult = readEntryResult(filePath);
|
|
4357
4588
|
if (readResult.kind === "missing") {
|
|
4358
4589
|
markResolved(name, false);
|
|
@@ -4418,10 +4649,10 @@ async function drainHookInbox(capture, inboxDir = FML_HOOK_INBOX_DIR) {
|
|
|
4418
4649
|
export {
|
|
4419
4650
|
readObserverAuthToken,
|
|
4420
4651
|
getOrCreateObserverAuthToken,
|
|
4421
|
-
HOOK_TARGETS,
|
|
4422
4652
|
CODEX_HOOK_EVENTS,
|
|
4423
4653
|
GEMINI_HOOK_EVENTS,
|
|
4424
4654
|
GROK_HOOK_EVENTS,
|
|
4655
|
+
SHELL_TOOL_NAMES,
|
|
4425
4656
|
parseHookCaptureRequest,
|
|
4426
4657
|
normalizeObserverHookCapture,
|
|
4427
4658
|
ObserverHookSpoolEntryTooLargeError,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveRepoFromCwd
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-7V7G7NVF.js";
|
|
4
4
|
import {
|
|
5
5
|
CONVEX_URL,
|
|
6
6
|
getActiveEnv,
|
|
7
7
|
getSelectedOrg,
|
|
8
8
|
getSiteUrlForConvexUrl,
|
|
9
9
|
getValidToken
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-A6HAEVJX.js";
|
|
11
11
|
|
|
12
12
|
// src/fml-client.ts
|
|
13
13
|
var LOGIN_EXPIRED_MESSAGE = "Authentication expired. Run `fml login` to sign in again, then restart Claude Code.";
|
|
@@ -79,7 +79,7 @@ function createFmlClient(token, options = {}) {
|
|
|
79
79
|
const repo = resolveRepoFromCwd(process.cwd());
|
|
80
80
|
if (repo) body.repo = repo.repo;
|
|
81
81
|
if (isServiceToken) {
|
|
82
|
-
const { readTokens, SERVICE_TOKEN_LOGIN_USER_ID } = await import("./token-store-
|
|
82
|
+
const { readTokens, SERVICE_TOKEN_LOGIN_USER_ID } = await import("./token-store-BT5THIAJ.js");
|
|
83
83
|
const stored = readTokens(options.envName);
|
|
84
84
|
const storedUserExternalId = stored?.tokenType === "service" && stored.user.id !== SERVICE_TOKEN_LOGIN_USER_ID ? stored.user.id : void 0;
|
|
85
85
|
const userExternalId = process.env.FML_USER_EXTERNAL_ID ?? storedUserExternalId;
|