@fml-inc/panopticon 0.1.0 → 0.1.2
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 +59 -29
- package/bin/hook-handler +0 -0
- package/bin/mcp-server +0 -0
- package/bin/proxy +0 -0
- package/bin/server +0 -0
- package/dist/api/client.d.ts +2 -2
- package/dist/api/client.js +1 -1
- package/dist/{chunk-3BUJ7URA.js → chunk-3ILOOWUF.js} +66 -2
- package/dist/chunk-3ILOOWUF.js.map +1 -0
- package/dist/{chunk-HQCY722C.js → chunk-3ZT3V7FP.js} +5 -5
- package/dist/{chunk-WLBNFVIG.js → chunk-BKGQJ76N.js} +47 -19
- package/dist/chunk-BKGQJ76N.js.map +1 -0
- package/dist/{chunk-3TZAKV3M.js → chunk-FMAHQRIU.js} +2 -2
- package/dist/{chunk-LWXF7YRG.js → chunk-GPTBERQD.js} +2 -2
- package/dist/{chunk-4SM2H22C.js → chunk-HO443ZQM.js} +1 -1
- package/dist/{chunk-4SM2H22C.js.map → chunk-HO443ZQM.js.map} +1 -1
- package/dist/{chunk-L7G27XWF.js → chunk-HRNZUHTA.js} +3 -3
- package/dist/{chunk-CF4GPWLI.js → chunk-J3HVD4VI.js} +2 -2
- package/dist/{chunk-SEXU2WYG.js → chunk-MEVW27U4.js} +5 -4
- package/dist/chunk-MEVW27U4.js.map +1 -0
- package/dist/{chunk-SUGSQ4YI.js → chunk-N7NCNJZU.js} +4 -4
- package/dist/{chunk-RX2RXHBH.js → chunk-NE7VBLQD.js} +6 -5
- package/dist/{chunk-RX2RXHBH.js.map → chunk-NE7VBLQD.js.map} +1 -1
- package/dist/{chunk-NXH7AONS.js → chunk-OROLSIWZ.js} +8 -6
- package/dist/chunk-OROLSIWZ.js.map +1 -0
- package/dist/{chunk-XLTCUH5A.js → chunk-OW52TNVA.js} +4 -4
- package/dist/{chunk-DZ5HJFB4.js → chunk-SKZHAYNF.js} +53 -2
- package/dist/chunk-SKZHAYNF.js.map +1 -0
- package/dist/{chunk-BVOE7A2Z.js → chunk-V3XR2TAN.js} +8 -6
- package/dist/chunk-V3XR2TAN.js.map +1 -0
- package/dist/{chunk-HRCEIYKU.js → chunk-WXPT6KG7.js} +2 -2
- package/dist/cli.js +6 -6
- package/dist/cli.js.map +1 -1
- package/dist/db.js +1 -1
- package/dist/doctor.js +4 -4
- package/dist/hooks/handler.js +4 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +15 -15
- package/dist/mcp/server.js +1 -1
- package/dist/otlp/server.js +5 -5
- package/dist/pricing.js +2 -2
- package/dist/proxy/server.js +5 -5
- package/dist/prune.js +2 -2
- package/dist/query.js +2 -2
- package/dist/{reparse-636YZCE3.js → reparse-VHUSGCPN.js} +5 -5
- package/dist/scanner.d.ts +7 -1
- package/dist/scanner.js +1 -1
- package/dist/server.js +13 -13
- package/dist/setup.js +3 -3
- package/dist/sync/index.d.ts +2 -2
- package/dist/sync/index.js +4 -4
- package/dist/{types-D-MYCBol.d.ts → types-DrhrWbWe.d.ts} +1 -0
- package/package.json +21 -13
- package/dist/chunk-3BUJ7URA.js.map +0 -1
- package/dist/chunk-BVOE7A2Z.js.map +0 -1
- package/dist/chunk-DZ5HJFB4.js.map +0 -1
- package/dist/chunk-NXH7AONS.js.map +0 -1
- package/dist/chunk-SEXU2WYG.js.map +0 -1
- package/dist/chunk-WLBNFVIG.js.map +0 -1
- /package/dist/{chunk-HQCY722C.js.map → chunk-3ZT3V7FP.js.map} +0 -0
- /package/dist/{chunk-3TZAKV3M.js.map → chunk-FMAHQRIU.js.map} +0 -0
- /package/dist/{chunk-LWXF7YRG.js.map → chunk-GPTBERQD.js.map} +0 -0
- /package/dist/{chunk-L7G27XWF.js.map → chunk-HRNZUHTA.js.map} +0 -0
- /package/dist/{chunk-CF4GPWLI.js.map → chunk-J3HVD4VI.js.map} +0 -0
- /package/dist/{chunk-SUGSQ4YI.js.map → chunk-N7NCNJZU.js.map} +0 -0
- /package/dist/{chunk-XLTCUH5A.js.map → chunk-OW52TNVA.js.map} +0 -0
- /package/dist/{chunk-HRCEIYKU.js.map → chunk-WXPT6KG7.js.map} +0 -0
- /package/dist/{reparse-636YZCE3.js.map → reparse-VHUSGCPN.js.map} +0 -0
package/README.md
CHANGED
|
@@ -121,18 +121,15 @@ Once installed, these tools are available to the AI coding tool via MCP:
|
|
|
121
121
|
|
|
122
122
|
| Tool | Description |
|
|
123
123
|
|------|-------------|
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `panopticon_status` | Database row counts |
|
|
134
|
-
| `panopticon_permissions_show` | Show current permission approvals and allowed tools/commands |
|
|
135
|
-
| `panopticon_permissions_apply` | Apply permission rules (allowed tools/commands via PreToolUse hook) |
|
|
124
|
+
| `sessions` | List recent sessions with stats (tokens, cost, model, project) |
|
|
125
|
+
| `timeline` | Messages and tool calls for a session, including child sessions (forks, subagents) |
|
|
126
|
+
| `costs` | Token usage and cost breakdowns by session, model, or day |
|
|
127
|
+
| `summary` | Activity summary — sessions, prompts, tools, files, costs. Ideal for standup updates |
|
|
128
|
+
| `plans` | Plans created via ExitPlanMode with full markdown content |
|
|
129
|
+
| `search` | Full-text search across events and messages (FTS5) |
|
|
130
|
+
| `get` | Fetch full untruncated details for a record by source and ID |
|
|
131
|
+
| `query` | Raw read-only SQL against the database |
|
|
132
|
+
| `status` | Database row counts |
|
|
136
133
|
|
|
137
134
|
## CLI
|
|
138
135
|
|
|
@@ -153,28 +150,30 @@ panopticon stop Stop the server
|
|
|
153
150
|
panopticon status Show server status and database stats
|
|
154
151
|
panopticon doctor Check system health, server, database, and configuration
|
|
155
152
|
|
|
156
|
-
panopticon logs [daemon] View daemon logs (
|
|
153
|
+
panopticon logs [daemon] View daemon logs (otlp, mcp)
|
|
157
154
|
-f, --follow Follow log output (like tail -f)
|
|
158
155
|
-n <lines> Number of lines to show (default 50)
|
|
159
156
|
|
|
160
157
|
panopticon sessions List recent sessions with stats
|
|
161
158
|
--limit <n> Max sessions to return (default 20)
|
|
162
159
|
--since <duration> Time filter (e.g. "24h", "7d")
|
|
163
|
-
panopticon timeline <id>
|
|
164
|
-
|
|
160
|
+
panopticon timeline <id> Get messages and tool calls for a session
|
|
161
|
+
--limit <n> Max messages to return (default 50)
|
|
162
|
+
--offset <n> Number of messages to skip
|
|
163
|
+
--full Return full content instead of truncated
|
|
165
164
|
panopticon costs Token usage and cost breakdowns
|
|
166
165
|
--group-by <g> Group by session, model, or day
|
|
167
166
|
panopticon summary Activity summary
|
|
168
167
|
panopticon plans List plans from ExitPlanMode events
|
|
169
|
-
panopticon search <query> Full-text search across
|
|
170
|
-
panopticon
|
|
168
|
+
panopticon search <query> Full-text search across events and messages
|
|
169
|
+
panopticon print <src> <id> Get full details for a record by source and ID
|
|
171
170
|
panopticon query <sql> Raw read-only SQL query
|
|
172
171
|
panopticon db-stats Show database row counts
|
|
173
172
|
|
|
174
|
-
panopticon
|
|
175
|
-
panopticon
|
|
176
|
-
|
|
177
|
-
panopticon
|
|
173
|
+
panopticon sync add <name> <url> Add or update a sync target
|
|
174
|
+
panopticon sync remove <name> Remove a sync target
|
|
175
|
+
panopticon sync list List sync targets
|
|
176
|
+
panopticon sync reset [target] Reset sync watermarks (re-syncs all data)
|
|
178
177
|
|
|
179
178
|
panopticon prune Delete old data from the database
|
|
180
179
|
--older-than 30d Max age (default: 30d)
|
|
@@ -182,7 +181,7 @@ panopticon prune Delete old data from the database
|
|
|
182
181
|
--vacuum Reclaim disk space after pruning
|
|
183
182
|
--yes Skip confirmation prompt
|
|
184
183
|
|
|
185
|
-
panopticon refresh-pricing Fetch latest model pricing from
|
|
184
|
+
panopticon refresh-pricing Fetch latest model pricing from LiteLLM
|
|
186
185
|
panopticon permissions show Show current approval rules
|
|
187
186
|
panopticon permissions apply Apply permission rules (JSON from stdin)
|
|
188
187
|
```
|
|
@@ -199,7 +198,7 @@ Daemon stdout/stderr is written to platform-specific log directories:
|
|
|
199
198
|
| Linux | `~/.local/state/panopticon/logs/` |
|
|
200
199
|
| Windows | `%LOCALAPPDATA%/panopticon/logs/` |
|
|
201
200
|
|
|
202
|
-
Log files: `server.log`, `
|
|
201
|
+
Log files: `server.log`, `otlp-receiver.log`, `mcp-server.log`, `proxy.log`, `hook-handler.log`.
|
|
203
202
|
|
|
204
203
|
## Configuration
|
|
205
204
|
|
|
@@ -250,17 +249,21 @@ SQLite with WAL mode. Location depends on platform (see data directory above).
|
|
|
250
249
|
| Table | Description |
|
|
251
250
|
|-------|-------------|
|
|
252
251
|
| `sessions` | Unified session metadata — aggregated from hooks, OTel, and scanner |
|
|
252
|
+
| `messages` | Parsed messages (user, assistant, system) with token usage and DAG metadata |
|
|
253
|
+
| `tool_calls` | Tool invocations extracted from messages, with inputs, results, and durations |
|
|
253
254
|
| `otel_logs` | OTel log records (api_request, tool_result, user_prompt, etc.) |
|
|
254
255
|
| `otel_metrics` | OTel metric data points (token usage, cost, active time, etc.) |
|
|
256
|
+
| `otel_spans` | OTel trace spans |
|
|
255
257
|
| `hook_events` | Plugin hook events with full payloads (tool inputs/outputs, tool results) |
|
|
256
|
-
| `hook_events_fts` | FTS5 full-text search index on hook payloads |
|
|
257
258
|
| `scanner_turns` | Per-turn token usage from session files (input, output, cache, reasoning) |
|
|
258
259
|
| `scanner_events` | Tool calls, errors, reasoning, file snapshots from session files |
|
|
259
260
|
| `scanner_file_watermarks` | Byte offsets for incremental session file parsing |
|
|
260
261
|
| `session_repositories` | Maps sessions to GitHub repositories |
|
|
261
262
|
| `session_cwds` | Maps sessions to working directories |
|
|
262
|
-
| `
|
|
263
|
-
| `
|
|
263
|
+
| `session_summary_deltas` | Incremental session summaries |
|
|
264
|
+
| `model_pricing` | Cached model pricing from LiteLLM |
|
|
265
|
+
| `watermarks` | Sync watermarks for OTLP export targets |
|
|
266
|
+
| `target_session_sync` | Per-target session sync state |
|
|
264
267
|
|
|
265
268
|
Query directly with `panopticon query` or via the `panopticon_query` MCP tool.
|
|
266
269
|
|
|
@@ -290,9 +293,18 @@ src/
|
|
|
290
293
|
├── server.ts Unified HTTP server (hooks, OTLP, proxy — single port)
|
|
291
294
|
├── sdk.ts SDK shim (observe() wrapper)
|
|
292
295
|
├── config.ts Panopticon paths, ports, defaults
|
|
296
|
+
├── setup.ts Install/uninstall logic
|
|
293
297
|
├── log.ts Log file paths (macOS/Linux/Windows)
|
|
294
298
|
├── repo.ts Git repository detection
|
|
295
299
|
├── toml.ts TOML read/write (for Codex config)
|
|
300
|
+
├── doctor.ts System health checks
|
|
301
|
+
├── sentry.ts Error reporting
|
|
302
|
+
├── eventConfig.ts Event type configuration
|
|
303
|
+
├── unified-config.ts Unified config management
|
|
304
|
+
├── api/
|
|
305
|
+
│ ├── client.ts API client for CLI/MCP queries via server
|
|
306
|
+
│ ├── routes.ts Server-side API route handlers
|
|
307
|
+
│ └── util.ts API utilities
|
|
296
308
|
├── targets/
|
|
297
309
|
│ ├── types.ts TargetAdapter interface (config, hooks, events, detect, proxy)
|
|
298
310
|
│ ├── registry.ts Map-based target registry (register, get, all)
|
|
@@ -306,13 +318,16 @@ src/
|
|
|
306
318
|
│ ├── query.ts Query helpers for MCP tools and CLI
|
|
307
319
|
│ ├── store.ts Data storage (insert hooks, OTel, upsert sessions)
|
|
308
320
|
│ ├── prune.ts Data retention / pruning
|
|
309
|
-
│
|
|
321
|
+
│ ├── sync-prune.ts Sync-aware pruning
|
|
322
|
+
│ └── pricing.ts Model pricing cache (LiteLLM)
|
|
310
323
|
├── scanner/
|
|
311
324
|
│ ├── index.ts Public API (createScannerLoop, scanOnce)
|
|
312
325
|
│ ├── loop.ts Poll loop — discovers files via target adapters, incremental parse
|
|
313
326
|
│ ├── reader.ts Byte-offset file reader (only reads new lines)
|
|
314
327
|
│ ├── store.ts Scanner DB operations (turns, events, watermarks, session upsert)
|
|
315
328
|
│ ├── reconcile.ts Compare scanner vs hooks/OTLP token data per session
|
|
329
|
+
│ ├── reparse.ts Re-parse session files from scratch
|
|
330
|
+
│ ├── categories.ts Tool call categorization
|
|
316
331
|
│ └── types.ts ScannerHandle, ScannerOptions
|
|
317
332
|
├── hooks/
|
|
318
333
|
│ ├── handler.ts Hook event handler (stdin JSON → server)
|
|
@@ -325,19 +340,34 @@ src/
|
|
|
325
340
|
│ ├── server.ts HTTP OTLP receiver (protobuf + JSON)
|
|
326
341
|
│ ├── decode-logs.ts OTel log record decoding
|
|
327
342
|
│ ├── decode-metrics.ts OTel metric decoding
|
|
343
|
+
│ ├── decode-traces.ts OTel trace/span decoding
|
|
328
344
|
│ └── proto.ts Protocol buffer definitions
|
|
329
345
|
├── sync/
|
|
330
346
|
│ ├── index.ts Public API (createSyncLoop, resetWatermarks)
|
|
331
347
|
│ ├── types.ts Interfaces (SyncTarget, SyncOptions, MergedEvent, OTLP types)
|
|
348
|
+
│ ├── config.ts Sync target configuration
|
|
349
|
+
│ ├── registry.ts Sync target registry
|
|
332
350
|
│ ├── loop.ts Poll loop with debounced scheduling
|
|
333
351
|
│ ├── reader.ts Batch reads from SQLite + hook/OTLP dedup
|
|
334
|
-
│ ├──
|
|
335
|
-
│ ├── watermark.ts Watermark persistence (sync-watermarks.db)
|
|
352
|
+
│ ├── watermark.ts Watermark persistence
|
|
336
353
|
│ └── post.ts HTTP POST with retry + exponential backoff
|
|
354
|
+
├── summary/
|
|
355
|
+
│ ├── index.ts Session summary public API
|
|
356
|
+
│ ├── llm.ts LLM-powered summary generation
|
|
357
|
+
│ └── loop.ts Background summary generation loop
|
|
358
|
+
├── archive/
|
|
359
|
+
│ ├── index.ts Archive public API
|
|
360
|
+
│ ├── backend.ts Archive backend interface
|
|
361
|
+
│ └── local.ts Local filesystem archive
|
|
362
|
+
├── workspaces/
|
|
363
|
+
│ ├── index.ts Workspace detection
|
|
364
|
+
│ ├── superset.ts Workspace superset logic
|
|
365
|
+
│ └── types.ts Workspace types
|
|
337
366
|
└── proxy/
|
|
338
367
|
├── server.ts API proxy — routes built from target registry
|
|
339
368
|
├── emit.ts Event emission from proxy captures
|
|
340
369
|
├── streaming.ts SSE stream accumulation (Anthropic + OpenAI)
|
|
370
|
+
├── ws-capture.ts WebSocket traffic capture
|
|
341
371
|
├── sessions.ts Proxy session tracking
|
|
342
372
|
└── formats/
|
|
343
373
|
├── types.ts Format parser interface
|
package/bin/hook-handler
CHANGED
|
File without changes
|
package/bin/mcp-server
CHANGED
|
File without changes
|
package/bin/proxy
CHANGED
|
File without changes
|
package/bin/server
CHANGED
|
File without changes
|
package/dist/api/client.d.ts
CHANGED
package/dist/api/client.js
CHANGED
|
@@ -139,6 +139,61 @@ function parseEnabledPlugins(settings) {
|
|
|
139
139
|
}
|
|
140
140
|
return result;
|
|
141
141
|
}
|
|
142
|
+
function resolvePluginVersionDir(pluginCacheDir) {
|
|
143
|
+
let entries;
|
|
144
|
+
try {
|
|
145
|
+
entries = fs.readdirSync(pluginCacheDir, { withFileTypes: true });
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name).filter((n) => n !== "unknown").sort((a, b) => {
|
|
150
|
+
const pa = a.split(".").map(Number);
|
|
151
|
+
const pb = b.split(".").map(Number);
|
|
152
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
153
|
+
const na = pa[i] ?? 0;
|
|
154
|
+
const nb = pb[i] ?? 0;
|
|
155
|
+
if (Number.isNaN(na) || Number.isNaN(nb)) return a.localeCompare(b);
|
|
156
|
+
if (na !== nb) return na - nb;
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
});
|
|
160
|
+
return dirs.length > 0 ? path.join(pluginCacheDir, dirs[dirs.length - 1]) : null;
|
|
161
|
+
}
|
|
162
|
+
function readPluginHooks(enabledPlugins) {
|
|
163
|
+
const claudeHome = path.join(os.homedir(), ".claude");
|
|
164
|
+
const results = [];
|
|
165
|
+
for (const plugin of enabledPlugins) {
|
|
166
|
+
if (!plugin.marketplace) continue;
|
|
167
|
+
const pluginCacheDir = path.join(
|
|
168
|
+
claudeHome,
|
|
169
|
+
"plugins",
|
|
170
|
+
"cache",
|
|
171
|
+
plugin.marketplace,
|
|
172
|
+
plugin.pluginName
|
|
173
|
+
);
|
|
174
|
+
const versionDir = resolvePluginVersionDir(pluginCacheDir);
|
|
175
|
+
if (!versionDir) continue;
|
|
176
|
+
const hooksJsonPath = path.join(versionDir, "hooks", "hooks.json");
|
|
177
|
+
const raw = readFileOrNull(hooksJsonPath);
|
|
178
|
+
if (!raw) continue;
|
|
179
|
+
try {
|
|
180
|
+
const parsed = JSON.parse(raw);
|
|
181
|
+
const hooksObj = parsed.hooks;
|
|
182
|
+
if (!hooksObj || typeof hooksObj !== "object" || Array.isArray(hooksObj))
|
|
183
|
+
continue;
|
|
184
|
+
const hooks = parseHooks({ hooks: hooksObj });
|
|
185
|
+
if (hooks.length > 0) {
|
|
186
|
+
results.push({
|
|
187
|
+
pluginName: plugin.pluginName,
|
|
188
|
+
marketplace: plugin.marketplace,
|
|
189
|
+
hooks
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
} catch {
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
142
197
|
function buildLayer(settingsPath, dirs, mcpJsonPath) {
|
|
143
198
|
const settings = readJsonOrNull(settingsPath);
|
|
144
199
|
const mcpFromSettings = settings ? parseMcpServers(settings) : [];
|
|
@@ -339,7 +394,16 @@ function readConfig(cwd) {
|
|
|
339
394
|
}
|
|
340
395
|
}
|
|
341
396
|
}
|
|
342
|
-
|
|
397
|
+
const pluginHooks = readPluginHooks(enabledPlugins);
|
|
398
|
+
return {
|
|
399
|
+
managed,
|
|
400
|
+
user,
|
|
401
|
+
project,
|
|
402
|
+
projectLocal,
|
|
403
|
+
instructions,
|
|
404
|
+
enabledPlugins,
|
|
405
|
+
pluginHooks
|
|
406
|
+
};
|
|
343
407
|
}
|
|
344
408
|
function writeSettings(level, patch, cwd) {
|
|
345
409
|
const root = cwd ? path.resolve(cwd) : process.cwd();
|
|
@@ -384,4 +448,4 @@ export {
|
|
|
384
448
|
writeSettings,
|
|
385
449
|
writeFile
|
|
386
450
|
};
|
|
387
|
-
//# sourceMappingURL=chunk-
|
|
451
|
+
//# sourceMappingURL=chunk-3ILOOWUF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/scanner.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ConfigLayer {\n settings: Record<string, unknown> | null;\n hooks: Array<{ event: string; matcher: string | null; type: string }>;\n mcpServers: Array<{ name: string; command: string }>;\n commands: Array<{ name: string; content: string }>;\n agents: Array<{ name: string; content: string }>;\n rules: Array<{ name: string; content: string }>;\n skills: Array<{ name: string; content: string }>;\n permissions: { allow: string[]; ask: string[]; deny: string[] };\n}\n\nexport interface PluginHooksSummary {\n pluginName: string;\n marketplace: string;\n hooks: ConfigLayer[\"hooks\"];\n}\n\nexport interface ClaudeCodeConfig {\n managed: ConfigLayer | null;\n user: ConfigLayer;\n project: ConfigLayer | null;\n projectLocal: ConfigLayer | null;\n instructions: Array<{ path: string; content: string; lineCount: number }>;\n enabledPlugins: Array<{ pluginName: string; marketplace: string }>;\n pluginHooks: PluginHooksSummary[];\n}\n\n// ---------------------------------------------------------------------------\n// File-system helpers\n// ---------------------------------------------------------------------------\n\nfunction readFileOrNull(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nfunction readJsonOrNull(filePath: string): Record<string, unknown> | null {\n const raw = readFileOrNull(filePath);\n if (raw === null) return null;\n try {\n return JSON.parse(raw) as Record<string, unknown>;\n } catch {\n return null;\n }\n}\n\nfunction readMdFiles(dir: string): Array<{ name: string; content: string }> {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const results: Array<{ name: string; content: string }> = [];\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".md\")) continue;\n const content = readFileOrNull(path.join(dir, entry.name));\n if (content === null) continue;\n results.push({ name: entry.name.replace(/\\.md$/, \"\"), content });\n }\n return results;\n}\n\nfunction readSkills(dir: string): Array<{ name: string; content: string }> {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const results: Array<{ name: string; content: string }> = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const content = readFileOrNull(path.join(dir, entry.name, \"SKILL.md\"));\n if (content !== null) {\n results.push({ name: entry.name, content });\n }\n }\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Settings parsers\n// ---------------------------------------------------------------------------\n\nfunction parseHooks(json: Record<string, unknown>): ConfigLayer[\"hooks\"] {\n const hooks: ConfigLayer[\"hooks\"] = [];\n const raw = json.hooks;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return hooks;\n for (const [event, entries] of Object.entries(\n raw as Record<string, unknown>,\n )) {\n if (!Array.isArray(entries)) continue;\n for (const entry of entries) {\n if (typeof entry !== \"object\" || entry === null) continue;\n const e = entry as Record<string, unknown>;\n const matcher = typeof e.matcher === \"string\" ? e.matcher : null;\n\n // Current format: entry has a nested `hooks` array\n if (Array.isArray(e.hooks)) {\n for (const hook of e.hooks) {\n if (typeof hook !== \"object\" || hook === null) continue;\n const h = hook as Record<string, unknown>;\n hooks.push({\n event,\n matcher,\n type: h.type === \"command\" ? \"command\" : \"script\",\n });\n }\n } else {\n // Legacy format: entry IS a hook directly\n hooks.push({\n event,\n matcher,\n type: e.command ? \"command\" : \"script\",\n });\n }\n }\n }\n return hooks;\n}\n\nfunction parseMcpServers(\n json: Record<string, unknown>,\n): ConfigLayer[\"mcpServers\"] {\n const servers: ConfigLayer[\"mcpServers\"] = [];\n const raw = json.mcpServers;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return servers;\n for (const [name, def] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof def !== \"object\" || def === null) continue;\n const s = def as Record<string, unknown>;\n servers.push({\n name,\n command: typeof s.command === \"string\" ? s.command : \"\",\n });\n }\n return servers;\n}\n\nfunction parsePermissions(\n json: Record<string, unknown>,\n): ConfigLayer[\"permissions\"] {\n const allow: string[] = [];\n const ask: string[] = [];\n const deny: string[] = [];\n\n function collectStrings(arr: unknown): string[] {\n if (!Array.isArray(arr)) return [];\n return arr.filter((v): v is string => typeof v === \"string\");\n }\n\n // Current format: permissions.allow / permissions.ask / permissions.deny\n const perms = json.permissions;\n if (perms && typeof perms === \"object\" && !Array.isArray(perms)) {\n const p = perms as Record<string, unknown>;\n allow.push(...collectStrings(p.allow));\n ask.push(...collectStrings(p.ask));\n deny.push(...collectStrings(p.deny));\n }\n\n // Legacy format: allowedTools / deniedTools (fall back if permissions empty)\n if (allow.length === 0 && deny.length === 0) {\n allow.push(...collectStrings(json.allowedTools));\n deny.push(...collectStrings(json.deniedTools));\n }\n\n return { allow, ask, deny };\n}\n\nfunction parseEnabledPlugins(\n settings: Record<string, unknown> | null,\n): Array<{ pluginName: string; marketplace: string }> {\n if (!settings) return [];\n const raw = settings.enabledPlugins;\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return [];\n const result: Array<{ pluginName: string; marketplace: string }> = [];\n for (const [key, enabled] of Object.entries(raw as Record<string, unknown>)) {\n if (!enabled) continue;\n const atIndex = key.indexOf(\"@\");\n if (atIndex > 0) {\n result.push({\n pluginName: key.slice(0, atIndex),\n marketplace: key.slice(atIndex + 1),\n });\n } else {\n result.push({ pluginName: key, marketplace: \"\" });\n }\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Plugin hook discovery\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the active version directory for a plugin in the cache.\n * Picks the highest semver directory, falling back to lexicographic sort.\n */\nfunction resolvePluginVersionDir(pluginCacheDir: string): string | null {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(pluginCacheDir, { withFileTypes: true });\n } catch {\n return null;\n }\n const dirs = entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .filter((n) => n !== \"unknown\")\n .sort((a, b) => {\n // Simple semver comparison: split on dots, compare numerically\n const pa = a.split(\".\").map(Number);\n const pb = b.split(\".\").map(Number);\n for (let i = 0; i < Math.max(pa.length, pb.length); i++) {\n const na = pa[i] ?? 0;\n const nb = pb[i] ?? 0;\n if (Number.isNaN(na) || Number.isNaN(nb)) return a.localeCompare(b);\n if (na !== nb) return na - nb;\n }\n return 0;\n });\n return dirs.length > 0\n ? path.join(pluginCacheDir, dirs[dirs.length - 1])\n : null;\n}\n\n/**\n * Read hooks from all enabled plugins' cache directories.\n */\nfunction readPluginHooks(\n enabledPlugins: Array<{ pluginName: string; marketplace: string }>,\n): PluginHooksSummary[] {\n const claudeHome = path.join(os.homedir(), \".claude\");\n const results: PluginHooksSummary[] = [];\n\n for (const plugin of enabledPlugins) {\n if (!plugin.marketplace) continue;\n const pluginCacheDir = path.join(\n claudeHome,\n \"plugins\",\n \"cache\",\n plugin.marketplace,\n plugin.pluginName,\n );\n const versionDir = resolvePluginVersionDir(pluginCacheDir);\n if (!versionDir) continue;\n\n const hooksJsonPath = path.join(versionDir, \"hooks\", \"hooks.json\");\n const raw = readFileOrNull(hooksJsonPath);\n if (!raw) continue;\n\n try {\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const hooksObj = parsed.hooks;\n if (!hooksObj || typeof hooksObj !== \"object\" || Array.isArray(hooksObj))\n continue;\n // Reuse parseHooks by wrapping in the shape it expects\n const hooks = parseHooks({ hooks: hooksObj });\n if (hooks.length > 0) {\n results.push({\n pluginName: plugin.pluginName,\n marketplace: plugin.marketplace,\n hooks,\n });\n }\n } catch {\n // Malformed hooks.json — skip\n }\n }\n\n return results;\n}\n\n// ---------------------------------------------------------------------------\n// Layer builder\n// ---------------------------------------------------------------------------\n\nfunction buildLayer(\n settingsPath: string,\n dirs: {\n commands?: string;\n agents?: string;\n rules?: string;\n skills?: string;\n },\n mcpJsonPath?: string,\n): ConfigLayer {\n const settings = readJsonOrNull(settingsPath);\n\n // MCP servers: merge settings.json and .mcp.json (settings wins on dupes)\n const mcpFromSettings = settings ? parseMcpServers(settings) : [];\n const mcpFromFile = mcpJsonPath\n ? parseMcpServers(readJsonOrNull(mcpJsonPath) ?? {})\n : [];\n const mcpNames = new Set(mcpFromSettings.map((s) => s.name));\n const mcpServers = [\n ...mcpFromSettings,\n ...mcpFromFile.filter((s) => !mcpNames.has(s.name)),\n ];\n\n return {\n settings,\n hooks: settings ? parseHooks(settings) : [],\n mcpServers,\n permissions: settings\n ? parsePermissions(settings)\n : { allow: [], ask: [], deny: [] },\n commands: dirs.commands ? readMdFiles(dirs.commands) : [],\n agents: dirs.agents ? readMdFiles(dirs.agents) : [],\n rules: dirs.rules ? readMdFiles(dirs.rules) : [],\n skills: dirs.skills ? readSkills(dirs.skills) : [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Git helpers\n// ---------------------------------------------------------------------------\n\nconst gitRootCache = new Map<string, string | null>();\n\n/**\n * Resolve the git repository root for a directory.\n * Cached per cwd for the lifetime of the process.\n */\nexport function resolveGitRoot(cwd: string): string | null {\n if (gitRootCache.has(cwd)) return gitRootCache.get(cwd)!;\n let root: string | null = null;\n try {\n root = execFileSync(\"git\", [\"-C\", cwd, \"rev-parse\", \"--show-toplevel\"], {\n encoding: \"utf-8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n } catch {\n // Not a git repo\n }\n gitRootCache.set(cwd, root);\n return root;\n}\n\n/**\n * Check if a file path is gitignored.\n */\nexport function isGitignored(filePath: string, cwd: string): boolean {\n try {\n execFileSync(\"git\", [\"-C\", cwd, \"check-ignore\", \"-q\", filePath], {\n timeout: 3000,\n stdio: [\"ignore\", \"ignore\", \"ignore\"],\n });\n return true; // exit code 0 = ignored\n } catch {\n return false; // exit code 1 = not ignored, or not a git repo\n }\n}\n\n// ---------------------------------------------------------------------------\n// Instruction discovery\n// ---------------------------------------------------------------------------\n\nfunction findPerDirectoryClaudeMd(\n root: string,\n excludePaths: Set<string>,\n): string[] {\n // Try git ls-files first (fast, respects .gitignore)\n const gitRoot = resolveGitRoot(root);\n if (gitRoot) {\n try {\n const output = execFileSync(\n \"git\",\n [\"-C\", gitRoot, \"ls-files\", \"--full-name\", \"*/CLAUDE.md\", \"CLAUDE.md\"],\n {\n encoding: \"utf-8\",\n timeout: 5000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n },\n ).trim();\n if (output) {\n return output\n .split(\"\\n\")\n .map((rel) => path.resolve(gitRoot, rel))\n .filter((p) => !excludePaths.has(p));\n }\n return [];\n } catch {\n // Fall through to find\n }\n }\n\n // Fallback: use find command\n try {\n const output = execFileSync(\n \"find\",\n [\n root,\n \"-name\",\n \"CLAUDE.md\",\n \"-not\",\n \"-path\",\n \"*/node_modules/*\",\n \"-not\",\n \"-path\",\n \"*/.git/*\",\n ],\n {\n encoding: \"utf-8\",\n timeout: 10000,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n },\n ).trim();\n if (output) {\n return output\n .split(\"\\n\")\n .map((p) => path.resolve(p))\n .filter((p) => !excludePaths.has(p));\n }\n } catch {\n // Fall through\n }\n\n return [];\n}\n\nfunction buildInstruction(\n filePath: string,\n): { path: string; content: string; lineCount: number } | null {\n const content = readFileOrNull(filePath);\n if (content === null) return null;\n return { path: filePath, content, lineCount: content.split(\"\\n\").length };\n}\n\nfunction getManagedDir(): string {\n if (process.platform === \"darwin\") {\n return \"/Library/Application Support/ClaudeCode\";\n }\n // Linux, WSL\n return \"/etc/claude-code\";\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Read all Claude Code config from the filesystem.\n * Returns structured layers (managed, user, project, projectLocal) plus\n * instruction files.\n */\nexport function readConfig(cwd?: string): ClaudeCodeConfig {\n const rawCwd = cwd ? path.resolve(cwd) : process.cwd();\n // Use git root if available so project config is found even from subdirs\n const root = resolveGitRoot(rawCwd) ?? rawCwd;\n const home = os.homedir();\n const claudeHome = path.join(home, \".claude\");\n const dotClaude = path.join(root, \".claude\");\n const managedDir = getManagedDir();\n\n // Managed (enterprise) layer\n let managed: ConfigLayer | null = null;\n const managedSettings = readJsonOrNull(\n path.join(managedDir, \"managed-settings.json\"),\n );\n if (managedSettings) {\n managed = {\n settings: managedSettings,\n hooks: parseHooks(managedSettings),\n mcpServers: parseMcpServers(managedSettings),\n permissions: parsePermissions(managedSettings),\n commands: [],\n agents: [],\n rules: [],\n skills: [],\n };\n }\n\n // User layer\n const user = buildLayer(\n path.join(claudeHome, \"settings.json\"),\n {\n commands: path.join(claudeHome, \"commands\"),\n rules: path.join(claudeHome, \"rules\"),\n skills: path.join(claudeHome, \"skills\"),\n },\n path.join(claudeHome, \".mcp.json\"),\n );\n\n // Project layer — null if no .claude directory\n let project: ConfigLayer | null = null;\n try {\n if (fs.statSync(dotClaude).isDirectory()) {\n project = buildLayer(\n path.join(dotClaude, \"settings.json\"),\n {\n commands: path.join(dotClaude, \"commands\"),\n agents: path.join(dotClaude, \"agents\"),\n rules: path.join(dotClaude, \"rules\"),\n skills: path.join(dotClaude, \"skills\"),\n },\n path.join(dotClaude, \".mcp.json\"),\n );\n }\n } catch {\n // no .claude directory\n }\n\n // Project local layer — null if no settings.local.json\n let projectLocal: ConfigLayer | null = null;\n const localSettings = readJsonOrNull(\n path.join(dotClaude, \"settings.local.json\"),\n );\n if (localSettings) {\n projectLocal = {\n settings: localSettings,\n hooks: parseHooks(localSettings),\n mcpServers: parseMcpServers(localSettings),\n permissions: parsePermissions(localSettings),\n commands: [],\n agents: [],\n rules: [],\n skills: [],\n };\n }\n\n // Instructions — fixed candidates + per-directory discovery\n const instructions: ClaudeCodeConfig[\"instructions\"] = [];\n const knownPaths = new Set<string>();\n\n const instructionCandidates = [\n path.join(managedDir, \"CLAUDE.md\"),\n path.join(claudeHome, \"CLAUDE.md\"),\n path.join(root, \"CLAUDE.md\"),\n path.join(dotClaude, \"CLAUDE.md\"),\n path.join(root, \"AGENTS.md\"),\n ];\n\n for (const p of instructionCandidates) {\n const resolved = path.resolve(p);\n const inst = buildInstruction(resolved);\n if (inst) {\n instructions.push(inst);\n knownPaths.add(resolved);\n }\n }\n\n for (const p of findPerDirectoryClaudeMd(root, knownPaths)) {\n const inst = buildInstruction(p);\n if (inst) instructions.push(inst);\n }\n\n // Enabled plugins — merge user + project, deduplicate\n const pluginSet = new Set<string>();\n const enabledPlugins: ClaudeCodeConfig[\"enabledPlugins\"] = [];\n for (const layer of [project, user]) {\n for (const p of parseEnabledPlugins(layer?.settings ?? null)) {\n const key = `${p.pluginName}@${p.marketplace}`;\n if (!pluginSet.has(key)) {\n pluginSet.add(key);\n enabledPlugins.push(p);\n }\n }\n }\n\n // Plugin hooks — scan enabled plugins' cache dirs for hooks.json\n const pluginHooks = readPluginHooks(enabledPlugins);\n\n return {\n managed,\n user,\n project,\n projectLocal,\n instructions,\n enabledPlugins,\n pluginHooks,\n };\n}\n\n/**\n * Merge-write a settings patch into the given layer's settings.json.\n */\nexport function writeSettings(\n level: \"project\" | \"projectLocal\" | \"user\",\n patch: Record<string, unknown>,\n cwd?: string,\n): void {\n const root = cwd ? path.resolve(cwd) : process.cwd();\n const home = os.homedir();\n\n let filePath: string;\n switch (level) {\n case \"project\":\n filePath = path.join(root, \".claude\", \"settings.json\");\n break;\n case \"projectLocal\":\n filePath = path.join(root, \".claude\", \"settings.local.json\");\n break;\n case \"user\":\n filePath = path.join(home, \".claude\", \"settings.json\");\n break;\n }\n\n const existing = readJsonOrNull(filePath) ?? {};\n const merged = { ...existing, ...patch };\n\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, `${JSON.stringify(merged, null, 2)}\\n`);\n}\n\n/**\n * Write a config file (command, agent, rule, or skill) to the given layer.\n */\nexport function writeFile(\n level: \"project\" | \"user\",\n type: \"command\" | \"agent\" | \"rule\" | \"skill\",\n name: string,\n content: string,\n cwd?: string,\n): void {\n const root = cwd ? path.resolve(cwd) : process.cwd();\n const home = os.homedir();\n\n const base =\n level === \"project\"\n ? path.join(root, \".claude\")\n : path.join(home, \".claude\");\n\n if (type === \"skill\") {\n const dir = path.join(base, \"skills\", name);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, \"SKILL.md\"), content);\n } else {\n const dir = path.join(base, `${type}s`);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${name}.md`), content);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
captureException
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-J3HVD4VI.js";
|
|
4
4
|
import {
|
|
5
5
|
log
|
|
6
6
|
} from "./chunk-7Q3BJMLG.js";
|
|
@@ -10,14 +10,14 @@ import {
|
|
|
10
10
|
readWatermark,
|
|
11
11
|
watermarkKey,
|
|
12
12
|
writeWatermark
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MEVW27U4.js";
|
|
14
14
|
import {
|
|
15
15
|
loadUnifiedConfig,
|
|
16
16
|
saveUnifiedConfig
|
|
17
17
|
} from "./chunk-QK5442ZP.js";
|
|
18
18
|
import {
|
|
19
19
|
getDb
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-SKZHAYNF.js";
|
|
21
21
|
|
|
22
22
|
// src/sync/config.ts
|
|
23
23
|
function loadSyncConfig() {
|
|
@@ -182,7 +182,7 @@ function createSyncLoop(opts) {
|
|
|
182
182
|
const postBatchSize = opts.postBatchSize ?? DEFAULT_POST_BATCH_SIZE;
|
|
183
183
|
const idleMs = opts.idleIntervalMs ?? DEFAULT_IDLE_MS;
|
|
184
184
|
const catchUpMs = opts.catchUpIntervalMs ?? DEFAULT_CATCHUP_MS;
|
|
185
|
-
const panopticonVersion = true ? "0.1.
|
|
185
|
+
const panopticonVersion = true ? "0.1.2+430c018" : "dev";
|
|
186
186
|
function resolveHeaders(target) {
|
|
187
187
|
const headers = { ...target.headers };
|
|
188
188
|
headers["X-Panopticon-Version"] = panopticonVersion;
|
|
@@ -425,4 +425,4 @@ export {
|
|
|
425
425
|
listTargets,
|
|
426
426
|
createSyncLoop
|
|
427
427
|
};
|
|
428
|
-
//# sourceMappingURL=chunk-
|
|
428
|
+
//# sourceMappingURL=chunk-3ZT3V7FP.js.map
|
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
import {
|
|
2
2
|
handleOtlpRequest
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-OW52TNVA.js";
|
|
4
4
|
import {
|
|
5
5
|
handleProxyRequest,
|
|
6
6
|
processHookEvent,
|
|
7
7
|
tunnelWebSocket
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NE7VBLQD.js";
|
|
9
9
|
import {
|
|
10
10
|
addTarget,
|
|
11
11
|
createSyncLoop,
|
|
12
12
|
listTargets,
|
|
13
13
|
removeTarget
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-3ZT3V7FP.js";
|
|
15
15
|
import {
|
|
16
16
|
createScannerLoop
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-OROLSIWZ.js";
|
|
18
18
|
import {
|
|
19
19
|
autoPrune,
|
|
20
20
|
pruneEstimate,
|
|
21
21
|
pruneExecute
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WXPT6KG7.js";
|
|
23
23
|
import {
|
|
24
24
|
addBreadcrumb,
|
|
25
25
|
captureException,
|
|
26
26
|
flushSentry,
|
|
27
27
|
initSentry,
|
|
28
28
|
setTag
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-J3HVD4VI.js";
|
|
30
30
|
import {
|
|
31
31
|
log
|
|
32
32
|
} from "./chunk-7Q3BJMLG.js";
|
|
33
33
|
import {
|
|
34
34
|
refreshPricing
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-FMAHQRIU.js";
|
|
36
36
|
import {
|
|
37
37
|
TABLE_SYNC_REGISTRY,
|
|
38
38
|
readWatermark,
|
|
39
39
|
resetWatermarks,
|
|
40
40
|
watermarkKey,
|
|
41
41
|
writeWatermark
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-MEVW27U4.js";
|
|
43
43
|
import {
|
|
44
44
|
loadUnifiedConfig
|
|
45
45
|
} from "./chunk-QK5442ZP.js";
|
|
@@ -53,10 +53,10 @@ import {
|
|
|
53
53
|
rawQuery,
|
|
54
54
|
search,
|
|
55
55
|
sessionTimeline
|
|
56
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-GPTBERQD.js";
|
|
57
57
|
import {
|
|
58
58
|
getDb
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-SKZHAYNF.js";
|
|
60
60
|
import {
|
|
61
61
|
config
|
|
62
62
|
} from "./chunk-K7YUPLES.js";
|
|
@@ -132,16 +132,44 @@ var EXEC = {
|
|
|
132
132
|
const target = p.target;
|
|
133
133
|
if (!target) throw new Error("target is required");
|
|
134
134
|
const db = getDb();
|
|
135
|
+
const WM_COLUMNS = {
|
|
136
|
+
messages: "wm_messages",
|
|
137
|
+
tool_calls: "wm_tool_calls",
|
|
138
|
+
scanner_turns: "wm_scanner_turns",
|
|
139
|
+
scanner_events: "wm_scanner_events",
|
|
140
|
+
hook_events: "wm_hook_events",
|
|
141
|
+
otel_logs: "wm_otel_logs",
|
|
142
|
+
otel_metrics: "wm_otel_metrics",
|
|
143
|
+
otel_spans: "wm_otel_spans"
|
|
144
|
+
};
|
|
135
145
|
const pending = {};
|
|
136
146
|
for (const desc of TABLE_SYNC_REGISTRY) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
const wmCol = WM_COLUMNS[desc.table];
|
|
148
|
+
if (desc.sessionLinked && wmCol) {
|
|
149
|
+
const total = db.prepare(`SELECT COUNT(*) as c FROM ${desc.table}`).get()?.c ?? 0;
|
|
150
|
+
const pendingCount = db.prepare(
|
|
151
|
+
`SELECT COUNT(*) as c FROM ${desc.table} t
|
|
152
|
+
LEFT JOIN target_session_sync tss
|
|
153
|
+
ON tss.session_id = t.session_id AND tss.target = ?
|
|
154
|
+
WHERE tss.${wmCol} IS NULL OR t.id > tss.${wmCol}`
|
|
155
|
+
).get(target)?.c ?? 0;
|
|
156
|
+
if (pendingCount > 0) {
|
|
157
|
+
pending[desc.table] = {
|
|
158
|
+
total,
|
|
159
|
+
synced: total - pendingCount,
|
|
160
|
+
pending: pendingCount
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
const key = watermarkKey(desc.table, target);
|
|
165
|
+
const wm = readWatermark(key);
|
|
166
|
+
const maxId = db.prepare(
|
|
167
|
+
`SELECT MAX(${desc.table === "sessions" ? "sync_seq" : "id"}) as m FROM ${desc.table}`
|
|
168
|
+
).get()?.m ?? 0;
|
|
169
|
+
const count = Math.max(0, maxId - wm);
|
|
170
|
+
if (count > 0) {
|
|
171
|
+
pending[desc.table] = { total: maxId, synced: wm, pending: count };
|
|
172
|
+
}
|
|
145
173
|
}
|
|
146
174
|
}
|
|
147
175
|
const totalPending = Object.values(pending).reduce(
|
|
@@ -444,4 +472,4 @@ export {
|
|
|
444
472
|
syncAwarePrune,
|
|
445
473
|
createUnifiedServer
|
|
446
474
|
};
|
|
447
|
-
//# sourceMappingURL=chunk-
|
|
475
|
+
//# sourceMappingURL=chunk-BKGQJ76N.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/api/routes.ts","../src/db/sync-prune.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport http from \"node:http\";\nimport { handleApiRequest } from \"./api/routes.js\";\nimport { config } from \"./config.js\";\nimport { autoPrune } from \"./db/prune.js\";\nimport { syncAwarePrune } from \"./db/sync-prune.js\";\nimport { type HookInput, processHookEvent } from \"./hooks/ingest.js\";\nimport { log } from \"./log.js\";\nimport { handleOtlpRequest } from \"./otlp/server.js\";\nimport { handleProxyRequest, tunnelWebSocket } from \"./proxy/server.js\";\nimport { createScannerLoop } from \"./scanner/index.js\";\nimport type { ScannerHandle } from \"./scanner/types.js\";\nimport {\n addBreadcrumb,\n captureException,\n flushSentry,\n initSentry,\n setTag,\n} from \"./sentry.js\";\nimport { createSyncLoop } from \"./sync/loop.js\";\nimport type { SyncHandle } from \"./sync/types.js\";\nimport { loadUnifiedConfig } from \"./unified-config.js\";\n\nfunction collectBody(req: http.IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nexport function createUnifiedServer(): http.Server {\n const server = http.createServer(async (req, res) => {\n const url = req.url ?? \"\";\n const method = req.method ?? \"\";\n\n // Health check\n if (url === \"/health\" && method === \"GET\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", port: config.port }));\n return;\n }\n\n // Hook event ingest\n if (url === \"/hooks\" && method === \"POST\") {\n try {\n const body = await collectBody(req);\n const data: HookInput = JSON.parse(body.toString(\"utf-8\"));\n addBreadcrumb(\"hooks\", `${data.hook_event_name ?? \"unknown\"} event`, {\n session_id: data.session_id,\n tool_name: data.tool_name,\n target: data.target ?? data.source,\n });\n const result = processHookEvent(data);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(result));\n } catch (err) {\n log.hooks.error(\"Hook ingest error:\", err);\n captureException(err, { component: \"hooks\" });\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"hook ingest failed\" }));\n }\n }\n return;\n }\n\n // OTLP ingest — /v1/logs, /v1/metrics, /v1/traces, or bare \"/\" (Gemini)\n if (\n method === \"POST\" &&\n (url.startsWith(\"/v1/\") || url === \"/\" || url === \"\")\n ) {\n await handleOtlpRequest(req, res);\n return;\n }\n\n // Proxy routes — /proxy/anthropic/*, /proxy/openai/*, /proxy/codex/*, /proxy/google/*\n if (url.startsWith(\"/proxy/\")) {\n if (method !== \"POST\") {\n res.writeHead(405);\n res.end();\n return;\n }\n addBreadcrumb(\"proxy\", `Proxy ${url}`);\n // Strip /proxy prefix so the proxy handler sees /anthropic/*, /openai/*, etc.\n req.url = url.slice(6);\n await handleProxyRequest(req, res);\n return;\n }\n\n // API routes — /api/tool, /api/exec\n if (url.startsWith(\"/api/\") && method === \"POST\") {\n await handleApiRequest(req, res);\n return;\n }\n\n // 404\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"not_found\" }));\n });\n\n // WebSocket upgrades for proxy routes\n server.on(\"upgrade\", (req, socket, head) => {\n const url = req.url ?? \"\";\n if (url.startsWith(\"/proxy/\")) {\n req.url = url.slice(6);\n tunnelWebSocket(req, socket, head);\n } else {\n socket.end(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n }\n });\n\n return server;\n}\n\n// When run directly, start the unified server\nconst entryScript = process.argv[1]?.replaceAll(\"\\\\\", \"/\") ?? \"\";\nif (entryScript.endsWith(\"/server.js\") || entryScript.endsWith(\"/server.ts\")) {\n const PRUNE_INTERVAL_MS = 60 * 60 * 1000; // 1 hour\n\n function runPrune(): void {\n try {\n const cfg = loadUnifiedConfig();\n addBreadcrumb(\"prune\", \"Running scheduled prune\");\n autoPrune(cfg.retention.maxAgeDays, cfg.retention.maxSizeMb);\n if (cfg.sync.targets.length > 0 && cfg.retention.syncedMaxAgeDays) {\n syncAwarePrune(cfg.sync.targets, cfg.retention);\n }\n } catch (err) {\n log.server.error(\"Prune error:\", err);\n captureException(err, { component: \"prune\" });\n }\n }\n\n const sentryActive = initSentry();\n if (sentryActive) log.server.info(\"Sentry: enabled\");\n\n const server = createUnifiedServer();\n let syncHandle: SyncHandle | null = null;\n let scannerHandle: ScannerHandle | null = null;\n let pruneTimer: ReturnType<typeof setInterval> | null = null;\n\n let takeoverAttempted = false;\n server.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") {\n if (takeoverAttempted) {\n log.server.warn(`Already running on ${config.host}:${config.port}`);\n process.exit(0);\n }\n takeoverAttempted = true;\n log.server.warn(`Port ${config.port} in use, attempting takeover...`);\n try {\n // Only kill the old panopticon server via PID file, not all\n // processes on the port (which could include Claude Code CLI)\n const pidFile = config.serverPidFile;\n const pidStr = fs.readFileSync(pidFile, \"utf-8\").trim();\n const oldPid = parseInt(pidStr, 10);\n if (oldPid && oldPid !== process.pid) {\n try {\n process.kill(oldPid, \"SIGTERM\");\n } catch {}\n }\n setTimeout(() => server.listen(config.port, config.host), 1500);\n } catch {\n log.server.warn(`Already running on ${config.host}:${config.port}`);\n process.exit(0);\n }\n return;\n }\n captureException(err, { component: \"server\" });\n throw err;\n });\n server.listen(config.port, config.host, () => {\n log.server.info(`Listening on ${config.host}:${config.port}`);\n\n const cfg = loadUnifiedConfig();\n\n // Start session file scanner first — sync is deferred until scanner\n // finishes any initial resync so we don't sync stale/partial data.\n scannerHandle = createScannerLoop({\n onReady: () => {\n if (cfg.sync.targets.length > 0) {\n log.sync.info(\n `Targets: ${cfg.sync.targets.map((t) => t.name).join(\", \")}`,\n );\n setTag(\"sync_targets\", cfg.sync.targets.length);\n syncHandle = createSyncLoop({\n targets: cfg.sync.targets,\n filter: cfg.sync.filter,\n });\n syncHandle.start();\n }\n },\n });\n scannerHandle.start();\n\n // Run prune on startup, then hourly\n runPrune();\n pruneTimer = setInterval(runPrune, PRUNE_INTERVAL_MS);\n pruneTimer.unref();\n });\n\n const shutdown = async () => {\n if (pruneTimer) clearInterval(pruneTimer);\n scannerHandle?.stop();\n syncHandle?.stop();\n await flushSentry();\n server.close();\n process.exit(0);\n };\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGHUP\", shutdown);\n}\n","/**\n * Server-side API route handler.\n *\n * Two endpoints:\n * POST /api/tool — read-only query dispatch (CLI + MCP)\n * POST /api/exec — write command dispatch (CLI only)\n */\nimport type http from \"node:http\";\nimport { refreshPricing } from \"../db/pricing.js\";\nimport { pruneEstimate, pruneExecute } from \"../db/prune.js\";\nimport {\n activitySummary,\n costBreakdown,\n dbStats,\n listPlans,\n listSessions,\n print,\n rawQuery,\n search,\n sessionTimeline,\n} from \"../db/query.js\";\nimport { getDb } from \"../db/schema.js\";\nimport { log } from \"../log.js\";\nimport { addTarget, listTargets, removeTarget } from \"../sync/config.js\";\nimport { TABLE_SYNC_REGISTRY } from \"../sync/registry.js\";\nimport type { SyncTarget } from \"../sync/types.js\";\nimport {\n readWatermark,\n resetWatermarks,\n watermarkKey,\n writeWatermark,\n} from \"../sync/watermark.js\";\n\n// ── Tool dispatch ────────────────────────────────────────────────────────────\n\ntype ToolFn = (params: Record<string, unknown>) => unknown;\n\nconst TOOLS: Record<string, ToolFn> = {\n sessions: (p) => listSessions(p as Parameters<typeof listSessions>[0]),\n timeline: (p) => sessionTimeline(p as Parameters<typeof sessionTimeline>[0]),\n costs: (p) => costBreakdown(p as Parameters<typeof costBreakdown>[0]),\n summary: (p) => activitySummary(p as Parameters<typeof activitySummary>[0]),\n plans: (p) => listPlans(p as Parameters<typeof listPlans>[0]),\n search: (p) => search(p as Parameters<typeof search>[0]),\n get: (p) => print(p as Parameters<typeof print>[0]),\n query: (p) => rawQuery((p as { sql: string }).sql),\n status: () => dbStats(),\n};\n\n// ── Exec dispatch ────────────────────────────────────────────────────────────\n\ntype ExecFn = (params: Record<string, unknown>) => unknown;\n\nconst EXEC: Record<string, ExecFn> = {\n prune: (p) => {\n const cutoffMs = p.cutoffMs as number;\n if (typeof cutoffMs !== \"number\") {\n throw new Error(\"cutoffMs is required and must be a number\");\n }\n if (p.dryRun) {\n return pruneEstimate(cutoffMs);\n }\n const result = pruneExecute(cutoffMs);\n if (p.vacuum) {\n const db = getDb();\n db.pragma(\"wal_checkpoint(TRUNCATE)\");\n db.exec(\"VACUUM\");\n }\n return result;\n },\n \"refresh-pricing\": () => refreshPricing(),\n \"sync-reset\": (p) => {\n const target = p.target as string | undefined;\n resetWatermarks(target);\n return { ok: true, target: target ?? \"all\" };\n },\n \"sync-watermark-get\": (p) => {\n const target = p.target as string;\n if (!target) throw new Error(\"target is required\");\n const table = p.table as string | undefined;\n if (table) {\n return {\n key: watermarkKey(table, target),\n value: readWatermark(watermarkKey(table, target)),\n };\n }\n // Return all watermarks for this target\n const watermarks: Record<string, number> = {};\n for (const desc of TABLE_SYNC_REGISTRY) {\n const key = watermarkKey(desc.table, target);\n watermarks[desc.table] = readWatermark(key);\n }\n return { target, watermarks };\n },\n \"sync-watermark-set\": (p) => {\n const target = p.target as string;\n const table = p.table as string;\n const value = p.value as number;\n if (!target) throw new Error(\"target is required\");\n if (!table) throw new Error(\"table is required\");\n if (typeof value !== \"number\") throw new Error(\"value must be a number\");\n const key = watermarkKey(table, target);\n writeWatermark(key, value);\n return { key, value };\n },\n \"sync-pending\": (p) => {\n const target = p.target as string;\n if (!target) throw new Error(\"target is required\");\n const db = getDb();\n\n /** Maps table name → wm column in target_session_sync. */\n const WM_COLUMNS: Record<string, string> = {\n messages: \"wm_messages\",\n tool_calls: \"wm_tool_calls\",\n scanner_turns: \"wm_scanner_turns\",\n scanner_events: \"wm_scanner_events\",\n hook_events: \"wm_hook_events\",\n otel_logs: \"wm_otel_logs\",\n otel_metrics: \"wm_otel_metrics\",\n otel_spans: \"wm_otel_spans\",\n };\n\n const pending: Record<\n string,\n { total: number; synced: number; pending: number }\n > = {};\n for (const desc of TABLE_SYNC_REGISTRY) {\n const wmCol = WM_COLUMNS[desc.table];\n if (desc.sessionLinked && wmCol) {\n // Session-linked tables: count rows beyond each session's per-session watermark,\n // plus rows belonging to sessions not yet tracked in target_session_sync.\n const total =\n (\n db.prepare(`SELECT COUNT(*) as c FROM ${desc.table}`).get() as {\n c: number;\n }\n )?.c ?? 0;\n const pendingCount =\n (\n db\n .prepare(\n `SELECT COUNT(*) as c FROM ${desc.table} t\n LEFT JOIN target_session_sync tss\n ON tss.session_id = t.session_id AND tss.target = ?\n WHERE tss.${wmCol} IS NULL OR t.id > tss.${wmCol}`,\n )\n .get(target) as { c: number }\n )?.c ?? 0;\n if (pendingCount > 0) {\n pending[desc.table] = {\n total,\n synced: total - pendingCount,\n pending: pendingCount,\n };\n }\n } else {\n // Sessions table and non-session-linked tables: use global watermark.\n const key = watermarkKey(desc.table, target);\n const wm = readWatermark(key);\n const maxId =\n (\n db\n .prepare(\n `SELECT MAX(${desc.table === \"sessions\" ? \"sync_seq\" : \"id\"}) as m FROM ${desc.table}`,\n )\n .get() as { m: number | null }\n )?.m ?? 0;\n const count = Math.max(0, maxId - wm);\n if (count > 0) {\n pending[desc.table] = { total: maxId, synced: wm, pending: count };\n }\n }\n }\n const totalPending = Object.values(pending).reduce(\n (s, v) => s + v.pending,\n 0,\n );\n return { target, totalPending, tables: pending };\n },\n \"sync-target-list\": () => {\n return { targets: listTargets() };\n },\n \"sync-target-add\": (p) => {\n const target = p as unknown as SyncTarget;\n if (!target.name) throw new Error(\"name is required\");\n if (!target.url) throw new Error(\"url is required\");\n addTarget(target);\n return { ok: true, name: target.name, url: target.url };\n },\n \"sync-target-remove\": (p) => {\n const name = p.name as string;\n if (!name) throw new Error(\"name is required\");\n const removed = removeTarget(name);\n return { ok: removed, name };\n },\n};\n\n// ── Request handler ──────────────────────────────────────────────────────────\n\nfunction collectBody(req: http.IncomingMessage): Promise<Buffer> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = [];\n req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n req.on(\"end\", () => resolve(Buffer.concat(chunks)));\n req.on(\"error\", reject);\n });\n}\n\nfunction jsonResponse(\n res: http.ServerResponse,\n status: number,\n data: unknown,\n): void {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(data));\n}\n\nexport async function handleApiRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n): Promise<void> {\n const url = req.url ?? \"\";\n\n let body: Record<string, unknown>;\n try {\n const raw = await collectBody(req);\n body = raw.length > 0 ? JSON.parse(raw.toString(\"utf-8\")) : {};\n } catch {\n jsonResponse(res, 400, { error: \"Invalid JSON body\" });\n return;\n }\n\n if (url === \"/api/tool\") {\n const name = body.name as string | undefined;\n if (!name || !(name in TOOLS)) {\n jsonResponse(res, 404, {\n error: `Unknown tool: ${name}`,\n available: Object.keys(TOOLS),\n });\n return;\n }\n try {\n const params = (body.params as Record<string, unknown>) ?? {};\n const result = TOOLS[name](params);\n jsonResponse(res, 200, result);\n } catch (err) {\n log.server.error(`API tool \"${name}\" error:`, err);\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return;\n }\n\n if (url === \"/api/exec\") {\n const command = body.command as string | undefined;\n if (!command || !(command in EXEC)) {\n jsonResponse(res, 404, {\n error: `Unknown command: ${command}`,\n available: Object.keys(EXEC),\n });\n return;\n }\n try {\n const params = (body.params as Record<string, unknown>) ?? {};\n const result = await EXEC[command](params);\n jsonResponse(res, 200, result ?? { ok: true });\n } catch (err) {\n log.server.error(`API exec \"${command}\" error:`, err);\n jsonResponse(res, 500, {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return;\n }\n\n jsonResponse(res, 404, { error: \"Unknown API endpoint\", url });\n}\n","import type { SyncTarget } from \"../sync/types.js\";\nimport { readWatermark, watermarkKey } from \"../sync/watermark.js\";\nimport type { RetentionConfig } from \"../unified-config.js\";\nimport { getDb } from \"./schema.js\";\n\nexport interface SyncPruneResult {\n hook_events: number;\n otel_logs: number;\n otel_metrics: number;\n}\n\n/**\n * Compute the minimum watermark for a table across all sync targets.\n * Returns 0 if no targets exist or any target has watermark 0 (hasn't synced yet).\n */\nexport function minWatermarkForTable(\n table: string,\n targets: SyncTarget[],\n): number {\n if (targets.length === 0) return 0;\n\n let min = Infinity;\n for (const t of targets) {\n const wm = readWatermark(watermarkKey(table, t.name));\n if (wm === 0) return 0;\n if (wm < min) min = wm;\n }\n return min === Infinity ? 0 : min;\n}\n\n/**\n * Aggressively prune rows that have been confirmed synced to ALL targets\n * and are older than `syncedMaxAgeDays`.\n *\n * No-op when:\n * - No sync targets configured\n * - `syncedMaxAgeDays` is not set\n * - Any target has watermark 0 for a given table (hasn't completed first sync)\n */\nexport function syncAwarePrune(\n targets: SyncTarget[],\n retention: RetentionConfig,\n): SyncPruneResult {\n const result: SyncPruneResult = {\n hook_events: 0,\n otel_logs: 0,\n otel_metrics: 0,\n };\n\n if (!retention.syncedMaxAgeDays || targets.length === 0) {\n return result;\n }\n\n const cutoffMs =\n Date.now() - retention.syncedMaxAgeDays * 24 * 60 * 60 * 1000;\n const cutoffNs = cutoffMs * 1_000_000;\n const db = getDb();\n\n const tx = db.transaction(() => {\n // -- hook_events --\n const hookMinWm = minWatermarkForTable(\"hook_events\", targets);\n if (hookMinWm > 0) {\n db.prepare(\n \"DELETE FROM hook_events_fts WHERE rowid IN (SELECT id FROM hook_events WHERE id <= ? AND timestamp_ms < ?)\",\n ).run(hookMinWm, cutoffMs);\n\n result.hook_events = db\n .prepare(\"DELETE FROM hook_events WHERE id <= ? AND timestamp_ms < ?\")\n .run(hookMinWm, cutoffMs).changes;\n }\n\n // -- otel_logs --\n const logsMinWm = minWatermarkForTable(\"otel_logs\", targets);\n if (logsMinWm > 0) {\n result.otel_logs = db\n .prepare(\"DELETE FROM otel_logs WHERE id <= ? AND timestamp_ns < ?\")\n .run(logsMinWm, cutoffNs).changes;\n }\n\n // -- otel_metrics --\n const metricsMinWm = minWatermarkForTable(\"otel_metrics\", targets);\n if (metricsMinWm > 0) {\n result.otel_metrics = db\n .prepare(\"DELETE FROM otel_metrics WHERE id <= ? AND timestamp_ns < ?\")\n .run(metricsMinWm, cutoffNs).changes;\n }\n\n // Session metadata (session_repositories, session_cwds) is local-only —\n // not synced to remote targets. Left to regular time/size-based pruneExecute.\n });\n\n tx();\n return result;\n}\n"],"mappings":"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","names":["collectBody","runPrune"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDb
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SKZHAYNF.js";
|
|
4
4
|
import {
|
|
5
5
|
config
|
|
6
6
|
} from "./chunk-K7YUPLES.js";
|
|
@@ -155,4 +155,4 @@ export {
|
|
|
155
155
|
refreshIfStale,
|
|
156
156
|
COST_EXPR
|
|
157
157
|
};
|
|
158
|
-
//# sourceMappingURL=chunk-
|
|
158
|
+
//# sourceMappingURL=chunk-FMAHQRIU.js.map
|