context-lens 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +62 -17
  2. package/dist/cli-utils.d.ts +1 -1
  3. package/dist/cli-utils.d.ts.map +1 -1
  4. package/dist/cli-utils.js +28 -13
  5. package/dist/cli-utils.js.map +1 -1
  6. package/dist/cli.js +77 -65
  7. package/dist/cli.js.map +1 -1
  8. package/dist/core/conversation.d.ts +54 -0
  9. package/dist/core/conversation.d.ts.map +1 -0
  10. package/dist/core/conversation.js +188 -0
  11. package/dist/core/conversation.js.map +1 -0
  12. package/dist/core/models.d.ts +30 -0
  13. package/dist/core/models.d.ts.map +1 -0
  14. package/dist/core/models.js +96 -0
  15. package/dist/core/models.js.map +1 -0
  16. package/dist/core/parse.d.ts +17 -0
  17. package/dist/core/parse.d.ts.map +1 -0
  18. package/dist/core/parse.js +349 -0
  19. package/dist/core/parse.js.map +1 -0
  20. package/dist/core/routing.d.ts +47 -0
  21. package/dist/core/routing.d.ts.map +1 -0
  22. package/dist/core/routing.js +132 -0
  23. package/dist/core/routing.js.map +1 -0
  24. package/dist/core/source.d.ts +22 -0
  25. package/dist/core/source.d.ts.map +1 -0
  26. package/dist/core/source.js +56 -0
  27. package/dist/core/source.js.map +1 -0
  28. package/dist/core/tokens.d.ts +11 -0
  29. package/dist/core/tokens.d.ts.map +1 -0
  30. package/dist/core/tokens.js +16 -0
  31. package/dist/core/tokens.js.map +1 -0
  32. package/dist/core.d.ts +12 -22
  33. package/dist/core.d.ts.map +1 -1
  34. package/dist/core.js +12 -471
  35. package/dist/core.js.map +1 -1
  36. package/dist/http/headers.d.ts +25 -0
  37. package/dist/http/headers.d.ts.map +1 -0
  38. package/dist/http/headers.js +54 -0
  39. package/dist/http/headers.js.map +1 -0
  40. package/dist/lhar-types.generated.d.ts +1 -1
  41. package/dist/lhar.d.ts +4 -4
  42. package/dist/lhar.d.ts.map +1 -1
  43. package/dist/lhar.js +190 -106
  44. package/dist/lhar.js.map +1 -1
  45. package/dist/server/config.d.ts +12 -0
  46. package/dist/server/config.d.ts.map +1 -0
  47. package/dist/server/config.js +33 -0
  48. package/dist/server/config.js.map +1 -0
  49. package/dist/server/projection.d.ts +9 -0
  50. package/dist/server/projection.d.ts.map +1 -0
  51. package/dist/server/projection.js +39 -0
  52. package/dist/server/projection.js.map +1 -0
  53. package/dist/server/proxy.d.ts +13 -0
  54. package/dist/server/proxy.d.ts.map +1 -0
  55. package/dist/server/proxy.js +232 -0
  56. package/dist/server/proxy.js.map +1 -0
  57. package/dist/server/store.d.ts +33 -0
  58. package/dist/server/store.d.ts.map +1 -0
  59. package/dist/server/store.js +350 -0
  60. package/dist/server/store.js.map +1 -0
  61. package/dist/server/webui.d.ts +5 -0
  62. package/dist/server/webui.d.ts.map +1 -0
  63. package/dist/server/webui.js +170 -0
  64. package/dist/server/webui.js.map +1 -0
  65. package/dist/server-utils.d.ts +2 -2
  66. package/dist/server-utils.d.ts.map +1 -1
  67. package/dist/server-utils.js +12 -21
  68. package/dist/server-utils.js.map +1 -1
  69. package/dist/server.js +30 -697
  70. package/dist/server.js.map +1 -1
  71. package/dist/types.d.ts +50 -10
  72. package/dist/types.d.ts.map +1 -1
  73. package/dist/version.generated.d.ts +2 -0
  74. package/dist/version.generated.d.ts.map +1 -0
  75. package/dist/version.generated.js +2 -0
  76. package/dist/version.generated.js.map +1 -0
  77. package/package.json +18 -6
  78. package/public/index.html +39 -12
  79. package/schema/lhar.schema.json +1 -1
@@ -0,0 +1,349 @@
1
+ import { estimateTokens } from "./tokens.js";
2
+ /**
3
+ * Parse a single item from the OpenAI Responses API `input` array.
4
+ *
5
+ * Maps typed items (function_call, function_call_output, reasoning, output_text, etc.)
6
+ * to a normalized `ParsedMessage` with a stable `role` and optional `contentBlocks`.
7
+ */
8
+ function parseResponsesItem(item) {
9
+ const type = item.type || "";
10
+ // Standard message with role/content (e.g. {"type":"message","role":"user","content":[...]})
11
+ if (item.role) {
12
+ const isSystem = item.role === "system" || item.role === "developer";
13
+ let content;
14
+ let contentBlocks = null;
15
+ if (typeof item.content === "string") {
16
+ content = item.content;
17
+ }
18
+ else if (Array.isArray(item.content)) {
19
+ contentBlocks = item.content;
20
+ content = item.content.map((b) => b.text || "").join("\n");
21
+ }
22
+ else {
23
+ content = JSON.stringify(item.content || item);
24
+ }
25
+ const tokens = estimateTokens(item.content ?? content);
26
+ return {
27
+ message: { role: item.role, content, contentBlocks, tokens },
28
+ tokens,
29
+ isSystem,
30
+ content,
31
+ };
32
+ }
33
+ // function_call → assistant tool_use
34
+ if (type === "function_call" || type === "custom_tool_call") {
35
+ const name = item.name || "unknown";
36
+ const args = item.arguments || "";
37
+ const content = name +
38
+ "(" +
39
+ (typeof args === "string"
40
+ ? args.slice(0, 200)
41
+ : JSON.stringify(args).slice(0, 200)) +
42
+ ")";
43
+ const tokens = estimateTokens(item);
44
+ const block = {
45
+ type: "tool_use",
46
+ id: item.call_id || "",
47
+ name,
48
+ input: typeof args === "string" ? {} : args || {},
49
+ };
50
+ return {
51
+ message: { role: "assistant", content, contentBlocks: [block], tokens },
52
+ tokens,
53
+ isSystem: false,
54
+ content,
55
+ };
56
+ }
57
+ // function_call_output → user tool_result
58
+ if (type === "function_call_output" || type === "custom_tool_call_output") {
59
+ const output = typeof item.output === "string"
60
+ ? item.output
61
+ : JSON.stringify(item.output || "");
62
+ const tokens = estimateTokens(output);
63
+ const block = {
64
+ type: "tool_result",
65
+ tool_use_id: item.call_id || "",
66
+ content: output,
67
+ };
68
+ return {
69
+ message: {
70
+ role: "user",
71
+ content: output,
72
+ contentBlocks: [block],
73
+ tokens,
74
+ },
75
+ tokens,
76
+ isSystem: false,
77
+ content: output,
78
+ };
79
+ }
80
+ // reasoning → assistant thinking
81
+ if (type === "reasoning") {
82
+ const summary = Array.isArray(item.summary)
83
+ ? item.summary.map((s) => s.text || "").join("\n")
84
+ : "";
85
+ const content = summary || "[reasoning]";
86
+ const tokens = estimateTokens(item);
87
+ return {
88
+ message: {
89
+ role: "assistant",
90
+ content,
91
+ contentBlocks: [{ type: "thinking", thinking: content }],
92
+ tokens,
93
+ },
94
+ tokens,
95
+ isSystem: false,
96
+ content,
97
+ };
98
+ }
99
+ // output_text → assistant text
100
+ if (type === "output_text") {
101
+ const text = item.text || "";
102
+ const tokens = estimateTokens(text);
103
+ return {
104
+ message: {
105
+ role: "assistant",
106
+ content: text,
107
+ contentBlocks: [{ type: "text", text }],
108
+ tokens,
109
+ },
110
+ tokens,
111
+ isSystem: false,
112
+ content: text,
113
+ };
114
+ }
115
+ // input_text → user text
116
+ if (type === "input_text") {
117
+ const text = item.text || "";
118
+ const tokens = estimateTokens(text);
119
+ return {
120
+ message: {
121
+ role: "user",
122
+ content: text,
123
+ contentBlocks: [{ type: "text", text }],
124
+ tokens,
125
+ },
126
+ tokens,
127
+ isSystem: false,
128
+ content: text,
129
+ };
130
+ }
131
+ // Fallback: serialize the whole item
132
+ const content = JSON.stringify(item);
133
+ const tokens = estimateTokens(content);
134
+ return {
135
+ message: { role: item.role || "user", content, tokens },
136
+ tokens,
137
+ isSystem: false,
138
+ content,
139
+ };
140
+ }
141
+ /**
142
+ * Parse a request body and extract normalized context information.
143
+ *
144
+ * This is the core "shape-normalizer" for Context Lens. It supports:
145
+ * - Anthropic Messages API
146
+ * - OpenAI Responses API and Chat Completions
147
+ * - ChatGPT backend schema (Codex subscription traffic)
148
+ * - Gemini (including Code Assist wrappers)
149
+ *
150
+ * @param provider - Provider inferred from routing (anthropic/openai/gemini/chatgpt/unknown).
151
+ * @param body - Parsed JSON request body.
152
+ * @param apiFormat - API schema family inferred from routing.
153
+ * @returns A `ContextInfo` with token estimates and normalized messages/blocks.
154
+ */
155
+ export function parseContextInfo(provider, body, apiFormat) {
156
+ const info = {
157
+ provider,
158
+ apiFormat,
159
+ model: body.model || "unknown",
160
+ systemTokens: 0,
161
+ toolsTokens: 0,
162
+ messagesTokens: 0,
163
+ totalTokens: 0,
164
+ systemPrompts: [],
165
+ tools: [],
166
+ messages: [],
167
+ };
168
+ if (provider === "anthropic") {
169
+ if (body.system) {
170
+ const systemText = typeof body.system === "string"
171
+ ? body.system
172
+ : Array.isArray(body.system)
173
+ ? body.system.map((b) => b.text || "").join("\n")
174
+ : JSON.stringify(body.system);
175
+ info.systemPrompts.push({ content: systemText });
176
+ info.systemTokens = estimateTokens(systemText);
177
+ }
178
+ if (body.tools && Array.isArray(body.tools)) {
179
+ info.tools = body.tools;
180
+ info.toolsTokens = estimateTokens(JSON.stringify(body.tools));
181
+ }
182
+ if (body.messages && Array.isArray(body.messages)) {
183
+ info.messages = body.messages.map((msg) => {
184
+ const contentBlocks = Array.isArray(msg.content) ? msg.content : null;
185
+ return {
186
+ role: msg.role,
187
+ content: typeof msg.content === "string"
188
+ ? msg.content
189
+ : JSON.stringify(msg.content),
190
+ contentBlocks,
191
+ tokens: estimateTokens(msg.content),
192
+ };
193
+ });
194
+ info.messagesTokens = info.messages.reduce((sum, m) => sum + m.tokens, 0);
195
+ }
196
+ }
197
+ else if (apiFormat === "responses" || provider === "chatgpt") {
198
+ if (body.instructions) {
199
+ info.systemPrompts.push({ content: body.instructions });
200
+ info.systemTokens = estimateTokens(body.instructions);
201
+ }
202
+ if (body.system) {
203
+ const systemText = typeof body.system === "string"
204
+ ? body.system
205
+ : Array.isArray(body.system)
206
+ ? body.system.map((b) => b.text || "").join("\n")
207
+ : JSON.stringify(body.system);
208
+ info.systemPrompts.push({ content: systemText });
209
+ info.systemTokens += estimateTokens(systemText);
210
+ }
211
+ const msgs = body.input || body.messages;
212
+ if (msgs) {
213
+ if (typeof msgs === "string") {
214
+ info.messages.push({
215
+ role: "user",
216
+ content: msgs,
217
+ tokens: estimateTokens(msgs),
218
+ });
219
+ info.messagesTokens = estimateTokens(msgs);
220
+ }
221
+ else if (Array.isArray(msgs)) {
222
+ msgs.forEach((item) => {
223
+ const parsed = parseResponsesItem(item);
224
+ if (parsed.isSystem) {
225
+ info.systemPrompts.push({ content: parsed.content });
226
+ info.systemTokens += parsed.tokens;
227
+ }
228
+ else {
229
+ info.messages.push(parsed.message);
230
+ info.messagesTokens += parsed.tokens;
231
+ }
232
+ });
233
+ }
234
+ }
235
+ if (body.tools && Array.isArray(body.tools)) {
236
+ info.tools = body.tools;
237
+ info.toolsTokens = estimateTokens(JSON.stringify(body.tools));
238
+ }
239
+ }
240
+ else if (provider === "gemini" || apiFormat === "gemini") {
241
+ // Gemini API: contents[], systemInstruction, tools[{functionDeclarations}]
242
+ // Code Assist wraps everything inside body.request: {contents, systemInstruction, tools, ...}
243
+ const geminiBody = body.request || body;
244
+ if (geminiBody.systemInstruction) {
245
+ const parts = geminiBody.systemInstruction.parts || [];
246
+ const systemText = parts.map((p) => p.text || "").join("\n");
247
+ info.systemPrompts.push({ content: systemText });
248
+ info.systemTokens = estimateTokens(systemText);
249
+ }
250
+ if (geminiBody.tools && Array.isArray(geminiBody.tools)) {
251
+ const allDecls = geminiBody.tools.flatMap((t) => t.functionDeclarations || []);
252
+ info.tools = allDecls;
253
+ info.toolsTokens = estimateTokens(JSON.stringify(geminiBody.tools));
254
+ }
255
+ if (geminiBody.contents && Array.isArray(geminiBody.contents)) {
256
+ info.messages = geminiBody.contents.map((turn) => {
257
+ const role = turn.role || "user";
258
+ const parts = turn.parts || [];
259
+ const contentBlocks = [];
260
+ const textParts = [];
261
+ for (const part of parts) {
262
+ if (part.text) {
263
+ textParts.push(part.text);
264
+ contentBlocks.push({ type: "text", text: part.text });
265
+ }
266
+ else if (part.functionCall) {
267
+ contentBlocks.push({
268
+ type: "tool_use",
269
+ id: part.functionCall.id || "",
270
+ name: part.functionCall.name || "",
271
+ input: part.functionCall.args || {},
272
+ });
273
+ }
274
+ else if (part.functionResponse) {
275
+ const resp = part.functionResponse.response;
276
+ // Gemini CLI wraps tool output in {output: "..."} or {error: "..."}
277
+ const respText = typeof resp === "string"
278
+ ? resp
279
+ : typeof resp?.output === "string"
280
+ ? resp.output
281
+ : typeof resp?.error === "string"
282
+ ? resp.error
283
+ : JSON.stringify(resp || "");
284
+ contentBlocks.push({
285
+ type: "tool_result",
286
+ tool_use_id: part.functionResponse.id || "",
287
+ content: respText,
288
+ });
289
+ }
290
+ else if (part.inlineData) {
291
+ contentBlocks.push({ type: "image" });
292
+ }
293
+ else if (part.executableCode) {
294
+ contentBlocks.push({
295
+ type: "text",
296
+ text: part.executableCode.code || "",
297
+ });
298
+ }
299
+ else if (part.codeExecutionResult) {
300
+ contentBlocks.push({
301
+ type: "text",
302
+ text: part.codeExecutionResult.output || "",
303
+ });
304
+ }
305
+ }
306
+ const content = textParts.join("\n") || JSON.stringify(parts);
307
+ const tokens = estimateTokens(turn);
308
+ return {
309
+ role: role === "model" ? "assistant" : role,
310
+ content,
311
+ contentBlocks,
312
+ tokens,
313
+ };
314
+ });
315
+ info.messagesTokens = info.messages.reduce((sum, m) => sum + m.tokens, 0);
316
+ }
317
+ }
318
+ else if (provider === "openai") {
319
+ if (body.messages && Array.isArray(body.messages)) {
320
+ body.messages.forEach((msg) => {
321
+ if (msg.role === "system" || msg.role === "developer") {
322
+ info.systemPrompts.push({ content: msg.content });
323
+ info.systemTokens += estimateTokens(msg.content);
324
+ }
325
+ else {
326
+ info.messages.push({
327
+ role: msg.role,
328
+ content: typeof msg.content === "string"
329
+ ? msg.content
330
+ : JSON.stringify(msg.content),
331
+ tokens: estimateTokens(msg.content),
332
+ });
333
+ info.messagesTokens += estimateTokens(msg.content);
334
+ }
335
+ });
336
+ }
337
+ if (body.tools && Array.isArray(body.tools)) {
338
+ info.tools = body.tools;
339
+ info.toolsTokens = estimateTokens(JSON.stringify(body.tools));
340
+ }
341
+ else if (body.functions && Array.isArray(body.functions)) {
342
+ info.tools = body.functions;
343
+ info.toolsTokens = estimateTokens(JSON.stringify(body.functions));
344
+ }
345
+ }
346
+ info.totalTokens = info.systemTokens + info.toolsTokens + info.messagesTokens;
347
+ return info;
348
+ }
349
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/core/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import type { ApiFormat, ExtractSourceResult, ParsedUrl, Provider, ResolveTargetResult, Upstreams } from "../types.js";
2
+ /**
3
+ * URL path segments that represent API resources rather than "source tool" prefixes.
4
+ *
5
+ * Example: `/v1/messages` should not treat `v1` as a source tag.
6
+ */
7
+ export declare const API_PATH_SEGMENTS: Set<string>;
8
+ /**
9
+ * Infer provider based on request path + headers.
10
+ *
11
+ * This is used for routing (choosing which upstream base URL to use) and parsing.
12
+ */
13
+ export declare function detectProvider(pathname: string, headers: Record<string, string | undefined>): Provider;
14
+ /**
15
+ * Infer the API "format" (schema family) from the request path.
16
+ *
17
+ * This is distinct from provider: e.g. OpenAI can be `responses` or `chat-completions`.
18
+ */
19
+ export declare function detectApiFormat(pathname: string): ApiFormat;
20
+ /**
21
+ * Classify an incoming request into `{ provider, apiFormat }`.
22
+ *
23
+ * Keep all path/format heuristics in one place to avoid drift between
24
+ * routing decisions and parsing decisions.
25
+ */
26
+ export declare function classifyRequest(pathname: string, headers: Record<string, string | undefined>): {
27
+ provider: Provider;
28
+ apiFormat: ApiFormat;
29
+ };
30
+ /**
31
+ * Extract a "source tool" tag from a request path.
32
+ *
33
+ * Example: `/claude/v1/messages` => `{ source: 'claude', cleanPath: '/v1/messages' }`.
34
+ *
35
+ * This tag is used for attribution in the UI/LHAR and for per-tool grouping.
36
+ */
37
+ export declare function extractSource(pathname: string): ExtractSourceResult;
38
+ /**
39
+ * Determine the final upstream target URL for a request.
40
+ *
41
+ * @param parsedUrl - Path + query extracted from the incoming request.
42
+ * @param headers - Headers used for detection and optional override.
43
+ * @param upstreams - Base URLs for each provider.
44
+ * @returns `{ targetUrl, provider }`.
45
+ */
46
+ export declare function resolveTargetUrl(parsedUrl: ParsedUrl, headers: Record<string, string | undefined>, upstreams: Upstreams): ResolveTargetResult;
47
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/core/routing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,mBAAmB,EACnB,SAAS,EACT,QAAQ,EACR,mBAAmB,EACnB,SAAS,EACV,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,aAW5B,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC1C,QAAQ,CAEV;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC1C;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAiC9C;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAqBnE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC3C,SAAS,EAAE,SAAS,GACnB,mBAAmB,CAsBrB"}
@@ -0,0 +1,132 @@
1
+ /**
2
+ * URL path segments that represent API resources rather than "source tool" prefixes.
3
+ *
4
+ * Example: `/v1/messages` should not treat `v1` as a source tag.
5
+ */
6
+ export const API_PATH_SEGMENTS = new Set([
7
+ "v1",
8
+ "v1beta",
9
+ "v1alpha",
10
+ "v1internal",
11
+ "responses",
12
+ "chat",
13
+ "models",
14
+ "embeddings",
15
+ "backend-api",
16
+ "api",
17
+ ]);
18
+ /**
19
+ * Infer provider based on request path + headers.
20
+ *
21
+ * This is used for routing (choosing which upstream base URL to use) and parsing.
22
+ */
23
+ export function detectProvider(pathname, headers) {
24
+ return classifyRequest(pathname, headers).provider;
25
+ }
26
+ /**
27
+ * Infer the API "format" (schema family) from the request path.
28
+ *
29
+ * This is distinct from provider: e.g. OpenAI can be `responses` or `chat-completions`.
30
+ */
31
+ export function detectApiFormat(pathname) {
32
+ return classifyRequest(pathname, {}).apiFormat;
33
+ }
34
+ /**
35
+ * Classify an incoming request into `{ provider, apiFormat }`.
36
+ *
37
+ * Keep all path/format heuristics in one place to avoid drift between
38
+ * routing decisions and parsing decisions.
39
+ */
40
+ export function classifyRequest(pathname, headers) {
41
+ // ChatGPT backend traffic (Codex subscription)
42
+ if (pathname.match(/^\/(api|backend-api)\//))
43
+ return { provider: "chatgpt", apiFormat: "chatgpt-backend" };
44
+ // Anthropic Messages API
45
+ if (pathname.includes("/v1/messages"))
46
+ return { provider: "anthropic", apiFormat: "anthropic-messages" };
47
+ if (pathname.includes("/v1/complete"))
48
+ return { provider: "anthropic", apiFormat: "unknown" };
49
+ if (headers["anthropic-version"])
50
+ return { provider: "anthropic", apiFormat: "unknown" };
51
+ // Gemini: must come BEFORE openai catch-all (which matches /models/)
52
+ const isGeminiPath = pathname.includes(":generateContent") ||
53
+ pathname.includes(":streamGenerateContent") ||
54
+ pathname.match(/\/v1(beta|alpha)\/models\//) ||
55
+ pathname.includes("/v1internal:");
56
+ if (isGeminiPath || headers["x-goog-api-key"])
57
+ return { provider: "gemini", apiFormat: "gemini" };
58
+ // OpenAI
59
+ if (pathname.includes("/responses"))
60
+ return { provider: "openai", apiFormat: "responses" };
61
+ if (pathname.includes("/chat/completions"))
62
+ return { provider: "openai", apiFormat: "chat-completions" };
63
+ if (pathname.match(/\/(models|embeddings)/))
64
+ return { provider: "openai", apiFormat: "unknown" };
65
+ if (headers.authorization?.startsWith("Bearer sk-"))
66
+ return { provider: "openai", apiFormat: "unknown" };
67
+ return { provider: "unknown", apiFormat: "unknown" };
68
+ }
69
+ /**
70
+ * Extract a "source tool" tag from a request path.
71
+ *
72
+ * Example: `/claude/v1/messages` => `{ source: 'claude', cleanPath: '/v1/messages' }`.
73
+ *
74
+ * This tag is used for attribution in the UI/LHAR and for per-tool grouping.
75
+ */
76
+ export function extractSource(pathname) {
77
+ const match = pathname.match(/^\/([^/]+)(\/.*)?$/);
78
+ if (match?.[2] && !API_PATH_SEGMENTS.has(match[1])) {
79
+ // `decodeURIComponent` may introduce `/` via `%2f` (path traversal) or throw on bad encodings.
80
+ // Treat suspicious/invalid tags as "no source tag" and route the request normally.
81
+ let decoded = match[1];
82
+ try {
83
+ decoded = decodeURIComponent(match[1]);
84
+ }
85
+ catch {
86
+ decoded = match[1];
87
+ }
88
+ if (decoded.includes("/") ||
89
+ decoded.includes("\\") ||
90
+ decoded.includes("..")) {
91
+ return { source: null, cleanPath: pathname };
92
+ }
93
+ return { source: decoded, cleanPath: match[2] || "/" };
94
+ }
95
+ return { source: null, cleanPath: pathname };
96
+ }
97
+ /**
98
+ * Determine the final upstream target URL for a request.
99
+ *
100
+ * @param parsedUrl - Path + query extracted from the incoming request.
101
+ * @param headers - Headers used for detection and optional override.
102
+ * @param upstreams - Base URLs for each provider.
103
+ * @returns `{ targetUrl, provider }`.
104
+ */
105
+ export function resolveTargetUrl(parsedUrl, headers, upstreams) {
106
+ const provider = classifyRequest(parsedUrl.pathname, headers).provider;
107
+ const search = parsedUrl.search || "";
108
+ let targetUrl = headers["x-target-url"];
109
+ if (!targetUrl) {
110
+ if (provider === "chatgpt") {
111
+ targetUrl = upstreams.chatgpt + parsedUrl.pathname + search;
112
+ }
113
+ else if (provider === "anthropic") {
114
+ targetUrl = upstreams.anthropic + parsedUrl.pathname + search;
115
+ }
116
+ else if (provider === "gemini") {
117
+ const isCodeAssist = parsedUrl.pathname.includes("/v1internal");
118
+ targetUrl =
119
+ (isCodeAssist ? upstreams.geminiCodeAssist : upstreams.gemini) +
120
+ parsedUrl.pathname +
121
+ search;
122
+ }
123
+ else {
124
+ targetUrl = upstreams.openai + parsedUrl.pathname + search;
125
+ }
126
+ }
127
+ else if (!targetUrl.startsWith("http")) {
128
+ targetUrl = targetUrl + parsedUrl.pathname + search;
129
+ }
130
+ return { targetUrl, provider };
131
+ }
132
+ //# sourceMappingURL=routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/core/routing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { ContextInfo, HeaderSignature, SourceSignature } from "../types.js";
2
+ /**
3
+ * Header signatures used to infer which CLI/tool produced a request.
4
+ *
5
+ * This is best-effort: many tools do not have stable identifiers.
6
+ */
7
+ export declare const HEADER_SIGNATURES: HeaderSignature[];
8
+ /**
9
+ * System-prompt signatures used as a fallback when headers are missing/ambiguous.
10
+ */
11
+ export declare const SOURCE_SIGNATURES: SourceSignature[];
12
+ /**
13
+ * Infer a human-friendly "source tool" label (claude/codex/gemini/aider/...) for attribution.
14
+ *
15
+ * Priority:
16
+ * 1. explicit source tag if provided and not `"unknown"`
17
+ * 2. request header signatures
18
+ * 3. system prompt signatures
19
+ * 4. fallback to `"unknown"`
20
+ */
21
+ export declare function detectSource(contextInfo: ContextInfo, source: string | null, headers?: Record<string, string>): string;
22
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../src/core/source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAK9C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,eAAe,EAK9C,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,MAAM,CA0BR"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Header signatures used to infer which CLI/tool produced a request.
3
+ *
4
+ * This is best-effort: many tools do not have stable identifiers.
5
+ */
6
+ export const HEADER_SIGNATURES = [
7
+ { header: "user-agent", pattern: /^claude-cli\//, source: "claude" },
8
+ { header: "user-agent", pattern: /aider/i, source: "aider" },
9
+ { header: "user-agent", pattern: /kimi/i, source: "kimi" },
10
+ { header: "user-agent", pattern: /^GeminiCLI\//, source: "gemini" },
11
+ ];
12
+ /**
13
+ * System-prompt signatures used as a fallback when headers are missing/ambiguous.
14
+ */
15
+ export const SOURCE_SIGNATURES = [
16
+ { pattern: "Act as an expert software developer", source: "aider" },
17
+ { pattern: "You are Claude Code", source: "claude" },
18
+ { pattern: "You are Kimi Code CLI", source: "kimi" },
19
+ { pattern: "operating inside pi, a coding agent harness", source: "pi" },
20
+ ];
21
+ /**
22
+ * Infer a human-friendly "source tool" label (claude/codex/gemini/aider/...) for attribution.
23
+ *
24
+ * Priority:
25
+ * 1. explicit source tag if provided and not `"unknown"`
26
+ * 2. request header signatures
27
+ * 3. system prompt signatures
28
+ * 4. fallback to `"unknown"`
29
+ */
30
+ export function detectSource(contextInfo, source, headers) {
31
+ if (source && source !== "unknown")
32
+ return source;
33
+ // Primary: check request headers
34
+ if (headers) {
35
+ for (const sig of HEADER_SIGNATURES) {
36
+ const val = headers[sig.header];
37
+ if (!val)
38
+ continue;
39
+ if (sig.pattern instanceof RegExp
40
+ ? sig.pattern.test(val)
41
+ : val.includes(sig.pattern)) {
42
+ return sig.source;
43
+ }
44
+ }
45
+ }
46
+ // Fallback: check system prompt content
47
+ const systemText = (contextInfo.systemPrompts || [])
48
+ .map((sp) => sp.content)
49
+ .join("\n");
50
+ for (const sig of SOURCE_SIGNATURES) {
51
+ if (systemText.includes(sig.pattern))
52
+ return sig.source;
53
+ }
54
+ return source || "unknown";
55
+ }
56
+ //# sourceMappingURL=source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.js","sourceRoot":"","sources":["../../src/core/source.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE;IACpE,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;IAC5D,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;IAC1D,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE;CACpE,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,EAAE,OAAO,EAAE,qCAAqC,EAAE,MAAM,EAAE,OAAO,EAAE;IACnE,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,EAAE,QAAQ,EAAE;IACpD,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,EAAE,MAAM,EAAE;IACpD,EAAE,OAAO,EAAE,6CAA6C,EAAE,MAAM,EAAE,IAAI,EAAE;CACzE,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY,CAC1B,WAAwB,EACxB,MAAqB,EACrB,OAAgC;IAEhC,IAAI,MAAM,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAElD,iCAAiC;IACjC,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;YACpC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChC,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,IACE,GAAG,CAAC,OAAO,YAAY,MAAM;gBAC3B,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;gBACvB,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAC7B,CAAC;gBACD,OAAO,GAAG,CAAC,MAAM,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,MAAM,UAAU,GAAG,CAAC,WAAW,CAAC,aAAa,IAAI,EAAE,CAAC;SACjD,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC;SACvB,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC;IAC1D,CAAC;IACD,OAAO,MAAM,IAAI,SAAS,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Lightweight token estimator used throughout Context Lens.
3
+ *
4
+ * This is intentionally simple and model-agnostic: we approximate tokens as `ceil(chars / 4)`.
5
+ * It is good enough for rough context utilization and relative comparisons.
6
+ *
7
+ * @param text - Value to estimate tokens for. Objects are stringified as JSON.
8
+ * @returns Estimated token count (>= 0).
9
+ */
10
+ export declare function estimateTokens(text: unknown): number;
11
+ //# sourceMappingURL=tokens.d.ts.map