@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,788 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getDb
|
|
3
|
+
} from "./chunk-DZ5HJFB4.js";
|
|
4
|
+
|
|
5
|
+
// src/sync/reader.ts
|
|
6
|
+
function parseJson(raw) {
|
|
7
|
+
if (!raw) return null;
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(raw);
|
|
10
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
var HOOK_EVENTS_SQL = `
|
|
16
|
+
SELECT h.id, h.session_id, h.sync_id, h.event_type, h.timestamp_ms, h.cwd, h.repository,
|
|
17
|
+
h.tool_name, decompress(h.payload) as payload,
|
|
18
|
+
h.user_prompt, h.file_path, h.command, h.tool_result,
|
|
19
|
+
s.target
|
|
20
|
+
FROM hook_events h
|
|
21
|
+
LEFT JOIN sessions s ON s.session_id = h.session_id
|
|
22
|
+
WHERE h.id > ?
|
|
23
|
+
ORDER BY h.id
|
|
24
|
+
LIMIT ?
|
|
25
|
+
`;
|
|
26
|
+
function readHookEvents(afterId, limit) {
|
|
27
|
+
const db = getDb();
|
|
28
|
+
const rawRows = db.prepare(HOOK_EVENTS_SQL).all(afterId, limit);
|
|
29
|
+
const rows = rawRows.map((r) => ({
|
|
30
|
+
hookId: r.id,
|
|
31
|
+
sessionId: r.session_id,
|
|
32
|
+
syncId: r.sync_id,
|
|
33
|
+
eventType: r.event_type,
|
|
34
|
+
timestampMs: r.timestamp_ms,
|
|
35
|
+
cwd: r.cwd,
|
|
36
|
+
repository: r.repository,
|
|
37
|
+
toolName: r.tool_name,
|
|
38
|
+
payload: parseJson(r.payload),
|
|
39
|
+
userPrompt: r.user_prompt,
|
|
40
|
+
filePath: r.file_path,
|
|
41
|
+
command: r.command,
|
|
42
|
+
toolResult: r.tool_result,
|
|
43
|
+
target: r.target
|
|
44
|
+
}));
|
|
45
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].hookId : afterId;
|
|
46
|
+
return { rows, maxId };
|
|
47
|
+
}
|
|
48
|
+
var HOOK_COVERED_BODIES = [
|
|
49
|
+
// Claude Code
|
|
50
|
+
"claude_code.user_prompt",
|
|
51
|
+
"claude_code.tool_decision",
|
|
52
|
+
"claude_code.tool_result",
|
|
53
|
+
// Gemini CLI
|
|
54
|
+
"gemini_cli.user_prompt",
|
|
55
|
+
"gemini_cli.tool_call",
|
|
56
|
+
"gemini_cli.hook_call"
|
|
57
|
+
];
|
|
58
|
+
var ALL_LOGS_SQL = `
|
|
59
|
+
SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,
|
|
60
|
+
severity_text, session_id, prompt_id, trace_id, span_id
|
|
61
|
+
FROM otel_logs
|
|
62
|
+
WHERE id > ?
|
|
63
|
+
ORDER BY id
|
|
64
|
+
LIMIT ?
|
|
65
|
+
`;
|
|
66
|
+
function mapOtelRows(rawRows) {
|
|
67
|
+
return rawRows.map((r) => ({
|
|
68
|
+
id: r.id,
|
|
69
|
+
syncId: r.sync_id,
|
|
70
|
+
timestampNs: r.timestamp_ns,
|
|
71
|
+
body: r.body,
|
|
72
|
+
attributes: parseJson(r.attributes),
|
|
73
|
+
resourceAttributes: parseJson(r.resource_attributes),
|
|
74
|
+
severityText: r.severity_text,
|
|
75
|
+
sessionId: r.session_id,
|
|
76
|
+
promptId: r.prompt_id,
|
|
77
|
+
traceId: r.trace_id,
|
|
78
|
+
spanId: r.span_id
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
function readOtelLogs(afterId, limit, hooksInstalled) {
|
|
82
|
+
const db = getDb();
|
|
83
|
+
if (!hooksInstalled) {
|
|
84
|
+
const rawRows2 = db.prepare(ALL_LOGS_SQL).all(afterId, limit);
|
|
85
|
+
const rows2 = mapOtelRows(rawRows2);
|
|
86
|
+
const maxId = rows2.length > 0 ? rows2[rows2.length - 1].id : afterId;
|
|
87
|
+
return { rows: rows2, maxId };
|
|
88
|
+
}
|
|
89
|
+
const scanMaxId = db.prepare(
|
|
90
|
+
"SELECT MAX(id) as m FROM (SELECT id FROM otel_logs WHERE id > ? ORDER BY id LIMIT ?)"
|
|
91
|
+
).get(afterId, limit).m;
|
|
92
|
+
if (scanMaxId == null) return { rows: [], maxId: afterId };
|
|
93
|
+
const rawRows = db.prepare(
|
|
94
|
+
`SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,
|
|
95
|
+
severity_text, session_id, prompt_id, trace_id, span_id
|
|
96
|
+
FROM otel_logs
|
|
97
|
+
WHERE id > ? AND id <= ?
|
|
98
|
+
AND body NOT IN (${HOOK_COVERED_BODIES.map(() => "?").join(", ")})
|
|
99
|
+
ORDER BY id`
|
|
100
|
+
).all(afterId, scanMaxId, ...HOOK_COVERED_BODIES);
|
|
101
|
+
const rows = mapOtelRows(rawRows);
|
|
102
|
+
return { rows, maxId: scanMaxId };
|
|
103
|
+
}
|
|
104
|
+
var METRICS_SQL = `
|
|
105
|
+
SELECT id, sync_id, timestamp_ns, name, value, metric_type, unit,
|
|
106
|
+
attributes, resource_attributes, session_id
|
|
107
|
+
FROM otel_metrics
|
|
108
|
+
WHERE id > ?
|
|
109
|
+
ORDER BY id
|
|
110
|
+
LIMIT ?
|
|
111
|
+
`;
|
|
112
|
+
function readMetrics(afterId, limit) {
|
|
113
|
+
const db = getDb();
|
|
114
|
+
const rawRows = db.prepare(METRICS_SQL).all(afterId, limit);
|
|
115
|
+
const rows = rawRows.map((r) => ({
|
|
116
|
+
id: r.id,
|
|
117
|
+
syncId: r.sync_id,
|
|
118
|
+
timestampNs: r.timestamp_ns,
|
|
119
|
+
name: r.name,
|
|
120
|
+
value: r.value,
|
|
121
|
+
metricType: r.metric_type,
|
|
122
|
+
unit: r.unit,
|
|
123
|
+
attributes: parseJson(r.attributes),
|
|
124
|
+
resourceAttributes: parseJson(r.resource_attributes),
|
|
125
|
+
sessionId: r.session_id
|
|
126
|
+
}));
|
|
127
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
128
|
+
return { rows, maxId };
|
|
129
|
+
}
|
|
130
|
+
var SCANNER_TURNS_SQL = `
|
|
131
|
+
SELECT t.id, t.session_id, t.sync_id, t.source, t.turn_index, t.timestamp_ms,
|
|
132
|
+
t.model, t.role, t.content_preview,
|
|
133
|
+
t.input_tokens, t.output_tokens, t.cache_read_tokens,
|
|
134
|
+
t.cache_creation_tokens, t.reasoning_tokens,
|
|
135
|
+
s.cli_version
|
|
136
|
+
FROM scanner_turns t
|
|
137
|
+
LEFT JOIN sessions s ON s.session_id = t.session_id
|
|
138
|
+
WHERE t.id > ?
|
|
139
|
+
ORDER BY t.id
|
|
140
|
+
LIMIT ?
|
|
141
|
+
`;
|
|
142
|
+
function readScannerTurns(afterId, limit) {
|
|
143
|
+
const db = getDb();
|
|
144
|
+
const rawRows = db.prepare(SCANNER_TURNS_SQL).all(afterId, limit);
|
|
145
|
+
const rows = rawRows.map((r) => ({
|
|
146
|
+
id: r.id,
|
|
147
|
+
sessionId: r.session_id,
|
|
148
|
+
syncId: r.sync_id,
|
|
149
|
+
source: r.source,
|
|
150
|
+
turnIndex: r.turn_index,
|
|
151
|
+
timestampMs: r.timestamp_ms,
|
|
152
|
+
model: r.model,
|
|
153
|
+
role: r.role,
|
|
154
|
+
contentPreview: r.content_preview,
|
|
155
|
+
inputTokens: r.input_tokens,
|
|
156
|
+
outputTokens: r.output_tokens,
|
|
157
|
+
cacheReadTokens: r.cache_read_tokens,
|
|
158
|
+
cacheCreationTokens: r.cache_creation_tokens,
|
|
159
|
+
reasoningTokens: r.reasoning_tokens,
|
|
160
|
+
cliVersion: r.cli_version
|
|
161
|
+
}));
|
|
162
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
163
|
+
return { rows, maxId };
|
|
164
|
+
}
|
|
165
|
+
var SCANNER_EVENTS_SQL = `
|
|
166
|
+
SELECT id, session_id, sync_id, source, event_type, timestamp_ms,
|
|
167
|
+
tool_name, tool_input, tool_output, content, metadata
|
|
168
|
+
FROM scanner_events
|
|
169
|
+
WHERE id > ?
|
|
170
|
+
ORDER BY id
|
|
171
|
+
LIMIT ?
|
|
172
|
+
`;
|
|
173
|
+
function readScannerEvents(afterId, limit) {
|
|
174
|
+
const db = getDb();
|
|
175
|
+
const rawRows = db.prepare(SCANNER_EVENTS_SQL).all(afterId, limit);
|
|
176
|
+
const rows = rawRows.map((r) => ({
|
|
177
|
+
id: r.id,
|
|
178
|
+
sessionId: r.session_id,
|
|
179
|
+
syncId: r.sync_id,
|
|
180
|
+
source: r.source,
|
|
181
|
+
eventType: r.event_type,
|
|
182
|
+
timestampMs: r.timestamp_ms,
|
|
183
|
+
toolName: r.tool_name,
|
|
184
|
+
toolInput: r.tool_input,
|
|
185
|
+
toolOutput: r.tool_output,
|
|
186
|
+
content: r.content,
|
|
187
|
+
metadata: parseJson(r.metadata)
|
|
188
|
+
}));
|
|
189
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
190
|
+
return { rows, maxId };
|
|
191
|
+
}
|
|
192
|
+
var SPANS_SQL = `
|
|
193
|
+
SELECT id, trace_id, span_id, parent_span_id, name, kind,
|
|
194
|
+
start_time_ns, end_time_ns, status_code, status_message,
|
|
195
|
+
attributes, resource_attributes, session_id
|
|
196
|
+
FROM otel_spans
|
|
197
|
+
WHERE id > ?
|
|
198
|
+
ORDER BY id
|
|
199
|
+
LIMIT ?
|
|
200
|
+
`;
|
|
201
|
+
function readOtelSpans(afterId, limit) {
|
|
202
|
+
const db = getDb();
|
|
203
|
+
const rawRows = db.prepare(SPANS_SQL).all(afterId, limit);
|
|
204
|
+
const rows = rawRows.map((r) => ({
|
|
205
|
+
id: r.id,
|
|
206
|
+
traceId: r.trace_id,
|
|
207
|
+
spanId: r.span_id,
|
|
208
|
+
parentSpanId: r.parent_span_id,
|
|
209
|
+
name: r.name,
|
|
210
|
+
kind: r.kind,
|
|
211
|
+
startTimeNs: r.start_time_ns,
|
|
212
|
+
endTimeNs: r.end_time_ns,
|
|
213
|
+
statusCode: r.status_code,
|
|
214
|
+
statusMessage: r.status_message,
|
|
215
|
+
attributes: parseJson(r.attributes),
|
|
216
|
+
resourceAttributes: parseJson(r.resource_attributes),
|
|
217
|
+
sessionId: r.session_id
|
|
218
|
+
}));
|
|
219
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
220
|
+
return { rows, maxId };
|
|
221
|
+
}
|
|
222
|
+
function parseJsonArray(raw) {
|
|
223
|
+
if (!raw) return [];
|
|
224
|
+
try {
|
|
225
|
+
const parsed = JSON.parse(raw);
|
|
226
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
227
|
+
} catch {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function parseJsonObject(raw) {
|
|
232
|
+
if (!raw) return {};
|
|
233
|
+
try {
|
|
234
|
+
const parsed = JSON.parse(raw);
|
|
235
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : {};
|
|
236
|
+
} catch {
|
|
237
|
+
return {};
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
var USER_CONFIG_SQL = `
|
|
241
|
+
SELECT id, device_name, snapshot_at_ms, content_hash,
|
|
242
|
+
permissions, enabled_plugins, hooks, commands, rules, skills
|
|
243
|
+
FROM user_config_snapshots
|
|
244
|
+
WHERE id > ?
|
|
245
|
+
ORDER BY id
|
|
246
|
+
LIMIT ?
|
|
247
|
+
`;
|
|
248
|
+
function readUserConfigSnapshots(afterId, limit) {
|
|
249
|
+
const db = getDb();
|
|
250
|
+
const rawRows = db.prepare(USER_CONFIG_SQL).all(afterId, limit);
|
|
251
|
+
const rows = rawRows.map((r) => ({
|
|
252
|
+
id: r.id,
|
|
253
|
+
deviceName: r.device_name,
|
|
254
|
+
snapshotAtMs: r.snapshot_at_ms,
|
|
255
|
+
contentHash: r.content_hash,
|
|
256
|
+
permissions: parseJsonObject(r.permissions),
|
|
257
|
+
enabledPlugins: parseJsonArray(r.enabled_plugins),
|
|
258
|
+
hooks: parseJsonArray(r.hooks),
|
|
259
|
+
commands: parseJsonArray(r.commands),
|
|
260
|
+
rules: parseJsonArray(r.rules),
|
|
261
|
+
skills: parseJsonArray(r.skills)
|
|
262
|
+
}));
|
|
263
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
264
|
+
return { rows, maxId };
|
|
265
|
+
}
|
|
266
|
+
var REPO_CONFIG_SQL = `
|
|
267
|
+
SELECT id, repository, cwd, session_id, snapshot_at_ms, content_hash,
|
|
268
|
+
hooks, mcp_servers, commands, agents, rules,
|
|
269
|
+
local_hooks, local_mcp_servers, local_permissions,
|
|
270
|
+
local_is_gitignored, instructions
|
|
271
|
+
FROM repo_config_snapshots
|
|
272
|
+
WHERE id > ?
|
|
273
|
+
ORDER BY id
|
|
274
|
+
LIMIT ?
|
|
275
|
+
`;
|
|
276
|
+
function readRepoConfigSnapshots(afterId, limit) {
|
|
277
|
+
const db = getDb();
|
|
278
|
+
const rawRows = db.prepare(REPO_CONFIG_SQL).all(afterId, limit);
|
|
279
|
+
const rows = rawRows.map((r) => ({
|
|
280
|
+
id: r.id,
|
|
281
|
+
repository: r.repository,
|
|
282
|
+
cwd: r.cwd,
|
|
283
|
+
sessionId: r.session_id,
|
|
284
|
+
snapshotAtMs: r.snapshot_at_ms,
|
|
285
|
+
contentHash: r.content_hash,
|
|
286
|
+
hooks: parseJsonArray(r.hooks),
|
|
287
|
+
mcpServers: parseJsonArray(r.mcp_servers),
|
|
288
|
+
commands: parseJsonArray(r.commands),
|
|
289
|
+
agents: parseJsonArray(r.agents),
|
|
290
|
+
rules: parseJsonArray(r.rules),
|
|
291
|
+
localHooks: parseJsonArray(r.local_hooks),
|
|
292
|
+
localMcpServers: parseJsonArray(r.local_mcp_servers),
|
|
293
|
+
localPermissions: parseJsonObject(r.local_permissions),
|
|
294
|
+
localIsGitignored: r.local_is_gitignored === 1,
|
|
295
|
+
instructions: parseJsonArray(r.instructions)
|
|
296
|
+
}));
|
|
297
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
298
|
+
return { rows, maxId };
|
|
299
|
+
}
|
|
300
|
+
var MESSAGES_SQL = `
|
|
301
|
+
SELECT id, session_id, ordinal, role, content, timestamp_ms,
|
|
302
|
+
has_thinking, has_tool_use, content_length, is_system,
|
|
303
|
+
model, token_usage, context_tokens, output_tokens,
|
|
304
|
+
has_context_tokens, has_output_tokens
|
|
305
|
+
FROM messages
|
|
306
|
+
WHERE id > ?
|
|
307
|
+
ORDER BY id
|
|
308
|
+
LIMIT ?
|
|
309
|
+
`;
|
|
310
|
+
function readMessages(afterId, limit) {
|
|
311
|
+
const db = getDb();
|
|
312
|
+
const rawRows = db.prepare(MESSAGES_SQL).all(afterId, limit);
|
|
313
|
+
const rows = rawRows.map((r) => ({
|
|
314
|
+
id: r.id,
|
|
315
|
+
sessionId: r.session_id,
|
|
316
|
+
ordinal: r.ordinal,
|
|
317
|
+
role: r.role,
|
|
318
|
+
content: r.content,
|
|
319
|
+
timestampMs: r.timestamp_ms,
|
|
320
|
+
hasThinking: r.has_thinking === 1,
|
|
321
|
+
hasToolUse: r.has_tool_use === 1,
|
|
322
|
+
contentLength: r.content_length,
|
|
323
|
+
isSystem: r.is_system === 1,
|
|
324
|
+
model: r.model,
|
|
325
|
+
tokenUsage: r.token_usage,
|
|
326
|
+
contextTokens: r.context_tokens,
|
|
327
|
+
outputTokens: r.output_tokens,
|
|
328
|
+
hasContextTokens: r.has_context_tokens === 1,
|
|
329
|
+
hasOutputTokens: r.has_output_tokens === 1
|
|
330
|
+
}));
|
|
331
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
332
|
+
return { rows, maxId };
|
|
333
|
+
}
|
|
334
|
+
var TOOL_CALLS_SQL = `
|
|
335
|
+
SELECT id, message_id, session_id, sync_id, tool_name, category, tool_use_id,
|
|
336
|
+
input_json, skill_name, result_content_length, result_content,
|
|
337
|
+
subagent_session_id
|
|
338
|
+
FROM tool_calls
|
|
339
|
+
WHERE id > ?
|
|
340
|
+
ORDER BY id
|
|
341
|
+
LIMIT ?
|
|
342
|
+
`;
|
|
343
|
+
function readToolCalls(afterId, limit) {
|
|
344
|
+
const db = getDb();
|
|
345
|
+
const rawRows = db.prepare(TOOL_CALLS_SQL).all(afterId, limit);
|
|
346
|
+
const rows = rawRows.map((r) => ({
|
|
347
|
+
id: r.id,
|
|
348
|
+
messageId: r.message_id,
|
|
349
|
+
sessionId: r.session_id,
|
|
350
|
+
syncId: r.sync_id,
|
|
351
|
+
toolName: r.tool_name,
|
|
352
|
+
category: r.category,
|
|
353
|
+
toolUseId: r.tool_use_id,
|
|
354
|
+
inputJson: r.input_json,
|
|
355
|
+
skillName: r.skill_name,
|
|
356
|
+
resultContentLength: r.result_content_length,
|
|
357
|
+
resultContent: r.result_content,
|
|
358
|
+
subagentSessionId: r.subagent_session_id
|
|
359
|
+
}));
|
|
360
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
361
|
+
return { rows, maxId };
|
|
362
|
+
}
|
|
363
|
+
var SESSIONS_SQL = `
|
|
364
|
+
SELECT session_id, target, started_at_ms, ended_at_ms, cwd, first_prompt,
|
|
365
|
+
permission_mode, agent_version,
|
|
366
|
+
total_input_tokens, total_output_tokens, total_cache_read_tokens,
|
|
367
|
+
total_cache_creation_tokens, total_reasoning_tokens, turn_count,
|
|
368
|
+
models, summary, tool_counts, hook_tool_counts, event_type_counts, hook_event_type_counts, sync_seq,
|
|
369
|
+
project, machine, message_count, user_message_count,
|
|
370
|
+
parent_session_id, relationship_type, is_automated, created_at
|
|
371
|
+
FROM sessions
|
|
372
|
+
WHERE sync_seq > ?
|
|
373
|
+
ORDER BY sync_seq
|
|
374
|
+
LIMIT ?
|
|
375
|
+
`;
|
|
376
|
+
function readSessions(afterSeq, limit) {
|
|
377
|
+
const db = getDb();
|
|
378
|
+
const rawRows = db.prepare(SESSIONS_SQL).all(afterSeq, limit);
|
|
379
|
+
if (rawRows.length === 0) return { rows: [], maxId: afterSeq };
|
|
380
|
+
const sessionIds = rawRows.map((r) => r.session_id);
|
|
381
|
+
const placeholders = sessionIds.map(() => "?").join(", ");
|
|
382
|
+
const repoRows = db.prepare(
|
|
383
|
+
`SELECT session_id, repository, first_seen_ms, git_user_name, git_user_email, branch
|
|
384
|
+
FROM session_repositories
|
|
385
|
+
WHERE session_id IN (${placeholders})`
|
|
386
|
+
).all(...sessionIds);
|
|
387
|
+
const cwdRows = db.prepare(
|
|
388
|
+
`SELECT session_id, cwd, first_seen_ms
|
|
389
|
+
FROM session_cwds
|
|
390
|
+
WHERE session_id IN (${placeholders})`
|
|
391
|
+
).all(...sessionIds);
|
|
392
|
+
const reposBySession = /* @__PURE__ */ new Map();
|
|
393
|
+
for (const r of repoRows) {
|
|
394
|
+
const list = reposBySession.get(r.session_id) ?? [];
|
|
395
|
+
list.push({
|
|
396
|
+
repository: r.repository,
|
|
397
|
+
firstSeenMs: r.first_seen_ms,
|
|
398
|
+
gitUserName: r.git_user_name,
|
|
399
|
+
gitUserEmail: r.git_user_email,
|
|
400
|
+
branch: r.branch
|
|
401
|
+
});
|
|
402
|
+
reposBySession.set(r.session_id, list);
|
|
403
|
+
}
|
|
404
|
+
const cwdsBySession = /* @__PURE__ */ new Map();
|
|
405
|
+
for (const r of cwdRows) {
|
|
406
|
+
const list = cwdsBySession.get(r.session_id) ?? [];
|
|
407
|
+
list.push({ cwd: r.cwd, firstSeenMs: r.first_seen_ms });
|
|
408
|
+
cwdsBySession.set(r.session_id, list);
|
|
409
|
+
}
|
|
410
|
+
const rows = rawRows.map((r) => ({
|
|
411
|
+
sessionId: r.session_id,
|
|
412
|
+
target: r.target,
|
|
413
|
+
startedAtMs: r.started_at_ms,
|
|
414
|
+
endedAtMs: r.ended_at_ms,
|
|
415
|
+
cwd: r.cwd,
|
|
416
|
+
firstPrompt: r.first_prompt,
|
|
417
|
+
permissionMode: r.permission_mode,
|
|
418
|
+
agentVersion: r.agent_version,
|
|
419
|
+
totalInputTokens: r.total_input_tokens,
|
|
420
|
+
totalOutputTokens: r.total_output_tokens,
|
|
421
|
+
totalCacheReadTokens: r.total_cache_read_tokens,
|
|
422
|
+
totalCacheCreationTokens: r.total_cache_creation_tokens,
|
|
423
|
+
totalReasoningTokens: r.total_reasoning_tokens,
|
|
424
|
+
turnCount: r.turn_count,
|
|
425
|
+
models: r.models,
|
|
426
|
+
summary: r.summary,
|
|
427
|
+
toolCounts: parseJsonObject(r.tool_counts),
|
|
428
|
+
hookToolCounts: parseJsonObject(r.hook_tool_counts),
|
|
429
|
+
eventTypeCounts: parseJsonObject(r.event_type_counts),
|
|
430
|
+
hookEventTypeCounts: parseJsonObject(r.hook_event_type_counts),
|
|
431
|
+
project: r.project,
|
|
432
|
+
machine: r.machine,
|
|
433
|
+
messageCount: r.message_count,
|
|
434
|
+
userMessageCount: r.user_message_count,
|
|
435
|
+
parentSessionId: r.parent_session_id,
|
|
436
|
+
relationshipType: r.relationship_type,
|
|
437
|
+
isAutomated: r.is_automated === 1,
|
|
438
|
+
createdAt: r.created_at,
|
|
439
|
+
repositories: reposBySession.get(r.session_id) ?? [],
|
|
440
|
+
cwds: cwdsBySession.get(r.session_id) ?? []
|
|
441
|
+
}));
|
|
442
|
+
const maxId = rawRows[rawRows.length - 1].sync_seq;
|
|
443
|
+
return { rows, maxId };
|
|
444
|
+
}
|
|
445
|
+
function readSessionMessages(sessionId, afterId, limit) {
|
|
446
|
+
const db = getDb();
|
|
447
|
+
const rawRows = db.prepare(
|
|
448
|
+
`SELECT id, session_id, ordinal, role, content, timestamp_ms,
|
|
449
|
+
has_thinking, has_tool_use, content_length, is_system,
|
|
450
|
+
model, token_usage, context_tokens, output_tokens,
|
|
451
|
+
has_context_tokens, has_output_tokens
|
|
452
|
+
FROM messages
|
|
453
|
+
WHERE session_id = ? AND id > ?
|
|
454
|
+
ORDER BY id
|
|
455
|
+
LIMIT ?`
|
|
456
|
+
).all(sessionId, afterId, limit);
|
|
457
|
+
const rows = rawRows.map((r) => ({
|
|
458
|
+
id: r.id,
|
|
459
|
+
sessionId: r.session_id,
|
|
460
|
+
ordinal: r.ordinal,
|
|
461
|
+
role: r.role,
|
|
462
|
+
content: r.content,
|
|
463
|
+
timestampMs: r.timestamp_ms,
|
|
464
|
+
hasThinking: r.has_thinking === 1,
|
|
465
|
+
hasToolUse: r.has_tool_use === 1,
|
|
466
|
+
contentLength: r.content_length,
|
|
467
|
+
isSystem: r.is_system === 1,
|
|
468
|
+
model: r.model,
|
|
469
|
+
tokenUsage: r.token_usage,
|
|
470
|
+
contextTokens: r.context_tokens,
|
|
471
|
+
outputTokens: r.output_tokens,
|
|
472
|
+
hasContextTokens: r.has_context_tokens === 1,
|
|
473
|
+
hasOutputTokens: r.has_output_tokens === 1
|
|
474
|
+
}));
|
|
475
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
476
|
+
return { rows, maxId };
|
|
477
|
+
}
|
|
478
|
+
function readSessionToolCalls(sessionId, afterId, limit) {
|
|
479
|
+
const db = getDb();
|
|
480
|
+
const rawRows = db.prepare(
|
|
481
|
+
`SELECT id, message_id, session_id, sync_id, tool_name, category, tool_use_id,
|
|
482
|
+
input_json, skill_name, result_content_length, result_content,
|
|
483
|
+
subagent_session_id
|
|
484
|
+
FROM tool_calls
|
|
485
|
+
WHERE session_id = ? AND id > ?
|
|
486
|
+
ORDER BY id
|
|
487
|
+
LIMIT ?`
|
|
488
|
+
).all(sessionId, afterId, limit);
|
|
489
|
+
const rows = rawRows.map((r) => ({
|
|
490
|
+
id: r.id,
|
|
491
|
+
messageId: r.message_id,
|
|
492
|
+
sessionId: r.session_id,
|
|
493
|
+
syncId: r.sync_id,
|
|
494
|
+
toolName: r.tool_name,
|
|
495
|
+
category: r.category,
|
|
496
|
+
toolUseId: r.tool_use_id,
|
|
497
|
+
inputJson: r.input_json,
|
|
498
|
+
skillName: r.skill_name,
|
|
499
|
+
resultContentLength: r.result_content_length,
|
|
500
|
+
resultContent: r.result_content,
|
|
501
|
+
subagentSessionId: r.subagent_session_id
|
|
502
|
+
}));
|
|
503
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
504
|
+
return { rows, maxId };
|
|
505
|
+
}
|
|
506
|
+
function readSessionScannerTurns(sessionId, afterId, limit) {
|
|
507
|
+
const db = getDb();
|
|
508
|
+
const rawRows = db.prepare(
|
|
509
|
+
`SELECT t.id, t.session_id, t.sync_id, t.source, t.turn_index, t.timestamp_ms,
|
|
510
|
+
t.model, t.role, t.content_preview,
|
|
511
|
+
t.input_tokens, t.output_tokens, t.cache_read_tokens,
|
|
512
|
+
t.cache_creation_tokens, t.reasoning_tokens,
|
|
513
|
+
s.cli_version
|
|
514
|
+
FROM scanner_turns t
|
|
515
|
+
LEFT JOIN sessions s ON s.session_id = t.session_id
|
|
516
|
+
WHERE t.session_id = ? AND t.id > ?
|
|
517
|
+
ORDER BY t.id
|
|
518
|
+
LIMIT ?`
|
|
519
|
+
).all(sessionId, afterId, limit);
|
|
520
|
+
const rows = rawRows.map((r) => ({
|
|
521
|
+
id: r.id,
|
|
522
|
+
sessionId: r.session_id,
|
|
523
|
+
syncId: r.sync_id,
|
|
524
|
+
source: r.source,
|
|
525
|
+
turnIndex: r.turn_index,
|
|
526
|
+
timestampMs: r.timestamp_ms,
|
|
527
|
+
model: r.model,
|
|
528
|
+
role: r.role,
|
|
529
|
+
contentPreview: r.content_preview,
|
|
530
|
+
inputTokens: r.input_tokens,
|
|
531
|
+
outputTokens: r.output_tokens,
|
|
532
|
+
cacheReadTokens: r.cache_read_tokens,
|
|
533
|
+
cacheCreationTokens: r.cache_creation_tokens,
|
|
534
|
+
reasoningTokens: r.reasoning_tokens,
|
|
535
|
+
cliVersion: r.cli_version
|
|
536
|
+
}));
|
|
537
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
538
|
+
return { rows, maxId };
|
|
539
|
+
}
|
|
540
|
+
function readSessionScannerEvents(sessionId, afterId, limit) {
|
|
541
|
+
const db = getDb();
|
|
542
|
+
const rawRows = db.prepare(
|
|
543
|
+
`SELECT id, session_id, sync_id, source, event_type, timestamp_ms,
|
|
544
|
+
tool_name, tool_input, tool_output, content, metadata
|
|
545
|
+
FROM scanner_events
|
|
546
|
+
WHERE session_id = ? AND id > ?
|
|
547
|
+
ORDER BY id
|
|
548
|
+
LIMIT ?`
|
|
549
|
+
).all(sessionId, afterId, limit);
|
|
550
|
+
const rows = rawRows.map((r) => ({
|
|
551
|
+
id: r.id,
|
|
552
|
+
sessionId: r.session_id,
|
|
553
|
+
syncId: r.sync_id,
|
|
554
|
+
source: r.source,
|
|
555
|
+
eventType: r.event_type,
|
|
556
|
+
timestampMs: r.timestamp_ms,
|
|
557
|
+
toolName: r.tool_name,
|
|
558
|
+
toolInput: r.tool_input,
|
|
559
|
+
toolOutput: r.tool_output,
|
|
560
|
+
content: r.content,
|
|
561
|
+
metadata: parseJson(r.metadata)
|
|
562
|
+
}));
|
|
563
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
564
|
+
return { rows, maxId };
|
|
565
|
+
}
|
|
566
|
+
function readSessionHookEvents(sessionId, afterId, limit) {
|
|
567
|
+
const db = getDb();
|
|
568
|
+
const rawRows = db.prepare(
|
|
569
|
+
`SELECT h.id, h.session_id, h.sync_id, h.event_type, h.timestamp_ms, h.cwd, h.repository,
|
|
570
|
+
h.tool_name, decompress(h.payload) as payload,
|
|
571
|
+
h.user_prompt, h.file_path, h.command, h.tool_result,
|
|
572
|
+
s.target
|
|
573
|
+
FROM hook_events h
|
|
574
|
+
LEFT JOIN sessions s ON s.session_id = h.session_id
|
|
575
|
+
WHERE h.session_id = ? AND h.id > ?
|
|
576
|
+
ORDER BY h.id
|
|
577
|
+
LIMIT ?`
|
|
578
|
+
).all(sessionId, afterId, limit);
|
|
579
|
+
const rows = rawRows.map((r) => ({
|
|
580
|
+
hookId: r.id,
|
|
581
|
+
sessionId: r.session_id,
|
|
582
|
+
syncId: r.sync_id,
|
|
583
|
+
eventType: r.event_type,
|
|
584
|
+
timestampMs: r.timestamp_ms,
|
|
585
|
+
cwd: r.cwd,
|
|
586
|
+
repository: r.repository,
|
|
587
|
+
toolName: r.tool_name,
|
|
588
|
+
payload: parseJson(r.payload),
|
|
589
|
+
userPrompt: r.user_prompt,
|
|
590
|
+
filePath: r.file_path,
|
|
591
|
+
command: r.command,
|
|
592
|
+
toolResult: r.tool_result,
|
|
593
|
+
target: r.target
|
|
594
|
+
}));
|
|
595
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].hookId : afterId;
|
|
596
|
+
return { rows, maxId };
|
|
597
|
+
}
|
|
598
|
+
function readSessionOtelLogs(sessionId, afterId, limit) {
|
|
599
|
+
const db = getDb();
|
|
600
|
+
const rawRows = db.prepare(
|
|
601
|
+
`SELECT id, sync_id, timestamp_ns, body, attributes, resource_attributes,
|
|
602
|
+
severity_text, session_id, prompt_id, trace_id, span_id
|
|
603
|
+
FROM otel_logs
|
|
604
|
+
WHERE session_id = ? AND id > ?
|
|
605
|
+
ORDER BY id
|
|
606
|
+
LIMIT ?`
|
|
607
|
+
).all(sessionId, afterId, limit);
|
|
608
|
+
const rows = mapOtelRows(rawRows);
|
|
609
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
610
|
+
return { rows, maxId };
|
|
611
|
+
}
|
|
612
|
+
function readSessionOtelMetrics(sessionId, afterId, limit) {
|
|
613
|
+
const db = getDb();
|
|
614
|
+
const rawRows = db.prepare(
|
|
615
|
+
`SELECT id, sync_id, timestamp_ns, name, value, metric_type, unit,
|
|
616
|
+
attributes, resource_attributes, session_id
|
|
617
|
+
FROM otel_metrics
|
|
618
|
+
WHERE session_id = ? AND id > ?
|
|
619
|
+
ORDER BY id
|
|
620
|
+
LIMIT ?`
|
|
621
|
+
).all(sessionId, afterId, limit);
|
|
622
|
+
const rows = rawRows.map((r) => ({
|
|
623
|
+
id: r.id,
|
|
624
|
+
syncId: r.sync_id,
|
|
625
|
+
timestampNs: r.timestamp_ns,
|
|
626
|
+
name: r.name,
|
|
627
|
+
value: r.value,
|
|
628
|
+
metricType: r.metric_type,
|
|
629
|
+
unit: r.unit,
|
|
630
|
+
attributes: parseJson(r.attributes),
|
|
631
|
+
resourceAttributes: parseJson(r.resource_attributes),
|
|
632
|
+
sessionId: r.session_id
|
|
633
|
+
}));
|
|
634
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
635
|
+
return { rows, maxId };
|
|
636
|
+
}
|
|
637
|
+
function readSessionOtelSpans(sessionId, afterId, limit) {
|
|
638
|
+
const db = getDb();
|
|
639
|
+
const rawRows = db.prepare(
|
|
640
|
+
`SELECT id, trace_id, span_id, parent_span_id, name, kind,
|
|
641
|
+
start_time_ns, end_time_ns, status_code, status_message,
|
|
642
|
+
attributes, resource_attributes, session_id
|
|
643
|
+
FROM otel_spans
|
|
644
|
+
WHERE session_id = ? AND id > ?
|
|
645
|
+
ORDER BY id
|
|
646
|
+
LIMIT ?`
|
|
647
|
+
).all(sessionId, afterId, limit);
|
|
648
|
+
const rows = rawRows.map((r) => ({
|
|
649
|
+
id: r.id,
|
|
650
|
+
traceId: r.trace_id,
|
|
651
|
+
spanId: r.span_id,
|
|
652
|
+
parentSpanId: r.parent_span_id,
|
|
653
|
+
name: r.name,
|
|
654
|
+
kind: r.kind,
|
|
655
|
+
startTimeNs: r.start_time_ns,
|
|
656
|
+
endTimeNs: r.end_time_ns,
|
|
657
|
+
statusCode: r.status_code,
|
|
658
|
+
statusMessage: r.status_message,
|
|
659
|
+
attributes: parseJson(r.attributes),
|
|
660
|
+
resourceAttributes: parseJson(r.resource_attributes),
|
|
661
|
+
sessionId: r.session_id
|
|
662
|
+
}));
|
|
663
|
+
const maxId = rows.length > 0 ? rows[rows.length - 1].id : afterId;
|
|
664
|
+
return { rows, maxId };
|
|
665
|
+
}
|
|
666
|
+
var SESSION_READERS = {
|
|
667
|
+
messages: readSessionMessages,
|
|
668
|
+
tool_calls: readSessionToolCalls,
|
|
669
|
+
scanner_turns: readSessionScannerTurns,
|
|
670
|
+
scanner_events: readSessionScannerEvents,
|
|
671
|
+
hook_events: readSessionHookEvents,
|
|
672
|
+
otel_logs: readSessionOtelLogs,
|
|
673
|
+
otel_metrics: readSessionOtelMetrics,
|
|
674
|
+
otel_spans: readSessionOtelSpans
|
|
675
|
+
};
|
|
676
|
+
|
|
677
|
+
// src/sync/registry.ts
|
|
678
|
+
var TABLE_SYNC_REGISTRY = [
|
|
679
|
+
// ── Session-linked tables (filtered by repo attribution) ─────────────────
|
|
680
|
+
{
|
|
681
|
+
table: "sessions",
|
|
682
|
+
logNoun: "sessions",
|
|
683
|
+
read: (afterId, limit) => readSessions(afterId, limit),
|
|
684
|
+
sessionLinked: true
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
table: "messages",
|
|
688
|
+
logNoun: "messages",
|
|
689
|
+
read: (afterId, limit) => readMessages(afterId, limit),
|
|
690
|
+
sessionLinked: true
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
table: "tool_calls",
|
|
694
|
+
logNoun: "tool calls",
|
|
695
|
+
read: (afterId, limit) => readToolCalls(afterId, limit),
|
|
696
|
+
sessionLinked: true
|
|
697
|
+
},
|
|
698
|
+
{
|
|
699
|
+
table: "scanner_turns",
|
|
700
|
+
logNoun: "turns",
|
|
701
|
+
read: (afterId, limit) => readScannerTurns(afterId, limit),
|
|
702
|
+
sessionLinked: true
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
table: "scanner_events",
|
|
706
|
+
logNoun: "events",
|
|
707
|
+
read: (afterId, limit) => readScannerEvents(afterId, limit),
|
|
708
|
+
sessionLinked: true
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
table: "hook_events",
|
|
712
|
+
logNoun: "events",
|
|
713
|
+
read: (afterId, limit) => readHookEvents(afterId, limit),
|
|
714
|
+
sessionLinked: true
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
table: "otel_logs",
|
|
718
|
+
logNoun: "logs",
|
|
719
|
+
read: (afterId, limit) => readOtelLogs(afterId, limit, false),
|
|
720
|
+
sessionLinked: true
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
table: "otel_metrics",
|
|
724
|
+
logNoun: "metrics",
|
|
725
|
+
read: (afterId, limit) => readMetrics(afterId, limit),
|
|
726
|
+
sessionLinked: true
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
table: "otel_spans",
|
|
730
|
+
logNoun: "spans",
|
|
731
|
+
read: (afterId, limit) => readOtelSpans(afterId, limit),
|
|
732
|
+
sessionLinked: true
|
|
733
|
+
},
|
|
734
|
+
// ── Non-session tables (always synced) ────────────────────────────────────
|
|
735
|
+
{
|
|
736
|
+
table: "user_config_snapshots",
|
|
737
|
+
logNoun: "snapshots",
|
|
738
|
+
read: (afterId, limit) => readUserConfigSnapshots(afterId, limit),
|
|
739
|
+
sessionLinked: false
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
table: "repo_config_snapshots",
|
|
743
|
+
logNoun: "snapshots",
|
|
744
|
+
read: (afterId, limit) => readRepoConfigSnapshots(afterId, limit),
|
|
745
|
+
sessionLinked: false
|
|
746
|
+
}
|
|
747
|
+
];
|
|
748
|
+
|
|
749
|
+
// src/sync/watermark.ts
|
|
750
|
+
function watermarkKey(table, targetName) {
|
|
751
|
+
return `${table}:${targetName}`;
|
|
752
|
+
}
|
|
753
|
+
function readWatermark(key) {
|
|
754
|
+
const db = getDb();
|
|
755
|
+
const row = db.prepare("SELECT value FROM watermarks WHERE key = ?").get(key);
|
|
756
|
+
return row?.value ?? 0;
|
|
757
|
+
}
|
|
758
|
+
function writeWatermark(key, value) {
|
|
759
|
+
const db = getDb();
|
|
760
|
+
db.prepare(
|
|
761
|
+
"INSERT INTO watermarks (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
|
|
762
|
+
).run(key, value);
|
|
763
|
+
}
|
|
764
|
+
function resetWatermarks(targetName) {
|
|
765
|
+
const db = getDb();
|
|
766
|
+
if (targetName) {
|
|
767
|
+
const stmt = db.prepare("DELETE FROM watermarks WHERE key = ?");
|
|
768
|
+
for (const desc of TABLE_SYNC_REGISTRY) {
|
|
769
|
+
stmt.run(watermarkKey(desc.table, targetName));
|
|
770
|
+
}
|
|
771
|
+
db.prepare("DELETE FROM target_session_sync WHERE target = ?").run(
|
|
772
|
+
targetName
|
|
773
|
+
);
|
|
774
|
+
} else {
|
|
775
|
+
db.prepare("DELETE FROM watermarks").run();
|
|
776
|
+
db.prepare("DELETE FROM target_session_sync").run();
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
export {
|
|
781
|
+
SESSION_READERS,
|
|
782
|
+
TABLE_SYNC_REGISTRY,
|
|
783
|
+
watermarkKey,
|
|
784
|
+
readWatermark,
|
|
785
|
+
writeWatermark,
|
|
786
|
+
resetWatermarks
|
|
787
|
+
};
|
|
788
|
+
//# sourceMappingURL=chunk-SEXU2WYG.js.map
|