@fml-inc/fml 0.8.7 → 0.8.10-dev.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fml",
3
- "version": "0.8.7",
3
+ "version": "0.8.10-dev.1",
4
4
  "description": "FML agent tools for Claude Code",
5
5
  "mcpServers": {
6
6
  "fml": {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runRawSyncOnce
3
- } from "./chunk-5JJQWV4G.js";
3
+ } from "./chunk-JPHN4VKM.js";
4
4
  import {
5
5
  resolvePackageRoot
6
6
  } from "./chunk-F57IFY3K.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  FML_HOOK_CAPTURE_DISABLED_FILE,
3
3
  FML_OBSERVER_LOG_FILE
4
- } from "./chunk-B3H2YWPJ.js";
4
+ } from "./chunk-HDAFEHUY.js";
5
5
 
6
6
  // src/json-file.ts
7
7
  import { randomUUID } from "crypto";
@@ -3,7 +3,7 @@ import {
3
3
  FML_OBSERVER_LOCK_FILE,
4
4
  FML_OBSERVER_PORT,
5
5
  FML_OBSERVER_STATE_FILE
6
- } from "./chunk-B3H2YWPJ.js";
6
+ } from "./chunk-HDAFEHUY.js";
7
7
 
8
8
  // ../../packages/panopticon/src/observer/auth.ts
9
9
  import crypto from "crypto";
@@ -695,6 +695,7 @@ var MAX_HOOK_DESTINATION_IDENTITY_BYTES = 4 * 1024;
695
695
  var DEFAULT_OTLP_OUTBOX_BATCH_SIZE = 100;
696
696
  var MAX_OTLP_OUTBOX_BATCH_SIZE = 100;
697
697
  var MAX_OTLP_ENVELOPE_BYTES = 16 * 1024 * 1024;
698
+ var DEFAULT_OTLP_SPOOL_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
698
699
  var DEFAULT_OTLP_SPOOL_MAX_BYTES = 1024 * 1024 * 1024;
699
700
  var MAX_OTLP_ENVELOPE_ID_BYTES = 1024;
700
701
  var MAX_OTLP_OBSERVER_INSTANCE_ID_BYTES = 4 * 1024;
@@ -703,8 +704,6 @@ var MAX_OTLP_DROP_REASON_BYTES = 4 * 1024;
703
704
  var MAX_OTLP_DESTINATION_ORGANIZATION_ID_BYTES = 128;
704
705
  var MAX_OTLP_DESTINATION_ORGANIZATION_SLUG_BYTES = 4 * 1024;
705
706
  var OWNERLESS_OTLP_DROP_REASON = "ownerless";
706
- var OTLP_CAPACITY_DROP_REASON = "spool_capacity";
707
- var OTLP_ENTRY_TOO_LARGE_DROP_REASON = "spool_entry_too_large";
708
707
  var OTLP_ROW_FIXED_LOGICAL_BYTES = 256;
709
708
  var MAX_TIMESTAMP_MS = 864e13;
710
709
  var QUARANTINE_PRUNE_INTERVAL_MS = 5 * 60 * 1e3;
@@ -1002,15 +1001,23 @@ function enforceOwnerOnlyMode(filePath, mode) {
1002
1001
  function openObserverDatabase(databaseFile, options = {}) {
1003
1002
  const maxHookSpoolBytes = options.maxHookSpoolBytes;
1004
1003
  const maxOtlpSpoolBytes = options.maxOtlpSpoolBytes ?? DEFAULT_OTLP_SPOOL_MAX_BYTES;
1005
- const maxHookQuarantineAgeMs = options.maxHookQuarantineAgeMs;
1006
- if (maxHookSpoolBytes !== void 0 && (!Number.isSafeInteger(maxHookSpoolBytes) || maxHookSpoolBytes < 1)) {
1004
+ if (!Number.isSafeInteger(maxOtlpSpoolBytes) || maxOtlpSpoolBytes < 1) {
1007
1005
  throw new Error(
1008
- "Hook spool maximum must be a positive safe integer number of bytes"
1006
+ "OTLP spool maximum must be a positive safe integer number of bytes"
1009
1007
  );
1010
1008
  }
1011
- if (!Number.isSafeInteger(maxOtlpSpoolBytes) || maxOtlpSpoolBytes < 1) {
1009
+ const otlpLowWaterBytes = Math.floor(maxOtlpSpoolBytes * 0.9);
1010
+ let otlpCapacityCleanupActive = false;
1011
+ const maxOtlpSpoolAgeMs = options.maxOtlpSpoolAgeMs ?? DEFAULT_OTLP_SPOOL_MAX_AGE_MS;
1012
+ if (!Number.isSafeInteger(maxOtlpSpoolAgeMs) || maxOtlpSpoolAgeMs < 1) {
1012
1013
  throw new Error(
1013
- "OTLP spool maximum must be a positive safe integer number of bytes"
1014
+ "OTLP spool maximum age must be a positive safe integer number of milliseconds"
1015
+ );
1016
+ }
1017
+ const maxHookQuarantineAgeMs = options.maxHookQuarantineAgeMs;
1018
+ if (maxHookSpoolBytes !== void 0 && (!Number.isSafeInteger(maxHookSpoolBytes) || maxHookSpoolBytes < 1)) {
1019
+ throw new Error(
1020
+ "Hook spool maximum must be a positive safe integer number of bytes"
1014
1021
  );
1015
1022
  }
1016
1023
  if (maxHookQuarantineAgeMs !== void 0 && (!Number.isSafeInteger(maxHookQuarantineAgeMs) || maxHookQuarantineAgeMs < 1)) {
@@ -2031,27 +2038,24 @@ function openObserverDatabase(databaseFile, options = {}) {
2031
2038
  last_drop_reason = ?
2032
2039
  WHERE singleton = 1
2033
2040
  `);
2034
- const otlpCapacityEvictionPlan = database.prepare(`
2035
- WITH ordered AS (
2036
- SELECT
2037
- logical_bytes,
2038
- length(body) AS body_bytes,
2039
- SUM(logical_bytes) OVER (ORDER BY id ASC) - logical_bytes
2040
- AS preceding_bytes
2041
- FROM otlp_envelope_outbox
2042
- )
2043
- SELECT
2044
- COUNT(*) AS record_count,
2045
- COALESCE(SUM(logical_bytes), 0) AS logical_bytes,
2046
- COALESCE(SUM(body_bytes), 0) AS body_bytes
2047
- FROM ordered
2048
- WHERE preceding_bytes < ?
2041
+ const hasExpiredOtlpEnvelopes = database.prepare(`
2042
+ SELECT 1 FROM otlp_envelope_outbox WHERE captured_at_ms < ? LIMIT 1
2049
2043
  `);
2050
- const evictOldestPendingOtlpEnvelopes = database.prepare(`
2051
- DELETE FROM otlp_envelope_outbox
2052
- WHERE id IN (
2053
- SELECT id FROM otlp_envelope_outbox ORDER BY id ASC LIMIT ?
2054
- )
2044
+ const expiredOtlpEnvelopes = database.prepare(`
2045
+ SELECT id, logical_bytes, length(body) AS body_bytes
2046
+ FROM otlp_envelope_outbox
2047
+ WHERE captured_at_ms < ?
2048
+ ORDER BY captured_at_ms, id
2049
+ LIMIT 100
2050
+ `);
2051
+ const oldestOtlpEnvelopes = database.prepare(`
2052
+ SELECT id, logical_bytes, length(body) AS body_bytes
2053
+ FROM otlp_envelope_outbox
2054
+ ORDER BY captured_at_ms, id
2055
+ LIMIT 100
2056
+ `);
2057
+ const deleteExpiredOtlpEnvelope = database.prepare(`
2058
+ DELETE FROM otlp_envelope_outbox WHERE id = ?
2055
2059
  `);
2056
2060
  const getDestinationPendingOtlpEnvelopeAccounting = database.prepare(`
2057
2061
  SELECT logical_bytes, length(body) AS body_bytes
@@ -2652,42 +2656,6 @@ function openObserverDatabase(databaseFile, options = {}) {
2652
2656
  );
2653
2657
  return null;
2654
2658
  }
2655
- if (logicalBytes > maxOtlpSpoolBytes) {
2656
- recordOtlpDrop(
2657
- 1,
2658
- logicalBytes,
2659
- envelope.body.byteLength,
2660
- capturedAtMs,
2661
- OTLP_ENTRY_TOO_LARGE_DROP_REASON
2662
- );
2663
- return null;
2664
- }
2665
- const retainedBytes = requireSpoolInteger(
2666
- getOtlpSpoolState.get(),
2667
- "logical_bytes"
2668
- );
2669
- const bytesToFree = retainedBytes + logicalBytes - maxOtlpSpoolBytes;
2670
- if (bytesToFree > 0) {
2671
- const plan = otlpCapacityEvictionPlan.get(bytesToFree);
2672
- const evictedRecords = requireSpoolInteger(plan, "record_count");
2673
- const evictedLogicalBytes = requireSpoolInteger(
2674
- plan,
2675
- "logical_bytes"
2676
- );
2677
- const evictedBodyBytes = requireSpoolInteger(plan, "body_bytes");
2678
- if (evictedRecords === 0 || evictedLogicalBytes < bytesToFree || evictOldestPendingOtlpEnvelopes.run(evictedRecords).changes !== evictedRecords) {
2679
- throw new Error(
2680
- "FML observer OTLP spool accounting could not enforce its limit"
2681
- );
2682
- }
2683
- recordOtlpDrop(
2684
- evictedRecords,
2685
- evictedLogicalBytes,
2686
- evictedBodyBytes,
2687
- capturedAtMs,
2688
- OTLP_CAPACITY_DROP_REASON
2689
- );
2690
- }
2691
2659
  captureOtlpEnvelope.run(
2692
2660
  envelopeId,
2693
2661
  envelope.observerInstanceId,
@@ -2885,6 +2853,80 @@ function openObserverDatabase(databaseFile, options = {}) {
2885
2853
  return droppedRecords;
2886
2854
  });
2887
2855
  },
2856
+ pruneExpiredOtlpEnvelopes: (nowMs = Date.now()) => {
2857
+ requireTimestamp(nowMs, "OTLP retention timestamp");
2858
+ const cutoffMs = nowMs - maxOtlpSpoolAgeMs;
2859
+ if (!hasExpiredOtlpEnvelopes.get(cutoffMs)) return 0;
2860
+ return runImmediateTransaction(() => {
2861
+ const rows = expiredOtlpEnvelopes.all(cutoffMs);
2862
+ let logicalBytes = 0;
2863
+ let bodyBytes = 0;
2864
+ for (const row of rows) {
2865
+ logicalBytes += requireSpoolInteger(row, "logical_bytes");
2866
+ bodyBytes += requireSpoolInteger(row, "body_bytes");
2867
+ if (deleteExpiredOtlpEnvelope.run(row.id).changes !== 1) {
2868
+ throw new Error(
2869
+ "FML observer OTLP retention deletion was incomplete"
2870
+ );
2871
+ }
2872
+ }
2873
+ recordOtlpDrop(
2874
+ rows.length,
2875
+ logicalBytes,
2876
+ bodyBytes,
2877
+ nowMs,
2878
+ "spool_expired"
2879
+ );
2880
+ return rows.length;
2881
+ });
2882
+ },
2883
+ pruneOtlpCapacity: (nowMs = Date.now()) => {
2884
+ requireTimestamp(nowMs, "OTLP capacity cleanup timestamp");
2885
+ const retainedBytes = requireSpoolInteger(
2886
+ getOtlpSpoolState.get(),
2887
+ "logical_bytes"
2888
+ );
2889
+ if (retainedBytes <= otlpLowWaterBytes)
2890
+ otlpCapacityCleanupActive = false;
2891
+ if (retainedBytes > maxOtlpSpoolBytes) otlpCapacityCleanupActive = true;
2892
+ if (!otlpCapacityCleanupActive) return 0;
2893
+ return runImmediateTransaction(() => {
2894
+ const currentBytes = requireSpoolInteger(
2895
+ getOtlpSpoolState.get(),
2896
+ "logical_bytes"
2897
+ );
2898
+ if (currentBytes <= otlpLowWaterBytes) {
2899
+ otlpCapacityCleanupActive = false;
2900
+ return 0;
2901
+ }
2902
+ let records = 0;
2903
+ let logicalBytes = 0;
2904
+ let bodyBytes = 0;
2905
+ for (const row of oldestOtlpEnvelopes.all()) {
2906
+ logicalBytes += requireSpoolInteger(row, "logical_bytes");
2907
+ bodyBytes += requireSpoolInteger(row, "body_bytes");
2908
+ if (deleteExpiredOtlpEnvelope.run(row.id).changes !== 1) {
2909
+ throw new Error(
2910
+ "FML observer OTLP capacity deletion was incomplete"
2911
+ );
2912
+ }
2913
+ records += 1;
2914
+ if (currentBytes - logicalBytes <= otlpLowWaterBytes) break;
2915
+ }
2916
+ if (records === 0)
2917
+ throw new Error(
2918
+ "FML observer OTLP capacity accounting is inconsistent"
2919
+ );
2920
+ recordOtlpDrop(
2921
+ records,
2922
+ logicalBytes,
2923
+ bodyBytes,
2924
+ nowMs,
2925
+ "spool_capacity"
2926
+ );
2927
+ return records;
2928
+ });
2929
+ },
2888
2930
  getOtlpSpoolStatus: () => {
2889
2931
  const row = getOtlpSpoolState.get();
2890
2932
  const lastDroppedAtMs = row?.last_dropped_at_ms;
@@ -2898,6 +2940,7 @@ function openObserverDatabase(databaseFile, options = {}) {
2898
2940
  return {
2899
2941
  unit: "logical_bytes",
2900
2942
  maxBytes: maxOtlpSpoolBytes,
2943
+ maxAgeMs: maxOtlpSpoolAgeMs,
2901
2944
  logicalBytes: requireSpoolInteger(row, "logical_bytes"),
2902
2945
  bodyBytes: requireSpoolInteger(row, "body_bytes"),
2903
2946
  droppedRecords: requireSpoolInteger(row, "dropped_records"),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Sentry
3
- } from "./chunk-B3H2YWPJ.js";
3
+ } from "./chunk-HDAFEHUY.js";
4
4
 
5
5
  // src/bin-utils.ts
6
6
  import fs from "fs";
@@ -53,7 +53,7 @@ var PACKAGE_NAME = "@fml-inc/fml";
53
53
  var UPDATE_TIMEOUT_MS = 12e4;
54
54
  var SETUP_TIMEOUT_MS = 3e5;
55
55
  function getCurrentVersion() {
56
- return true ? "0.8.7+af45d2af" : "unknown";
56
+ return true ? "0.8.10-dev.1+09fae5b6" : "unknown";
57
57
  }
58
58
  function parseVersion(version) {
59
59
  const match = SEMVER_PATTERN.exec(version);
@@ -7,7 +7,7 @@ import {
7
7
  getSelectedOrg,
8
8
  getSiteUrlForConvexUrl,
9
9
  getValidToken
10
- } from "./chunk-B3H2YWPJ.js";
10
+ } from "./chunk-HDAFEHUY.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-ORR5MIJL.js");
82
+ const { readTokens, SERVICE_TOKEN_LOGIN_USER_ID } = await import("./token-store-GYU3AXBK.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;
@@ -193,6 +193,7 @@ function ensureDirs() {
193
193
 
194
194
  // src/config.ts
195
195
  var DEFAULT_PROD_URL = "https://api.fml.inc";
196
+ var DEFAULT_SYNC_URL = DEFAULT_PROD_URL;
196
197
  var DEFAULT_TARGET_NAME = "fml";
197
198
  var LEGACY_HOSTED_BACKEND_URLS = [
198
199
  "https://trustworthy-chihuahua-382.convex.site"
@@ -338,9 +339,6 @@ function getSiteUrlForConvexUrl(convexUrl) {
338
339
  var initialized = false;
339
340
  var sentry = null;
340
341
  var client = null;
341
- function isSentryEnabled() {
342
- return process.env.SENTRY_ENABLED === "true";
343
- }
344
342
  async function loadSentry() {
345
343
  if (sentry) return sentry;
346
344
  try {
@@ -350,15 +348,35 @@ async function loadSentry() {
350
348
  return null;
351
349
  }
352
350
  }
353
- async function initSentry() {
354
- if (initialized) return;
351
+ async function initSentry(forceRefresh = false) {
352
+ if (initialized && !forceRefresh) return;
355
353
  initialized = true;
356
- if (!isSentryEnabled()) return;
354
+ let config;
355
+ try {
356
+ const { resolveCliSentryConfig } = await import("./telemetry-config-HRSNDMTG.js");
357
+ config = process.env.SENTRY_ENABLED === "false" ? null : await resolveCliSentryConfig(
358
+ process.env.SENTRY_ENABLED === "true" && true ? "https://4c243521c03917c886d0bf33bc0038c8@o4510167429873664.ingest.us.sentry.io/4511078213156864" : "",
359
+ forceRefresh
360
+ );
361
+ } catch {
362
+ config = null;
363
+ }
364
+ if (client) {
365
+ try {
366
+ await client.close(2e3);
367
+ } catch {
368
+ }
369
+ client = null;
370
+ }
371
+ if (!config?.dsn) {
372
+ sentry?.getCurrentScope().setClient(void 0);
373
+ return;
374
+ }
357
375
  const mod = await loadSentry();
358
376
  if (!mod) return;
359
377
  client = new mod.ServerRuntimeClient({
360
- dsn: "https://4c243521c03917c886d0bf33bc0038c8@o4510167429873664.ingest.us.sentry.io/4511078213156864",
361
- environment: process.env.DEPLOYMENT_ENV ?? process.env.NODE_ENV ?? "production",
378
+ dsn: config.dsn,
379
+ environment: config.environment,
362
380
  tracesSampleRate: 0,
363
381
  integrations: [],
364
382
  stackParser: mod.createStackParser(mod.nodeStackLineParser()),
@@ -1378,7 +1396,7 @@ async function getValidToken(opts) {
1378
1396
  return stored.accessToken;
1379
1397
  }
1380
1398
  if (stored.tokenType === "service") {
1381
- return await getOrStartTokenRefresh(
1399
+ const accessToken2 = await getOrStartTokenRefresh(
1382
1400
  serviceRefreshPromises,
1383
1401
  key,
1384
1402
  forceRefresh,
@@ -1424,6 +1442,12 @@ async function getValidToken(opts) {
1424
1442
  }),
1425
1443
  opts?.signal
1426
1444
  );
1445
+ await refreshTelemetryConfigAfterTokenRefresh(
1446
+ envName,
1447
+ accessToken2,
1448
+ opts?.signal
1449
+ );
1450
+ return accessToken2;
1427
1451
  }
1428
1452
  if (stored.refreshInvalidAt) {
1429
1453
  console.error(
@@ -1431,13 +1455,34 @@ async function getValidToken(opts) {
1431
1455
  );
1432
1456
  return null;
1433
1457
  }
1434
- return await getOrStartTokenRefresh(
1458
+ const accessToken = await getOrStartTokenRefresh(
1435
1459
  oauthRefreshPromises,
1436
1460
  key,
1437
1461
  forceRefresh,
1438
1462
  () => refreshTokenWithLock(envName, forceRefresh, opts?.signal),
1439
1463
  opts?.signal
1440
1464
  );
1465
+ await refreshTelemetryConfigAfterTokenRefresh(
1466
+ envName,
1467
+ accessToken,
1468
+ opts?.signal
1469
+ );
1470
+ return accessToken;
1471
+ }
1472
+ async function refreshTelemetryConfigAfterTokenRefresh(envName, accessToken, signal) {
1473
+ if (!accessToken || signal?.aborted) return;
1474
+ try {
1475
+ const current = readTokens(envName);
1476
+ if (current?.accessToken !== accessToken) return;
1477
+ const { refreshCliTelemetryConfig } = await import("./telemetry-config-HRSNDMTG.js");
1478
+ await refreshCliTelemetryConfig({
1479
+ envName,
1480
+ accessToken,
1481
+ siteUrl: current.siteUrl,
1482
+ signal
1483
+ });
1484
+ } catch {
1485
+ }
1441
1486
  }
1442
1487
  async function refreshTokenWithLock(envName, forceRefresh = false, signal) {
1443
1488
  const lock = await acquireRefreshLock(envName, signal);
@@ -1543,7 +1588,9 @@ async function refreshToken(stored, lock, envName, signal) {
1543
1588
  },
1544
1589
  orgId: stored.orgId,
1545
1590
  orgSlug: stored.orgSlug,
1546
- workosClientId: stored.workosClientId
1591
+ workosClientId: stored.workosClientId,
1592
+ // The backend this login belongs to does not change on rotation.
1593
+ ...stored.siteUrl ? { siteUrl: stored.siteUrl } : {}
1547
1594
  };
1548
1595
  const pendingPath = stagePendingOAuthRefresh(
1549
1596
  stored.refreshToken,
@@ -1580,8 +1627,6 @@ async function refreshToken(stored, lock, envName, signal) {
1580
1627
  }
1581
1628
 
1582
1629
  export {
1583
- initSentry,
1584
- Sentry,
1585
1630
  resolveFmlDefaultDirectories,
1586
1631
  FML_DATA_DIR,
1587
1632
  FML_STATE_DIR,
@@ -1606,6 +1651,7 @@ export {
1606
1651
  observerOnlyOverrideWarning,
1607
1652
  ensureDirs,
1608
1653
  DEFAULT_PROD_URL,
1654
+ DEFAULT_SYNC_URL,
1609
1655
  DEFAULT_TARGET_NAME,
1610
1656
  isHostedBackendUrl,
1611
1657
  isValidEnvName,
@@ -1632,5 +1678,7 @@ export {
1632
1678
  SERVICE_TOKEN_LOGIN_USER_ID,
1633
1679
  clearStoredCredentials,
1634
1680
  storeServiceRefreshToken,
1635
- getValidToken
1681
+ getValidToken,
1682
+ initSentry,
1683
+ Sentry
1636
1684
  };
@@ -6,7 +6,7 @@ import {
6
6
  getSiteUrlForConvexUrl,
7
7
  getSyncDestinationIdentity,
8
8
  getValidToken
9
- } from "./chunk-B3H2YWPJ.js";
9
+ } from "./chunk-HDAFEHUY.js";
10
10
 
11
11
  // ../../packages/panopticon/src/artifact.ts
12
12
  import {
@@ -6,7 +6,7 @@ import {
6
6
  getSiteUrlForConvexUrl,
7
7
  getSyncDestinationIdentity,
8
8
  getValidToken
9
- } from "./chunk-B3H2YWPJ.js";
9
+ } from "./chunk-HDAFEHUY.js";
10
10
 
11
11
  // src/hooks/sync-loop.ts
12
12
  import { createHash } from "crypto";
package/dist/cli.js CHANGED
@@ -2,19 +2,19 @@
2
2
  import {
3
3
  createFmlClient,
4
4
  getAuthenticatedClient
5
- } from "./chunk-LOJPIJZW.js";
5
+ } from "./chunk-GDOXY4W2.js";
6
6
  import {
7
7
  execBinSync,
8
8
  handleUpdate,
9
9
  resolveBin
10
- } from "./chunk-XFALHTDL.js";
10
+ } from "./chunk-5SI6S4HW.js";
11
11
  import {
12
12
  openObserverLog,
13
13
  readJsonFile,
14
14
  setHookCaptureDisabled,
15
15
  writeJsonFile,
16
16
  writeTextFileAtomic
17
- } from "./chunk-LPSZCTTJ.js";
17
+ } from "./chunk-2ZCXGYI5.js";
18
18
  import {
19
19
  CODEX_HOOK_EVENTS,
20
20
  GEMINI_HOOK_EVENTS,
@@ -29,7 +29,7 @@ import {
29
29
  readObserverAuthToken,
30
30
  readObserverStateStatus,
31
31
  removeObserverStateIfOwned
32
- } from "./chunk-4YLSKFDU.js";
32
+ } from "./chunk-5PEFBQNX.js";
33
33
  import "./chunk-7V7G7NVF.js";
34
34
  import {
35
35
  claudeConfigDirectory,
@@ -38,7 +38,7 @@ import {
38
38
  geminiConfigDirectory,
39
39
  initializeSyncSettings,
40
40
  runRawSyncOnce
41
- } from "./chunk-5JJQWV4G.js";
41
+ } from "./chunk-JPHN4VKM.js";
42
42
  import {
43
43
  resolvePackageRoot
44
44
  } from "./chunk-F57IFY3K.js";
@@ -86,7 +86,7 @@ import {
86
86
  storeServiceRefreshToken,
87
87
  writeEnvConfig,
88
88
  writeTokens
89
- } from "./chunk-B3H2YWPJ.js";
89
+ } from "./chunk-HDAFEHUY.js";
90
90
  import "./chunk-MLKGABMK.js";
91
91
 
92
92
  // src/cli.ts
@@ -132,7 +132,7 @@ var STOP_TIMEOUT_MS = 5e3;
132
132
  var FORCE_STOP_TIMEOUT_MS = 2e3;
133
133
  var POLL_INTERVAL_MS = 50;
134
134
  function expectedObserverVersion() {
135
- return true ? "0.8.7+af45d2af" : "dev";
135
+ return true ? "0.8.10-dev.1+09fae5b6" : "dev";
136
136
  }
137
137
  function delay(ms) {
138
138
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -7438,6 +7438,7 @@ async function runServiceTokenLogin(refreshToken, backendUrl) {
7438
7438
  throw new Error("Service token login failed.");
7439
7439
  }
7440
7440
  commitLoginEnvironment(environment);
7441
+ await initSentry(true);
7441
7442
  await selectOrg(environment);
7442
7443
  console.log("Logged in with FML service token.");
7443
7444
  console.log(
@@ -7478,6 +7479,7 @@ async function runInteractiveLogin(opts = {}) {
7478
7479
  );
7479
7480
  }
7480
7481
  await linkGitHubIdentity(environment);
7482
+ await initSentry(true);
7481
7483
  await selectOrg(environment);
7482
7484
  return;
7483
7485
  }
@@ -7492,6 +7494,7 @@ async function runInteractiveLogin(opts = {}) {
7492
7494
  commitLoginEnvironment(environment);
7493
7495
  console.log(`Logged in as ${result.name} (${result.email})`);
7494
7496
  await linkGitHubIdentity(environment);
7497
+ await initSentry(true);
7495
7498
  await selectOrg(environment);
7496
7499
  }
7497
7500
  async function handleLogin(opts) {
@@ -8159,6 +8162,7 @@ function otlpSpoolStatus(value) {
8159
8162
  const logicalBytes = nonNegativeInteger("logicalBytes");
8160
8163
  const bodyBytes = nonNegativeInteger("bodyBytes");
8161
8164
  const maxBytes = nonNegativeInteger("maxBytes");
8165
+ const maxAgeMs = nonNegativeInteger("maxAgeMs");
8162
8166
  const droppedRecords = nonNegativeInteger("droppedRecords");
8163
8167
  const droppedLogicalBytes = nonNegativeInteger("droppedLogicalBytes");
8164
8168
  const droppedBodyBytes = nonNegativeInteger("droppedBodyBytes");
@@ -8169,6 +8173,7 @@ function otlpSpoolStatus(value) {
8169
8173
  logicalBytes,
8170
8174
  bodyBytes,
8171
8175
  maxBytes,
8176
+ ...maxAgeMs !== void 0 && maxAgeMs > 0 ? { maxAgeMs } : {},
8172
8177
  droppedRecords,
8173
8178
  droppedLogicalBytes,
8174
8179
  droppedBodyBytes,
@@ -8360,9 +8365,14 @@ async function handleStatus() {
8360
8365
  console.log(
8361
8366
  ` Spool: ${formatBytes2(otlp.spool.logicalBytes)} / ${formatBytes2(otlp.spool.maxBytes)} (${usedPercent.toFixed(1)}%; ${formatBytes2(otlp.spool.bodyBytes)} encoded bodies)`
8362
8367
  );
8368
+ if (otlp.spool.maxAgeMs !== void 0) {
8369
+ console.log(
8370
+ ` Retention: ${otlp.spool.maxAgeMs / 36e5} hour(s) (background cleanup)`
8371
+ );
8372
+ }
8363
8373
  if (usedPercent >= 80) {
8364
8374
  console.log(
8365
- ` Attention: the oldest pending telemetry is dropped when the spool reaches ${formatBytes2(otlp.spool.maxBytes)}`
8375
+ ` Attention: background cleanup removes oldest telemetry toward 90% of the ${formatBytes2(otlp.spool.maxBytes)} byte target after overflow; checks run every 5 minutes`
8366
8376
  );
8367
8377
  }
8368
8378
  if (otlp.spool.droppedRecords > 0) {
@@ -8997,7 +9007,7 @@ async function handleConfigDetail(opts) {
8997
9007
  // src/commands/tools.ts
8998
9008
  import { readFileSync } from "fs";
8999
9009
  import path29 from "path";
9000
- var pluginVersion = true ? "0.8.7+af45d2af" : void 0;
9010
+ var pluginVersion = true ? "0.8.10-dev.1+09fae5b6" : void 0;
9001
9011
  async function getApi() {
9002
9012
  const api = await getAuthenticatedClient();
9003
9013
  if (!api) {
@@ -9154,7 +9164,7 @@ if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.
9154
9164
  printBanner();
9155
9165
  }
9156
9166
  var program = new Command().name("fml").description("FML CLI and agent tools").version(
9157
- true ? "0.8.7+af45d2af" : "dev"
9167
+ true ? "0.8.10-dev.1+09fae5b6" : "dev"
9158
9168
  );
9159
9169
  function formatCommandInventory(cmd, prefix = "") {
9160
9170
  const name = prefix ? `${prefix} ${cmd.name()}` : cmd.name();
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  hookCaptureIsDisabled,
4
4
  openObserverLog
5
- } from "./chunk-LPSZCTTJ.js";
5
+ } from "./chunk-2ZCXGYI5.js";
6
6
  import {
7
7
  getActiveHookDestinationBinding
8
- } from "./chunk-PJWMSHHZ.js";
8
+ } from "./chunk-WE7MAWAW.js";
9
9
  import {
10
10
  HookInboxCapacityError,
11
11
  OBSERVER_HEARTBEAT_STALE_MS,
@@ -19,7 +19,7 @@ import {
19
19
  readObserverAuthToken,
20
20
  readObserverState,
21
21
  recordHookInboxLoss
22
- } from "./chunk-4YLSKFDU.js";
22
+ } from "./chunk-5PEFBQNX.js";
23
23
  import {
24
24
  resolveRepositoryRootFromCwd,
25
25
  resolveRepositorySlugFromCwd
@@ -35,7 +35,7 @@ import {
35
35
  FML_OBSERVER_ENDPOINT_FILE,
36
36
  FML_OBSERVER_STATE_FILE,
37
37
  getValidToken
38
- } from "./chunk-B3H2YWPJ.js";
38
+ } from "./chunk-HDAFEHUY.js";
39
39
  import "./chunk-MLKGABMK.js";
40
40
 
41
41
  // src/hooks/handler.ts
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  createFmlClient,
4
4
  getAuthenticatedClient
5
- } from "../chunk-LOJPIJZW.js";
5
+ } from "../chunk-GDOXY4W2.js";
6
6
  import {
7
7
  resolveRepoFromCwd
8
8
  } from "../chunk-7V7G7NVF.js";
@@ -17,7 +17,7 @@ import {
17
17
  getValidToken,
18
18
  initSentry,
19
19
  readTokens
20
- } from "../chunk-B3H2YWPJ.js";
20
+ } from "../chunk-HDAFEHUY.js";
21
21
  import "../chunk-MLKGABMK.js";
22
22
 
23
23
  // src/mcp/server.ts