@fml-inc/fml 0.0.30 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2019 +0,0 @@
1
- import {
2
- closeDb,
3
- config,
4
- getDb,
5
- loadUnifiedConfig,
6
- readWatermark,
7
- watermarkKey
8
- } from "./chunk-NLFKYSN7.js";
9
- import "./chunk-UALBZA5X.js";
10
- import "./chunk-2CRFCFE4.js";
11
- import "./chunk-PZ5AY32C.js";
12
-
13
- // node_modules/.pnpm/@fml-inc+panopticon@0.2.2/node_modules/@fml-inc/panopticon/dist/chunk-QVK6VGCV.js
14
- import fs2 from "fs";
15
- import os from "os";
16
- import path from "path";
17
- import fs from "fs";
18
- import fs3 from "fs";
19
- import os2 from "os";
20
- import path2 from "path";
21
- import fs4 from "fs";
22
- import os3 from "os";
23
- import path3 from "path";
24
- var targets = /* @__PURE__ */ new Map();
25
- function registerTarget(adapter) {
26
- if (targets.has(adapter.id)) {
27
- throw new Error(`Target "${adapter.id}" is already registered`);
28
- }
29
- targets.set(adapter.id, adapter);
30
- }
31
- function allTargets() {
32
- return [...targets.values()];
33
- }
34
- function defaultToolCategory(_toolName) {
35
- return "";
36
- }
37
- function readNewLines(filePath, fromByteOffset) {
38
- let fd;
39
- try {
40
- fd = fs.openSync(filePath, "r");
41
- } catch {
42
- return { lines: [], newByteOffset: fromByteOffset };
43
- }
44
- try {
45
- const size = fs.fstatSync(fd).size;
46
- if (size <= fromByteOffset) {
47
- return { lines: [], newByteOffset: fromByteOffset };
48
- }
49
- const bytesToRead = size - fromByteOffset;
50
- const buf = Buffer.alloc(bytesToRead);
51
- fs.readSync(fd, buf, 0, bytesToRead, fromByteOffset);
52
- const text = buf.toString("utf-8");
53
- const lastNewline = text.lastIndexOf("\n");
54
- if (lastNewline === -1) {
55
- return { lines: [], newByteOffset: fromByteOffset };
56
- }
57
- const complete = text.slice(0, lastNewline);
58
- const lines = complete.split("\n").filter((l) => l.length > 0);
59
- const bytesConsumed = Buffer.byteLength(
60
- text.slice(0, lastNewline + 1),
61
- "utf-8"
62
- );
63
- return {
64
- lines,
65
- newByteOffset: fromByteOffset + bytesConsumed
66
- };
67
- } finally {
68
- fs.closeSync(fd);
69
- }
70
- }
71
- var CLAUDE_TOOL_CATEGORIES = {
72
- Read: "Read",
73
- read_file: "Read",
74
- ReadNotebook: "Read",
75
- Edit: "Edit",
76
- StrReplace: "Edit",
77
- MultiEdit: "Edit",
78
- Write: "Write",
79
- create_file: "Write",
80
- NotebookEdit: "Write",
81
- Bash: "Bash",
82
- Grep: "Grep",
83
- Glob: "Glob",
84
- list_dir: "Glob",
85
- Task: "Task",
86
- Agent: "Task",
87
- TaskCreate: "Task",
88
- TaskUpdate: "Task",
89
- Skill: "Tool",
90
- WebSearch: "Web",
91
- WebFetch: "Web",
92
- ToolSearch: "Web"
93
- };
94
- function claudeToolCategory(toolName) {
95
- const mapped = CLAUDE_TOOL_CATEGORIES[toolName];
96
- if (mapped) return mapped;
97
- if (toolName.startsWith("mcp__")) return "MCP";
98
- if (toolName.toLowerCase().includes("subagent")) return "Task";
99
- return defaultToolCategory(toolName);
100
- }
101
- var SYSTEM_MESSAGE_PREFIXES = [
102
- "This session is being continued",
103
- "[Request interrupted",
104
- "<task-notification>",
105
- "<local-command-",
106
- "Stop hook feedback:"
107
- ];
108
- function isSystemMessage(content) {
109
- const trimmed = content.trimStart();
110
- return SYSTEM_MESSAGE_PREFIXES.some((p) => trimmed.startsWith(p));
111
- }
112
- var CMD_NAME_RE = /<command-name>([^<]+)<\/command-name>/;
113
- var CMD_ARGS_RE = /<command-args>([^<]*)<\/command-args>/;
114
- var CMD_MSG_RE = /<command-message>([^<]+)<\/command-message>/;
115
- var CMD_STRIP_RE = /<\/?(?:command-name|command-message|command-args)>[^<]*<\/(?:command-name|command-message|command-args)>|<\/?(?:command-name|command-message|command-args)>/g;
116
- function extractCommandText(content) {
117
- const trimmed = content.replace(/^\uFEFF/, "").trimStart();
118
- if (!trimmed.startsWith("<command-message>") && !trimmed.startsWith("<command-name>")) {
119
- return [content, false];
120
- }
121
- const stripped = trimmed.replace(CMD_STRIP_RE, "");
122
- if (stripped.trim() !== "") {
123
- return [content, false];
124
- }
125
- const nameMatch = CMD_NAME_RE.exec(content);
126
- if (!nameMatch) {
127
- const msgMatch = CMD_MSG_RE.exec(content);
128
- if (msgMatch) return [`/${msgMatch[1]}`, true];
129
- return [content, false];
130
- }
131
- let name = nameMatch[1];
132
- if (!name.startsWith("/")) name = `/${name}`;
133
- const argsMatch = CMD_ARGS_RE.exec(content);
134
- const args = argsMatch?.[1]?.trim();
135
- return [args ? `${name} ${args}` : name, true];
136
- }
137
- function extractToolResultTextLength(content) {
138
- if (typeof content === "string") return content.length;
139
- if (Array.isArray(content)) {
140
- let len = 0;
141
- for (const b of content) {
142
- if (typeof b === "object" && b !== null && b.type === "text") {
143
- const text = b.text;
144
- if (typeof text === "string") len += text.length;
145
- }
146
- }
147
- return len;
148
- }
149
- return 0;
150
- }
151
- var FORK_THRESHOLD = 3;
152
- function hasDAGFork(entries) {
153
- for (let i = 1; i < entries.length; i++) {
154
- if (entries[i].parentUuid !== entries[i - 1].uuid) return true;
155
- }
156
- return false;
157
- }
158
- function detectForks(entries, sessionId) {
159
- const children = /* @__PURE__ */ new Map();
160
- const roots = [];
161
- const uuidSet = /* @__PURE__ */ new Set();
162
- for (let i = 0; i < entries.length; i++) {
163
- const e = entries[i];
164
- uuidSet.add(e.uuid);
165
- if (!e.parentUuid) {
166
- roots.push(i);
167
- } else {
168
- const kids = children.get(e.parentUuid) ?? [];
169
- kids.push(i);
170
- children.set(e.parentUuid, kids);
171
- }
172
- }
173
- if (roots.length !== 1) {
174
- return { mainDagIndices: entries.map((_, i) => i), forkBranches: [] };
175
- }
176
- for (const e of entries) {
177
- if (e.parentUuid && !uuidSet.has(e.parentUuid)) {
178
- return { mainDagIndices: entries.map((_, i) => i), forkBranches: [] };
179
- }
180
- }
181
- const forkBranches = [];
182
- function countUserTurns(startIdx) {
183
- const stack = [startIdx];
184
- let count = 0;
185
- while (stack.length > 0) {
186
- const idx = stack.pop();
187
- if (entries[idx].type === "user") count++;
188
- for (const k of children.get(entries[idx].uuid) ?? []) stack.push(k);
189
- }
190
- return count;
191
- }
192
- function walkBranch(startIdx, ownerId) {
193
- const pathIndices = [];
194
- let current = startIdx;
195
- while (current !== null) {
196
- pathIndices.push(current);
197
- const kids = children.get(entries[current].uuid) ?? [];
198
- if (kids.length === 0) {
199
- current = null;
200
- } else if (kids.length === 1) {
201
- current = kids[0];
202
- } else {
203
- const firstChildTurns = countUserTurns(kids[0]);
204
- if (firstChildTurns <= FORK_THRESHOLD) {
205
- current = kids[kids.length - 1];
206
- } else {
207
- for (let k = 1; k < kids.length; k++) {
208
- const branchIndices = walkBranch(kids[k], ownerId);
209
- forkBranches.push({
210
- parentId: ownerId,
211
- dagIndices: branchIndices
212
- });
213
- }
214
- current = kids[0];
215
- }
216
- }
217
- }
218
- return pathIndices;
219
- }
220
- const mainDagIndices = walkBranch(roots[0], sessionId);
221
- return { mainDagIndices, forkBranches };
222
- }
223
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
224
- var CLAUDE_DIR = path.join(os.homedir(), ".claude");
225
- var claude = {
226
- id: "claude",
227
- config: {
228
- dir: CLAUDE_DIR,
229
- configPath: path.join(CLAUDE_DIR, "settings.json"),
230
- configFormat: "json"
231
- },
232
- hooks: {
233
- // Claude Code uses plugin marketplace, not direct hook registration.
234
- // Marketplace setup is handled separately in the install command;
235
- // this method handles the settings.json portion only.
236
- events: [],
237
- applyInstallConfig(existing, _opts) {
238
- const settings = { ...existing };
239
- settings.extraKnownMarketplaces = settings.extraKnownMarketplaces ?? {};
240
- settings.extraKnownMarketplaces["local-plugins"] = {
241
- source: { source: "directory", path: config.marketplaceDir }
242
- };
243
- settings.enabledPlugins = settings.enabledPlugins ?? {};
244
- settings.enabledPlugins["panopticon@local-plugins"] = true;
245
- const hooks = settings.hooks;
246
- if (hooks) {
247
- for (const event of Object.keys(hooks)) {
248
- const entries = hooks[event];
249
- if (!Array.isArray(entries)) continue;
250
- hooks[event] = entries.filter(
251
- (h) => !(typeof h === "object" && h !== null && JSON.stringify(h).includes("hook-handler"))
252
- );
253
- if (hooks[event].length === 0) delete hooks[event];
254
- }
255
- if (Object.keys(hooks).length === 0) delete settings.hooks;
256
- }
257
- return settings;
258
- },
259
- removeInstallConfig(existing) {
260
- const settings = { ...existing };
261
- const marketplaces = settings.extraKnownMarketplaces;
262
- if (marketplaces) {
263
- delete marketplaces["local-plugins"];
264
- if (Object.keys(marketplaces).length === 0)
265
- delete settings.extraKnownMarketplaces;
266
- }
267
- const plugins = settings.enabledPlugins;
268
- if (plugins) {
269
- delete plugins["panopticon@local-plugins"];
270
- delete plugins["fml@local-plugins"];
271
- if (Object.keys(plugins).length === 0) delete settings.enabledPlugins;
272
- }
273
- return settings;
274
- }
275
- },
276
- shellEnv: {
277
- envVars(port, proxy) {
278
- const vars = [
279
- ["CLAUDE_CODE_ENABLE_TELEMETRY", "1"]
280
- ];
281
- if (proxy) {
282
- vars.push([
283
- "ANTHROPIC_BASE_URL",
284
- `http://localhost:${port}/proxy/anthropic`
285
- ]);
286
- }
287
- return vars;
288
- }
289
- },
290
- events: {
291
- // Claude Code already sends canonical event names
292
- eventMap: {},
293
- formatPermissionResponse({ allow, reason }) {
294
- return {
295
- hookSpecificOutput: {
296
- hookEventName: "PreToolUse",
297
- permissionDecision: allow ? "allow" : "deny",
298
- permissionDecisionReason: reason
299
- }
300
- };
301
- }
302
- },
303
- detect: {
304
- displayName: "Claude Code",
305
- isInstalled: () => fs2.existsSync(CLAUDE_DIR),
306
- isConfigured() {
307
- try {
308
- const settings = JSON.parse(
309
- fs2.readFileSync(path.join(CLAUDE_DIR, "settings.json"), "utf-8")
310
- );
311
- const plugins = settings.enabledPlugins ?? {};
312
- return !!plugins["panopticon@local-plugins"] || !!plugins["fml@local-plugins"];
313
- } catch {
314
- return false;
315
- }
316
- }
317
- },
318
- proxy: {
319
- upstreamHost: "api.anthropic.com",
320
- accumulatorType: "anthropic"
321
- },
322
- otel: {
323
- metrics: {
324
- metricNames: ["claude_code.token.usage"],
325
- aggregation: "SUM",
326
- tokenTypeAttrs: ["$.type"],
327
- modelAttrs: ["$.model"]
328
- }
329
- },
330
- ident: {
331
- modelPatterns: [/^claude-/]
332
- },
333
- scanner: {
334
- normalizeToolCategory: claudeToolCategory,
335
- discover() {
336
- const projectsDir = path.join(CLAUDE_DIR, "projects");
337
- const files = [];
338
- const safeReaddir = (d) => {
339
- try {
340
- return fs2.readdirSync(d);
341
- } catch {
342
- return [];
343
- }
344
- };
345
- const safeIsDir = (d) => {
346
- try {
347
- return fs2.statSync(d).isDirectory();
348
- } catch {
349
- return false;
350
- }
351
- };
352
- try {
353
- for (const slug of fs2.readdirSync(projectsDir)) {
354
- const slugDir = path.join(projectsDir, slug);
355
- if (!safeIsDir(slugDir)) continue;
356
- for (const entry of fs2.readdirSync(slugDir)) {
357
- const entryPath = path.join(slugDir, entry);
358
- if (entry.endsWith(".jsonl")) {
359
- files.push({ filePath: entryPath });
360
- }
361
- if (safeIsDir(entryPath)) {
362
- const subagentsDir = path.join(entryPath, "subagents");
363
- for (const sub of safeReaddir(subagentsDir)) {
364
- if (sub.endsWith(".jsonl")) {
365
- files.push({
366
- filePath: path.join(subagentsDir, sub)
367
- });
368
- }
369
- }
370
- }
371
- }
372
- }
373
- } catch {
374
- }
375
- return files;
376
- },
377
- parseFile(filePath, fromByteOffset) {
378
- const { lines, newByteOffset } = readNewLines(filePath, fromByteOffset);
379
- if (lines.length === 0) return null;
380
- const fileUuid = path.basename(filePath, ".jsonl");
381
- const isSubagentPath = filePath.includes("/subagents/");
382
- let meta;
383
- const turns = [];
384
- const events = [];
385
- const messages = [];
386
- let turnIndex = 0;
387
- let ordinal = 0;
388
- let firstPrompt;
389
- const subagentMap = /* @__PURE__ */ new Map();
390
- const orphanedToolResults = /* @__PURE__ */ new Map();
391
- const dagEntries = [];
392
- const msgLineIdx = [];
393
- const turnLineIdx = [];
394
- for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
395
- const line = lines[lineIdx];
396
- let obj;
397
- try {
398
- obj = JSON.parse(line);
399
- } catch {
400
- continue;
401
- }
402
- const type = obj.type;
403
- const sessionId = obj.sessionId;
404
- const agentId = obj.agentId;
405
- const tsMs = obj.timestamp ? new Date(obj.timestamp).getTime() : Date.now();
406
- const uuid = obj.uuid;
407
- const parentUuid = obj.parentUuid;
408
- if (uuid && (type === "user" || type === "assistant")) {
409
- dagEntries.push({
410
- uuid,
411
- parentUuid: parentUuid ?? "",
412
- type,
413
- lineIndex: lineIdx,
414
- timestampMs: tsMs
415
- });
416
- }
417
- if (!meta && sessionId) {
418
- const common = {
419
- cliVersion: obj.version,
420
- cwd: obj.cwd,
421
- startedAtMs: tsMs
422
- };
423
- if (agentId) {
424
- meta = {
425
- sessionId: `agent-${agentId}`,
426
- parentSessionId: sessionId,
427
- relationshipType: "subagent",
428
- ...common
429
- };
430
- } else if (!isSubagentPath && UUID_RE.test(fileUuid) && sessionId !== fileUuid) {
431
- meta = {
432
- sessionId: fileUuid,
433
- parentSessionId: sessionId,
434
- relationshipType: "continuation",
435
- ...common
436
- };
437
- } else {
438
- meta = { sessionId, ...common };
439
- }
440
- }
441
- const sid = meta?.sessionId ?? sessionId ?? "";
442
- if (type === "user") {
443
- const msg = obj.message;
444
- const content = msg?.content;
445
- let preview;
446
- const textParts = [];
447
- const toolResults = /* @__PURE__ */ new Map();
448
- if (typeof content === "string") {
449
- preview = content.slice(0, 200);
450
- textParts.push(content);
451
- } else if (Array.isArray(content)) {
452
- for (const block of content) {
453
- if (typeof block !== "object" || block === null) continue;
454
- const b = block;
455
- if (b.type === "text" && typeof b.text === "string") {
456
- textParts.push(b.text);
457
- } else if (b.type === "tool_result") {
458
- const textLen = extractToolResultTextLength(b.content);
459
- const raw = typeof b.content === "string" ? b.content : JSON.stringify(b.content ?? "");
460
- toolResults.set(b.tool_use_id, {
461
- contentLength: textLen,
462
- contentRaw: raw,
463
- timestampMs: tsMs
464
- });
465
- }
466
- }
467
- if (textParts.length > 0) {
468
- preview = textParts[0].slice(0, 200);
469
- }
470
- }
471
- if (!firstPrompt && preview) firstPrompt = preview;
472
- let fullContent = textParts.join("\n");
473
- const isMeta = obj.isMeta === true;
474
- const isCompact = obj.isCompactSummary === true;
475
- if (isMeta || isCompact) {
476
- } else if (fullContent.length === 0 && toolResults.size > 0) {
477
- for (const [id, result] of toolResults) {
478
- orphanedToolResults.set(id, result);
479
- }
480
- } else if (fullContent.length > 0) {
481
- const [converted, wasCommand] = extractCommandText(fullContent);
482
- if (wasCommand) {
483
- fullContent = converted;
484
- }
485
- const isSystem = isSystemMessage(fullContent);
486
- messages.push({
487
- sessionId: sid,
488
- ordinal: ordinal++,
489
- role: "user",
490
- content: fullContent,
491
- timestampMs: tsMs,
492
- hasThinking: false,
493
- hasToolUse: false,
494
- isSystem,
495
- contentLength: fullContent.length,
496
- hasContextTokens: false,
497
- hasOutputTokens: false,
498
- uuid,
499
- parentUuid: parentUuid ?? void 0,
500
- toolCalls: [],
501
- toolResults
502
- });
503
- msgLineIdx.push(lineIdx);
504
- }
505
- turns.push({
506
- sessionId: sid,
507
- turnIndex: turnIndex++,
508
- timestampMs: tsMs,
509
- role: "user",
510
- contentPreview: preview,
511
- inputTokens: 0,
512
- outputTokens: 0,
513
- cacheReadTokens: 0,
514
- cacheCreationTokens: 0,
515
- reasoningTokens: 0
516
- });
517
- turnLineIdx.push(lineIdx);
518
- }
519
- if (type === "assistant") {
520
- const msg = obj.message;
521
- const usage = msg?.usage;
522
- const model = msg?.model;
523
- if (meta && model && !meta.model) meta.model = model;
524
- const textParts = [];
525
- let hasThinking = false;
526
- let hasToolUse = false;
527
- const toolCalls = [];
528
- const content = msg?.content;
529
- if (Array.isArray(content)) {
530
- for (const block of content) {
531
- if (typeof block !== "object" || block === null) continue;
532
- const b = block;
533
- if (b.type === "text" && typeof b.text === "string") {
534
- textParts.push(b.text);
535
- }
536
- if (b.type === "thinking") {
537
- hasThinking = true;
538
- if (typeof b.thinking === "string") {
539
- textParts.push(`[Thinking]
540
- ${b.thinking}
541
- [/Thinking]`);
542
- }
543
- events.push({
544
- sessionId: sid,
545
- eventType: "thinking",
546
- timestampMs: tsMs,
547
- content: typeof b.thinking === "string" ? b.thinking.slice(0, 2e3) : void 0,
548
- metadata: {
549
- has_signature: !!b.signature
550
- }
551
- });
552
- }
553
- if (b.type === "tool_use") {
554
- hasToolUse = true;
555
- const toolName = b.name ?? "";
556
- const input = b.input;
557
- const inputJson = input ? JSON.stringify(input) : void 0;
558
- let skillName;
559
- if (toolName === "Skill" && input) {
560
- skillName = input.skill ?? input.name;
561
- }
562
- toolCalls.push({
563
- toolUseId: b.id ?? "",
564
- toolName,
565
- category: claudeToolCategory(toolName),
566
- inputJson,
567
- skillName,
568
- timestampMs: tsMs
569
- });
570
- events.push({
571
- sessionId: sid,
572
- eventType: "tool_call",
573
- timestampMs: tsMs,
574
- toolName,
575
- toolInput: inputJson?.slice(0, 1e4),
576
- metadata: { tool_use_id: b.id }
577
- });
578
- }
579
- }
580
- }
581
- const fullContent = textParts.join("\n");
582
- const inputTokens = usage?.input_tokens ?? 0;
583
- const outTokens = usage?.output_tokens ?? 0;
584
- const cacheRead = usage?.cache_read_input_tokens ?? 0;
585
- const cacheCreation = usage?.cache_creation_input_tokens ?? 0;
586
- const ctxTokens = inputTokens + cacheRead + cacheCreation;
587
- const hasCtx = inputTokens > 0 || cacheRead > 0 || cacheCreation > 0;
588
- messages.push({
589
- sessionId: sid,
590
- ordinal: ordinal++,
591
- role: "assistant",
592
- content: fullContent,
593
- timestampMs: tsMs,
594
- hasThinking,
595
- hasToolUse,
596
- isSystem: false,
597
- contentLength: fullContent.length,
598
- model,
599
- tokenUsage: usage ? JSON.stringify(usage) : void 0,
600
- contextTokens: hasCtx ? ctxTokens : void 0,
601
- outputTokens: outTokens > 0 ? outTokens : void 0,
602
- hasContextTokens: hasCtx,
603
- hasOutputTokens: outTokens > 0,
604
- uuid,
605
- parentUuid: parentUuid ?? void 0,
606
- toolCalls,
607
- toolResults: /* @__PURE__ */ new Map()
608
- });
609
- msgLineIdx.push(lineIdx);
610
- turns.push({
611
- sessionId: sid,
612
- turnIndex: turnIndex++,
613
- timestampMs: tsMs,
614
- model,
615
- role: "assistant",
616
- inputTokens,
617
- outputTokens: outTokens,
618
- cacheReadTokens: cacheRead,
619
- cacheCreationTokens: cacheCreation,
620
- reasoningTokens: 0
621
- });
622
- turnLineIdx.push(lineIdx);
623
- }
624
- if (type === "user") {
625
- const msg = obj.message;
626
- const content = msg?.content;
627
- if (Array.isArray(content)) {
628
- for (const block of content) {
629
- if (typeof block !== "object" || block === null) continue;
630
- const b = block;
631
- if (b.type === "tool_result") {
632
- const resultContent = b.content;
633
- events.push({
634
- sessionId: sid,
635
- eventType: "tool_result",
636
- timestampMs: tsMs,
637
- toolOutput: typeof resultContent === "string" ? resultContent.slice(0, 500) : void 0,
638
- metadata: {
639
- tool_use_id: b.tool_use_id,
640
- is_error: b.is_error
641
- }
642
- });
643
- }
644
- if (b.type === "image") {
645
- const src = b.source;
646
- events.push({
647
- sessionId: sid,
648
- eventType: "image",
649
- timestampMs: tsMs,
650
- metadata: {
651
- media_type: src?.media_type,
652
- source_type: src?.type
653
- }
654
- });
655
- }
656
- }
657
- }
658
- }
659
- if (type === "system") {
660
- const data = obj.data;
661
- const level = obj.level;
662
- const subtype = obj.subtype;
663
- if (data?.type === "api_error" || level === "error") {
664
- events.push({
665
- sessionId: sid,
666
- eventType: "error",
667
- timestampMs: tsMs,
668
- content: typeof data?.message === "string" ? data.message : void 0,
669
- metadata: {
670
- level,
671
- retryAttempt: data?.retryAttempt,
672
- maxRetries: data?.maxRetries,
673
- retryInMs: data?.retryInMs
674
- }
675
- });
676
- } else if (subtype === "stop_hook_summary" || level === "suggestion") {
677
- events.push({
678
- sessionId: sid,
679
- eventType: subtype ?? "system",
680
- timestampMs: tsMs,
681
- metadata: {
682
- subtype,
683
- level,
684
- hookCount: obj.hookCount,
685
- hookInfos: obj.hookInfos,
686
- stopReason: obj.stopReason,
687
- preventedContinuation: obj.preventedContinuation
688
- }
689
- });
690
- }
691
- }
692
- if (type === "file-history-snapshot") {
693
- const data = obj.data;
694
- const messageId = obj.messageId;
695
- events.push({
696
- sessionId: sid,
697
- eventType: "file_snapshot",
698
- timestampMs: tsMs,
699
- metadata: { messageId, ...data ?? {} }
700
- });
701
- }
702
- if (type === "progress") {
703
- const data = obj.data;
704
- const hookEvent = data?.hookEvent;
705
- const progressType = data?.type;
706
- if (hookEvent || data?.durationMs) {
707
- events.push({
708
- sessionId: sid,
709
- eventType: hookEvent ? `progress:${hookEvent}` : "progress",
710
- timestampMs: tsMs,
711
- toolName: data?.hookName ?? data?.toolName,
712
- metadata: {
713
- hookEvent,
714
- durationMs: data?.durationMs
715
- }
716
- });
717
- } else if (progressType === "agent_progress") {
718
- const tuid = data?.parentToolUseID ?? obj.parentToolUseID;
719
- const agentId2 = data?.agentId ?? obj.agentId;
720
- if (tuid && agentId2) {
721
- subagentMap.set(tuid, `agent-${agentId2}`);
722
- }
723
- events.push({
724
- sessionId: sid,
725
- eventType: "agent_progress",
726
- timestampMs: tsMs,
727
- metadata: {
728
- parentToolUseID: tuid,
729
- toolUseID: obj.toolUseID
730
- }
731
- });
732
- }
733
- }
734
- if (type === "queue-operation") {
735
- const operation = obj.operation;
736
- if (operation === "enqueue" && typeof obj.content === "string") {
737
- try {
738
- const qc = JSON.parse(obj.content);
739
- const tuid = qc.tool_use_id;
740
- const taskId = qc.task_id;
741
- if (tuid && taskId) {
742
- subagentMap.set(tuid, `agent-${taskId}`);
743
- }
744
- } catch {
745
- const tuidMatch = obj.content.match(
746
- /<tool-use-id>([^<]+)<\/tool-use-id>/
747
- );
748
- const taskMatch = obj.content.match(
749
- /<task-id>([^<]+)<\/task-id>/
750
- );
751
- if (tuidMatch?.[1] && taskMatch?.[1]) {
752
- subagentMap.set(tuidMatch[1], `agent-${taskMatch[1]}`);
753
- }
754
- }
755
- }
756
- events.push({
757
- sessionId: sid,
758
- eventType: `queue:${operation ?? "unknown"}`,
759
- timestampMs: tsMs,
760
- content: typeof obj.content === "string" ? obj.content.slice(0, 500) : void 0
761
- });
762
- }
763
- if (type === "last-prompt") {
764
- events.push({
765
- sessionId: sid,
766
- eventType: "last_prompt",
767
- timestampMs: tsMs,
768
- content: typeof obj.lastPrompt === "string" ? obj.lastPrompt.slice(0, 500) : void 0
769
- });
770
- }
771
- }
772
- if (meta && firstPrompt && !meta.firstPrompt)
773
- meta.firstPrompt = firstPrompt;
774
- if (subagentMap.size > 0) {
775
- for (const msg of messages) {
776
- for (const tc of msg.toolCalls) {
777
- const agentSid = subagentMap.get(tc.toolUseId);
778
- if (agentSid && (tc.category === "Task" || tc.toolName === "Agent"))
779
- tc.subagentSessionId = agentSid;
780
- }
781
- }
782
- }
783
- if (fromByteOffset > 0 && dagEntries.length > 1 && hasDAGFork(dagEntries)) {
784
- return {
785
- meta,
786
- turns: [],
787
- events: [],
788
- messages: [],
789
- newByteOffset: fromByteOffset,
790
- // don't advance watermark
791
- needsFullReparse: true
792
- };
793
- }
794
- if (fromByteOffset === 0 && meta && dagEntries.length > 1 && hasDAGFork(dagEntries)) {
795
- const { mainDagIndices, forkBranches } = detectForks(
796
- dagEntries,
797
- meta.sessionId
798
- );
799
- if (forkBranches.length > 0) {
800
- const mainLineSet = new Set(
801
- mainDagIndices.map((i) => dagEntries[i].lineIndex)
802
- );
803
- const mainMessages = messages.filter(
804
- (_, i) => mainLineSet.has(msgLineIdx[i])
805
- );
806
- const mainTurns = turns.filter(
807
- (_, i) => mainLineSet.has(turnLineIdx[i])
808
- );
809
- for (let i = 0; i < mainMessages.length; i++) {
810
- mainMessages[i] = { ...mainMessages[i], ordinal: i };
811
- }
812
- for (let i = 0; i < mainTurns.length; i++) {
813
- mainTurns[i] = { ...mainTurns[i], turnIndex: i };
814
- }
815
- const forks = [];
816
- for (const branch of forkBranches) {
817
- const branchLineSet = new Set(
818
- branch.dagIndices.map((i) => dagEntries[i].lineIndex)
819
- );
820
- const forkUuid = dagEntries[branch.dagIndices[0]].uuid;
821
- const forkSessionId = `${meta.sessionId}-${forkUuid}`;
822
- const forkStartMs = dagEntries[branch.dagIndices[0]].timestampMs;
823
- const forkMessages = messages.filter((_, i) => branchLineSet.has(msgLineIdx[i])).map((m, i) => ({
824
- ...m,
825
- sessionId: forkSessionId,
826
- ordinal: i
827
- }));
828
- const forkTurns = turns.filter((_, i) => branchLineSet.has(turnLineIdx[i])).map((t, i) => ({
829
- ...t,
830
- sessionId: forkSessionId,
831
- turnIndex: i
832
- }));
833
- forks.push({
834
- meta: {
835
- sessionId: forkSessionId,
836
- parentSessionId: branch.parentId,
837
- relationshipType: "fork",
838
- model: meta.model,
839
- cwd: meta.cwd,
840
- cliVersion: meta.cliVersion,
841
- startedAtMs: forkStartMs
842
- },
843
- turns: forkTurns,
844
- events: [],
845
- // events stay in main result
846
- messages: forkMessages,
847
- newByteOffset
848
- });
849
- }
850
- return {
851
- meta,
852
- turns: mainTurns,
853
- events,
854
- // all events stay in main
855
- messages: mainMessages,
856
- newByteOffset,
857
- forks,
858
- orphanedToolResults: orphanedToolResults.size > 0 ? orphanedToolResults : void 0
859
- };
860
- }
861
- }
862
- return {
863
- meta,
864
- turns,
865
- events,
866
- messages,
867
- newByteOffset,
868
- orphanedToolResults: orphanedToolResults.size > 0 ? orphanedToolResults : void 0
869
- };
870
- }
871
- }
872
- };
873
- registerTarget(claude);
874
- var GEMINI_TOOL_CATEGORIES = {
875
- read_file: "Read",
876
- read_many_files: "Read",
877
- write_file: "Write",
878
- edit_file: "Edit",
879
- run_shell_command: "Bash",
880
- shell: "Bash",
881
- glob: "Glob",
882
- list_directory: "Glob",
883
- grep: "Grep",
884
- search_files: "Grep",
885
- web_search: "Web"
886
- };
887
- function geminiToolCategory(toolName) {
888
- return GEMINI_TOOL_CATEGORIES[toolName] ?? defaultToolCategory(toolName);
889
- }
890
- var GEMINI_DIR = path2.join(os2.homedir(), ".gemini");
891
- var gemini = {
892
- id: "gemini",
893
- config: {
894
- dir: GEMINI_DIR,
895
- configPath: path2.join(GEMINI_DIR, "settings.json"),
896
- configFormat: "json"
897
- },
898
- hooks: {
899
- // Gemini's native event names
900
- events: ["SessionStart", "BeforeModel", "BeforeTool", "AfterTool"],
901
- applyInstallConfig(existing, opts) {
902
- const settings = { ...existing };
903
- const hookBin = path2.join(opts.pluginRoot, "bin", "hook-handler");
904
- const mcpBin = path2.join(opts.pluginRoot, "bin", "mcp-server");
905
- const hooks = structuredClone(
906
- settings.hooks ?? {}
907
- );
908
- for (const event of this.events) {
909
- hooks[event] = hooks[event] || [];
910
- hooks[event] = hooks[event].map((group) => ({
911
- ...group,
912
- hooks: (group.hooks || []).filter(
913
- (h) => !h.command?.includes("panopticon") && !h.path?.includes("panopticon")
914
- )
915
- })).filter((group) => group.hooks.length > 0);
916
- hooks[event].push({
917
- hooks: [
918
- {
919
- type: "command",
920
- command: `node ${hookBin} gemini ${opts.port}${opts.proxy ? " --proxy" : ""}`
921
- }
922
- ]
923
- });
924
- }
925
- settings.hooks = hooks;
926
- settings.mcpServers = settings.mcpServers || {};
927
- settings.mcpServers.panopticon = {
928
- command: "node",
929
- args: [mcpBin]
930
- };
931
- settings.telemetry = settings.telemetry || {};
932
- Object.assign(settings.telemetry, {
933
- enabled: true,
934
- target: "local",
935
- otlpProtocol: "http",
936
- otlpEndpoint: `http://localhost:${opts.port}`
937
- });
938
- return settings;
939
- },
940
- removeInstallConfig(existing) {
941
- const settings = { ...existing };
942
- const hooks = settings.hooks;
943
- if (hooks) {
944
- for (const event of Object.keys(hooks)) {
945
- hooks[event] = hooks[event].map((group) => ({
946
- ...group,
947
- hooks: (group.hooks || []).filter(
948
- (h) => !h.command?.includes("panopticon") && !h.path?.includes("panopticon")
949
- )
950
- })).filter((group) => group.hooks.length > 0);
951
- if (hooks[event].length === 0) delete hooks[event];
952
- }
953
- if (Object.keys(hooks).length === 0) delete settings.hooks;
954
- }
955
- delete settings.hooksConfig;
956
- const servers = settings.mcpServers;
957
- if (servers) {
958
- delete servers.panopticon;
959
- if (Object.keys(servers).length === 0) delete settings.mcpServers;
960
- }
961
- delete settings.telemetry;
962
- return settings;
963
- }
964
- },
965
- shellEnv: {
966
- envVars(port) {
967
- return [
968
- ["GEMINI_TELEMETRY_ENABLED", "true"],
969
- ["GEMINI_TELEMETRY_TARGET", "local"],
970
- ["GEMINI_TELEMETRY_USE_COLLECTOR", "true"],
971
- ["GEMINI_TELEMETRY_OTLP_ENDPOINT", `http://localhost:${port}`],
972
- ["GEMINI_TELEMETRY_OTLP_PROTOCOL", "http"],
973
- ["GEMINI_TELEMETRY_LOG_PROMPTS", "true"]
974
- ];
975
- }
976
- },
977
- events: {
978
- eventMap: {
979
- BeforeTool: "PreToolUse",
980
- AfterTool: "PostToolUse",
981
- BeforeModel: "UserPromptSubmit"
982
- },
983
- normalizePayload(data) {
984
- const messages = data.llm_request;
985
- if (messages?.messages && Array.isArray(messages.messages)) {
986
- const lastUser = [...messages.messages].reverse().find((m) => m.role === "user");
987
- if (lastUser?.content) {
988
- const text = typeof lastUser.content === "string" ? lastUser.content : Array.isArray(lastUser.content) ? lastUser.content.filter((p) => p.type === "text").map((p) => p.text).join("\n") : "";
989
- if (text) {
990
- data.user_prompt = text;
991
- }
992
- }
993
- }
994
- return data;
995
- },
996
- formatPermissionResponse({ allow, reason }) {
997
- return { decision: allow ? "allow" : "deny", reason };
998
- }
999
- },
1000
- detect: {
1001
- displayName: "Gemini CLI",
1002
- isInstalled: () => fs3.existsSync(GEMINI_DIR),
1003
- isConfigured() {
1004
- try {
1005
- const settings = JSON.parse(
1006
- fs3.readFileSync(path2.join(GEMINI_DIR, "settings.json"), "utf-8")
1007
- );
1008
- return !!settings.telemetry?.enabled;
1009
- } catch {
1010
- return false;
1011
- }
1012
- }
1013
- },
1014
- proxy: {
1015
- upstreamHost: "generativelanguage.googleapis.com",
1016
- accumulatorType: "openai"
1017
- },
1018
- otel: {
1019
- serviceName: "gemini-cli",
1020
- metrics: {
1021
- metricNames: ["gemini_cli.token.usage", "gen_ai.client.token.usage"],
1022
- aggregation: "MAX",
1023
- tokenTypeAttrs: ['$."gen_ai.token.type"'],
1024
- modelAttrs: ['$."gen_ai.response.model"']
1025
- }
1026
- },
1027
- scanner: {
1028
- normalizeToolCategory: geminiToolCategory,
1029
- discover() {
1030
- const tmpDir = path2.join(GEMINI_DIR, "tmp");
1031
- const files = [];
1032
- const safeReaddir = (d) => {
1033
- try {
1034
- return fs3.readdirSync(d);
1035
- } catch {
1036
- return [];
1037
- }
1038
- };
1039
- for (const project of safeReaddir(tmpDir)) {
1040
- const chatsDir = path2.join(tmpDir, project, "chats");
1041
- for (const entry of safeReaddir(chatsDir)) {
1042
- if (entry.startsWith("session-") && entry.endsWith(".json"))
1043
- files.push({ filePath: path2.join(chatsDir, entry) });
1044
- }
1045
- }
1046
- return files;
1047
- },
1048
- parseFile(filePath, fromByteOffset) {
1049
- let size;
1050
- try {
1051
- size = fs3.statSync(filePath).size;
1052
- } catch {
1053
- return null;
1054
- }
1055
- if (size <= fromByteOffset) return null;
1056
- let session;
1057
- try {
1058
- session = JSON.parse(fs3.readFileSync(filePath, "utf-8"));
1059
- } catch {
1060
- return null;
1061
- }
1062
- const sessionId = session.sessionId;
1063
- if (!sessionId) return null;
1064
- const messages = session.messages;
1065
- if (!messages?.length) return null;
1066
- const meta = {
1067
- sessionId,
1068
- startedAtMs: session.startTime ? new Date(session.startTime).getTime() : void 0
1069
- };
1070
- const turns = [];
1071
- const events = [];
1072
- const parsedMessages = [];
1073
- let turnIndex = 0;
1074
- let ordinal = 0;
1075
- let firstPrompt;
1076
- for (const msg of messages) {
1077
- const type = msg.type;
1078
- const timestamp = msg.timestamp;
1079
- const timestampMs = timestamp ? new Date(timestamp).getTime() : Date.now();
1080
- if (type === "user") {
1081
- const content = msg.content;
1082
- const textParts = [];
1083
- if (content) {
1084
- for (const block of content) {
1085
- if (block.text) textParts.push(block.text);
1086
- }
1087
- }
1088
- const fullContent = textParts.join("\n");
1089
- const preview = textParts[0]?.slice(0, 200);
1090
- if (!firstPrompt && preview) firstPrompt = preview;
1091
- turns.push({
1092
- sessionId,
1093
- turnIndex: turnIndex++,
1094
- timestampMs,
1095
- role: "user",
1096
- contentPreview: preview,
1097
- inputTokens: 0,
1098
- outputTokens: 0,
1099
- cacheReadTokens: 0,
1100
- cacheCreationTokens: 0,
1101
- reasoningTokens: 0
1102
- });
1103
- if (fullContent.length > 0) {
1104
- parsedMessages.push({
1105
- sessionId,
1106
- ordinal: ordinal++,
1107
- role: "user",
1108
- content: fullContent,
1109
- timestampMs,
1110
- hasThinking: false,
1111
- hasToolUse: false,
1112
- isSystem: false,
1113
- contentLength: fullContent.length,
1114
- hasContextTokens: false,
1115
- hasOutputTokens: false,
1116
- toolCalls: [],
1117
- toolResults: /* @__PURE__ */ new Map()
1118
- });
1119
- }
1120
- }
1121
- if (type === "gemini") {
1122
- const model = msg.model;
1123
- const tokens = msg.tokens;
1124
- if (model && !meta.model) meta.model = model;
1125
- const inputTokens = tokens?.input ?? 0;
1126
- const outTokens = tokens?.output ?? 0;
1127
- const cacheRead = tokens?.cached ?? 0;
1128
- const reasoning = tokens?.thoughts ?? 0;
1129
- turns.push({
1130
- sessionId,
1131
- turnIndex: turnIndex++,
1132
- timestampMs,
1133
- model,
1134
- role: "assistant",
1135
- contentPreview: typeof msg.content === "string" ? msg.content.slice(0, 200) : void 0,
1136
- inputTokens,
1137
- outputTokens: outTokens,
1138
- cacheReadTokens: cacheRead,
1139
- cacheCreationTokens: 0,
1140
- reasoningTokens: reasoning
1141
- });
1142
- const textParts = [];
1143
- if (typeof msg.content === "string") textParts.push(msg.content);
1144
- const msgToolCalls = msg.toolCalls;
1145
- const toolCalls = [];
1146
- const toolResults = /* @__PURE__ */ new Map();
1147
- if (msgToolCalls) {
1148
- for (let tcIdx = 0; tcIdx < msgToolCalls.length; tcIdx++) {
1149
- const tc = msgToolCalls[tcIdx];
1150
- const toolName = tc.name ?? tc.displayName ?? "";
1151
- const inputJson = tc.args ? JSON.stringify(tc.args) : void 0;
1152
- const toolUseId = tc.id || `${toolName}-${timestampMs}-${tcIdx}`;
1153
- toolCalls.push({
1154
- toolUseId,
1155
- toolName,
1156
- category: geminiToolCategory(toolName),
1157
- inputJson
1158
- });
1159
- const result = tc.result;
1160
- const fnResponse = result?.[0]?.functionResponse;
1161
- const responseObj = fnResponse?.response;
1162
- const output = typeof responseObj?.output === "string" ? responseObj.output : fnResponse ? JSON.stringify(fnResponse) : void 0;
1163
- if (output) {
1164
- toolResults.set(toolUseId, {
1165
- contentLength: output.length,
1166
- contentRaw: output
1167
- });
1168
- }
1169
- events.push({
1170
- sessionId,
1171
- eventType: "tool_call",
1172
- timestampMs,
1173
- toolName,
1174
- toolInput: inputJson?.slice(0, 1e3),
1175
- toolOutput: output?.slice(0, 1e3)
1176
- });
1177
- }
1178
- }
1179
- let hasThinking = false;
1180
- const thoughts = msg.thoughts;
1181
- if (thoughts) {
1182
- hasThinking = true;
1183
- for (const t of thoughts) {
1184
- const text = t.description ?? t.subject;
1185
- if (text) textParts.push(`[Thinking]
1186
- ${text}
1187
- [/Thinking]`);
1188
- events.push({
1189
- sessionId,
1190
- eventType: "reasoning",
1191
- timestampMs,
1192
- content: text?.slice(0, 500)
1193
- });
1194
- }
1195
- }
1196
- const fullContent = textParts.join("\n");
1197
- const ctxTokens = inputTokens + cacheRead;
1198
- const hasCtx = inputTokens > 0 || cacheRead > 0;
1199
- parsedMessages.push({
1200
- sessionId,
1201
- ordinal: ordinal++,
1202
- role: "assistant",
1203
- content: fullContent,
1204
- timestampMs,
1205
- hasThinking,
1206
- hasToolUse: toolCalls.length > 0,
1207
- isSystem: false,
1208
- contentLength: fullContent.length,
1209
- model,
1210
- tokenUsage: tokens ? JSON.stringify(tokens) : void 0,
1211
- contextTokens: hasCtx ? ctxTokens : void 0,
1212
- outputTokens: outTokens > 0 ? outTokens : void 0,
1213
- hasContextTokens: hasCtx,
1214
- hasOutputTokens: outTokens > 0,
1215
- toolCalls,
1216
- toolResults
1217
- });
1218
- }
1219
- if (type === "info") {
1220
- events.push({
1221
- sessionId,
1222
- eventType: "info",
1223
- timestampMs,
1224
- content: typeof msg.content === "string" ? msg.content.slice(0, 500) : void 0
1225
- });
1226
- }
1227
- }
1228
- if (firstPrompt) meta.firstPrompt = firstPrompt;
1229
- return {
1230
- meta,
1231
- turns,
1232
- events,
1233
- messages: parsedMessages,
1234
- newByteOffset: size,
1235
- absoluteIndices: true
1236
- };
1237
- }
1238
- }
1239
- };
1240
- registerTarget(gemini);
1241
- var CODEX_TOOL_CATEGORIES = {
1242
- shell_command: "Bash",
1243
- shell: "Bash",
1244
- exec_command: "Bash",
1245
- write_stdin: "Bash",
1246
- run_command: "Bash",
1247
- read_file: "Read",
1248
- write_file: "Write",
1249
- create_file: "Write",
1250
- edit_file: "Edit",
1251
- list_dir: "Glob",
1252
- grep_search: "Grep",
1253
- finder: "Grep",
1254
- spawn_agent: "Task"
1255
- };
1256
- function codexToolCategory(toolName) {
1257
- return CODEX_TOOL_CATEGORIES[toolName] ?? defaultToolCategory(toolName);
1258
- }
1259
- var CODEX_SYSTEM_PREFIXES = [
1260
- "# AGENTS.md",
1261
- "<environment_context>",
1262
- "<INSTRUCTIONS>",
1263
- "<subagent_notification>"
1264
- ];
1265
- function isCodexSystemMessage(content) {
1266
- return CODEX_SYSTEM_PREFIXES.some((p) => content.startsWith(p));
1267
- }
1268
- var CODEX_DIR = path3.join(os3.homedir(), ".codex");
1269
- var CODEX_HOOKS_JSON = path3.join(CODEX_DIR, "hooks.json");
1270
- var HOOK_EVENTS = [
1271
- "SessionStart",
1272
- "UserPromptSubmit",
1273
- "PreToolUse",
1274
- "PostToolUse",
1275
- "Stop"
1276
- ];
1277
- function readHooksJson() {
1278
- try {
1279
- return JSON.parse(fs4.readFileSync(CODEX_HOOKS_JSON, "utf-8"));
1280
- } catch {
1281
- return {};
1282
- }
1283
- }
1284
- function writeHooksJson(data) {
1285
- fs4.mkdirSync(CODEX_DIR, { recursive: true });
1286
- fs4.writeFileSync(CODEX_HOOKS_JSON, `${JSON.stringify(data, null, 2)}
1287
- `);
1288
- }
1289
- var codex = {
1290
- id: "codex",
1291
- config: {
1292
- dir: CODEX_DIR,
1293
- configPath: path3.join(CODEX_DIR, "config.toml"),
1294
- configFormat: "toml"
1295
- },
1296
- hooks: {
1297
- events: HOOK_EVENTS,
1298
- applyInstallConfig(existing, opts) {
1299
- const codexConfig = { ...existing };
1300
- const hookBin = path3.join(opts.pluginRoot, "bin", "hook-handler");
1301
- const mcpBin = path3.join(opts.pluginRoot, "bin", "mcp-server");
1302
- codexConfig.features = codexConfig.features ?? {};
1303
- codexConfig.features.codex_hooks = true;
1304
- codexConfig.suppress_unstable_features_warning = true;
1305
- delete codexConfig.hooks;
1306
- const hooksFile = readHooksJson();
1307
- const hooks = hooksFile.hooks ?? {};
1308
- const proxyFlag = opts.proxy ? " --proxy" : "";
1309
- const command = `node ${hookBin} codex ${opts.port}${proxyFlag}`;
1310
- for (const event of HOOK_EVENTS) {
1311
- const groups = hooks[event] ?? [];
1312
- hooks[event] = groups.filter((g) => {
1313
- const h = g.hooks ?? [];
1314
- return !h.some(
1315
- (entry) => entry.command?.includes("panopticon")
1316
- );
1317
- });
1318
- hooks[event].push({
1319
- hooks: [{ type: "command", command, timeout: 10 }]
1320
- });
1321
- }
1322
- hooksFile.hooks = hooks;
1323
- writeHooksJson(hooksFile);
1324
- if (opts.proxy) {
1325
- codexConfig.openai_base_url = `http://localhost:${opts.port}/proxy/codex`;
1326
- } else {
1327
- delete codexConfig.openai_base_url;
1328
- }
1329
- delete codexConfig.telemetry;
1330
- const endpoint = `http://localhost:${opts.port}`;
1331
- const exporterConfig = { endpoint, protocol: "binary" };
1332
- codexConfig.otel = {
1333
- ...codexConfig.otel ?? {},
1334
- log_user_prompt: true,
1335
- exporter: { "otlp-http": exporterConfig },
1336
- trace_exporter: { "otlp-http": exporterConfig },
1337
- metrics_exporter: { "otlp-http": exporterConfig }
1338
- };
1339
- const mcpServers = codexConfig.mcp_servers ?? {};
1340
- mcpServers.panopticon = { command: "node", args: [mcpBin] };
1341
- codexConfig.mcp_servers = mcpServers;
1342
- return codexConfig;
1343
- },
1344
- removeInstallConfig(existing) {
1345
- const cfg = { ...existing };
1346
- const features = cfg.features;
1347
- if (features) {
1348
- delete features.codex_hooks;
1349
- if (Object.keys(features).length === 0) delete cfg.features;
1350
- }
1351
- delete cfg.suppress_unstable_features_warning;
1352
- delete cfg.hooks;
1353
- const hooksFile = readHooksJson();
1354
- const hooks = hooksFile.hooks;
1355
- if (hooks) {
1356
- for (const event of Object.keys(hooks)) {
1357
- hooks[event] = hooks[event].filter((g) => {
1358
- const h = g.hooks ?? [];
1359
- return !h.some(
1360
- (entry) => entry.command?.includes("panopticon")
1361
- );
1362
- });
1363
- if (hooks[event].length === 0) delete hooks[event];
1364
- }
1365
- if (Object.keys(hooks).length === 0) {
1366
- try {
1367
- fs4.unlinkSync(CODEX_HOOKS_JSON);
1368
- } catch {
1369
- }
1370
- } else {
1371
- hooksFile.hooks = hooks;
1372
- writeHooksJson(hooksFile);
1373
- }
1374
- }
1375
- if (typeof cfg.openai_base_url === "string" && cfg.openai_base_url.includes("panopticon")) {
1376
- delete cfg.openai_base_url;
1377
- }
1378
- delete cfg.telemetry;
1379
- const otel = cfg.otel;
1380
- if (otel) {
1381
- delete otel.log_user_prompt;
1382
- delete otel.exporter;
1383
- delete otel.trace_exporter;
1384
- delete otel.metrics_exporter;
1385
- if (Object.keys(otel).length === 0) delete cfg.otel;
1386
- }
1387
- const servers = cfg.mcp_servers;
1388
- if (servers) {
1389
- delete servers.panopticon;
1390
- if (Object.keys(servers).length === 0) delete cfg.mcp_servers;
1391
- }
1392
- return cfg;
1393
- }
1394
- },
1395
- shellEnv: {
1396
- // Codex CLI reads its config from TOML, no shell env vars needed
1397
- envVars() {
1398
- return [];
1399
- }
1400
- },
1401
- events: {
1402
- // Codex uses snake_case but the hook handler already accepts both cases;
1403
- // no mapping needed since ingest.ts normalizes at storage time
1404
- eventMap: {},
1405
- formatPermissionResponse({ allow, reason }) {
1406
- return {
1407
- hookSpecificOutput: {
1408
- hookEventName: "PreToolUse",
1409
- permissionDecision: allow ? "allow" : "deny",
1410
- permissionDecisionReason: reason
1411
- }
1412
- };
1413
- }
1414
- },
1415
- detect: {
1416
- displayName: "Codex CLI",
1417
- isInstalled: () => fs4.existsSync(CODEX_DIR),
1418
- isConfigured() {
1419
- try {
1420
- const content = fs4.readFileSync(CODEX_HOOKS_JSON, "utf-8");
1421
- return content.includes("panopticon");
1422
- } catch {
1423
- return false;
1424
- }
1425
- }
1426
- },
1427
- otel: {
1428
- serviceName: "codex_cli_rs",
1429
- metrics: {
1430
- metricNames: ["codex.turn.token_usage"],
1431
- aggregation: "SUM",
1432
- tokenTypeAttrs: ["$.token_type"],
1433
- modelAttrs: ["$.model"],
1434
- tokenTypeMap: {
1435
- cached_input: "cacheRead",
1436
- reasoning_output: "output"
1437
- },
1438
- excludeTokenTypes: ["total"]
1439
- },
1440
- logFields: {
1441
- eventTypeExprs: ["body", `json_extract(attributes, '$."event.name"')`],
1442
- timestampMsExprs: [
1443
- "CAST(timestamp_ns / 1000000 AS INTEGER)",
1444
- `CAST(strftime('%s', json_extract(attributes, '$."event.timestamp"')) AS INTEGER) * 1000`
1445
- ]
1446
- }
1447
- },
1448
- ident: {
1449
- modelPatterns: [/^(gpt-|o[1-9]|chatgpt-)/]
1450
- },
1451
- proxy: {
1452
- upstreamHost(headers) {
1453
- const auth = headers.authorization ?? "";
1454
- return auth.startsWith("Bearer eyJ") ? "chatgpt.com" : "api.openai.com";
1455
- },
1456
- rewritePath(requestPath, headers) {
1457
- const auth = headers.authorization ?? "";
1458
- const isChatGptOAuth = auth.startsWith("Bearer eyJ");
1459
- return isChatGptOAuth ? `/backend-api/codex${requestPath}` : `/v1${requestPath}`;
1460
- },
1461
- accumulatorType: "openai"
1462
- },
1463
- scanner: {
1464
- normalizeToolCategory: codexToolCategory,
1465
- discover() {
1466
- const sessionsDir = path3.join(CODEX_DIR, "sessions");
1467
- const files = [];
1468
- const safeReaddir = (d) => {
1469
- try {
1470
- return fs4.readdirSync(d);
1471
- } catch {
1472
- return [];
1473
- }
1474
- };
1475
- for (const year of safeReaddir(sessionsDir)) {
1476
- for (const month of safeReaddir(path3.join(sessionsDir, year))) {
1477
- for (const day of safeReaddir(path3.join(sessionsDir, year, month))) {
1478
- const dayDir = path3.join(sessionsDir, year, month, day);
1479
- for (const entry of safeReaddir(dayDir)) {
1480
- if (entry.endsWith(".jsonl"))
1481
- files.push({ filePath: path3.join(dayDir, entry) });
1482
- }
1483
- }
1484
- }
1485
- }
1486
- return files;
1487
- },
1488
- parseFile(filePath, fromByteOffset) {
1489
- const { lines, newByteOffset } = readNewLines(filePath, fromByteOffset);
1490
- if (lines.length === 0) return null;
1491
- let meta;
1492
- const turns = [];
1493
- const events = [];
1494
- const messages = [];
1495
- let turnIndex = 0;
1496
- let ordinal = 0;
1497
- let currentModel;
1498
- let firstPrompt;
1499
- let pendingToolCalls = [];
1500
- let pendingAssistantContent = "";
1501
- const toolResultsByCallId = /* @__PURE__ */ new Map();
1502
- for (const line of lines) {
1503
- let obj;
1504
- try {
1505
- obj = JSON.parse(line);
1506
- } catch {
1507
- continue;
1508
- }
1509
- const type = obj.type;
1510
- const timestamp = obj.timestamp;
1511
- const tsMs = timestamp ? new Date(timestamp).getTime() : Date.now();
1512
- const payload = obj.payload;
1513
- if (type === "session_meta" && payload) {
1514
- meta = {
1515
- sessionId: payload.id,
1516
- cwd: payload.cwd,
1517
- cliVersion: payload.cli_version,
1518
- startedAtMs: payload.timestamp ? new Date(payload.timestamp).getTime() : tsMs
1519
- };
1520
- }
1521
- if (type === "turn_context" && payload) {
1522
- currentModel = payload.model;
1523
- if (meta && currentModel && !meta.model) meta.model = currentModel;
1524
- }
1525
- const sid = meta?.sessionId ?? "";
1526
- if (type === "event_msg" && payload) {
1527
- const eventType = payload.type;
1528
- if (eventType === "user_message") {
1529
- const message = payload.message;
1530
- if (!firstPrompt && message) firstPrompt = message.slice(0, 200);
1531
- turns.push({
1532
- sessionId: sid,
1533
- turnIndex: turnIndex++,
1534
- timestampMs: tsMs,
1535
- model: currentModel,
1536
- role: "user",
1537
- contentPreview: message?.slice(0, 200),
1538
- inputTokens: 0,
1539
- outputTokens: 0,
1540
- cacheReadTokens: 0,
1541
- cacheCreationTokens: 0,
1542
- reasoningTokens: 0
1543
- });
1544
- if (message && !isCodexSystemMessage(message)) {
1545
- messages.push({
1546
- sessionId: sid,
1547
- ordinal: ordinal++,
1548
- role: "user",
1549
- content: message,
1550
- timestampMs: tsMs,
1551
- hasThinking: false,
1552
- hasToolUse: false,
1553
- isSystem: false,
1554
- contentLength: message.length,
1555
- hasContextTokens: false,
1556
- hasOutputTokens: false,
1557
- toolCalls: [],
1558
- toolResults: /* @__PURE__ */ new Map()
1559
- });
1560
- }
1561
- }
1562
- if (eventType === "token_count") {
1563
- const info = payload.info;
1564
- if (!info) continue;
1565
- const lastUsage = info.last_token_usage;
1566
- if (!lastUsage) continue;
1567
- const inputTokens = lastUsage.input_tokens ?? 0;
1568
- const outTokens = lastUsage.output_tokens ?? 0;
1569
- const cacheRead = lastUsage.cached_input_tokens ?? 0;
1570
- const reasoning = lastUsage.reasoning_output_tokens ?? 0;
1571
- const ctxTokens = inputTokens + cacheRead;
1572
- const hasCtx = inputTokens > 0 || cacheRead > 0;
1573
- turns.push({
1574
- sessionId: sid,
1575
- turnIndex: turnIndex++,
1576
- timestampMs: tsMs,
1577
- model: currentModel,
1578
- role: "assistant",
1579
- inputTokens,
1580
- outputTokens: outTokens,
1581
- cacheReadTokens: cacheRead,
1582
- cacheCreationTokens: 0,
1583
- reasoningTokens: reasoning
1584
- });
1585
- const toolResults = /* @__PURE__ */ new Map();
1586
- for (const tc of pendingToolCalls) {
1587
- const result = toolResultsByCallId.get(tc.toolUseId);
1588
- if (result) toolResults.set(tc.toolUseId, result);
1589
- }
1590
- messages.push({
1591
- sessionId: sid,
1592
- ordinal: ordinal++,
1593
- role: "assistant",
1594
- content: pendingAssistantContent,
1595
- timestampMs: tsMs,
1596
- hasThinking: false,
1597
- hasToolUse: pendingToolCalls.length > 0,
1598
- isSystem: false,
1599
- contentLength: pendingAssistantContent.length,
1600
- model: currentModel,
1601
- tokenUsage: JSON.stringify(lastUsage),
1602
- contextTokens: hasCtx ? ctxTokens : void 0,
1603
- outputTokens: outTokens > 0 ? outTokens : void 0,
1604
- hasContextTokens: hasCtx,
1605
- hasOutputTokens: outTokens > 0,
1606
- toolCalls: pendingToolCalls,
1607
- toolResults
1608
- });
1609
- pendingToolCalls = [];
1610
- pendingAssistantContent = "";
1611
- }
1612
- if (eventType === "agent_message") {
1613
- events.push({
1614
- sessionId: sid,
1615
- eventType: "agent_message",
1616
- timestampMs: tsMs,
1617
- content: payload.message?.slice(0, 500),
1618
- metadata: { phase: payload.phase }
1619
- });
1620
- }
1621
- }
1622
- if (type === "response_item") {
1623
- const p = obj.payload;
1624
- const itemType = p?.type;
1625
- if (itemType === "message" || itemType === "text") {
1626
- const text = p?.text ?? p?.content ?? p?.output_text;
1627
- if (text) {
1628
- pendingAssistantContent += (pendingAssistantContent ? "\n" : "") + text;
1629
- }
1630
- }
1631
- if (itemType === "function_call") {
1632
- const toolName = p?.name ?? "";
1633
- const callId = p?.call_id ?? "";
1634
- const inputJson = typeof p?.arguments === "string" ? p.arguments : JSON.stringify(p?.arguments);
1635
- pendingToolCalls.push({
1636
- toolUseId: callId,
1637
- toolName,
1638
- category: codexToolCategory(toolName),
1639
- inputJson
1640
- });
1641
- events.push({
1642
- sessionId: sid,
1643
- eventType: "tool_call",
1644
- timestampMs: tsMs,
1645
- toolName,
1646
- toolInput: inputJson?.slice(0, 1e3),
1647
- metadata: { call_id: callId }
1648
- });
1649
- }
1650
- if (itemType === "function_call_output") {
1651
- const callId = p?.call_id ?? "";
1652
- const output = typeof p?.output === "string" ? p.output : void 0;
1653
- if (output) {
1654
- toolResultsByCallId.set(callId, {
1655
- contentLength: output.length,
1656
- contentRaw: output
1657
- });
1658
- }
1659
- events.push({
1660
- sessionId: sid,
1661
- eventType: "tool_result",
1662
- timestampMs: tsMs,
1663
- toolOutput: output?.slice(0, 1e3),
1664
- metadata: { call_id: callId }
1665
- });
1666
- }
1667
- }
1668
- }
1669
- if (meta && firstPrompt && !meta.firstPrompt)
1670
- meta.firstPrompt = firstPrompt;
1671
- return { meta, turns, events, messages, newByteOffset };
1672
- }
1673
- }
1674
- };
1675
- registerTarget(codex);
1676
-
1677
- // node_modules/.pnpm/@fml-inc+panopticon@0.2.2/node_modules/@fml-inc/panopticon/dist/chunk-SHXOVUQD.js
1678
- function dbStats() {
1679
- const db = getDb();
1680
- const logs = db.prepare("SELECT COUNT(*) as count FROM otel_logs").get();
1681
- const metrics = db.prepare("SELECT COUNT(*) as count FROM otel_metrics").get();
1682
- const hooks = db.prepare("SELECT COUNT(*) as count FROM hook_events").get();
1683
- const spans = db.prepare("SELECT COUNT(*) as count FROM otel_spans").get();
1684
- const sessions = db.prepare("SELECT COUNT(*) as count FROM sessions").get();
1685
- const scannerTurns = db.prepare("SELECT COUNT(*) as count FROM scanner_turns").get();
1686
- const scannerEvents = db.prepare("SELECT COUNT(*) as count FROM scanner_events").get();
1687
- const messages = db.prepare("SELECT COUNT(*) as count FROM messages").get();
1688
- const toolCalls = db.prepare("SELECT COUNT(*) as count FROM tool_calls").get();
1689
- return {
1690
- sessions: sessions.count,
1691
- messages: messages.count,
1692
- tool_calls: toolCalls.count,
1693
- scanner_turns: scannerTurns.count,
1694
- scanner_events: scannerEvents.count,
1695
- hook_events: hooks.count,
1696
- otel_logs: logs.count,
1697
- otel_metrics: metrics.count,
1698
- otel_spans: spans.count
1699
- };
1700
- }
1701
-
1702
- // node_modules/.pnpm/@fml-inc+panopticon@0.2.2/node_modules/@fml-inc/panopticon/dist/chunk-N4KQ56HJ.js
1703
- import fs5 from "fs";
1704
- import http from "http";
1705
- import os4 from "os";
1706
- import path4 from "path";
1707
- function checkHealth(port, host) {
1708
- return new Promise((resolve) => {
1709
- const req = http.request(
1710
- { hostname: host, port, path: "/health", method: "GET", timeout: 2e3 },
1711
- (res) => {
1712
- res.resume();
1713
- resolve(res.statusCode === 200);
1714
- }
1715
- );
1716
- req.on("error", () => resolve(false));
1717
- req.on("timeout", () => {
1718
- req.destroy();
1719
- resolve(false);
1720
- });
1721
- req.end();
1722
- });
1723
- }
1724
- async function doctor() {
1725
- const checks = [];
1726
- const isSandbox = process.env.SANDBOX !== void 0 || fs5.existsSync(path4.join(os4.homedir(), ".sandbox-home"));
1727
- if (!fs5.existsSync(config.dbPath)) {
1728
- checks.push({
1729
- label: "Database",
1730
- status: "fail",
1731
- detail: `Not found at ${config.dbPath}. Run install to initialize.`
1732
- });
1733
- } else {
1734
- try {
1735
- const stats = dbStats();
1736
- const total = stats.hook_events + stats.otel_logs + stats.otel_metrics;
1737
- checks.push({
1738
- label: "Database",
1739
- status: "ok",
1740
- detail: `${total} rows (${stats.hook_events} hooks, ${stats.otel_logs} logs, ${stats.otel_metrics} metrics)`
1741
- });
1742
- closeDb();
1743
- } catch (err) {
1744
- checks.push({
1745
- label: "Database",
1746
- status: "fail",
1747
- detail: `Error: ${err instanceof Error ? err.message : String(err)}`
1748
- });
1749
- }
1750
- }
1751
- const serverUp = await checkHealth(config.port, "127.0.0.1");
1752
- if (serverUp) {
1753
- checks.push({
1754
- label: "Server",
1755
- status: "ok",
1756
- detail: `Listening on 127.0.0.1:${config.port}`
1757
- });
1758
- } else {
1759
- checks.push({
1760
- label: "Server",
1761
- status: "warn",
1762
- detail: `Not responding on port ${config.port}`
1763
- });
1764
- }
1765
- const shellRc = path4.join(
1766
- os4.homedir(),
1767
- process.env.SHELL?.includes("zsh") ? ".zshrc" : ".bashrc"
1768
- );
1769
- const rcContent = fs5.existsSync(shellRc) ? fs5.readFileSync(shellRc, "utf-8") : "";
1770
- const hasTelemetry = rcContent.includes("CLAUDE_CODE_ENABLE_TELEMETRY");
1771
- const hasEndpoint = rcContent.includes("OTEL_EXPORTER_OTLP_ENDPOINT");
1772
- if (hasTelemetry && hasEndpoint) {
1773
- checks.push({
1774
- label: "Shell Env",
1775
- status: "ok",
1776
- detail: `Telemetry configured in ${path4.basename(shellRc)}`
1777
- });
1778
- } else if (hasTelemetry || hasEndpoint) {
1779
- checks.push({
1780
- label: "Shell Env",
1781
- status: "warn",
1782
- detail: `Partial config in ${path4.basename(shellRc)} \u2014 re-run install`
1783
- });
1784
- } else {
1785
- checks.push({
1786
- label: "Shell Env",
1787
- status: "warn",
1788
- detail: `Not configured. Run install to set up telemetry.`
1789
- });
1790
- }
1791
- const tools = [];
1792
- for (const t of allTargets()) {
1793
- if (t.detect.isInstalled()) {
1794
- tools.push({
1795
- name: t.detect.displayName,
1796
- dir: t.config.dir,
1797
- configured: t.detect.isConfigured()
1798
- });
1799
- }
1800
- }
1801
- if (tools.length === 0) {
1802
- checks.push({
1803
- label: "Tools",
1804
- status: "warn",
1805
- detail: "No supported coding tools found"
1806
- });
1807
- } else {
1808
- const configured = tools.filter((t) => t.configured);
1809
- const unconfigured = tools.filter((t) => !t.configured);
1810
- if (unconfigured.length === 0) {
1811
- checks.push({
1812
- label: "Tools",
1813
- status: "ok",
1814
- detail: configured.map((t) => t.name).join(", ")
1815
- });
1816
- } else {
1817
- const detail = tools.map((t) => `${t.name} ${t.configured ? "\u2713" : "(not configured)"}`).join(", ");
1818
- checks.push({
1819
- label: "Tools",
1820
- status: "warn",
1821
- detail: `${detail} \u2014 re-run install`
1822
- });
1823
- }
1824
- }
1825
- if (fs5.existsSync(config.dbPath)) {
1826
- try {
1827
- const db = getDb();
1828
- const latest = db.prepare("SELECT MAX(timestamp_ms) as ts FROM hook_events").get();
1829
- closeDb();
1830
- if (latest?.ts) {
1831
- const ago = Date.now() - latest.ts;
1832
- const minutes = Math.floor(ago / 6e4);
1833
- if (minutes < 60) {
1834
- checks.push({
1835
- label: "Data Flow",
1836
- status: "ok",
1837
- detail: `Last event ${minutes}m ago`
1838
- });
1839
- } else if (minutes < 1440) {
1840
- checks.push({
1841
- label: "Data Flow",
1842
- status: "ok",
1843
- detail: `Last event ${Math.floor(minutes / 60)}h ago`
1844
- });
1845
- } else {
1846
- checks.push({
1847
- label: "Data Flow",
1848
- status: "warn",
1849
- detail: `Last event ${Math.floor(minutes / 1440)}d ago`
1850
- });
1851
- }
1852
- } else {
1853
- checks.push({
1854
- label: "Data Flow",
1855
- status: "warn",
1856
- detail: "No events recorded yet"
1857
- });
1858
- }
1859
- } catch {
1860
- }
1861
- }
1862
- try {
1863
- const cfg = loadUnifiedConfig();
1864
- const targets2 = cfg.sync.targets;
1865
- if (targets2.length === 0) {
1866
- checks.push({
1867
- label: "Sync",
1868
- status: "ok",
1869
- detail: "No targets configured"
1870
- });
1871
- } else {
1872
- const tables = ["hook_events", "otel_logs", "otel_metrics"];
1873
- const targetDetails = [];
1874
- for (const t of targets2) {
1875
- const watermarks = tables.map(
1876
- (table) => readWatermark(watermarkKey(table, t.name))
1877
- );
1878
- const minWm = Math.min(...watermarks);
1879
- const wmLabel = minWm > 0 ? `synced to #${minWm}` : "not synced yet";
1880
- targetDetails.push(`${t.name} \u2192 ${t.url} (${wmLabel})`);
1881
- }
1882
- checks.push({
1883
- label: "Sync",
1884
- status: "ok",
1885
- detail: `${targets2.length} target${targets2.length > 1 ? "s" : ""}: ${targetDetails.join("; ")}`
1886
- });
1887
- }
1888
- } catch {
1889
- checks.push({
1890
- label: "Sync",
1891
- status: "warn",
1892
- detail: "Could not read sync config"
1893
- });
1894
- }
1895
- try {
1896
- const cfg = loadUnifiedConfig();
1897
- const dsn = process.env.PANOPTICON_SENTRY_DSN ?? cfg.sentryDsn;
1898
- checks.push({
1899
- label: "Sentry",
1900
- status: dsn ? "ok" : "ok",
1901
- detail: dsn ? "Configured" : "Not configured (optional)"
1902
- });
1903
- } catch {
1904
- }
1905
- let recentEvents = [];
1906
- let recentErrors = [];
1907
- if (fs5.existsSync(config.dbPath)) {
1908
- try {
1909
- const db = getDb();
1910
- const events = db.prepare(
1911
- "SELECT event_type, tool_name, timestamp_ms FROM hook_events ORDER BY id DESC LIMIT 3"
1912
- ).all();
1913
- recentEvents = events.map((e) => ({
1914
- eventType: e.event_type,
1915
- toolName: e.tool_name,
1916
- timestamp: new Date(e.timestamp_ms).toISOString()
1917
- }));
1918
- const errors = db.prepare(
1919
- "SELECT id, body FROM otel_logs WHERE severity_text = 'ERROR' ORDER BY id DESC LIMIT 3"
1920
- ).all();
1921
- recentErrors = errors.map((e) => ({
1922
- id: e.id,
1923
- body: (e.body ?? "").slice(0, 200)
1924
- }));
1925
- closeDb();
1926
- } catch {
1927
- }
1928
- }
1929
- return {
1930
- checks,
1931
- system: {
1932
- os: `${os4.platform()} (${os4.release()} ${os4.arch()})`,
1933
- node: process.version,
1934
- sandbox: isSandbox
1935
- },
1936
- recentEvents,
1937
- recentErrors
1938
- };
1939
- }
1940
-
1941
- // node_modules/.pnpm/@fml-inc+panopticon@0.2.2/node_modules/@fml-inc/panopticon/dist/chunk-ZEC4LRKS.js
1942
- import fs6 from "fs";
1943
- import os5 from "os";
1944
- import path5 from "path";
1945
- var CLAUDE_DESKTOP_DIR = path5.join(
1946
- os5.homedir(),
1947
- "Library",
1948
- "Application Support",
1949
- "Claude"
1950
- );
1951
- var CLAUDE_DESKTOP_CONFIG = path5.join(
1952
- CLAUDE_DESKTOP_DIR,
1953
- "claude_desktop_config.json"
1954
- );
1955
- var claudeDesktop = {
1956
- id: "claude-desktop",
1957
- config: {
1958
- dir: CLAUDE_DESKTOP_DIR,
1959
- configPath: CLAUDE_DESKTOP_CONFIG,
1960
- configFormat: "json"
1961
- },
1962
- hooks: {
1963
- // Claude Desktop uses MCP servers, not hooks
1964
- events: [],
1965
- applyInstallConfig(existing, opts) {
1966
- const cfg = { ...existing };
1967
- const serverBin = path5.join(opts.pluginRoot, "bin", "mcp-server");
1968
- cfg.mcpServers = cfg.mcpServers ?? {};
1969
- cfg.mcpServers.panopticon = {
1970
- command: "node",
1971
- args: [serverBin]
1972
- };
1973
- return cfg;
1974
- },
1975
- removeInstallConfig(existing) {
1976
- const cfg = { ...existing };
1977
- const servers = cfg.mcpServers;
1978
- if (servers) {
1979
- delete servers.panopticon;
1980
- if (Object.keys(servers).length === 0) delete cfg.mcpServers;
1981
- }
1982
- return cfg;
1983
- }
1984
- },
1985
- shellEnv: {
1986
- envVars() {
1987
- return [];
1988
- }
1989
- },
1990
- events: {
1991
- eventMap: {},
1992
- formatPermissionResponse({ allow, reason }) {
1993
- return {
1994
- hookSpecificOutput: {
1995
- hookEventName: "PreToolUse",
1996
- permissionDecision: allow ? "allow" : "deny",
1997
- permissionDecisionReason: reason
1998
- }
1999
- };
2000
- }
2001
- },
2002
- detect: {
2003
- displayName: "Claude Desktop",
2004
- isInstalled: () => fs6.existsSync(CLAUDE_DESKTOP_DIR),
2005
- isConfigured() {
2006
- try {
2007
- const cfg = JSON.parse(fs6.readFileSync(CLAUDE_DESKTOP_CONFIG, "utf-8"));
2008
- return !!cfg.mcpServers?.panopticon;
2009
- } catch {
2010
- return false;
2011
- }
2012
- }
2013
- }
2014
- };
2015
- registerTarget(claudeDesktop);
2016
- export {
2017
- doctor
2018
- };
2019
- //# sourceMappingURL=doctor-YV6KQEGW.js.map