@expressots/studio-agent 4.0.0-preview.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.
Files changed (86) hide show
  1. package/README.md +143 -0
  2. package/dist/agent.d.ts +127 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +1031 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/discovery/index.d.ts +2 -0
  7. package/dist/discovery/index.d.ts.map +1 -0
  8. package/dist/discovery/index.js +2 -0
  9. package/dist/discovery/index.js.map +1 -0
  10. package/dist/discovery/route-scanner.d.ts +35 -0
  11. package/dist/discovery/route-scanner.d.ts.map +1 -0
  12. package/dist/discovery/route-scanner.js +385 -0
  13. package/dist/discovery/route-scanner.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/instrumentation/index.d.ts +2 -0
  19. package/dist/instrumentation/index.d.ts.map +1 -0
  20. package/dist/instrumentation/index.js +2 -0
  21. package/dist/instrumentation/index.js.map +1 -0
  22. package/dist/instrumentation/tracer.d.ts +40 -0
  23. package/dist/instrumentation/tracer.d.ts.map +1 -0
  24. package/dist/instrumentation/tracer.js +190 -0
  25. package/dist/instrumentation/tracer.js.map +1 -0
  26. package/dist/introspection/container-introspector.d.ts +81 -0
  27. package/dist/introspection/container-introspector.d.ts.map +1 -0
  28. package/dist/introspection/container-introspector.js +251 -0
  29. package/dist/introspection/container-introspector.js.map +1 -0
  30. package/dist/logging/log-capture.d.ts +58 -0
  31. package/dist/logging/log-capture.d.ts.map +1 -0
  32. package/dist/logging/log-capture.js +184 -0
  33. package/dist/logging/log-capture.js.map +1 -0
  34. package/dist/recording/index.d.ts +2 -0
  35. package/dist/recording/index.d.ts.map +1 -0
  36. package/dist/recording/index.js +2 -0
  37. package/dist/recording/index.js.map +1 -0
  38. package/dist/recording/request-recorder.d.ts +43 -0
  39. package/dist/recording/request-recorder.d.ts.map +1 -0
  40. package/dist/recording/request-recorder.js +373 -0
  41. package/dist/recording/request-recorder.js.map +1 -0
  42. package/dist/security/fix-resolver.d.ts +40 -0
  43. package/dist/security/fix-resolver.d.ts.map +1 -0
  44. package/dist/security/fix-resolver.js +283 -0
  45. package/dist/security/fix-resolver.js.map +1 -0
  46. package/dist/security/fix-runner.d.ts +60 -0
  47. package/dist/security/fix-runner.d.ts.map +1 -0
  48. package/dist/security/fix-runner.js +188 -0
  49. package/dist/security/fix-runner.js.map +1 -0
  50. package/dist/security/index.d.ts +140 -0
  51. package/dist/security/index.d.ts.map +1 -0
  52. package/dist/security/index.js +460 -0
  53. package/dist/security/index.js.map +1 -0
  54. package/dist/security/lockfile-graph.d.ts +69 -0
  55. package/dist/security/lockfile-graph.d.ts.map +1 -0
  56. package/dist/security/lockfile-graph.js +245 -0
  57. package/dist/security/lockfile-graph.js.map +1 -0
  58. package/dist/security/npm-audit.d.ts +67 -0
  59. package/dist/security/npm-audit.d.ts.map +1 -0
  60. package/dist/security/npm-audit.js +320 -0
  61. package/dist/security/npm-audit.js.map +1 -0
  62. package/dist/security/osv-cache.d.ts +51 -0
  63. package/dist/security/osv-cache.d.ts.map +1 -0
  64. package/dist/security/osv-cache.js +99 -0
  65. package/dist/security/osv-cache.js.map +1 -0
  66. package/dist/security/osv-client.d.ts +47 -0
  67. package/dist/security/osv-client.d.ts.map +1 -0
  68. package/dist/security/osv-client.js +247 -0
  69. package/dist/security/osv-client.js.map +1 -0
  70. package/dist/security/posture-analyzer.d.ts +44 -0
  71. package/dist/security/posture-analyzer.d.ts.map +1 -0
  72. package/dist/security/posture-analyzer.js +397 -0
  73. package/dist/security/posture-analyzer.js.map +1 -0
  74. package/dist/security/reachability.d.ts +59 -0
  75. package/dist/security/reachability.d.ts.map +1 -0
  76. package/dist/security/reachability.js +302 -0
  77. package/dist/security/reachability.js.map +1 -0
  78. package/dist/security/score.d.ts +36 -0
  79. package/dist/security/score.d.ts.map +1 -0
  80. package/dist/security/score.js +94 -0
  81. package/dist/security/score.js.map +1 -0
  82. package/dist/types/index.d.ts +587 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +14 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/package.json +75 -0
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # @expressots/studio-agent
2
+
3
+ Instrumentation agent for ExpressoTS Studio - provides route discovery, OpenTelemetry tracing, and request recording.
4
+
5
+ ## Features
6
+
7
+ - **Route Discovery**: Automatically scans your ExpressoTS application to discover all routes, controllers, and services
8
+ - **OpenTelemetry Tracing**: Full distributed tracing with automatic instrumentation
9
+ - **Request Recording**: Record and replay HTTP requests for debugging
10
+ - **WebSocket Communication**: Real-time updates to Studio UI
11
+ - **Metrics Collection**: P50/P95/P99 latency, error rates, and more
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @expressots/studio-agent
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```typescript
22
+ import { StudioAgent } from '@expressots/studio-agent';
23
+
24
+ const agent = new StudioAgent({
25
+ port: 3334,
26
+ serviceName: 'my-app',
27
+ enableRecording: true,
28
+ });
29
+
30
+ // Start the agent
31
+ await agent.start();
32
+
33
+ // Use the middleware (optional, for request recording)
34
+ app.use(agent.createMiddleware());
35
+
36
+ // Get discovered routes
37
+ const routes = agent.getRoutes();
38
+
39
+ // Stop when done
40
+ await agent.stop();
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ ```typescript
46
+ interface AgentConfig {
47
+ /** Port for the agent WebSocket server (default: 3334) */
48
+ port: number;
49
+
50
+ /** Path to store SQLite database (default: '.studio/studio.db') */
51
+ dbPath: string;
52
+
53
+ /** Enable request/response recording (default: true) */
54
+ enableRecording: boolean;
55
+
56
+ /** Maximum number of recorded exchanges to keep (default: 1000) */
57
+ maxRecordedExchanges: number;
58
+
59
+ /** Enable performance profiling (default: true) */
60
+ enableProfiling: boolean;
61
+
62
+ /** Sample rate for tracing 0-1 (default: 1.0) */
63
+ traceSampleRate: number;
64
+
65
+ /** Custom service name (default: 'expressots-app') */
66
+ serviceName: string;
67
+
68
+ /** Express app instance for runtime route scanning */
69
+ expressApp?: any;
70
+ }
71
+ ```
72
+
73
+ ## Components
74
+
75
+ ### StudioAgent
76
+
77
+ Main orchestrator that coordinates all functionality.
78
+
79
+ ### RouteScanner
80
+
81
+ Scans TypeScript source files to discover routes and their metadata:
82
+
83
+ ```typescript
84
+ import { RouteScanner } from '@expressots/studio-agent';
85
+
86
+ const scanner = new RouteScanner('./src');
87
+ const structure = await scanner.scan();
88
+
89
+ console.log(structure.controllers);
90
+ console.log(structure.services);
91
+ console.log(structure.dependencies);
92
+ ```
93
+
94
+ ### RequestRecorder
95
+
96
+ Records HTTP requests/responses for later replay:
97
+
98
+ ```typescript
99
+ import { RequestRecorder } from '@expressots/studio-agent';
100
+
101
+ const recorder = new RequestRecorder('.studio/studio.db');
102
+ await recorder.initialize();
103
+
104
+ // Get recent exchanges
105
+ const exchanges = recorder.getRecentExchanges(100);
106
+
107
+ // Search by path
108
+ const results = recorder.searchExchanges('/api/users', 'GET');
109
+ ```
110
+
111
+ ### StudioTracer
112
+
113
+ OpenTelemetry tracer with custom span processing:
114
+
115
+ ```typescript
116
+ import { StudioTracer } from '@expressots/studio-agent';
117
+
118
+ const tracer = new StudioTracer('my-service');
119
+ await tracer.start((trace) => {
120
+ console.log('Trace completed:', trace.traceId);
121
+ });
122
+
123
+ // Create custom spans
124
+ await tracer.createSpan('my-operation', async () => {
125
+ // Your code here
126
+ }, { attribute: 'value' });
127
+ ```
128
+
129
+ ## WebSocket Events
130
+
131
+ The agent emits real-time events to connected clients:
132
+
133
+ | Event | Description |
134
+ |-------|-------------|
135
+ | `routes` | Discovered routes |
136
+ | `trace` | Completed trace |
137
+ | `request` | New request recorded |
138
+ | `metrics` | Updated metrics |
139
+ | `structure` | Application structure |
140
+
141
+ ## License
142
+
143
+ MIT © ExpressoTS
@@ -0,0 +1,127 @@
1
+ /**
2
+ * StudioAgent - Main orchestrator for ExpressoTS Studio instrumentation
3
+ *
4
+ * Provides:
5
+ * - OpenTelemetry instrumentation
6
+ * - Route discovery
7
+ * - Request/response recording
8
+ * - WebSocket communication with Studio UI
9
+ */
10
+ import { type ContainerSnapshot } from './introspection/container-introspector.js';
11
+ import { type LogEntry } from './logging/log-capture.js';
12
+ import type { AgentConfig, RouteInfo, AppStructure, AppMetrics, EndpointStats, RuntimeInfo } from './types/index.js';
13
+ export declare class StudioAgent {
14
+ private config;
15
+ private tracer;
16
+ private scanner;
17
+ private recorder;
18
+ private introspector;
19
+ private containerSnapshot;
20
+ private logCapture;
21
+ private securityEngine;
22
+ private io;
23
+ private httpServer;
24
+ private routes;
25
+ private appStructure;
26
+ private metrics;
27
+ private endpointStats;
28
+ private responseTimes;
29
+ private startTime;
30
+ private isRunning;
31
+ constructor(config?: Partial<AgentConfig>);
32
+ /** Start the Studio Agent */
33
+ start(): Promise<void>;
34
+ /**
35
+ * Stand up the SecurityEngine and kick off the first scan. The
36
+ * engine reuses the existing Socket.IO server — every transition in
37
+ * its report goes out as a `WSMessage<'security'>` envelope, gated
38
+ * on at least one connected client.
39
+ */
40
+ private startSecurityEngine;
41
+ /** Get the captured container snapshot (or null if unavailable). */
42
+ getContainerSnapshot(): ContainerSnapshot | null;
43
+ /** Stop the Studio Agent */
44
+ stop(): Promise<void>;
45
+ /**
46
+ * Tear down the WebSocket / HTTP server with a hard timeout so the
47
+ * host's graceful shutdown never hangs on a slow socket.io drain.
48
+ *
49
+ * If the close doesn't complete in time, we move on — the OS reclaims
50
+ * the port the moment the host process exits, so the next hot-reload
51
+ * start succeeds anyway. (`tsx --watch` / `nodemon` will SIGKILL us
52
+ * otherwise, which surfaces to the user as "Failed running ./src/main.ts".)
53
+ */
54
+ private shutdownWebSocketServer;
55
+ /** Scan application for routes */
56
+ scanRoutes(): Promise<void>;
57
+ /** Get discovered routes */
58
+ getRoutes(): RouteInfo[];
59
+ /** Get application structure */
60
+ getAppStructure(): AppStructure | null;
61
+ /** Get current metrics */
62
+ getMetrics(): AppMetrics;
63
+ /** Get endpoint statistics (without internal durations array) */
64
+ getEndpointStats(): EndpointStats[];
65
+ /**
66
+ * Apply runtime details that the host application only knows after
67
+ * boot (e.g. the actual port returned by `app.listen()`, total startup
68
+ * duration, count of registered interceptors).
69
+ *
70
+ * Called by the adapter integration once the HTTP server is listening.
71
+ * Re-broadcasts the updated runtime info so connected Studio clients
72
+ * see fresh values without waiting for the next metrics tick.
73
+ */
74
+ updateRuntimeInfo(patch: {
75
+ appPort?: number;
76
+ globalPrefix?: string;
77
+ startupMs?: number;
78
+ interceptorCount?: number;
79
+ providerCount?: number;
80
+ middlewareCount?: number;
81
+ runtimeItems?: import('./types/index.js').RuntimeItems;
82
+ }): void;
83
+ /**
84
+ * Build a snapshot of runtime information for the Status dashboard.
85
+ *
86
+ * Pulls together:
87
+ * - host process info (`pid`, `nodeVersion`, `platform`, etc.)
88
+ * - explicit values passed via `AgentConfig` (port, prefix, startupMs)
89
+ * - counts derived from the latest discovery scan
90
+ * - best-effort framework versions from the host's `node_modules`
91
+ *
92
+ * Designed to be cheap to call on every WebSocket connection.
93
+ */
94
+ getRuntimeInfo(): RuntimeInfo;
95
+ /** Start WebSocket server */
96
+ private startWebSocketServer;
97
+ /**
98
+ * `httpServer.listen()` that survives transient `EADDRINUSE` from
99
+ * hot-reload races — when `tsx --watch` (or nodemon) restarts the host
100
+ * process before the previous run has released the agent port. We
101
+ * retry a few times with exponential-ish backoff before giving up.
102
+ *
103
+ * On final failure throws an `Error` whose `.code` is preserved so
104
+ * the integration layer (`@expressots/adapter-express`) can decide
105
+ * whether to surface it; today it just logs a warning and the host
106
+ * app keeps running (Studio is opt-in dev tooling).
107
+ */
108
+ private listenWithRetry;
109
+ /** Single attempt — resolves on `listening`, rejects on `error`. */
110
+ private listenOnce;
111
+ /** Handle incoming trace */
112
+ private handleTrace;
113
+ /** Update endpoint statistics */
114
+ private updateEndpointStats;
115
+ /** Replay a recorded request */
116
+ private replayRequest;
117
+ /** Broadcast message to all connected clients */
118
+ private broadcast;
119
+ /** Create WebSocket message */
120
+ private createMessage;
121
+ /** Start metrics collection interval */
122
+ private startMetricsCollection;
123
+ /** Create Express middleware for request/response recording */
124
+ createMiddleware(): (req: any, res: any, next: any) => any;
125
+ }
126
+ export type { LogEntry };
127
+ //# sourceMappingURL=agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAKrE,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EAET,YAAY,EACZ,UAAU,EACV,aAAa,EAGb,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAgE1B,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAkB;IAClC,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,EAAE,CAA+B;IACzC,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,GAAE,OAAO,CAAC,WAAW,CAAM;IA2C7C,6BAA6B;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoD5B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IA2B3B,oEAAoE;IACpE,oBAAoB,IAAI,iBAAiB,GAAG,IAAI;IAIhD,4BAA4B;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B3B;;;;;;;;OAQG;YACW,uBAAuB;IAyCrC,kCAAkC;IAC5B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BjC,4BAA4B;IAC5B,SAAS,IAAI,SAAS,EAAE;IAIxB,gCAAgC;IAChC,eAAe,IAAI,YAAY,GAAG,IAAI;IAItC,0BAA0B;IAC1B,UAAU,IAAI,UAAU;IAQxB,iEAAiE;IACjE,gBAAgB,IAAI,aAAa,EAAE;IAInC;;;;;;;;OAQG;IACH,iBAAiB,CAAC,KAAK,EAAE;QACvB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,OAAO,kBAAkB,EAAE,YAAY,CAAC;KACxD,GAAG,IAAI;IA0BR;;;;;;;;;;OAUG;IACH,cAAc,IAAI,WAAW;IAgE7B,6BAA6B;YACf,oBAAoB;IA+RlC;;;;;;;;;;OAUG;YACW,eAAe;IA2B7B,oEAAoE;IACpE,OAAO,CAAC,UAAU;IAgBlB,4BAA4B;IAC5B,OAAO,CAAC,WAAW;IAkCnB,iCAAiC;IACjC,OAAO,CAAC,mBAAmB;IA6D3B,gCAAgC;YAClB,aAAa;IAqF3B,iDAAiD;IACjD,OAAO,CAAC,SAAS;IAMjB,+BAA+B;IAC/B,OAAO,CAAC,aAAa;IAQrB,wCAAwC;IACxC,OAAO,CAAC,sBAAsB;IAwB9B,+DAA+D;IAC/D,gBAAgB,KACN,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,GAAG;CA+JxC;AAED,YAAY,EAAE,QAAQ,EAAE,CAAC"}