@fml-inc/fml 0.8.2 → 0.8.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/dist/cli.js +4 -4
- package/dist/hook-handler.js +76 -45
- package/dist/observer-server.js +1 -1
- package/dist/pi/extension.js +2774 -98
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -124,7 +124,7 @@ var POLL_INTERVAL_MS = 50;
|
|
|
124
124
|
var LOG_ROTATE_BYTES = 10 * 1024 * 1024;
|
|
125
125
|
var LOG_ROTATE_FILES = 3;
|
|
126
126
|
function expectedObserverVersion() {
|
|
127
|
-
return true ? "0.8.
|
|
127
|
+
return true ? "0.8.3+499a546c" : "dev";
|
|
128
128
|
}
|
|
129
129
|
function delay(ms) {
|
|
130
130
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -7898,7 +7898,7 @@ var PACKAGE_NAME = "@fml-inc/fml";
|
|
|
7898
7898
|
var UPDATE_TIMEOUT_MS = 12e4;
|
|
7899
7899
|
var SETUP_TIMEOUT_MS = 3e5;
|
|
7900
7900
|
function getCurrentVersion() {
|
|
7901
|
-
return true ? "0.8.
|
|
7901
|
+
return true ? "0.8.3+499a546c" : "unknown";
|
|
7902
7902
|
}
|
|
7903
7903
|
function parseVersion(version) {
|
|
7904
7904
|
const match = SEMVER_PATTERN.exec(version);
|
|
@@ -9037,7 +9037,7 @@ async function handleConfigDetail(opts) {
|
|
|
9037
9037
|
// src/commands/tools.ts
|
|
9038
9038
|
import { readFileSync } from "fs";
|
|
9039
9039
|
import path29 from "path";
|
|
9040
|
-
var pluginVersion = true ? "0.8.
|
|
9040
|
+
var pluginVersion = true ? "0.8.3+499a546c" : void 0;
|
|
9041
9041
|
async function getApi() {
|
|
9042
9042
|
const api = await getAuthenticatedClient();
|
|
9043
9043
|
if (!api) {
|
|
@@ -9194,7 +9194,7 @@ if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.
|
|
|
9194
9194
|
printBanner();
|
|
9195
9195
|
}
|
|
9196
9196
|
var program = new Command().name("fml").description("FML CLI and agent tools").version(
|
|
9197
|
-
true ? "0.8.
|
|
9197
|
+
true ? "0.8.3+499a546c" : "dev"
|
|
9198
9198
|
);
|
|
9199
9199
|
function formatCommandInventory(cmd, prefix = "") {
|
|
9200
9200
|
const name = prefix ? `${prefix} ${cmd.name()}` : cmd.name();
|
package/dist/hook-handler.js
CHANGED
|
@@ -56,6 +56,14 @@ var EDIT_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
|
56
56
|
"write_file"
|
|
57
57
|
]);
|
|
58
58
|
var READ_TOOL_NAMES = /* @__PURE__ */ new Set(["read", "read_file"]);
|
|
59
|
+
function hookContextFileOperation(toolName) {
|
|
60
|
+
const normalizedToolName = toolName.toLowerCase();
|
|
61
|
+
if (EDIT_TOOL_NAMES.has(normalizedToolName)) return "edit";
|
|
62
|
+
if (READ_TOOL_NAMES.has(normalizedToolName) || SHELL_TOOL_NAMES.has(normalizedToolName)) {
|
|
63
|
+
return "read";
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
59
67
|
function sameDestination(left, right) {
|
|
60
68
|
return left.environment === right.environment && left.targetUrl === right.targetUrl && left.destinationIdentity === right.destinationIdentity;
|
|
61
69
|
}
|
|
@@ -130,7 +138,7 @@ function pruneOldMarkers(directory, nowMs) {
|
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
}
|
|
133
|
-
function
|
|
141
|
+
function canonicalHookContextFilePath(filePath, cwd, repositoryRoot) {
|
|
134
142
|
const windows = [filePath, cwd, repositoryRoot].some(
|
|
135
143
|
(value) => value !== void 0 && value !== null && /^[A-Za-z]:[\\/]/.test(value)
|
|
136
144
|
);
|
|
@@ -159,7 +167,9 @@ function canonicalFilePath(filePath, cwd, repositoryRoot) {
|
|
|
159
167
|
return resolved.replace(/\\/g, "/");
|
|
160
168
|
}
|
|
161
169
|
function buildServerHookContextRequest(target2, event, stateDir = FML_HOOK_CONTEXT_STATE_DIR, repositoryRoot = null) {
|
|
162
|
-
if (target2 !== "claude" && target2 !== "codex")
|
|
170
|
+
if (target2 !== "claude" && target2 !== "codex" && target2 !== "pi") {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
163
173
|
if (!event.repository) return null;
|
|
164
174
|
const markerSessionId = `${target2}:${event.sessionId}`;
|
|
165
175
|
const base = {
|
|
@@ -185,11 +195,14 @@ function buildServerHookContextRequest(target2, event, stateDir = FML_HOOK_CONTE
|
|
|
185
195
|
if (event.eventType !== "PreToolUse" || !event.toolName) {
|
|
186
196
|
return null;
|
|
187
197
|
}
|
|
188
|
-
const
|
|
189
|
-
const fileOperation = EDIT_TOOL_NAMES.has(toolName) ? "edit" : READ_TOOL_NAMES.has(toolName) || SHELL_TOOL_NAMES.has(toolName) ? "read" : null;
|
|
198
|
+
const fileOperation = hookContextFileOperation(event.toolName);
|
|
190
199
|
if (!fileOperation) return null;
|
|
191
200
|
if (!event.filePath) return null;
|
|
192
|
-
const filePath =
|
|
201
|
+
const filePath = canonicalHookContextFilePath(
|
|
202
|
+
event.filePath,
|
|
203
|
+
event.cwd,
|
|
204
|
+
repositoryRoot
|
|
205
|
+
);
|
|
193
206
|
if (pathContextWasEmitted(
|
|
194
207
|
markerSessionId,
|
|
195
208
|
`${event.repository}:${filePath}`,
|
|
@@ -432,39 +445,19 @@ function renderHookContextReceipt(receiptItems, nowMs) {
|
|
|
432
445
|
function appendModelContextGuidance(additionalContext) {
|
|
433
446
|
return `${additionalContext}${CONTEXT_GUIDANCE_SEPARATOR}${MODEL_CONTEXT_GUIDANCE}`;
|
|
434
447
|
}
|
|
435
|
-
function
|
|
436
|
-
const receipt = renderHookContextReceipt(receiptItems, request.nowMs);
|
|
437
|
-
return {
|
|
438
|
-
...receipt ? { systemMessage: receipt } : {},
|
|
439
|
-
hookSpecificOutput: {
|
|
440
|
-
hookEventName: request.eventType,
|
|
441
|
-
additionalContext: appendModelContextGuidance(additionalContext)
|
|
442
|
-
}
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
async function requestServerHookContext(target2, event, destination, opts = {}) {
|
|
448
|
+
async function requestServerHookContextInjection(request, destination, opts = {}) {
|
|
446
449
|
const deadlineSignal = AbortSignal.timeout(
|
|
447
450
|
opts.timeoutMs ?? HOOK_CONTEXT_TIMEOUT_MS
|
|
448
451
|
);
|
|
449
|
-
const stateDir = opts.stateDir ?? FML_HOOK_CONTEXT_STATE_DIR;
|
|
450
|
-
pruneOldMarkers(stateDir, Date.now());
|
|
451
|
-
const repositoryRoot = event.eventType === "PreToolUse" && event.cwd ? (opts.resolveRepositoryRoot ?? ((cwd) => resolveRepositoryRootFromCwd(cwd, 250)))(event.cwd) : null;
|
|
452
|
-
const request = buildServerHookContextRequest(
|
|
453
|
-
target2,
|
|
454
|
-
event,
|
|
455
|
-
stateDir,
|
|
456
|
-
repositoryRoot
|
|
457
|
-
);
|
|
458
|
-
if (!request) return {};
|
|
459
452
|
try {
|
|
460
453
|
const token = await getValidToken({
|
|
461
454
|
env: destination.environment,
|
|
462
455
|
signal: deadlineSignal
|
|
463
456
|
});
|
|
464
|
-
if (!token) return
|
|
457
|
+
if (!token) return null;
|
|
465
458
|
const currentDestination = getActiveHookDestinationBinding();
|
|
466
459
|
if (!currentDestination || !sameDestination(destination, currentDestination)) {
|
|
467
|
-
return
|
|
460
|
+
return null;
|
|
468
461
|
}
|
|
469
462
|
const response = await (opts.fetch ?? fetch)(
|
|
470
463
|
`${destination.targetUrl.replace(/\/+$/, "")}${HOOK_CONTEXT_ENDPOINT}`,
|
|
@@ -478,34 +471,72 @@ async function requestServerHookContext(target2, event, destination, opts = {})
|
|
|
478
471
|
signal: deadlineSignal
|
|
479
472
|
}
|
|
480
473
|
);
|
|
481
|
-
if (!response.ok) return
|
|
474
|
+
if (!response.ok) return null;
|
|
482
475
|
const body = await response.json();
|
|
483
|
-
if (!body || typeof body !== "object" || Array.isArray(body)) return
|
|
476
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
484
477
|
const responseBody = body;
|
|
485
478
|
const additionalContext = responseBody.additionalContext;
|
|
486
|
-
if (
|
|
479
|
+
if (additionalContext === null) {
|
|
480
|
+
return { additionalContext: null, receipt: null };
|
|
481
|
+
}
|
|
482
|
+
if (typeof additionalContext !== "string" || additionalContext.length === 0 || additionalContext.length > MAX_ADDITIONAL_CONTEXT_CHARS) {
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const receiptItems = receiptItemsFromResponse(responseBody.receiptItems);
|
|
486
|
+
return {
|
|
487
|
+
additionalContext: appendModelContextGuidance(additionalContext),
|
|
488
|
+
// A malformed receipt is presentation-only and must not suppress model
|
|
489
|
+
// context. The renderer also fails open independently.
|
|
490
|
+
receipt: receiptItems === null ? null : renderHookContextReceipt(receiptItems, request.nowMs)
|
|
491
|
+
};
|
|
492
|
+
} catch {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function hookOutput(request, injection) {
|
|
497
|
+
return {
|
|
498
|
+
...injection.receipt ? { systemMessage: injection.receipt } : {},
|
|
499
|
+
hookSpecificOutput: {
|
|
500
|
+
hookEventName: request.eventType,
|
|
501
|
+
additionalContext: injection.additionalContext
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
async function requestServerHookContext(target2, event, destination, opts = {}) {
|
|
506
|
+
const stateDir = opts.stateDir ?? FML_HOOK_CONTEXT_STATE_DIR;
|
|
507
|
+
pruneOldMarkers(stateDir, Date.now());
|
|
508
|
+
const repositoryRoot = event.eventType === "PreToolUse" && event.cwd ? (opts.resolveRepositoryRoot ?? ((cwd) => resolveRepositoryRootFromCwd(cwd, 250)))(event.cwd) : null;
|
|
509
|
+
const request = buildServerHookContextRequest(
|
|
510
|
+
target2,
|
|
511
|
+
event,
|
|
512
|
+
stateDir,
|
|
513
|
+
repositoryRoot
|
|
514
|
+
);
|
|
515
|
+
if (!request) return {};
|
|
516
|
+
const injection = await requestServerHookContextInjection(
|
|
517
|
+
request,
|
|
518
|
+
destination,
|
|
519
|
+
opts
|
|
520
|
+
);
|
|
521
|
+
if (injection === null) return {};
|
|
522
|
+
if (injection.additionalContext === null) {
|
|
523
|
+
if (request.eventType === "PreToolUse" && request.filePath) {
|
|
487
524
|
claimPathContext(
|
|
488
525
|
`${target2}:${request.sessionId}`,
|
|
489
526
|
`${request.repository}:${request.filePath}`,
|
|
490
527
|
stateDir
|
|
491
528
|
);
|
|
492
|
-
return {};
|
|
493
|
-
}
|
|
494
|
-
if (typeof additionalContext !== "string" || additionalContext.length === 0 || additionalContext.length > MAX_ADDITIONAL_CONTEXT_CHARS) {
|
|
495
|
-
return {};
|
|
496
529
|
}
|
|
497
|
-
const receiptItems = receiptItemsFromResponse(responseBody.receiptItems);
|
|
498
|
-
if (request.eventType === "PreToolUse" && request.filePath && !claimPathContext(
|
|
499
|
-
`${target2}:${request.sessionId}`,
|
|
500
|
-
`${request.repository}:${request.filePath}`,
|
|
501
|
-
stateDir
|
|
502
|
-
)) {
|
|
503
|
-
return {};
|
|
504
|
-
}
|
|
505
|
-
return hookOutput(request, additionalContext, receiptItems ?? []);
|
|
506
|
-
} catch {
|
|
507
530
|
return {};
|
|
508
531
|
}
|
|
532
|
+
if (request.eventType === "PreToolUse" && request.filePath && !claimPathContext(
|
|
533
|
+
`${target2}:${request.sessionId}`,
|
|
534
|
+
`${request.repository}:${request.filePath}`,
|
|
535
|
+
stateDir
|
|
536
|
+
)) {
|
|
537
|
+
return {};
|
|
538
|
+
}
|
|
539
|
+
return hookOutput(request, injection);
|
|
509
540
|
}
|
|
510
541
|
|
|
511
542
|
// src/hooks/handler.ts
|
package/dist/observer-server.js
CHANGED
|
@@ -1239,7 +1239,7 @@ var hookRejectionStatus = () => ({
|
|
|
1239
1239
|
...lastRejectedReason !== void 0 ? { lastRejectedReason } : {},
|
|
1240
1240
|
...lastRejectedTarget !== void 0 ? { lastRejectedTarget } : {}
|
|
1241
1241
|
});
|
|
1242
|
-
var packageVersion = true ? "0.8.
|
|
1242
|
+
var packageVersion = true ? "0.8.3+499a546c" : "dev";
|
|
1243
1243
|
var health = () => ({
|
|
1244
1244
|
status: "ok",
|
|
1245
1245
|
service: OBSERVER_SERVICE_NAME,
|