@fml-inc/fml 0.8.6 → 0.8.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4,30 +4,32 @@ import {
4
4
  getAuthenticatedClient
5
5
  } from "./chunk-LOJPIJZW.js";
6
6
  import {
7
- OBSERVER_HEARTBEAT_STALE_MS,
8
7
  execBinSync,
9
- getProcessIdentity,
10
8
  handleUpdate,
11
- inspectObserverLease,
12
- isPidAlive,
13
- readObserverStateStatus,
14
- removeObserverStateIfOwned,
15
9
  resolveBin
16
- } from "./chunk-E2MATWOU.js";
10
+ } from "./chunk-XFALHTDL.js";
17
11
  import {
12
+ openObserverLog,
18
13
  readJsonFile,
19
14
  setHookCaptureDisabled,
20
15
  writeJsonFile,
21
16
  writeTextFileAtomic
22
- } from "./chunk-2DQQXBWM.js";
17
+ } from "./chunk-LPSZCTTJ.js";
23
18
  import {
24
19
  CODEX_HOOK_EVENTS,
25
20
  GEMINI_HOOK_EVENTS,
26
21
  GROK_HOOK_EVENTS,
22
+ OBSERVER_HEARTBEAT_STALE_MS,
23
+ getProcessIdentity,
24
+ inspectObserverLease,
27
25
  isObserverHealth,
26
+ isPidAlive,
27
+ readHookInboxCapacityCounters,
28
28
  readHookInboxStatus,
29
- readObserverAuthToken
30
- } from "./chunk-5TOOBX7I.js";
29
+ readObserverAuthToken,
30
+ readObserverStateStatus,
31
+ removeObserverStateIfOwned
32
+ } from "./chunk-4YLSKFDU.js";
31
33
  import "./chunk-7V7G7NVF.js";
32
34
  import {
33
35
  claudeConfigDirectory,
@@ -35,14 +37,17 @@ import {
35
37
  createRawArtifactSync,
36
38
  geminiConfigDirectory,
37
39
  initializeSyncSettings,
38
- resolvePackageRoot,
39
40
  runRawSyncOnce
40
- } from "./chunk-V2XC7JTB.js";
41
+ } from "./chunk-5JJQWV4G.js";
42
+ import {
43
+ resolvePackageRoot
44
+ } from "./chunk-F57IFY3K.js";
41
45
  import {
42
46
  DEFAULT_PROD_URL,
43
47
  DEFAULT_TARGET_NAME,
44
48
  FML_DATA_DIR,
45
49
  FML_HOOK_CAPTURE_DISABLED_FILE,
50
+ FML_HOOK_INBOX_DIR,
46
51
  FML_LEGACY_OBSERVER_DB_FILE,
47
52
  FML_LOG_DIR,
48
53
  FML_OBSERVER_AUTH_TOKEN_FILE,
@@ -126,10 +131,8 @@ var START_TIMEOUT_MS = process.platform === "win32" ? 15e3 : 8e3;
126
131
  var STOP_TIMEOUT_MS = 5e3;
127
132
  var FORCE_STOP_TIMEOUT_MS = 2e3;
128
133
  var POLL_INTERVAL_MS = 50;
129
- var LOG_ROTATE_BYTES = 10 * 1024 * 1024;
130
- var LOG_ROTATE_FILES = 3;
131
134
  function expectedObserverVersion() {
132
- return true ? "0.8.6+f7c7191d" : "dev";
135
+ return true ? "0.8.7+af45d2af" : "dev";
133
136
  }
134
137
  function delay(ms) {
135
138
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -255,34 +258,6 @@ async function wakeObserverSync() {
255
258
  return false;
256
259
  }
257
260
  }
258
- function rotateObserverLog() {
259
- let size = 0;
260
- try {
261
- size = fs.statSync(FML_OBSERVER_LOG_FILE).size;
262
- } catch {
263
- return;
264
- }
265
- if (size < LOG_ROTATE_BYTES) return;
266
- try {
267
- fs.rmSync(`${FML_OBSERVER_LOG_FILE}.${LOG_ROTATE_FILES}`, { force: true });
268
- for (let index = LOG_ROTATE_FILES - 1; index >= 1; index -= 1) {
269
- const source = `${FML_OBSERVER_LOG_FILE}.${index}`;
270
- if (fs.existsSync(source)) {
271
- fs.renameSync(source, `${FML_OBSERVER_LOG_FILE}.${index + 1}`);
272
- }
273
- }
274
- fs.renameSync(FML_OBSERVER_LOG_FILE, `${FML_OBSERVER_LOG_FILE}.1`);
275
- } catch {
276
- }
277
- }
278
- function openObserverLog() {
279
- fs.mkdirSync(path.dirname(FML_OBSERVER_LOG_FILE), {
280
- recursive: true,
281
- mode: 448
282
- });
283
- rotateObserverLog();
284
- return fs.openSync(FML_OBSERVER_LOG_FILE, "a", 384);
285
- }
286
261
  async function waitForReady(timeoutMs = START_TIMEOUT_MS, abortError) {
287
262
  const startedAtMs = Date.now();
288
263
  while (Date.now() - startedAtMs < timeoutMs) {
@@ -1797,7 +1772,7 @@ function asRecord2(value) {
1797
1772
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1798
1773
  }
1799
1774
  function singlePath(value) {
1800
- const path29 = [];
1775
+ const path30 = [];
1801
1776
  let current = value;
1802
1777
  while (true) {
1803
1778
  if (Array.isArray(current) && current.length === 1) {
@@ -1805,13 +1780,13 @@ function singlePath(value) {
1805
1780
  continue;
1806
1781
  }
1807
1782
  const record = asRecord2(current);
1808
- if (!record) return path29;
1783
+ if (!record) return path30;
1809
1784
  const keys = Object.keys(record);
1810
- if (keys.length === 0) return path29;
1785
+ if (keys.length === 0) return path30;
1811
1786
  if (keys.length !== 1) throw new Error("ambiguous TOML statement path");
1812
1787
  const key = keys[0];
1813
1788
  if (key === void 0) throw new Error("missing TOML statement key");
1814
- path29.push(key);
1789
+ path30.push(key);
1815
1790
  current = record[key];
1816
1791
  }
1817
1792
  }
@@ -1893,8 +1868,8 @@ function scanStatements(source) {
1893
1868
  }
1894
1869
  return statements;
1895
1870
  }
1896
- function startsWithPath(path29, prefix) {
1897
- return prefix.every((part, index) => path29[index] === part);
1871
+ function startsWithPath(path30, prefix) {
1872
+ return prefix.every((part, index) => path30[index] === part);
1898
1873
  }
1899
1874
  function pathsEqual(left, right) {
1900
1875
  return left.length === right.length && left.every((part, index) => part === right[index]);
@@ -1920,9 +1895,9 @@ function appendToml(source, addition) {
1920
1895
  function hasOwn(record, key) {
1921
1896
  return Object.prototype.hasOwnProperty.call(record, key);
1922
1897
  }
1923
- function updatedValueAtPath(updated, path29) {
1898
+ function updatedValueAtPath(updated, path30) {
1924
1899
  let current = updated;
1925
- for (const part of path29) {
1900
+ for (const part of path30) {
1926
1901
  const record = asRecord2(current);
1927
1902
  if (!record || !hasOwn(record, part)) return { exists: false };
1928
1903
  current = record[part];
@@ -1936,22 +1911,22 @@ function normalizedPaths(updates) {
1936
1911
  throw new Error("TOML update path cannot be empty");
1937
1912
  }
1938
1913
  if (paths.some(
1939
- (path29) => pathsEqual(path29, update.path) || startsWithPath(update.path, path29)
1914
+ (path30) => pathsEqual(path30, update.path) || startsWithPath(update.path, path30)
1940
1915
  )) {
1941
1916
  continue;
1942
1917
  }
1943
1918
  for (let index = paths.length - 1; index >= 0; index -= 1) {
1944
- const path29 = paths[index];
1945
- if (path29 && startsWithPath(path29, update.path)) paths.splice(index, 1);
1919
+ const path30 = paths[index];
1920
+ if (path30 && startsWithPath(path30, update.path)) paths.splice(index, 1);
1946
1921
  }
1947
1922
  paths.push([...update.path]);
1948
1923
  }
1949
1924
  return paths;
1950
1925
  }
1951
- function valueDocument(path29, value) {
1926
+ function valueDocument(path30, value) {
1952
1927
  let nested = value;
1953
- for (let index = path29.length - 1; index >= 0; index -= 1) {
1954
- const part = path29[index];
1928
+ for (let index = path30.length - 1; index >= 0; index -= 1) {
1929
+ const part = path30[index];
1955
1930
  if (part === void 0) throw new Error("TOML update path is invalid");
1956
1931
  nested = { [part]: nested };
1957
1932
  }
@@ -1970,12 +1945,12 @@ function insertAfterStatement(source, statement, addition) {
1970
1945
  const separator = before.endsWith(eol) ? "" : eol;
1971
1946
  return `${before}${separator}${normalized}${eol}${source.slice(statement.end)}`;
1972
1947
  }
1973
- function insertUpdatedValue(source, path29, value) {
1974
- const key = path29.at(-1);
1948
+ function insertUpdatedValue(source, path30, value) {
1949
+ const key = path30.at(-1);
1975
1950
  if (key === void 0) throw new Error("TOML update path cannot be empty");
1976
1951
  const assignment = localAssignment(key, value);
1977
1952
  if (assignment !== null) {
1978
- const parent = path29.slice(0, -1);
1953
+ const parent = path30.slice(0, -1);
1979
1954
  const parentTables = scanStatements(source).filter(
1980
1955
  (statement) => statement.isTable && pathsEqual(statement.path, parent)
1981
1956
  );
@@ -1989,17 +1964,17 @@ function insertUpdatedValue(source, path29, value) {
1989
1964
  return insertAfterStatement(source, parentTable, assignment);
1990
1965
  }
1991
1966
  }
1992
- return appendToml(source, stringify(valueDocument(path29, value)));
1967
+ return appendToml(source, stringify(valueDocument(path30, value)));
1993
1968
  }
1994
1969
  function updateTomlPaths(source, updated, updates) {
1995
1970
  const paths = normalizedPaths(updates);
1996
1971
  if (paths.length === 0) return source;
1997
1972
  const stripped = removeStatements(source, scanStatements(source), paths);
1998
1973
  let result = stripped;
1999
- for (const path29 of paths) {
2000
- const next = updatedValueAtPath(updated, path29);
1974
+ for (const path30 of paths) {
1975
+ const next = updatedValueAtPath(updated, path30);
2001
1976
  if (!next.exists) continue;
2002
- result = insertUpdatedValue(result, path29, next.value);
1977
+ result = insertUpdatedValue(result, path30, next.value);
2003
1978
  }
2004
1979
  let reparsed;
2005
1980
  try {
@@ -2627,7 +2602,7 @@ function codexConfigUpdatePaths(original, updated) {
2627
2602
  }
2628
2603
  }
2629
2604
  }
2630
- return paths.map((path29) => ({ path: path29 }));
2605
+ return paths.map((path30) => ({ path: path30 }));
2631
2606
  }
2632
2607
  function updateCodexConfigToml(source, original, updated) {
2633
2608
  return updateTomlPaths(
@@ -3133,7 +3108,7 @@ function longestCommonArraySubsequence(original, updated) {
3133
3108
  }
3134
3109
  return matches;
3135
3110
  }
3136
- function arrayDiff(original, updated, path29) {
3111
+ function arrayDiff(original, updated, path30) {
3137
3112
  const updates = [];
3138
3113
  const matches = longestCommonArraySubsequence(original, updated);
3139
3114
  let originalStart = 0;
@@ -3151,19 +3126,19 @@ function arrayDiff(original, updated, path29) {
3151
3126
  ...jsoncUpdatesForObjectDiff(
3152
3127
  original[originalStart + offset],
3153
3128
  updated[updatedStart + offset],
3154
- [...path29, currentIndex + offset]
3129
+ [...path30, currentIndex + offset]
3155
3130
  )
3156
3131
  );
3157
3132
  }
3158
3133
  for (let offset = pairedCount; offset < originalCount; offset += 1) {
3159
3134
  updates.push({
3160
- path: [...path29, currentIndex + pairedCount],
3135
+ path: [...path30, currentIndex + pairedCount],
3161
3136
  value: void 0
3162
3137
  });
3163
3138
  }
3164
3139
  for (let offset = pairedCount; offset < updatedCount; offset += 1) {
3165
3140
  updates.push({
3166
- path: [...path29, currentIndex + offset],
3141
+ path: [...path30, currentIndex + offset],
3167
3142
  value: updated[updatedStart + offset],
3168
3143
  isArrayInsertion: true
3169
3144
  });
@@ -3176,21 +3151,21 @@ function arrayDiff(original, updated, path29) {
3176
3151
  }
3177
3152
  return updates;
3178
3153
  }
3179
- function jsoncUpdatesForObjectDiff(original, updated, path29 = []) {
3154
+ function jsoncUpdatesForObjectDiff(original, updated, path30 = []) {
3180
3155
  if (isDeepStrictEqual3(original, updated)) return [];
3181
3156
  if (Array.isArray(original) && Array.isArray(updated)) {
3182
- return arrayDiff(original, updated, path29);
3157
+ return arrayDiff(original, updated, path30);
3183
3158
  }
3184
3159
  const originalObject = asObject(original);
3185
3160
  const updatedObject = asObject(updated);
3186
- if (!originalObject || !updatedObject) return [{ path: path29, value: updated }];
3161
+ if (!originalObject || !updatedObject) return [{ path: path30, value: updated }];
3187
3162
  const keys = /* @__PURE__ */ new Set([
3188
3163
  ...Object.keys(originalObject),
3189
3164
  ...Object.keys(updatedObject)
3190
3165
  ]);
3191
3166
  return [...keys].flatMap(
3192
3167
  (key) => jsoncUpdatesForObjectDiff(originalObject[key], updatedObject[key], [
3193
- ...path29,
3168
+ ...path30,
3194
3169
  key
3195
3170
  ])
3196
3171
  );
@@ -5188,13 +5163,67 @@ async function handleInstall(opts = {}) {
5188
5163
  // src/commands/sync.ts
5189
5164
  import { statSync } from "fs";
5190
5165
 
5166
+ // src/hooks/inbox-refusals.ts
5167
+ import fs10 from "fs";
5168
+ import path22 from "path";
5169
+ var FML_HOOK_INBOX_LOSS_LEDGER_FILE = path22.join(
5170
+ FML_OBSERVER_DIR,
5171
+ "hook-inbox-loss.reported.json"
5172
+ );
5173
+ function readLedger(ledgerFile) {
5174
+ try {
5175
+ const parsed = JSON.parse(fs10.readFileSync(ledgerFile, "utf8"));
5176
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
5177
+ const record = parsed;
5178
+ const entries = record.reportedEntries;
5179
+ const bytes = record.reportedBytes;
5180
+ if (typeof entries === "number" && Number.isSafeInteger(entries) && entries >= 0 && typeof bytes === "number" && Number.isSafeInteger(bytes) && bytes >= 0) {
5181
+ return { reportedEntries: entries, reportedBytes: bytes };
5182
+ }
5183
+ }
5184
+ } catch {
5185
+ }
5186
+ return { reportedEntries: 0, reportedBytes: 0 };
5187
+ }
5188
+ function takeUnreportedInboxLoss(options = {}) {
5189
+ const inboxDir = options.inboxDir ?? FML_HOOK_INBOX_DIR;
5190
+ const ledgerFile = options.ledgerFile ?? FML_HOOK_INBOX_LOSS_LEDGER_FILE;
5191
+ try {
5192
+ const counters = readHookInboxCapacityCounters(inboxDir);
5193
+ if (!counters) return null;
5194
+ const ledger = readLedger(ledgerFile);
5195
+ if (counters.lostEntries === ledger.reportedEntries && counters.lostBytes === ledger.reportedBytes) {
5196
+ return null;
5197
+ }
5198
+ const entries = Math.max(0, counters.lostEntries - ledger.reportedEntries);
5199
+ const bytes = Math.max(0, counters.lostBytes - ledger.reportedBytes);
5200
+ fs10.mkdirSync(path22.dirname(ledgerFile), { recursive: true, mode: 448 });
5201
+ writeTextFileAtomic(
5202
+ ledgerFile,
5203
+ `${JSON.stringify({
5204
+ reportedEntries: counters.lostEntries,
5205
+ reportedBytes: counters.lostBytes
5206
+ })}
5207
+ `
5208
+ );
5209
+ if (entries === 0) return null;
5210
+ return {
5211
+ entries,
5212
+ bytes,
5213
+ ...counters.lastLostAtMs === void 0 ? {} : { lastAtMs: counters.lastLostAtMs }
5214
+ };
5215
+ } catch {
5216
+ return null;
5217
+ }
5218
+ }
5219
+
5191
5220
  // src/commands/otel-config.ts
5192
- import fs11 from "fs";
5221
+ import fs12 from "fs";
5193
5222
  import os6 from "os";
5194
- import path26 from "path";
5223
+ import path27 from "path";
5195
5224
 
5196
5225
  // ../../packages/agent-targets/src/install/otel/claude.ts
5197
- import path22 from "path";
5226
+ import path23 from "path";
5198
5227
 
5199
5228
  // ../../packages/agent-targets/src/install/otel/shared.ts
5200
5229
  import { isDeepStrictEqual as isDeepStrictEqual7 } from "util";
@@ -5331,9 +5360,9 @@ var MANAGED_MARKER = "FML_OTEL_MANAGED";
5331
5360
  var CLAUDE_OTEL_SOURCE_LIMITATION = "Claude Code may truncate inline raw API bodies and tool content at 60 KiB and redact extended-thinking content before export";
5332
5361
  function configPath(context) {
5333
5362
  if (context.configPathOverride) {
5334
- return path22.resolve(context.configPathOverride);
5363
+ return path23.resolve(context.configPathOverride);
5335
5364
  }
5336
- return path22.join(
5365
+ return path23.join(
5337
5366
  claudeConfigDirectory({ env: context.env, homeDir: context.homeDir }),
5338
5367
  "settings.json"
5339
5368
  );
@@ -5504,7 +5533,7 @@ var CLAUDE_OTEL_INSTALLER = Object.freeze({
5504
5533
 
5505
5534
  // ../../packages/agent-targets/src/install/otel/codex.ts
5506
5535
  import { isDeepStrictEqual as isDeepStrictEqual8 } from "util";
5507
- import path23 from "path";
5536
+ import path24 from "path";
5508
5537
  import { parse as parse5 } from "smol-toml";
5509
5538
  var TARGET14 = agentTarget("codex");
5510
5539
  var TARGET_ID6 = TARGET14.id;
@@ -5513,13 +5542,13 @@ var AUTHENTICATION2 = "bearer-header";
5513
5542
  var CODEX_OTEL_SOURCE_LIMITATION = "Codex OTel has no documented raw API body or full tool-content export switch; tool-result content is an output snippet";
5514
5543
  function configPath2(context) {
5515
5544
  if (context.configPathOverride) {
5516
- return path23.resolve(context.configPathOverride);
5545
+ return path24.resolve(context.configPathOverride);
5517
5546
  }
5518
5547
  const configuredHome = context.env.CODEX_HOME;
5519
- const codexHome = path23.resolve(
5520
- typeof configuredHome === "string" && configuredHome.trim().length > 0 ? configuredHome : path23.join(context.homeDir, ".codex")
5548
+ const codexHome = path24.resolve(
5549
+ typeof configuredHome === "string" && configuredHome.trim().length > 0 ? configuredHome : path24.join(context.homeDir, ".codex")
5521
5550
  );
5522
- return path23.join(codexHome, "config.toml");
5551
+ return path24.join(codexHome, "config.toml");
5523
5552
  }
5524
5553
  function readToml(snapshot) {
5525
5554
  const failure = snapshotError2(snapshot);
@@ -5739,7 +5768,7 @@ var CODEX_OTEL_INSTALLER = Object.freeze({
5739
5768
  });
5740
5769
 
5741
5770
  // ../../packages/agent-targets/src/install/otel/gemini.ts
5742
- import path24 from "path";
5771
+ import path25 from "path";
5743
5772
  var TARGET15 = agentTarget("gemini");
5744
5773
  var TARGET_ID7 = TARGET15.id;
5745
5774
  var DISPLAY_NAME7 = TARGET15.displayName;
@@ -5747,9 +5776,9 @@ var AUTHENTICATION3 = "path-token";
5747
5776
  var GEMINI_OTEL_SOURCE_LIMITATION = "Gemini CLI has no documented OTLP header setting; FML uses an observer-local tokenized loopback path";
5748
5777
  function configPath3(context) {
5749
5778
  if (context.configPathOverride) {
5750
- return path24.resolve(context.configPathOverride);
5779
+ return path25.resolve(context.configPathOverride);
5751
5780
  }
5752
- return path24.join(geminiConfigDirectory(context), "settings.json");
5781
+ return path25.join(geminiConfigDirectory(context), "settings.json");
5753
5782
  }
5754
5783
  function managedTelemetry(context) {
5755
5784
  const destination = resolveDestination(context.observer);
@@ -5903,8 +5932,8 @@ var OTEL_INSTALLER_CAPABILITIES = Object.freeze([
5903
5932
  ]);
5904
5933
 
5905
5934
  // src/commands/otel-config-state.ts
5906
- import fs10 from "fs";
5907
- import path25 from "path";
5935
+ import fs11 from "fs";
5936
+ import path26 from "path";
5908
5937
  var OTEL_CONFIG_STATE_SCHEMA_VERSION = 1;
5909
5938
  function emptyState2() {
5910
5939
  return Object.freeze({
@@ -5918,12 +5947,12 @@ function invalidState2(statePath, reason) {
5918
5947
  );
5919
5948
  }
5920
5949
  function enforceOwnerOnlyMode(statePath) {
5921
- if (process.platform !== "win32") fs10.chmodSync(statePath, 384);
5950
+ if (process.platform !== "win32") fs11.chmodSync(statePath, 384);
5922
5951
  }
5923
5952
  function readStateFile(statePath) {
5924
5953
  let stat;
5925
5954
  try {
5926
- stat = fs10.lstatSync(statePath);
5955
+ stat = fs11.lstatSync(statePath);
5927
5956
  } catch (error) {
5928
5957
  if (error.code === "ENOENT") {
5929
5958
  return { kind: "missing" };
@@ -5941,7 +5970,7 @@ function readStateFile(statePath) {
5941
5970
  }
5942
5971
  try {
5943
5972
  enforceOwnerOnlyMode(statePath);
5944
- return { kind: "file", contents: fs10.readFileSync(statePath, "utf8") };
5973
+ return { kind: "file", contents: fs11.readFileSync(statePath, "utf8") };
5945
5974
  } catch (error) {
5946
5975
  return {
5947
5976
  kind: "invalid",
@@ -5950,7 +5979,7 @@ function readStateFile(statePath) {
5950
5979
  }
5951
5980
  }
5952
5981
  function otelConfigStateEntryKey(targetId, configPath4) {
5953
- const resolved = path25.resolve(configPath4);
5982
+ const resolved = path26.resolve(configPath4);
5954
5983
  const pathKey = process.platform === "win32" ? resolved.toLowerCase() : resolved;
5955
5984
  return `${targetId}\0${pathKey}`;
5956
5985
  }
@@ -5986,7 +6015,7 @@ function parseState2(source, statePath) {
5986
6015
  if (typeof observerRecord.baseUrl !== "string" || observerRecord.baseUrl.length === 0 || typeof observerRecord.authToken !== "string" || observerRecord.authToken.length === 0) {
5987
6016
  throw invalidState2(statePath, "contains an invalid observer destination");
5988
6017
  }
5989
- const resolvedPath = path25.resolve(entry.path);
6018
+ const resolvedPath = path26.resolve(entry.path);
5990
6019
  const key = otelConfigStateEntryKey(entry.targetId, resolvedPath);
5991
6020
  if (keys.has(key)) {
5992
6021
  throw invalidState2(statePath, "contains duplicate entries");
@@ -6024,7 +6053,7 @@ function loadFmlOtelConfigState(statePath) {
6024
6053
  function serializeState2(entries) {
6025
6054
  const sorted = [...entries].map((entry) => ({
6026
6055
  targetId: entry.targetId,
6027
- path: path25.resolve(entry.path),
6056
+ path: path26.resolve(entry.path),
6028
6057
  observer: {
6029
6058
  baseUrl: entry.observer.baseUrl,
6030
6059
  authToken: entry.observer.authToken
@@ -6061,7 +6090,7 @@ function isKnownOtelTargetId(targetId) {
6061
6090
  }
6062
6091
 
6063
6092
  // src/commands/otel-config.ts
6064
- var DEFAULT_OTEL_CONFIG_STATE_PATH = path26.join(
6093
+ var DEFAULT_OTEL_CONFIG_STATE_PATH = path27.join(
6065
6094
  FML_DATA_DIR,
6066
6095
  "otel-config.json"
6067
6096
  );
@@ -6070,7 +6099,7 @@ function readLocalObserverOtelDestination(options) {
6070
6099
  if (!authToken) return null;
6071
6100
  try {
6072
6101
  const parsed = JSON.parse(
6073
- fs11.readFileSync(options.endpointFile, "utf8")
6102
+ fs12.readFileSync(options.endpointFile, "utf8")
6074
6103
  );
6075
6104
  const endpoint = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
6076
6105
  const host = endpoint?.host;
@@ -6087,7 +6116,7 @@ function readLocalObserverOtelDestination(options) {
6087
6116
  function readOtelFile(filePath) {
6088
6117
  let stat;
6089
6118
  try {
6090
- stat = fs11.lstatSync(filePath);
6119
+ stat = fs12.lstatSync(filePath);
6091
6120
  } catch (error) {
6092
6121
  if (error.code === "ENOENT") {
6093
6122
  return { path: filePath, kind: "missing" };
@@ -6116,7 +6145,7 @@ function readOtelFile(filePath) {
6116
6145
  return {
6117
6146
  path: filePath,
6118
6147
  kind: "file",
6119
- contents: fs11.readFileSync(filePath, "utf8")
6148
+ contents: fs12.readFileSync(filePath, "utf8")
6120
6149
  };
6121
6150
  } catch (error) {
6122
6151
  return {
@@ -6138,7 +6167,7 @@ function assertMutationPrecondition2(mutation) {
6138
6167
  throw new Error(`configuration changed while planning ${mutation.path}`);
6139
6168
  }
6140
6169
  function applyMutation2(mutation) {
6141
- if (mutation.after === null) fs11.rmSync(mutation.path);
6170
+ if (mutation.after === null) fs12.rmSync(mutation.path);
6142
6171
  else writeTextFileAtomic(mutation.path, mutation.after);
6143
6172
  }
6144
6173
  function enforceOwnerOnlyConfigPermissions(filePath) {
@@ -6151,7 +6180,7 @@ function enforceOwnerOnlyConfigPermissions(filePath) {
6151
6180
  );
6152
6181
  }
6153
6182
  try {
6154
- fs11.chmodSync(filePath, 384);
6183
+ fs12.chmodSync(filePath, 384);
6155
6184
  } catch (error) {
6156
6185
  const detail = error instanceof Error ? error.message : String(error);
6157
6186
  throw new Error(
@@ -6246,7 +6275,7 @@ function plannerContext3(observer, options, configPathOverride, configOwnershipR
6246
6275
  observer,
6247
6276
  homeDir: options.homeDir ?? os6.homedir(),
6248
6277
  env: options.env ?? process.env,
6249
- ...configPathOverride ? { configPathOverride: path26.resolve(configPathOverride) } : {},
6278
+ ...configPathOverride ? { configPathOverride: path27.resolve(configPathOverride) } : {},
6250
6279
  ...configOwnershipRecorded ? { configOwnershipRecorded: true } : {},
6251
6280
  readFile: readOtelFile
6252
6281
  };
@@ -6289,7 +6318,7 @@ function commitStateEntry(state, key, next) {
6289
6318
  function stateEntry(plan, observer) {
6290
6319
  return Object.freeze({
6291
6320
  targetId: plan.targetId,
6292
- path: path26.resolve(plan.configPath),
6321
+ path: path27.resolve(plan.configPath),
6293
6322
  observer: Object.freeze({
6294
6323
  baseUrl: observer.baseUrl,
6295
6324
  authToken: observer.authToken
@@ -6633,6 +6662,13 @@ async function handleSyncEnable(opts = {}) {
6633
6662
  if (observer.status === "already_running") {
6634
6663
  await wakeObserverSync();
6635
6664
  }
6665
+ const loss = takeUnreportedInboxLoss();
6666
+ if (loss && !opts.json) {
6667
+ const lastAt = loss.lastAtMs ? ` (last at ${new Date(loss.lastAtMs).toISOString()})` : "";
6668
+ console.warn(
6669
+ `Warning: ${loss.entries} hook event(s) (${formatBytes(loss.bytes)}) were lost: the hook inbox was full and no FML observer was running to take them${lastAt}. Keep the observer running, or raise FML_HOOK_INBOX_MAX_BYTES, to avoid further loss.`
6670
+ );
6671
+ }
6636
6672
  const port = observer.runtime.health?.port;
6637
6673
  const authToken = port ? readObserverAuthToken(FML_OBSERVER_AUTH_TOKEN_FILE) : null;
6638
6674
  if (port && authToken) {
@@ -6650,7 +6686,8 @@ async function handleSyncEnable(opts = {}) {
6650
6686
  ok: true,
6651
6687
  sync: settings,
6652
6688
  observer,
6653
- ...observer.retainedLegacyDatabase ? { retainedLegacyDatabase: observer.retainedLegacyDatabase } : {}
6689
+ ...observer.retainedLegacyDatabase ? { retainedLegacyDatabase: observer.retainedLegacyDatabase } : {},
6690
+ ...loss ? { hookInboxLoss: loss } : {}
6654
6691
  });
6655
6692
  } else {
6656
6693
  const pid = observer.runtime.health?.pid;
@@ -6859,8 +6896,8 @@ async function canOpenBrowser() {
6859
6896
  if (process.env.CODESPACES) return false;
6860
6897
  if (process.env.REMOTE_CONTAINERS) return false;
6861
6898
  try {
6862
- const fs13 = await import("fs");
6863
- if (fs13.existsSync("/.dockerenv")) return false;
6899
+ const fs14 = await import("fs");
6900
+ if (fs14.existsSync("/.dockerenv")) return false;
6864
6901
  } catch {
6865
6902
  }
6866
6903
  return true;
@@ -7523,9 +7560,9 @@ async function handleSetup(dependencies = defaultDependencies, options = {}) {
7523
7560
  }
7524
7561
 
7525
7562
  // src/commands/uninstall.ts
7526
- import fs12 from "fs";
7563
+ import fs13 from "fs";
7527
7564
  import os7 from "os";
7528
- import path27 from "path";
7565
+ import path28 from "path";
7529
7566
  import readline from "readline/promises";
7530
7567
  var PURGE_WARNING = "This deletes FML credentials for ALL environments, logs, sync state, and every pending best-effort OTLP envelope.";
7531
7568
  function tryDisableHookCapture() {
@@ -7541,19 +7578,19 @@ function tryDisableHookCapture() {
7541
7578
  }
7542
7579
  }
7543
7580
  function resolvePreservedPaths(preservedFile) {
7544
- const logicalPath = path27.resolve(preservedFile);
7581
+ const logicalPath = path28.resolve(preservedFile);
7545
7582
  try {
7546
- const canonicalPath = fs12.realpathSync(preservedFile);
7583
+ const canonicalPath = fs13.realpathSync(preservedFile);
7547
7584
  return pathsAreSame(canonicalPath, logicalPath) ? [logicalPath] : [logicalPath, canonicalPath];
7548
7585
  } catch {
7549
7586
  return [logicalPath];
7550
7587
  }
7551
7588
  }
7552
7589
  function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot = false) {
7553
- const resolvedDirectory = path27.resolve(directory);
7590
+ const resolvedDirectory = path28.resolve(directory);
7554
7591
  let directoryStats;
7555
7592
  try {
7556
- directoryStats = fs12.lstatSync(directory);
7593
+ directoryStats = fs13.lstatSync(directory);
7557
7594
  } catch (error) {
7558
7595
  if (error.code === "ENOENT") return true;
7559
7596
  throw error;
@@ -7564,14 +7601,14 @@ function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot
7564
7601
  (preservedPath) => pathContains(resolvedDirectory, preservedPath)
7565
7602
  );
7566
7603
  if (!containsLogicalMarker) {
7567
- fs12.rmSync(directory, { force: true });
7604
+ fs13.rmSync(directory, { force: true });
7568
7605
  return true;
7569
7606
  }
7570
7607
  return false;
7571
7608
  }
7572
7609
  const directoryPaths = [resolvedDirectory];
7573
7610
  try {
7574
- const canonicalDirectory = fs12.realpathSync(directory);
7611
+ const canonicalDirectory = fs13.realpathSync(directory);
7575
7612
  if (!pathsAreSame(canonicalDirectory, resolvedDirectory)) {
7576
7613
  directoryPaths.push(canonicalDirectory);
7577
7614
  }
@@ -7583,26 +7620,26 @@ function purgeDirectoryPreservingPaths(directory, preservedPaths, configuredRoot
7583
7620
  )
7584
7621
  );
7585
7622
  if (!containsPreservedPath) {
7586
- fs12.rmSync(directory, { recursive: true, force: true });
7623
+ fs13.rmSync(directory, { recursive: true, force: true });
7587
7624
  return true;
7588
7625
  }
7589
7626
  const isPreservedPath = directoryPaths.some(
7590
7627
  (directoryPath) => preservedPaths.some(
7591
- (preservedPath) => pathsAreSame(directoryPath, path27.resolve(preservedPath))
7628
+ (preservedPath) => pathsAreSame(directoryPath, path28.resolve(preservedPath))
7592
7629
  )
7593
7630
  );
7594
7631
  if (isPreservedPath) return true;
7595
7632
  if (!directoryStats.isDirectory()) return false;
7596
7633
  let entries;
7597
7634
  try {
7598
- entries = fs12.readdirSync(directory);
7635
+ entries = fs13.readdirSync(directory);
7599
7636
  } catch (error) {
7600
7637
  if (error.code === "ENOENT") return true;
7601
7638
  throw error;
7602
7639
  }
7603
7640
  let complete = true;
7604
7641
  for (const entry of entries) {
7605
- const entryPath = path27.join(directory, entry);
7642
+ const entryPath = path28.join(directory, entry);
7606
7643
  if (!purgeDirectoryPreservingPaths(entryPath, preservedPaths, false)) {
7607
7644
  complete = false;
7608
7645
  }
@@ -7621,13 +7658,13 @@ function purgeDirectoryPreservingFiles(directory, preservedFiles) {
7621
7658
  }
7622
7659
  function finishCustomRootPurge(directory) {
7623
7660
  const marker = managedDirectoryMarker(directory);
7624
- fs12.rmSync(marker, { force: true });
7661
+ fs13.rmSync(marker, { force: true });
7625
7662
  try {
7626
- fs12.rmdirSync(directory);
7663
+ fs13.rmdirSync(directory);
7627
7664
  } catch (error) {
7628
7665
  if (error.code === "ENOENT") return;
7629
7666
  try {
7630
- fs12.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
7667
+ fs13.writeFileSync(marker, FML_MANAGED_DIRECTORY_MARKER_CONTENT, {
7631
7668
  encoding: "utf8",
7632
7669
  flag: "wx",
7633
7670
  mode: 384
@@ -7725,7 +7762,7 @@ async function handleUninstall(opts) {
7725
7762
  return;
7726
7763
  }
7727
7764
  for (const root of purgeRoots) {
7728
- if (!fs12.existsSync(root.directory) && pathContains(root.directory, FML_OBSERVER_DIR)) {
7765
+ if (!fs13.existsSync(root.directory) && pathContains(root.directory, FML_OBSERVER_DIR)) {
7729
7766
  prepareManagedDirectory(root);
7730
7767
  }
7731
7768
  }
@@ -7849,7 +7886,7 @@ async function handleUninstall(opts) {
7849
7886
  )
7850
7887
  );
7851
7888
  }
7852
- fs12.rmSync(paths.pluginCacheDir, { recursive: true, force: true });
7889
+ fs13.rmSync(paths.pluginCacheDir, { recursive: true, force: true });
7853
7890
  const reportIntegrationWarning = (message) => {
7854
7891
  integrationsRemoved = false;
7855
7892
  console.warn(message.replace(/^warn:/u, "Warning:"));
@@ -8300,6 +8337,11 @@ async function handleStatus() {
8300
8337
  );
8301
8338
  }
8302
8339
  }
8340
+ if (hookInbox.lostEntries > 0) {
8341
+ console.log(
8342
+ ` Inbox loss: ${hookInbox.lostEntries} record(s), ${formatBytes2(hookInbox.lostBytes)} refused with no observer to take them${hookInbox.lastLostAtMs !== void 0 ? ` (last at ${new Date(hookInbox.lastLostAtMs).toISOString()})` : ""}`
8343
+ );
8344
+ }
8303
8345
  if (hookInbox.retiredEntries > 0) {
8304
8346
  console.log(
8305
8347
  ` Inbox retired: ${hookInbox.retiredEntries} record(s), ${formatBytes2(hookInbox.retiredBytes)}`
@@ -8954,8 +8996,8 @@ async function handleConfigDetail(opts) {
8954
8996
 
8955
8997
  // src/commands/tools.ts
8956
8998
  import { readFileSync } from "fs";
8957
- import path28 from "path";
8958
- var pluginVersion = true ? "0.8.6+f7c7191d" : void 0;
8999
+ import path29 from "path";
9000
+ var pluginVersion = true ? "0.8.7+af45d2af" : void 0;
8959
9001
  async function getApi() {
8960
9002
  const api = await getAuthenticatedClient();
8961
9003
  if (!api) {
@@ -9062,7 +9104,7 @@ async function handleToolsCall(name, opts) {
9062
9104
  }
9063
9105
  const raw = opts.file ? (() => {
9064
9106
  try {
9065
- return readFileSync(path28.resolve(opts.file), "utf8");
9107
+ return readFileSync(path29.resolve(opts.file), "utf8");
9066
9108
  } catch (err) {
9067
9109
  const msg = err instanceof Error ? err.message : String(err);
9068
9110
  console.error(`Could not read args file: ${msg}`);
@@ -9112,7 +9154,7 @@ if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.
9112
9154
  printBanner();
9113
9155
  }
9114
9156
  var program = new Command().name("fml").description("FML CLI and agent tools").version(
9115
- true ? "0.8.6+f7c7191d" : "dev"
9157
+ true ? "0.8.7+af45d2af" : "dev"
9116
9158
  );
9117
9159
  function formatCommandInventory(cmd, prefix = "") {
9118
9160
  const name = prefix ? `${prefix} ${cmd.name()}` : cmd.name();