context-lens 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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/README.md CHANGED
@@ -34,6 +34,7 @@ npx context-lens ...
34
34
  context-lens claude
35
35
  context-lens codex
36
36
  context-lens gemini
37
+ context-lens gm # alias for gemini
37
38
  context-lens aider --model claude-sonnet-4
38
39
  context-lens pi
39
40
  context-lens -- python my_agent.py
@@ -43,6 +44,42 @@ Or without installing: replace `context-lens` with `npx context-lens`.
43
44
 
44
45
  This starts the proxy (port 4040), opens the web UI (http://localhost:4041), sets the right env vars, and runs your command. Multiple tools can share one proxy; just open more terminals.
45
46
 
47
+ ## CLI options
48
+
49
+ ```bash
50
+ context-lens --help
51
+ context-lens --version
52
+ context-lens --privacy=minimal claude
53
+ context-lens --no-open codex
54
+ context-lens --no-ui -- claude
55
+ context-lens doctor
56
+ context-lens background start --no-ui
57
+ context-lens background status
58
+ context-lens background stop
59
+ ```
60
+
61
+ - `--help`, `--version`: show usage/version and exit
62
+ - `--privacy <minimal|standard|full>`: controls privacy mode passed to the analysis server
63
+ - `--no-open`: do not auto-open `http://localhost:4041` when launching a command
64
+ - `--no-ui`: run proxy only (no analysis/web UI server) for capture-only data gathering
65
+ - `--no-update-check`: skip npm update check for this run
66
+
67
+ `--no-ui` is not compatible with `codex` subscription mode (`mitmproxy` ingestion depends on `http://localhost:4041/api/ingest`).
68
+
69
+ Built-in commands:
70
+ - `doctor`: run local diagnostics (ports, mitmproxy availability, cert path, writable dirs, background state)
71
+ - `background start [--no-ui]`: start detached proxy (and analysis/web UI unless `--no-ui`)
72
+ - `background status`: show detached process state
73
+ - `background stop`: stop detached process state
74
+
75
+ Aliases:
76
+ - `cc` -> `claude`
77
+ - `cpi` -> `pi`
78
+ - `cx` -> `codex`
79
+ - `gm` -> `gemini`
80
+
81
+ By default, the CLI does a cached (once per day) non-blocking check for new npm versions and prints an upgrade hint when a newer release is available. Disable globally with `CONTEXT_LENS_NO_UPDATE_CHECK=1`.
82
+
46
83
  ## Supported Providers
47
84
 
48
85
  | Provider | Method | Status | Environment Variable |
@@ -153,22 +190,25 @@ If Codex fails with certificate trust errors, install/trust the mitmproxy CA cer
153
190
 
154
191
  ## How It Works
155
192
 
156
- Context Lens sits between your coding tool and the LLM API, capturing requests in transit.
157
-
158
- **Reverse proxy (Claude Code, aider, OpenAI API tools)**
193
+ 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**.
159
194
 
160
195
  ```
161
- Tool ─HTTP─▶ Context Lens (:4040) ─HTTPS─▶ api.anthropic.com / api.openai.com
162
-
163
-
164
- Web UI (:4041)
196
+ Tool ─HTTP─▶ Proxy (:4040) ─HTTPS─▶ api.anthropic.com / api.openai.com
197
+
198
+ capture files
199
+
200
+ Analysis Server (:4041) → Web UI
165
201
  ```
166
202
 
167
- The CLI sets env vars like `ANTHROPIC_BASE_URL=http://localhost:4040` so the tool sends requests to the proxy instead of the real API. The proxy buffers each request body, parses the JSON to extract context structure (system prompts, tools, messages), forwards the raw bytes upstream with all original headers intact, then captures the response on the way back. The tool never knows it's being proxied.
203
+ The **proxy** (`src/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. This is an intentional architectural constraint: your API keys pass through the proxy, so it must stay small, auditable, and free of transitive supply-chain risk.
204
+
205
+ The **analysis server** picks up those captures, parses request bodies, estimates tokens, groups requests into conversations, computes composition breakdowns, calculates costs, scores context health, and scans for prompt injection patterns. It serves the web UI and API. The two sides communicate only through capture files on disk, so the analysis server, CLI, and web UI are free to use whatever dependencies they need without affecting the proxy's trust boundary.
206
+
207
+ The CLI sets env vars like `ANTHROPIC_BASE_URL=http://localhost:4040` so the tool sends requests to the proxy instead of the real API. The tool never knows it's being proxied.
168
208
 
169
209
  **Forward HTTPS proxy (Codex subscription mode)**
170
210
 
171
- Some tools can't be reverse-proxied. Codex with a ChatGPT subscription authenticates against `chatgpt.com`, which is behind Cloudflare. A reverse proxy changes the TLS fingerprint, causing Cloudflare to reject the request with a 403. For these tools, Context Lens uses mitmproxy as a forward HTTPS proxy instead:
211
+ Codex with a ChatGPT subscription authenticates against `chatgpt.com`, which is behind Cloudflare. A reverse proxy changes the TLS fingerprint, causing Cloudflare to reject the request. For this case, Context Lens uses mitmproxy as a forward HTTPS proxy:
172
212
 
173
213
  ```
174
214
  Tool ─HTTPS via proxy─▶ mitmproxy (:8080) ─HTTPS─▶ chatgpt.com
@@ -176,14 +216,10 @@ Tool ─HTTPS via proxy─▶ mitmproxy (:8080) ─HTTPS─▶ chatgpt.com
176
216
  mitm_addon.py
177
217
 
178
218
 
179
- Web UI /api/ingest
219
+ Analysis Server /api/ingest
180
220
  ```
181
221
 
182
- The tool makes its own TLS connection through the proxy, preserving its native TLS fingerprint. The mitmproxy addon intercepts completed request/response pairs and posts them to Context Lens's ingest API. The tool needs `https_proxy` and `SSL_CERT_FILE` env vars set to route through mitmproxy and trust its CA certificate.
183
-
184
- **What the proxy captures**
185
-
186
- Each request is parsed to extract: model name, system prompts, tool definitions, message history (with per-message token estimates), and content block types (text, tool calls, tool results, images, thinking). The response is captured to extract usage stats and cost. Requests are grouped into conversations using session IDs (Anthropic `metadata.user_id`), response chaining (OpenAI `previous_response_id`), or a fingerprint of the system prompt + first user message.
222
+ The tool makes its own TLS connection through the proxy, preserving its native fingerprint. The mitmproxy addon intercepts completed request/response pairs and posts them to the analysis server's ingest API. The tool needs `https_proxy` and `SSL_CERT_FILE` env vars set to route through mitmproxy and trust its CA certificate.
187
223
 
188
224
  ## Why Context Lens?
189
225
 
@@ -208,7 +244,7 @@ Context Lens is for developers who want to understand and optimize their coding
208
244
 
209
245
  ## Data
210
246
 
211
- Captured requests are kept in memory (last 100) and persisted to `data/state.jsonl` across restarts. Each session is also logged as a separate `.lhar` file in `data/`. Use the Reset button in the UI to clear everything.
247
+ Captured requests are kept in memory (last 100 sessions) and persisted to `~/.context-lens/data/state.jsonl` across restarts. Each session is also logged as a separate `.lhar` file in `~/.context-lens/data/`. Use the Reset button in the UI to clear everything.
212
248
 
213
249
  ## License
214
250
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Capture ingestion: bridges raw proxy captures into the Store.
3
+ *
4
+ * Takes a CaptureData object (raw request/response from disk) and
5
+ * runs the full analysis pipeline, then stores the result.
6
+ */
7
+ import type { CaptureData } from "../proxy/capture.js";
8
+ import type { Store } from "../server/store.js";
9
+ /**
10
+ * Process a single capture and feed it into the Store.
11
+ */
12
+ export declare function ingestCapture(store: Store, capture: CaptureData): void;
13
+ //# sourceMappingURL=ingest.d.ts.map
@@ -0,0 +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,CAsEtE"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Capture ingestion: bridges raw proxy captures into the Store.
3
+ *
4
+ * Takes a CaptureData object (raw request/response from disk) and
5
+ * runs the full analysis pipeline, then stores the result.
6
+ */
7
+ import { estimateTokens, parseContextInfo } from "../core.js";
8
+ /**
9
+ * Process a single capture and feed it into the Store.
10
+ */
11
+ export function ingestCapture(store, capture) {
12
+ const { provider, apiFormat, requestBody, responseBody } = capture;
13
+ // Build contextInfo from the request body
14
+ let contextInfo;
15
+ if (requestBody) {
16
+ const body = { ...requestBody };
17
+ // Gemini: model is in the URL path, not in the body
18
+ if (apiFormat === "gemini" && !body.model) {
19
+ const modelMatch = capture.path.match(/\/models\/([^/:]+)/);
20
+ if (modelMatch)
21
+ body.model = modelMatch[1];
22
+ }
23
+ contextInfo = parseContextInfo(provider, body, apiFormat);
24
+ }
25
+ else {
26
+ // Non-JSON request: create a raw contextInfo
27
+ contextInfo = {
28
+ provider,
29
+ apiFormat: "raw",
30
+ model: "unknown",
31
+ systemTokens: 0,
32
+ toolsTokens: 0,
33
+ messagesTokens: estimateTokens(responseBody),
34
+ totalTokens: estimateTokens(responseBody),
35
+ systemPrompts: [],
36
+ tools: [],
37
+ messages: [
38
+ {
39
+ role: "raw",
40
+ content: responseBody.substring(0, 2000),
41
+ tokens: estimateTokens(responseBody),
42
+ },
43
+ ],
44
+ };
45
+ }
46
+ // Parse the response
47
+ let responseData;
48
+ if (capture.responseIsStreaming) {
49
+ responseData = { streaming: true, chunks: responseBody };
50
+ }
51
+ else {
52
+ try {
53
+ responseData = JSON.parse(responseBody);
54
+ }
55
+ catch {
56
+ responseData = { raw: responseBody };
57
+ }
58
+ }
59
+ // Build request metadata
60
+ const meta = {
61
+ httpStatus: capture.responseStatus,
62
+ timings: {
63
+ ...capture.timings,
64
+ tokens_per_second: null,
65
+ },
66
+ requestBytes: capture.requestBytes,
67
+ responseBytes: capture.responseBytes,
68
+ targetUrl: capture.targetUrl,
69
+ requestHeaders: capture.requestHeaders,
70
+ responseHeaders: capture.responseHeaders,
71
+ };
72
+ // Feed into the store (which handles fingerprinting, scoring, etc.)
73
+ store.storeRequest(contextInfo, responseData, capture.source, requestBody ?? undefined, meta, capture.requestHeaders);
74
+ }
75
+ //# sourceMappingURL=ingest.js.map
@@ -0,0 +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,CACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Context Lens Analysis Server.
4
+ *
5
+ * Watches the capture directory for new files from the proxy,
6
+ * processes them through the analysis pipeline, and serves
7
+ * the Web UI and API.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/analysis/server.ts"],"names":[],"mappings":";AAEA;;;;;;GAMG"}
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Context Lens Analysis Server.
4
+ *
5
+ * Watches the capture directory for new files from the proxy,
6
+ * processes them through the analysis pipeline, and serves
7
+ * the Web UI and API.
8
+ */
9
+ import fs from "node:fs";
10
+ import { homedir } from "node:os";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { serve } from "@hono/node-server";
14
+ import { Store } from "../server/store.js";
15
+ import { createApp, loadHtmlUI } from "../server/webui.js";
16
+ import { ingestCapture } from "./ingest.js";
17
+ import { CaptureWatcher } from "./watcher.js";
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = path.dirname(__filename);
20
+ // --- Config ---
21
+ const bindHost = process.env.CONTEXT_LENS_BIND_HOST || "127.0.0.1";
22
+ const port = parseInt(process.env.CONTEXT_LENS_ANALYSIS_PORT || "4041", 10);
23
+ const captureDir = process.env.CONTEXT_LENS_CAPTURE_DIR ||
24
+ path.join(homedir(), ".context-lens", "captures");
25
+ const privacyEnv = (process.env.CONTEXT_LENS_PRIVACY || "standard").toLowerCase();
26
+ const privacy = privacyEnv === "minimal" || privacyEnv === "full" ? privacyEnv : "standard";
27
+ // Data directory: check for explicit env, then legacy location, then new default.
28
+ // Pre-split installs stored data in <project>/data/ next to dist/.
29
+ function resolveDataDir() {
30
+ if (process.env.CONTEXT_LENS_DATA_DIR)
31
+ return process.env.CONTEXT_LENS_DATA_DIR;
32
+ // Legacy location: <project>/data/ (sibling of dist/)
33
+ const legacyDir = path.resolve(__dirname, "..", "..", "data");
34
+ const legacyState = path.join(legacyDir, "state.jsonl");
35
+ if (fs.existsSync(legacyState)) {
36
+ console.log(`📦 Found existing data at legacy location: ${legacyDir}`);
37
+ return legacyDir;
38
+ }
39
+ return path.join(homedir(), ".context-lens", "data");
40
+ }
41
+ const dataDir = resolveDataDir();
42
+ const maxSessions = 200;
43
+ const maxCompactMessages = 60;
44
+ // --- Setup ---
45
+ const store = new Store({
46
+ dataDir,
47
+ stateFile: path.join(dataDir, "state.jsonl"),
48
+ maxSessions,
49
+ maxCompactMessages,
50
+ privacy,
51
+ });
52
+ store.loadState();
53
+ // --- Capture watcher ---
54
+ const isUtilityEndpoint = (capturePath) => /\/count_tokens\b|:countTokens\b|:loadCodeAssist\b|:retrieveUserQuota\b|:listExperiments\b|:onboardUser\b|:fetchAdminControls\b|:recordCodeAssistMetrics\b/.test(capturePath);
55
+ const watcher = new CaptureWatcher({
56
+ captureDir,
57
+ onCapture: (capture, filename) => {
58
+ // Skip utility endpoints
59
+ if (isUtilityEndpoint(capture.path))
60
+ return;
61
+ try {
62
+ ingestCapture(store, capture);
63
+ }
64
+ catch (err) {
65
+ console.error(`Ingest error (${filename}):`, err instanceof Error ? err.message : String(err));
66
+ }
67
+ },
68
+ deleteAfterProcessing: true,
69
+ });
70
+ watcher.start();
71
+ // --- Web UI server ---
72
+ const projectDistDir = path.resolve(__dirname, "..");
73
+ const htmlUI = loadHtmlUI();
74
+ const app = createApp(store, htmlUI, projectDistDir);
75
+ const server = serve({ fetch: app.fetch, hostname: bindHost, port }, (info) => {
76
+ console.log(`🌐 Context Lens Analysis running on http://${info.address}:${info.port}`);
77
+ console.log(`📁 Watching captures → ${captureDir}`);
78
+ console.log(`💾 Data → ${dataDir}`);
79
+ });
80
+ server.on("error", (err) => {
81
+ if (err.code === "EADDRINUSE") {
82
+ console.log(`🌐 Context Lens Analysis already running on port ${port}`);
83
+ watcher.stop();
84
+ process.exit(0);
85
+ }
86
+ throw err;
87
+ });
88
+ // --- Graceful shutdown ---
89
+ function shutdown() {
90
+ watcher.stop();
91
+ server.close();
92
+ }
93
+ process.on("SIGINT", shutdown);
94
+ process.on("SIGTERM", shutdown);
95
+ //# sourceMappingURL=server.js.map
@@ -0,0 +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,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;AACxB,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAE9B,gBAAgB;AAEhB,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;IACtB,OAAO;IACP,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;IAC5C,WAAW;IACX,kBAAkB;IAClB,OAAO;CACR,CAAC,CAAC;AAEH,KAAK,CAAC,SAAS,EAAE,CAAC;AAElB,0BAA0B;AAE1B,MAAM,iBAAiB,GAAG,CAAC,WAAmB,EAAW,EAAE,CACzD,2JAA2J,CAAC,IAAI,CAC9J,WAAW,CACZ,CAAC;AAEJ,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC;IACjC,UAAU;IACV,SAAS,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE;QAC/B,yBAAyB;QACzB,IAAI,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,OAAO;QAE5C,IAAI,CAAC;YACH,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAChC,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CACX,iBAAiB,QAAQ,IAAI,EAC7B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;QACJ,CAAC;IACH,CAAC;IACD,qBAAqB,EAAE,IAAI;CAC5B,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,EAAE,CAAC;AAEhB,wBAAwB;AAExB,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AACrD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;AAErD,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE;IAC5E,OAAO,CAAC,GAAG,CACT,8CAA8C,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAC1E,CAAC;IACF,OAAO,CAAC,GAAG,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAA0B,EAAE,EAAE;IAChD,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,oDAAoD,IAAI,EAAE,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,CAAC;AACZ,CAAC,CAAC,CAAC;AAEH,4BAA4B;AAE5B,SAAS,QAAQ;IACf,OAAO,CAAC,IAAI,EAAE,CAAC;IACf,MAAM,CAAC,KAAK,EAAE,CAAC;AACjB,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Capture directory watcher.
3
+ *
4
+ * Watches for new capture files written by the proxy, reads them,
5
+ * and feeds them into the analysis pipeline (Store).
6
+ *
7
+ * On startup, replays all existing capture files in sorted order.
8
+ * Then watches for new files via fs.watch. Ignores .tmp files
9
+ * (the proxy writes atomically: .tmp then rename).
10
+ */
11
+ import type { CaptureData } from "../proxy/capture.js";
12
+ export type CaptureHandler = (capture: CaptureData, filename: string) => void;
13
+ export interface CaptureWatcherOptions {
14
+ captureDir: string;
15
+ onCapture: CaptureHandler;
16
+ /** Delete capture files after successful processing (default: true) */
17
+ deleteAfterProcessing?: boolean;
18
+ /** Poll interval in ms when fs.watch is unreliable (default: 0 = disabled) */
19
+ pollInterval?: number;
20
+ }
21
+ export declare class CaptureWatcher {
22
+ private readonly captureDir;
23
+ private readonly onCapture;
24
+ private readonly deleteAfterProcessing;
25
+ private readonly pollInterval;
26
+ private readonly processed;
27
+ private readonly processing;
28
+ private watcher;
29
+ private pollTimer;
30
+ private running;
31
+ constructor(opts: CaptureWatcherOptions);
32
+ /**
33
+ * Start watching. Replays existing captures first, then watches for new ones.
34
+ */
35
+ start(): void;
36
+ /**
37
+ * Stop watching.
38
+ */
39
+ stop(): void;
40
+ /**
41
+ * Process all existing capture files in sorted order.
42
+ */
43
+ private replayExisting;
44
+ /**
45
+ * Start fs.watch on the capture directory, with optional polling fallback.
46
+ */
47
+ private startWatch;
48
+ private startPolling;
49
+ private scanForNew;
50
+ /**
51
+ * Read and process a single capture file.
52
+ */
53
+ private processFile;
54
+ }
55
+ //# sourceMappingURL=watcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../src/analysis/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,cAAc,CAAC;IAC1B,uEAAuE;IACvE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAC3C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;IAC/C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,qBAAqB;IAOvC;;OAEG;IACH,KAAK,IAAI,IAAI;IAWb;;OAEG;IACH,IAAI,IAAI,IAAI;IAYZ;;OAEG;IACH,OAAO,CAAC,cAAc;IAiBtB;;OAEG;IACH,OAAO,CAAC,UAAU;IAkClB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,OAAO,CAAC,WAAW;CA4BpB"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Capture directory watcher.
3
+ *
4
+ * Watches for new capture files written by the proxy, reads them,
5
+ * and feeds them into the analysis pipeline (Store).
6
+ *
7
+ * On startup, replays all existing capture files in sorted order.
8
+ * Then watches for new files via fs.watch. Ignores .tmp files
9
+ * (the proxy writes atomically: .tmp then rename).
10
+ */
11
+ import fs from "node:fs";
12
+ import { join } from "node:path";
13
+ export class CaptureWatcher {
14
+ captureDir;
15
+ onCapture;
16
+ deleteAfterProcessing;
17
+ pollInterval;
18
+ processed = new Set();
19
+ processing = new Set();
20
+ watcher = null;
21
+ pollTimer = null;
22
+ running = false;
23
+ constructor(opts) {
24
+ this.captureDir = opts.captureDir;
25
+ this.onCapture = opts.onCapture;
26
+ this.deleteAfterProcessing = opts.deleteAfterProcessing ?? true;
27
+ this.pollInterval = opts.pollInterval ?? 0;
28
+ }
29
+ /**
30
+ * Start watching. Replays existing captures first, then watches for new ones.
31
+ */
32
+ start() {
33
+ if (this.running)
34
+ return;
35
+ this.running = true;
36
+ // Replay existing captures
37
+ this.replayExisting();
38
+ // Start watching for new files
39
+ this.startWatch();
40
+ }
41
+ /**
42
+ * Stop watching.
43
+ */
44
+ stop() {
45
+ this.running = false;
46
+ if (this.watcher) {
47
+ this.watcher.close();
48
+ this.watcher = null;
49
+ }
50
+ if (this.pollTimer) {
51
+ clearInterval(this.pollTimer);
52
+ this.pollTimer = null;
53
+ }
54
+ }
55
+ /**
56
+ * Process all existing capture files in sorted order.
57
+ */
58
+ replayExisting() {
59
+ if (!fs.existsSync(this.captureDir))
60
+ return;
61
+ const files = fs
62
+ .readdirSync(this.captureDir)
63
+ .filter((f) => f.endsWith(".json") && !f.endsWith(".tmp"))
64
+ .sort();
65
+ for (const filename of files) {
66
+ this.processFile(filename);
67
+ }
68
+ if (files.length > 0) {
69
+ console.log(`📂 Replayed ${this.processed.size} existing captures`);
70
+ }
71
+ }
72
+ /**
73
+ * Start fs.watch on the capture directory, with optional polling fallback.
74
+ */
75
+ startWatch() {
76
+ // Ensure the directory exists before watching
77
+ if (!fs.existsSync(this.captureDir)) {
78
+ fs.mkdirSync(this.captureDir, { recursive: true });
79
+ }
80
+ try {
81
+ this.watcher = fs.watch(this.captureDir, (eventType, filename) => {
82
+ if (!filename)
83
+ return;
84
+ if (!filename.endsWith(".json") || filename.endsWith(".tmp"))
85
+ return;
86
+ // Small delay to ensure rename is complete
87
+ setTimeout(() => this.processFile(filename), 10);
88
+ });
89
+ this.watcher.on("error", (err) => {
90
+ console.error("Watcher error:", err.message);
91
+ // Fall back to polling
92
+ this.watcher = null;
93
+ this.startPolling();
94
+ });
95
+ }
96
+ catch (err) {
97
+ console.error("fs.watch failed, falling back to polling:", err instanceof Error ? err.message : String(err));
98
+ this.startPolling();
99
+ }
100
+ // Optional: also poll for reliability (fs.watch can miss events on some platforms)
101
+ if (this.pollInterval > 0) {
102
+ this.startPolling();
103
+ }
104
+ }
105
+ startPolling() {
106
+ if (this.pollTimer)
107
+ return;
108
+ const interval = this.pollInterval > 0 ? this.pollInterval : 1000;
109
+ this.pollTimer = setInterval(() => {
110
+ if (!this.running)
111
+ return;
112
+ this.scanForNew();
113
+ }, interval);
114
+ }
115
+ scanForNew() {
116
+ if (!fs.existsSync(this.captureDir))
117
+ return;
118
+ const files = fs
119
+ .readdirSync(this.captureDir)
120
+ .filter((f) => f.endsWith(".json") && !f.endsWith(".tmp"))
121
+ .sort();
122
+ const present = new Set(files);
123
+ // Keep dedupe state bounded to files that still exist.
124
+ // This prevents unbounded growth in long-running processes.
125
+ for (const filename of this.processed) {
126
+ if (!present.has(filename))
127
+ this.processed.delete(filename);
128
+ }
129
+ for (const filename of files) {
130
+ if (!this.processed.has(filename)) {
131
+ this.processFile(filename);
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Read and process a single capture file.
137
+ */
138
+ processFile(filename) {
139
+ if (this.processed.has(filename) || this.processing.has(filename))
140
+ return;
141
+ const filePath = join(this.captureDir, filename);
142
+ this.processing.add(filename);
143
+ try {
144
+ const content = fs.readFileSync(filePath, "utf8");
145
+ const capture = JSON.parse(content);
146
+ this.processed.add(filename);
147
+ this.onCapture(capture, filename);
148
+ if (this.deleteAfterProcessing) {
149
+ try {
150
+ fs.unlinkSync(filePath);
151
+ this.processed.delete(filename);
152
+ }
153
+ catch {
154
+ /* file may already be gone */
155
+ }
156
+ }
157
+ }
158
+ catch (err) {
159
+ // File might still be being written, or it's corrupt
160
+ const msg = err instanceof Error ? err.message : String(err);
161
+ if (msg.includes("ENOENT"))
162
+ return; // File disappeared, ignore
163
+ console.error(`Capture read error (${filename}):`, msg);
164
+ }
165
+ finally {
166
+ this.processing.delete(filename);
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/analysis/watcher.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,26 @@
1
1
  import type { ToolConfig } from "./types.js";
2
+ export interface ParsedCliArgs {
3
+ showHelp: boolean;
4
+ showVersion: boolean;
5
+ noOpen: boolean;
6
+ noUi: boolean;
7
+ noUpdateCheck: boolean;
8
+ privacyLevel?: string;
9
+ commandName?: string;
10
+ commandArguments: string[];
11
+ error?: string;
12
+ }
2
13
  export declare function getToolConfig(toolName: string): ToolConfig;
14
+ export declare function resolveCommandAlias(commandName: string): string;
15
+ export declare function parseCliArgs(args: string[]): ParsedCliArgs;
16
+ export declare function formatHelpText(): string;
3
17
  export declare const CLI_CONSTANTS: {
4
18
  readonly PROXY_URL: "http://localhost:4040";
5
19
  readonly MITM_PORT: 8080;
6
20
  readonly MITM_PROXY_URL: "http://localhost:8080";
7
21
  readonly PI_AGENT_DIR_PREFIX: "/tmp/context-lens-pi-agent-";
22
+ readonly COMMAND_ALIASES: Record<string, string>;
23
+ readonly KNOWN_PRIVACY_LEVELS: readonly ["minimal", "standard", "full"];
8
24
  readonly MITM_ADDON_PATH: string;
9
25
  };
10
26
  //# sourceMappingURL=cli-utils.d.ts.map
@@ -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;AA+D7C,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAY1D;AAGD,eAAO,MAAM,aAAa;;;;;;CAOhB,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;AAoB7C,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,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA2DD,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,CAiH1D;AAED,wBAAgB,cAAc,IAAI,MAAM,CAqDvC;AAGD,eAAO,MAAM,aAAa;;;;;;;;CAShB,CAAC"}