@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.
- package/README.md +143 -0
- package/dist/agent.d.ts +127 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +1031 -0
- package/dist/agent.js.map +1 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +2 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/route-scanner.d.ts +35 -0
- package/dist/discovery/route-scanner.d.ts.map +1 -0
- package/dist/discovery/route-scanner.js +385 -0
- package/dist/discovery/route-scanner.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/index.d.ts +2 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +2 -0
- package/dist/instrumentation/index.js.map +1 -0
- package/dist/instrumentation/tracer.d.ts +40 -0
- package/dist/instrumentation/tracer.d.ts.map +1 -0
- package/dist/instrumentation/tracer.js +190 -0
- package/dist/instrumentation/tracer.js.map +1 -0
- package/dist/introspection/container-introspector.d.ts +81 -0
- package/dist/introspection/container-introspector.d.ts.map +1 -0
- package/dist/introspection/container-introspector.js +251 -0
- package/dist/introspection/container-introspector.js.map +1 -0
- package/dist/logging/log-capture.d.ts +58 -0
- package/dist/logging/log-capture.d.ts.map +1 -0
- package/dist/logging/log-capture.js +184 -0
- package/dist/logging/log-capture.js.map +1 -0
- package/dist/recording/index.d.ts +2 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +2 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/request-recorder.d.ts +43 -0
- package/dist/recording/request-recorder.d.ts.map +1 -0
- package/dist/recording/request-recorder.js +373 -0
- package/dist/recording/request-recorder.js.map +1 -0
- package/dist/security/fix-resolver.d.ts +40 -0
- package/dist/security/fix-resolver.d.ts.map +1 -0
- package/dist/security/fix-resolver.js +283 -0
- package/dist/security/fix-resolver.js.map +1 -0
- package/dist/security/fix-runner.d.ts +60 -0
- package/dist/security/fix-runner.d.ts.map +1 -0
- package/dist/security/fix-runner.js +188 -0
- package/dist/security/fix-runner.js.map +1 -0
- package/dist/security/index.d.ts +140 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +460 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/lockfile-graph.d.ts +69 -0
- package/dist/security/lockfile-graph.d.ts.map +1 -0
- package/dist/security/lockfile-graph.js +245 -0
- package/dist/security/lockfile-graph.js.map +1 -0
- package/dist/security/npm-audit.d.ts +67 -0
- package/dist/security/npm-audit.d.ts.map +1 -0
- package/dist/security/npm-audit.js +320 -0
- package/dist/security/npm-audit.js.map +1 -0
- package/dist/security/osv-cache.d.ts +51 -0
- package/dist/security/osv-cache.d.ts.map +1 -0
- package/dist/security/osv-cache.js +99 -0
- package/dist/security/osv-cache.js.map +1 -0
- package/dist/security/osv-client.d.ts +47 -0
- package/dist/security/osv-client.d.ts.map +1 -0
- package/dist/security/osv-client.js +247 -0
- package/dist/security/osv-client.js.map +1 -0
- package/dist/security/posture-analyzer.d.ts +44 -0
- package/dist/security/posture-analyzer.d.ts.map +1 -0
- package/dist/security/posture-analyzer.js +397 -0
- package/dist/security/posture-analyzer.js.map +1 -0
- package/dist/security/reachability.d.ts +59 -0
- package/dist/security/reachability.d.ts.map +1 -0
- package/dist/security/reachability.js +302 -0
- package/dist/security/reachability.js.map +1 -0
- package/dist/security/score.d.ts +36 -0
- package/dist/security/score.d.ts.map +1 -0
- package/dist/security/score.js +94 -0
- package/dist/security/score.js.map +1 -0
- package/dist/types/index.d.ts +587 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +14 -0
- package/dist/types/index.js.map +1 -0
- 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
|
package/dist/agent.d.ts
ADDED
|
@@ -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"}
|