@runtimescope/collector 0.10.8 → 0.10.9
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/chunk-CYDXIW4L.js +38 -0
- package/dist/chunk-CYDXIW4L.js.map +1 -0
- package/dist/{chunk-TT3VVKUE.js → chunk-YAXXO3X4.js} +96 -46
- package/dist/chunk-YAXXO3X4.js.map +1 -0
- package/dist/dashboard.js +28 -24
- package/dist/dashboard.js.map +1 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/standalone.js +31 -19
- package/dist/standalone.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-TT3VVKUE.js.map +0 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// src/log.ts
|
|
2
|
+
function formatArg(a) {
|
|
3
|
+
if (typeof a === "string") return a;
|
|
4
|
+
if (a instanceof Error) return a.stack ?? a.message;
|
|
5
|
+
if (a === null || a === void 0) return String(a);
|
|
6
|
+
if (typeof a === "object") {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(a);
|
|
9
|
+
} catch {
|
|
10
|
+
return String(a);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return String(a);
|
|
14
|
+
}
|
|
15
|
+
function write(stream, args) {
|
|
16
|
+
try {
|
|
17
|
+
if (!stream.writable) {
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const formatted = args.map(formatArg).join(" ");
|
|
21
|
+
stream.write(formatted + "\n");
|
|
22
|
+
} catch {
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
var safeLog = {
|
|
27
|
+
error(...args) {
|
|
28
|
+
write(process.stderr, args);
|
|
29
|
+
},
|
|
30
|
+
warn(...args) {
|
|
31
|
+
write(process.stderr, args);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export {
|
|
36
|
+
safeLog
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=chunk-CYDXIW4L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/log.ts"],"sourcesContent":["// ============================================================\n// safeLog — EPIPE-resilient stderr writes\n//\n// Why this exists:\n// v0.10.8 fixed a class of bug where the npx-spawned MCP server got\n// reparented to init when Claude Code exited, then entered a tight\n// uncaughtException → console.error → uncaughtException loop against\n// a closed stderr pipe. The fix wrapped the two handler entry points,\n// but the codebase has 100+ raw `console.error` / `process.stderr.write`\n// sites in ordinary code paths (PM discovery, otel exporter, WAL\n// recovery, etc.). Any one of those can re-trigger the same loop if\n// it fires while stderr is broken — the v0.10.8 fix is necessary but\n// not sufficient.\n//\n// See audit: docs/audits/0001-collector-process-lifetime.md F1\n// See ADR: docs/decisions/0001-audit-then-rust.md\n//\n// Contract:\n// - Synchronous writes to stderr, formatted similarly to console.error.\n// - If stderr is unwritable (parent died, pipe closed), the call exits\n// the process with code 1 rather than throwing. We cannot meaningfully\n// surface anything to a dead pipe; the only behavior that doesn't\n// cascade into a CPU-pegged loop is to bail.\n// - Drop-in replacement for console.error / console.warn — supports\n// multi-arg format (`safeLog.error('foo:', err.message)`).\n// - Never throws; never re-enters. Safe to call from inside an\n// uncaughtException handler.\n// ============================================================\n\nfunction formatArg(a: unknown): string {\n if (typeof a === 'string') return a;\n if (a instanceof Error) return a.stack ?? a.message;\n if (a === null || a === undefined) return String(a);\n if (typeof a === 'object') {\n try {\n return JSON.stringify(a);\n } catch {\n // Circular refs or non-serializable values — fall back to toString.\n return String(a);\n }\n }\n return String(a);\n}\n\nfunction write(stream: NodeJS.WriteStream, args: unknown[]): void {\n try {\n if (!stream.writable) {\n // Pipe is gone. Don't try to log anything else, just exit.\n // We use code 1 to distinguish \"I exited because my stderr broke\"\n // from \"I exited because my stdin closed\" (which uses code 0).\n process.exit(1);\n }\n const formatted = args.map(formatArg).join(' ');\n stream.write(formatted + '\\n');\n } catch {\n // The write itself threw (EPIPE landed between the writable check and\n // the write). Same conclusion: exit, do not loop.\n process.exit(1);\n }\n}\n\nexport const safeLog = {\n error(...args: unknown[]): void {\n write(process.stderr, args);\n },\n warn(...args: unknown[]): void {\n write(process.stderr, args);\n },\n};\n"],"mappings":";AA6BA,SAAS,UAAU,GAAoB;AACrC,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,MAAI,aAAa,MAAO,QAAO,EAAE,SAAS,EAAE;AAC5C,MAAI,MAAM,QAAQ,MAAM,OAAW,QAAO,OAAO,CAAC;AAClD,MAAI,OAAO,MAAM,UAAU;AACzB,QAAI;AACF,aAAO,KAAK,UAAU,CAAC;AAAA,IACzB,QAAQ;AAEN,aAAO,OAAO,CAAC;AAAA,IACjB;AAAA,EACF;AACA,SAAO,OAAO,CAAC;AACjB;AAEA,SAAS,MAAM,QAA4B,MAAuB;AAChE,MAAI;AACF,QAAI,CAAC,OAAO,UAAU;AAIpB,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,UAAM,YAAY,KAAK,IAAI,SAAS,EAAE,KAAK,GAAG;AAC9C,WAAO,MAAM,YAAY,IAAI;AAAA,EAC/B,QAAQ;AAGN,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEO,IAAM,UAAU;AAAA,EACrB,SAAS,MAAuB;AAC9B,UAAM,QAAQ,QAAQ,IAAI;AAAA,EAC5B;AAAA,EACA,QAAQ,MAAuB;AAC7B,UAAM,QAAQ,QAAQ,IAAI;AAAA,EAC5B;AACF;","names":[]}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
safeLog
|
|
3
|
+
} from "./chunk-CYDXIW4L.js";
|
|
1
4
|
import {
|
|
2
5
|
__require
|
|
3
6
|
} from "./chunk-UP2VWCW5.js";
|
|
@@ -483,6 +486,7 @@ var SqliteStore = class _SqliteStore {
|
|
|
483
486
|
this.db = this.openDatabase(options);
|
|
484
487
|
const flushInterval = options.flushIntervalMs ?? 100;
|
|
485
488
|
this.flushTimer = setInterval(() => this.flush(), flushInterval);
|
|
489
|
+
this.flushTimer.unref?.();
|
|
486
490
|
}
|
|
487
491
|
openDatabase(options) {
|
|
488
492
|
const Db = getDatabase();
|
|
@@ -500,14 +504,14 @@ var SqliteStore = class _SqliteStore {
|
|
|
500
504
|
this.prepareStatements(db);
|
|
501
505
|
return db;
|
|
502
506
|
} catch (err) {
|
|
503
|
-
|
|
507
|
+
safeLog.error(
|
|
504
508
|
`[RuntimeScope] SQLite database corrupt or unreadable (${err.message}), recreating...`
|
|
505
509
|
);
|
|
506
510
|
try {
|
|
507
511
|
if (existsSync(options.dbPath)) {
|
|
508
512
|
const backupPath = `${options.dbPath}.corrupt.${Date.now()}`;
|
|
509
513
|
renameSync(options.dbPath, backupPath);
|
|
510
|
-
|
|
514
|
+
safeLog.error(`[RuntimeScope] Renamed corrupt DB to ${backupPath}`);
|
|
511
515
|
}
|
|
512
516
|
for (const suffix of ["-wal", "-shm"]) {
|
|
513
517
|
const p = options.dbPath + suffix;
|
|
@@ -626,7 +630,7 @@ var SqliteStore = class _SqliteStore {
|
|
|
626
630
|
try {
|
|
627
631
|
insertMany();
|
|
628
632
|
} catch (err) {
|
|
629
|
-
|
|
633
|
+
safeLog.error("[RuntimeScope] SQLite flush error:", err.message);
|
|
630
634
|
}
|
|
631
635
|
}
|
|
632
636
|
saveSession(info) {
|
|
@@ -890,7 +894,7 @@ function isSqliteAvailable() {
|
|
|
890
894
|
_available = true;
|
|
891
895
|
} catch {
|
|
892
896
|
_available = false;
|
|
893
|
-
|
|
897
|
+
safeLog.error(
|
|
894
898
|
"[RuntimeScope] better-sqlite3 is not available \u2014 running in memory-only mode.\n[RuntimeScope] Historical data persistence is disabled. To fix this:\n[RuntimeScope] macOS: xcode-select --install\n[RuntimeScope] Ubuntu: sudo apt-get install build-essential python3\n[RuntimeScope] Windows: npm install --global windows-build-tools\n[RuntimeScope] Then run: npm rebuild better-sqlite3"
|
|
895
899
|
);
|
|
896
900
|
}
|
|
@@ -1503,7 +1507,7 @@ var OtelExporter = class {
|
|
|
1503
1507
|
const now = Date.now();
|
|
1504
1508
|
if (now - this.lastFailureLog < 6e4) return;
|
|
1505
1509
|
this.lastFailureLog = now;
|
|
1506
|
-
|
|
1510
|
+
safeLog.error(`[RuntimeScope] ${msg}`);
|
|
1507
1511
|
}
|
|
1508
1512
|
};
|
|
1509
1513
|
function traceIdFromSession(sessionId) {
|
|
@@ -1644,7 +1648,7 @@ var SessionRateLimiter = class {
|
|
|
1644
1648
|
maybeWarn(sessionId, w, now) {
|
|
1645
1649
|
if (now - w.lastWarning >= 6e4) {
|
|
1646
1650
|
w.lastWarning = now;
|
|
1647
|
-
|
|
1651
|
+
safeLog.error(
|
|
1648
1652
|
`[RuntimeScope] Rate limiting session ${sessionId.slice(0, 8)}... (dropped ${this._droppedTotal} total)`
|
|
1649
1653
|
);
|
|
1650
1654
|
}
|
|
@@ -1694,6 +1698,15 @@ var CollectorServer = class {
|
|
|
1694
1698
|
// RSS. ensureSqliteStore() updates this on every access.
|
|
1695
1699
|
sqliteStoreLastAccess = /* @__PURE__ */ new Map();
|
|
1696
1700
|
wals = /* @__PURE__ */ new Map();
|
|
1701
|
+
// Per-project last-access timestamps for WAL handles, mirroring
|
|
1702
|
+
// sqliteStoreLastAccess. The audit (docs/audits/0001 §F3) found that
|
|
1703
|
+
// WAL handles followed the same "open on first use, only closed on
|
|
1704
|
+
// stop()" lifetime as the SQLite stores did pre-v0.10.8 — fewer bytes
|
|
1705
|
+
// per handle than SQLite, but each holds an open FD, so on machines
|
|
1706
|
+
// with many projects the leak is real (ulimit risk + small RSS drift).
|
|
1707
|
+
// ensureWal() updates this on every access; the sqliteEvictTimer sweep
|
|
1708
|
+
// evicts idle handles in the same pass.
|
|
1709
|
+
walsLastAccess = /* @__PURE__ */ new Map();
|
|
1697
1710
|
ready = false;
|
|
1698
1711
|
metrics = new MetricsRegistry();
|
|
1699
1712
|
startedAt = Date.now();
|
|
@@ -1717,6 +1730,7 @@ var CollectorServer = class {
|
|
|
1717
1730
|
}
|
|
1718
1731
|
if (this.rateLimiter.isEnabled()) {
|
|
1719
1732
|
this.pruneTimer = setInterval(() => this.rateLimiter.prune(), 6e4);
|
|
1733
|
+
this.pruneTimer.unref?.();
|
|
1720
1734
|
}
|
|
1721
1735
|
this.counters = {
|
|
1722
1736
|
eventsTotal: this.metrics.counter(
|
|
@@ -1774,7 +1788,7 @@ var CollectorServer = class {
|
|
|
1774
1788
|
this.store.onEvent((event) => {
|
|
1775
1789
|
this.otelExporter?.ingest(event);
|
|
1776
1790
|
});
|
|
1777
|
-
|
|
1791
|
+
safeLog.error(
|
|
1778
1792
|
`[RuntimeScope] OpenTelemetry export enabled \u2192 ${otelOptions.endpoint}`
|
|
1779
1793
|
);
|
|
1780
1794
|
}
|
|
@@ -1802,6 +1816,24 @@ var CollectorServer = class {
|
|
|
1802
1816
|
getSqliteStores() {
|
|
1803
1817
|
return this.sqliteStores;
|
|
1804
1818
|
}
|
|
1819
|
+
/**
|
|
1820
|
+
* Diagnostic counters for currently-open per-project resource handles
|
|
1821
|
+
* and pending bidirectional commands. Exposed for tests + future
|
|
1822
|
+
* tray-app health surfaces.
|
|
1823
|
+
*
|
|
1824
|
+
* - `sqliteStores` / `wals`: open handle counts. The eviction sweep
|
|
1825
|
+
* (5-minute idle in production, env-overridable for tests) drives
|
|
1826
|
+
* these down to zero for idle projects and back up on reconnect.
|
|
1827
|
+
* - `pendingCommands`: in-flight WS commands (server → SDK). Settles
|
|
1828
|
+
* on response or via the per-command timeout — see audit F5.
|
|
1829
|
+
*/
|
|
1830
|
+
getOpenHandleCounts() {
|
|
1831
|
+
return {
|
|
1832
|
+
sqliteStores: this.sqliteStores.size,
|
|
1833
|
+
wals: this.wals.size,
|
|
1834
|
+
pendingCommands: this.pendingCommands.size
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1805
1837
|
getRateLimiter() {
|
|
1806
1838
|
return this.rateLimiter;
|
|
1807
1839
|
}
|
|
@@ -1848,7 +1880,7 @@ var CollectorServer = class {
|
|
|
1848
1880
|
sqliteBytes = sqliteStore.snapshotTo(sqlitePath);
|
|
1849
1881
|
eventCount = sqliteStore.getEventCount({ project: projectName });
|
|
1850
1882
|
} catch (err) {
|
|
1851
|
-
|
|
1883
|
+
safeLog.error(
|
|
1852
1884
|
`[RuntimeScope] Snapshot of "${projectName}" SQLite failed:`,
|
|
1853
1885
|
err.message
|
|
1854
1886
|
);
|
|
@@ -1860,7 +1892,7 @@ var CollectorServer = class {
|
|
|
1860
1892
|
try {
|
|
1861
1893
|
walBytes = wal.snapshotTo(join2(projectDir, "wal"));
|
|
1862
1894
|
} catch (err) {
|
|
1863
|
-
|
|
1895
|
+
safeLog.error(
|
|
1864
1896
|
`[RuntimeScope] Snapshot of "${projectName}" WAL failed:`,
|
|
1865
1897
|
err.message
|
|
1866
1898
|
);
|
|
@@ -1889,7 +1921,7 @@ var CollectorServer = class {
|
|
|
1889
1921
|
try {
|
|
1890
1922
|
this.runStartupRecovery();
|
|
1891
1923
|
} catch (err) {
|
|
1892
|
-
|
|
1924
|
+
safeLog.error("[RuntimeScope] Startup recovery failed (non-fatal):", err.message);
|
|
1893
1925
|
}
|
|
1894
1926
|
}
|
|
1895
1927
|
this.ready = true;
|
|
@@ -1929,7 +1961,7 @@ var CollectorServer = class {
|
|
|
1929
1961
|
try {
|
|
1930
1962
|
store.close();
|
|
1931
1963
|
} catch (e) {
|
|
1932
|
-
|
|
1964
|
+
safeLog.error(
|
|
1933
1965
|
`[RuntimeScope] Failed to close idle SQLite store "${projectName}":`,
|
|
1934
1966
|
e.message
|
|
1935
1967
|
);
|
|
@@ -1939,6 +1971,22 @@ var CollectorServer = class {
|
|
|
1939
1971
|
this.sqliteStoreLastAccess.delete(projectName);
|
|
1940
1972
|
this.store.clearSqliteStoreIfMatches(projectName);
|
|
1941
1973
|
}
|
|
1974
|
+
for (const [projectName, wal] of this.wals) {
|
|
1975
|
+
if (liveProjects.has(projectName)) continue;
|
|
1976
|
+
const lastAccess = this.walsLastAccess.get(projectName) ?? 0;
|
|
1977
|
+
if (now - lastAccess < IDLE_TIMEOUT_MS) continue;
|
|
1978
|
+
try {
|
|
1979
|
+
wal.close();
|
|
1980
|
+
} catch (e) {
|
|
1981
|
+
safeLog.error(
|
|
1982
|
+
`[RuntimeScope] Failed to close idle WAL "${projectName}":`,
|
|
1983
|
+
e.message
|
|
1984
|
+
);
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
this.wals.delete(projectName);
|
|
1988
|
+
this.walsLastAccess.delete(projectName);
|
|
1989
|
+
}
|
|
1942
1990
|
}, SWEEP_INTERVAL_MS);
|
|
1943
1991
|
this.sqliteEvictTimer.unref?.();
|
|
1944
1992
|
}
|
|
@@ -1990,7 +2038,7 @@ var CollectorServer = class {
|
|
|
1990
2038
|
}
|
|
1991
2039
|
}
|
|
1992
2040
|
if (walReplayed > 0 || warmed > 0) {
|
|
1993
|
-
|
|
2041
|
+
safeLog.error(
|
|
1994
2042
|
`[RuntimeScope] Recovery: ${walReplayed} WAL events replayed, ${warmed} events warmed into ring buffer.`
|
|
1995
2043
|
);
|
|
1996
2044
|
}
|
|
@@ -2006,7 +2054,7 @@ var CollectorServer = class {
|
|
|
2006
2054
|
this.setupConnectionHandler(wss);
|
|
2007
2055
|
this.setupPersistentErrorHandler(wss);
|
|
2008
2056
|
this.startHeartbeat(wss);
|
|
2009
|
-
|
|
2057
|
+
safeLog.error(`[RuntimeScope] Collector listening on wss://${host}:${port}`);
|
|
2010
2058
|
resolve2();
|
|
2011
2059
|
});
|
|
2012
2060
|
httpsServer.on("error", (err) => {
|
|
@@ -2021,7 +2069,7 @@ var CollectorServer = class {
|
|
|
2021
2069
|
this.setupConnectionHandler(wss);
|
|
2022
2070
|
this.setupPersistentErrorHandler(wss);
|
|
2023
2071
|
this.startHeartbeat(wss);
|
|
2024
|
-
|
|
2072
|
+
safeLog.error(`[RuntimeScope] Collector listening on ws://${host}:${port}`);
|
|
2025
2073
|
resolve2();
|
|
2026
2074
|
});
|
|
2027
2075
|
wss.on("error", (err) => {
|
|
@@ -2034,12 +2082,12 @@ var CollectorServer = class {
|
|
|
2034
2082
|
handleStartError(err, port, host, retriesLeft, retryDelayMs, tls, resolve2, reject) {
|
|
2035
2083
|
if (err.code === "EADDRINUSE" && retriesLeft > 0) {
|
|
2036
2084
|
const nextPort = port + 1;
|
|
2037
|
-
|
|
2085
|
+
safeLog.error(
|
|
2038
2086
|
`[RuntimeScope] Port ${port} in use, trying ${nextPort}...`
|
|
2039
2087
|
);
|
|
2040
2088
|
this.tryStart(nextPort, host, retriesLeft - 1, retryDelayMs, tls).then(resolve2).catch(reject);
|
|
2041
2089
|
} else {
|
|
2042
|
-
|
|
2090
|
+
safeLog.error("[RuntimeScope] WebSocket server error:", err.message);
|
|
2043
2091
|
reject(err);
|
|
2044
2092
|
}
|
|
2045
2093
|
}
|
|
@@ -2055,9 +2103,9 @@ var CollectorServer = class {
|
|
|
2055
2103
|
sqliteStore = new SqliteStore({ dbPath });
|
|
2056
2104
|
this.sqliteStores.set(projectName, sqliteStore);
|
|
2057
2105
|
this.store.setSqliteStore(sqliteStore, projectName);
|
|
2058
|
-
|
|
2106
|
+
safeLog.error(`[RuntimeScope] SQLite store opened for project "${projectName}"`);
|
|
2059
2107
|
} catch (err) {
|
|
2060
|
-
|
|
2108
|
+
safeLog.error(
|
|
2061
2109
|
`[RuntimeScope] Failed to open SQLite for "${projectName}":`,
|
|
2062
2110
|
err.message
|
|
2063
2111
|
);
|
|
@@ -2084,6 +2132,7 @@ var CollectorServer = class {
|
|
|
2084
2132
|
*/
|
|
2085
2133
|
ensureWal(projectName) {
|
|
2086
2134
|
if (!this.projectManager) return null;
|
|
2135
|
+
this.walsLastAccess.set(projectName, Date.now());
|
|
2087
2136
|
let wal = this.wals.get(projectName);
|
|
2088
2137
|
if (wal) return wal;
|
|
2089
2138
|
const dir = this.walDirFor(projectName);
|
|
@@ -2094,7 +2143,7 @@ var CollectorServer = class {
|
|
|
2094
2143
|
this.wals.set(projectName, wal);
|
|
2095
2144
|
return wal;
|
|
2096
2145
|
} catch (err) {
|
|
2097
|
-
|
|
2146
|
+
safeLog.error(
|
|
2098
2147
|
`[RuntimeScope] Failed to open WAL for "${projectName}":`,
|
|
2099
2148
|
err.message
|
|
2100
2149
|
);
|
|
@@ -2131,7 +2180,7 @@ var CollectorServer = class {
|
|
|
2131
2180
|
if (sqliteStore && replayed > 0) {
|
|
2132
2181
|
sqliteStore.flush();
|
|
2133
2182
|
for (const file of files) Wal.deleteSealed(file);
|
|
2134
|
-
|
|
2183
|
+
safeLog.error(
|
|
2135
2184
|
`[RuntimeScope] WAL recovery: replayed ${replayed} events for "${projectName}"`
|
|
2136
2185
|
);
|
|
2137
2186
|
}
|
|
@@ -2151,7 +2200,7 @@ var CollectorServer = class {
|
|
|
2151
2200
|
/** Catch runtime errors on the WSS so an unhandled error doesn't crash the process */
|
|
2152
2201
|
setupPersistentErrorHandler(wss) {
|
|
2153
2202
|
wss.on("error", (err) => {
|
|
2154
|
-
|
|
2203
|
+
safeLog.error("[RuntimeScope] WebSocket server runtime error:", err.message);
|
|
2155
2204
|
});
|
|
2156
2205
|
}
|
|
2157
2206
|
/** Ping all connected clients every 15s — terminate those that don't respond */
|
|
@@ -2167,6 +2216,7 @@ var CollectorServer = class {
|
|
|
2167
2216
|
ws.ping();
|
|
2168
2217
|
}
|
|
2169
2218
|
}, 15e3);
|
|
2219
|
+
this.heartbeatTimer.unref?.();
|
|
2170
2220
|
}
|
|
2171
2221
|
setupConnectionHandler(wss) {
|
|
2172
2222
|
wss.on("connection", (ws) => {
|
|
@@ -2201,7 +2251,7 @@ var CollectorServer = class {
|
|
|
2201
2251
|
const msg = JSON.parse(data.toString());
|
|
2202
2252
|
this.handleMessage(ws, msg);
|
|
2203
2253
|
} catch {
|
|
2204
|
-
|
|
2254
|
+
safeLog.error("[RuntimeScope] Malformed WebSocket message, ignoring");
|
|
2205
2255
|
}
|
|
2206
2256
|
});
|
|
2207
2257
|
ws.on("close", (code) => {
|
|
@@ -2214,7 +2264,7 @@ var CollectorServer = class {
|
|
|
2214
2264
|
if (sqliteStore) {
|
|
2215
2265
|
sqliteStore.updateSessionDisconnected(clientInfo.sessionId, Date.now());
|
|
2216
2266
|
}
|
|
2217
|
-
|
|
2267
|
+
safeLog.error(`[RuntimeScope] Session ${clientInfo.sessionId} disconnected`);
|
|
2218
2268
|
for (const cb of this.disconnectCallbacks) {
|
|
2219
2269
|
try {
|
|
2220
2270
|
cb(clientInfo.sessionId, clientInfo.projectName, clientInfo.projectId);
|
|
@@ -2225,7 +2275,7 @@ var CollectorServer = class {
|
|
|
2225
2275
|
this.clients.delete(ws);
|
|
2226
2276
|
});
|
|
2227
2277
|
ws.on("error", (err) => {
|
|
2228
|
-
|
|
2278
|
+
safeLog.error("[RuntimeScope] WebSocket client error:", err.message);
|
|
2229
2279
|
});
|
|
2230
2280
|
});
|
|
2231
2281
|
}
|
|
@@ -2299,7 +2349,7 @@ var CollectorServer = class {
|
|
|
2299
2349
|
connectedAt: msg.timestamp,
|
|
2300
2350
|
sdkVersion: payload.sdkVersion
|
|
2301
2351
|
});
|
|
2302
|
-
|
|
2352
|
+
safeLog.error(
|
|
2303
2353
|
`[RuntimeScope] Session ${payload.sessionId} connected (${payload.appName} v${payload.sdkVersion})`
|
|
2304
2354
|
);
|
|
2305
2355
|
for (const cb of this.connectCallbacks) {
|
|
@@ -2334,7 +2384,7 @@ var CollectorServer = class {
|
|
|
2334
2384
|
wal.append(accepted);
|
|
2335
2385
|
wal.commit();
|
|
2336
2386
|
} catch (err) {
|
|
2337
|
-
|
|
2387
|
+
safeLog.error("[RuntimeScope] WAL append/commit failed:", err.message);
|
|
2338
2388
|
this.counters.eventsDropped.inc(accepted.length, { reason: "wal_backpressure" });
|
|
2339
2389
|
}
|
|
2340
2390
|
}
|
|
@@ -2345,7 +2395,7 @@ var CollectorServer = class {
|
|
|
2345
2395
|
try {
|
|
2346
2396
|
this.checkpointWal(clientInfo.projectName, wal);
|
|
2347
2397
|
} catch (err) {
|
|
2348
|
-
|
|
2398
|
+
safeLog.error("[RuntimeScope] WAL checkpoint failed:", err.message);
|
|
2349
2399
|
}
|
|
2350
2400
|
}
|
|
2351
2401
|
break;
|
|
@@ -2457,14 +2507,14 @@ var CollectorServer = class {
|
|
|
2457
2507
|
try {
|
|
2458
2508
|
wal.close();
|
|
2459
2509
|
} catch {
|
|
2460
|
-
|
|
2510
|
+
safeLog.error(`[RuntimeScope] WAL close error for "${name}" (non-fatal)`);
|
|
2461
2511
|
}
|
|
2462
2512
|
}
|
|
2463
2513
|
this.wals.clear();
|
|
2464
2514
|
for (const [name, sqliteStore] of this.sqliteStores) {
|
|
2465
2515
|
try {
|
|
2466
2516
|
sqliteStore.close();
|
|
2467
|
-
|
|
2517
|
+
safeLog.error(`[RuntimeScope] SQLite store closed for "${name}"`);
|
|
2468
2518
|
} catch {
|
|
2469
2519
|
}
|
|
2470
2520
|
}
|
|
@@ -2472,7 +2522,7 @@ var CollectorServer = class {
|
|
|
2472
2522
|
if (this.wss) {
|
|
2473
2523
|
this.wss.close();
|
|
2474
2524
|
this.wss = null;
|
|
2475
|
-
|
|
2525
|
+
safeLog.error("[RuntimeScope] Collector stopped");
|
|
2476
2526
|
}
|
|
2477
2527
|
this.ready = false;
|
|
2478
2528
|
}
|
|
@@ -5047,7 +5097,7 @@ var HttpServer = class {
|
|
|
5047
5097
|
} catch (err) {
|
|
5048
5098
|
const isAddrInUse = err.code === "EADDRINUSE";
|
|
5049
5099
|
if (isAddrInUse && attempt < maxRetries) {
|
|
5050
|
-
|
|
5100
|
+
safeLog.error(`[RuntimeScope] HTTP port ${port} in use, trying ${port + 1}...`);
|
|
5051
5101
|
continue;
|
|
5052
5102
|
}
|
|
5053
5103
|
throw err;
|
|
@@ -5081,7 +5131,7 @@ var HttpServer = class {
|
|
|
5081
5131
|
this.activePort = boundPort;
|
|
5082
5132
|
this.startedAt = Date.now();
|
|
5083
5133
|
const proto = tls ? "https" : "http";
|
|
5084
|
-
|
|
5134
|
+
safeLog.error(`[RuntimeScope] HTTP API listening on ${proto}://${host}:${boundPort}`);
|
|
5085
5135
|
resolve2();
|
|
5086
5136
|
});
|
|
5087
5137
|
server.on("error", (err) => {
|
|
@@ -5113,7 +5163,7 @@ var HttpServer = class {
|
|
|
5113
5163
|
return new Promise((resolve2) => {
|
|
5114
5164
|
this.server.close(() => {
|
|
5115
5165
|
this.server = null;
|
|
5116
|
-
|
|
5166
|
+
safeLog.error("[RuntimeScope] HTTP API stopped");
|
|
5117
5167
|
resolve2();
|
|
5118
5168
|
});
|
|
5119
5169
|
});
|
|
@@ -6996,7 +7046,7 @@ var ProjectDiscovery = class {
|
|
|
6996
7046
|
return mergeResults(claudeResult, runtimeResult);
|
|
6997
7047
|
} catch (err) {
|
|
6998
7048
|
const msg = err instanceof Error ? err.message : String(err);
|
|
6999
|
-
|
|
7049
|
+
safeLog.error(`${LOG_PREFIX} Fatal discovery error: ${msg}`);
|
|
7000
7050
|
result.errors.push(`Fatal discovery error: ${msg}`);
|
|
7001
7051
|
return result;
|
|
7002
7052
|
}
|
|
@@ -7024,7 +7074,7 @@ var ProjectDiscovery = class {
|
|
|
7024
7074
|
entries = dirEntries.filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7025
7075
|
} catch (err) {
|
|
7026
7076
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7027
|
-
|
|
7077
|
+
safeLog.error(`${LOG_PREFIX} Failed to read Claude projects dir: ${msg}`);
|
|
7028
7078
|
result.errors.push(`Failed to read Claude projects dir: ${msg}`);
|
|
7029
7079
|
return result;
|
|
7030
7080
|
}
|
|
@@ -7033,7 +7083,7 @@ var ProjectDiscovery = class {
|
|
|
7033
7083
|
await this.processClaudeProject(key, result);
|
|
7034
7084
|
} catch (err) {
|
|
7035
7085
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7036
|
-
|
|
7086
|
+
safeLog.error(`${LOG_PREFIX} Error processing Claude project ${key}: ${msg}`);
|
|
7037
7087
|
result.errors.push(`Claude project ${key}: ${msg}`);
|
|
7038
7088
|
}
|
|
7039
7089
|
}
|
|
@@ -7100,13 +7150,13 @@ var ProjectDiscovery = class {
|
|
|
7100
7150
|
}
|
|
7101
7151
|
} catch (err) {
|
|
7102
7152
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7103
|
-
|
|
7153
|
+
safeLog.error(`${LOG_PREFIX} Error processing RuntimeScope project ${projectName}: ${msg}`);
|
|
7104
7154
|
result.errors.push(`RuntimeScope project ${projectName}: ${msg}`);
|
|
7105
7155
|
}
|
|
7106
7156
|
}
|
|
7107
7157
|
} catch (err) {
|
|
7108
7158
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7109
|
-
|
|
7159
|
+
safeLog.error(`${LOG_PREFIX} Failed to list RuntimeScope projects: ${msg}`);
|
|
7110
7160
|
result.errors.push(`Failed to list RuntimeScope projects: ${msg}`);
|
|
7111
7161
|
}
|
|
7112
7162
|
return result;
|
|
@@ -7119,7 +7169,7 @@ var ProjectDiscovery = class {
|
|
|
7119
7169
|
const existingProjects = await this.pmStore.listProjects();
|
|
7120
7170
|
const project = existingProjects.find((p) => p.id === projectId);
|
|
7121
7171
|
if (!project) {
|
|
7122
|
-
|
|
7172
|
+
safeLog.error(`${LOG_PREFIX} Project not found: ${projectId}`);
|
|
7123
7173
|
return 0;
|
|
7124
7174
|
}
|
|
7125
7175
|
if (!project.claudeProjectKey) {
|
|
@@ -7160,12 +7210,12 @@ var ProjectDiscovery = class {
|
|
|
7160
7210
|
sessionsIndexed++;
|
|
7161
7211
|
} catch (err) {
|
|
7162
7212
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7163
|
-
|
|
7213
|
+
safeLog.error(`${LOG_PREFIX} Error indexing session ${jsonlFile}: ${msg}`);
|
|
7164
7214
|
}
|
|
7165
7215
|
}
|
|
7166
7216
|
} catch (err) {
|
|
7167
7217
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7168
|
-
|
|
7218
|
+
safeLog.error(`${LOG_PREFIX} Error indexing sessions for project ${projectId}: ${msg}`);
|
|
7169
7219
|
}
|
|
7170
7220
|
return sessionsIndexed;
|
|
7171
7221
|
}
|
|
@@ -7283,12 +7333,12 @@ var ProjectDiscovery = class {
|
|
|
7283
7333
|
}
|
|
7284
7334
|
} catch (err) {
|
|
7285
7335
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7286
|
-
|
|
7336
|
+
safeLog.error(`${LOG_PREFIX} Error indexing session ${jsonlFile} in ${claudeKey}: ${msg}`);
|
|
7287
7337
|
}
|
|
7288
7338
|
}
|
|
7289
7339
|
} catch (err) {
|
|
7290
7340
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7291
|
-
|
|
7341
|
+
safeLog.error(`${LOG_PREFIX} Error scanning sessions for ${claudeKey}: ${msg}`);
|
|
7292
7342
|
}
|
|
7293
7343
|
return counts;
|
|
7294
7344
|
}
|
|
@@ -7382,7 +7432,7 @@ var ProjectDiscovery = class {
|
|
|
7382
7432
|
};
|
|
7383
7433
|
} catch (err) {
|
|
7384
7434
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7385
|
-
|
|
7435
|
+
safeLog.error(`${LOG_PREFIX} Failed to parse session ${sessionId}: ${msg}`);
|
|
7386
7436
|
return {
|
|
7387
7437
|
id: sessionId,
|
|
7388
7438
|
projectId,
|
|
@@ -7432,7 +7482,7 @@ var ProjectDiscovery = class {
|
|
|
7432
7482
|
await this.pmStore.upsertCapexEntry(entry);
|
|
7433
7483
|
} catch (err) {
|
|
7434
7484
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7435
|
-
|
|
7485
|
+
safeLog.error(`${LOG_PREFIX} Failed to create CapEx stub for session ${session.id}: ${msg}`);
|
|
7436
7486
|
}
|
|
7437
7487
|
}
|
|
7438
7488
|
};
|
|
@@ -7482,4 +7532,4 @@ export {
|
|
|
7482
7532
|
parseSessionJsonl,
|
|
7483
7533
|
ProjectDiscovery
|
|
7484
7534
|
};
|
|
7485
|
-
//# sourceMappingURL=chunk-
|
|
7535
|
+
//# sourceMappingURL=chunk-YAXXO3X4.js.map
|