@runtimescope/collector 0.10.0 → 0.10.2

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/index.d.ts CHANGED
@@ -915,10 +915,23 @@ declare class SqliteStore {
915
915
  addEvent(event: RuntimeEvent, project: string): void;
916
916
  flush(): void;
917
917
  saveSession(info: SessionInfoExtended): void;
918
+ /**
919
+ * Read every session record stored for a project. Used by the in-memory
920
+ * EventStore at startup to rehydrate the session→projectId map after a
921
+ * crash, so post-recovery `?project_id=...` queries return the right rows.
922
+ * Each row is returned as a `SessionInfo` shape compatible with EventStore.
923
+ */
924
+ getStoredSessions(project: string): SessionInfoExtended[];
918
925
  updateSessionDisconnected(sessionId: string, disconnectedAt: number): void;
919
926
  saveSessionMetrics(sessionId: string, project: string, metrics: unknown, label?: string): void;
920
927
  /** Remove oldest snapshots for a session beyond the retention limit */
921
928
  private pruneSnapshots;
929
+ /**
930
+ * Return the most recent `limit` events for a project, in chronological
931
+ * order (oldest-first). Used at startup to warm the in-memory ring buffer
932
+ * so MCP tools see recent activity immediately after a collector restart.
933
+ */
934
+ getRecentEvents(project: string, limit: number): RuntimeEvent[];
922
935
  getEvents(filter: HistoricalFilter): RuntimeEvent[];
923
936
  getEventCount(filter: HistoricalFilter): number;
924
937
  getSessions(project: string, limit?: number): SessionInfoExtended[];
@@ -929,6 +942,16 @@ declare class SqliteStore {
929
942
  private migrateSessionMetrics;
930
943
  deleteOldEvents(beforeTimestamp: number): number;
931
944
  vacuum(): void;
945
+ /**
946
+ * Atomically copy the live database to `targetPath` using SQLite's
947
+ * `VACUUM INTO`. The destination is a self-contained, defragmented copy of
948
+ * the DB at the moment the statement runs — no readers are blocked beyond
949
+ * the duration of the copy, and the WAL/journal contents are folded in.
950
+ * The target file must not already exist; SQLite refuses to overwrite.
951
+ *
952
+ * Returns the size in bytes of the resulting file.
953
+ */
954
+ snapshotTo(targetPath: string): number;
932
955
  close(): void;
933
956
  }
934
957
 
@@ -973,12 +996,36 @@ declare class EventStore {
973
996
  setRedactor(redactor: Redactor): void;
974
997
  get eventCount(): number;
975
998
  setSqliteStore(store: SqliteStore, project: string): void;
999
+ /**
1000
+ * Pre-load recent events from a SqliteStore into the in-memory ring buffer.
1001
+ * Used at collector startup to make MCP tools immediately useful after a
1002
+ * restart — without this, the buffer is empty until the SDK reconnects and
1003
+ * generates new traffic.
1004
+ *
1005
+ * Also rehydrates the session→projectId map from SqliteStore's `sessions`
1006
+ * table. Without this, queries filtered by `project_id` after a crash
1007
+ * return nothing because the in-memory map is empty: events are tagged
1008
+ * by sessionId, and matchesProjectId() looks up the session record to
1009
+ * find the project. (Pre-fix: only sessions whose `session` event happens
1010
+ * to be in the warmed window got rehydrated, which is rare in long runs.)
1011
+ *
1012
+ * Does NOT propagate to SqliteStore (we just read FROM it). Ring-buffer
1013
+ * eviction handles overflow naturally if multiple projects are warmed.
1014
+ */
1015
+ warmFromSqlite(sqliteStore: SqliteStore, project: string, limit: number): void;
976
1016
  onEvent(callback: (event: RuntimeEvent) => void): void;
977
1017
  removeEventListener(callback: (event: RuntimeEvent) => void): void;
978
1018
  addEvent(event: RuntimeEvent): void;
979
1019
  getNetworkRequests(filter?: NetworkFilter): NetworkEvent[];
980
1020
  getConsoleMessages(filter?: ConsoleFilter): ConsoleEvent[];
981
1021
  getSessionInfo(): SessionInfo[];
1022
+ /**
1023
+ * Fast O(sessions) event count for a specific projectId. Callers that just
1024
+ * want to know "have any events arrived for project X yet?" should use this
1025
+ * instead of `getAllEvents(..., projectId).length`, which allocates and
1026
+ * filters the entire 10K-event ring buffer on every call.
1027
+ */
1028
+ eventCountForProject(projectId: string): number;
982
1029
  markDisconnected(sessionId: string): void;
983
1030
  getEventTimeline(filter?: TimelineFilter): RuntimeEvent[];
984
1031
  getAllEvents(sinceSeconds?: number, sessionId?: string, projectId?: string): RuntimeEvent[];
@@ -1175,6 +1222,166 @@ declare function isValidProjectId(id: string): boolean;
1175
1222
  */
1176
1223
  declare function getOrCreateProjectId(projectManager: ProjectManager, appName: string): string;
1177
1224
 
1225
+ /**
1226
+ * Minimal Prometheus-compatible metrics registry. Zero dependencies — the
1227
+ * exposition format is small enough that pulling in `prom-client` would be
1228
+ * heavier than just emitting the text ourselves.
1229
+ *
1230
+ * Two metric kinds are supported:
1231
+ * - Counter (monotonic) for things like total events, dropped events.
1232
+ * - Gauge (point-in-time) for things like current sessions connected,
1233
+ * ring-buffer occupancy. Gauges accept a `collect` callback so they
1234
+ * compute their value at scrape time rather than tracking state.
1235
+ *
1236
+ * Spec reference: https://prometheus.io/docs/instrumenting/exposition_formats/
1237
+ */
1238
+ type LabelValues = Record<string, string>;
1239
+ interface Sample {
1240
+ labels: LabelValues;
1241
+ value: number;
1242
+ }
1243
+ declare abstract class Metric {
1244
+ readonly name: string;
1245
+ readonly help: string;
1246
+ readonly labelNames: string[];
1247
+ constructor(name: string, help: string, labelNames?: string[]);
1248
+ abstract type(): 'counter' | 'gauge';
1249
+ abstract collect(): Sample[];
1250
+ }
1251
+ declare class Counter extends Metric {
1252
+ private values;
1253
+ type(): 'counter';
1254
+ inc(value?: number, labels?: LabelValues): void;
1255
+ reset(): void;
1256
+ collect(): Sample[];
1257
+ }
1258
+ declare class Gauge extends Metric {
1259
+ private values;
1260
+ private collectFn;
1261
+ type(): 'gauge';
1262
+ /**
1263
+ * Set a fixed value for the given label set. Per the Prometheus spec, gauges
1264
+ * may take any numeric value including +Inf / -Inf / NaN — the renderer
1265
+ * handles formatting. Only actually-not-a-number inputs (`undefined`, etc.)
1266
+ * are silently dropped.
1267
+ */
1268
+ set(value: number, labels?: LabelValues): void;
1269
+ /**
1270
+ * Compute the gauge dynamically at scrape time. Useful for things like
1271
+ * "currently-connected sessions" where caching a stale value is wrong.
1272
+ * Mutually exclusive with `set()` — last writer wins.
1273
+ */
1274
+ setCollect(fn: () => number | Sample[]): void;
1275
+ collect(): Sample[];
1276
+ }
1277
+ declare class MetricsRegistry {
1278
+ private metrics;
1279
+ counter(name: string, help: string, labelNames?: string[]): Counter;
1280
+ gauge(name: string, help: string, labelNames?: string[]): Gauge;
1281
+ /** Serialize every registered metric in Prometheus exposition format. */
1282
+ render(): string;
1283
+ }
1284
+
1285
+ /**
1286
+ * OpenTelemetry exporter — one-way bridge from RuntimeScope events to OTLP/HTTP.
1287
+ *
1288
+ * Each RuntimeScope event maps to one OTel signal:
1289
+ * - `network` → trace span (HTTP client, kind=CLIENT)
1290
+ * - `database` → trace span (DB client, kind=CLIENT)
1291
+ * - `render` → trace span (kind=INTERNAL, attr `react.component`)
1292
+ * - `console` → log record. `level=error` is severity=ERROR, with the
1293
+ * stack trace as an `exception.stacktrace` attribute.
1294
+ * - `performance` → gauge metric (Web Vitals: LCP / FCP / CLS / INP / TTFB).
1295
+ *
1296
+ * State, session, ui-interaction, dom_snapshot, custom, navigation, and recon
1297
+ * events have no clean OTel mapping and are skipped — downstream tools can
1298
+ * still see everything via the RuntimeScope HTTP API directly.
1299
+ *
1300
+ * Session = trace. All events for one `sessionId` share a deterministic 16-byte
1301
+ * trace ID derived from `sha256(sessionId)`, so a downstream OTel UI groups
1302
+ * them. Span IDs are random per event.
1303
+ *
1304
+ * Wire format is OTLP/HTTP with JSON encoding — chosen specifically because it
1305
+ * needs no Protobuf compiler, no deps, and is supported by every modern
1306
+ * collector (otel-collector, Honeycomb refinery, Vector, Datadog, Tempo, etc.).
1307
+ *
1308
+ * Configuration is opt-in via env vars:
1309
+ * - RUNTIMESCOPE_OTEL_ENDPOINT — e.g. "http://localhost:4318" (no path)
1310
+ * - RUNTIMESCOPE_OTEL_HEADERS — "k1=v1,k2=v2" — for bearer tokens etc.
1311
+ * - RUNTIMESCOPE_OTEL_SERVICE_NAME — defaults to "runtimescope-collector"
1312
+ *
1313
+ * Failure semantics: best-effort. We batch in memory, flush on a timer or
1314
+ * size threshold, and drop on POST failure (logging at most once per failure).
1315
+ * If the OTel endpoint is down, RuntimeScope itself is unaffected — that's
1316
+ * the whole point of running this as a sidecar exporter.
1317
+ */
1318
+
1319
+ interface OtelExporterOptions {
1320
+ /** Base URL of the OTel collector — no path. e.g. "http://localhost:4318". */
1321
+ endpoint: string;
1322
+ /** `service.name` resource attribute. Default "runtimescope-collector". */
1323
+ serviceName?: string;
1324
+ /** `service.namespace` resource attribute — useful for projectId. */
1325
+ serviceNamespace?: string;
1326
+ /** Extra HTTP headers (auth tokens, tenant IDs, etc.). */
1327
+ headers?: Record<string, string>;
1328
+ /** Force flush after this many milliseconds even if the buffer is small. Default 10s. */
1329
+ flushIntervalMs?: number;
1330
+ /** Force flush once we've accumulated this many signals. Default 1000. */
1331
+ maxBatchSize?: number;
1332
+ }
1333
+ declare class OtelExporter {
1334
+ private spans;
1335
+ private logs;
1336
+ private metrics;
1337
+ private flushTimer;
1338
+ private closed;
1339
+ private lastFailureLog;
1340
+ private readonly endpoint;
1341
+ private readonly serviceName;
1342
+ private readonly serviceNamespace;
1343
+ private readonly headers;
1344
+ private readonly maxBatchSize;
1345
+ constructor(options: OtelExporterOptions);
1346
+ /** Convert a RuntimeScope event to its OTel signal(s) and buffer. */
1347
+ ingest(event: RuntimeEvent): void;
1348
+ /** POST any buffered signals to the OTLP endpoint. */
1349
+ flush(): Promise<void>;
1350
+ close(): Promise<void>;
1351
+ private networkToSpan;
1352
+ private databaseToSpan;
1353
+ /**
1354
+ * Render events bundle multiple component profiles in one event. We emit
1355
+ * one span per profile so the OTel UI can show each component's render
1356
+ * timing independently — sharing the same trace via the sessionId.
1357
+ */
1358
+ private renderToSpans;
1359
+ /**
1360
+ * `console` events with `level: 'error'` carry stack traces; we surface them
1361
+ * via OTel's exception attributes so log-search backends can show them
1362
+ * inline. Lower-severity console events become plain log records.
1363
+ */
1364
+ private consoleToLog;
1365
+ private performanceToMetric;
1366
+ private resourceAttributes;
1367
+ private wrapSpans;
1368
+ private wrapLogs;
1369
+ private wrapMetrics;
1370
+ private send;
1371
+ /** Rate-limit failure logs to once every 60s — don't spam stderr. */
1372
+ private logFailure;
1373
+ }
1374
+ /**
1375
+ * Build a deterministic 16-byte trace ID from the sessionId so every event
1376
+ * for one session belongs to the same trace in the downstream UI.
1377
+ */
1378
+ declare function traceIdFromSession(sessionId: string): string;
1379
+ declare function randomSpanId(): string;
1380
+ /** Parse `RUNTIMESCOPE_OTEL_HEADERS="k1=v1,k2=v2"` into a header map. */
1381
+ declare function parseOtelHeaders(raw: string | undefined): Record<string, string>;
1382
+ /** Build options from environment variables. Returns null if OTel is not configured. */
1383
+ declare function otelOptionsFromEnv(): OtelExporterOptions | null;
1384
+
1178
1385
  interface RateLimitConfig {
1179
1386
  maxEventsPerSecond?: number;
1180
1387
  maxEventsPerMinute?: number;
@@ -1212,6 +1419,22 @@ declare function loadTlsOptions(config: TlsConfig): SecureContextOptions;
1212
1419
  */
1213
1420
  declare function resolveTlsConfig(): TlsConfig | null;
1214
1421
 
1422
+ interface SnapshotResult {
1423
+ /** Absolute path to the snapshot directory. */
1424
+ path: string;
1425
+ /** ISO-like timestamp embedded in the directory name. */
1426
+ timestamp: string;
1427
+ /** Per-project sizes + event counts. */
1428
+ projects: {
1429
+ name: string;
1430
+ sqliteBytes: number;
1431
+ walBytes: number;
1432
+ eventCount: number;
1433
+ }[];
1434
+ /** Total bytes written across all projects. */
1435
+ totalBytes: number;
1436
+ }
1437
+
1215
1438
  interface CollectorServerOptions {
1216
1439
  port?: number;
1217
1440
  host?: string;
@@ -1222,6 +1445,13 @@ interface CollectorServerOptions {
1222
1445
  authManager?: AuthManager;
1223
1446
  rateLimits?: RateLimitConfig;
1224
1447
  tls?: TlsConfig;
1448
+ /**
1449
+ * Optional OpenTelemetry exporter config. When set (or when
1450
+ * `RUNTIMESCOPE_OTEL_ENDPOINT` is in the environment), every event the
1451
+ * store accepts is converted into an OTLP signal and shipped to the
1452
+ * configured endpoint. Failures are logged but never break ingestion.
1453
+ */
1454
+ otel?: OtelExporterOptions;
1225
1455
  }
1226
1456
  declare class CollectorServer {
1227
1457
  private wss;
@@ -1233,6 +1463,12 @@ declare class CollectorServer {
1233
1463
  private pendingHandshakes;
1234
1464
  private pendingCommands;
1235
1465
  private sqliteStores;
1466
+ private wals;
1467
+ private ready;
1468
+ private metrics;
1469
+ private startedAt;
1470
+ private counters;
1471
+ private otelExporter;
1236
1472
  private connectCallbacks;
1237
1473
  private disconnectCallbacks;
1238
1474
  private pruneTimer;
@@ -1240,6 +1476,8 @@ declare class CollectorServer {
1240
1476
  private tlsConfig;
1241
1477
  private pmStore;
1242
1478
  constructor(options?: CollectorServerOptions);
1479
+ /** Public access to the metrics registry — HttpServer renders this at /metrics. */
1480
+ getMetricsRegistry(): MetricsRegistry;
1243
1481
  getStore(): EventStore;
1244
1482
  getPort(): number | null;
1245
1483
  getClientCount(): number;
@@ -1251,10 +1489,55 @@ declare class CollectorServer {
1251
1489
  setPmStore(pmStore: PmStoreLike | null): void;
1252
1490
  onConnect(cb: (sessionId: string, projectName: string, projectId?: string) => void): void;
1253
1491
  onDisconnect(cb: (sessionId: string, projectName: string, projectId?: string) => void): void;
1492
+ /** True after start() finishes recovery. False during startup or after stop(). */
1493
+ isReady(): boolean;
1494
+ /**
1495
+ * Snapshot every project's SQLite DB and WAL into a fresh directory under
1496
+ * `<runtimescope-root>/snapshots/<ISO>/`. Atomic via SQLite's `VACUUM INTO`;
1497
+ * non-blocking for ongoing event ingestion (the live DB keeps accepting
1498
+ * writes during the copy).
1499
+ *
1500
+ * Returns metadata for the admin endpoint to serialize.
1501
+ */
1502
+ createSnapshot(): SnapshotResult;
1254
1503
  start(options?: CollectorServerOptions): Promise<void>;
1504
+ /**
1505
+ * On collector startup, for each known project:
1506
+ * 1. Replay any sealed/active WAL files into SqliteStore (mirror of the
1507
+ * lazy recovery in `ensureWal`, but proactive — handles the case where
1508
+ * a crashed project never reconnects).
1509
+ * 2. Warm the in-memory ring buffer with recent events from SqliteStore so
1510
+ * MCP tools see history immediately, not just events from the next
1511
+ * session that connects.
1512
+ *
1513
+ * Runs synchronously — better-sqlite3 is sync — so callers can `await
1514
+ * collector.start()` and trust the buffer is hot when it returns.
1515
+ */
1516
+ private runStartupRecovery;
1255
1517
  private tryStart;
1256
1518
  private handleStartError;
1257
1519
  private ensureSqliteStore;
1520
+ private walDirFor;
1521
+ /**
1522
+ * Open (or return) the WAL for a project. Every event ingested for this
1523
+ * project is first appended + fsync'd here before being pushed to the ring
1524
+ * buffer and SqliteStore, so a crash between receipt and SqliteStore flush
1525
+ * doesn't lose acknowledged events.
1526
+ */
1527
+ private ensureWal;
1528
+ /**
1529
+ * Replay any sealed or non-empty active WAL files into SqliteStore, then
1530
+ * delete them. Called lazily the first time a project's WAL is opened — if
1531
+ * the prior collector crashed mid-batch, those events survive in the WAL
1532
+ * and we'd otherwise leave them stranded on disk forever.
1533
+ */
1534
+ private recoverWalForProject;
1535
+ /**
1536
+ * Rotate the project's WAL, flush SqliteStore so the rotated file's events
1537
+ * are persisted, then delete the sealed file. Called when the active file
1538
+ * has grown past its rotate threshold.
1539
+ */
1540
+ private checkpointWal;
1258
1541
  /** Catch runtime errors on the WSS so an unhandled error doesn't crash the process */
1259
1542
  private setupPersistentErrorHandler;
1260
1543
  /** Ping all connected clients every 15s — terminate those that don't respond */
@@ -1404,6 +1687,85 @@ interface MigrationResult {
1404
1687
  */
1405
1688
  declare function migrateProjectIds(projectManager: ProjectManager, pmStore?: MigratablePmStore | null): MigrationResult;
1406
1689
 
1690
+ /**
1691
+ * Write-ahead log for the collector. One WAL directory per project, holding
1692
+ * an append-only JSONL file (`active.jsonl`) plus zero or more sealed files
1693
+ * that have been rotated out. The durability contract is:
1694
+ *
1695
+ * append(events) + commit() → events are on disk (fsync'd).
1696
+ *
1697
+ * `append` buffers into the OS file table (a plain `write` syscall); `commit`
1698
+ * calls `fsync` to force the bytes to stable storage. Callers should batch
1699
+ * appends within a single SDK message and call `commit` once per batch.
1700
+ *
1701
+ * On rotation, the active file is renamed to `sealed-<ts>-<seq>.jsonl` and a
1702
+ * fresh active file is opened. Sealed files stay on disk until the collector
1703
+ * confirms the corresponding events are durable in SqliteStore, at which
1704
+ * point they're safe to delete.
1705
+ *
1706
+ * On crash + restart, any remaining sealed or active files are evidence of
1707
+ * events that may not have reached SqliteStore. Replay feeds them back into
1708
+ * the store path, then deletes the WAL files.
1709
+ */
1710
+
1711
+ interface WalOptions {
1712
+ /** Directory to hold WAL files — created if missing. */
1713
+ dir: string;
1714
+ /** Rotate the active file once it grows past this size in bytes. */
1715
+ rotateSizeBytes?: number;
1716
+ }
1717
+ declare class Wal {
1718
+ private dir;
1719
+ private rotateSize;
1720
+ private fd;
1721
+ private activeSize;
1722
+ private seq;
1723
+ private closed;
1724
+ constructor(options: WalOptions);
1725
+ private openActive;
1726
+ private activePath;
1727
+ /**
1728
+ * Buffer events into the active file. Not durable yet — must be followed by
1729
+ * `commit()` before the caller treats the events as persisted.
1730
+ */
1731
+ append(events: RuntimeEvent[]): void;
1732
+ /** fsync the active file. Durability contract: returns only after bytes are on stable storage. */
1733
+ commit(): void;
1734
+ /** True if the active file has exceeded the rotate threshold. */
1735
+ shouldRotate(): boolean;
1736
+ /**
1737
+ * Rotate the active file: fsync, close, rename to `sealed-<ts>-<seq>.jsonl`,
1738
+ * then open a fresh active file. Returns the sealed path so the caller can
1739
+ * schedule deletion after confirming persistence elsewhere.
1740
+ */
1741
+ rotate(): string | null;
1742
+ /** Remove a sealed file once its events are durable in the downstream store. */
1743
+ static deleteSealed(path: string): void;
1744
+ /** List sealed files in this WAL's directory, sorted oldest-first. */
1745
+ listSealed(): string[];
1746
+ /**
1747
+ * Parse a WAL file back into its events, skipping any corrupt or truncated
1748
+ * trailing line. A crash mid-append can leave a partial line; the parser
1749
+ * tolerates it because fsync had never completed for that line, which means
1750
+ * the event was never treated as durable.
1751
+ */
1752
+ static readFile(path: string): RuntimeEvent[];
1753
+ /**
1754
+ * List the WAL files that need recovery for a project: any sealed files
1755
+ * plus the active file if it has content. Returned in ingestion order
1756
+ * (sealed oldest-first, then active last).
1757
+ */
1758
+ static listRecoveryFiles(dir: string): string[];
1759
+ close(): void;
1760
+ /**
1761
+ * Copy the active file (post-fsync) and any sealed files into `targetDir`.
1762
+ * Returns the total bytes copied. Used by snapshot endpoints — pairing the
1763
+ * SQLite snapshot with the WAL means a restore captures any events that
1764
+ * hadn't yet been drained into SQLite at snapshot time.
1765
+ */
1766
+ snapshotTo(targetDir: string): number;
1767
+ }
1768
+
1407
1769
  declare function isSqliteAvailable(): boolean;
1408
1770
 
1409
1771
  declare class ApiDiscoveryEngine {
@@ -1578,7 +1940,17 @@ interface PmWorkspace {
1578
1940
  * events from that key.
1579
1941
  */
1580
1942
  interface PmApiKey {
1943
+ /**
1944
+ * Raw secret. Populated ONLY by `createApiKey()`'s return value — the caller
1945
+ * must store it on the spot. Every subsequent read (list/get) returns this
1946
+ * field as an empty string; the server stores a SHA-256 hash, not the
1947
+ * plaintext, so there is no way to recover it later.
1948
+ */
1581
1949
  key: string;
1950
+ /** User-visible identifier — first ~11 chars of the raw token, e.g. "tk_9f2a1c3b". Safe to log. */
1951
+ keyPrefix: string;
1952
+ /** Last 4 chars of the raw token — for visual confirmation ("...a4f1"). */
1953
+ keyLast4: string;
1582
1954
  workspaceId: string;
1583
1955
  label: string;
1584
1956
  createdAt: number;
@@ -1806,8 +2178,26 @@ declare class PmStore {
1806
2178
  setProjectWorkspace(projectId: string, workspaceId: string): void;
1807
2179
  createApiKey(workspaceId: string, label: string, expiresAt?: number): PmApiKey;
1808
2180
  listApiKeys(workspaceId: string): PmApiKey[];
1809
- revokeApiKey(key: string): void;
1810
- getWorkspaceByApiKey(key: string): PmWorkspace | null;
2181
+ /** Revoke by the public prefix (what the dashboard shows), not the raw secret. */
2182
+ revokeApiKey(prefix: string): void;
2183
+ /** Look up an API key's workspace by its public prefix. Used for per-workspace authorization on the revoke route. */
2184
+ findApiKeyByPrefix(prefix: string): PmApiKey | null;
2185
+ /**
2186
+ * True if any non-revoked workspace API key exists. The HTTP auth gate uses
2187
+ * this to enforce auth whenever workspace keys are in play, even when the
2188
+ * global AuthManager has no keys configured. Without this check, a user who
2189
+ * creates a workspace key expecting it to gate access gets no auth at all
2190
+ * (the H5 bypass).
2191
+ */
2192
+ hasActiveApiKeys(): boolean;
2193
+ /**
2194
+ * Given a runtime projectId (proj_xxx), return the workspaceId it belongs to,
2195
+ * or null if the projectId isn't registered with any PM project. Used to
2196
+ * enforce per-workspace isolation on event-read routes — the caller can only
2197
+ * query events from runtime projects owned by their workspace.
2198
+ */
2199
+ getWorkspaceIdByRuntimeProjectId(runtimeProjectId: string): string | null;
2200
+ getWorkspaceByApiKey(rawKey: string): PmWorkspace | null;
1811
2201
  private mapProjectRow;
1812
2202
  createTask(task: PmTask): PmTask;
1813
2203
  updateTask(id: string, updates: Partial<PmTask>): void;
@@ -1978,6 +2368,10 @@ declare class HttpServer {
1978
2368
  private connectedSessionsGetter;
1979
2369
  private pmStore;
1980
2370
  private projectManager;
2371
+ private isReadyGetter;
2372
+ private snapshotFn;
2373
+ private lastSnapshotAt;
2374
+ private renderMetricsFn;
1981
2375
  constructor(store: EventStore, processMonitor?: ProcessMonitor, options?: {
1982
2376
  authManager?: AuthManager;
1983
2377
  allowedOrigins?: string[];
@@ -1989,6 +2383,37 @@ declare class HttpServer {
1989
2383
  projectName: string;
1990
2384
  }[];
1991
2385
  projectManager?: ProjectManager;
2386
+ /**
2387
+ * Returns true once the collector has finished startup recovery
2388
+ * (WAL replay + ring-buffer warm). Drives the `/readyz` probe — load
2389
+ * balancers and attach-mode detection should wait for ready before
2390
+ * routing traffic.
2391
+ */
2392
+ isReady?: () => boolean;
2393
+ /**
2394
+ * Called by `POST /api/v1/admin/snapshot`. Implementations should copy
2395
+ * every project's SQLite + WAL into a fresh directory and return a
2396
+ * manifest. Admin-only on the route side; rate-limited to prevent
2397
+ * snapshot loops.
2398
+ */
2399
+ createSnapshot?: () => {
2400
+ path: string;
2401
+ timestamp: string;
2402
+ projects: {
2403
+ name: string;
2404
+ sqliteBytes: number;
2405
+ walBytes: number;
2406
+ eventCount: number;
2407
+ }[];
2408
+ totalBytes: number;
2409
+ };
2410
+ /**
2411
+ * Returns the Prometheus exposition body for `GET /metrics`. Public
2412
+ * (no auth) so a Prometheus scraper can hit it without configuring a
2413
+ * bearer token — that's the standard pattern. Opt out via the
2414
+ * `RUNTIMESCOPE_DISABLE_METRICS=1` env var.
2415
+ */
2416
+ renderMetrics?: () => string;
1992
2417
  });
1993
2418
  private registerRoutes;
1994
2419
  /**
@@ -1996,6 +2421,21 @@ declare class HttpServer {
1996
2421
  * Tries multiple locations for monorepo and installed-package scenarios.
1997
2422
  */
1998
2423
  private resolveSdkPath;
2424
+ getPort(): number;
2425
+ /**
2426
+ * Validate the `project_id` query parameter against the caller's workspace.
2427
+ *
2428
+ * Returns:
2429
+ * - `string` — the caller is authorized; pass to store.
2430
+ * - `undefined` — caller is admin and didn't specify a project_id (all projects allowed).
2431
+ * - `false` — not authorized (a 400 or 403 response has already been written); the handler must return immediately.
2432
+ *
2433
+ * Callers with a workspace-scoped token MUST provide `project_id`, and it
2434
+ * must resolve to a PM project in the caller's workspace. Runtime projectIds
2435
+ * without a PM record (never registered via setup_project) fall through to
2436
+ * admin-only; workspace-scoped callers get 403.
2437
+ */
2438
+ private authorizeProjectIdParam;
1999
2439
  start(options?: HttpServerOptions): Promise<void>;
2000
2440
  private tryStart;
2001
2441
  stop(): Promise<void>;
@@ -2051,4 +2491,4 @@ declare function parseProcessList(): ProcessInfo[];
2051
2491
  /** Get RSS memory in MB for a given PID. Cross-platform. */
2052
2492
  declare function getProcessMemoryMB(pid: number): number;
2053
2493
 
2054
- export { type ApiChangeRecord, type ApiContract, type ApiContractField, ApiDiscoveryEngine, type ApiEndpoint, type ApiEndpointHealth, type ApiKeyEntry, type AuthConfig, type AuthInfo, AuthManager, BUILT_IN_RULES, type BackgroundSpriteSheet, type BaseEvent, type BuildMeta, type BuildStatus, type CSSArchitecture, type CSSCustomProperty, type CapexClassification, type CapexSummary, type CaptureConfig, CollectorServer, type CollectorServerOptions, type ColorToken, type CommandResponse, type ComputedStyleEntry, ConnectionManager, type ConsoleEvent, type ConsoleFilter, type ConsoleLevel, type CustomEvent, type CustomEventFilter, DataBrowser, type DatabaseConnectionConfig, type DatabaseEvent, type DatabaseFilter, type DatabaseOperation, type DatabaseSchema, type DatabaseSource, type DeployLog, type DetectedBuildTool, type DetectedFramework, type DetectedHosting, type DetectedIssue, type DetectedMetaFramework, type DetectedUILibrary, type DevProcess, type DevProcessType, type DomSnapshotEvent, type ElementSnapshotNode, type EventBatchPayload, EventStore, type EventType, type FontFaceInfo, type FontUsage, type FormFieldInfo, type GitCommit, type GitFileChange, type GitFileStatus, type GitStatus, type GlobalConfig, type GraphQLOperation, type HandshakePayload, type HeadingInfo, type HistoricalFilter, HttpServer, type HttpServerOptions, type IconFontInfo, type ImageAsset, type IndexSuggestion, InfraConnector, type InfraOverview, type InfrastructureConfig, type InlineSVGAsset, type InteractiveElementInfo, type IssueSeverity, type LandmarkInfo, type LayoutNode, type ManagedConnection, type MaskSpriteSheet, type MemoryFile, type MetricDelta, type NavigationEvent, type NetworkEvent, type NetworkFilter, type NormalizedQueryStats, type PerformanceEvent, type PerformanceFilter, type PerformanceMetricName, type PmApiKey, type PmCapexEntry, type PmNote, type PmProject, type PmSession, PmStore, type PmStoreIndexSource, type PmTask, type PmWorkspace, type PortUsage, type ProcessInfo, ProcessMonitor, type ProjectConfig, ProjectDiscovery, ProjectManager, type ProjectPhase, type ProjectStatus, type RateLimitConfig, type ReadOptions, type ReadResult, type ReconAccessibilityEvent, type ReconAssetInventoryEvent, type ReconComputedStylesEvent, type ReconDesignTokensEvent, type ReconElementSnapshotEvent, type ReconEventType, type ReconFilter, type ReconFontsEvent, type ReconLayoutTreeEvent, type ReconMetadataEvent, type RedactionRule, Redactor, type RedactorConfig, type RenderComponentProfile, type RenderEvent, type RenderFilter, RingBuffer, type RulesFiles, type RuntimeEvent, type RuntimeLog, type RuntimeScopeProjectConfig, type SVGSpriteSymbol, type SchemaColumn, type SchemaForeignKey, type SchemaIndex, SchemaIntrospector, type SchemaTable, type SdkEntry, type ServerCommand, type ServerMetricName, type ServiceInfo, type SessionDiffResult, type SessionEvent, type SessionInfo, type SessionInfoExtended, SessionManager, type SessionMetrics, SessionRateLimiter, type SessionSnapshot, type SessionStats, type SpacingValue, type SpriteFrame, SqliteStore, type SqliteStoreOptions, type StateEvent, type StateFilter, type TaskPriority, type TaskSource, type TaskStatus, type TechStackDetection, type TimelineFilter, type TlsConfig, type ToolResponse, type TypographyToken, type UIInteractionAction, type UIInteractionEvent, type UIInteractionFilter, type UserContext, type WSMessage, type WebVitalRating, type WorkType, type WriteOptions, type WriteResult, aggregateQueryStats, calculateActiveMinutes, calculateCostMicrodollars, compareSessions, detectIssues, detectN1Queries, detectOverfetching, detectSlowQueries, findPidsInDirectory, generateApiKey, generateProjectId, getListenPorts, getOrCreateProjectId, getPidsOnPort, getProcessCwd, getProcessMemoryMB, isSqliteAvailable, isValidProjectId, loadTlsOptions, migrateProjectIds, parseProcessList, parseSessionJsonl, readProjectConfig, resolveProjectAppNames, resolveTlsConfig, scaffoldProjectConfig, suggestIndexes, writeProjectConfig };
2494
+ export { type ApiChangeRecord, type ApiContract, type ApiContractField, ApiDiscoveryEngine, type ApiEndpoint, type ApiEndpointHealth, type ApiKeyEntry, type AuthConfig, type AuthInfo, AuthManager, BUILT_IN_RULES, type BackgroundSpriteSheet, type BaseEvent, type BuildMeta, type BuildStatus, type CSSArchitecture, type CSSCustomProperty, type CapexClassification, type CapexSummary, type CaptureConfig, CollectorServer, type CollectorServerOptions, type ColorToken, type CommandResponse, type ComputedStyleEntry, ConnectionManager, type ConsoleEvent, type ConsoleFilter, type ConsoleLevel, Counter, type CustomEvent, type CustomEventFilter, DataBrowser, type DatabaseConnectionConfig, type DatabaseEvent, type DatabaseFilter, type DatabaseOperation, type DatabaseSchema, type DatabaseSource, type DeployLog, type DetectedBuildTool, type DetectedFramework, type DetectedHosting, type DetectedIssue, type DetectedMetaFramework, type DetectedUILibrary, type DevProcess, type DevProcessType, type DomSnapshotEvent, type ElementSnapshotNode, type EventBatchPayload, EventStore, type EventType, type FontFaceInfo, type FontUsage, type FormFieldInfo, Gauge, type GitCommit, type GitFileChange, type GitFileStatus, type GitStatus, type GlobalConfig, type GraphQLOperation, type HandshakePayload, type HeadingInfo, type HistoricalFilter, HttpServer, type HttpServerOptions, type IconFontInfo, type ImageAsset, type IndexSuggestion, InfraConnector, type InfraOverview, type InfrastructureConfig, type InlineSVGAsset, type InteractiveElementInfo, type IssueSeverity, type LabelValues, type LandmarkInfo, type LayoutNode, type ManagedConnection, type MaskSpriteSheet, type MemoryFile, type MetricDelta, MetricsRegistry, type NavigationEvent, type NetworkEvent, type NetworkFilter, type NormalizedQueryStats, OtelExporter, type OtelExporterOptions, type PerformanceEvent, type PerformanceFilter, type PerformanceMetricName, type PmApiKey, type PmCapexEntry, type PmNote, type PmProject, type PmSession, PmStore, type PmStoreIndexSource, type PmTask, type PmWorkspace, type PortUsage, type ProcessInfo, ProcessMonitor, type ProjectConfig, ProjectDiscovery, ProjectManager, type ProjectPhase, type ProjectStatus, type RateLimitConfig, type ReadOptions, type ReadResult, type ReconAccessibilityEvent, type ReconAssetInventoryEvent, type ReconComputedStylesEvent, type ReconDesignTokensEvent, type ReconElementSnapshotEvent, type ReconEventType, type ReconFilter, type ReconFontsEvent, type ReconLayoutTreeEvent, type ReconMetadataEvent, type RedactionRule, Redactor, type RedactorConfig, type RenderComponentProfile, type RenderEvent, type RenderFilter, RingBuffer, type RulesFiles, type RuntimeEvent, type RuntimeLog, type RuntimeScopeProjectConfig, type SVGSpriteSymbol, type SchemaColumn, type SchemaForeignKey, type SchemaIndex, SchemaIntrospector, type SchemaTable, type SdkEntry, type ServerCommand, type ServerMetricName, type ServiceInfo, type SessionDiffResult, type SessionEvent, type SessionInfo, type SessionInfoExtended, SessionManager, type SessionMetrics, SessionRateLimiter, type SessionSnapshot, type SessionStats, type SnapshotResult, type SpacingValue, type SpriteFrame, SqliteStore, type SqliteStoreOptions, type StateEvent, type StateFilter, type TaskPriority, type TaskSource, type TaskStatus, type TechStackDetection, type TimelineFilter, type TlsConfig, type ToolResponse, type TypographyToken, type UIInteractionAction, type UIInteractionEvent, type UIInteractionFilter, type UserContext, type WSMessage, Wal, type WalOptions, type WebVitalRating, type WorkType, type WriteOptions, type WriteResult, aggregateQueryStats, calculateActiveMinutes, calculateCostMicrodollars, compareSessions, detectIssues, detectN1Queries, detectOverfetching, detectSlowQueries, findPidsInDirectory, generateApiKey, generateProjectId, getListenPorts, getOrCreateProjectId, getPidsOnPort, getProcessCwd, getProcessMemoryMB, isSqliteAvailable, isValidProjectId, loadTlsOptions, migrateProjectIds, otelOptionsFromEnv, parseOtelHeaders, parseProcessList, parseSessionJsonl, randomSpanId, readProjectConfig, resolveProjectAppNames, resolveTlsConfig, scaffoldProjectConfig, suggestIndexes, traceIdFromSession, writeProjectConfig };
package/dist/index.js CHANGED
@@ -2,8 +2,12 @@ import {
2
2
  AuthManager,
3
3
  BUILT_IN_RULES,
4
4
  CollectorServer,
5
+ Counter,
5
6
  EventStore,
7
+ Gauge,
6
8
  HttpServer,
9
+ MetricsRegistry,
10
+ OtelExporter,
7
11
  PmStore,
8
12
  ProjectDiscovery,
9
13
  ProjectManager,
@@ -12,6 +16,7 @@ import {
12
16
  SessionManager,
13
17
  SessionRateLimiter,
14
18
  SqliteStore,
19
+ Wal,
15
20
  calculateActiveMinutes,
16
21
  calculateCostMicrodollars,
17
22
  findPidsInDirectory,
@@ -26,14 +31,18 @@ import {
26
31
  isValidProjectId,
27
32
  loadTlsOptions,
28
33
  migrateProjectIds,
34
+ otelOptionsFromEnv,
35
+ parseOtelHeaders,
29
36
  parseProcessList,
30
37
  parseSessionJsonl,
38
+ randomSpanId,
31
39
  readProjectConfig,
32
40
  resolveProjectAppNames,
33
41
  resolveTlsConfig,
34
42
  scaffoldProjectConfig,
43
+ traceIdFromSession,
35
44
  writeProjectConfig
36
- } from "./chunk-WWFIEANS.js";
45
+ } from "./chunk-M2V4EFJY.js";
37
46
  import {
38
47
  __require
39
48
  } from "./chunk-UP2VWCW5.js";
@@ -2036,10 +2045,14 @@ export {
2036
2045
  BUILT_IN_RULES,
2037
2046
  CollectorServer,
2038
2047
  ConnectionManager,
2048
+ Counter,
2039
2049
  DataBrowser,
2040
2050
  EventStore,
2051
+ Gauge,
2041
2052
  HttpServer,
2042
2053
  InfraConnector,
2054
+ MetricsRegistry,
2055
+ OtelExporter,
2043
2056
  PmStore,
2044
2057
  ProcessMonitor,
2045
2058
  ProjectDiscovery,
@@ -2050,6 +2063,7 @@ export {
2050
2063
  SessionManager,
2051
2064
  SessionRateLimiter,
2052
2065
  SqliteStore,
2066
+ Wal,
2053
2067
  aggregateQueryStats,
2054
2068
  calculateActiveMinutes,
2055
2069
  calculateCostMicrodollars,
@@ -2070,13 +2084,17 @@ export {
2070
2084
  isValidProjectId,
2071
2085
  loadTlsOptions,
2072
2086
  migrateProjectIds,
2087
+ otelOptionsFromEnv,
2088
+ parseOtelHeaders,
2073
2089
  parseProcessList,
2074
2090
  parseSessionJsonl,
2091
+ randomSpanId,
2075
2092
  readProjectConfig,
2076
2093
  resolveProjectAppNames,
2077
2094
  resolveTlsConfig,
2078
2095
  scaffoldProjectConfig,
2079
2096
  suggestIndexes,
2097
+ traceIdFromSession,
2080
2098
  writeProjectConfig
2081
2099
  };
2082
2100
  //# sourceMappingURL=index.js.map