@sovara/runner 0.3.3 → 0.4.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.
- package/README.md +30 -35
- package/dist/agentCredentials.d.ts +13 -0
- package/dist/agentCredentials.d.ts.map +1 -0
- package/dist/agentCredentials.js +90 -0
- package/dist/agentCredentials.js.map +1 -0
- package/dist/capture/responseEnvelope.d.ts +6 -1
- package/dist/capture/responseEnvelope.d.ts.map +1 -1
- package/dist/capture/responseEnvelope.js +24 -3
- package/dist/capture/responseEnvelope.js.map +1 -1
- package/dist/capture/sensitiveHeaders.d.ts +7 -0
- package/dist/capture/sensitiveHeaders.d.ts.map +1 -0
- package/dist/capture/sensitiveHeaders.js +41 -0
- package/dist/capture/sensitiveHeaders.js.map +1 -0
- package/dist/capture/streamNormalizers.d.ts +6 -0
- package/dist/capture/streamNormalizers.d.ts.map +1 -0
- package/dist/capture/streamNormalizers.js +301 -0
- package/dist/capture/streamNormalizers.js.map +1 -0
- package/dist/claudeAgentSdk/wrapper.d.ts.map +1 -1
- package/dist/claudeAgentSdk/wrapper.js +8 -20
- package/dist/claudeAgentSdk/wrapper.js.map +1 -1
- package/dist/codeVersion.d.ts +0 -2
- package/dist/codeVersion.d.ts.map +1 -1
- package/dist/codeVersion.js +0 -18
- package/dist/codeVersion.js.map +1 -1
- package/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -4
- package/dist/constants.js.map +1 -1
- package/dist/context.d.ts +24 -15
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +231 -62
- package/dist/context.js.map +1 -1
- package/dist/env.d.ts +1 -2
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +2 -75
- package/dist/env.js.map +1 -1
- package/dist/failsafe.d.ts +8 -0
- package/dist/failsafe.d.ts.map +1 -0
- package/dist/failsafe.js +28 -0
- package/dist/failsafe.js.map +1 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/lessons.d.ts +6 -0
- package/dist/lessons.d.ts.map +1 -0
- package/dist/lessons.js +68 -0
- package/dist/lessons.js.map +1 -0
- package/dist/lessonsBlockGuidance.json +3 -0
- package/dist/mcpPatch.d.ts.map +1 -1
- package/dist/mcpPatch.js +17 -13
- package/dist/mcpPatch.js.map +1 -1
- package/dist/payload/editIoExcludePatterns.json +3 -0
- package/dist/platformSession.d.ts +3 -0
- package/dist/platformSession.d.ts.map +1 -0
- package/dist/platformSession.js +15 -0
- package/dist/platformSession.js.map +1 -0
- package/dist/runtimeClient.d.ts +46 -36
- package/dist/runtimeClient.d.ts.map +1 -1
- package/dist/runtimeClient.js +106 -90
- package/dist/runtimeClient.js.map +1 -1
- package/dist/serverLifecycle.d.ts +11 -1
- package/dist/serverLifecycle.d.ts.map +1 -1
- package/dist/serverLifecycle.js +54 -14
- package/dist/serverLifecycle.js.map +1 -1
- package/dist/toolTraceUtils.d.ts.map +1 -1
- package/dist/toolTraceUtils.js +6 -4
- package/dist/toolTraceUtils.js.map +1 -1
- package/dist/transport/awsSdkHttp2Patch.d.ts.map +1 -1
- package/dist/transport/awsSdkHttp2Patch.js +34 -25
- package/dist/transport/awsSdkHttp2Patch.js.map +1 -1
- package/dist/transport/fetchPatch.d.ts.map +1 -1
- package/dist/transport/fetchPatch.js +33 -17
- package/dist/transport/fetchPatch.js.map +1 -1
- package/dist/transport/nodeHttpPatch.d.ts.map +1 -1
- package/dist/transport/nodeHttpPatch.js +19 -12
- package/dist/transport/nodeHttpPatch.js.map +1 -1
- package/package.json +5 -4
- package/dist/annotation.d.ts +0 -3
- package/dist/annotation.d.ts.map +0 -1
- package/dist/annotation.js +0 -27
- package/dist/annotation.js.map +0 -1
- package/dist/claudeAgentSdk/parseHook.d.ts +0 -20
- package/dist/claudeAgentSdk/parseHook.d.ts.map +0 -1
- package/dist/claudeAgentSdk/parseHook.js +0 -160
- package/dist/claudeAgentSdk/parseHook.js.map +0 -1
- package/dist/priors.d.ts +0 -7
- package/dist/priors.d.ts.map +0 -1
- package/dist/priors.js +0 -87
- package/dist/priors.js.map +0 -1
- package/dist/priorsBlockGuidance.json +0 -3
- package/dist/projectContext.d.ts +0 -14
- package/dist/projectContext.d.ts.map +0 -1
- package/dist/projectContext.js +0 -32
- package/dist/projectContext.js.map +0 -1
- package/dist/sseListener.d.ts +0 -9
- package/dist/sseListener.d.ts.map +0 -1
- package/dist/sseListener.js +0 -56
- package/dist/sseListener.js.map +0 -1
- package/dist/user.d.ts +0 -11
- package/dist/user.d.ts.map +0 -1
- package/dist/user.js +0 -31
- package/dist/user.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
# @sovara/runner
|
|
2
2
|
|
|
3
|
-
TypeScript runner for [Sovara](https://
|
|
3
|
+
TypeScript runner for [Sovara](https://docs.sovara-labs.com) — wrap your script in a
|
|
4
4
|
single function and Sovara records every LLM call, tool invocation, and log
|
|
5
|
-
line into
|
|
5
|
+
line into structured run steps.
|
|
6
6
|
|
|
7
7
|
The runner is the TS counterpart to the Python `sovara` runner. It targets
|
|
8
|
-
the same Sovara server, produces the same
|
|
8
|
+
the same Sovara server, produces the same step shapes, and is safe to mix
|
|
9
9
|
with Python runs in the same project.
|
|
10
10
|
|
|
11
|
+
Documentation: https://docs.sovara-labs.com
|
|
12
|
+
|
|
11
13
|
## Requirements
|
|
12
14
|
|
|
13
15
|
- Node 18 or newer (built-in `fetch`).
|
|
14
|
-
- A reachable Sovara backend. For local use, open the Sovara desktop app
|
|
15
|
-
|
|
16
|
+
- A reachable Sovara backend. For local use, open the Sovara desktop app.
|
|
17
|
+
Enterprise agent projects should use their project-specific connection flow.
|
|
16
18
|
- A Sovara user and project, configured once with `sovara init`.
|
|
17
19
|
|
|
18
20
|
## Install
|
|
@@ -66,7 +68,7 @@ Importing `@sovara/runner` patches two transports at module load:
|
|
|
66
68
|
Inside `withSovaraRun(...)`, calls to whitelisted endpoints are observed:
|
|
67
69
|
the runner builds a request envelope, posts it to the Sovara server's
|
|
68
70
|
`/internal/runner/llm/prepare` endpoint (which optionally applies
|
|
69
|
-
runtime preparation such as
|
|
71
|
+
runtime preparation such as lessons injection), executes the prepared
|
|
70
72
|
request on the wire, and records the response or exception. Outside the
|
|
71
73
|
run scope, both patches are no-ops.
|
|
72
74
|
|
|
@@ -93,9 +95,9 @@ completes.
|
|
|
93
95
|
```ts
|
|
94
96
|
export type WithSovaraRunOptions = {
|
|
95
97
|
name: string; // required run name
|
|
96
|
-
|
|
98
|
+
clientRunId?: string; // optional project-scoped correlation id
|
|
99
|
+
url?: string; // server URL (default: http://127.0.0.1:5959)
|
|
97
100
|
captureLogs?: boolean; // default: true
|
|
98
|
-
user?: { fullName?: string; email?: string }; // overrides only
|
|
99
101
|
project?: { name?: string; description?: string };
|
|
100
102
|
};
|
|
101
103
|
|
|
@@ -115,14 +117,13 @@ whatever `fn` returns.
|
|
|
115
117
|
user setup is missing.
|
|
116
118
|
3. Resolves the project for `process.cwd()`. Fails hard with a `sovara init`
|
|
117
119
|
hint if the cwd isn't part of a known Sovara project.
|
|
118
|
-
4. Registers the run,
|
|
119
|
-
|
|
120
|
+
4. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
|
|
121
|
+
deregisters in `finally`.
|
|
120
122
|
|
|
121
|
-
**Nested call** (called from inside another `withSovaraRun`):
|
|
123
|
+
**Nested top-level call** (called from inside another `withSovaraRun`):
|
|
122
124
|
|
|
123
|
-
The nested call is
|
|
124
|
-
the
|
|
125
|
-
when `fn` returns.
|
|
125
|
+
The nested `withSovaraRun` call is ignored with a warning and `fn` runs inside
|
|
126
|
+
the existing active scope. Use `withSovaraSubrun(...)` for child execution.
|
|
126
127
|
|
|
127
128
|
`fn` exceptions propagate untouched. The run is deregistered in either
|
|
128
129
|
case.
|
|
@@ -131,12 +132,12 @@ case.
|
|
|
131
132
|
|
|
132
133
|
```ts
|
|
133
134
|
await withSovaraRun("pipeline", async () => {
|
|
134
|
-
const docs = await
|
|
135
|
-
await
|
|
135
|
+
const docs = await withSovaraSubrun("retrieve", async () => loadDocs());
|
|
136
|
+
await withSovaraSubrun("summarize", async () => summarize(docs));
|
|
136
137
|
});
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
Every
|
|
140
|
+
Every explicit `withSovaraSubrun` becomes a child run under the active scope.
|
|
140
141
|
|
|
141
142
|
### Logs
|
|
142
143
|
|
|
@@ -171,8 +172,8 @@ the `sovara init` requirement — they just refine the values.
|
|
|
171
172
|
### `trace(fn, options?)`
|
|
172
173
|
|
|
173
174
|
Use `trace` when an important custom function or tool does not make an LLM
|
|
174
|
-
HTTP call on its own, but should still appear as a tool
|
|
175
|
-
run
|
|
175
|
+
HTTP call on its own, but should still appear as a tool step in the Sovara
|
|
176
|
+
run steps.
|
|
176
177
|
|
|
177
178
|
```ts
|
|
178
179
|
import { trace, withSovaraRun } from "@sovara/runner";
|
|
@@ -190,15 +191,15 @@ await withSovaraRun("support-agent", async () => {
|
|
|
190
191
|
});
|
|
191
192
|
```
|
|
192
193
|
|
|
193
|
-
`trace` records function arguments as the
|
|
194
|
-
the
|
|
194
|
+
`trace` records function arguments as the step input and the return value as
|
|
195
|
+
the step output. If the wrapped function throws, the error type and message
|
|
195
196
|
are recorded and the original exception is re-thrown. Outside an active
|
|
196
197
|
`withSovaraRun(...)` scope, the wrapper calls the function normally without
|
|
197
198
|
recording anything.
|
|
198
199
|
|
|
199
200
|
```ts
|
|
200
201
|
export type TraceOptions = {
|
|
201
|
-
name?: string; // display name for the tool
|
|
202
|
+
name?: string; // display name for the tool step, default: function/method name
|
|
202
203
|
meta?: Record<string, unknown>; // optional metadata stored with input/output
|
|
203
204
|
};
|
|
204
205
|
```
|
|
@@ -233,13 +234,13 @@ That single import change is the entire integration. The wrapper:
|
|
|
233
234
|
|
|
234
235
|
- Routes the spawned Claude CLI subprocess through Sovara's local Claude
|
|
235
236
|
proxy by setting `ANTHROPIC_BASE_URL` in the subprocess env, so every
|
|
236
|
-
LLM request the SDK makes shows up
|
|
237
|
+
LLM request the SDK makes shows up as a run step.
|
|
237
238
|
- Registers a unique proxy client with the server before each `query()` /
|
|
238
239
|
`startup()` so concurrent SDK calls don't collide.
|
|
239
240
|
- Sets `CLAUDE_CODE_ENTRYPOINT=sdk-ts` and `SOVARA_CLAUDE_PROXY_ACTIVE=1`
|
|
240
241
|
to mark the subprocess as running under Sovara.
|
|
241
242
|
- Wraps the returned `AsyncIterable` so each yielded message is parsed
|
|
242
|
-
for `tool_use` / `tool_result` blocks and recorded as
|
|
243
|
+
for `tool_use` / `tool_result` blocks and recorded as steps on the
|
|
243
244
|
run.
|
|
244
245
|
- Throws if `options.transport` (custom transport) is supplied — this
|
|
245
246
|
matches the Python runner's behavior.
|
|
@@ -284,30 +285,24 @@ for await (const message of handle.query("What did I install last?")) {
|
|
|
284
285
|
|
|
285
286
|
| Variable | Purpose | Default |
|
|
286
287
|
| ---------------------------- | -------------------------------------------------------------------------- | ----------------------- |
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
| `PYTHON_PORT` | Server port (used to compute the default URL) | `5959` |
|
|
288
|
+
| `SOVARA_EXEC_SERVER_PORT` | Exec server port (used to compute the default URL) | `5960` |
|
|
289
|
+
| `SOVARA_EXEC_SERVER_URL` | Explicit exec server URL | `http://127.0.0.1:5960` |
|
|
290
290
|
| `SOVARA_CLAUDE_PROXY_ACTIVE` | Set automatically by `@sovara/runner/claude` to prevent nested proxy loops | unset |
|
|
291
291
|
|
|
292
292
|
### Server URL
|
|
293
293
|
|
|
294
|
-
|
|
294
|
+
The TypeScript runner uses the local exec server by default. Pass `url` per call only when the caller explicitly manages a different exec-server connection:
|
|
295
295
|
|
|
296
296
|
```ts
|
|
297
297
|
await withSovaraRun({ name: "demo", url: "http://localhost:6000" }, fn);
|
|
298
298
|
```
|
|
299
299
|
|
|
300
|
-
Or set `SOVARA_SERVER_URL` once for the process.
|
|
301
|
-
|
|
302
300
|
## Error messages
|
|
303
301
|
|
|
304
302
|
The runner fails fast for misconfiguration. Common ones:
|
|
305
303
|
|
|
306
|
-
- **`Sovara
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
- **``Sovara user setup is missing. Run `sovara init --user-name <full-name> --user-email <email>` and retry.``**
|
|
310
|
-
Run the suggested `sovara init` command.
|
|
304
|
+
- **`Sovara exec server is not reachable at <url>. Run sovara-exec-server start or use the Sovara CLI package that bundles it.`**
|
|
305
|
+
Start the exec server or pass `url` when the caller owns exec-server setup.
|
|
311
306
|
|
|
312
307
|
- **``Sovara project setup is missing for <cwd>. Run `sovara init --project-root "<cwd>" --project-name <project-name>` and retry.``**
|
|
313
308
|
Run the suggested `sovara init` command.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type ProjectConfig = {
|
|
2
|
+
projectName: string;
|
|
3
|
+
projectRoot: string;
|
|
4
|
+
};
|
|
5
|
+
export type AgentConnection = {
|
|
6
|
+
projectId: string;
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
agentToken: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function findProjectConfig(startPath: string): ProjectConfig | null;
|
|
11
|
+
export declare function resolveProjectConfig(cwd: string, explicitProjectName?: string): ProjectConfig;
|
|
12
|
+
export declare function loadAgentConnection(projectName: string, home?: string): AgentConnection | null;
|
|
13
|
+
//# sourceMappingURL=agentCredentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentCredentials.d.ts","sourceRoot":"","sources":["../src/agentCredentials.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,aAAa,GAAG;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAOF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwBzE;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,mBAAmB,CAAC,EAAE,MAAM,GAC3B,aAAa,CAYf;AAMD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,IAAI,GAAE,MAAkB,GACvB,eAAe,GAAG,IAAI,CA2CxB"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
function normalizeProjectName(projectName) {
|
|
5
|
+
const normalized = projectName?.trim();
|
|
6
|
+
return normalized ? normalized : null;
|
|
7
|
+
}
|
|
8
|
+
export function findProjectConfig(startPath) {
|
|
9
|
+
let current = resolve(startPath);
|
|
10
|
+
while (true) {
|
|
11
|
+
const configPath = join(current, ".sovara");
|
|
12
|
+
try {
|
|
13
|
+
if (existsSync(configPath) && statSync(configPath).isFile()) {
|
|
14
|
+
const projectName = normalizeProjectName(readFileSync(configPath, "utf8"));
|
|
15
|
+
if (!projectName) {
|
|
16
|
+
throw new Error("Project name is required.");
|
|
17
|
+
}
|
|
18
|
+
return { projectName, projectRoot: current };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
if (error instanceof Error) {
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
throw new Error("Could not read Sovara project configuration.");
|
|
26
|
+
}
|
|
27
|
+
const parent = dirname(current);
|
|
28
|
+
if (parent === current) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
current = parent;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function resolveProjectConfig(cwd, explicitProjectName) {
|
|
35
|
+
const explicit = normalizeProjectName(explicitProjectName);
|
|
36
|
+
const local = findProjectConfig(cwd);
|
|
37
|
+
if (explicit) {
|
|
38
|
+
return { projectName: explicit, projectRoot: local?.projectRoot ?? cwd };
|
|
39
|
+
}
|
|
40
|
+
if (local) {
|
|
41
|
+
return local;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Sovara project setup is missing for ${cwd}. Run \`sovara init --project-root ${JSON.stringify(cwd)} --project-name <project-name>\` and retry.`);
|
|
44
|
+
}
|
|
45
|
+
function credentialsPath(home) {
|
|
46
|
+
return join(home, ".sovara", "agent_connections.json");
|
|
47
|
+
}
|
|
48
|
+
export function loadAgentConnection(projectName, home = homedir()) {
|
|
49
|
+
const account = normalizeProjectName(projectName);
|
|
50
|
+
if (!account)
|
|
51
|
+
return null;
|
|
52
|
+
let raw;
|
|
53
|
+
try {
|
|
54
|
+
raw = readFileSync(credentialsPath(home), "utf8");
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
let payload;
|
|
60
|
+
try {
|
|
61
|
+
payload = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (typeof payload !== "object" || payload === null)
|
|
67
|
+
return null;
|
|
68
|
+
const entry = payload[account];
|
|
69
|
+
if (typeof entry !== "object" || entry === null)
|
|
70
|
+
return null;
|
|
71
|
+
const projectId = entry.project_id;
|
|
72
|
+
const serverUrl = entry.server_url;
|
|
73
|
+
const agentToken = entry.agent_token;
|
|
74
|
+
if (typeof projectId !== "string" ||
|
|
75
|
+
typeof serverUrl !== "string" ||
|
|
76
|
+
typeof agentToken !== "string") {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const normalizedProjectId = projectId.trim();
|
|
80
|
+
const normalizedUrl = serverUrl.trim().replace(/\/$/, "");
|
|
81
|
+
const normalizedToken = agentToken.trim();
|
|
82
|
+
if (!normalizedProjectId || !normalizedUrl || !normalizedToken)
|
|
83
|
+
return null;
|
|
84
|
+
return {
|
|
85
|
+
projectId: normalizedProjectId,
|
|
86
|
+
serverUrl: normalizedUrl,
|
|
87
|
+
agentToken: normalizedToken,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=agentCredentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentCredentials.js","sourceRoot":"","sources":["../src/agentCredentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,10 +4,15 @@ export type ResponseEnvelope = {
|
|
|
4
4
|
request_url: string | null;
|
|
5
5
|
headers: Record<string, string>;
|
|
6
6
|
content: unknown;
|
|
7
|
-
_response_format?: "sse";
|
|
7
|
+
_response_format?: "sse" | "ndjson";
|
|
8
8
|
sse?: {
|
|
9
9
|
events: Array<Record<string, unknown>>;
|
|
10
10
|
};
|
|
11
|
+
ndjson?: {
|
|
12
|
+
events: unknown[];
|
|
13
|
+
};
|
|
14
|
+
_stream_normalization?: Record<string, string>;
|
|
15
|
+
error?: unknown;
|
|
11
16
|
_encoding?: string;
|
|
12
17
|
};
|
|
13
18
|
export declare function buildResponseEnvelope(response: Response): Promise<ResponseEnvelope>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responseEnvelope.d.ts","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"responseEnvelope.d.ts","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IACpC,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;IACjD,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AA8BF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAqCzF"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { scrubSensitiveHeaders } from "./sensitiveHeaders.js";
|
|
2
|
+
import { parseSseEvents } from "./sseUtils.js";
|
|
3
|
+
import { normalizeNdjsonStream, normalizeSseStream, parseNdjsonEvents, } from "./streamNormalizers.js";
|
|
2
4
|
function headersToObject(headers) {
|
|
3
5
|
const out = {};
|
|
4
6
|
for (const [key, value] of headers.entries()) {
|
|
@@ -13,8 +15,20 @@ function inferEncoding(contentType) {
|
|
|
13
15
|
const match = /charset=([^;]+)/i.exec(contentType);
|
|
14
16
|
return match ? match[1].trim() : "utf-8";
|
|
15
17
|
}
|
|
18
|
+
function shouldParseNdjson(requestUrl) {
|
|
19
|
+
if (!requestUrl) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const path = new URL(requestUrl).pathname;
|
|
24
|
+
return path.endsWith("/api/chat") || path.endsWith("/api/generate");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
16
30
|
export async function buildResponseEnvelope(response) {
|
|
17
|
-
const headers = headersToObject(response.headers);
|
|
31
|
+
const headers = scrubSensitiveHeaders(headersToObject(response.headers));
|
|
18
32
|
const contentType = response.headers.get("content-type");
|
|
19
33
|
const text = await response.text();
|
|
20
34
|
const envelope = {
|
|
@@ -28,8 +42,15 @@ export async function buildResponseEnvelope(response) {
|
|
|
28
42
|
const parsedSse = parseSseEvents(text);
|
|
29
43
|
if (parsedSse !== null) {
|
|
30
44
|
envelope._response_format = "sse";
|
|
31
|
-
envelope.content = extractSseDisplayContent(parsedSse);
|
|
32
45
|
envelope.sse = { events: parsedSse };
|
|
46
|
+
Object.assign(envelope, normalizeSseStream(parsedSse, envelope.request_url));
|
|
47
|
+
return envelope;
|
|
48
|
+
}
|
|
49
|
+
const parsedNdjson = shouldParseNdjson(envelope.request_url) ? parseNdjsonEvents(text) : null;
|
|
50
|
+
if (parsedNdjson !== null) {
|
|
51
|
+
envelope._response_format = "ndjson";
|
|
52
|
+
envelope.ndjson = { events: parsedNdjson };
|
|
53
|
+
Object.assign(envelope, normalizeNdjsonStream(parsedNdjson, envelope.request_url));
|
|
33
54
|
return envelope;
|
|
34
55
|
}
|
|
35
56
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responseEnvelope.js","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"responseEnvelope.js","sourceRoot":"","sources":["../../src/capture/responseEnvelope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function isSensitiveHeader(name: string): boolean;
|
|
2
|
+
export declare function scrubSensitiveHeaders(headers: Record<string, string>): Record<string, string>;
|
|
3
|
+
export declare function scrubRequestEnvelopeForStorage<T extends {
|
|
4
|
+
headers: Record<string, string>;
|
|
5
|
+
}>(envelope: T): T;
|
|
6
|
+
export declare function extractSensitiveHeaders(headers: Record<string, string>): Record<string, string>;
|
|
7
|
+
//# sourceMappingURL=sensitiveHeaders.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitiveHeaders.d.ts","sourceRoot":"","sources":["../../src/capture/sensitiveHeaders.ts"],"names":[],"mappings":"AAcA,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB;AAKD,wBAAgB,8BAA8B,CAAC,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,EAC1F,QAAQ,EAAE,CAAC,GACV,CAAC,CAEH;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Provider auth / session headers that must never be persisted to recorded
|
|
2
|
+
// trace data. They are stripped from stored request/response envelopes; replay
|
|
3
|
+
// re-supplies auth from the user's configured API key, so dropping them here is
|
|
4
|
+
// safe. Live outbound requests keep their headers (see fetch patch re-merge).
|
|
5
|
+
const SENSITIVE_HEADER_NAMES = new Set([
|
|
6
|
+
"authorization",
|
|
7
|
+
"proxy-authorization",
|
|
8
|
+
"x-api-key",
|
|
9
|
+
"api-key",
|
|
10
|
+
"x-goog-api-key",
|
|
11
|
+
"cookie",
|
|
12
|
+
"set-cookie",
|
|
13
|
+
]);
|
|
14
|
+
export function isSensitiveHeader(name) {
|
|
15
|
+
return SENSITIVE_HEADER_NAMES.has(name.toLowerCase());
|
|
16
|
+
}
|
|
17
|
+
export function scrubSensitiveHeaders(headers) {
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
20
|
+
if (!isSensitiveHeader(key)) {
|
|
21
|
+
out[key] = value;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
// Returns a copy of a request envelope safe to persist: auth headers are
|
|
27
|
+
// stripped while the original (used for the live outbound request) is left
|
|
28
|
+
// untouched. Generic over the envelope shape to avoid an import cycle.
|
|
29
|
+
export function scrubRequestEnvelopeForStorage(envelope) {
|
|
30
|
+
return { ...envelope, headers: scrubSensitiveHeaders(envelope.headers) };
|
|
31
|
+
}
|
|
32
|
+
export function extractSensitiveHeaders(headers) {
|
|
33
|
+
const out = {};
|
|
34
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
35
|
+
if (isSensitiveHeader(key)) {
|
|
36
|
+
out[key] = value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=sensitiveHeaders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitiveHeaders.js","sourceRoot":"","sources":["../../src/capture/sensitiveHeaders.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,gFAAgF;AAChF,8EAA8E;AAC9E,MAAM,sBAAsB,GAAwB,IAAI,GAAG,CAAC;IAC1D,eAAe;IACf,qBAAqB;IACrB,WAAW;IACX,SAAS;IACT,gBAAgB;IAChB,QAAQ;IACR,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,OAA+B;IAE/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,yEAAyE;AACzE,2EAA2E;AAC3E,uEAAuE;AACvE,MAAM,UAAU,8BAA8B,CAC5C,QAAW;IAEX,OAAO,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;AAC3E,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAA+B;IAE/B,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,IAAI,iBAAiB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
type RecordValue = Record<string, unknown>;
|
|
2
|
+
export declare function normalizeSseStream(events: Array<RecordValue>, requestUrl: string | null): RecordValue;
|
|
3
|
+
export declare function parseNdjsonEvents(text: string): unknown[] | null;
|
|
4
|
+
export declare function normalizeNdjsonStream(events: unknown[], requestUrl: string | null): RecordValue;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=streamNormalizers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streamNormalizers.d.ts","sourceRoot":"","sources":["../../src/capture/streamNormalizers.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAiL3C,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,EAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,GACxB,WAAW,CA4Cb;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAchE;AA6CD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,CAoC/F"}
|