offrouter-core 0.2.0 → 0.2.2
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/dist/audit.d.ts +137 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +320 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/credential-chain.d.ts +84 -0
- package/dist/auth/credential-chain.d.ts.map +1 -0
- package/dist/auth/credential-chain.js +150 -0
- package/dist/auth/credential-chain.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +7 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychain.d.ts +60 -0
- package/dist/auth/keychain.d.ts.map +1 -0
- package/dist/auth/keychain.js +104 -0
- package/dist/auth/keychain.js.map +1 -0
- package/dist/auth/oauth-pkce.d.ts +138 -0
- package/dist/auth/oauth-pkce.d.ts.map +1 -0
- package/dist/auth/oauth-pkce.js +375 -0
- package/dist/auth/oauth-pkce.js.map +1 -0
- package/dist/auth/oauth-server.d.ts +36 -0
- package/dist/auth/oauth-server.d.ts.map +1 -0
- package/dist/auth/oauth-server.js +210 -0
- package/dist/auth/oauth-server.js.map +1 -0
- package/dist/config.d.ts +76 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +355 -0
- package/dist/config.js.map +1 -0
- package/dist/delegation.d.ts +123 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +455 -0
- package/dist/delegation.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/policy.d.ts +45 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +318 -0
- package/dist/policy.js.map +1 -0
- package/{src/protocol.ts → dist/protocol.d.ts} +2 -2
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +6 -0
- package/dist/protocol.js.map +1 -0
- package/dist/providers/adapter.d.ts +137 -0
- package/dist/providers/adapter.d.ts.map +1 -0
- package/dist/providers/adapter.js +163 -0
- package/dist/providers/adapter.js.map +1 -0
- package/dist/providers/catalog-data.d.ts +128 -0
- package/dist/providers/catalog-data.d.ts.map +1 -0
- package/dist/providers/catalog-data.js +397 -0
- package/dist/providers/catalog-data.js.map +1 -0
- package/dist/providers/catalog.d.ts +63 -0
- package/dist/providers/catalog.d.ts.map +1 -0
- package/dist/providers/catalog.js +394 -0
- package/dist/providers/catalog.js.map +1 -0
- package/dist/providers/fake.d.ts +46 -0
- package/dist/providers/fake.d.ts.map +1 -0
- package/dist/providers/fake.js +234 -0
- package/dist/providers/fake.js.map +1 -0
- package/dist/providers/openai-compatible.d.ts +65 -0
- package/dist/providers/openai-compatible.d.ts.map +1 -0
- package/dist/providers/openai-compatible.js +434 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/proxy/responses-mapper.d.ts +366 -0
- package/dist/proxy/responses-mapper.d.ts.map +1 -0
- package/dist/proxy/responses-mapper.js +517 -0
- package/dist/proxy/responses-mapper.js.map +1 -0
- package/dist/proxy/responses-server.d.ts +29 -0
- package/dist/proxy/responses-server.d.ts.map +1 -0
- package/dist/proxy/responses-server.js +360 -0
- package/dist/proxy/responses-server.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +296 -0
- package/dist/router.js.map +1 -0
- package/dist/schemas.d.ts +560 -0
- package/dist/schemas.d.ts.map +1 -0
- package/{src/schemas.ts → dist/schemas.js} +83 -103
- package/dist/schemas.js.map +1 -0
- package/dist/secrets.d.ts +112 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +326 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +117 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/usage-file.d.ts +59 -0
- package/dist/usage-file.d.ts.map +1 -0
- package/dist/usage-file.js +316 -0
- package/dist/usage-file.js.map +1 -0
- package/dist/usage.d.ts +235 -0
- package/dist/usage.d.ts.map +1 -0
- package/dist/usage.js +517 -0
- package/dist/usage.js.map +1 -0
- package/package.json +9 -4
- package/src/audit.test.ts +0 -302
- package/src/audit.ts +0 -553
- package/src/auth/credential-chain.test.ts +0 -326
- package/src/auth/credential-chain.ts +0 -235
- package/src/auth/index.ts +0 -45
- package/src/auth/keychain.test.ts +0 -265
- package/src/auth/keychain.ts +0 -168
- package/src/auth/oauth-pkce.test.ts +0 -329
- package/src/auth/oauth-pkce.ts +0 -571
- package/src/auth/oauth-server.test.ts +0 -83
- package/src/auth/oauth-server.ts +0 -296
- package/src/config.test.ts +0 -479
- package/src/config.ts +0 -505
- package/src/delegation.test.ts +0 -368
- package/src/delegation.ts +0 -605
- package/src/index.ts +0 -280
- package/src/policy.test.ts +0 -634
- package/src/policy.ts +0 -420
- package/src/providers/adapter.test.ts +0 -293
- package/src/providers/adapter.ts +0 -328
- package/src/providers/catalog-data.test.ts +0 -258
- package/src/providers/catalog-data.ts +0 -498
- package/src/providers/catalog.test.ts +0 -84
- package/src/providers/catalog.ts +0 -483
- package/src/providers/fake.ts +0 -312
- package/src/providers/openai-compatible.test.ts +0 -366
- package/src/providers/openai-compatible.ts +0 -554
- package/src/proxy/responses-mapper.test.ts +0 -290
- package/src/proxy/responses-mapper.ts +0 -736
- package/src/proxy/responses-server.test.ts +0 -322
- package/src/proxy/responses-server.ts +0 -469
- package/src/router.test.ts +0 -699
- package/src/router.ts +0 -352
- package/src/schemas.test.ts +0 -291
- package/src/secrets.test.ts +0 -271
- package/src/secrets.ts +0 -461
- package/src/types.ts +0 -173
- package/src/usage-file.test.ts +0 -243
- package/src/usage-file.ts +0 -435
- package/src/usage.test.ts +0 -335
- package/src/usage.ts +0 -859
- package/tsconfig.json +0 -9
package/src/audit.test.ts
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { ROUTE_PROTOCOL_VERSION } from "./protocol.js";
|
|
3
|
-
import { InMemoryAuditStore, type AuditRecord } from "./audit.js";
|
|
4
|
-
import type { RouteDecision, RouteRequest } from "./types.js";
|
|
5
|
-
|
|
6
|
-
const RAW_PROMPT =
|
|
7
|
-
"super secret user intent with sk-ant-api03-abcdefghijklmnopqrstuv embedded";
|
|
8
|
-
const PROMPT_DIGEST = "sha256:deadbeefcafebabe";
|
|
9
|
-
|
|
10
|
-
function baseRequest(overrides: Partial<RouteRequest> = {}): RouteRequest {
|
|
11
|
-
return {
|
|
12
|
-
protocolVersion: ROUTE_PROTOCOL_VERSION,
|
|
13
|
-
requestId: "req_1",
|
|
14
|
-
harness: { kind: "claude", profile: "claude-personal" },
|
|
15
|
-
task: {
|
|
16
|
-
promptPreview: RAW_PROMPT,
|
|
17
|
-
promptDigest: PROMPT_DIGEST,
|
|
18
|
-
kind: "explain",
|
|
19
|
-
risk: "low",
|
|
20
|
-
},
|
|
21
|
-
workspace: { cwd: "/tmp/project", trusted: true },
|
|
22
|
-
constraints: { subscriptionFirst: true, allowApiKeyFallback: true },
|
|
23
|
-
...overrides,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function baseDecision(
|
|
28
|
-
overrides: Partial<RouteDecision> = {},
|
|
29
|
-
): RouteDecision {
|
|
30
|
-
return {
|
|
31
|
-
protocolVersion: ROUTE_PROTOCOL_VERSION,
|
|
32
|
-
decisionId: "dec_1",
|
|
33
|
-
requestId: "req_1",
|
|
34
|
-
route: {
|
|
35
|
-
provider: "claude",
|
|
36
|
-
model: "sonnet",
|
|
37
|
-
authTier: "subscription",
|
|
38
|
-
authScope: "first-party",
|
|
39
|
-
},
|
|
40
|
-
reason: "subscription_preferred",
|
|
41
|
-
explanation: "Selected healthy subscription candidate.",
|
|
42
|
-
fallbackChain: [
|
|
43
|
-
{
|
|
44
|
-
provider: "claude",
|
|
45
|
-
model: "sonnet",
|
|
46
|
-
authTier: "subscription",
|
|
47
|
-
reason: "primary",
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
auditSummary: `route=claude/sonnet tier=subscription profile=claude-personal digest=${PROMPT_DIGEST}`,
|
|
51
|
-
blocked: false,
|
|
52
|
-
needsConfiguration: false,
|
|
53
|
-
...overrides,
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function collectText(record: AuditRecord): string {
|
|
58
|
-
return [
|
|
59
|
-
record.recordId,
|
|
60
|
-
record.kind,
|
|
61
|
-
record.requestId,
|
|
62
|
-
record.decisionId,
|
|
63
|
-
record.profile,
|
|
64
|
-
record.promptDigest,
|
|
65
|
-
record.promptPreviewRedacted,
|
|
66
|
-
record.auditSummary,
|
|
67
|
-
record.reason,
|
|
68
|
-
record.explanation,
|
|
69
|
-
record.apiKeyFallbackReason,
|
|
70
|
-
...(record.policyDenials?.map((d) => d.message) ?? []),
|
|
71
|
-
JSON.stringify(record.route),
|
|
72
|
-
JSON.stringify(record.fallbackChain),
|
|
73
|
-
]
|
|
74
|
-
.filter((s): s is string => typeof s === "string")
|
|
75
|
-
.join("\n");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
describe("InMemoryAuditStore", () => {
|
|
79
|
-
it("appends a redacted decision with digest, not raw prompt text", async () => {
|
|
80
|
-
const store = new InMemoryAuditStore();
|
|
81
|
-
const request = baseRequest();
|
|
82
|
-
const decision = baseDecision();
|
|
83
|
-
|
|
84
|
-
const record = await store.appendDecision(request, decision);
|
|
85
|
-
|
|
86
|
-
expect(record.promptDigest).toBe(PROMPT_DIGEST);
|
|
87
|
-
expect(record.auditSummary).toContain(PROMPT_DIGEST);
|
|
88
|
-
expect(record.promptPreviewRedacted).toBeUndefined();
|
|
89
|
-
expect(collectText(record)).not.toContain(RAW_PROMPT);
|
|
90
|
-
expect(collectText(record)).not.toContain(
|
|
91
|
-
"sk-ant-api03-abcdefghijklmnopqrstuv",
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const listed = await store.list({ requestId: "req_1" });
|
|
95
|
-
expect(listed).toHaveLength(1);
|
|
96
|
-
expect(listed[0]?.promptDigest).toBe(PROMPT_DIGEST);
|
|
97
|
-
expect(collectText(listed[0]!)).not.toContain(RAW_PROMPT);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("records API-key fallback reason when paid capacity is selected", async () => {
|
|
101
|
-
const store = new InMemoryAuditStore();
|
|
102
|
-
const request = baseRequest({ requestId: "req_fallback" });
|
|
103
|
-
const decision = baseDecision({
|
|
104
|
-
decisionId: "dec_fallback",
|
|
105
|
-
requestId: "req_fallback",
|
|
106
|
-
route: {
|
|
107
|
-
provider: "openai",
|
|
108
|
-
model: "gpt-4o-mini",
|
|
109
|
-
authTier: "api-key",
|
|
110
|
-
authScope: "third-party",
|
|
111
|
-
},
|
|
112
|
-
reason: "api_key_fallback",
|
|
113
|
-
explanation:
|
|
114
|
-
"Selected API-key candidate openai/gpt-4o-mini after subscription capacity was exhausted.",
|
|
115
|
-
fallbackChain: [
|
|
116
|
-
{
|
|
117
|
-
provider: "openai",
|
|
118
|
-
model: "gpt-4o-mini",
|
|
119
|
-
authTier: "api-key",
|
|
120
|
-
reason: "api_key_fallback",
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
provider: "claude",
|
|
124
|
-
model: "sonnet",
|
|
125
|
-
authTier: "subscription",
|
|
126
|
-
reason: "subscription_exhausted",
|
|
127
|
-
},
|
|
128
|
-
],
|
|
129
|
-
auditSummary: `route=openai/gpt-4o-mini tier=api-key profile=claude-personal digest=${PROMPT_DIGEST}`,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const record = await store.appendDecision(request, decision);
|
|
133
|
-
|
|
134
|
-
expect(record.kind).toBe("api_key_fallback");
|
|
135
|
-
expect(record.apiKeyFallbackReason).toBe("api_key_fallback");
|
|
136
|
-
expect(record.route?.authTier).toBe("api-key");
|
|
137
|
-
expect(record.explanation).toMatch(/API-key|subscription/i);
|
|
138
|
-
expect(collectText(record)).not.toContain(RAW_PROMPT);
|
|
139
|
-
|
|
140
|
-
// Explicit recorder path also works.
|
|
141
|
-
const explicit = await store.recordApiKeyFallback({
|
|
142
|
-
requestId: "req_fallback",
|
|
143
|
-
decisionId: "dec_fallback",
|
|
144
|
-
reason: "subscription_exhausted",
|
|
145
|
-
explanation: "Subscription quota exhausted; using paid API-key route.",
|
|
146
|
-
route: decision.route,
|
|
147
|
-
promptDigest: PROMPT_DIGEST,
|
|
148
|
-
});
|
|
149
|
-
expect(explicit.kind).toBe("api_key_fallback");
|
|
150
|
-
expect(explicit.apiKeyFallbackReason).toBe("subscription_exhausted");
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it("redacts explicit API-key fallback reason fields", async () => {
|
|
154
|
-
const secret = "sk-fallback-reason-secret-abcdefghijklmnopqrstuvwxyz";
|
|
155
|
-
const store = new InMemoryAuditStore({ knownSecrets: [secret] });
|
|
156
|
-
|
|
157
|
-
const record = await store.recordApiKeyFallback({
|
|
158
|
-
requestId: "req_fallback_secret",
|
|
159
|
-
decisionId: "dec_fallback_secret",
|
|
160
|
-
reason: `subscription exhausted after provider returned ${secret}`,
|
|
161
|
-
explanation: `fallback activated after ${secret}`,
|
|
162
|
-
promptDigest: "sha256:fallback-secret",
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
expect(record.reason).not.toContain(secret);
|
|
166
|
-
expect(record.explanation).not.toContain(secret);
|
|
167
|
-
expect(record.apiKeyFallbackReason).not.toContain(secret);
|
|
168
|
-
expect(JSON.stringify(record)).not.toContain(secret);
|
|
169
|
-
expect(record.apiKeyFallbackReason).toContain("[REDACTED]");
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it("tracks cancellation state for an in-process request", async () => {
|
|
173
|
-
const store = new InMemoryAuditStore();
|
|
174
|
-
const request = baseRequest({ requestId: "req_cancel" });
|
|
175
|
-
const decision = baseDecision({
|
|
176
|
-
decisionId: "dec_cancel",
|
|
177
|
-
requestId: "req_cancel",
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
await store.appendDecision(request, decision);
|
|
181
|
-
await expect(store.getCancellationState("req_cancel")).resolves.toBe(
|
|
182
|
-
"active",
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
const cancelled = await store.markCancelled("req_cancel", {
|
|
186
|
-
reason: "user_cancelled",
|
|
187
|
-
});
|
|
188
|
-
expect(cancelled?.kind).toBe("cancellation");
|
|
189
|
-
expect(cancelled?.cancellationState).toBe("cancelled");
|
|
190
|
-
expect(cancelled?.cancelledAt).toBeTruthy();
|
|
191
|
-
await expect(store.getCancellationState("req_cancel")).resolves.toBe(
|
|
192
|
-
"cancelled",
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
// Completed does not override cancelled.
|
|
196
|
-
const after = await store.markCompleted("req_cancel");
|
|
197
|
-
expect(after?.cancellationState).toBe("cancelled");
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it("marks completed on successful finish without leaking prompt", async () => {
|
|
201
|
-
const store = new InMemoryAuditStore();
|
|
202
|
-
const request = baseRequest({ requestId: "req_done" });
|
|
203
|
-
await store.appendDecision(
|
|
204
|
-
request,
|
|
205
|
-
baseDecision({ decisionId: "dec_done", requestId: "req_done" }),
|
|
206
|
-
);
|
|
207
|
-
|
|
208
|
-
const done = await store.markCompleted("req_done");
|
|
209
|
-
expect(done?.cancellationState).toBe("completed");
|
|
210
|
-
expect(collectText(done!)).not.toContain(RAW_PROMPT);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("never leaks raw prompt or secrets by default (including toJSON)", async () => {
|
|
214
|
-
const secret = "sk-super-secret-value-never-in-audit-012345";
|
|
215
|
-
const store = new InMemoryAuditStore({ knownSecrets: [secret] });
|
|
216
|
-
const prompt = `please use ${secret} and do bad things`;
|
|
217
|
-
const request = baseRequest({
|
|
218
|
-
requestId: "req_secret",
|
|
219
|
-
task: {
|
|
220
|
-
promptPreview: prompt,
|
|
221
|
-
promptDigest: "sha256:secrets",
|
|
222
|
-
kind: "code",
|
|
223
|
-
risk: "high",
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
const decision = baseDecision({
|
|
227
|
-
decisionId: "dec_secret",
|
|
228
|
-
requestId: "req_secret",
|
|
229
|
-
explanation: `Failed with key ${secret} mid-flight`,
|
|
230
|
-
auditSummary: `digest=sha256:secrets secret=${secret}`,
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
const record = await store.appendDecision(request, decision);
|
|
234
|
-
const blob = collectText(record);
|
|
235
|
-
expect(blob).not.toContain(secret);
|
|
236
|
-
expect(blob).not.toContain(prompt);
|
|
237
|
-
expect(blob).toMatch(/\[REDACTED\]/);
|
|
238
|
-
|
|
239
|
-
const json = JSON.stringify(store.toJSON());
|
|
240
|
-
expect(json).not.toContain(secret);
|
|
241
|
-
expect(json).not.toContain(prompt);
|
|
242
|
-
expect(json).not.toContain(RAW_PROMPT);
|
|
243
|
-
// Digests are fine.
|
|
244
|
-
expect(store.toJSON()).toMatchObject({
|
|
245
|
-
kind: "in-memory-audit",
|
|
246
|
-
digests: expect.arrayContaining(["sha256:secrets"]),
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
it("stores opt-in redacted previews only, truncated and scrubbed", async () => {
|
|
251
|
-
const store = new InMemoryAuditStore();
|
|
252
|
-
const prompt =
|
|
253
|
-
"sk-ant-api03-abcdefghijklmnopqrstuv please analyze the large attached source listing for memory leaks";
|
|
254
|
-
const request = baseRequest({
|
|
255
|
-
requestId: "req_preview",
|
|
256
|
-
task: {
|
|
257
|
-
promptPreview: prompt,
|
|
258
|
-
promptDigest: "sha256:preview",
|
|
259
|
-
kind: "review",
|
|
260
|
-
risk: "medium",
|
|
261
|
-
},
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
const record = await store.appendDecision(
|
|
265
|
-
request,
|
|
266
|
-
baseDecision({
|
|
267
|
-
decisionId: "dec_preview",
|
|
268
|
-
requestId: "req_preview",
|
|
269
|
-
auditSummary: "digest=sha256:preview",
|
|
270
|
-
}),
|
|
271
|
-
{ includePromptPreview: true, previewMaxChars: 40 },
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
expect(record.promptPreviewRedacted).toBeDefined();
|
|
275
|
-
expect(record.promptPreviewRedacted!.length).toBeLessThanOrEqual(40);
|
|
276
|
-
expect(record.promptPreviewRedacted).not.toContain(
|
|
277
|
-
"sk-ant-api03-abcdefghijklmnopqrstuv",
|
|
278
|
-
);
|
|
279
|
-
expect(record.promptPreviewRedacted).toMatch(/\[REDACTED\]/);
|
|
280
|
-
// Full raw prompt still absent.
|
|
281
|
-
expect(collectText(record)).not.toContain(prompt);
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
it("serializes concurrent appends without losing records", async () => {
|
|
285
|
-
const store = new InMemoryAuditStore();
|
|
286
|
-
const n = 20;
|
|
287
|
-
await Promise.all(
|
|
288
|
-
Array.from({ length: n }, (_, i) =>
|
|
289
|
-
store.appendDecision(
|
|
290
|
-
baseRequest({ requestId: `req_conc_${i}` }),
|
|
291
|
-
baseDecision({
|
|
292
|
-
decisionId: `dec_conc_${i}`,
|
|
293
|
-
requestId: `req_conc_${i}`,
|
|
294
|
-
auditSummary: `digest=${PROMPT_DIGEST} n=${i}`,
|
|
295
|
-
}),
|
|
296
|
-
),
|
|
297
|
-
),
|
|
298
|
-
);
|
|
299
|
-
const all = await store.list();
|
|
300
|
-
expect(all).toHaveLength(n);
|
|
301
|
-
});
|
|
302
|
-
});
|