@curatedmcp/tokenshield-core 0.2.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.
Files changed (61) hide show
  1. package/dist/index.d.ts +15 -0
  2. package/dist/index.js +11 -0
  3. package/dist/index.js.map +1 -0
  4. package/dist/ledger.d.ts +33 -0
  5. package/dist/ledger.js +141 -0
  6. package/dist/ledger.js.map +1 -0
  7. package/dist/pricing.d.ts +5 -0
  8. package/dist/pricing.js +83 -0
  9. package/dist/pricing.js.map +1 -0
  10. package/dist/processors/conversation-dedup.d.ts +23 -0
  11. package/dist/processors/conversation-dedup.js +71 -0
  12. package/dist/processors/conversation-dedup.js.map +1 -0
  13. package/dist/processors/pipeline.d.ts +10 -0
  14. package/dist/processors/pipeline.js +89 -0
  15. package/dist/processors/pipeline.js.map +1 -0
  16. package/dist/processors/response-cache.d.ts +53 -0
  17. package/dist/processors/response-cache.js +129 -0
  18. package/dist/processors/response-cache.js.map +1 -0
  19. package/dist/processors/types.d.ts +54 -0
  20. package/dist/processors/types.js +2 -0
  21. package/dist/processors/types.js.map +1 -0
  22. package/dist/providers/anthropic.d.ts +6 -0
  23. package/dist/providers/anthropic.js +216 -0
  24. package/dist/providers/anthropic.js.map +1 -0
  25. package/dist/providers/registry.d.ts +4 -0
  26. package/dist/providers/registry.js +7 -0
  27. package/dist/providers/registry.js.map +1 -0
  28. package/dist/providers/types.d.ts +79 -0
  29. package/dist/providers/types.js +2 -0
  30. package/dist/providers/types.js.map +1 -0
  31. package/dist/proxy/anthropic-passthrough.d.ts +13 -0
  32. package/dist/proxy/anthropic-passthrough.js +363 -0
  33. package/dist/proxy/anthropic-passthrough.js.map +1 -0
  34. package/dist/proxy/sse.d.ts +20 -0
  35. package/dist/proxy/sse.js +59 -0
  36. package/dist/proxy/sse.js.map +1 -0
  37. package/dist/proxy/usage.d.ts +25 -0
  38. package/dist/proxy/usage.js +82 -0
  39. package/dist/proxy/usage.js.map +1 -0
  40. package/dist/server.d.ts +18 -0
  41. package/dist/server.js +130 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/types.d.ts +36 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +38 -0
  47. package/src/index.ts +31 -0
  48. package/src/ledger.ts +232 -0
  49. package/src/pricing.ts +93 -0
  50. package/src/processors/conversation-dedup.ts +77 -0
  51. package/src/processors/pipeline.ts +104 -0
  52. package/src/processors/response-cache.ts +161 -0
  53. package/src/processors/types.ts +58 -0
  54. package/src/providers/anthropic.ts +236 -0
  55. package/src/providers/registry.ts +10 -0
  56. package/src/providers/types.ts +87 -0
  57. package/src/proxy/anthropic-passthrough.ts +393 -0
  58. package/src/proxy/sse.ts +58 -0
  59. package/src/proxy/usage.ts +98 -0
  60. package/src/server.ts +154 -0
  61. package/src/types.ts +47 -0
@@ -0,0 +1,129 @@
1
+ import { createHash } from "node:crypto";
2
+ import { canonicalize } from "../providers/anthropic.js";
3
+ /**
4
+ * Tiny, conservative response cache for the Anthropic JSON endpoint.
5
+ *
6
+ * Caches IFF: temperature === 0 AND stream === false. Anthropic only guarantees
7
+ * deterministic outputs under these conditions, so caching anything else risks
8
+ * serving a stale response a user wouldn't expect.
9
+ *
10
+ * Bounded by total byte budget (default 64 MB) with LRU eviction.
11
+ * Default TTL: 10 minutes.
12
+ */
13
+ export class ResponseCache {
14
+ maxBytes;
15
+ defaultTtlMs;
16
+ map = new Map();
17
+ currentBytes = 0;
18
+ hits = 0;
19
+ misses = 0;
20
+ constructor(maxBytes = 64 * 1024 * 1024, defaultTtlMs = 10 * 60 * 1000) {
21
+ this.maxBytes = maxBytes;
22
+ this.defaultTtlMs = defaultTtlMs;
23
+ }
24
+ static keyFor(body) {
25
+ if (!body || typeof body !== "object")
26
+ return null;
27
+ const obj = body;
28
+ if (obj["stream"] === true)
29
+ return null;
30
+ if (obj["temperature"] !== 0)
31
+ return null;
32
+ return createHash("sha256")
33
+ .update(canonicalize({
34
+ model: obj["model"] ?? null,
35
+ system: obj["system"] ?? null,
36
+ tools: obj["tools"] ?? null,
37
+ tool_choice: obj["tool_choice"] ?? null,
38
+ messages: obj["messages"] ?? [],
39
+ max_tokens: obj["max_tokens"] ?? null,
40
+ temperature: obj["temperature"] ?? null,
41
+ top_p: obj["top_p"] ?? null,
42
+ top_k: obj["top_k"] ?? null,
43
+ stop_sequences: obj["stop_sequences"] ?? null,
44
+ }))
45
+ .digest("hex");
46
+ }
47
+ /** Returns a hit if the body is cacheable AND fresh; otherwise null. */
48
+ lookup(body) {
49
+ const key = ResponseCache.keyFor(body);
50
+ if (key === null)
51
+ return null;
52
+ const entry = this.map.get(key);
53
+ if (entry === undefined) {
54
+ this.misses++;
55
+ return null;
56
+ }
57
+ const now = Date.now();
58
+ if (entry.expiresAt < now) {
59
+ this.evict(key, entry);
60
+ this.misses++;
61
+ return null;
62
+ }
63
+ // LRU: refresh insertion order
64
+ this.map.delete(key);
65
+ this.map.set(key, entry);
66
+ this.hits++;
67
+ return {
68
+ status: entry.status,
69
+ headers: entry.headers,
70
+ body: entry.body,
71
+ usage: entry.usage,
72
+ model: entry.model,
73
+ cachedAgoMs: now - entry.storedAt,
74
+ cachedBytes: entry.bytes,
75
+ };
76
+ }
77
+ /** Store a response for a cacheable request. No-op if request isn't cacheable. */
78
+ store(body, response, ttlMs) {
79
+ const key = ResponseCache.keyFor(body);
80
+ if (key === null)
81
+ return;
82
+ // Only cache 2xx; 4xx/5xx are likely transient or user errors
83
+ if (response.status < 200 || response.status >= 300)
84
+ return;
85
+ const bytes = response.body.length;
86
+ if (bytes > this.maxBytes / 4)
87
+ return; // skip absurdly large bodies
88
+ this.evictIfNeeded(bytes);
89
+ const now = Date.now();
90
+ const entry = {
91
+ status: response.status,
92
+ headers: { ...response.headers },
93
+ body: response.body,
94
+ storedAt: now,
95
+ expiresAt: now + (ttlMs ?? this.defaultTtlMs),
96
+ bytes,
97
+ usage: response.usage,
98
+ model: response.model,
99
+ };
100
+ // Drop pre-existing entry under the same key (refresh)
101
+ const prior = this.map.get(key);
102
+ if (prior !== undefined) {
103
+ this.currentBytes -= prior.bytes;
104
+ this.map.delete(key);
105
+ }
106
+ this.map.set(key, entry);
107
+ this.currentBytes += bytes;
108
+ }
109
+ evictIfNeeded(incomingBytes) {
110
+ while (this.currentBytes + incomingBytes > this.maxBytes && this.map.size > 0) {
111
+ const oldest = this.map.keys().next();
112
+ if (oldest.done === true)
113
+ break;
114
+ const key = oldest.value;
115
+ const e = this.map.get(key);
116
+ if (e === undefined)
117
+ break;
118
+ this.evict(key, e);
119
+ }
120
+ }
121
+ evict(key, entry) {
122
+ this.currentBytes -= entry.bytes;
123
+ this.map.delete(key);
124
+ }
125
+ stats() {
126
+ return { hits: this.hits, misses: this.misses, entries: this.map.size, bytes: this.currentBytes };
127
+ }
128
+ }
129
+ //# sourceMappingURL=response-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-cache.js","sourceRoot":"","sources":["../../src/processors/response-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { Conversation } from "../providers/types.js";
2
+ import type { ProviderId } from "../providers/types.js";
3
+ export interface ProcessorContext {
4
+ providerId: ProviderId;
5
+ /** Stable fingerprint of the conversation (system + first user message hash). */
6
+ conversationFingerprint: string;
7
+ /** Inbound request raw bytes (read-only). */
8
+ inboundBytes: number;
9
+ }
10
+ export interface ProcessorEffect {
11
+ /** Stable identifier for accounting (e.g. "conversation-dedup"). */
12
+ name: string;
13
+ /** Bytes saved by this processor's modifications. */
14
+ bytesSaved: number;
15
+ /** Optional structured detail for the dashboard / diff-mode. */
16
+ detail?: Record<string, unknown>;
17
+ }
18
+ export interface ProcessorResult {
19
+ /** Updated conversation (may be a new object; processors must return one). */
20
+ conversation: Conversation;
21
+ /** Effects applied (zero or one entry per call). */
22
+ effects: ProcessorEffect[];
23
+ }
24
+ /**
25
+ * A request-side processor inspects + rewrites the conversation BEFORE it goes
26
+ * upstream. It must be:
27
+ * - deterministic: same input → same output, so prompt caching still hits
28
+ * - fail-open: any throw is caught by the pipeline; original conversation is preserved
29
+ * - bounded: never adds unbounded latency (no network calls in v0.2)
30
+ */
31
+ export interface Processor {
32
+ readonly id: string;
33
+ readonly enabledByDefault: boolean;
34
+ /**
35
+ * Mutate the conversation if appropriate. MUST return a new conversation
36
+ * (or the same reference if no change). Any throw is treated as a soft
37
+ * failure: the pipeline reverts to the input and trips the breaker.
38
+ */
39
+ onRequest(conversation: Conversation, ctx: ProcessorContext): ProcessorResult;
40
+ }
41
+ export interface PipelineOptions {
42
+ processors: Processor[];
43
+ enabled: Set<string>;
44
+ }
45
+ export interface PipelineRunResult {
46
+ conversation: Conversation;
47
+ effects: ProcessorEffect[];
48
+ bytesIn: number;
49
+ bytesOut: number;
50
+ errors: Array<{
51
+ processor: string;
52
+ message: string;
53
+ }>;
54
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/processors/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import type { Provider } from "./types.js";
2
+ declare function canonicalize(value: unknown): string;
3
+ declare function sha256(s: string): string;
4
+ declare function byteLength(value: unknown): number;
5
+ export declare const anthropic: Provider;
6
+ export { canonicalize, sha256, byteLength };
@@ -0,0 +1,216 @@
1
+ import { createHash } from "node:crypto";
2
+ import { emptyUsage } from "../pricing.js";
3
+ function fromAnthropic(u) {
4
+ if (!u)
5
+ return emptyUsage();
6
+ return {
7
+ inputTokens: u.input_tokens ?? 0,
8
+ outputTokens: u.output_tokens ?? 0,
9
+ cacheCreationInputTokens: u.cache_creation_input_tokens ?? 0,
10
+ cacheReadInputTokens: u.cache_read_input_tokens ?? 0,
11
+ };
12
+ }
13
+ function canonicalize(value) {
14
+ if (value === null || value === undefined)
15
+ return "null";
16
+ if (typeof value === "string")
17
+ return JSON.stringify(value);
18
+ if (typeof value !== "object")
19
+ return JSON.stringify(value);
20
+ if (Array.isArray(value)) {
21
+ return "[" + value.map(canonicalize).join(",") + "]";
22
+ }
23
+ const obj = value;
24
+ const keys = Object.keys(obj).sort();
25
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
26
+ }
27
+ function sha256(s) {
28
+ return createHash("sha256").update(s).digest("hex");
29
+ }
30
+ function byteLength(value) {
31
+ if (typeof value === "string")
32
+ return Buffer.byteLength(value, "utf8");
33
+ return Buffer.byteLength(JSON.stringify(value ?? ""), "utf8");
34
+ }
35
+ class AnthropicStreamAccumulator {
36
+ current = emptyUsage();
37
+ modelFromEvent = null;
38
+ observe(event) {
39
+ if (event.event !== "message_start" && event.event !== "message_delta")
40
+ return;
41
+ let parsed;
42
+ try {
43
+ parsed = JSON.parse(event.data);
44
+ }
45
+ catch {
46
+ return;
47
+ }
48
+ if (!parsed || typeof parsed !== "object")
49
+ return;
50
+ const obj = parsed;
51
+ if (event.event === "message_start") {
52
+ const message = obj["message"];
53
+ if (message) {
54
+ if (typeof message["model"] === "string") {
55
+ this.modelFromEvent = message["model"];
56
+ }
57
+ this.current = fromAnthropic(message["usage"]);
58
+ }
59
+ }
60
+ else {
61
+ const usage = obj["usage"];
62
+ if (usage) {
63
+ const u = fromAnthropic(usage);
64
+ this.current = {
65
+ inputTokens: this.current.inputTokens || u.inputTokens,
66
+ outputTokens: u.outputTokens,
67
+ cacheCreationInputTokens: this.current.cacheCreationInputTokens || u.cacheCreationInputTokens,
68
+ cacheReadInputTokens: this.current.cacheReadInputTokens || u.cacheReadInputTokens,
69
+ };
70
+ }
71
+ }
72
+ }
73
+ total() {
74
+ return { ...this.current };
75
+ }
76
+ model() {
77
+ return this.modelFromEvent;
78
+ }
79
+ }
80
+ function blocksFromAnthropic(content) {
81
+ // Anthropic accepts string OR array of blocks for message.content
82
+ if (typeof content === "string") {
83
+ return [{ kind: "text", text: content }];
84
+ }
85
+ if (!Array.isArray(content))
86
+ return [{ kind: "other", raw: content }];
87
+ return content.map((block) => {
88
+ if (!block || typeof block !== "object")
89
+ return { kind: "other", raw: block };
90
+ const b = block;
91
+ const type = b["type"];
92
+ if (type === "text" && typeof b["text"] === "string") {
93
+ return { kind: "text", text: b["text"] };
94
+ }
95
+ if (type === "tool_use" && typeof b["id"] === "string" && typeof b["name"] === "string") {
96
+ return { kind: "tool_use", id: b["id"], name: b["name"], input: b["input"] };
97
+ }
98
+ if (type === "tool_result" && typeof b["tool_use_id"] === "string") {
99
+ const content = b["content"];
100
+ const hash = sha256(canonicalize(content));
101
+ return {
102
+ kind: "tool_result",
103
+ tool_use_id: b["tool_use_id"],
104
+ content,
105
+ contentHash: hash,
106
+ contentBytes: byteLength(content),
107
+ };
108
+ }
109
+ return { kind: "other", raw: block };
110
+ });
111
+ }
112
+ function blocksToAnthropic(blocks) {
113
+ // Preserve array-vs-string shape: if it was originally a single text block from a
114
+ // string, we still emit an array. Anthropic accepts both.
115
+ return blocks.map((block) => {
116
+ switch (block.kind) {
117
+ case "text":
118
+ return { type: "text", text: block.text };
119
+ case "tool_use":
120
+ return { type: "tool_use", id: block.id, name: block.name, input: block.input };
121
+ case "tool_result": {
122
+ if (block.pointer) {
123
+ const stub = `[tokenshield: identical to tool_result ${block.pointer.priorToolUseId} ` +
124
+ `at message ${block.pointer.priorMessageIndex}, ` +
125
+ `sha:${block.contentHash.slice(0, 8)} — ${block.pointer.elidedBytes} bytes elided]`;
126
+ return { type: "tool_result", tool_use_id: block.tool_use_id, content: stub };
127
+ }
128
+ return { type: "tool_result", tool_use_id: block.tool_use_id, content: block.content };
129
+ }
130
+ case "other":
131
+ return block.raw;
132
+ }
133
+ });
134
+ }
135
+ function extractSystem(body) {
136
+ const sys = body["system"];
137
+ if (typeof sys === "string")
138
+ return sys;
139
+ if (Array.isArray(sys)) {
140
+ return sys
141
+ .map((b) => (typeof b === "object" && b !== null && typeof b["text"] === "string" ? b["text"] : ""))
142
+ .join("\n\n");
143
+ }
144
+ return null;
145
+ }
146
+ export const anthropic = {
147
+ id: "anthropic",
148
+ matches(pathname) {
149
+ return pathname.startsWith("/v1/messages") || pathname.startsWith("/v1/complete");
150
+ },
151
+ extractModel(body) {
152
+ if (!body || typeof body !== "object")
153
+ return "unknown";
154
+ const m = body["model"];
155
+ return typeof m === "string" ? m : "unknown";
156
+ },
157
+ isStreaming(body) {
158
+ if (!body || typeof body !== "object")
159
+ return false;
160
+ return body["stream"] === true;
161
+ },
162
+ usageFromResponseJson(body) {
163
+ if (!body || typeof body !== "object")
164
+ return { usage: emptyUsage(), model: null };
165
+ const obj = body;
166
+ return {
167
+ usage: fromAnthropic(obj["usage"]),
168
+ model: typeof obj["model"] === "string" ? obj["model"] : null,
169
+ };
170
+ },
171
+ createStreamAccumulator() {
172
+ return new AnthropicStreamAccumulator();
173
+ },
174
+ toConversation(body) {
175
+ if (!body || typeof body !== "object")
176
+ return null;
177
+ const obj = body;
178
+ const rawMessages = obj["messages"];
179
+ if (!Array.isArray(rawMessages))
180
+ return null;
181
+ const messages = rawMessages
182
+ .map((m) => {
183
+ if (!m || typeof m !== "object")
184
+ return null;
185
+ const msg = m;
186
+ const role = msg["role"];
187
+ if (role !== "user" && role !== "assistant")
188
+ return null;
189
+ return { role, blocks: blocksFromAnthropic(msg["content"]) };
190
+ })
191
+ .filter((m) => m !== null);
192
+ const model = typeof obj["model"] === "string" ? obj["model"] : "unknown";
193
+ const tempRaw = obj["temperature"];
194
+ const temperature = typeof tempRaw === "number" ? tempRaw : null;
195
+ return {
196
+ model,
197
+ system: extractSystem(obj),
198
+ messages,
199
+ temperature,
200
+ raw: obj,
201
+ };
202
+ },
203
+ applyConversation(body, conversation) {
204
+ if (!body || typeof body !== "object")
205
+ return body;
206
+ const obj = { ...body };
207
+ obj["messages"] = conversation.messages.map((m) => ({
208
+ role: m.role,
209
+ content: blocksToAnthropic(m.blocks),
210
+ }));
211
+ return obj;
212
+ },
213
+ };
214
+ // helpers exported for tests
215
+ export { canonicalize, sha256, byteLength };
216
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { Provider } from "./types.js";
2
+ import { anthropic } from "./anthropic.js";
3
+ export declare function providerForPath(pathname: string): Provider | null;
4
+ export { anthropic };
@@ -0,0 +1,7 @@
1
+ import { anthropic } from "./anthropic.js";
2
+ const PROVIDERS = [anthropic];
3
+ export function providerForPath(pathname) {
4
+ return PROVIDERS.find((p) => p.matches(pathname)) ?? null;
5
+ }
6
+ export { anthropic };
7
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/providers/registry.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,SAAS,GAAe,CAAC,SAAS,CAAC,CAAC;AAE1C,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,IAAI,CAAC;AAC5D,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,CAAC"}
@@ -0,0 +1,79 @@
1
+ import type { SSEEvent, UsageCounts } from "../types.js";
2
+ export type ProviderId = "anthropic" | "openai" | "gemini";
3
+ /**
4
+ * A Provider knows how to:
5
+ * - claim a URL path
6
+ * - parse usage from streamed SSE events
7
+ * - parse usage from non-streaming JSON responses
8
+ * - extract the model + stream flag from a request body
9
+ *
10
+ * Processors operate on the abstract Conversation/Message model the
11
+ * provider produces, so the dedup/cache logic doesn't need to know
12
+ * Anthropic vs OpenAI shapes.
13
+ */
14
+ export interface Provider {
15
+ readonly id: ProviderId;
16
+ /** Does this provider handle the given inbound URL path? */
17
+ matches(pathname: string): boolean;
18
+ /** Extract model name from a parsed request body. Returns "unknown" if absent. */
19
+ extractModel(body: unknown): string;
20
+ /** Did the request ask for SSE streaming? */
21
+ isStreaming(body: unknown): boolean;
22
+ /** Parse non-streaming response body (already JSON.parsed). */
23
+ usageFromResponseJson(body: unknown): {
24
+ usage: UsageCounts;
25
+ model: string | null;
26
+ };
27
+ /** Streaming usage accumulator factory — one per request. */
28
+ createStreamAccumulator(): StreamAccumulator;
29
+ /** Adapt the inbound body into a normalised Conversation for processors. */
30
+ toConversation(body: unknown): Conversation | null;
31
+ /** Apply processor-modified conversation back into a body shape ready to forward. */
32
+ applyConversation(body: unknown, conversation: Conversation): unknown;
33
+ }
34
+ export interface StreamAccumulator {
35
+ observe(event: SSEEvent): void;
36
+ total(): UsageCounts;
37
+ model(): string | null;
38
+ }
39
+ export interface Conversation {
40
+ model: string;
41
+ /** System prompt text (concatenated if provider supports an array). */
42
+ system: string | null;
43
+ messages: ConvMessage[];
44
+ /** Approximate temperature; null when not specified. Used by cache safety check. */
45
+ temperature: number | null;
46
+ /** Provider-specific extras forwarded untouched. */
47
+ raw: Record<string, unknown>;
48
+ }
49
+ export interface ConvMessage {
50
+ role: "user" | "assistant";
51
+ blocks: ConvBlock[];
52
+ }
53
+ export type ConvBlock = {
54
+ kind: "text";
55
+ text: string;
56
+ } | {
57
+ kind: "tool_use";
58
+ id: string;
59
+ name: string;
60
+ input: unknown;
61
+ } | {
62
+ kind: "tool_result";
63
+ tool_use_id: string;
64
+ /** Original content body (string or structured) — kept intact for first-occurrence. */
65
+ content: unknown;
66
+ /** Stable hash of `content` after canonicalization. */
67
+ contentHash: string;
68
+ /** Approx byte size of original content (for accounting). */
69
+ contentBytes: number;
70
+ /** If set, dedup has replaced the content with a pointer; original is "elided". */
71
+ pointer?: {
72
+ priorMessageIndex: number;
73
+ priorToolUseId: string;
74
+ elidedBytes: number;
75
+ };
76
+ } | {
77
+ kind: "other";
78
+ raw: unknown;
79
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { RequestRecord, ProxyConfig } from "../types.js";
3
+ type RecordSink = (record: RequestRecord) => void;
4
+ export declare function setProcessorEnabled(id: string, enabled: boolean): void;
5
+ export declare function getProcessorEnabledIds(): string[];
6
+ export declare function getResponseCacheStats(): {
7
+ hits: number;
8
+ misses: number;
9
+ entries: number;
10
+ bytes: number;
11
+ };
12
+ export declare function handleAnthropicRequest(req: IncomingMessage, res: ServerResponse, config: ProxyConfig, sink: RecordSink): Promise<void>;
13
+ export {};