@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,169 @@
1
+ import {
2
+ config
3
+ } from "./chunk-K7YUPLES.js";
4
+
5
+ // src/api/client.ts
6
+ import http from "http";
7
+ function post(path, body, timeoutMs) {
8
+ return new Promise((resolve, reject) => {
9
+ const json = JSON.stringify(body);
10
+ const req = http.request(
11
+ {
12
+ hostname: config.host,
13
+ port: config.port,
14
+ path,
15
+ method: "POST",
16
+ headers: {
17
+ "Content-Type": "application/json",
18
+ "Content-Length": Buffer.byteLength(json)
19
+ },
20
+ timeout: timeoutMs
21
+ },
22
+ (res) => {
23
+ const chunks = [];
24
+ res.on("data", (chunk) => chunks.push(chunk));
25
+ res.on("end", () => {
26
+ const text = Buffer.concat(chunks).toString("utf-8");
27
+ let parsed;
28
+ try {
29
+ parsed = JSON.parse(text);
30
+ } catch {
31
+ reject(
32
+ new Error(
33
+ `Invalid JSON response from server: ${text.slice(0, 200)}`
34
+ )
35
+ );
36
+ return;
37
+ }
38
+ if (res.statusCode && res.statusCode >= 400 && typeof parsed === "object" && parsed !== null && "error" in parsed) {
39
+ reject(new Error(parsed.error));
40
+ return;
41
+ }
42
+ resolve(parsed);
43
+ });
44
+ }
45
+ );
46
+ req.on("error", (err) => {
47
+ if (err.code === "ECONNREFUSED") {
48
+ reject(
49
+ new Error(
50
+ "Panopticon server is not running. Start with: panopticon start"
51
+ )
52
+ );
53
+ return;
54
+ }
55
+ reject(err);
56
+ });
57
+ req.on("timeout", () => {
58
+ req.destroy();
59
+ reject(new Error("Request to panopticon server timed out"));
60
+ });
61
+ req.write(json);
62
+ req.end();
63
+ });
64
+ }
65
+ var TOOL_TIMEOUT = 3e4;
66
+ function callTool(name, params) {
67
+ return post("/api/tool", { name, params }, TOOL_TIMEOUT);
68
+ }
69
+ function listSessions(opts) {
70
+ return callTool(
71
+ "sessions",
72
+ opts
73
+ );
74
+ }
75
+ function sessionTimeline(opts) {
76
+ return callTool(
77
+ "timeline",
78
+ opts
79
+ );
80
+ }
81
+ function costBreakdown(opts) {
82
+ return callTool(
83
+ "costs",
84
+ opts
85
+ );
86
+ }
87
+ function activitySummary(opts) {
88
+ return callTool(
89
+ "summary",
90
+ opts
91
+ );
92
+ }
93
+ function listPlans(opts) {
94
+ return callTool("plans", opts);
95
+ }
96
+ function search(opts) {
97
+ return callTool(
98
+ "search",
99
+ opts
100
+ );
101
+ }
102
+ function print(opts) {
103
+ return callTool("get", opts);
104
+ }
105
+ function rawQuery(sql) {
106
+ return callTool("query", { sql });
107
+ }
108
+ function dbStats() {
109
+ return callTool("status");
110
+ }
111
+ var EXEC_TIMEOUT = 6e4;
112
+ function callExec(command, params) {
113
+ return post("/api/exec", { command, params }, EXEC_TIMEOUT);
114
+ }
115
+ function pruneEstimate(cutoffMs) {
116
+ return callExec("prune", { cutoffMs, dryRun: true });
117
+ }
118
+ function pruneExecute(cutoffMs, opts) {
119
+ return callExec("prune", { cutoffMs, ...opts });
120
+ }
121
+ function refreshPricing() {
122
+ return callExec("refresh-pricing");
123
+ }
124
+ function syncReset(target) {
125
+ return callExec("sync-reset", target ? { target } : {});
126
+ }
127
+ function syncWatermarkGet(target, table) {
128
+ return callExec("sync-watermark-get", { target, table });
129
+ }
130
+ function syncWatermarkSet(target, table, value) {
131
+ return callExec("sync-watermark-set", { target, table, value });
132
+ }
133
+ function syncPending(target) {
134
+ return callExec("sync-pending", { target });
135
+ }
136
+ function syncTargetList() {
137
+ return callExec("sync-target-list");
138
+ }
139
+ function syncTargetAdd(target) {
140
+ return callExec("sync-target-add", target);
141
+ }
142
+ function syncTargetRemove(name) {
143
+ return callExec("sync-target-remove", { name });
144
+ }
145
+
146
+ export {
147
+ callTool,
148
+ listSessions,
149
+ sessionTimeline,
150
+ costBreakdown,
151
+ activitySummary,
152
+ listPlans,
153
+ search,
154
+ print,
155
+ rawQuery,
156
+ dbStats,
157
+ callExec,
158
+ pruneEstimate,
159
+ pruneExecute,
160
+ refreshPricing,
161
+ syncReset,
162
+ syncWatermarkGet,
163
+ syncWatermarkSet,
164
+ syncPending,
165
+ syncTargetList,
166
+ syncTargetAdd,
167
+ syncTargetRemove
168
+ };
169
+ //# sourceMappingURL=chunk-4SM2H22C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api/client.ts"],"sourcesContent":["/**\n * HTTP client for the panopticon server API.\n *\n * Provides typed wrappers around POST /api/tool and POST /api/exec\n * so that CLI and MCP can query the server instead of opening the\n * database directly.\n */\nimport http from \"node:http\";\nimport { config } from \"../config.js\";\nimport type {\n ActivitySummaryResult,\n SearchResult,\n SessionListResult,\n SessionTimelineResult,\n SpendingResult,\n} from \"../types.js\";\n\n// ── Core transport ───────────────────────────────────────────────────────────\n\nfunction post(\n path: string,\n body: unknown,\n timeoutMs: number,\n): Promise<unknown> {\n return new Promise((resolve, reject) => {\n const json = JSON.stringify(body);\n const req = http.request(\n {\n hostname: config.host,\n port: config.port,\n path,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(json),\n },\n timeout: timeoutMs,\n },\n (res) => {\n const chunks: Buffer[] = [];\n res.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n res.on(\"end\", () => {\n const text = Buffer.concat(chunks).toString(\"utf-8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n reject(\n new Error(\n `Invalid JSON response from server: ${text.slice(0, 200)}`,\n ),\n );\n return;\n }\n if (\n res.statusCode &&\n res.statusCode >= 400 &&\n typeof parsed === \"object\" &&\n parsed !== null &&\n \"error\" in parsed\n ) {\n reject(new Error((parsed as { error: string }).error));\n return;\n }\n resolve(parsed);\n });\n },\n );\n req.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"ECONNREFUSED\") {\n reject(\n new Error(\n \"Panopticon server is not running. Start with: panopticon start\",\n ),\n );\n return;\n }\n reject(err);\n });\n req.on(\"timeout\", () => {\n req.destroy();\n reject(new Error(\"Request to panopticon server timed out\"));\n });\n req.write(json);\n req.end();\n });\n}\n\n// ── Tool calls (read-only) ──────────────────────────────────────────────────\n\nconst TOOL_TIMEOUT = 30_000;\n\nexport function callTool(\n name: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/tool\", { name, params }, TOOL_TIMEOUT);\n}\n\nexport function listSessions(opts?: {\n limit?: number;\n since?: string;\n}): Promise<SessionListResult> {\n return callTool(\n \"sessions\",\n opts as Record<string, unknown>,\n ) as Promise<SessionListResult>;\n}\n\nexport function sessionTimeline(opts: {\n sessionId: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SessionTimelineResult> {\n return callTool(\n \"timeline\",\n opts as Record<string, unknown>,\n ) as Promise<SessionTimelineResult>;\n}\n\nexport function costBreakdown(opts?: {\n since?: string;\n groupBy?: \"session\" | \"model\" | \"day\";\n}): Promise<SpendingResult> {\n return callTool(\n \"costs\",\n opts as Record<string, unknown>,\n ) as Promise<SpendingResult>;\n}\n\nexport function activitySummary(opts?: {\n since?: string;\n}): Promise<ActivitySummaryResult> {\n return callTool(\n \"summary\",\n opts as Record<string, unknown>,\n ) as Promise<ActivitySummaryResult>;\n}\n\nexport function listPlans(opts?: {\n session_id?: string;\n since?: string;\n limit?: number;\n}): Promise<unknown> {\n return callTool(\"plans\", opts as Record<string, unknown>);\n}\n\nexport function search(opts: {\n query: string;\n eventTypes?: string[];\n since?: string;\n limit?: number;\n offset?: number;\n fullPayloads?: boolean;\n}): Promise<SearchResult> {\n return callTool(\n \"search\",\n opts as Record<string, unknown>,\n ) as Promise<SearchResult>;\n}\n\nexport function print(opts: {\n source: \"hook\" | \"otel\" | \"message\";\n id: number;\n}): Promise<unknown> {\n return callTool(\"get\", opts as Record<string, unknown>);\n}\n\nexport function rawQuery(sql: string): Promise<unknown> {\n return callTool(\"query\", { sql });\n}\n\nexport function dbStats(): Promise<unknown> {\n return callTool(\"status\");\n}\n\n// ── Exec calls (write operations, CLI only) ─────────────────────────────────\n\nconst EXEC_TIMEOUT = 60_000;\n\nexport function callExec(\n command: string,\n params?: Record<string, unknown>,\n): Promise<unknown> {\n return post(\"/api/exec\", { command, params }, EXEC_TIMEOUT);\n}\n\nexport function pruneEstimate(cutoffMs: number): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, dryRun: true });\n}\n\nexport function pruneExecute(\n cutoffMs: number,\n opts?: { vacuum?: boolean },\n): Promise<unknown> {\n return callExec(\"prune\", { cutoffMs, ...opts });\n}\n\nexport function refreshPricing(): Promise<unknown> {\n return callExec(\"refresh-pricing\");\n}\n\nexport function syncReset(target?: string): Promise<unknown> {\n return callExec(\"sync-reset\", target ? { target } : {});\n}\n\nexport function syncWatermarkGet(\n target: string,\n table?: string,\n): Promise<unknown> {\n return callExec(\"sync-watermark-get\", { target, table });\n}\n\nexport function syncWatermarkSet(\n target: string,\n table: string,\n value: number,\n): Promise<unknown> {\n return callExec(\"sync-watermark-set\", { target, table, value });\n}\n\nexport function syncPending(target: string): Promise<{\n target: string;\n totalPending: number;\n tables: Record<string, { maxId: number; watermark: number; pending: number }>;\n}> {\n return callExec(\"sync-pending\", { target }) as Promise<{\n target: string;\n totalPending: number;\n tables: Record<\n string,\n { maxId: number; watermark: number; pending: number }\n >;\n }>;\n}\n\nexport function syncTargetList(): Promise<unknown> {\n return callExec(\"sync-target-list\");\n}\n\nexport function syncTargetAdd(target: {\n name: string;\n url: string;\n token?: string;\n tokenCommand?: string;\n}): Promise<unknown> {\n return callExec(\"sync-target-add\", target as Record<string, unknown>);\n}\n\nexport function syncTargetRemove(name: string): Promise<unknown> {\n return callExec(\"sync-target-remove\", { name });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,62 @@
1
+ // src/log.ts
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { Logger } from "tslog";
6
+ function getLogDir() {
7
+ switch (process.platform) {
8
+ case "darwin":
9
+ return path.join(os.homedir(), "Library", "Logs", "panopticon");
10
+ case "win32":
11
+ return path.join(
12
+ process.env.LOCALAPPDATA ?? path.join(os.homedir(), "AppData", "Local"),
13
+ "panopticon",
14
+ "logs"
15
+ );
16
+ default:
17
+ return path.join(
18
+ process.env.XDG_STATE_HOME ?? path.join(os.homedir(), ".local", "state"),
19
+ "panopticon",
20
+ "logs"
21
+ );
22
+ }
23
+ }
24
+ var LOG_DIR = getLogDir();
25
+ var logPaths = {
26
+ server: path.join(LOG_DIR, "server.log"),
27
+ otlp: path.join(LOG_DIR, "otlp-receiver.log"),
28
+ mcp: path.join(LOG_DIR, "mcp-server.log"),
29
+ proxy: path.join(LOG_DIR, "proxy.log"),
30
+ hook: path.join(LOG_DIR, "hook-handler.log")
31
+ };
32
+ var DAEMON_NAMES = Object.keys(logPaths);
33
+ function openLogFd(daemon) {
34
+ fs.mkdirSync(LOG_DIR, { recursive: true });
35
+ return fs.openSync(logPaths[daemon], "a");
36
+ }
37
+ var root = new Logger({
38
+ name: "panopticon",
39
+ type: "pretty",
40
+ prettyLogTimeZone: "UTC",
41
+ stylePrettyLogs: false,
42
+ prettyLogTemplate: "{{dateIsoStr}} [{{name}}] {{logLevelName}} "
43
+ });
44
+ var log = {
45
+ server: root.getSubLogger({ name: "server" }),
46
+ scanner: root.getSubLogger({ name: "scanner" }),
47
+ sync: root.getSubLogger({ name: "sync" }),
48
+ proxy: root.getSubLogger({ name: "proxy" }),
49
+ llm: root.getSubLogger({ name: "llm" }),
50
+ mcp: root.getSubLogger({ name: "mcp" }),
51
+ otlp: root.getSubLogger({ name: "otlp" }),
52
+ hooks: root.getSubLogger({ name: "hooks" })
53
+ };
54
+
55
+ export {
56
+ LOG_DIR,
57
+ logPaths,
58
+ DAEMON_NAMES,
59
+ openLogFd,
60
+ log
61
+ };
62
+ //# sourceMappingURL=chunk-7Q3BJMLG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/log.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { Logger } from \"tslog\";\n\nfunction getLogDir(): string {\n switch (process.platform) {\n case \"darwin\":\n return path.join(os.homedir(), \"Library\", \"Logs\", \"panopticon\");\n case \"win32\":\n return path.join(\n process.env.LOCALAPPDATA ?? path.join(os.homedir(), \"AppData\", \"Local\"),\n \"panopticon\",\n \"logs\",\n );\n default:\n // Linux and other Unix-like\n return path.join(\n process.env.XDG_STATE_HOME ??\n path.join(os.homedir(), \".local\", \"state\"),\n \"panopticon\",\n \"logs\",\n );\n }\n}\n\nexport const LOG_DIR = getLogDir();\n\nexport const logPaths = {\n server: path.join(LOG_DIR, \"server.log\"),\n otlp: path.join(LOG_DIR, \"otlp-receiver.log\"),\n mcp: path.join(LOG_DIR, \"mcp-server.log\"),\n proxy: path.join(LOG_DIR, \"proxy.log\"),\n hook: path.join(LOG_DIR, \"hook-handler.log\"),\n} as const;\n\nexport type DaemonName = keyof typeof logPaths;\n\nexport const DAEMON_NAMES = Object.keys(logPaths) as DaemonName[];\n\n/**\n * Open a log file in append mode, returning the fd.\n * Pass the fd to spawn's stdio array: [\"ignore\", fd, fd]\n * Close the fd after spawn — the child inherits its own copy.\n */\nexport function openLogFd(daemon: DaemonName): number {\n fs.mkdirSync(LOG_DIR, { recursive: true });\n return fs.openSync(logPaths[daemon], \"a\");\n}\n\nconst root = new Logger({\n name: \"panopticon\",\n type: \"pretty\",\n prettyLogTimeZone: \"UTC\",\n stylePrettyLogs: false,\n prettyLogTemplate: \"{{dateIsoStr}} [{{name}}] {{logLevelName}}\\t\",\n});\n\nexport const log = {\n server: root.getSubLogger({ name: \"server\" }),\n scanner: root.getSubLogger({ name: \"scanner\" }),\n sync: root.getSubLogger({ name: \"sync\" }),\n proxy: root.getSubLogger({ name: \"proxy\" }),\n llm: root.getSubLogger({ name: \"llm\" }),\n mcp: root.getSubLogger({ name: \"mcp\" }),\n otlp: root.getSubLogger({ name: \"otlp\" }),\n hooks: root.getSubLogger({ name: \"hooks\" }),\n};\n"],"mappings":";AAAA,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,cAAc;AAEvB,SAAS,YAAoB;AAC3B,UAAQ,QAAQ,UAAU;AAAA,IACxB,KAAK;AACH,aAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,QAAQ,YAAY;AAAA,IAChE,KAAK;AACH,aAAO,KAAK;AAAA,QACV,QAAQ,IAAI,gBAAgB,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,OAAO;AAAA,QACtE;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEE,aAAO,KAAK;AAAA,QACV,QAAQ,IAAI,kBACV,KAAK,KAAK,GAAG,QAAQ,GAAG,UAAU,OAAO;AAAA,QAC3C;AAAA,QACA;AAAA,MACF;AAAA,EACJ;AACF;AAEO,IAAM,UAAU,UAAU;AAE1B,IAAM,WAAW;AAAA,EACtB,QAAQ,KAAK,KAAK,SAAS,YAAY;AAAA,EACvC,MAAM,KAAK,KAAK,SAAS,mBAAmB;AAAA,EAC5C,KAAK,KAAK,KAAK,SAAS,gBAAgB;AAAA,EACxC,OAAO,KAAK,KAAK,SAAS,WAAW;AAAA,EACrC,MAAM,KAAK,KAAK,SAAS,kBAAkB;AAC7C;AAIO,IAAM,eAAe,OAAO,KAAK,QAAQ;AAOzC,SAAS,UAAU,QAA4B;AACpD,KAAG,UAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACzC,SAAO,GAAG,SAAS,SAAS,MAAM,GAAG,GAAG;AAC1C;AAEA,IAAM,OAAO,IAAI,OAAO;AAAA,EACtB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,mBAAmB;AACrB,CAAC;AAEM,IAAM,MAAM;AAAA,EACjB,QAAQ,KAAK,aAAa,EAAE,MAAM,SAAS,CAAC;AAAA,EAC5C,SAAS,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAAA,EAC9C,MAAM,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAAA,EACxC,OAAO,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC1C,KAAK,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAAA,EACtC,KAAK,KAAK,aAAa,EAAE,MAAM,MAAM,CAAC;AAAA,EACtC,MAAM,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAAA,EACxC,OAAO,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AAC5C;","names":[]}
@@ -0,0 +1,412 @@
1
+ import {
2
+ getDb
3
+ } from "./chunk-DZ5HJFB4.js";
4
+
5
+ // src/db/store.ts
6
+ import { createHash } from "crypto";
7
+ import { gzipSync } from "zlib";
8
+ var INSERT_LOG_SQL = `
9
+ INSERT INTO otel_logs (timestamp_ns, observed_timestamp_ns, severity_number, severity_text, body, attributes, resource_attributes, session_id, prompt_id, trace_id, span_id)
10
+ VALUES (@timestamp_ns, @observed_timestamp_ns, @severity_number, @severity_text, @body, @attributes, @resource_attributes, @session_id, @prompt_id, @trace_id, @span_id)
11
+ `;
12
+ var INSERT_METRIC_SQL = `
13
+ INSERT INTO otel_metrics (timestamp_ns, name, value, metric_type, unit, attributes, resource_attributes, session_id)
14
+ VALUES (@timestamp_ns, @name, @value, @metric_type, @unit, @attributes, @resource_attributes, @session_id)
15
+ `;
16
+ var INSERT_SPAN_SQL = `
17
+ INSERT OR IGNORE INTO otel_spans (trace_id, span_id, parent_span_id, name, kind, start_time_ns, end_time_ns, status_code, status_message, attributes, resource_attributes, session_id)
18
+ VALUES (@trace_id, @span_id, @parent_span_id, @name, @kind, @start_time_ns, @end_time_ns, @status_code, @status_message, @attributes, @resource_attributes, @session_id)
19
+ `;
20
+ var INSERT_HOOK_SQL = `
21
+ INSERT INTO hook_events (session_id, event_type, timestamp_ms, cwd, repository, tool_name,
22
+ target, user_prompt, file_path, command, tool_result, plan, allowed_prompts, payload)
23
+ VALUES (@session_id, @event_type, @timestamp_ms, @cwd, @repository, @tool_name,
24
+ @target, @user_prompt, @file_path, @command, @tool_result, @plan, @allowed_prompts, @payload)
25
+ `;
26
+ function insertOtelLogs(rows) {
27
+ const db = getDb();
28
+ const stmt = db.prepare(INSERT_LOG_SQL);
29
+ const insertMany = db.transaction((rows2) => {
30
+ for (const row of rows2) {
31
+ stmt.run({
32
+ timestamp_ns: row.timestamp_ns,
33
+ observed_timestamp_ns: row.observed_timestamp_ns ?? null,
34
+ severity_number: row.severity_number ?? null,
35
+ severity_text: row.severity_text ?? null,
36
+ body: row.body ?? null,
37
+ attributes: row.attributes ? JSON.stringify(row.attributes) : null,
38
+ resource_attributes: row.resource_attributes ? JSON.stringify(row.resource_attributes) : null,
39
+ session_id: row.session_id ?? null,
40
+ prompt_id: row.prompt_id ?? null,
41
+ trace_id: row.trace_id ?? null,
42
+ span_id: row.span_id ?? null
43
+ });
44
+ }
45
+ });
46
+ insertMany(rows);
47
+ }
48
+ function insertOtelMetrics(rows) {
49
+ const db = getDb();
50
+ const stmt = db.prepare(INSERT_METRIC_SQL);
51
+ const insertMany = db.transaction((rows2) => {
52
+ for (const row of rows2) {
53
+ const sessionId = row.session_id ?? null;
54
+ stmt.run({
55
+ timestamp_ns: row.timestamp_ns,
56
+ name: row.name,
57
+ value: row.value,
58
+ metric_type: row.metric_type ?? null,
59
+ unit: row.unit ?? null,
60
+ attributes: row.attributes ? JSON.stringify(row.attributes) : null,
61
+ resource_attributes: row.resource_attributes ? JSON.stringify(row.resource_attributes) : null,
62
+ session_id: sessionId
63
+ });
64
+ }
65
+ });
66
+ insertMany(rows);
67
+ }
68
+ function insertOtelSpans(rows) {
69
+ const db = getDb();
70
+ const stmt = db.prepare(INSERT_SPAN_SQL);
71
+ const insertMany = db.transaction((rows2) => {
72
+ for (const row of rows2) {
73
+ stmt.run({
74
+ trace_id: row.trace_id,
75
+ span_id: row.span_id,
76
+ parent_span_id: row.parent_span_id ?? null,
77
+ name: row.name,
78
+ kind: row.kind ?? null,
79
+ start_time_ns: row.start_time_ns,
80
+ end_time_ns: row.end_time_ns,
81
+ status_code: row.status_code ?? null,
82
+ status_message: row.status_message ?? null,
83
+ attributes: row.attributes ? JSON.stringify(row.attributes) : null,
84
+ resource_attributes: row.resource_attributes ? JSON.stringify(row.resource_attributes) : null,
85
+ session_id: row.session_id ?? null
86
+ });
87
+ }
88
+ });
89
+ insertMany(rows);
90
+ }
91
+ function upsertSessionRepository(sessionId, repository, timestampMs, gitIdentity, branch) {
92
+ const db = getDb();
93
+ const existing = db.prepare(
94
+ `SELECT 1 FROM session_repositories WHERE session_id = ? AND repository = ?`
95
+ ).get(sessionId, repository);
96
+ db.prepare(
97
+ `INSERT INTO session_repositories (session_id, repository, first_seen_ms, git_user_name, git_user_email, branch)
98
+ VALUES (?, ?, ?, ?, ?, ?)
99
+ ON CONFLICT(session_id, repository) DO UPDATE SET
100
+ git_user_name = COALESCE(session_repositories.git_user_name, excluded.git_user_name),
101
+ git_user_email = COALESCE(session_repositories.git_user_email, excluded.git_user_email),
102
+ branch = COALESCE(excluded.branch, session_repositories.branch)`
103
+ ).run(
104
+ sessionId,
105
+ repository,
106
+ timestampMs,
107
+ gitIdentity?.name ?? null,
108
+ gitIdentity?.email ?? null,
109
+ branch ?? null
110
+ );
111
+ if (!existing) {
112
+ db.prepare(
113
+ `UPDATE sessions SET sync_seq = COALESCE(sync_seq, 0) + 1 WHERE session_id = ?`
114
+ ).run(sessionId);
115
+ }
116
+ }
117
+ function upsertSessionCwd(sessionId, cwd, timestampMs) {
118
+ const db = getDb();
119
+ db.prepare(
120
+ "INSERT INTO session_cwds (session_id, cwd, first_seen_ms) VALUES (?, ?, ?) ON CONFLICT DO NOTHING"
121
+ ).run(sessionId, cwd, timestampMs);
122
+ }
123
+ function contentHash(obj) {
124
+ return createHash("sha256").update(JSON.stringify(obj, Object.keys(obj).sort())).digest("hex");
125
+ }
126
+ function insertUserConfigSnapshot(snap) {
127
+ const db = getDb();
128
+ const hash = contentHash({
129
+ permissions: snap.permissions,
130
+ enabledPlugins: snap.enabledPlugins,
131
+ hooks: snap.hooks,
132
+ commands: snap.commands,
133
+ rules: snap.rules,
134
+ skills: snap.skills
135
+ });
136
+ const existing = db.prepare(
137
+ "SELECT content_hash FROM user_config_snapshots WHERE device_name = ? ORDER BY snapshot_at_ms DESC LIMIT 1"
138
+ ).get(snap.deviceName);
139
+ if (existing?.content_hash === hash) return false;
140
+ db.prepare(
141
+ `INSERT INTO user_config_snapshots
142
+ (device_name, snapshot_at_ms, content_hash, permissions, enabled_plugins, hooks, commands, rules, skills)
143
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
144
+ ).run(
145
+ snap.deviceName,
146
+ Date.now(),
147
+ hash,
148
+ JSON.stringify(snap.permissions),
149
+ JSON.stringify(snap.enabledPlugins),
150
+ JSON.stringify(snap.hooks),
151
+ JSON.stringify(snap.commands),
152
+ JSON.stringify(snap.rules),
153
+ JSON.stringify(snap.skills)
154
+ );
155
+ return true;
156
+ }
157
+ function insertRepoConfigSnapshot(snap) {
158
+ const db = getDb();
159
+ const hash = contentHash({
160
+ hooks: snap.hooks,
161
+ mcpServers: snap.mcpServers,
162
+ commands: snap.commands,
163
+ agents: snap.agents,
164
+ rules: snap.rules,
165
+ localHooks: snap.localHooks,
166
+ localMcpServers: snap.localMcpServers,
167
+ localPermissions: snap.localPermissions,
168
+ localIsGitignored: snap.localIsGitignored,
169
+ instructions: snap.instructions
170
+ });
171
+ const existing = db.prepare(
172
+ "SELECT content_hash FROM repo_config_snapshots WHERE repository = ? ORDER BY snapshot_at_ms DESC LIMIT 1"
173
+ ).get(snap.repository);
174
+ if (existing?.content_hash === hash) return false;
175
+ db.prepare(
176
+ `INSERT INTO repo_config_snapshots
177
+ (repository, cwd, session_id, snapshot_at_ms, content_hash,
178
+ hooks, mcp_servers, commands, agents, rules,
179
+ local_hooks, local_mcp_servers, local_permissions, local_is_gitignored,
180
+ instructions)
181
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
182
+ ).run(
183
+ snap.repository,
184
+ snap.cwd,
185
+ snap.sessionId ?? null,
186
+ Date.now(),
187
+ hash,
188
+ JSON.stringify(snap.hooks),
189
+ JSON.stringify(snap.mcpServers),
190
+ JSON.stringify(snap.commands),
191
+ JSON.stringify(snap.agents),
192
+ JSON.stringify(snap.rules),
193
+ JSON.stringify(snap.localHooks),
194
+ JSON.stringify(snap.localMcpServers),
195
+ JSON.stringify(snap.localPermissions),
196
+ snap.localIsGitignored ? 1 : 0,
197
+ JSON.stringify(snap.instructions)
198
+ );
199
+ return true;
200
+ }
201
+ function upsertSession(row) {
202
+ const db = getDb();
203
+ db.prepare(
204
+ `INSERT INTO sessions (session_id, target, started_at_ms, ended_at_ms, first_prompt,
205
+ permission_mode, agent_version, model, cli_version, scanner_file_path,
206
+ total_input_tokens, total_output_tokens, total_cache_read_tokens,
207
+ total_cache_creation_tokens, total_reasoning_tokens, turn_count,
208
+ otel_input_tokens, otel_output_tokens, otel_cache_read_tokens, otel_cache_creation_tokens,
209
+ models, project, created_at, parent_session_id, relationship_type, is_automated,
210
+ has_hooks, has_otel, has_scanner)
211
+ VALUES (@session_id, @target, @started_at_ms, @ended_at_ms, @first_prompt,
212
+ @permission_mode, @agent_version, @model, @cli_version, @scanner_file_path,
213
+ @total_input_tokens, @total_output_tokens, @total_cache_read_tokens,
214
+ @total_cache_creation_tokens, @total_reasoning_tokens, @turn_count,
215
+ @otel_input_tokens, @otel_output_tokens, @otel_cache_read_tokens, @otel_cache_creation_tokens,
216
+ @model, @project, @created_at, @parent_session_id, @relationship_type, @is_automated,
217
+ @has_hooks, @has_otel, @has_scanner)
218
+ ON CONFLICT(session_id) DO UPDATE SET
219
+ target = COALESCE(excluded.target, sessions.target),
220
+ started_at_ms = COALESCE(excluded.started_at_ms, sessions.started_at_ms),
221
+ ended_at_ms = COALESCE(excluded.ended_at_ms, sessions.ended_at_ms),
222
+ first_prompt = COALESCE(sessions.first_prompt, excluded.first_prompt),
223
+ permission_mode = COALESCE(excluded.permission_mode, sessions.permission_mode),
224
+ agent_version = COALESCE(excluded.agent_version, sessions.agent_version),
225
+ model = COALESCE(excluded.model, sessions.model),
226
+ cli_version = COALESCE(excluded.cli_version, sessions.cli_version),
227
+ scanner_file_path = COALESCE(excluded.scanner_file_path, sessions.scanner_file_path),
228
+ total_input_tokens = COALESCE(excluded.total_input_tokens, sessions.total_input_tokens),
229
+ total_output_tokens = COALESCE(excluded.total_output_tokens, sessions.total_output_tokens),
230
+ total_cache_read_tokens = COALESCE(excluded.total_cache_read_tokens, sessions.total_cache_read_tokens),
231
+ total_cache_creation_tokens = COALESCE(excluded.total_cache_creation_tokens, sessions.total_cache_creation_tokens),
232
+ total_reasoning_tokens = COALESCE(excluded.total_reasoning_tokens, sessions.total_reasoning_tokens),
233
+ turn_count = COALESCE(excluded.turn_count, sessions.turn_count),
234
+ otel_input_tokens = COALESCE(excluded.otel_input_tokens, sessions.otel_input_tokens),
235
+ otel_output_tokens = COALESCE(excluded.otel_output_tokens, sessions.otel_output_tokens),
236
+ otel_cache_read_tokens = COALESCE(excluded.otel_cache_read_tokens, sessions.otel_cache_read_tokens),
237
+ otel_cache_creation_tokens = COALESCE(excluded.otel_cache_creation_tokens, sessions.otel_cache_creation_tokens),
238
+ models = CASE
239
+ WHEN excluded.model IS NULL THEN sessions.models
240
+ WHEN sessions.models IS NULL THEN excluded.model
241
+ WHEN sessions.models LIKE '%' || excluded.model || '%' THEN sessions.models
242
+ ELSE sessions.models || ',' || excluded.model
243
+ END,
244
+ project = COALESCE(sessions.project, excluded.project),
245
+ parent_session_id = COALESCE(excluded.parent_session_id, sessions.parent_session_id),
246
+ relationship_type = COALESCE(excluded.relationship_type, sessions.relationship_type),
247
+ is_automated = COALESCE(excluded.is_automated, sessions.is_automated),
248
+ has_hooks = MAX(COALESCE(sessions.has_hooks, 0), COALESCE(excluded.has_hooks, 0)),
249
+ has_otel = MAX(COALESCE(sessions.has_otel, 0), COALESCE(excluded.has_otel, 0)),
250
+ has_scanner = MAX(COALESCE(sessions.has_scanner, 0), COALESCE(excluded.has_scanner, 0)),
251
+ sync_dirty = 1,
252
+ sync_seq = COALESCE(sessions.sync_seq, 0) + 1`
253
+ ).run({
254
+ session_id: row.session_id,
255
+ target: row.target ?? null,
256
+ started_at_ms: row.started_at_ms ?? null,
257
+ ended_at_ms: row.ended_at_ms ?? null,
258
+ first_prompt: row.first_prompt ?? null,
259
+ permission_mode: row.permission_mode ?? null,
260
+ agent_version: row.agent_version ?? null,
261
+ model: row.model ?? null,
262
+ cli_version: row.cli_version ?? null,
263
+ scanner_file_path: row.scanner_file_path ?? null,
264
+ total_input_tokens: row.total_input_tokens ?? null,
265
+ total_output_tokens: row.total_output_tokens ?? null,
266
+ total_cache_read_tokens: row.total_cache_read_tokens ?? null,
267
+ total_cache_creation_tokens: row.total_cache_creation_tokens ?? null,
268
+ total_reasoning_tokens: row.total_reasoning_tokens ?? null,
269
+ turn_count: row.turn_count ?? null,
270
+ otel_input_tokens: row.otel_input_tokens ?? null,
271
+ otel_output_tokens: row.otel_output_tokens ?? null,
272
+ otel_cache_read_tokens: row.otel_cache_read_tokens ?? null,
273
+ otel_cache_creation_tokens: row.otel_cache_creation_tokens ?? null,
274
+ project: row.project ?? null,
275
+ created_at: row.created_at ?? null,
276
+ parent_session_id: row.parent_session_id ?? null,
277
+ relationship_type: row.relationship_type ?? null,
278
+ is_automated: row.is_automated ?? null,
279
+ has_hooks: row.has_hooks ?? null,
280
+ has_otel: row.has_otel ?? null,
281
+ has_scanner: row.has_scanner ?? null
282
+ });
283
+ }
284
+ var AUTOMATED_PREFIXES = [
285
+ "You are a code reviewer.",
286
+ "You are a security code reviewer.",
287
+ "You are a design reviewer.",
288
+ "You are a code assistant. Your task is to address",
289
+ "You are a code review insights analyst.",
290
+ "You are reviewing whether an implementation matches",
291
+ "You are a plan document reviewer.",
292
+ "You are a spec document reviewer.",
293
+ "You are summarizing a day of AI agent activity.",
294
+ "You are analyzing AI agent sessions.",
295
+ "## Analysis Request",
296
+ "# Fix Request"
297
+ ];
298
+ var AUTOMATED_SUBSTRINGS = ["invoked by roborev to perform this review"];
299
+ function isAutomatedPrompt(firstPrompt) {
300
+ for (const p of AUTOMATED_PREFIXES) {
301
+ if (firstPrompt.startsWith(p)) return true;
302
+ }
303
+ for (const s of AUTOMATED_SUBSTRINGS) {
304
+ if (firstPrompt.includes(s)) return true;
305
+ }
306
+ return false;
307
+ }
308
+ function updateSessionMessageCounts(sessionId) {
309
+ const db = getDb();
310
+ const counts = db.prepare(
311
+ `SELECT
312
+ (SELECT COUNT(*) FROM messages WHERE session_id = ?) as msg_count,
313
+ (SELECT COUNT(*) FROM messages WHERE session_id = ? AND role = 'user' AND is_system = 0) as user_count,
314
+ (SELECT first_prompt FROM sessions WHERE session_id = ?) as first_prompt`
315
+ ).get(sessionId, sessionId, sessionId);
316
+ const isAutomated = counts.user_count <= 1 && counts.first_prompt != null && isAutomatedPrompt(counts.first_prompt) ? 1 : 0;
317
+ db.prepare(
318
+ `UPDATE sessions SET
319
+ message_count = ?,
320
+ user_message_count = ?,
321
+ is_automated = CASE WHEN ? > 1 THEN 0 ELSE ? END,
322
+ sync_seq = COALESCE(sync_seq, 0) + 1
323
+ WHERE session_id = ?`
324
+ ).run(
325
+ counts.msg_count,
326
+ counts.user_count,
327
+ counts.user_count,
328
+ isAutomated,
329
+ sessionId
330
+ );
331
+ }
332
+ function incrementToolCount(sessionId, toolName) {
333
+ const db = getDb();
334
+ db.prepare(
335
+ `UPDATE sessions
336
+ SET hook_tool_counts = JSON_SET(
337
+ COALESCE(hook_tool_counts, '{}'),
338
+ '$.' || @tool,
339
+ COALESCE(JSON_EXTRACT(hook_tool_counts, '$.' || @tool), 0) + 1
340
+ )
341
+ WHERE session_id = @session_id`
342
+ ).run({ session_id: sessionId, tool: toolName });
343
+ }
344
+ function incrementEventTypeCount(sessionId, eventType) {
345
+ const db = getDb();
346
+ db.prepare(
347
+ `UPDATE sessions
348
+ SET hook_event_type_counts = JSON_SET(
349
+ COALESCE(hook_event_type_counts, '{}'),
350
+ '$.' || @event_type,
351
+ COALESCE(JSON_EXTRACT(hook_event_type_counts, '$.' || @event_type), 0) + 1
352
+ )
353
+ WHERE session_id = @session_id`
354
+ ).run({ session_id: sessionId, event_type: eventType });
355
+ }
356
+ function extractStr(obj, key) {
357
+ const v = obj?.[key];
358
+ return typeof v === "string" ? v : void 0;
359
+ }
360
+ function insertHookEvent(row) {
361
+ const db = getDb();
362
+ const data = row.payload;
363
+ const toolInput = data.tool_input;
364
+ const userPrompt = extractStr(data, "prompt") ?? extractStr(data, "user_prompt");
365
+ const filePath = extractStr(toolInput, "file_path");
366
+ const command = extractStr(toolInput, "command");
367
+ const plan = extractStr(toolInput, "plan");
368
+ const toolResultRaw = data.tool_result ?? data.tool_response;
369
+ const toolResult = toolResultRaw ? typeof toolResultRaw === "string" ? toolResultRaw : JSON.stringify(toolResultRaw) : void 0;
370
+ const allowedPrompts = toolInput?.allowedPrompts ? JSON.stringify(toolInput.allowedPrompts) : void 0;
371
+ const fullJson = JSON.stringify(data);
372
+ const insertWithFts = db.transaction(() => {
373
+ db.prepare(INSERT_HOOK_SQL).run({
374
+ session_id: row.session_id,
375
+ event_type: row.event_type,
376
+ timestamp_ms: row.timestamp_ms,
377
+ cwd: row.cwd ?? null,
378
+ repository: row.repository ?? null,
379
+ tool_name: row.tool_name ?? null,
380
+ target: row.target ?? null,
381
+ user_prompt: userPrompt ?? null,
382
+ file_path: filePath ?? null,
383
+ command: command ?? null,
384
+ tool_result: toolResult ?? null,
385
+ plan: plan ?? null,
386
+ allowed_prompts: allowedPrompts ?? null,
387
+ payload: gzipSync(Buffer.from(fullJson))
388
+ });
389
+ const { id } = db.prepare("SELECT last_insert_rowid() as id").get();
390
+ db.prepare("INSERT INTO hook_events_fts(rowid, payload) VALUES (?, ?)").run(
391
+ id,
392
+ fullJson
393
+ );
394
+ });
395
+ insertWithFts();
396
+ }
397
+
398
+ export {
399
+ insertOtelLogs,
400
+ insertOtelMetrics,
401
+ insertOtelSpans,
402
+ upsertSessionRepository,
403
+ upsertSessionCwd,
404
+ insertUserConfigSnapshot,
405
+ insertRepoConfigSnapshot,
406
+ upsertSession,
407
+ updateSessionMessageCounts,
408
+ incrementToolCount,
409
+ incrementEventTypeCount,
410
+ insertHookEvent
411
+ };
412
+ //# sourceMappingURL=chunk-BVOE7A2Z.js.map