@fml-inc/panopticon 0.1.0

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