@fml-inc/fml 0.8.9 → 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.
|
@@ -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
|
-
|
|
1006
|
-
if (maxHookSpoolBytes !== void 0 && (!Number.isSafeInteger(maxHookSpoolBytes) || maxHookSpoolBytes < 1)) {
|
|
1004
|
+
if (!Number.isSafeInteger(maxOtlpSpoolBytes) || maxOtlpSpoolBytes < 1) {
|
|
1007
1005
|
throw new Error(
|
|
1008
|
-
"
|
|
1006
|
+
"OTLP spool maximum must be a positive safe integer number of bytes"
|
|
1009
1007
|
);
|
|
1010
1008
|
}
|
|
1011
|
-
|
|
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
|
|
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
|
|
2035
|
-
|
|
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
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
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"),
|
|
@@ -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.
|
|
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);
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
execBinSync,
|
|
8
8
|
handleUpdate,
|
|
9
9
|
resolveBin
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-5SI6S4HW.js";
|
|
11
11
|
import {
|
|
12
12
|
openObserverLog,
|
|
13
13
|
readJsonFile,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
readObserverAuthToken,
|
|
30
30
|
readObserverStateStatus,
|
|
31
31
|
removeObserverStateIfOwned
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-5PEFBQNX.js";
|
|
33
33
|
import "./chunk-7V7G7NVF.js";
|
|
34
34
|
import {
|
|
35
35
|
claudeConfigDirectory,
|
|
@@ -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.
|
|
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));
|
|
@@ -8162,6 +8162,7 @@ function otlpSpoolStatus(value) {
|
|
|
8162
8162
|
const logicalBytes = nonNegativeInteger("logicalBytes");
|
|
8163
8163
|
const bodyBytes = nonNegativeInteger("bodyBytes");
|
|
8164
8164
|
const maxBytes = nonNegativeInteger("maxBytes");
|
|
8165
|
+
const maxAgeMs = nonNegativeInteger("maxAgeMs");
|
|
8165
8166
|
const droppedRecords = nonNegativeInteger("droppedRecords");
|
|
8166
8167
|
const droppedLogicalBytes = nonNegativeInteger("droppedLogicalBytes");
|
|
8167
8168
|
const droppedBodyBytes = nonNegativeInteger("droppedBodyBytes");
|
|
@@ -8172,6 +8173,7 @@ function otlpSpoolStatus(value) {
|
|
|
8172
8173
|
logicalBytes,
|
|
8173
8174
|
bodyBytes,
|
|
8174
8175
|
maxBytes,
|
|
8176
|
+
...maxAgeMs !== void 0 && maxAgeMs > 0 ? { maxAgeMs } : {},
|
|
8175
8177
|
droppedRecords,
|
|
8176
8178
|
droppedLogicalBytes,
|
|
8177
8179
|
droppedBodyBytes,
|
|
@@ -8363,9 +8365,14 @@ async function handleStatus() {
|
|
|
8363
8365
|
console.log(
|
|
8364
8366
|
` Spool: ${formatBytes2(otlp.spool.logicalBytes)} / ${formatBytes2(otlp.spool.maxBytes)} (${usedPercent.toFixed(1)}%; ${formatBytes2(otlp.spool.bodyBytes)} encoded bodies)`
|
|
8365
8367
|
);
|
|
8368
|
+
if (otlp.spool.maxAgeMs !== void 0) {
|
|
8369
|
+
console.log(
|
|
8370
|
+
` Retention: ${otlp.spool.maxAgeMs / 36e5} hour(s) (background cleanup)`
|
|
8371
|
+
);
|
|
8372
|
+
}
|
|
8366
8373
|
if (usedPercent >= 80) {
|
|
8367
8374
|
console.log(
|
|
8368
|
-
` Attention:
|
|
8375
|
+
` Attention: background cleanup removes oldest telemetry toward 90% of the ${formatBytes2(otlp.spool.maxBytes)} byte target after overflow; checks run every 5 minutes`
|
|
8369
8376
|
);
|
|
8370
8377
|
}
|
|
8371
8378
|
if (otlp.spool.droppedRecords > 0) {
|
|
@@ -9000,7 +9007,7 @@ async function handleConfigDetail(opts) {
|
|
|
9000
9007
|
// src/commands/tools.ts
|
|
9001
9008
|
import { readFileSync } from "fs";
|
|
9002
9009
|
import path29 from "path";
|
|
9003
|
-
var pluginVersion = true ? "0.8.
|
|
9010
|
+
var pluginVersion = true ? "0.8.10-dev.1+09fae5b6" : void 0;
|
|
9004
9011
|
async function getApi() {
|
|
9005
9012
|
const api = await getAuthenticatedClient();
|
|
9006
9013
|
if (!api) {
|
|
@@ -9157,7 +9164,7 @@ if (process.argv.length <= 2 || process.argv.includes("--help") || process.argv.
|
|
|
9157
9164
|
printBanner();
|
|
9158
9165
|
}
|
|
9159
9166
|
var program = new Command().name("fml").description("FML CLI and agent tools").version(
|
|
9160
|
-
true ? "0.8.
|
|
9167
|
+
true ? "0.8.10-dev.1+09fae5b6" : "dev"
|
|
9161
9168
|
);
|
|
9162
9169
|
function formatCommandInventory(cmd, prefix = "") {
|
|
9163
9170
|
const name = prefix ? `${prefix} ${cmd.name()}` : cmd.name();
|
package/dist/hook-handler.js
CHANGED
package/dist/observer-server.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isWinShim,
|
|
6
6
|
quoteWinArg,
|
|
7
7
|
resolveBin
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5SI6S4HW.js";
|
|
9
9
|
import {
|
|
10
10
|
createHookSyncLoop,
|
|
11
11
|
getActiveHookDestinationBinding
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
removeObserverStateIfOwned,
|
|
31
31
|
retireHookInboxEntry,
|
|
32
32
|
updateObserverState
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-5PEFBQNX.js";
|
|
34
34
|
import "./chunk-7V7G7NVF.js";
|
|
35
35
|
import {
|
|
36
36
|
ObserverSyncCycleAbortedError,
|
|
@@ -225,7 +225,6 @@ var MAX_ERROR_TEXT_BYTES = 1e3;
|
|
|
225
225
|
var MAX_REJECTION_REASON_BYTES = 3 * 1024;
|
|
226
226
|
var MAX_OTLP_RESPONSE_BYTES = 64 * 1024;
|
|
227
227
|
var MAX_OTLP_UPLOAD_ATTEMPTS = 10;
|
|
228
|
-
var MAX_OTLP_RETRY_AGE_MS = 24 * 60 * 6e4;
|
|
229
228
|
var OUTBOX_BATCH_SIZE = 1;
|
|
230
229
|
var DRAIN_INTERVAL_MS = 25;
|
|
231
230
|
function combineWarnings(...warnings) {
|
|
@@ -675,6 +674,7 @@ async function runOtlpSyncCycle(opts) {
|
|
|
675
674
|
if (totalPending === 0) {
|
|
676
675
|
return { kind: "drained", pending: 0, uploaded: 0 };
|
|
677
676
|
}
|
|
677
|
+
const maxAgeMs = opts.database.getOtlpSpoolStatus().maxAgeMs;
|
|
678
678
|
const destinationResolution = await resolveDestination();
|
|
679
679
|
if (destinationResolution.kind !== "ready") {
|
|
680
680
|
return {
|
|
@@ -736,7 +736,7 @@ async function runOtlpSyncCycle(opts) {
|
|
|
736
736
|
};
|
|
737
737
|
for (const envelope of envelopes) {
|
|
738
738
|
if (opts.signal?.aborted) throw opts.signal.reason;
|
|
739
|
-
if (cycleStartedAtMs - envelope.capturedAtMs >=
|
|
739
|
+
if (cycleStartedAtMs - envelope.capturedAtMs >= maxAgeMs) {
|
|
740
740
|
dropped += opts.database.dropOtlpEnvelopes(
|
|
741
741
|
destination,
|
|
742
742
|
[
|
|
@@ -1046,6 +1046,17 @@ function parseOtlpSpoolMaxBytes(configured) {
|
|
|
1046
1046
|
}
|
|
1047
1047
|
return bytes;
|
|
1048
1048
|
}
|
|
1049
|
+
function parseOtlpSpoolMaxAgeMs(configured) {
|
|
1050
|
+
if (configured === void 0) return void 0;
|
|
1051
|
+
const value = configured.trim();
|
|
1052
|
+
const milliseconds = Number(value) * 60 * 60 * 1e3;
|
|
1053
|
+
if (!POSITIVE_INTEGER_PATTERN.test(value) || !Number.isSafeInteger(milliseconds)) {
|
|
1054
|
+
throw new Error(
|
|
1055
|
+
"FML_OTLP_SPOOL_MAX_AGE_HOURS must be a positive safe integer number of hours"
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
return milliseconds;
|
|
1059
|
+
}
|
|
1049
1060
|
|
|
1050
1061
|
// src/observer/sync-loop.ts
|
|
1051
1062
|
var OBSERVER_IDLE_INTERVAL_MS = 5 * 6e4;
|
|
@@ -1348,7 +1359,8 @@ var HOOK_INBOX_DRAIN_INTERVAL_MS = 15e3;
|
|
|
1348
1359
|
var HOOK_INBOX_DRAIN_BUDGET_MS = 5e3;
|
|
1349
1360
|
var HOOK_INBOX_DRAIN_ENTRIES = 100;
|
|
1350
1361
|
var HOOK_INBOX_DRAIN_BULK_ENTRIES = 1e3;
|
|
1351
|
-
var
|
|
1362
|
+
var SPOOL_CLEANUP_INTERVAL_MS = 5 * 60 * 1e3;
|
|
1363
|
+
var OTLP_CLEANUP_BATCH_DELAY_MS = 100;
|
|
1352
1364
|
var OBSERVER_DIRECTORY_LEASE = {
|
|
1353
1365
|
stateFile: FML_OBSERVER_STATE_FILE,
|
|
1354
1366
|
lockFile: FML_OBSERVER_LOCK_FILE
|
|
@@ -1452,6 +1464,9 @@ var hookQuarantineMaxAgeMs = parseHookQuarantineMaxAgeMs(
|
|
|
1452
1464
|
var otlpSpoolMaxBytes = parseOtlpSpoolMaxBytes(
|
|
1453
1465
|
process.env.FML_OTLP_SPOOL_MAX_BYTES
|
|
1454
1466
|
);
|
|
1467
|
+
var otlpSpoolMaxAgeMs = parseOtlpSpoolMaxAgeMs(
|
|
1468
|
+
process.env.FML_OTLP_SPOOL_MAX_AGE_HOURS
|
|
1469
|
+
);
|
|
1455
1470
|
ensureDirs();
|
|
1456
1471
|
var isolationWarning = observerOnlyOverrideWarning();
|
|
1457
1472
|
if (isolationWarning) console.warn(isolationWarning);
|
|
@@ -1486,6 +1501,7 @@ var fatalShutdownScheduled = false;
|
|
|
1486
1501
|
var heartbeatTimer = null;
|
|
1487
1502
|
var hookInboxDrainTimer = null;
|
|
1488
1503
|
var hookInboxDrainPromise = null;
|
|
1504
|
+
var otlpPruneTimer = null;
|
|
1489
1505
|
var hookQuarantinePruneTimer = null;
|
|
1490
1506
|
var stateUpdateRetryTimer = null;
|
|
1491
1507
|
var syncLoop = null;
|
|
@@ -1510,9 +1526,10 @@ try {
|
|
|
1510
1526
|
// FML_STATE_DIR can be caller-managed. Keep SQLite payload files private
|
|
1511
1527
|
// without changing permissions on the shared parent directory.
|
|
1512
1528
|
restrictDatabaseDirectory: false,
|
|
1529
|
+
maxOtlpSpoolAgeMs: otlpSpoolMaxAgeMs,
|
|
1530
|
+
maxOtlpSpoolBytes: otlpSpoolMaxBytes,
|
|
1513
1531
|
...hookSpoolMaxBytes === void 0 ? {} : { maxHookSpoolBytes: hookSpoolMaxBytes },
|
|
1514
|
-
...hookQuarantineMaxAgeMs === void 0 ? {} : { maxHookQuarantineAgeMs: hookQuarantineMaxAgeMs }
|
|
1515
|
-
...otlpSpoolMaxBytes === void 0 ? {} : { maxOtlpSpoolBytes: otlpSpoolMaxBytes }
|
|
1532
|
+
...hookQuarantineMaxAgeMs === void 0 ? {} : { maxHookQuarantineAgeMs: hookQuarantineMaxAgeMs }
|
|
1516
1533
|
});
|
|
1517
1534
|
} catch (error) {
|
|
1518
1535
|
removeInitialLeasesBestEffort(initial.instanceId);
|
|
@@ -1546,7 +1563,7 @@ var hookRejectionStatus = () => ({
|
|
|
1546
1563
|
...lastRejectedReason !== void 0 ? { lastRejectedReason } : {},
|
|
1547
1564
|
...lastRejectedTarget !== void 0 ? { lastRejectedTarget } : {}
|
|
1548
1565
|
});
|
|
1549
|
-
var packageVersion = true ? "0.8.
|
|
1566
|
+
var packageVersion = true ? "0.8.10-dev.1+09fae5b6" : "dev";
|
|
1550
1567
|
var health = () => ({
|
|
1551
1568
|
status: "ok",
|
|
1552
1569
|
service: OBSERVER_SERVICE_NAME,
|
|
@@ -1611,6 +1628,7 @@ var shutdown = async () => {
|
|
|
1611
1628
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
1612
1629
|
if (hookInboxDrainTimer) clearInterval(hookInboxDrainTimer);
|
|
1613
1630
|
if (hookQuarantinePruneTimer) clearInterval(hookQuarantinePruneTimer);
|
|
1631
|
+
if (otlpPruneTimer) clearTimeout(otlpPruneTimer);
|
|
1614
1632
|
if (stateUpdateRetryTimer) clearTimeout(stateUpdateRetryTimer);
|
|
1615
1633
|
sessionEndSyncWakeScheduler.stop();
|
|
1616
1634
|
autoUpdater?.stop();
|
|
@@ -1866,8 +1884,30 @@ try {
|
|
|
1866
1884
|
} catch (error) {
|
|
1867
1885
|
scheduleFatalShutdown(error);
|
|
1868
1886
|
}
|
|
1869
|
-
},
|
|
1887
|
+
}, SPOOL_CLEANUP_INTERVAL_MS);
|
|
1870
1888
|
hookQuarantinePruneTimer.unref();
|
|
1889
|
+
let nextOtlpAgeCheckAtMs = 0;
|
|
1890
|
+
const pruneOtlp = () => {
|
|
1891
|
+
if (shuttingDown) return;
|
|
1892
|
+
try {
|
|
1893
|
+
const nowMs = Date.now();
|
|
1894
|
+
let removed = 0;
|
|
1895
|
+
if (nowMs >= nextOtlpAgeCheckAtMs) {
|
|
1896
|
+
removed = database.pruneExpiredOtlpEnvelopes(nowMs);
|
|
1897
|
+
if (removed < 100)
|
|
1898
|
+
nextOtlpAgeCheckAtMs = nowMs + SPOOL_CLEANUP_INTERVAL_MS;
|
|
1899
|
+
}
|
|
1900
|
+
if (removed === 0) removed = database.pruneOtlpCapacity(nowMs);
|
|
1901
|
+
otlpPruneTimer = setTimeout(
|
|
1902
|
+
pruneOtlp,
|
|
1903
|
+
removed > 0 ? OTLP_CLEANUP_BATCH_DELAY_MS : SPOOL_CLEANUP_INTERVAL_MS
|
|
1904
|
+
);
|
|
1905
|
+
otlpPruneTimer.unref();
|
|
1906
|
+
} catch (error) {
|
|
1907
|
+
scheduleFatalShutdown(error);
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
pruneOtlp();
|
|
1871
1911
|
hookSyncLoop = createHookSyncLoop({
|
|
1872
1912
|
database,
|
|
1873
1913
|
version: packageVersion
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fml-inc/fml",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.10-dev.1",
|
|
4
4
|
"description": "FML CLI and agent tools",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"open": "^10.1.0",
|
|
34
34
|
"ora": "^8.2.0",
|
|
35
35
|
"picocolors": "^1.1.1",
|
|
36
|
-
"smol-toml": "1.7.
|
|
36
|
+
"smol-toml": "1.7.1",
|
|
37
37
|
"zod": "4.1.12"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|