@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.
- package/.claude-plugin/plugin.json +10 -0
- package/LICENSE +5 -0
- package/README.md +363 -0
- package/bin/hook-handler +3 -0
- package/bin/mcp-server +3 -0
- package/bin/panopticon +3 -0
- package/bin/proxy +3 -0
- package/bin/server +3 -0
- package/dist/api/client.d.ts +67 -0
- package/dist/api/client.js +48 -0
- package/dist/api/client.js.map +1 -0
- package/dist/chunk-3BUJ7URA.js +387 -0
- package/dist/chunk-3BUJ7URA.js.map +1 -0
- package/dist/chunk-3TZAKV3M.js +158 -0
- package/dist/chunk-3TZAKV3M.js.map +1 -0
- package/dist/chunk-4SM2H22C.js +169 -0
- package/dist/chunk-4SM2H22C.js.map +1 -0
- package/dist/chunk-7Q3BJMLG.js +62 -0
- package/dist/chunk-7Q3BJMLG.js.map +1 -0
- package/dist/chunk-BVOE7A2Z.js +412 -0
- package/dist/chunk-BVOE7A2Z.js.map +1 -0
- package/dist/chunk-CF4GPWLI.js +170 -0
- package/dist/chunk-CF4GPWLI.js.map +1 -0
- package/dist/chunk-DZ5HJFB4.js +467 -0
- package/dist/chunk-DZ5HJFB4.js.map +1 -0
- package/dist/chunk-HQCY722C.js +428 -0
- package/dist/chunk-HQCY722C.js.map +1 -0
- package/dist/chunk-HRCEIYKU.js +134 -0
- package/dist/chunk-HRCEIYKU.js.map +1 -0
- package/dist/chunk-K7YUPLES.js +76 -0
- package/dist/chunk-K7YUPLES.js.map +1 -0
- package/dist/chunk-L7G27XWF.js +130 -0
- package/dist/chunk-L7G27XWF.js.map +1 -0
- package/dist/chunk-LWXF7YRG.js +626 -0
- package/dist/chunk-LWXF7YRG.js.map +1 -0
- package/dist/chunk-NXH7AONS.js +1120 -0
- package/dist/chunk-NXH7AONS.js.map +1 -0
- package/dist/chunk-QK5442ZP.js +55 -0
- package/dist/chunk-QK5442ZP.js.map +1 -0
- package/dist/chunk-QVK6VGCV.js +1703 -0
- package/dist/chunk-QVK6VGCV.js.map +1 -0
- package/dist/chunk-RX2RXHBH.js +1699 -0
- package/dist/chunk-RX2RXHBH.js.map +1 -0
- package/dist/chunk-SEXU2WYG.js +788 -0
- package/dist/chunk-SEXU2WYG.js.map +1 -0
- package/dist/chunk-SUGSQ4YI.js +264 -0
- package/dist/chunk-SUGSQ4YI.js.map +1 -0
- package/dist/chunk-TGXFVAID.js +138 -0
- package/dist/chunk-TGXFVAID.js.map +1 -0
- package/dist/chunk-WLBNFVIG.js +447 -0
- package/dist/chunk-WLBNFVIG.js.map +1 -0
- package/dist/chunk-XLTCUH5A.js +1072 -0
- package/dist/chunk-XLTCUH5A.js.map +1 -0
- package/dist/chunk-YVRWVDIA.js +146 -0
- package/dist/chunk-YVRWVDIA.js.map +1 -0
- package/dist/chunk-ZEC4LRKS.js +176 -0
- package/dist/chunk-ZEC4LRKS.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1084 -0
- package/dist/cli.js.map +1 -0
- package/dist/config-NwoZC-GM.d.ts +20 -0
- package/dist/db.d.ts +46 -0
- package/dist/db.js +15 -0
- package/dist/db.js.map +1 -0
- package/dist/doctor.d.ts +37 -0
- package/dist/doctor.js +14 -0
- package/dist/doctor.js.map +1 -0
- package/dist/hooks/handler.d.ts +23 -0
- package/dist/hooks/handler.js +295 -0
- package/dist/hooks/handler.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +243 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/otlp/server.d.ts +7 -0
- package/dist/otlp/server.js +17 -0
- package/dist/otlp/server.js.map +1 -0
- package/dist/permissions.d.ts +33 -0
- package/dist/permissions.js +14 -0
- package/dist/permissions.js.map +1 -0
- package/dist/pricing.d.ts +29 -0
- package/dist/pricing.js +13 -0
- package/dist/pricing.js.map +1 -0
- package/dist/proxy/server.d.ts +10 -0
- package/dist/proxy/server.js +20 -0
- package/dist/proxy/server.js.map +1 -0
- package/dist/prune.d.ts +18 -0
- package/dist/prune.js +13 -0
- package/dist/prune.js.map +1 -0
- package/dist/query.d.ts +56 -0
- package/dist/query.js +27 -0
- package/dist/query.js.map +1 -0
- package/dist/reparse-636YZCE3.js +14 -0
- package/dist/reparse-636YZCE3.js.map +1 -0
- package/dist/repo.d.ts +17 -0
- package/dist/repo.js +9 -0
- package/dist/repo.js.map +1 -0
- package/dist/scanner.d.ts +73 -0
- package/dist/scanner.js +15 -0
- package/dist/scanner.js.map +1 -0
- package/dist/sdk.d.ts +82 -0
- package/dist/sdk.js +208 -0
- package/dist/sdk.js.map +1 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.js +25 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +35 -0
- package/dist/setup.js +19 -0
- package/dist/setup.js.map +1 -0
- package/dist/sync/index.d.ts +29 -0
- package/dist/sync/index.js +32 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/targets.d.ts +279 -0
- package/dist/targets.js +20 -0
- package/dist/targets.js.map +1 -0
- package/dist/types-D-MYCBol.d.ts +128 -0
- package/dist/types.d.ts +164 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/hooks/hooks.json +274 -0
- package/package.json +124 -0
- 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
|