context-lens 0.3.2 → 0.4.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 (126) hide show
  1. package/README.md +52 -16
  2. package/dist/analysis/ingest.d.ts +13 -0
  3. package/dist/analysis/ingest.d.ts.map +1 -0
  4. package/dist/analysis/ingest.js +75 -0
  5. package/dist/analysis/ingest.js.map +1 -0
  6. package/dist/analysis/server.d.ts +10 -0
  7. package/dist/analysis/server.d.ts.map +1 -0
  8. package/dist/analysis/server.js +95 -0
  9. package/dist/analysis/server.js.map +1 -0
  10. package/dist/analysis/watcher.d.ts +55 -0
  11. package/dist/analysis/watcher.d.ts.map +1 -0
  12. package/dist/analysis/watcher.js +170 -0
  13. package/dist/analysis/watcher.js.map +1 -0
  14. package/dist/cli-utils.d.ts +16 -0
  15. package/dist/cli-utils.d.ts.map +1 -1
  16. package/dist/cli-utils.js +185 -3
  17. package/dist/cli-utils.js.map +1 -1
  18. package/dist/cli.js +629 -88
  19. package/dist/cli.js.map +1 -1
  20. package/dist/core/conversation.d.ts +8 -1
  21. package/dist/core/conversation.d.ts.map +1 -1
  22. package/dist/core/conversation.js +38 -12
  23. package/dist/core/conversation.js.map +1 -1
  24. package/dist/core/parse.d.ts.map +1 -1
  25. package/dist/core/parse.js +17 -1
  26. package/dist/core/parse.js.map +1 -1
  27. package/dist/core/session-analysis.d.ts +100 -0
  28. package/dist/core/session-analysis.d.ts.map +1 -0
  29. package/dist/core/session-analysis.js +435 -0
  30. package/dist/core/session-analysis.js.map +1 -0
  31. package/dist/core/session-format.d.ts +20 -0
  32. package/dist/core/session-format.d.ts.map +1 -0
  33. package/dist/core/session-format.js +298 -0
  34. package/dist/core/session-format.js.map +1 -0
  35. package/dist/core.d.ts +4 -0
  36. package/dist/core.d.ts.map +1 -1
  37. package/dist/core.js +2 -0
  38. package/dist/core.js.map +1 -1
  39. package/dist/lhar/reader.d.ts +22 -0
  40. package/dist/lhar/reader.d.ts.map +1 -0
  41. package/dist/lhar/reader.js +43 -0
  42. package/dist/lhar/reader.js.map +1 -0
  43. package/dist/lhar/record.d.ts.map +1 -1
  44. package/dist/lhar/record.js +3 -0
  45. package/dist/lhar/record.js.map +1 -1
  46. package/dist/lhar/response.d.ts +8 -0
  47. package/dist/lhar/response.d.ts.map +1 -1
  48. package/dist/lhar/response.js +44 -0
  49. package/dist/lhar/response.js.map +1 -1
  50. package/dist/lhar/tools.d.ts +17 -0
  51. package/dist/lhar/tools.d.ts.map +1 -0
  52. package/dist/lhar/tools.js +48 -0
  53. package/dist/lhar/tools.js.map +1 -0
  54. package/dist/lhar-types.generated.d.ts +34 -0
  55. package/dist/lhar-types.generated.d.ts.map +1 -1
  56. package/dist/lhar.d.ts +4 -1
  57. package/dist/lhar.d.ts.map +1 -1
  58. package/dist/lhar.js +5 -1
  59. package/dist/lhar.js.map +1 -1
  60. package/dist/proxy/capture.d.ts +40 -0
  61. package/dist/proxy/capture.d.ts.map +1 -0
  62. package/dist/proxy/capture.js +56 -0
  63. package/dist/proxy/capture.js.map +1 -0
  64. package/dist/proxy/config.d.ts +16 -0
  65. package/dist/proxy/config.d.ts.map +1 -0
  66. package/dist/proxy/config.js +34 -0
  67. package/dist/proxy/config.js.map +1 -0
  68. package/dist/proxy/forward.d.ts +20 -0
  69. package/dist/proxy/forward.d.ts.map +1 -0
  70. package/dist/proxy/forward.js +210 -0
  71. package/dist/proxy/forward.js.map +1 -0
  72. package/dist/proxy/headers.d.ts +16 -0
  73. package/dist/proxy/headers.d.ts.map +1 -0
  74. package/dist/proxy/headers.js +37 -0
  75. package/dist/proxy/headers.js.map +1 -0
  76. package/dist/proxy/routing.d.ts +44 -0
  77. package/dist/proxy/routing.d.ts.map +1 -0
  78. package/dist/proxy/routing.js +114 -0
  79. package/dist/proxy/routing.js.map +1 -0
  80. package/dist/proxy/server.d.ts +27 -0
  81. package/dist/proxy/server.d.ts.map +1 -0
  82. package/dist/proxy/server.js +55 -0
  83. package/dist/proxy/server.js.map +1 -0
  84. package/dist/schemas.d.ts +328 -0
  85. package/dist/schemas.d.ts.map +1 -0
  86. package/dist/schemas.js +249 -0
  87. package/dist/schemas.js.map +1 -0
  88. package/dist/server/api.d.ts +3 -4
  89. package/dist/server/api.d.ts.map +1 -1
  90. package/dist/server/api.js +195 -220
  91. package/dist/server/api.js.map +1 -1
  92. package/dist/server/store.d.ts +23 -7
  93. package/dist/server/store.d.ts.map +1 -1
  94. package/dist/server/store.js +141 -50
  95. package/dist/server/store.js.map +1 -1
  96. package/dist/server/webui.d.ts +5 -2
  97. package/dist/server/webui.d.ts.map +1 -1
  98. package/dist/server/webui.js +32 -13
  99. package/dist/server/webui.js.map +1 -1
  100. package/dist/server-utils.d.ts +1 -2
  101. package/dist/server-utils.d.ts.map +1 -1
  102. package/dist/server-utils.js +0 -2
  103. package/dist/server-utils.js.map +1 -1
  104. package/dist/types.d.ts +1 -1
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/version.generated.d.ts +1 -1
  107. package/dist/version.generated.js +1 -1
  108. package/mitm_addon.py +99 -28
  109. package/package.json +12 -5
  110. package/schema/lhar.schema.json +50 -0
  111. package/dist/server/config.d.ts +0 -13
  112. package/dist/server/config.d.ts.map +0 -1
  113. package/dist/server/config.js +0 -36
  114. package/dist/server/config.js.map +0 -1
  115. package/dist/server/proxy.d.ts +0 -13
  116. package/dist/server/proxy.d.ts.map +0 -1
  117. package/dist/server/proxy.js +0 -218
  118. package/dist/server/proxy.js.map +0 -1
  119. package/dist/server/static.d.ts +0 -9
  120. package/dist/server/static.d.ts.map +0 -1
  121. package/dist/server/static.js +0 -78
  122. package/dist/server/static.js.map +0 -1
  123. package/dist/server.d.ts +0 -3
  124. package/dist/server.d.ts.map +0 -1
  125. package/dist/server.js +0 -43
  126. package/dist/server.js.map +0 -1
package/mitm_addon.py CHANGED
@@ -4,16 +4,20 @@ mitmproxy addon for Context Lens.
4
4
  Captures LLM API requests passing through mitmproxy and forwards them
5
5
  to Context Lens's ingest API for visualization.
6
6
 
7
- Usage:
8
- mitmdump -s mitm_addon.py
7
+ This addon is used for Codex (subscription mode) and other tools that
8
+ connect directly to their APIs over HTTPS and cannot be redirected to
9
+ a custom base URL. The CLI starts mitmproxy automatically when needed.
9
10
 
10
- Then run codex through the proxy:
11
- https_proxy=http://localhost:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem codex "prompt"
11
+ Manual usage:
12
+ context-lens background start
13
+ mitmdump -p 8888 -s mitm_addon.py
14
+ https_proxy=http://localhost:8888 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem codex "prompt"
12
15
 
13
16
  View at http://localhost:4041
14
17
  """
15
18
 
16
19
  import json
20
+ import time
17
21
  import urllib.request
18
22
  from mitmproxy import http
19
23
 
@@ -23,7 +27,7 @@ INGEST_URL = "http://localhost:4041/api/ingest"
23
27
  CAPTURE_PATTERNS = [
24
28
  # Codex subscription
25
29
  ("chatgpt.com", "/backend-api/codex/responses", "chatgpt", "codex"),
26
- # OpenAI API (if someone uses forward proxy for API key tools too)
30
+ # OpenAI API
27
31
  ("api.openai.com", "/v1/responses", "openai", "openai"),
28
32
  ("api.openai.com", "/v1/chat/completions", "openai", "openai"),
29
33
  # Anthropic API
@@ -41,6 +45,28 @@ def match_request(flow: http.HTTPFlow):
41
45
  return None, None
42
46
 
43
47
 
48
+ def _detect_api_format(path: str) -> str:
49
+ """Detect API format from the request path."""
50
+ if "/backend-api/" in path:
51
+ return "responses"
52
+ if "/responses" in path:
53
+ return "responses"
54
+ if "/chat/completions" in path:
55
+ return "chat-completions"
56
+ if "/v1/messages" in path:
57
+ return "anthropic-messages"
58
+ return "unknown"
59
+
60
+
61
+ def _parse_sse_response(text: str) -> str:
62
+ """Parse SSE event stream, extracting data lines as raw text.
63
+
64
+ Returns the raw SSE text for the capture. The analysis server
65
+ handles SSE parsing during ingestion.
66
+ """
67
+ return text
68
+
69
+
44
70
  def response(flow: http.HTTPFlow):
45
71
  """Called when a response is received."""
46
72
  if flow.request.method != "POST":
@@ -50,34 +76,77 @@ def response(flow: http.HTTPFlow):
50
76
  if not provider:
51
77
  return
52
78
 
79
+ # Parse request body (mitmproxy auto-decompresses zstd/gzip)
53
80
  try:
54
- request_body = json.loads(flow.request.get_text())
55
- except (json.JSONDecodeError, ValueError):
81
+ request_text = flow.request.get_text()
82
+ request_body = json.loads(request_text)
83
+ except (json.JSONDecodeError, ValueError, Exception) as e:
84
+ print(f"[context-lens] Could not parse request body: {e}")
56
85
  return
57
86
 
87
+ # Get response body (may be JSON or SSE event stream)
88
+ response_body = ""
89
+ response_is_streaming = False
58
90
  try:
59
- response_body = json.loads(flow.response.get_text())
60
- except (json.JSONDecodeError, ValueError):
61
- response_body = {}
62
-
63
- # Detect API format from path
64
- path = flow.request.path
65
- if "/responses" in path:
66
- api_format = "responses"
67
- elif "/chat/completions" in path:
68
- api_format = "chat-completions"
69
- elif "/v1/messages" in path:
70
- api_format = "anthropic-messages"
71
- else:
72
- api_format = "chatgpt-backend"
73
-
74
- payload = json.dumps({
91
+ response_text = flow.response.get_text()
92
+ content_type = flow.response.headers.get("content-type", "")
93
+ if "text/event-stream" in content_type or response_text.startswith("event:"):
94
+ response_body = response_text
95
+ response_is_streaming = True
96
+ else:
97
+ response_body = response_text
98
+ except Exception as e:
99
+ print(f"[context-lens] Could not read response body: {e}")
100
+ response_body = ""
101
+
102
+ # Build capture in the same format as proxy/capture.ts
103
+ api_format = _detect_api_format(flow.request.path)
104
+
105
+ # Select safe headers (no auth tokens)
106
+ safe_request_headers = {}
107
+ for k, v in flow.request.headers.items():
108
+ lower = k.lower()
109
+ if lower in ("content-type", "content-encoding", "accept",
110
+ "user-agent", "anthropic-version", "openai-beta",
111
+ "x-request-id"):
112
+ safe_request_headers[k] = v
113
+
114
+ safe_response_headers = {}
115
+ for k, v in flow.response.headers.items():
116
+ lower = k.lower()
117
+ if lower in ("content-type", "x-request-id", "x-ratelimit-limit-requests",
118
+ "x-ratelimit-remaining-requests", "x-ratelimit-limit-tokens",
119
+ "x-ratelimit-remaining-tokens", "openai-processing-ms",
120
+ "anthropic-ratelimit-requests-limit",
121
+ "anthropic-ratelimit-requests-remaining"):
122
+ safe_response_headers[k] = v
123
+
124
+ capture = {
125
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
126
+ "method": "POST",
127
+ "path": flow.request.path,
128
+ "source": source,
75
129
  "provider": provider,
76
130
  "apiFormat": api_format,
77
- "source": source,
78
- "body": request_body,
79
- "response": response_body,
80
- }).encode()
131
+ "targetUrl": flow.request.pretty_url,
132
+ "requestHeaders": safe_request_headers,
133
+ "requestBody": request_body,
134
+ "requestBytes": len(flow.request.raw_content),
135
+ "responseStatus": flow.response.status_code,
136
+ "responseHeaders": safe_response_headers,
137
+ "responseBody": response_body,
138
+ "responseIsStreaming": response_is_streaming,
139
+ "responseBytes": len(flow.response.raw_content),
140
+ "timings": {
141
+ "send_ms": 0,
142
+ "wait_ms": 0,
143
+ "receive_ms": 0,
144
+ "total_ms": int((flow.response.timestamp_end - flow.request.timestamp_start) * 1000)
145
+ if flow.response.timestamp_end and flow.request.timestamp_start else 0,
146
+ },
147
+ }
148
+
149
+ payload = json.dumps(capture).encode()
81
150
 
82
151
  try:
83
152
  req = urllib.request.Request(
@@ -85,6 +154,8 @@ def response(flow: http.HTTPFlow):
85
154
  data=payload,
86
155
  headers={"Content-Type": "application/json"},
87
156
  )
88
- urllib.request.urlopen(req, timeout=2)
157
+ urllib.request.urlopen(req, timeout=5)
158
+ model = request_body.get("model", "unknown")
159
+ print(f"[context-lens] Captured {source}/{provider} request (model={model})")
89
160
  except Exception as e:
90
161
  print(f"[context-lens] Failed to ingest: {e}")
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "context-lens",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "description": "Local HTTP proxy that intercepts LLM API calls and visualizes context windows",
6
- "main": "dist/server.js",
6
+ "main": "dist/proxy/server.js",
7
7
  "bin": {
8
- "context-lens": "./dist/cli.js"
8
+ "context-lens": "./dist/cli.js",
9
+ "context-lens-proxy": "./dist/proxy/server.js",
10
+ "context-lens-analysis": "./dist/analysis/server.js"
9
11
  },
10
12
  "scripts": {
11
13
  "generate:types": "json2ts schema/lhar.schema.json src/lhar-types.generated.ts --unreachableDefinitions",
@@ -13,10 +15,10 @@
13
15
  "build": "pnpm run generate:version && pnpm run generate:types && tsc",
14
16
  "clean:test": "node -e \"require('node:fs').rmSync('dist-test', { recursive: true, force: true })\"",
15
17
  "build:test": "pnpm run generate:version && pnpm run generate:types && pnpm run clean:test && tsc -p tsconfig.test.json",
16
- "start": "node dist/server.js",
18
+ "start": "node dist/cli.js",
17
19
  "test": "pnpm run build:test && node --test --experimental-test-isolation=none 'dist-test/test/**/*.test.js'",
18
20
  "dev": "tsc --watch",
19
- "postbuild": "chmod +x dist/cli.js",
21
+ "postbuild": "chmod +x dist/cli.js dist/proxy/server.js dist/analysis/server.js",
20
22
  "lint": "biome check .",
21
23
  "lint:fix": "biome check --write .",
22
24
  "prepublishOnly": "pnpm run build"
@@ -58,5 +60,10 @@
58
60
  "@types/node": "^25.2.2",
59
61
  "json-schema-to-typescript": "^15.0.4",
60
62
  "typescript": "^5.9.3"
63
+ },
64
+ "dependencies": {
65
+ "@hono/node-server": "^1.19.9",
66
+ "hono": "^4.11.9",
67
+ "valibot": "^1.2.0"
61
68
  }
62
69
  }
@@ -21,6 +21,46 @@
21
21
  ]
22
22
  },
23
23
 
24
+ "ToolDefinitionEntry": {
25
+ "type": "object",
26
+ "description": "A tool/function available to the model in this request.",
27
+ "properties": {
28
+ "name": { "type": "string" },
29
+ "description": {
30
+ "oneOf": [
31
+ { "type": "string" },
32
+ { "type": "null" }
33
+ ]
34
+ }
35
+ },
36
+ "required": ["name", "description"],
37
+ "additionalProperties": false
38
+ },
39
+
40
+ "ToolCallEntry": {
41
+ "type": "object",
42
+ "description": "A tool invocation found in the conversation history of this request.",
43
+ "properties": {
44
+ "name": { "type": "string" },
45
+ "call_id": {
46
+ "oneOf": [
47
+ { "type": "string" },
48
+ { "type": "null" }
49
+ ]
50
+ },
51
+ "arguments": {
52
+ "description": "The arguments passed to the tool. Object when parsed, string when raw/unparsed.",
53
+ "oneOf": [
54
+ { "type": "object" },
55
+ { "type": "string" },
56
+ { "type": "null" }
57
+ ]
58
+ }
59
+ },
60
+ "required": ["name", "call_id", "arguments"],
61
+ "additionalProperties": false
62
+ },
63
+
24
64
  "CompositionEntry": {
25
65
  "type": "object",
26
66
  "properties": {
@@ -256,6 +296,16 @@
256
296
  "required": ["tokens_added_this_turn", "cumulative_tokens", "compaction_detected"],
257
297
  "additionalProperties": false
258
298
  },
299
+ "tool_definitions": {
300
+ "type": "array",
301
+ "description": "Tool/function schemas available to the model in this request.",
302
+ "items": { "$ref": "#/definitions/ToolDefinitionEntry" }
303
+ },
304
+ "tool_calls": {
305
+ "type": "array",
306
+ "description": "Tool invocations found in the conversation history of this request.",
307
+ "items": { "$ref": "#/definitions/ToolCallEntry" }
308
+ },
259
309
  "security": {
260
310
  "type": "object",
261
311
  "properties": {
@@ -1,13 +0,0 @@
1
- import type { PrivacyLevel, Upstreams } from "../types.js";
2
- export interface ServerConfig {
3
- upstreams: Upstreams;
4
- bindHost: string;
5
- allowTargetOverride: boolean;
6
- dataDir: string;
7
- stateFile: string;
8
- maxSessions: number;
9
- maxCompactMessages: number;
10
- privacy: PrivacyLevel;
11
- }
12
- export declare function loadServerConfig(baseDir: string): ServerConfig;
13
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CA8C9D"}
@@ -1,36 +0,0 @@
1
- import path from "node:path";
2
- export function loadServerConfig(baseDir) {
3
- // Upstream targets, configurable via env vars
4
- const UPSTREAM_OPENAI_URL = process.env.UPSTREAM_OPENAI_URL || "https://api.openai.com/v1";
5
- const UPSTREAM_ANTHROPIC_URL = process.env.UPSTREAM_ANTHROPIC_URL || "https://api.anthropic.com";
6
- const UPSTREAM_CHATGPT_URL = process.env.UPSTREAM_CHATGPT_URL || "https://chatgpt.com";
7
- const UPSTREAM_GEMINI_URL = process.env.UPSTREAM_GEMINI_URL ||
8
- "https://generativelanguage.googleapis.com";
9
- const UPSTREAM_GEMINI_CODE_ASSIST_URL = process.env.UPSTREAM_GEMINI_CODE_ASSIST_URL ||
10
- "https://cloudcode-pa.googleapis.com";
11
- // Safety defaults:
12
- // - Bind only to localhost unless explicitly overridden.
13
- // - Do not honor `x-target-url` unless explicitly enabled (prevents accidental open-proxy/SSRF).
14
- const bindHost = process.env.CONTEXT_LENS_BIND_HOST || "127.0.0.1";
15
- const allowTargetOverride = process.env.CONTEXT_LENS_ALLOW_TARGET_OVERRIDE === "1";
16
- const privacyEnv = (process.env.CONTEXT_LENS_PRIVACY || "standard").toLowerCase();
17
- const privacy = privacyEnv === "minimal" || privacyEnv === "full" ? privacyEnv : "standard";
18
- const dataDir = path.join(baseDir, "..", "data");
19
- return {
20
- upstreams: {
21
- openai: UPSTREAM_OPENAI_URL,
22
- anthropic: UPSTREAM_ANTHROPIC_URL,
23
- chatgpt: UPSTREAM_CHATGPT_URL,
24
- gemini: UPSTREAM_GEMINI_URL,
25
- geminiCodeAssist: UPSTREAM_GEMINI_CODE_ASSIST_URL,
26
- },
27
- bindHost,
28
- allowTargetOverride,
29
- dataDir,
30
- stateFile: path.join(dataDir, "state.jsonl"),
31
- maxSessions: 100,
32
- maxCompactMessages: 60,
33
- privacy,
34
- };
35
- }
36
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAe7B,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,8CAA8C;IAC9C,MAAM,mBAAmB,GACvB,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,2BAA2B,CAAC;IACjE,MAAM,sBAAsB,GAC1B,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,2BAA2B,CAAC;IACpE,MAAM,oBAAoB,GACxB,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,qBAAqB,CAAC;IAC5D,MAAM,mBAAmB,GACvB,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,2CAA2C,CAAC;IAC9C,MAAM,+BAA+B,GACnC,OAAO,CAAC,GAAG,CAAC,+BAA+B;QAC3C,qCAAqC,CAAC;IAExC,mBAAmB;IACnB,yDAAyD;IACzD,iGAAiG;IACjG,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,WAAW,CAAC;IACnE,MAAM,mBAAmB,GACvB,OAAO,CAAC,GAAG,CAAC,kCAAkC,KAAK,GAAG,CAAC;IAEzD,MAAM,UAAU,GAAG,CACjB,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,UAAU,CAC/C,CAAC,WAAW,EAAE,CAAC;IAChB,MAAM,OAAO,GACX,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;IAE9E,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAEjD,OAAO;QACL,SAAS,EAAE;YACT,MAAM,EAAE,mBAAmB;YAC3B,SAAS,EAAE,sBAAsB;YACjC,OAAO,EAAE,oBAAoB;YAC7B,MAAM,EAAE,mBAAmB;YAC3B,gBAAgB,EAAE,+BAA+B;SAClD;QACD,QAAQ;QACR,mBAAmB;QACnB,OAAO;QACP,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC5C,WAAW,EAAE,GAAG;QAChB,kBAAkB,EAAE,EAAE;QACtB,OAAO;KACR,CAAC;AACJ,CAAC"}
@@ -1,13 +0,0 @@
1
- import http from "node:http";
2
- import url from "node:url";
3
- import type { Upstreams } from "../types.js";
4
- import type { Store } from "./store.js";
5
- export declare function forwardRequest(req: http.IncomingMessage, res: http.ServerResponse, parsedUrl: url.UrlWithStringQuery, body: Buffer | null, opts: {
6
- upstreams: Upstreams;
7
- allowTargetOverride: boolean;
8
- }): void;
9
- export declare function createProxyHandler(store: Store, opts: {
10
- upstreams: Upstreams;
11
- allowTargetOverride: boolean;
12
- }): (req: http.IncomingMessage, res: http.ServerResponse) => void;
13
- //# sourceMappingURL=proxy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/server/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,GAAG,MAAM,UAAU,CAAC;AAS3B,OAAO,KAAK,EAA4B,SAAS,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AA6CxC,wBAAgB,cAAc,CAC5B,GAAG,EAAE,IAAI,CAAC,eAAe,EACzB,GAAG,EAAE,IAAI,CAAC,cAAc,EACxB,SAAS,EAAE,GAAG,CAAC,kBAAkB,EACjC,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,IAAI,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,GAC3D,IAAI,CA2CN;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,mBAAmB,EAAE,OAAO,CAAA;CAAE,GAC3D,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CA8N/D"}
@@ -1,218 +0,0 @@
1
- import http from "node:http";
2
- import https from "node:https";
3
- import url from "node:url";
4
- import { detectApiFormat, estimateTokens, extractSource, parseContextInfo, resolveTargetUrl, } from "../core.js";
5
- import { headersForResolution, selectHeaders } from "../server-utils.js";
6
- function buildForwardHeaders(reqHeaders, targetHost, bodyLength) {
7
- const forwardHeaders = { ...reqHeaders };
8
- delete forwardHeaders["x-target-url"];
9
- delete forwardHeaders.host;
10
- if (targetHost) {
11
- forwardHeaders.host = targetHost;
12
- }
13
- if (bodyLength != null) {
14
- delete forwardHeaders["transfer-encoding"];
15
- forwardHeaders["content-length"] = bodyLength;
16
- }
17
- return forwardHeaders;
18
- }
19
- function attachProxyLifecycleHandlers(res, proxyReq) {
20
- // Abort upstream request if client disconnects
21
- res.on("close", () => {
22
- if (!proxyReq.destroyed)
23
- proxyReq.destroy();
24
- });
25
- proxyReq.on("error", (err) => {
26
- // Suppress errors from client-initiated disconnects
27
- if (res.destroyed)
28
- return;
29
- const detail = err.message || ("code" in err ? err.code : "unknown");
30
- console.error("Proxy error:", detail);
31
- if (!res.headersSent) {
32
- res.writeHead(502, { "Content-Type": "application/json" });
33
- }
34
- if (!res.destroyed) {
35
- res.end(JSON.stringify({ error: "Proxy error", details: err.message }));
36
- }
37
- });
38
- }
39
- export function forwardRequest(req, res, parsedUrl, body, opts) {
40
- const { targetUrl } = resolveTargetUrl({ pathname: parsedUrl.pathname, search: parsedUrl.search }, headersForResolution(req.headers, req.socket.remoteAddress, opts.allowTargetOverride), opts.upstreams);
41
- const targetParsed = url.parse(targetUrl);
42
- // When we buffer the body, replace chunked encoding with exact content-length
43
- const forwardHeaders = buildForwardHeaders(req.headers, targetParsed.host, body ? body.length : undefined);
44
- const protocol = targetParsed.protocol === "https:" ? https : http;
45
- const proxyReq = protocol.request({
46
- hostname: targetParsed.hostname,
47
- port: targetParsed.port,
48
- path: targetParsed.path,
49
- method: req.method,
50
- headers: forwardHeaders,
51
- }, (proxyRes) => {
52
- if (!res.headersSent)
53
- res.writeHead(proxyRes.statusCode, proxyRes.headers);
54
- proxyRes.pipe(res);
55
- proxyRes.on("error", (err) => {
56
- console.error("Upstream response error (forward):", err.message);
57
- if (!res.destroyed)
58
- res.end();
59
- });
60
- });
61
- attachProxyLifecycleHandlers(res, proxyReq);
62
- if (body)
63
- proxyReq.write(body);
64
- proxyReq.end();
65
- }
66
- export function createProxyHandler(store, opts) {
67
- return function handleProxy(req, res) {
68
- const parsedUrl = url.parse(req.url);
69
- const { source, cleanPath } = extractSource(parsedUrl.pathname);
70
- // Use clean path (without source prefix) for routing
71
- const cleanUrl = { ...parsedUrl, pathname: cleanPath };
72
- const { targetUrl, provider } = resolveTargetUrl({ pathname: cleanPath, search: parsedUrl.search }, headersForResolution(req.headers, req.socket.remoteAddress, opts.allowTargetOverride), opts.upstreams);
73
- const hasAuth = !!req.headers.authorization;
74
- const sourceTag = source ? `[${source}]` : "";
75
- console.log(`${req.method} ${req.url} → ${targetUrl} [${provider}] ${sourceTag} auth=${hasAuth}`);
76
- // For non-POST requests (GET /v1/models, OPTIONS, etc.), pass through directly
77
- if (req.method !== "POST") {
78
- forwardRequest(req, res, cleanUrl, null, opts);
79
- return;
80
- }
81
- // Collect body as raw Buffers to avoid corrupting multi-byte UTF-8 at chunk boundaries
82
- const chunks = [];
83
- let clientAborted = false;
84
- req.on("data", (chunk) => {
85
- chunks.push(chunk);
86
- });
87
- req.on("error", () => {
88
- clientAborted = true;
89
- });
90
- req.on("end", () => {
91
- if (clientAborted)
92
- return;
93
- const bodyBuffer = Buffer.concat(chunks);
94
- const body = bodyBuffer.toString("utf8");
95
- let bodyData;
96
- try {
97
- bodyData = JSON.parse(body);
98
- }
99
- catch (_e) {
100
- console.log(` ⚠ Body is not JSON (${bodyBuffer.length} bytes), capturing raw`);
101
- // Still capture the raw request even if body isn't JSON
102
- const rawInfo = {
103
- provider,
104
- apiFormat: "raw",
105
- model: "unknown",
106
- systemTokens: 0,
107
- toolsTokens: 0,
108
- messagesTokens: estimateTokens(body),
109
- totalTokens: estimateTokens(body),
110
- systemPrompts: [],
111
- tools: [],
112
- messages: [
113
- {
114
- role: "raw",
115
- content: body.substring(0, 2000),
116
- tokens: estimateTokens(body),
117
- },
118
- ],
119
- };
120
- store.storeRequest(rawInfo, { raw: true }, source, undefined, undefined, selectHeaders(req.headers));
121
- forwardRequest(req, res, cleanUrl, bodyBuffer, opts);
122
- return;
123
- }
124
- console.log(` ✓ Parsed JSON body (${Object.keys(bodyData).join(", ")})`);
125
- const apiFormat = detectApiFormat(cleanPath);
126
- // Gemini: model is in the URL path, not in the body
127
- if (apiFormat === "gemini" && !bodyData.model) {
128
- const modelMatch = cleanPath.match(/\/models\/([^/:]+)/);
129
- if (modelMatch)
130
- bodyData.model = modelMatch[1];
131
- }
132
- const contextInfo = parseContextInfo(provider, bodyData, apiFormat);
133
- // Skip capturing utility endpoints (count_tokens, etc.). Not real conversation turns
134
- const isUtilityEndpoint = /\/count_tokens\b|:countTokens\b|:loadCodeAssist\b|:retrieveUserQuota\b|:listExperiments\b|:onboardUser\b|:fetchAdminControls\b|:recordCodeAssistMetrics\b/.test(cleanPath);
135
- const targetParsed = url.parse(targetUrl);
136
- // Ensure content-length matches the exact bytes we forward
137
- const forwardHeaders = buildForwardHeaders(req.headers, targetParsed.host, bodyBuffer.length);
138
- // Make request to actual API
139
- const protocol = targetParsed.protocol === "https:" ? https : http;
140
- const startTime = performance.now();
141
- let firstByteTime = 0;
142
- const reqBytes = bodyBuffer.length;
143
- const proxyReq = protocol.request({
144
- hostname: targetParsed.hostname,
145
- port: targetParsed.port,
146
- path: targetParsed.path,
147
- method: req.method,
148
- headers: forwardHeaders,
149
- }, (proxyRes) => {
150
- console.log(` ← ${proxyRes.statusCode} ${proxyRes.statusMessage}`);
151
- // Forward response headers
152
- res.writeHead(proxyRes.statusCode, proxyRes.headers);
153
- const httpStatus = proxyRes.statusCode || 0;
154
- // Handle streaming vs non-streaming
155
- const isStreaming = proxyRes.headers["content-type"]?.includes("text/event-stream");
156
- let respBytes = 0;
157
- const capturedReqHeaders = selectHeaders(req.headers);
158
- const capturedResHeaders = selectHeaders(proxyRes.headers);
159
- // Collect response as Buffer[] to avoid corrupting multi-byte UTF-8 at chunk boundaries
160
- const respChunks = [];
161
- proxyRes.on("data", (chunk) => {
162
- if (!firstByteTime)
163
- firstByteTime = performance.now();
164
- respBytes += chunk.length;
165
- respChunks.push(chunk);
166
- if (!res.destroyed)
167
- res.write(chunk);
168
- });
169
- proxyRes.on("end", () => {
170
- const endTime = performance.now();
171
- if (!firstByteTime)
172
- firstByteTime = endTime;
173
- const meta = {
174
- httpStatus,
175
- timings: {
176
- send_ms: Math.round(firstByteTime - startTime),
177
- wait_ms: Math.round(firstByteTime - startTime),
178
- receive_ms: Math.round(endTime - firstByteTime),
179
- total_ms: Math.round(endTime - startTime),
180
- tokens_per_second: null,
181
- },
182
- requestBytes: reqBytes,
183
- responseBytes: respBytes,
184
- targetUrl,
185
- requestHeaders: capturedReqHeaders,
186
- responseHeaders: capturedResHeaders,
187
- };
188
- const respBody = Buffer.concat(respChunks).toString("utf8");
189
- if (!isUtilityEndpoint) {
190
- if (isStreaming) {
191
- store.storeRequest(contextInfo, { streaming: true, chunks: respBody }, source, bodyData, meta, capturedReqHeaders);
192
- }
193
- else {
194
- try {
195
- const responseData = JSON.parse(respBody);
196
- store.storeRequest(contextInfo, responseData, source, bodyData, meta, capturedReqHeaders);
197
- }
198
- catch (_e) {
199
- store.storeRequest(contextInfo, { raw: respBody }, source, bodyData, meta, capturedReqHeaders);
200
- }
201
- }
202
- }
203
- if (!res.destroyed)
204
- res.end();
205
- });
206
- proxyRes.on("error", (err) => {
207
- console.error("Upstream response error:", err.message);
208
- if (!res.destroyed)
209
- res.end();
210
- });
211
- });
212
- attachProxyLifecycleHandlers(res, proxyReq);
213
- proxyReq.write(bodyBuffer);
214
- proxyReq.end();
215
- });
216
- };
217
- }
218
- //# sourceMappingURL=proxy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/server/proxy.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- import type http from "node:http";
2
- /**
3
- * Create a static file handler that serves from `uiDir` if it exists,
4
- * otherwise falls back to serving `fallbackHtml` at `/`.
5
- *
6
- * Returns a function that handles the request, always responds.
7
- */
8
- export declare function createStaticHandler(uiDir: string | null, fallbackHtml: string): (req: http.IncomingMessage, res: http.ServerResponse) => void;
9
- //# sourceMappingURL=static.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"static.d.ts","sourceRoot":"","sources":["../../src/server/static.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAgBlC;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,YAAY,EAAE,MAAM,GACnB,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,KAAK,IAAI,CA+D/D"}