@runtimescope/collector 0.6.2 → 0.7.0

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.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AuthManager,
4
+ CollectorServer,
5
+ HttpServer,
6
+ PmStore,
7
+ ProjectDiscovery,
8
+ ProjectManager,
9
+ Redactor,
10
+ SessionManager,
11
+ SqliteStore,
12
+ resolveTlsConfig
13
+ } from "./chunk-6JZXAFPC.js";
14
+
15
+ // src/standalone.ts
16
+ import { existsSync } from "fs";
17
+ import { join } from "path";
18
+ var HOST = process.env.RUNTIMESCOPE_HOST ?? "127.0.0.1";
19
+ var COLLECTOR_PORT = parseInt(process.env.RUNTIMESCOPE_PORT ?? "9092", 10);
20
+ var HTTP_PORT = parseInt(process.env.RUNTIMESCOPE_HTTP_PORT ?? "9093", 10);
21
+ var BUFFER_SIZE = parseInt(process.env.RUNTIMESCOPE_BUFFER_SIZE ?? "10000", 10);
22
+ var RETENTION_DAYS = parseInt(process.env.RUNTIMESCOPE_RETENTION_DAYS ?? "30", 10);
23
+ async function main() {
24
+ console.error("[RuntimeScope] Starting standalone collector...");
25
+ const projectManager = new ProjectManager();
26
+ projectManager.ensureGlobalDir();
27
+ const globalConfig = projectManager.getGlobalConfig();
28
+ const authManager = new AuthManager({
29
+ enabled: globalConfig.auth?.enabled ?? false,
30
+ apiKeys: globalConfig.auth?.apiKeys ?? []
31
+ });
32
+ const tlsConfig = resolveTlsConfig() ?? globalConfig.tls ?? void 0;
33
+ const redactor = new Redactor({
34
+ enabled: globalConfig.redaction?.enabled ?? false,
35
+ useBuiltIn: true,
36
+ rules: globalConfig.redaction?.rules?.map((r) => ({
37
+ name: r.name,
38
+ pattern: new RegExp(r.pattern, "gi"),
39
+ replacement: r.replacement
40
+ }))
41
+ });
42
+ const corsOrigins = process.env.RUNTIMESCOPE_CORS_ORIGINS?.split(",").map((s) => s.trim()) ?? globalConfig.corsOrigins;
43
+ if (authManager.isEnabled()) {
44
+ console.error(`[RuntimeScope] Auth enabled (${globalConfig.auth?.apiKeys?.length ?? 0} API keys)`);
45
+ }
46
+ if (tlsConfig) {
47
+ console.error(`[RuntimeScope] TLS enabled (cert: ${tlsConfig.certPath})`);
48
+ }
49
+ if (redactor.isEnabled()) {
50
+ console.error("[RuntimeScope] Payload redaction enabled");
51
+ }
52
+ const collector = new CollectorServer({
53
+ bufferSize: BUFFER_SIZE,
54
+ projectManager,
55
+ authManager,
56
+ rateLimits: globalConfig.rateLimits,
57
+ tls: tlsConfig
58
+ });
59
+ await collector.start({ port: COLLECTOR_PORT, host: HOST, maxRetries: 5, retryDelayMs: 1e3 });
60
+ const store = collector.getStore();
61
+ if (redactor.isEnabled()) {
62
+ store.setRedactor(redactor);
63
+ }
64
+ const sqliteStores = collector.getSqliteStores();
65
+ const sessionManager = new SessionManager(projectManager, sqliteStores, store);
66
+ collector.onDisconnect((sessionId, projectName) => {
67
+ try {
68
+ sessionManager.createSnapshot(sessionId, projectName);
69
+ console.error(`[RuntimeScope] Session ${sessionId} metrics saved`);
70
+ } catch {
71
+ }
72
+ });
73
+ const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1e3;
74
+ for (const projectName of projectManager.listProjects()) {
75
+ const dbPath = projectManager.getProjectDbPath(projectName);
76
+ if (existsSync(dbPath)) {
77
+ try {
78
+ const tempStore = new SqliteStore({ dbPath });
79
+ const deleted = tempStore.deleteOldEvents(cutoffMs);
80
+ if (deleted > 0) {
81
+ console.error(`[RuntimeScope] Pruned ${deleted} events older than ${RETENTION_DAYS}d from "${projectName}"`);
82
+ }
83
+ tempStore.close();
84
+ } catch {
85
+ }
86
+ }
87
+ }
88
+ const pmDbPath = join(projectManager.rootDir, "pm.db");
89
+ const pmStore = new PmStore({ dbPath: pmDbPath });
90
+ const discovery = new ProjectDiscovery(pmStore, projectManager);
91
+ discovery.discoverAll().then((result) => {
92
+ console.error(`[RuntimeScope] PM: ${result.projectsDiscovered} projects, ${result.sessionsDiscovered} sessions discovered`);
93
+ }).catch((err) => {
94
+ console.error("[RuntimeScope] PM discovery error:", err.message);
95
+ });
96
+ const httpServer = new HttpServer(store, void 0, {
97
+ authManager,
98
+ allowedOrigins: corsOrigins,
99
+ rateLimiter: collector.getRateLimiter(),
100
+ pmStore,
101
+ discovery
102
+ });
103
+ try {
104
+ await httpServer.start({ port: HTTP_PORT, host: HOST, tls: tlsConfig });
105
+ } catch (err) {
106
+ console.error("[RuntimeScope] HTTP API failed to start:", err.message);
107
+ }
108
+ collector.onConnect((sessionId, projectName) => {
109
+ httpServer.broadcastSessionChange("session_connected", sessionId, projectName);
110
+ });
111
+ collector.onDisconnect((sessionId, projectName) => {
112
+ httpServer.broadcastSessionChange("session_disconnected", sessionId, projectName);
113
+ });
114
+ const proto = tlsConfig ? "wss" : "ws";
115
+ const httpProto = tlsConfig ? "https" : "http";
116
+ console.error(`[RuntimeScope] Standalone collector ready`);
117
+ console.error(`[RuntimeScope] WebSocket: ${proto}://${HOST}:${COLLECTOR_PORT}`);
118
+ console.error(`[RuntimeScope] HTTP API: ${httpProto}://${HOST}:${HTTP_PORT}`);
119
+ console.error(`[RuntimeScope] Health: ${httpProto}://${HOST}:${HTTP_PORT}/api/health`);
120
+ console.error(`[RuntimeScope] Ingest: POST ${httpProto}://${HOST}:${HTTP_PORT}/api/events`);
121
+ let shuttingDown = false;
122
+ const shutdown = async () => {
123
+ if (shuttingDown) return;
124
+ shuttingDown = true;
125
+ console.error("[RuntimeScope] Shutting down...");
126
+ await httpServer.stop();
127
+ collector.stop();
128
+ pmStore.close();
129
+ process.exit(0);
130
+ };
131
+ process.on("SIGINT", () => {
132
+ shutdown();
133
+ });
134
+ process.on("SIGTERM", () => {
135
+ shutdown();
136
+ });
137
+ }
138
+ main().catch((err) => {
139
+ console.error("[RuntimeScope] Fatal error:", err);
140
+ process.exit(1);
141
+ });
142
+ //# sourceMappingURL=standalone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/standalone.ts"],"sourcesContent":["#!/usr/bin/env node\n\n// ============================================================\n// RuntimeScope Standalone Collector\n// Runs CollectorServer + HttpServer as a standalone service\n// without MCP, Playwright, or ProcessMonitor.\n//\n// Usage:\n// node dist/standalone.js\n// npx @runtimescope/collector\n// docker run runtimescope\n// ============================================================\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { CollectorServer } from './server.js';\nimport { HttpServer } from './http-server.js';\nimport { ProjectManager } from './project-manager.js';\nimport { PmStore } from './pm/pm-store.js';\nimport { ProjectDiscovery } from './pm/project-discovery.js';\nimport { SessionManager } from './session-manager.js';\nimport { SqliteStore } from './sqlite-store.js';\nimport { AuthManager } from './auth.js';\nimport { Redactor } from './redactor.js';\nimport { resolveTlsConfig } from './tls.js';\n\nconst HOST = process.env.RUNTIMESCOPE_HOST ?? '127.0.0.1';\n// Standalone defaults to 9092/9093 to avoid conflicting with the MCP server (9090/9091)\nconst COLLECTOR_PORT = parseInt(process.env.RUNTIMESCOPE_PORT ?? '9092', 10);\nconst HTTP_PORT = parseInt(process.env.RUNTIMESCOPE_HTTP_PORT ?? '9093', 10);\nconst BUFFER_SIZE = parseInt(process.env.RUNTIMESCOPE_BUFFER_SIZE ?? '10000', 10);\nconst RETENTION_DAYS = parseInt(process.env.RUNTIMESCOPE_RETENTION_DAYS ?? '30', 10);\n\nasync function main() {\n console.error('[RuntimeScope] Starting standalone collector...');\n\n // 1. Initialize project management + config\n const projectManager = new ProjectManager();\n projectManager.ensureGlobalDir();\n const globalConfig = projectManager.getGlobalConfig();\n\n // 2. Security: auth, TLS, redaction, CORS\n const authManager = new AuthManager({\n enabled: globalConfig.auth?.enabled ?? false,\n apiKeys: globalConfig.auth?.apiKeys ?? [],\n });\n\n const tlsConfig = resolveTlsConfig() ?? globalConfig.tls ?? undefined;\n\n const redactor = new Redactor({\n enabled: globalConfig.redaction?.enabled ?? false,\n useBuiltIn: true,\n rules: globalConfig.redaction?.rules?.map(r => ({\n name: r.name,\n pattern: new RegExp(r.pattern, 'gi'),\n replacement: r.replacement,\n })),\n });\n\n const corsOrigins = process.env.RUNTIMESCOPE_CORS_ORIGINS?.split(',').map(s => s.trim())\n ?? globalConfig.corsOrigins;\n\n if (authManager.isEnabled()) {\n console.error(`[RuntimeScope] Auth enabled (${globalConfig.auth?.apiKeys?.length ?? 0} API keys)`);\n }\n if (tlsConfig) {\n console.error(`[RuntimeScope] TLS enabled (cert: ${tlsConfig.certPath})`);\n }\n if (redactor.isEnabled()) {\n console.error('[RuntimeScope] Payload redaction enabled');\n }\n\n // 3. Start collector WebSocket server\n const collector = new CollectorServer({\n bufferSize: BUFFER_SIZE,\n projectManager,\n authManager,\n rateLimits: globalConfig.rateLimits,\n tls: tlsConfig,\n });\n await collector.start({ port: COLLECTOR_PORT, host: HOST, maxRetries: 5, retryDelayMs: 1000 });\n\n const store = collector.getStore();\n\n // Wire redactor for defense-in-depth\n if (redactor.isEnabled()) {\n store.setRedactor(redactor);\n }\n\n // 4. Session management — auto-snapshot on disconnect\n const sqliteStores = collector.getSqliteStores();\n const sessionManager = new SessionManager(projectManager, sqliteStores, store);\n\n collector.onDisconnect((sessionId, projectName) => {\n try {\n sessionManager.createSnapshot(sessionId, projectName);\n console.error(`[RuntimeScope] Session ${sessionId} metrics saved`);\n } catch {\n // Non-fatal\n }\n });\n\n // 5. Retention pruning\n const cutoffMs = Date.now() - RETENTION_DAYS * 24 * 60 * 60 * 1000;\n for (const projectName of projectManager.listProjects()) {\n const dbPath = projectManager.getProjectDbPath(projectName);\n if (existsSync(dbPath)) {\n try {\n const tempStore = new SqliteStore({ dbPath });\n const deleted = tempStore.deleteOldEvents(cutoffMs);\n if (deleted > 0) {\n console.error(`[RuntimeScope] Pruned ${deleted} events older than ${RETENTION_DAYS}d from \"${projectName}\"`);\n }\n tempStore.close();\n } catch {\n // Non-fatal\n }\n }\n }\n\n // 6. Project Management layer\n const pmDbPath = join(projectManager.rootDir, 'pm.db');\n const pmStore = new PmStore({ dbPath: pmDbPath });\n const discovery = new ProjectDiscovery(pmStore, projectManager);\n\n // Run discovery in background (non-blocking)\n discovery.discoverAll().then((result) => {\n console.error(`[RuntimeScope] PM: ${result.projectsDiscovered} projects, ${result.sessionsDiscovered} sessions discovered`);\n }).catch((err) => {\n console.error('[RuntimeScope] PM discovery error:', (err as Error).message);\n });\n\n // 7. Start HTTP API server (with POST /api/events + PM routes)\n const httpServer = new HttpServer(store, undefined, {\n authManager,\n allowedOrigins: corsOrigins,\n rateLimiter: collector.getRateLimiter(),\n pmStore,\n discovery,\n });\n\n try {\n await httpServer.start({ port: HTTP_PORT, host: HOST, tls: tlsConfig });\n } catch (err) {\n console.error('[RuntimeScope] HTTP API failed to start:', (err as Error).message);\n }\n\n // Push session connect/disconnect to dashboard in real-time\n collector.onConnect((sessionId, projectName) => {\n httpServer.broadcastSessionChange('session_connected', sessionId, projectName);\n });\n collector.onDisconnect((sessionId, projectName) => {\n httpServer.broadcastSessionChange('session_disconnected', sessionId, projectName);\n });\n\n // 7. Startup summary\n const proto = tlsConfig ? 'wss' : 'ws';\n const httpProto = tlsConfig ? 'https' : 'http';\n console.error(`[RuntimeScope] Standalone collector ready`);\n console.error(`[RuntimeScope] WebSocket: ${proto}://${HOST}:${COLLECTOR_PORT}`);\n console.error(`[RuntimeScope] HTTP API: ${httpProto}://${HOST}:${HTTP_PORT}`);\n console.error(`[RuntimeScope] Health: ${httpProto}://${HOST}:${HTTP_PORT}/api/health`);\n console.error(`[RuntimeScope] Ingest: POST ${httpProto}://${HOST}:${HTTP_PORT}/api/events`);\n\n // 8. Graceful shutdown\n let shuttingDown = false;\n const shutdown = async () => {\n if (shuttingDown) return;\n shuttingDown = true;\n console.error('[RuntimeScope] Shutting down...');\n\n await httpServer.stop();\n collector.stop();\n pmStore.close();\n\n process.exit(0);\n };\n\n process.on('SIGINT', () => { shutdown(); });\n process.on('SIGTERM', () => { shutdown(); });\n}\n\nmain().catch((err) => {\n console.error('[RuntimeScope] Fatal error:', err);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;;;;;;AAaA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AAYrB,IAAM,OAAO,QAAQ,IAAI,qBAAqB;AAE9C,IAAM,iBAAiB,SAAS,QAAQ,IAAI,qBAAqB,QAAQ,EAAE;AAC3E,IAAM,YAAY,SAAS,QAAQ,IAAI,0BAA0B,QAAQ,EAAE;AAC3E,IAAM,cAAc,SAAS,QAAQ,IAAI,4BAA4B,SAAS,EAAE;AAChF,IAAM,iBAAiB,SAAS,QAAQ,IAAI,+BAA+B,MAAM,EAAE;AAEnF,eAAe,OAAO;AACpB,UAAQ,MAAM,iDAAiD;AAG/D,QAAM,iBAAiB,IAAI,eAAe;AAC1C,iBAAe,gBAAgB;AAC/B,QAAM,eAAe,eAAe,gBAAgB;AAGpD,QAAM,cAAc,IAAI,YAAY;AAAA,IAClC,SAAS,aAAa,MAAM,WAAW;AAAA,IACvC,SAAS,aAAa,MAAM,WAAW,CAAC;AAAA,EAC1C,CAAC;AAED,QAAM,YAAY,iBAAiB,KAAK,aAAa,OAAO;AAE5D,QAAM,WAAW,IAAI,SAAS;AAAA,IAC5B,SAAS,aAAa,WAAW,WAAW;AAAA,IAC5C,YAAY;AAAA,IACZ,OAAO,aAAa,WAAW,OAAO,IAAI,QAAM;AAAA,MAC9C,MAAM,EAAE;AAAA,MACR,SAAS,IAAI,OAAO,EAAE,SAAS,IAAI;AAAA,MACnC,aAAa,EAAE;AAAA,IACjB,EAAE;AAAA,EACJ,CAAC;AAED,QAAM,cAAc,QAAQ,IAAI,2BAA2B,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,KAClF,aAAa;AAElB,MAAI,YAAY,UAAU,GAAG;AAC3B,YAAQ,MAAM,gCAAgC,aAAa,MAAM,SAAS,UAAU,CAAC,YAAY;AAAA,EACnG;AACA,MAAI,WAAW;AACb,YAAQ,MAAM,qCAAqC,UAAU,QAAQ,GAAG;AAAA,EAC1E;AACA,MAAI,SAAS,UAAU,GAAG;AACxB,YAAQ,MAAM,0CAA0C;AAAA,EAC1D;AAGA,QAAM,YAAY,IAAI,gBAAgB;AAAA,IACpC,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,YAAY,aAAa;AAAA,IACzB,KAAK;AAAA,EACP,CAAC;AACD,QAAM,UAAU,MAAM,EAAE,MAAM,gBAAgB,MAAM,MAAM,YAAY,GAAG,cAAc,IAAK,CAAC;AAE7F,QAAM,QAAQ,UAAU,SAAS;AAGjC,MAAI,SAAS,UAAU,GAAG;AACxB,UAAM,YAAY,QAAQ;AAAA,EAC5B;AAGA,QAAM,eAAe,UAAU,gBAAgB;AAC/C,QAAM,iBAAiB,IAAI,eAAe,gBAAgB,cAAc,KAAK;AAE7E,YAAU,aAAa,CAAC,WAAW,gBAAgB;AACjD,QAAI;AACF,qBAAe,eAAe,WAAW,WAAW;AACpD,cAAQ,MAAM,0BAA0B,SAAS,gBAAgB;AAAA,IACnE,QAAQ;AAAA,IAER;AAAA,EACF,CAAC;AAGD,QAAM,WAAW,KAAK,IAAI,IAAI,iBAAiB,KAAK,KAAK,KAAK;AAC9D,aAAW,eAAe,eAAe,aAAa,GAAG;AACvD,UAAM,SAAS,eAAe,iBAAiB,WAAW;AAC1D,QAAI,WAAW,MAAM,GAAG;AACtB,UAAI;AACF,cAAM,YAAY,IAAI,YAAY,EAAE,OAAO,CAAC;AAC5C,cAAM,UAAU,UAAU,gBAAgB,QAAQ;AAClD,YAAI,UAAU,GAAG;AACf,kBAAQ,MAAM,yBAAyB,OAAO,sBAAsB,cAAc,WAAW,WAAW,GAAG;AAAA,QAC7G;AACA,kBAAU,MAAM;AAAA,MAClB,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AAGA,QAAM,WAAW,KAAK,eAAe,SAAS,OAAO;AACrD,QAAM,UAAU,IAAI,QAAQ,EAAE,QAAQ,SAAS,CAAC;AAChD,QAAM,YAAY,IAAI,iBAAiB,SAAS,cAAc;AAG9D,YAAU,YAAY,EAAE,KAAK,CAAC,WAAW;AACvC,YAAQ,MAAM,sBAAsB,OAAO,kBAAkB,cAAc,OAAO,kBAAkB,sBAAsB;AAAA,EAC5H,CAAC,EAAE,MAAM,CAAC,QAAQ;AAChB,YAAQ,MAAM,sCAAuC,IAAc,OAAO;AAAA,EAC5E,CAAC;AAGD,QAAM,aAAa,IAAI,WAAW,OAAO,QAAW;AAAA,IAClD;AAAA,IACA,gBAAgB;AAAA,IAChB,aAAa,UAAU,eAAe;AAAA,IACtC;AAAA,IACA;AAAA,EACF,CAAC;AAED,MAAI;AACF,UAAM,WAAW,MAAM,EAAE,MAAM,WAAW,MAAM,MAAM,KAAK,UAAU,CAAC;AAAA,EACxE,SAAS,KAAK;AACZ,YAAQ,MAAM,4CAA6C,IAAc,OAAO;AAAA,EAClF;AAGA,YAAU,UAAU,CAAC,WAAW,gBAAgB;AAC9C,eAAW,uBAAuB,qBAAqB,WAAW,WAAW;AAAA,EAC/E,CAAC;AACD,YAAU,aAAa,CAAC,WAAW,gBAAgB;AACjD,eAAW,uBAAuB,wBAAwB,WAAW,WAAW;AAAA,EAClF,CAAC;AAGD,QAAM,QAAQ,YAAY,QAAQ;AAClC,QAAM,YAAY,YAAY,UAAU;AACxC,UAAQ,MAAM,2CAA2C;AACzD,UAAQ,MAAM,+BAA+B,KAAK,MAAM,IAAI,IAAI,cAAc,EAAE;AAChF,UAAQ,MAAM,+BAA+B,SAAS,MAAM,IAAI,IAAI,SAAS,EAAE;AAC/E,UAAQ,MAAM,+BAA+B,SAAS,MAAM,IAAI,IAAI,SAAS,aAAa;AAC1F,UAAQ,MAAM,oCAAoC,SAAS,MAAM,IAAI,IAAI,SAAS,aAAa;AAG/F,MAAI,eAAe;AACnB,QAAM,WAAW,YAAY;AAC3B,QAAI,aAAc;AAClB,mBAAe;AACf,YAAQ,MAAM,iCAAiC;AAE/C,UAAM,WAAW,KAAK;AACtB,cAAU,KAAK;AACf,YAAQ,MAAM;AAEd,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,GAAG,UAAU,MAAM;AAAE,aAAS;AAAA,EAAG,CAAC;AAC1C,UAAQ,GAAG,WAAW,MAAM;AAAE,aAAS;AAAA,EAAG,CAAC;AAC7C;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,+BAA+B,GAAG;AAChD,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runtimescope/collector",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Event collector and persistence layer for RuntimeScope",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,6 +11,9 @@
11
11
  "types": "./dist/index.d.ts"
12
12
  }
13
13
  },
14
+ "bin": {
15
+ "runtimescope-collector": "./dist/standalone.js"
16
+ },
14
17
  "files": [
15
18
  "dist"
16
19
  ],