@sovara/runner 0.3.4 → 0.4.4
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 +24 -24
- 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/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 +23 -10
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +230 -56
- package/dist/context.js.map +1 -1
- package/dist/env.d.ts +0 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +0 -2
- 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 -30
- package/dist/runtimeClient.d.ts.map +1 -1
- package/dist/runtimeClient.js +105 -68
- 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 +2 -2
- 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/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/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
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
11
|
Documentation: https://docs.sovara-labs.com
|
|
@@ -68,7 +68,7 @@ Importing `@sovara/runner` patches two transports at module load:
|
|
|
68
68
|
Inside `withSovaraRun(...)`, calls to whitelisted endpoints are observed:
|
|
69
69
|
the runner builds a request envelope, posts it to the Sovara server's
|
|
70
70
|
`/internal/runner/llm/prepare` endpoint (which optionally applies
|
|
71
|
-
runtime preparation such as
|
|
71
|
+
runtime preparation such as lessons injection), executes the prepared
|
|
72
72
|
request on the wire, and records the response or exception. Outside the
|
|
73
73
|
run scope, both patches are no-ops.
|
|
74
74
|
|
|
@@ -95,6 +95,7 @@ completes.
|
|
|
95
95
|
```ts
|
|
96
96
|
export type WithSovaraRunOptions = {
|
|
97
97
|
name: string; // required run name
|
|
98
|
+
clientRunId?: string; // optional project-scoped correlation id
|
|
98
99
|
url?: string; // server URL (default: http://127.0.0.1:5959)
|
|
99
100
|
captureLogs?: boolean; // default: true
|
|
100
101
|
project?: { name?: string; description?: string };
|
|
@@ -116,14 +117,13 @@ whatever `fn` returns.
|
|
|
116
117
|
user setup is missing.
|
|
117
118
|
3. Resolves the project for `process.cwd()`. Fails hard with a `sovara init`
|
|
118
119
|
hint if the cwd isn't part of a known Sovara project.
|
|
119
|
-
4. Registers the run,
|
|
120
|
-
|
|
120
|
+
4. Registers the run, runs `fn` inside an `AsyncLocalStorage` scope, and
|
|
121
|
+
deregisters in `finally`.
|
|
121
122
|
|
|
122
|
-
**Nested call** (called from inside another `withSovaraRun`):
|
|
123
|
+
**Nested top-level call** (called from inside another `withSovaraRun`):
|
|
123
124
|
|
|
124
|
-
The nested call is
|
|
125
|
-
the
|
|
126
|
-
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.
|
|
127
127
|
|
|
128
128
|
`fn` exceptions propagate untouched. The run is deregistered in either
|
|
129
129
|
case.
|
|
@@ -132,12 +132,12 @@ case.
|
|
|
132
132
|
|
|
133
133
|
```ts
|
|
134
134
|
await withSovaraRun("pipeline", async () => {
|
|
135
|
-
const docs = await
|
|
136
|
-
await
|
|
135
|
+
const docs = await withSovaraSubrun("retrieve", async () => loadDocs());
|
|
136
|
+
await withSovaraSubrun("summarize", async () => summarize(docs));
|
|
137
137
|
});
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
-
Every
|
|
140
|
+
Every explicit `withSovaraSubrun` becomes a child run under the active scope.
|
|
141
141
|
|
|
142
142
|
### Logs
|
|
143
143
|
|
|
@@ -172,8 +172,8 @@ the `sovara init` requirement — they just refine the values.
|
|
|
172
172
|
### `trace(fn, options?)`
|
|
173
173
|
|
|
174
174
|
Use `trace` when an important custom function or tool does not make an LLM
|
|
175
|
-
HTTP call on its own, but should still appear as a tool
|
|
176
|
-
run
|
|
175
|
+
HTTP call on its own, but should still appear as a tool step in the Sovara
|
|
176
|
+
run steps.
|
|
177
177
|
|
|
178
178
|
```ts
|
|
179
179
|
import { trace, withSovaraRun } from "@sovara/runner";
|
|
@@ -191,15 +191,15 @@ await withSovaraRun("support-agent", async () => {
|
|
|
191
191
|
});
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
-
`trace` records function arguments as the
|
|
195
|
-
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
|
|
196
196
|
are recorded and the original exception is re-thrown. Outside an active
|
|
197
197
|
`withSovaraRun(...)` scope, the wrapper calls the function normally without
|
|
198
198
|
recording anything.
|
|
199
199
|
|
|
200
200
|
```ts
|
|
201
201
|
export type TraceOptions = {
|
|
202
|
-
name?: string; // display name for the tool
|
|
202
|
+
name?: string; // display name for the tool step, default: function/method name
|
|
203
203
|
meta?: Record<string, unknown>; // optional metadata stored with input/output
|
|
204
204
|
};
|
|
205
205
|
```
|
|
@@ -234,13 +234,13 @@ That single import change is the entire integration. The wrapper:
|
|
|
234
234
|
|
|
235
235
|
- Routes the spawned Claude CLI subprocess through Sovara's local Claude
|
|
236
236
|
proxy by setting `ANTHROPIC_BASE_URL` in the subprocess env, so every
|
|
237
|
-
LLM request the SDK makes shows up
|
|
237
|
+
LLM request the SDK makes shows up as a run step.
|
|
238
238
|
- Registers a unique proxy client with the server before each `query()` /
|
|
239
239
|
`startup()` so concurrent SDK calls don't collide.
|
|
240
240
|
- Sets `CLAUDE_CODE_ENTRYPOINT=sdk-ts` and `SOVARA_CLAUDE_PROXY_ACTIVE=1`
|
|
241
241
|
to mark the subprocess as running under Sovara.
|
|
242
242
|
- Wraps the returned `AsyncIterable` so each yielded message is parsed
|
|
243
|
-
for `tool_use` / `tool_result` blocks and recorded as
|
|
243
|
+
for `tool_use` / `tool_result` blocks and recorded as steps on the
|
|
244
244
|
run.
|
|
245
245
|
- Throws if `options.transport` (custom transport) is supplied — this
|
|
246
246
|
matches the Python runner's behavior.
|
|
@@ -285,13 +285,13 @@ for await (const message of handle.query("What did I install last?")) {
|
|
|
285
285
|
|
|
286
286
|
| Variable | Purpose | Default |
|
|
287
287
|
| ---------------------------- | -------------------------------------------------------------------------- | ----------------------- |
|
|
288
|
-
| `
|
|
289
|
-
| `
|
|
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
|
-
The TypeScript runner uses the local
|
|
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);
|
|
@@ -301,8 +301,8 @@ await withSovaraRun({ name: "demo", url: "http://localhost:6000" }, fn);
|
|
|
301
301
|
|
|
302
302
|
The runner fails fast for misconfiguration. Common ones:
|
|
303
303
|
|
|
304
|
-
- **`Sovara
|
|
305
|
-
|
|
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.
|
|
306
306
|
|
|
307
307
|
- **``Sovara project setup is missing for <cwd>. Run `sovara init --project-root "<cwd>" --project-name <project-name>` and retry.``**
|
|
308
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"}
|