context-lens 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -20
- package/dist/analysis/server.js +2 -2
- package/dist/analysis/server.js.map +1 -1
- package/dist/cli-utils.d.ts +5 -0
- package/dist/cli-utils.d.ts.map +1 -1
- package/dist/cli-utils.js +57 -1
- package/dist/cli-utils.js.map +1 -1
- package/dist/cli.js +85 -11
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +35 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +113 -0
- package/dist/config.js.map +1 -0
- package/dist/core/models.d.ts +6 -38
- package/dist/core/models.d.ts.map +1 -1
- package/dist/core/models.js +6 -213
- package/dist/core/models.js.map +1 -1
- package/dist/core/routing.d.ts.map +1 -1
- package/dist/core/routing.js +7 -1
- package/dist/core/routing.js.map +1 -1
- package/dist/core/security.d.ts.map +1 -1
- package/dist/core/security.js +23 -0
- package/dist/core/security.js.map +1 -1
- package/dist/core/waste.d.ts +73 -0
- package/dist/core/waste.d.ts.map +1 -0
- package/dist/core/waste.js +283 -0
- package/dist/core/waste.js.map +1 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +1 -0
- package/dist/core.js.map +1 -1
- package/dist/http/headers.d.ts +9 -17
- package/dist/http/headers.d.ts.map +1 -1
- package/dist/http/headers.js +10 -38
- package/dist/http/headers.js.map +1 -1
- package/dist/proxy/config.d.ts.map +1 -1
- package/dist/proxy/config.js +13 -0
- package/dist/proxy/config.js.map +1 -1
- package/dist/proxy/server.js +22 -0
- package/dist/proxy/server.js.map +1 -1
- package/dist/schemas.d.ts +14 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +8 -0
- package/dist/schemas.js.map +1 -1
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js.map +1 -1
- package/dist/server/projection.d.ts.map +1 -1
- package/dist/server/projection.js +1 -0
- package/dist/server/projection.js.map +1 -1
- package/dist/server/store.d.ts.map +1 -1
- package/dist/server/store.js +100 -10
- package/dist/server/store.js.map +1 -1
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.generated.d.ts +1 -1
- package/dist/version.generated.js +1 -1
- package/mitm_addon.py +3 -0
- package/package.json +7 -4
- package/ui/dist/assets/index-B1EiZYbW.css +1 -0
- package/ui/dist/assets/index-DDiXESm5.js +52 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-DYoafQtJ.css +0 -1
- package/ui/dist/assets/index-t5l_d6Oq.js +0 -52
package/README.md
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
See what's actually filling your context window. Context Lens is a local proxy that captures LLM API calls from your coding tools and shows you a composition breakdown: what percentage is system prompts, tool definitions, conversation history, tool results, thinking blocks. It answers the question every developer asks: "why is this session so expensive?"
|
|
8
8
|
|
|
9
|
-
Works with Claude Code, Codex, Gemini CLI, Aider, Pi, and anything else that talks to OpenAI/Anthropic/Google APIs. No code changes needed.
|
|
9
|
+
Works with Claude Code, Codex, Gemini CLI, Cline, 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
|
|
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
12
|
|
|
13
13
|

|
|
14
14
|
|
|
@@ -26,6 +26,7 @@ npm install -g context-lens
|
|
|
26
26
|
context-lens claude
|
|
27
27
|
context-lens codex
|
|
28
28
|
context-lens gemini
|
|
29
|
+
context-lens cline
|
|
29
30
|
context-lens aider --model claude-sonnet-4
|
|
30
31
|
context-lens pi
|
|
31
32
|
context-lens -- python my_agent.py
|
|
@@ -39,7 +40,10 @@ This starts the proxy (port 4040), opens the web UI (http://localhost:4041), set
|
|
|
39
40
|
context-lens --privacy=minimal claude # minimal|standard|full
|
|
40
41
|
context-lens --no-open codex # don't auto-open the UI
|
|
41
42
|
context-lens --no-ui -- claude # proxy only, no UI
|
|
42
|
-
context-lens
|
|
43
|
+
context-lens --redact claude # strip secrets before capture
|
|
44
|
+
context-lens --redact=pii claude # broader PII redaction
|
|
45
|
+
context-lens --redact --rehydrate claude # restore original values in responses
|
|
46
|
+
context-lens doctor # check ports, certs, config, background state
|
|
43
47
|
context-lens background start # start detached proxy + UI
|
|
44
48
|
context-lens background status
|
|
45
49
|
context-lens background stop
|
|
@@ -48,6 +52,29 @@ context-lens stop # shorthand for background stop
|
|
|
48
52
|
|
|
49
53
|
Aliases: `cc` → `claude`, `cx` → `codex`, `gm` → `gemini`. For `pi`, add `alias cpi='context-lens pi'` to your shell rc.
|
|
50
54
|
|
|
55
|
+
## Configuration
|
|
56
|
+
|
|
57
|
+
Persistent settings live in `~/.context-lens/config.toml`. CLI flags always override config file values. The file is not created automatically; create it if you want persistent defaults.
|
|
58
|
+
|
|
59
|
+
```toml
|
|
60
|
+
# Context Lens configuration
|
|
61
|
+
# ~/.context-lens/config.toml
|
|
62
|
+
|
|
63
|
+
[proxy]
|
|
64
|
+
# port = 4040
|
|
65
|
+
# redact = "secrets" # secrets | pii | strict
|
|
66
|
+
# rehydrate = false
|
|
67
|
+
|
|
68
|
+
[ui]
|
|
69
|
+
# port = 4041
|
|
70
|
+
# no_open = false
|
|
71
|
+
|
|
72
|
+
[privacy]
|
|
73
|
+
# level = "standard" # minimal | standard | full
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Run `context-lens doctor` to see the active config path and current values.
|
|
77
|
+
|
|
51
78
|
## Docker
|
|
52
79
|
|
|
53
80
|
A pre-built image is published to GitHub Container Registry on every release:
|
|
@@ -74,14 +101,38 @@ ANTHROPIC_BASE_URL=http://localhost:4040/claude claude
|
|
|
74
101
|
OPENAI_BASE_URL=http://localhost:4040 codex
|
|
75
102
|
```
|
|
76
103
|
|
|
104
|
+
### OpenAI-compatible providers
|
|
105
|
+
|
|
106
|
+
If your tool talks to an OpenAI-compatible endpoint (Ollama, OpenRouter, Together, vLLM, etc.), set `UPSTREAM_OPENAI_URL` so the proxy knows where to forward:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
docker run -d \
|
|
110
|
+
-p 4040:4040 -p 4041:4041 \
|
|
111
|
+
-e CONTEXT_LENS_BIND_HOST=0.0.0.0 \
|
|
112
|
+
-e UPSTREAM_OPENAI_URL=https://openrouter.ai/api/v1 \
|
|
113
|
+
-v ~/.context-lens:/root/.context-lens \
|
|
114
|
+
ghcr.io/larsderidder/context-lens:latest
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Then point your tool at the proxy (e.g. `"baseURL": "http://localhost:4040/opencode"`).
|
|
118
|
+
|
|
119
|
+
For services running on the Docker host (like Ollama), use `host.docker.internal` as the hostname:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
UPSTREAM_OPENAI_URL=http://host.docker.internal:11434/v1
|
|
123
|
+
```
|
|
124
|
+
|
|
77
125
|
### Environment variables
|
|
78
126
|
|
|
79
127
|
| Variable | Default | Description |
|
|
80
128
|
| :--- | :--- | :--- |
|
|
81
129
|
| `CONTEXT_LENS_BIND_HOST` | `127.0.0.1` | Set to `0.0.0.0` to accept connections from outside the container |
|
|
130
|
+
| `UPSTREAM_OPENAI_URL` | _(auto-detect)_ | Forward OpenAI-format requests to this URL (for Ollama, vLLM, OpenRouter, etc.) |
|
|
82
131
|
| `CONTEXT_LENS_INGEST_URL` | _(file-based)_ | POST captures to a remote URL instead of writing to disk |
|
|
83
132
|
| `CONTEXT_LENS_PRIVACY` | `standard` | Privacy level: `minimal`, `standard`, or `full` |
|
|
84
133
|
| `CONTEXT_LENS_NO_UPDATE_CHECK` | `0` | Set to `1` to skip the npm update check |
|
|
134
|
+
| `CONTEXT_LENS_MAX_SESSIONS` | `200` | Maximum number of conversations to keep in memory |
|
|
135
|
+
| `CONTEXT_LENS_MAX_COMPACT_MESSAGES` | `60` | Maximum messages per entry when compacting for storage |
|
|
85
136
|
|
|
86
137
|
### Split-container setup
|
|
87
138
|
|
|
@@ -117,24 +168,25 @@ services:
|
|
|
117
168
|
| **OpenAI** | Reverse Proxy | ✅ Stable | `OPENAI_BASE_URL` |
|
|
118
169
|
| **Google Gemini** | Reverse Proxy | 🧪 Experimental | `GOOGLE_GEMINI_BASE_URL` |
|
|
119
170
|
| **ChatGPT (Subscription)** | MITM Proxy | ✅ Stable | `https_proxy` |
|
|
171
|
+
| **Cline** | MITM Proxy | ✅ Stable | `https_proxy` + `NODE_EXTRA_CA_CERTS` |
|
|
120
172
|
| **Pi Coding Agent** | Reverse Proxy (temporary per-run config) | ✅ Stable | `PI_CODING_AGENT_DIR` (set by wrapper) |
|
|
121
173
|
| **OpenAI-Compatible** | Reverse Proxy | ✅ Stable | `UPSTREAM_OPENAI_URL` + `OPENAI_BASE_URL` |
|
|
122
174
|
| **Aider / Generic** | Reverse Proxy | ✅ Stable | Detects standard patterns |
|
|
123
175
|
|
|
124
176
|
## What You Get
|
|
125
177
|
|
|
126
|
-
-
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
178
|
+
- Composition treemap: visual breakdown of what's filling your context (system prompts, tool definitions, tool results, messages, thinking, images)
|
|
179
|
+
- Cost tracking: per-turn and per-session cost estimates across models
|
|
180
|
+
- Conversation threading: groups API calls by session, shows main agent vs subagent turns
|
|
181
|
+
- Agent breakdown: token usage and cost per agent within a session
|
|
182
|
+
- Timeline: bar chart of context size over time, filterable by main/all/cost
|
|
183
|
+
- Context diff: turn-to-turn delta showing what grew, shrank, or appeared
|
|
184
|
+
- Findings: flags large tool results, unused tool definitions, context overflow risk, compaction events
|
|
185
|
+
- Auto-detection: recognizes Claude Code, Codex, aider, Pi, and others by source tag or system prompt
|
|
186
|
+
- Session tagging: label sessions with custom tags, filter the session list by tag
|
|
187
|
+
- LHAR export: download session data as LHAR (LLM HTTP Archive) format ([doc](docs/LHAR.md))
|
|
188
|
+
- State persistence: data survives restarts; delete individual sessions or reset all from the UI
|
|
189
|
+
- Streaming support: passes through SSE chunks in real-time
|
|
138
190
|
|
|
139
191
|
### Screenshots
|
|
140
192
|
|
|
@@ -179,9 +231,38 @@ If you prefer to configure it manually, set `baseUrl` in `~/.pi/agent/models.jso
|
|
|
179
231
|
}
|
|
180
232
|
```
|
|
181
233
|
|
|
234
|
+
### Redaction
|
|
235
|
+
|
|
236
|
+
The `--redact` flag strips sensitive values from requests before they are written to disk, useful when sharing captures or exporting LHAR files.
|
|
237
|
+
|
|
238
|
+
| Preset | What it removes |
|
|
239
|
+
| :--- | :--- |
|
|
240
|
+
| `secrets` (default) | API keys, tokens, passwords, bearer credentials |
|
|
241
|
+
| `pii` | Secrets plus names, email addresses, phone numbers, IP addresses |
|
|
242
|
+
| `strict` | PII plus any value that looks like it could identify a person or system |
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
context-lens --redact claude # secrets preset (default)
|
|
246
|
+
context-lens --redact=pii claude # broader PII removal
|
|
247
|
+
context-lens --redact=strict claude # maximum removal
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Redaction is **one-way by default**: redacted values are permanently removed from captures. To enable reversible redaction (original values stored in memory and restored in responses), add `--rehydrate`:
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
context-lens --redact --rehydrate claude
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
To always redact, set it in `~/.context-lens/config.toml`:
|
|
257
|
+
|
|
258
|
+
```toml
|
|
259
|
+
[proxy]
|
|
260
|
+
redact = "secrets"
|
|
261
|
+
```
|
|
262
|
+
|
|
182
263
|
### OpenCode
|
|
183
264
|
|
|
184
|
-
OpenCode connects to multiple providers simultaneously over HTTPS. Use `context-lens opencode
|
|
265
|
+
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
266
|
|
|
186
267
|
```bash
|
|
187
268
|
pipx install mitmproxy
|
|
@@ -194,6 +275,17 @@ If you only use OpenCode with a single OpenAI-compatible endpoint (e.g. OpenCode
|
|
|
194
275
|
UPSTREAM_OPENAI_URL=https://opencode.ai/zen/v1 context-lens -- opencode "prompt"
|
|
195
276
|
```
|
|
196
277
|
|
|
278
|
+
### Cline
|
|
279
|
+
|
|
280
|
+
Cline with Anthropic OAuth routes through `api.cline.bot` rather than `api.anthropic.com`, so `ANTHROPIC_BASE_URL` has no effect. Use mitmproxy to intercept the traffic:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
pipx install mitmproxy
|
|
284
|
+
context-lens cline
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Cline is a Node.js process, so it uses `NODE_EXTRA_CA_CERTS` (not `SSL_CERT_FILE`) to trust the mitmproxy CA certificate; the CLI handles this automatically.
|
|
288
|
+
|
|
197
289
|
### OpenAI-Compatible Endpoints
|
|
198
290
|
|
|
199
291
|
Many providers expose OpenAI-compatible APIs (OpenRouter, Together, Groq, Fireworks, Ollama, vLLM, etc.). Override the upstream URL to point at your provider:
|
|
@@ -206,7 +298,7 @@ UPSTREAM_OPENAI_URL=https://my-provider.com/v1 context-lens -- my-tool "prompt"
|
|
|
206
298
|
|
|
207
299
|
### Codex Subscription Mode
|
|
208
300
|
|
|
209
|
-
Codex with a ChatGPT subscription needs mitmproxy for HTTPS interception (Cloudflare blocks reverse proxies)
|
|
301
|
+
Codex with a ChatGPT subscription needs mitmproxy for HTTPS interception (Cloudflare blocks reverse proxies); the CLI handles this automatically. Just make sure `mitmdump` is installed:
|
|
210
302
|
|
|
211
303
|
```bash
|
|
212
304
|
pipx install mitmproxy
|
|
@@ -248,11 +340,11 @@ The CLI sets env vars like `ANTHROPIC_BASE_URL=http://localhost:4040` so the too
|
|
|
248
340
|
|
|
249
341
|
Tools like [Langfuse](https://langfuse.com/) and [Braintrust](https://braintrust.dev/) are great for observability when you control the code: you add their SDK, instrument your calls, and get traces in a dashboard. Context Lens solves a different problem.
|
|
250
342
|
|
|
251
|
-
|
|
343
|
+
Claude Code, Codex, Gemini CLI, and Aider are closed-source binaries; you can't add an SDK to them. Context Lens works as a transparent proxy, capturing everything without touching the tool's code.
|
|
252
344
|
|
|
253
|
-
|
|
345
|
+
Most observability tools show input/output token totals. Context Lens breaks down *what's inside* the context window: how much is system prompts, tool definitions, conversation history, tool results, thinking blocks. That breakdown is what you need to understand why sessions get expensive.
|
|
254
346
|
|
|
255
|
-
|
|
347
|
+
Everything runs on your machine. No accounts, no cloud, no data leaving your network.
|
|
256
348
|
|
|
257
349
|
| | Context Lens | Langfuse / Braintrust |
|
|
258
350
|
|:---|:---|:---|
|
package/dist/analysis/server.js
CHANGED
|
@@ -40,8 +40,8 @@ function resolveDataDir() {
|
|
|
40
40
|
return path.join(homedir(), ".context-lens", "data");
|
|
41
41
|
}
|
|
42
42
|
const dataDir = resolveDataDir();
|
|
43
|
-
const maxSessions = 200;
|
|
44
|
-
const maxCompactMessages = 60;
|
|
43
|
+
const maxSessions = parseInt(process.env.CONTEXT_LENS_MAX_SESSIONS || "200", 10);
|
|
44
|
+
const maxCompactMessages = parseInt(process.env.CONTEXT_LENS_MAX_COMPACT_MESSAGES || "60", 10);
|
|
45
45
|
// --- Setup ---
|
|
46
46
|
const store = new Store({
|
|
47
47
|
dataDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/analysis/server.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,iBAAiB;AAEjB,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,WAAW,CAAC;AACnE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,0BAA0B,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;AAE5E,MAAM,UAAU,GACd,OAAO,CAAC,GAAG,CAAC,wBAAwB;IACpC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,UAAU,CAAC,CAAC;AAEpD,MAAM,UAAU,GAAG,CACjB,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,UAAU,CAC/C,CAAC,WAAW,EAAE,CAAC;AAChB,MAAM,OAAO,GACX,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC;AAE9E,kFAAkF;AAClF,mEAAmE;AACnE,SAAS,cAAc;IACrB,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB;QACnC,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAE3C,sDAAsD;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACxD,IAAI,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,8CAA8C,SAAS,EAAE,CAAC,CAAC;QACvE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,eAAe,EAAE,MAAM,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;AAEjC,MAAM,WAAW,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/analysis/server.ts"],"names":[],"mappings":"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"}
|
package/dist/cli-utils.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ToolConfig } from "./types.js";
|
|
2
|
+
declare const KNOWN_REDACT_PRESETS: readonly ["secrets", "pii", "strict"];
|
|
3
|
+
type RedactPreset = (typeof KNOWN_REDACT_PRESETS)[number];
|
|
2
4
|
export interface ParsedCliArgs {
|
|
3
5
|
showHelp: boolean;
|
|
4
6
|
showVersion: boolean;
|
|
@@ -7,6 +9,8 @@ export interface ParsedCliArgs {
|
|
|
7
9
|
noUpdateCheck: boolean;
|
|
8
10
|
useMitm: boolean;
|
|
9
11
|
privacyLevel?: string;
|
|
12
|
+
redactPreset?: RedactPreset;
|
|
13
|
+
rehydrate?: boolean;
|
|
10
14
|
commandName?: string;
|
|
11
15
|
commandArguments: string[];
|
|
12
16
|
error?: string;
|
|
@@ -25,4 +29,5 @@ export declare const CLI_CONSTANTS: {
|
|
|
25
29
|
readonly KNOWN_PRIVACY_LEVELS: readonly ["minimal", "standard", "full"];
|
|
26
30
|
readonly MITM_ADDON_PATH: string;
|
|
27
31
|
};
|
|
32
|
+
export {};
|
|
28
33
|
//# sourceMappingURL=cli-utils.d.ts.map
|
package/dist/cli-utils.d.ts.map
CHANGED
|
@@ -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;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;
|
|
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,QAAA,MAAM,oBAAoB,uCAAwC,CAAC;AACnE,KAAK,YAAY,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAM1D,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,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsGD,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,CA0J1D;AAED,wBAAgB,cAAc,IAAI,MAAM,CAuEvC;AAGD,eAAO,MAAM,aAAa;;;;;;;;;CAUhB,CAAC"}
|
package/dist/cli-utils.js
CHANGED
|
@@ -16,6 +16,10 @@ const COMMAND_ALIASES = {
|
|
|
16
16
|
oc: "opencode",
|
|
17
17
|
};
|
|
18
18
|
const KNOWN_PRIVACY_LEVELS = ["minimal", "standard", "full"];
|
|
19
|
+
const KNOWN_REDACT_PRESETS = ["secrets", "pii", "strict"];
|
|
20
|
+
function isRedactPreset(value) {
|
|
21
|
+
return KNOWN_REDACT_PRESETS.includes(value);
|
|
22
|
+
}
|
|
19
23
|
function isPrivacyLevel(value) {
|
|
20
24
|
return KNOWN_PRIVACY_LEVELS.includes(value);
|
|
21
25
|
}
|
|
@@ -42,6 +46,23 @@ const TOOL_CONFIG = {
|
|
|
42
46
|
serverEnv: {},
|
|
43
47
|
needsMitm: true,
|
|
44
48
|
},
|
|
49
|
+
cline: {
|
|
50
|
+
// Cline with Anthropic OAuth routes through api.cline.bot (their own
|
|
51
|
+
// backend), not directly to api.anthropic.com. Setting ANTHROPIC_BASE_URL
|
|
52
|
+
// has no effect in OAuth mode, so we use mitmproxy to intercept the
|
|
53
|
+
// HTTPS traffic to api.cline.bot and capture the /v1/messages calls.
|
|
54
|
+
//
|
|
55
|
+
// Cline is a Node.js process. Node ignores SSL_CERT_FILE and requires
|
|
56
|
+
// NODE_EXTRA_CA_CERTS to trust the mitmproxy CA certificate.
|
|
57
|
+
childEnv: {
|
|
58
|
+
https_proxy: MITM_PROXY_URL,
|
|
59
|
+
SSL_CERT_FILE: "", // for any native/curl components
|
|
60
|
+
NODE_EXTRA_CA_CERTS: "", // filled in by cli.ts with mitmproxy CA cert path
|
|
61
|
+
},
|
|
62
|
+
extraArgs: [],
|
|
63
|
+
serverEnv: {},
|
|
64
|
+
needsMitm: true,
|
|
65
|
+
},
|
|
45
66
|
aider: {
|
|
46
67
|
childEnv: {
|
|
47
68
|
ANTHROPIC_BASE_URL: `${PROXY_URL}/aider`,
|
|
@@ -119,6 +140,8 @@ export function parseCliArgs(args) {
|
|
|
119
140
|
let noUpdateCheck = false;
|
|
120
141
|
let useMitm = false;
|
|
121
142
|
let privacyLevel;
|
|
143
|
+
let redactPreset;
|
|
144
|
+
let rehydrate = false;
|
|
122
145
|
let explicitSeparator = false;
|
|
123
146
|
let commandStartIndex = -1;
|
|
124
147
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -156,6 +179,31 @@ export function parseCliArgs(args) {
|
|
|
156
179
|
useMitm = true;
|
|
157
180
|
continue;
|
|
158
181
|
}
|
|
182
|
+
if (arg === "--redact") {
|
|
183
|
+
redactPreset = "secrets";
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (arg === "--rehydrate") {
|
|
187
|
+
rehydrate = true;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (arg.startsWith("--redact=")) {
|
|
191
|
+
const value = arg.split("=", 2)[1];
|
|
192
|
+
if (!isRedactPreset(value)) {
|
|
193
|
+
return {
|
|
194
|
+
showHelp,
|
|
195
|
+
showVersion,
|
|
196
|
+
noOpen,
|
|
197
|
+
noUi,
|
|
198
|
+
noUpdateCheck,
|
|
199
|
+
useMitm,
|
|
200
|
+
commandArguments: [],
|
|
201
|
+
error: `Error: Invalid redact preset '${value}'. Must be one of: ${KNOWN_REDACT_PRESETS.join(", ")}`,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
redactPreset = value;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
159
207
|
if (arg === "--privacy") {
|
|
160
208
|
if (i + 1 >= args.length) {
|
|
161
209
|
return {
|
|
@@ -212,6 +260,8 @@ export function parseCliArgs(args) {
|
|
|
212
260
|
noUpdateCheck,
|
|
213
261
|
useMitm,
|
|
214
262
|
privacyLevel,
|
|
263
|
+
redactPreset,
|
|
264
|
+
rehydrate,
|
|
215
265
|
commandArguments: [],
|
|
216
266
|
error: "Error: No command specified after --",
|
|
217
267
|
};
|
|
@@ -224,6 +274,8 @@ export function parseCliArgs(args) {
|
|
|
224
274
|
noUpdateCheck,
|
|
225
275
|
useMitm,
|
|
226
276
|
privacyLevel,
|
|
277
|
+
redactPreset,
|
|
278
|
+
rehydrate,
|
|
227
279
|
commandName,
|
|
228
280
|
commandArguments,
|
|
229
281
|
};
|
|
@@ -244,6 +296,7 @@ export function formatHelpText() {
|
|
|
244
296
|
"Examples:",
|
|
245
297
|
" context-lens claude",
|
|
246
298
|
" context-lens codex",
|
|
299
|
+
" context-lens cline",
|
|
247
300
|
" context-lens opencode",
|
|
248
301
|
" context-lens gm",
|
|
249
302
|
" context-lens bryti",
|
|
@@ -264,6 +317,8 @@ export function formatHelpText() {
|
|
|
264
317
|
" --no-ui Run proxy only (no analysis/web UI server)",
|
|
265
318
|
" --no-update-check Skip npm update check for this run",
|
|
266
319
|
" --mitm Use mitmproxy for interception instead of base URL override (pi only)",
|
|
320
|
+
" --redact[=preset] Strip sensitive data before capture (experimental). Preset: secrets|pii|strict (default: secrets)",
|
|
321
|
+
" --rehydrate With --redact: restore original values in responses (off by default)",
|
|
267
322
|
"",
|
|
268
323
|
"Command aliases:",
|
|
269
324
|
" cc -> claude",
|
|
@@ -281,7 +336,8 @@ export function formatHelpText() {
|
|
|
281
336
|
"",
|
|
282
337
|
"Notes:",
|
|
283
338
|
" - No command starts standalone mode (proxy + analysis/web UI by default).",
|
|
284
|
-
" - 'codex' and 'opencode' use mitmproxy for HTTPS interception (requires mitmproxy; install: pipx install mitmproxy).",
|
|
339
|
+
" - 'codex', 'cline', and 'opencode' use mitmproxy for HTTPS interception (requires mitmproxy; install: pipx install mitmproxy).",
|
|
340
|
+
" - 'cline' with Anthropic OAuth routes through api.cline.bot; mitmproxy intercepts that traffic.",
|
|
285
341
|
" - 'pi --mitm' uses mitmproxy for full interception, useful for subscription-based models (openai-codex provider).",
|
|
286
342
|
" - 'doctor' is a local diagnostics command.",
|
|
287
343
|
" - 'background' manages detached proxy/web-ui processes.",
|
package/dist/cli-utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"cli-utils.js","sourceRoot":"","sources":["../src/cli-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import http from "node:http";
|
|
@@ -9,6 +9,7 @@ import { homedir, platform, tmpdir } from "node:os";
|
|
|
9
9
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { CLI_CONSTANTS, formatHelpText, getToolConfig, parseCliArgs, } from "./cli-utils.js";
|
|
12
|
+
import { loadConfig } from "./config.js";
|
|
12
13
|
import { VERSION } from "./version.generated.js";
|
|
13
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
15
|
const __dirname = dirname(__filename);
|
|
@@ -29,8 +30,19 @@ if (parsedArgs.showVersion) {
|
|
|
29
30
|
console.log(VERSION);
|
|
30
31
|
process.exit(0);
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
// Load user config — CLI flags take precedence over config file values
|
|
34
|
+
const userConfig = loadConfig();
|
|
35
|
+
const privacyLevel = parsedArgs.privacyLevel ?? userConfig.privacy.level;
|
|
36
|
+
if (privacyLevel !== undefined) {
|
|
37
|
+
process.env.CONTEXT_LENS_PRIVACY = privacyLevel;
|
|
38
|
+
}
|
|
39
|
+
const redactPreset = parsedArgs.redactPreset ?? userConfig.proxy.redact;
|
|
40
|
+
if (redactPreset !== undefined) {
|
|
41
|
+
process.env.CONTEXT_LENS_REDACT = redactPreset;
|
|
42
|
+
}
|
|
43
|
+
const rehydrate = parsedArgs.rehydrate ?? userConfig.proxy.rehydrate;
|
|
44
|
+
if (rehydrate) {
|
|
45
|
+
process.env.CONTEXT_LENS_REHYDRATE = "1";
|
|
34
46
|
}
|
|
35
47
|
if (!parsedArgs.noUpdateCheck &&
|
|
36
48
|
process.env.CONTEXT_LENS_NO_UPDATE_CHECK !== "1") {
|
|
@@ -96,7 +108,7 @@ else if (!parsedArgs.commandName) {
|
|
|
96
108
|
else {
|
|
97
109
|
const commandName = parsedArgs.commandName;
|
|
98
110
|
const commandArguments = parsedArgs.commandArguments;
|
|
99
|
-
const noOpen = parsedArgs.noOpen;
|
|
111
|
+
const noOpen = parsedArgs.noOpen || userConfig.ui.noOpen;
|
|
100
112
|
const noUi = parsedArgs.noUi;
|
|
101
113
|
const useMitm = parsedArgs.useMitm;
|
|
102
114
|
// Get tool-specific config, with optional mitmproxy override for pi
|
|
@@ -248,7 +260,9 @@ else {
|
|
|
248
260
|
const output = data.toString();
|
|
249
261
|
if (!proxyReady)
|
|
250
262
|
process.stderr.write(output);
|
|
251
|
-
if (output.includes("Context Lens Proxy running")
|
|
263
|
+
if ((output.includes("Context Lens Proxy running") ||
|
|
264
|
+
output.includes("@contextio/proxy running")) &&
|
|
265
|
+
!proxyReady) {
|
|
252
266
|
proxyReady = true;
|
|
253
267
|
checkBothReady();
|
|
254
268
|
}
|
|
@@ -415,11 +429,22 @@ else {
|
|
|
415
429
|
}
|
|
416
430
|
}
|
|
417
431
|
}
|
|
418
|
-
// Fill in mitmproxy CA cert path for tools that need HTTPS interception
|
|
419
|
-
|
|
432
|
+
// Fill in mitmproxy CA cert path for tools that need HTTPS interception.
|
|
433
|
+
// SSL_CERT_FILE is used by OpenSSL/curl (native binaries).
|
|
434
|
+
// NODE_EXTRA_CA_CERTS is used by Node.js processes (e.g. Cline).
|
|
435
|
+
if (toolConfig.needsMitm) {
|
|
420
436
|
const certPath = join(homedir(), ".mitmproxy", "mitmproxy-ca-cert.pem");
|
|
421
437
|
if (fs.existsSync(certPath)) {
|
|
422
|
-
childEnv.SSL_CERT_FILE
|
|
438
|
+
if (childEnv.SSL_CERT_FILE === "")
|
|
439
|
+
childEnv.SSL_CERT_FILE = certPath;
|
|
440
|
+
if (childEnv.NODE_EXTRA_CA_CERTS === "")
|
|
441
|
+
childEnv.NODE_EXTRA_CA_CERTS = certPath;
|
|
442
|
+
// On macOS, Codex is compiled with rustls + native-roots, which reads
|
|
443
|
+
// the system Keychain and ignores SSL_CERT_FILE entirely. If the cert
|
|
444
|
+
// is not trusted in the Keychain, Codex cannot connect through mitmproxy.
|
|
445
|
+
if (platform() === "darwin" && commandName === "codex") {
|
|
446
|
+
warnIfMitmCertNotTrustedMacOs(certPath);
|
|
447
|
+
}
|
|
423
448
|
}
|
|
424
449
|
else {
|
|
425
450
|
console.error(`Warning: mitmproxy CA cert not found at ${certPath}. Run 'mitmdump' once to generate it.`);
|
|
@@ -743,6 +768,27 @@ else {
|
|
|
743
768
|
return targetDir;
|
|
744
769
|
}
|
|
745
770
|
}
|
|
771
|
+
// On macOS, Codex uses rustls with native-roots (the system Keychain) and
|
|
772
|
+
// completely ignores SSL_CERT_FILE. If the mitmproxy CA cert is not trusted
|
|
773
|
+
// in the Keychain, Codex will fail to connect through mitmproxy with a
|
|
774
|
+
// "stream disconnected before completion" error. Check and warn.
|
|
775
|
+
function warnIfMitmCertNotTrustedMacOs(certPath) {
|
|
776
|
+
try {
|
|
777
|
+
// `security verify-cert` exits 0 when the cert chain is trusted by macOS.
|
|
778
|
+
const result = spawnSync("security", ["verify-cert", "-c", certPath], {
|
|
779
|
+
stdio: "pipe",
|
|
780
|
+
});
|
|
781
|
+
if (result.status !== 0) {
|
|
782
|
+
console.error("\n⚠️ mitmproxy CA cert is not trusted in the macOS Keychain.");
|
|
783
|
+
console.error(" Codex uses the system certificate store (not SSL_CERT_FILE).");
|
|
784
|
+
console.error(" Run this once to trust it, then retry:");
|
|
785
|
+
console.error(` sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${certPath}\n`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
catch {
|
|
789
|
+
// Non-fatal: if 'security' isn't available, skip the check.
|
|
790
|
+
}
|
|
791
|
+
}
|
|
746
792
|
// Open browser (cross-platform)
|
|
747
793
|
function openBrowser(url) {
|
|
748
794
|
const cmd = platform() === "darwin"
|
|
@@ -1114,9 +1160,21 @@ async function runDoctor() {
|
|
|
1114
1160
|
const mitmdumpPath = findBinaryOnPath("mitmdump");
|
|
1115
1161
|
info("mitmdump (Codex, pi --mitm)", mitmdumpPath ?? "not found (install: pipx install mitmproxy)");
|
|
1116
1162
|
const certPath = join(homedir(), ".mitmproxy", "mitmproxy-ca-cert.pem");
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1163
|
+
const certExists = fs.existsSync(certPath);
|
|
1164
|
+
info("mitm CA cert (Codex, pi --mitm)", certExists ? certPath : "not present (run 'mitmdump' once to generate)");
|
|
1165
|
+
if (certExists && platform() === "darwin") {
|
|
1166
|
+
try {
|
|
1167
|
+
const result = spawnSync("security", ["verify-cert", "-c", certPath], {
|
|
1168
|
+
stdio: "pipe",
|
|
1169
|
+
});
|
|
1170
|
+
info("mitm CA cert trusted in macOS Keychain", result.status === 0
|
|
1171
|
+
? "yes"
|
|
1172
|
+
: `NO — Codex will fail. Fix: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${certPath}`);
|
|
1173
|
+
}
|
|
1174
|
+
catch {
|
|
1175
|
+
// Non-fatal.
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1120
1178
|
const contextDir = join(homedir(), ".context-lens");
|
|
1121
1179
|
const dataDir = join(contextDir, "data");
|
|
1122
1180
|
report("context dir writable", checkWritableDir(contextDir), contextDir);
|
|
@@ -1130,6 +1188,22 @@ async function runDoctor() {
|
|
|
1130
1188
|
}
|
|
1131
1189
|
const lockfileExists = fs.existsSync(LOCKFILE);
|
|
1132
1190
|
report("lockfile", true, lockfileExists ? `${LOCKFILE} present` : `${LOCKFILE} absent`);
|
|
1191
|
+
const configPath = join(homedir(), ".context-lens", "config.toml");
|
|
1192
|
+
const configExists = fs.existsSync(configPath);
|
|
1193
|
+
info("config file", configExists
|
|
1194
|
+
? configPath
|
|
1195
|
+
: `not present — create ${configPath} to set defaults`);
|
|
1196
|
+
if (configExists) {
|
|
1197
|
+
const cfg = loadConfig();
|
|
1198
|
+
if (cfg.proxy.redact)
|
|
1199
|
+
info("config: redact", cfg.proxy.redact);
|
|
1200
|
+
if (cfg.proxy.rehydrate)
|
|
1201
|
+
info("config: rehydrate", "true");
|
|
1202
|
+
if (cfg.ui.noOpen)
|
|
1203
|
+
info("config: no_open", "true");
|
|
1204
|
+
if (cfg.privacy.level)
|
|
1205
|
+
info("config: privacy", cfg.privacy.level);
|
|
1206
|
+
}
|
|
1133
1207
|
if (hasFailures) {
|
|
1134
1208
|
console.log("Doctor result: issues found.");
|
|
1135
1209
|
return 1;
|