context-lens 0.5.5 → 0.6.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 (97) hide show
  1. package/README.md +36 -6
  2. package/dist/analysis/ingest.d.ts.map +1 -1
  3. package/dist/analysis/ingest.js +12 -5
  4. package/dist/analysis/ingest.js.map +1 -1
  5. package/dist/analysis/server.js +5 -0
  6. package/dist/analysis/server.js.map +1 -1
  7. package/dist/cli-utils.d.ts +1 -0
  8. package/dist/cli-utils.d.ts.map +1 -1
  9. package/dist/cli-utils.js +36 -3
  10. package/dist/cli-utils.js.map +1 -1
  11. package/dist/cli.js +193 -7
  12. package/dist/cli.js.map +1 -1
  13. package/dist/core/conversation.d.ts +1 -0
  14. package/dist/core/conversation.d.ts.map +1 -1
  15. package/dist/core/conversation.js +12 -1
  16. package/dist/core/conversation.js.map +1 -1
  17. package/dist/core/models.d.ts +7 -1
  18. package/dist/core/models.d.ts.map +1 -1
  19. package/dist/core/models.js +95 -13
  20. package/dist/core/models.js.map +1 -1
  21. package/dist/core/parse.d.ts.map +1 -1
  22. package/dist/core/parse.js +26 -25
  23. package/dist/core/parse.js.map +1 -1
  24. package/dist/core/routing.d.ts +0 -7
  25. package/dist/core/routing.d.ts.map +1 -1
  26. package/dist/core/routing.js +18 -4
  27. package/dist/core/routing.js.map +1 -1
  28. package/dist/core/session-format.d.ts.map +1 -1
  29. package/dist/core/source.d.ts +1 -0
  30. package/dist/core/source.d.ts.map +1 -1
  31. package/dist/core/source.js +9 -1
  32. package/dist/core/source.js.map +1 -1
  33. package/dist/core/tokenizer.d.ts +30 -0
  34. package/dist/core/tokenizer.d.ts.map +1 -0
  35. package/dist/core/tokenizer.js +149 -0
  36. package/dist/core/tokenizer.js.map +1 -0
  37. package/dist/core/tokens.d.ts +11 -5
  38. package/dist/core/tokens.d.ts.map +1 -1
  39. package/dist/core/tokens.js +14 -7
  40. package/dist/core/tokens.js.map +1 -1
  41. package/dist/core.d.ts +2 -1
  42. package/dist/core.d.ts.map +1 -1
  43. package/dist/core.js +2 -1
  44. package/dist/core.js.map +1 -1
  45. package/dist/lhar/composition.d.ts.map +1 -1
  46. package/dist/lhar/composition.js +40 -39
  47. package/dist/lhar/composition.js.map +1 -1
  48. package/dist/lhar/export.js +1 -1
  49. package/dist/lhar/export.js.map +1 -1
  50. package/dist/proxy/capture.d.ts +3 -25
  51. package/dist/proxy/capture.d.ts.map +1 -1
  52. package/dist/proxy/capture.js +1 -3
  53. package/dist/proxy/capture.js.map +1 -1
  54. package/dist/proxy/config.d.ts +1 -2
  55. package/dist/proxy/config.d.ts.map +1 -1
  56. package/dist/proxy/config.js +0 -1
  57. package/dist/proxy/config.js.map +1 -1
  58. package/dist/proxy/server.d.ts +8 -19
  59. package/dist/proxy/server.d.ts.map +1 -1
  60. package/dist/proxy/server.js +97 -44
  61. package/dist/proxy/server.js.map +1 -1
  62. package/dist/server/api.d.ts.map +1 -1
  63. package/dist/server/api.js +62 -4
  64. package/dist/server/api.js.map +1 -1
  65. package/dist/server/store.d.ts +7 -0
  66. package/dist/server/store.d.ts.map +1 -1
  67. package/dist/server/store.js +90 -11
  68. package/dist/server/store.js.map +1 -1
  69. package/dist/server/tags-store.d.ts +33 -0
  70. package/dist/server/tags-store.d.ts.map +1 -0
  71. package/dist/server/tags-store.js +150 -0
  72. package/dist/server/tags-store.js.map +1 -0
  73. package/dist/server/webui.js +1 -1
  74. package/dist/server/webui.js.map +1 -1
  75. package/dist/types.d.ts +3 -0
  76. package/dist/types.d.ts.map +1 -1
  77. package/dist/version.generated.d.ts +1 -1
  78. package/dist/version.generated.js +1 -1
  79. package/mitm_addon.py +30 -6
  80. package/package.json +5 -3
  81. package/ui/dist/assets/index-DYoafQtJ.css +1 -0
  82. package/ui/dist/assets/index-t5l_d6Oq.js +52 -0
  83. package/ui/dist/index.html +2 -2
  84. package/dist/proxy/forward.d.ts +0 -20
  85. package/dist/proxy/forward.d.ts.map +0 -1
  86. package/dist/proxy/forward.js +0 -210
  87. package/dist/proxy/forward.js.map +0 -1
  88. package/dist/proxy/headers.d.ts +0 -16
  89. package/dist/proxy/headers.d.ts.map +0 -1
  90. package/dist/proxy/headers.js +0 -37
  91. package/dist/proxy/headers.js.map +0 -1
  92. package/dist/proxy/routing.d.ts +0 -45
  93. package/dist/proxy/routing.d.ts.map +0 -1
  94. package/dist/proxy/routing.js +0 -139
  95. package/dist/proxy/routing.js.map +0 -1
  96. package/ui/dist/assets/index-D1eb5Vsp.css +0 -1
  97. package/ui/dist/assets/index-JtXLWNzT.js +0 -35
package/README.md CHANGED
@@ -8,6 +8,8 @@ See what's actually filling your context window. Context Lens is a local proxy t
8
8
 
9
9
  Works with Claude Code, Codex, Gemini CLI, Aider, Pi, and anything else that talks to OpenAI/Anthropic/Google APIs. No code changes needed.
10
10
 
11
+ **Using AI coding tools across a team?** Token costs compound fast when every developer runs agents all day. Context Lens gives you per-session visibility into where the budget goes — which tools, which patterns, which sessions are outliers. Export sessions as [LHAR](docs/LHAR.md) to share and compare. Team dashboards are on the roadmap; if that's relevant for you, [open an issue](https://github.com/larsderidder/context-lens/issues) or watch this repo.
12
+
11
13
  ![Context Lens UI](screenshot-overview.png)
12
14
 
13
15
  ## Installation
@@ -41,9 +43,10 @@ context-lens doctor # check ports, certs, background state
41
43
  context-lens background start # start detached proxy + UI
42
44
  context-lens background status
43
45
  context-lens background stop
46
+ context-lens stop # shorthand for background stop
44
47
  ```
45
48
 
46
- Aliases: `cc` → `claude`, `cx` → `codex`, `cpi` → `pi`, `gm` `gemini`.
49
+ Aliases: `cc` → `claude`, `cx` → `codex`, `gm` → `gemini`. For `pi`, add `alias cpi='context-lens pi'` to your shell rc.
47
50
 
48
51
  ## Docker
49
52
 
@@ -128,6 +131,7 @@ services:
128
131
  - **Context diff:** turn-to-turn delta showing what grew, shrank, or appeared
129
132
  - **Findings:** flags large tool results, unused tool definitions, context overflow risk, compaction events
130
133
  - **Auto-detection:** recognizes Claude Code, Codex, aider, Pi, and others by source tag or system prompt
134
+ - **Session tagging:** label sessions with custom tags, filter the session list by tag
131
135
  - **LHAR export:** download session data as LHAR (LLM HTTP Archive) format ([doc](docs/LHAR.md))
132
136
  - **State persistence:** data survives restarts; delete individual sessions or reset all from the UI
133
137
  - **Streaming support:** passes through SSE chunks in real-time
@@ -175,14 +179,29 @@ If you prefer to configure it manually, set `baseUrl` in `~/.pi/agent/models.jso
175
179
  }
176
180
  ```
177
181
 
178
- ### OpenAI-Compatible Endpoints
182
+ ### OpenCode
183
+
184
+ OpenCode connects to multiple providers simultaneously over HTTPS. Use `context-lens opencode` — it routes all traffic through mitmproxy so every provider call is captured regardless of which model is active:
185
+
186
+ ```bash
187
+ pipx install mitmproxy
188
+ context-lens opencode
189
+ ```
179
190
 
180
- Many providers expose OpenAI-compatible APIs (OpenRouter, Together, Groq, Fireworks, Ollama, vLLM, OpenCode Zen, etc.). Override the upstream URL to point at your provider:
191
+ If you only use OpenCode with a single OpenAI-compatible endpoint (e.g. OpenCode Zen), you can also use the base URL override approach instead:
181
192
 
182
193
  ```bash
183
194
  UPSTREAM_OPENAI_URL=https://opencode.ai/zen/v1 context-lens -- opencode "prompt"
184
195
  ```
185
196
 
197
+ ### OpenAI-Compatible Endpoints
198
+
199
+ Many providers expose OpenAI-compatible APIs (OpenRouter, Together, Groq, Fireworks, Ollama, vLLM, etc.). Override the upstream URL to point at your provider:
200
+
201
+ ```bash
202
+ UPSTREAM_OPENAI_URL=https://my-provider.com/v1 context-lens -- my-tool "prompt"
203
+ ```
204
+
186
205
  `UPSTREAM_OPENAI_URL` is global: all OpenAI-format requests go to that upstream. Use separate proxy instances if you need to hit multiple endpoints simultaneously.
187
206
 
188
207
  ### Codex Subscription Mode
@@ -196,6 +215,17 @@ context-lens codex
196
215
 
197
216
  If Codex fails with certificate trust errors, install/trust the mitmproxy CA certificate (`~/.mitmproxy/mitmproxy-ca-cert.pem`) for your environment.
198
217
 
218
+ ### Pi with ChatGPT Subscription Models
219
+
220
+ Pi's `openai-codex` provider (e.g. `gpt-5.2-codex`) connects directly to `chatgpt.com` and cannot be redirected via base URL overrides. Use the `--mitm` flag to route through mitmproxy instead:
221
+
222
+ ```bash
223
+ pipx install mitmproxy
224
+ context-lens pi --mitm
225
+ ```
226
+
227
+ Standard OpenAI API models in Pi work fine without `--mitm`.
228
+
199
229
  ## How It Works
200
230
 
201
231
  Context Lens sits between your coding tool and the LLM API, capturing requests in transit. It has two parts: a **proxy** and an **analysis server**.
@@ -208,7 +238,7 @@ Tool ─HTTP─▶ Proxy (:4040) ─HTTPS─▶ api.anthropic.com / api.open
208
238
  Analysis Server (:4041) → Web UI
209
239
  ```
210
240
 
211
- The **proxy** forwards requests to the LLM API and writes each request/response pair to disk. It has **zero external dependencies** (only Node.js built-ins), so you can read the entire proxy source and verify it does nothing unexpected with your API keys.
241
+ The **proxy** forwards requests to the LLM API and writes each request/response pair to disk. It is built on [`@contextio/proxy`](https://github.com/larsderidder/contextio), a minimal package with no external dependencies, so you can read the entire proxy source and verify it does nothing unexpected with your API keys.
212
242
 
213
243
  The **analysis server** picks up those captures, parses request bodies, estimates tokens, groups requests into conversations, computes composition breakdowns, calculates costs, and scores context health. It serves the web UI and API.
214
244
 
@@ -231,9 +261,9 @@ Tools like [Langfuse](https://langfuse.com/) and [Braintrust](https://braintrust
231
261
  | **Context composition breakdown** | Yes (treemap, per-category) | Token totals only |
232
262
  | **Runs locally** | Yes, entirely | Cloud or self-hosted server |
233
263
  | **Prompt management & evals** | No | Yes |
234
- | **Team/production use** | No (single-user, local) | Yes |
264
+ | **Team/production use** | Individual today, team features planned | Yes |
235
265
 
236
- Context Lens is for developers who want to understand and optimize their coding agent sessions. If you need production monitoring, prompt versioning, or team dashboards, use Langfuse.
266
+ Context Lens is for developers who want to understand and optimize their coding agent sessions. If you need production monitoring, prompt versioning, or evals, use Langfuse.
237
267
 
238
268
  ## Data
239
269
 
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/analysis/ingest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAGhD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAuEtE"}
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/analysis/ingest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAGhD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAgFtE"}
@@ -12,7 +12,9 @@ export function ingestCapture(store, capture) {
12
12
  const { provider, apiFormat, requestBody, responseBody } = capture;
13
13
  // Build contextInfo from the request body
14
14
  let contextInfo;
15
- if (requestBody) {
15
+ if (requestBody &&
16
+ typeof requestBody === "object" &&
17
+ !Array.isArray(requestBody)) {
16
18
  const body = { ...requestBody };
17
19
  // Gemini: model is in the URL path, not in the body
18
20
  if (apiFormat === "gemini" && !body.model) {
@@ -24,21 +26,22 @@ export function ingestCapture(store, capture) {
24
26
  }
25
27
  else {
26
28
  // Non-JSON request: create a raw contextInfo
29
+ const rawTokens = estimateTokens(responseBody);
27
30
  contextInfo = {
28
31
  provider,
29
32
  apiFormat: "raw",
30
33
  model: "unknown",
31
34
  systemTokens: 0,
32
35
  toolsTokens: 0,
33
- messagesTokens: estimateTokens(responseBody),
34
- totalTokens: estimateTokens(responseBody),
36
+ messagesTokens: rawTokens,
37
+ totalTokens: rawTokens,
35
38
  systemPrompts: [],
36
39
  tools: [],
37
40
  messages: [
38
41
  {
39
42
  role: "raw",
40
43
  content: responseBody.substring(0, 2000),
41
- tokens: estimateTokens(responseBody),
44
+ tokens: rawTokens,
42
45
  },
43
46
  ],
44
47
  };
@@ -70,6 +73,10 @@ export function ingestCapture(store, capture) {
70
73
  responseHeaders: capture.responseHeaders,
71
74
  };
72
75
  // Feed into the store (which handles fingerprinting, scoring, etc.)
73
- store.storeRequest(contextInfo, responseData, capture.source, requestBody ?? undefined, meta, capture.requestHeaders, capture.sessionId ?? null);
76
+ store.storeRequest(contextInfo, responseData, capture.source, requestBody &&
77
+ typeof requestBody === "object" &&
78
+ !Array.isArray(requestBody)
79
+ ? requestBody
80
+ : undefined, meta, capture.requestHeaders, capture.sessionId ?? null);
74
81
  }
75
82
  //# sourceMappingURL=ingest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/analysis/ingest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK9D;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,OAAoB;IAC9D,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAEnE,0CAA0C;IAC1C,IAAI,WAAwB,CAAC;IAC7B,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;QAChC,oDAAoD;QACpD,IAAI,SAAS,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC5D,IAAI,UAAU;gBAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,WAAW,GAAG,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,6CAA6C;QAC7C,WAAW,GAAG;YACZ,QAAQ;YACR,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,SAAS;YAChB,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,CAAC;YACd,cAAc,EAAE,cAAc,CAAC,YAAY,CAAC;YAC5C,WAAW,EAAE,cAAc,CAAC,YAAY,CAAC;YACzC,aAAa,EAAE,EAAE;YACjB,KAAK,EAAE,EAAE;YACT,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,KAAK;oBACX,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;oBACxC,MAAM,EAAE,cAAc,CAAC,YAAY,CAAC;iBACrC;aACF;SACF,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,IAAI,YAA0B,CAAC;IAC/B,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;QAChC,YAAY,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,IAAI,GAAgB;QACxB,UAAU,EAAE,OAAO,CAAC,cAAc;QAClC,OAAO,EAAE;YACP,GAAG,OAAO,CAAC,OAAO;YAClB,iBAAiB,EAAE,IAAI;SACxB;QACD,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,eAAe;KACzC,CAAC;IAEF,oEAAoE;IACpE,KAAK,CAAC,YAAY,CAChB,WAAW,EACX,YAAY,EACZ,OAAO,CAAC,MAAM,EACd,WAAW,IAAI,SAAS,EACxB,IAAI,EACJ,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,SAAS,IAAI,IAAI,CAC1B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/analysis/ingest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAK9D;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,OAAoB;IAC9D,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC;IAEnE,0CAA0C;IAC1C,IAAI,WAAwB,CAAC;IAC7B,IACE,WAAW;QACX,OAAO,WAAW,KAAK,QAAQ;QAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,EAC3B,CAAC;QACD,MAAM,IAAI,GAAG,EAAE,GAAI,WAAuC,EAAE,CAAC;QAC7D,oDAAoD;QACpD,IAAI,SAAS,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC5D,IAAI,UAAU;gBAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC7C,CAAC;QACD,WAAW,GAAG,gBAAgB,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,6CAA6C;QAC7C,MAAM,SAAS,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;QAC/C,WAAW,GAAG;YACZ,QAAQ;YACR,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,SAAS;YAChB,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,CAAC;YACd,cAAc,EAAE,SAAS;YACzB,WAAW,EAAE,SAAS;YACtB,aAAa,EAAE,EAAE;YACjB,KAAK,EAAE,EAAE;YACT,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,KAAK;oBACX,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,CAAC;oBACxC,MAAM,EAAE,SAAS;iBAClB;aACF;SACF,CAAC;IACJ,CAAC;IAED,qBAAqB;IACrB,IAAI,YAA0B,CAAC;IAC/B,IAAI,OAAO,CAAC,mBAAmB,EAAE,CAAC;QAChC,YAAY,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAC3D,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,GAAG,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IAED,yBAAyB;IACzB,MAAM,IAAI,GAAgB;QACxB,UAAU,EAAE,OAAO,CAAC,cAAc;QAClC,OAAO,EAAE;YACP,GAAG,OAAO,CAAC,OAAO;YAClB,iBAAiB,EAAE,IAAI;SACxB;QACD,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,eAAe,EAAE,OAAO,CAAC,eAAe;KACzC,CAAC;IAEF,oEAAoE;IACpE,KAAK,CAAC,YAAY,CAChB,WAAW,EACX,YAAY,EACZ,OAAO,CAAC,MAAM,EACd,WAAW;QACT,OAAO,WAAW,KAAK,QAAQ;QAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;QAC3B,CAAC,CAAE,WAAmC;QACtC,CAAC,CAAC,SAAS,EACb,IAAI,EACJ,OAAO,CAAC,cAAc,EACtB,OAAO,CAAC,SAAS,IAAI,IAAI,CAC1B,CAAC;AACJ,CAAC"}
@@ -11,6 +11,7 @@ import { homedir } from "node:os";
11
11
  import path from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { serve } from "@hono/node-server";
14
+ import { initTokenizer } from "../core.js";
14
15
  import { Store } from "../server/store.js";
15
16
  import { createApp, loadHtmlUI } from "../server/webui.js";
16
17
  import { ingestCapture } from "./ingest.js";
@@ -50,6 +51,10 @@ const store = new Store({
50
51
  privacy,
51
52
  });
52
53
  store.loadState();
54
+ // --- Initialize tokenizer ---
55
+ // Preload tiktoken encodings before processing captures.
56
+ // Non-blocking: falls back to estimation if loading fails.
57
+ await initTokenizer();
53
58
  // --- Capture watcher ---
54
59
  const isUtilityEndpoint = (capturePath) => /\/count_tokens\b|:countTokens\b|:loadCodeAssist\b|:retrieveUserQuota\b|:listExperiments\b|:onboardUser\b|:fetchAdminControls\b|:recordCodeAssistMetrics\b/.test(capturePath);
55
60
  const watcher = new CaptureWatcher({
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/analysis/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/analysis/server.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ export declare const CLI_CONSTANTS: {
20
20
  readonly MITM_PORT: 8080;
21
21
  readonly MITM_PROXY_URL: "http://localhost:8080";
22
22
  readonly PI_AGENT_DIR_PREFIX: "/tmp/context-lens-pi-agent-";
23
+ readonly BRYTI_DATA_DIR_PREFIX: "/tmp/context-lens-bryti-";
23
24
  readonly COMMAND_ALIASES: Record<string, string>;
24
25
  readonly KNOWN_PRIVACY_LEVELS: readonly ["minimal", "standard", "full"];
25
26
  readonly MITM_ADDON_PATH: string;
@@ -1 +1 @@
1
- {"version":3,"file":"cli-utils.d.ts","sourceRoot":"","sources":["../src/cli-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAmB7C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4DD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAY1D;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2H1D;AAED,wBAAgB,cAAc,IAAI,MAAM,CA8DvC;AAGD,eAAO,MAAM,aAAa;;;;;;;;CAShB,CAAC"}
1
+ {"version":3,"file":"cli-utils.d.ts","sourceRoot":"","sources":["../src/cli-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAqB7C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqFD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAY1D;AAED,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,aAAa,CA2H1D;AAED,wBAAgB,cAAc,IAAI,MAAM,CAmEvC;AAGD,eAAO,MAAM,aAAa;;;;;;;;;CAUhB,CAAC"}
package/dist/cli-utils.js CHANGED
@@ -8,10 +8,12 @@ const PROXY_URL = "http://localhost:4040";
8
8
  const MITM_PORT = 8080;
9
9
  const MITM_PROXY_URL = `http://localhost:${MITM_PORT}`;
10
10
  const PI_AGENT_DIR_PREFIX = "/tmp/context-lens-pi-agent-";
11
+ const BRYTI_DATA_DIR_PREFIX = "/tmp/context-lens-bryti-";
11
12
  const COMMAND_ALIASES = {
12
13
  cc: "claude",
13
14
  cx: "codex",
14
15
  gm: "gemini",
16
+ oc: "opencode",
15
17
  };
16
18
  const KNOWN_PRIVACY_LEVELS = ["minimal", "standard", "full"];
17
19
  function isPrivacyLevel(value) {
@@ -49,6 +51,19 @@ const TOOL_CONFIG = {
49
51
  serverEnv: {},
50
52
  needsMitm: false,
51
53
  },
54
+ opencode: {
55
+ // OpenCode connects directly to each provider's official API over HTTPS
56
+ // and cannot be redirected via base URL env vars alone when using multiple
57
+ // providers simultaneously. We use mitmproxy as a forward HTTPS proxy so
58
+ // all provider traffic is captured regardless of which model is active.
59
+ childEnv: {
60
+ https_proxy: MITM_PROXY_URL,
61
+ SSL_CERT_FILE: "", // filled in by cli.ts with mitmproxy CA cert path
62
+ },
63
+ extraArgs: [],
64
+ serverEnv: {},
65
+ needsMitm: true,
66
+ },
52
67
  gemini: {
53
68
  childEnv: {
54
69
  GOOGLE_GEMINI_BASE_URL: `${PROXY_URL}/gemini/`, // API-key auth path
@@ -69,6 +84,18 @@ const TOOL_CONFIG = {
69
84
  serverEnv: {},
70
85
  needsMitm: false,
71
86
  },
87
+ bryti: {
88
+ // Bryti reads base_url from its config.yml, not env vars. We point it at
89
+ // a temporary data dir where cli.ts writes a proxy-aware config.yml copy.
90
+ // Everything else in the temp dir is symlinked back to the real data dir
91
+ // so all runtime writes (history, memory DB, etc.) go to the right place.
92
+ childEnv: {
93
+ BRYTI_DATA_DIR: BRYTI_DATA_DIR_PREFIX,
94
+ },
95
+ extraArgs: [],
96
+ serverEnv: {},
97
+ needsMitm: false,
98
+ },
72
99
  };
73
100
  export function getToolConfig(toolName) {
74
101
  return (TOOL_CONFIG[toolName] || {
@@ -210,16 +237,20 @@ export function formatHelpText() {
210
237
  " context-lens [global-options] -- [command] [args...]",
211
238
  " context-lens [global-options] (no command = standalone mode)",
212
239
  " context-lens doctor",
240
+ " context-lens stop",
213
241
  " context-lens background <start|stop|status> [--no-ui]",
214
242
  " context-lens analyze <session.lhar> [options]",
215
243
  "",
216
244
  "Examples:",
217
245
  " context-lens claude",
218
246
  " context-lens codex",
247
+ " context-lens opencode",
219
248
  " context-lens gm",
249
+ " context-lens bryti",
220
250
  " context-lens --privacy=minimal aider --model claude-sonnet-4",
221
251
  " context-lens -- python my_agent.py",
222
252
  " context-lens doctor",
253
+ " context-lens stop",
223
254
  " context-lens background start --no-ui",
224
255
  " context-lens analyze ~/.context-lens/data/claude-abc123.lhar",
225
256
  " context-lens analyze session.lhar --json --main-only",
@@ -238,9 +269,10 @@ export function formatHelpText() {
238
269
  " cc -> claude",
239
270
  " cx -> codex",
240
271
  " gm -> gemini",
272
+ " oc -> opencode",
241
273
  "",
242
- "Binary aliases:",
243
- " picl Equivalent to 'context-lens pi'",
274
+ "Shell alias (add to ~/.zshrc or ~/.bashrc):",
275
+ " alias cpi='context-lens pi'",
244
276
  "",
245
277
  "Environment variables:",
246
278
  " UPSTREAM_OPENAI_URL Override OpenAI upstream (for OpenAI-compatible APIs)",
@@ -249,7 +281,7 @@ export function formatHelpText() {
249
281
  "",
250
282
  "Notes:",
251
283
  " - No command starts standalone mode (proxy + analysis/web UI by default).",
252
- " - 'codex' uses mitmproxy for HTTPS interception (requires mitmproxy; install: pipx install mitmproxy).",
284
+ " - 'codex' and 'opencode' use mitmproxy for HTTPS interception (requires mitmproxy; install: pipx install mitmproxy).",
253
285
  " - 'pi --mitm' uses mitmproxy for full interception, useful for subscription-based models (openai-codex provider).",
254
286
  " - 'doctor' is a local diagnostics command.",
255
287
  " - 'background' manages detached proxy/web-ui processes.",
@@ -270,6 +302,7 @@ export const CLI_CONSTANTS = {
270
302
  MITM_PORT,
271
303
  MITM_PROXY_URL,
272
304
  PI_AGENT_DIR_PREFIX,
305
+ BRYTI_DATA_DIR_PREFIX,
273
306
  COMMAND_ALIASES,
274
307
  KNOWN_PRIVACY_LEVELS,
275
308
  // Resolved relative to compiled output (dist/ or dist-test/), matching cli.ts behavior.
@@ -1 +1 @@
1
- {"version":3,"file":"cli-utils.js","sourceRoot":"","sources":["../src/cli-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli-utils.js","sourceRoot":"","sources":["../src/cli-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,sHAAsH;AACtH,MAAM,SAAS,GAAG,uBAAuB,CAAC;AAC1C,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,cAAc,GAAG,oBAAoB,SAAS,EAAE,CAAC;AACvD,MAAM,mBAAmB,GAAG,6BAA6B,CAAC;AAC1D,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AACzD,MAAM,eAAe,GAA2B;IAC9C,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,OAAO;IACX,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,UAAU;CACf,CAAC;AACF,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAU,CAAC;AAgBtE,SAAS,cAAc,CAAC,KAAa;IACnC,OAAQ,oBAA0C,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,WAAW,GAA+B;IAC9C,MAAM,EAAE;QACN,QAAQ,EAAE,EAAE,kBAAkB,EAAE,GAAG,SAAS,SAAS,EAAE;QACvD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACL,6DAA6D;QAC7D,uEAAuE;QACvE,wEAAwE;QACxE,EAAE;QACF,kEAAkE;QAClE,8DAA8D;QAC9D,gCAAgC;QAChC,QAAQ,EAAE;YACR,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,EAAE,EAAE,kDAAkD;SACtE;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,IAAI;KAChB;IACD,KAAK,EAAE;QACL,QAAQ,EAAE;YACR,kBAAkB,EAAE,GAAG,SAAS,QAAQ;YACxC,eAAe,EAAE,GAAG,SAAS,QAAQ;SACtC;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB;IACD,QAAQ,EAAE;QACR,wEAAwE;QACxE,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,QAAQ,EAAE;YACR,WAAW,EAAE,cAAc;YAC3B,aAAa,EAAE,EAAE,EAAE,kDAAkD;SACtE;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,IAAI;KAChB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE;YACR,sBAAsB,EAAE,GAAG,SAAS,UAAU,EAAE,oBAAoB;YACpE,sBAAsB,EAAE,GAAG,SAAS,UAAU,EAAE,sBAAsB;YACtE,oBAAoB,EAAE,GAAG,SAAS,SAAS,EAAE,0BAA0B;SACxE;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB;IACD,EAAE,EAAE;QACF,0EAA0E;QAC1E,2DAA2D;QAC3D,QAAQ,EAAE;YACR,mBAAmB,EAAE,mBAAmB;SACzC;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB;IACD,KAAK,EAAE;QACL,yEAAyE;QACzE,0EAA0E;QAC1E,yEAAyE;QACzE,0EAA0E;QAC1E,QAAQ,EAAE;YACR,cAAc,EAAE,qBAAqB;SACtC;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,CACL,WAAW,CAAC,QAAQ,CAAC,IAAI;QACvB,QAAQ,EAAE;YACR,kBAAkB,EAAE,GAAG,SAAS,IAAI,QAAQ,EAAE;YAC9C,eAAe,EAAE,GAAG,SAAS,IAAI,QAAQ,EAAE;SAC5C;QACD,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,KAAK;KACjB,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,WAAmB;IACrD,OAAO,eAAe,CAAC,WAAW,CAAC,IAAI,WAAW,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,YAAgC,CAAC;IACrC,IAAI,iBAAiB,GAAG,KAAK,CAAC;IAC9B,IAAI,iBAAiB,GAAG,CAAC,CAAC,CAAC;IAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,iBAAiB,GAAG,IAAI,CAAC;YACzB,iBAAiB,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,MAAM;QACR,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,iBAAiB,GAAG,CAAC,CAAC;YACtB,MAAM;QACR,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACrC,QAAQ,GAAG,IAAI,CAAC;YAChB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACxC,WAAW,GAAG,IAAI,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACxB,MAAM,GAAG,IAAI,CAAC;YACd,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,IAAI,GAAG,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,mBAAmB,EAAE,CAAC;YAChC,aAAa,GAAG,IAAI,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACrB,OAAO,GAAG,IAAI,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBACzB,OAAO;oBACL,QAAQ;oBACR,WAAW;oBACX,MAAM;oBACN,IAAI;oBACJ,aAAa;oBACb,OAAO;oBACP,gBAAgB,EAAE,EAAE;oBACpB,KAAK,EACH,8EAA8E;iBACjF,CAAC;YACJ,CAAC;YACD,YAAY,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACjC,YAAY,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpC,SAAS;QACX,CAAC;QACD,OAAO;YACL,QAAQ;YACR,WAAW;YACX,MAAM;YACN,IAAI;YACJ,aAAa;YACb,OAAO;YACP,gBAAgB,EAAE,EAAE;YACpB,KAAK,EAAE,0BAA0B,GAAG,yCAAyC;SAC9E,CAAC;IACJ,CAAC;IAED,IAAI,YAAY,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC;QAClD,OAAO;YACL,QAAQ;YACR,WAAW;YACX,MAAM;YACN,IAAI;YACJ,aAAa;YACb,OAAO;YACP,gBAAgB,EAAE,EAAE;YACpB,KAAK,EAAE,iCAAiC,YAAY,sBAAsB,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;SAC5G,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GACd,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7E,MAAM,gBAAgB,GACpB,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAElE,IAAI,iBAAiB,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,EAAE,CAAC;QAClE,OAAO;YACL,QAAQ;YACR,WAAW;YACX,MAAM;YACN,IAAI;YACJ,aAAa;YACb,OAAO;YACP,YAAY;YACZ,gBAAgB,EAAE,EAAE;YACpB,KAAK,EAAE,sCAAsC;SAC9C,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,WAAW;QACX,MAAM;QACN,IAAI;QACJ,aAAa;QACb,OAAO;QACP,YAAY;QACZ,WAAW;QACX,gBAAgB;KACjB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO;QACL,iBAAiB,OAAO,EAAE;QAC1B,EAAE;QACF,QAAQ;QACR,6DAA6D;QAC7D,wDAAwD;QACxD,kEAAkE;QAClE,uBAAuB;QACvB,qBAAqB;QACrB,yDAAyD;QACzD,iDAAiD;QACjD,EAAE;QACF,WAAW;QACX,uBAAuB;QACvB,sBAAsB;QACtB,yBAAyB;QACzB,mBAAmB;QACnB,sBAAsB;QACtB,gEAAgE;QAChE,sCAAsC;QACtC,uBAAuB;QACvB,qBAAqB;QACrB,yCAAyC;QACzC,gEAAgE;QAChE,wDAAwD;QACxD,kEAAkE;QAClE,EAAE;QACF,iBAAiB;QACjB,8CAA8C;QAC9C,uCAAuC;QACvC,mEAAmE;QACnE,gEAAgE;QAChE,qEAAqE;QACrE,6DAA6D;QAC7D,gGAAgG;QAChG,EAAE;QACF,kBAAkB;QAClB,gBAAgB;QAChB,eAAe;QACf,gBAAgB;QAChB,kBAAkB;QAClB,EAAE;QACF,6CAA6C;QAC7C,+BAA+B;QAC/B,EAAE;QACF,wBAAwB;QACxB,oFAAoF;QACpF,0DAA0D;QAC1D,uDAAuD;QACvD,EAAE;QACF,QAAQ;QACR,6EAA6E;QAC7E,wHAAwH;QACxH,qHAAqH;QACrH,8CAA8C;QAC9C,2DAA2D;QAC3D,kEAAkE;QAClE,EAAE;QACF,kBAAkB;QAClB,sEAAsE;QACtE,uDAAuD;QACvD,6DAA6D;QAC7D,qEAAqE;QACrE,oEAAoE;QACpE,+DAA+D;KAChE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED,mDAAmD;AACnD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS;IACT,SAAS;IACT,cAAc;IACd,mBAAmB;IACnB,qBAAqB;IACrB,eAAe;IACf,oBAAoB;IACpB,wFAAwF;IACxF,eAAe,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,eAAe,CAAC;CAC/C,CAAC"}
package/dist/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
+ import { randomBytes } from "node:crypto";
3
4
  import fs from "node:fs";
4
5
  import http from "node:http";
5
6
  import https from "node:https";
@@ -14,13 +15,8 @@ const __dirname = dirname(__filename);
14
15
  // Known tool config: env vars for the child process, extra CLI args, server env vars, and whether mitmproxy is needed
15
16
  // Note: actual tool config lives in cli-utils.ts so it can be unit-tested without importing this entrypoint.
16
17
  const LOCKFILE = "/tmp/context-lens.lock";
17
- // When invoked as `picl`, default to the `pi` tool if no command is given.
18
- const binaryName = process.argv[1] ? process.argv[1].split("/").pop() : "";
19
18
  const rawArgs = process.argv.slice(2);
20
- const isPiclInvocation = binaryName === "picl" &&
21
- !rawArgs.some((a) => !a.startsWith("-")) &&
22
- !rawArgs.includes("--");
23
- const parsedArgs = parseCliArgs(isPiclInvocation ? ["pi", ...rawArgs] : rawArgs);
19
+ const parsedArgs = parseCliArgs(rawArgs);
24
20
  if (parsedArgs.error) {
25
21
  console.error(parsedArgs.error);
26
22
  process.exit(1);
@@ -46,6 +42,9 @@ if (parsedArgs.commandName === "analyze") {
46
42
  else if (parsedArgs.commandName === "doctor") {
47
43
  void runDoctor().then((exitCode) => process.exit(exitCode));
48
44
  }
45
+ else if (parsedArgs.commandName === "stop") {
46
+ process.exit(backgroundStop());
47
+ }
49
48
  else if (parsedArgs.commandName === "background") {
50
49
  void runBackgroundCommand(parsedArgs.commandArguments, parsedArgs.noUi).then((exitCode) => process.exit(exitCode));
51
50
  }
@@ -184,6 +183,7 @@ else {
184
183
  let mitmReady = false;
185
184
  let childProcess = null;
186
185
  let piAgentDirToCleanup = null;
186
+ let brytiDataDirToCleanup = null;
187
187
  let shouldShutdownServers = false;
188
188
  let cleanupDidRun = false;
189
189
  const requiresAnalysis = !noUi;
@@ -337,6 +337,11 @@ else {
337
337
  String(CLI_CONSTANTS.MITM_PORT),
338
338
  ], {
339
339
  stdio: ["ignore", "pipe", "pipe"],
340
+ env: {
341
+ ...process.env,
342
+ CONTEXT_LENS_SOURCE: commandName,
343
+ CONTEXT_LENS_SESSION_ID: randomBytes(4).toString("hex"),
344
+ },
340
345
  });
341
346
  mitmProcess.on("error", (err) => {
342
347
  console.error("Failed to start mitmproxy:", err.message);
@@ -373,6 +378,43 @@ else {
373
378
  ...process.env,
374
379
  ...toolConfig.childEnv,
375
380
  };
381
+ // Embed a per-invocation session ID into proxy base URLs so that separate
382
+ // CLI runs are always grouped into distinct conversations, even when they
383
+ // start with identical prompts. The session ID is injected as a path
384
+ // segment after the source tag, which extractSource() picks up as a
385
+ // stable conversation key for the lifetime of this process.
386
+ //
387
+ // Format: http://localhost:4040/<source>/<session-id>/
388
+ // Example: http://localhost:4040/gemini/a1b2c3d4/
389
+ //
390
+ // Codex uses mitmproxy and has its own chaining via previous_response_id.
391
+ // Claude Code and Pi embed their own session IDs in request metadata.
392
+ // Tools without built-in session IDs (Gemini, Aider, custom) rely on this.
393
+ if (!toolConfig.needsMitm) {
394
+ const sessionTag = randomBytes(4).toString("hex"); // 8 hex chars
395
+ // For bryti, the proxy URL is baked into config.yml (not an env var),
396
+ // so the session tag loop below won't reach it. Pass it to prepareBrytiDataDir
397
+ // so it can embed the tag directly into the patched config.yml base_url.
398
+ if (commandName === "bryti") {
399
+ childEnv.BRYTI_DATA_DIR = prepareBrytiDataDir(childEnv.BRYTI_DATA_DIR, sessionTag);
400
+ }
401
+ for (const key of Object.keys(childEnv)) {
402
+ const val = childEnv[key];
403
+ if (typeof val !== "string")
404
+ continue;
405
+ // Match any value that points at our proxy and ends with /<source> or /<source>/
406
+ const proxyBase = `http://localhost:4040/`;
407
+ if (!val.startsWith(proxyBase))
408
+ continue;
409
+ const hadTrailingSlash = val.endsWith("/");
410
+ const after = val.slice(proxyBase.length).replace(/\/$/, "");
411
+ // Only inject if the remaining path is just the source tag (no session already)
412
+ if (after && !after.includes("/")) {
413
+ const suffix = hadTrailingSlash ? "/" : "";
414
+ childEnv[key] = `${proxyBase}${after}/${sessionTag}${suffix}`;
415
+ }
416
+ }
417
+ }
376
418
  // Fill in mitmproxy CA cert path for tools that need HTTPS interception
377
419
  if (toolConfig.needsMitm && childEnv.SSL_CERT_FILE === "") {
378
420
  const certPath = join(homedir(), ".mitmproxy", "mitmproxy-ca-cert.pem");
@@ -386,9 +428,18 @@ else {
386
428
  if (commandName === "pi" && !useMitm) {
387
429
  childEnv.PI_CODING_AGENT_DIR = preparePiAgentDir(childEnv.PI_CODING_AGENT_DIR);
388
430
  }
431
+ // For bryti: if dist/cli.js exists in cwd, use it directly (dev mode).
432
+ // Otherwise fall back to the globally installed bryti binary.
433
+ let spawnCommand = commandName;
434
+ let spawnArgs = allArgs;
435
+ if (commandName === "bryti" &&
436
+ fs.existsSync(resolve(process.cwd(), "dist", "cli.js"))) {
437
+ spawnCommand = process.execPath; // node
438
+ spawnArgs = [resolve(process.cwd(), "dist", "cli.js"), ...allArgs];
439
+ }
389
440
  // Spawn the child process with inherited stdio (interactive)
390
441
  // No shell: true. Avoids intermediate process that breaks signal delivery
391
- childProcess = spawn(commandName, allArgs, {
442
+ childProcess = spawn(spawnCommand, spawnArgs, {
392
443
  stdio: "inherit",
393
444
  env: childEnv,
394
445
  });
@@ -565,6 +616,133 @@ else {
565
616
  return targetDir;
566
617
  }
567
618
  }
619
+ /**
620
+ * Create a temporary Bryti data directory with a proxy-aware config.yml.
621
+ *
622
+ * Bryti reads base_url for each model provider from its config.yml and does
623
+ * not respect environment variable overrides. We copy the real config (from
624
+ * the original BRYTI_DATA_DIR or ./data) into a temp dir, rewriting every
625
+ * provider's base_url to point at the context-lens proxy, then set
626
+ * BRYTI_DATA_DIR to the temp dir so Bryti picks up the patched config.
627
+ *
628
+ * The original config is never modified. The temp dir is cleaned up on exit.
629
+ *
630
+ * State dirs (users, history, logs, etc.) are symlinked into the temp dir so
631
+ * bryti's runtime writes go back to the real data dir.
632
+ *
633
+ * The .pi dir is NOT symlinked: bryti regenerates .pi/settings.json on every
634
+ * startup (via ensureDataDirs/writeExtensionSettings), which would append the
635
+ * temp extensions path to the real settings.json and cause duplicate tool
636
+ * registrations on the next run. Instead we copy .pi into the temp dir so
637
+ * bryti reads existing auth/models from there but writes only to temp.
638
+ *
639
+ * files/ is NOT symlinked for the same reason: bryti's writeExtensionSettings
640
+ * would register both the real and temp extensions paths, loading each
641
+ * extension twice and causing tool conflict errors.
642
+ *
643
+ * If no config.yml is found in the source data dir, we warn and fall back to
644
+ * the temp dir without a config patch (bryti will error on its own).
645
+ */
646
+ function prepareBrytiDataDir(targetDirEnv, sessionTag) {
647
+ const dirPrefix = targetDirEnv && targetDirEnv.length > 0
648
+ ? targetDirEnv
649
+ : join(tmpdir(), "context-lens-bryti-");
650
+ const targetDir = fs.mkdtempSync(dirPrefix);
651
+ brytiDataDirToCleanup = targetDir;
652
+ // Find the real bryti data dir: check BRYTI_DATA_DIR in current env (before
653
+ // our override) or fall back to ./data relative to cwd.
654
+ const realDataDir = resolve(process.env.BRYTI_DATA_DIR || join(process.cwd(), "data"));
655
+ const sourceConfigPath = join(realDataDir, "config.yml");
656
+ try {
657
+ if (!fs.existsSync(sourceConfigPath)) {
658
+ console.error(`Warning: no Bryti config.yml found at ${sourceConfigPath}. ` +
659
+ "Bryti will start without a proxy-patched config.");
660
+ return targetDir;
661
+ }
662
+ const raw = fs.readFileSync(sourceConfigPath, "utf-8");
663
+ // Patch every `base_url:` value in the YAML that looks like an HTTP(S)
664
+ // URL (or is empty, meaning default Anthropic). We replace all provider
665
+ // base URLs with the proxy URL so all traffic is captured.
666
+ //
667
+ // We do a targeted line-level rewrite rather than full YAML parse+emit to
668
+ // avoid disturbing formatting, comments, or env-var substitution markers
669
+ // (${VAR}) that would fail a raw parse before substitution.
670
+ // Include the session tag in the proxy URL so all requests from this
671
+ // bryti invocation share a stable conversation ID in context-lens.
672
+ const proxyBase = `${CLI_CONSTANTS.PROXY_URL}/bryti/${sessionTag}`;
673
+ const patched = raw
674
+ .split("\n")
675
+ .map((line) => {
676
+ // Match lines like: base_url: "..." or base_url: '' or base_url:
677
+ // Only rewrite lines that are clearly provider base_url fields.
678
+ const m = line.match(/^(\s*base_url:\s*)(.*)$/);
679
+ if (!m)
680
+ return line;
681
+ // Preserve the indent + key, replace the value
682
+ return `${m[1]}"${proxyBase}"`;
683
+ })
684
+ .join("\n");
685
+ const targetConfigPath = join(targetDir, "config.yml");
686
+ fs.writeFileSync(targetConfigPath, patched, "utf-8");
687
+ // Symlink state dirs that should persist back to the real data dir.
688
+ // Exclude config.yml (patched above), .pi (copied below), and files/
689
+ // (both contain paths that bryti writes into settings.json on startup,
690
+ // which would corrupt the real settings with temp dir paths).
691
+ const SYMLINK_ENTRIES = [
692
+ "users",
693
+ "history",
694
+ "logs",
695
+ "pending",
696
+ "skills",
697
+ "usage",
698
+ "whatsapp-auth",
699
+ "core-memory.md",
700
+ "sessions",
701
+ "extensions",
702
+ ];
703
+ for (const name of SYMLINK_ENTRIES) {
704
+ const src = join(realDataDir, name);
705
+ if (!fs.existsSync(src))
706
+ continue;
707
+ const dst = join(targetDir, name);
708
+ try {
709
+ fs.symlinkSync(src, dst);
710
+ }
711
+ catch {
712
+ // Non-fatal
713
+ }
714
+ }
715
+ // Copy .pi into the temp dir so bryti reads existing auth/models.json
716
+ // but all writes (including settings.json rewrites) stay in temp.
717
+ const realPiDir = join(realDataDir, ".pi");
718
+ const tempPiDir = join(targetDir, ".pi");
719
+ if (fs.existsSync(realPiDir)) {
720
+ fs.mkdirSync(tempPiDir, { recursive: true });
721
+ for (const entry of fs.readdirSync(realPiDir, {
722
+ withFileTypes: true,
723
+ })) {
724
+ const src = join(realPiDir, entry.name);
725
+ const dst = join(tempPiDir, entry.name);
726
+ try {
727
+ if (entry.isDirectory()) {
728
+ fs.symlinkSync(src, dst);
729
+ }
730
+ else {
731
+ fs.copyFileSync(src, dst);
732
+ }
733
+ }
734
+ catch {
735
+ // Non-fatal
736
+ }
737
+ }
738
+ }
739
+ return targetDir;
740
+ }
741
+ catch (err) {
742
+ console.error("Warning: failed to prepare Bryti proxy config:", err instanceof Error ? err.message : String(err));
743
+ return targetDir;
744
+ }
745
+ }
568
746
  // Open browser (cross-platform)
569
747
  function openBrowser(url) {
570
748
  const cmd = platform() === "darwin"
@@ -595,6 +773,14 @@ else {
595
773
  console.error("Warning: failed to clean up temporary Pi config dir:", err instanceof Error ? err.message : String(err));
596
774
  }
597
775
  }
776
+ if (brytiDataDirToCleanup) {
777
+ try {
778
+ fs.rmSync(brytiDataDirToCleanup, { recursive: true, force: true });
779
+ }
780
+ catch (err) {
781
+ console.error("Warning: failed to clean up temporary Bryti data dir:", err instanceof Error ? err.message : String(err));
782
+ }
783
+ }
598
784
  if (remainingRefs === 0 && shouldShutdownServers) {
599
785
  if (proxyProcess && !proxyProcess.killed)
600
786
  proxyProcess.kill();