zozul-cli 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/.env.example +44 -0
- package/.github/workflows/publish.yml +26 -0
- package/DEVELOPMENT.md +288 -0
- package/LICENSE +201 -0
- package/README.md +178 -0
- package/dist/cli/commands.d.ts +3 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +307 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/format.d.ts +5 -0
- package/dist/cli/format.d.ts.map +1 -0
- package/dist/cli/format.js +115 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/context/index.d.ts +8 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +37 -0
- package/dist/context/index.js.map +1 -0
- package/dist/dashboard/html.d.ts +17 -0
- package/dist/dashboard/html.d.ts.map +1 -0
- package/dist/dashboard/html.js +79 -0
- package/dist/dashboard/html.js.map +1 -0
- package/dist/dashboard/index.html +1245 -0
- package/dist/hooks/config.d.ts +19 -0
- package/dist/hooks/config.d.ts.map +1 -0
- package/dist/hooks/config.js +106 -0
- package/dist/hooks/config.js.map +1 -0
- package/dist/hooks/git.d.ts +6 -0
- package/dist/hooks/git.d.ts.map +1 -0
- package/dist/hooks/git.js +73 -0
- package/dist/hooks/git.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/server.d.ts +16 -0
- package/dist/hooks/server.d.ts.map +1 -0
- package/dist/hooks/server.js +349 -0
- package/dist/hooks/server.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/otel/config.d.ts +36 -0
- package/dist/otel/config.d.ts.map +1 -0
- package/dist/otel/config.js +109 -0
- package/dist/otel/config.js.map +1 -0
- package/dist/otel/index.d.ts +4 -0
- package/dist/otel/index.d.ts.map +1 -0
- package/dist/otel/index.js +3 -0
- package/dist/otel/index.js.map +1 -0
- package/dist/otel/receiver.d.ts +10 -0
- package/dist/otel/receiver.d.ts.map +1 -0
- package/dist/otel/receiver.js +155 -0
- package/dist/otel/receiver.js.map +1 -0
- package/dist/parser/index.d.ts +4 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +3 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/ingest.d.ts +20 -0
- package/dist/parser/ingest.d.ts.map +1 -0
- package/dist/parser/ingest.js +98 -0
- package/dist/parser/ingest.js.map +1 -0
- package/dist/parser/jsonl.d.ts +14 -0
- package/dist/parser/jsonl.d.ts.map +1 -0
- package/dist/parser/jsonl.js +202 -0
- package/dist/parser/jsonl.js.map +1 -0
- package/dist/parser/types.d.ts +81 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js +9 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/parser/watcher.d.ts +16 -0
- package/dist/parser/watcher.d.ts.map +1 -0
- package/dist/parser/watcher.js +103 -0
- package/dist/parser/watcher.js.map +1 -0
- package/dist/pricing/index.d.ts +2 -0
- package/dist/pricing/index.d.ts.map +1 -0
- package/dist/pricing/index.js +37 -0
- package/dist/pricing/index.js.map +1 -0
- package/dist/service/index.d.ts +31 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +252 -0
- package/dist/service/index.js.map +1 -0
- package/dist/storage/db.d.ts +75 -0
- package/dist/storage/db.d.ts.map +1 -0
- package/dist/storage/db.js +117 -0
- package/dist/storage/db.js.map +1 -0
- package/dist/storage/index.d.ts +4 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/repo.d.ts +162 -0
- package/dist/storage/repo.d.ts.map +1 -0
- package/dist/storage/repo.js +472 -0
- package/dist/storage/repo.js.map +1 -0
- package/dist/sync/client.d.ts +24 -0
- package/dist/sync/client.d.ts.map +1 -0
- package/dist/sync/client.js +41 -0
- package/dist/sync/client.js.map +1 -0
- package/dist/sync/index.d.ts +18 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +135 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/sync.test.d.ts +2 -0
- package/dist/sync/sync.test.d.ts.map +1 -0
- package/dist/sync/sync.test.js +412 -0
- package/dist/sync/sync.test.js.map +1 -0
- package/dist/sync/transform.d.ts +80 -0
- package/dist/sync/transform.d.ts.map +1 -0
- package/dist/sync/transform.js +90 -0
- package/dist/sync/transform.js.map +1 -0
- package/package.json +50 -0
- package/src/cli/commands.ts +332 -0
- package/src/cli/format.ts +133 -0
- package/src/context/index.ts +42 -0
- package/src/dashboard/html.ts +97 -0
- package/src/dashboard/index.html +1245 -0
- package/src/hooks/config.ts +119 -0
- package/src/hooks/git.ts +77 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/server.ts +397 -0
- package/src/index.ts +6 -0
- package/src/otel/config.ts +141 -0
- package/src/otel/index.ts +8 -0
- package/src/otel/receiver.ts +183 -0
- package/src/parser/index.ts +3 -0
- package/src/parser/ingest.ts +119 -0
- package/src/parser/jsonl.ts +241 -0
- package/src/parser/types.ts +89 -0
- package/src/parser/watcher.ts +116 -0
- package/src/pricing/index.ts +51 -0
- package/src/service/index.ts +272 -0
- package/src/storage/db.ts +198 -0
- package/src/storage/index.ts +3 -0
- package/src/storage/repo.ts +601 -0
- package/src/sync/client.ts +63 -0
- package/src/sync/index.ts +207 -0
- package/src/sync/sync.test.ts +447 -0
- package/src/sync/transform.ts +184 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
export class SessionRepo {
|
|
2
|
+
db;
|
|
3
|
+
constructor(db) {
|
|
4
|
+
this.db = db;
|
|
5
|
+
}
|
|
6
|
+
upsertSession(session) {
|
|
7
|
+
this.db.prepare(`
|
|
8
|
+
INSERT INTO sessions (id, project_path, started_at, ended_at, total_input_tokens,
|
|
9
|
+
total_output_tokens, total_cache_read_tokens, total_cache_creation_tokens,
|
|
10
|
+
total_cost_usd, total_turns, total_duration_ms, model)
|
|
11
|
+
VALUES (@id, @project_path, @started_at, @ended_at, @total_input_tokens,
|
|
12
|
+
@total_output_tokens, @total_cache_read_tokens, @total_cache_creation_tokens,
|
|
13
|
+
@total_cost_usd, @total_turns, @total_duration_ms, @model)
|
|
14
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
15
|
+
started_at = MIN(started_at, @started_at),
|
|
16
|
+
project_path = COALESCE(@project_path, project_path),
|
|
17
|
+
ended_at = COALESCE(@ended_at, ended_at),
|
|
18
|
+
total_turns = @total_turns,
|
|
19
|
+
model = COALESCE(@model, model),
|
|
20
|
+
-- Use MAX so that OTEL-accumulated values are never clobbered by a JSONL re-ingest
|
|
21
|
+
total_input_tokens = MAX(total_input_tokens, @total_input_tokens),
|
|
22
|
+
total_output_tokens = MAX(total_output_tokens, @total_output_tokens),
|
|
23
|
+
total_cache_read_tokens = MAX(total_cache_read_tokens, @total_cache_read_tokens),
|
|
24
|
+
total_cache_creation_tokens = MAX(total_cache_creation_tokens, @total_cache_creation_tokens),
|
|
25
|
+
total_cost_usd = MAX(total_cost_usd, @total_cost_usd),
|
|
26
|
+
total_duration_ms = MAX(total_duration_ms, @total_duration_ms)
|
|
27
|
+
`).run({
|
|
28
|
+
...session,
|
|
29
|
+
ended_at: session.ended_at ?? null,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Accumulate metric deltas from a single OTEL export batch into the sessions row.
|
|
34
|
+
* Creates a stub session row if none exists yet (OTEL may arrive before JSONL ingest).
|
|
35
|
+
* All numeric fields are additive deltas — never a full replacement.
|
|
36
|
+
*/
|
|
37
|
+
updateSessionFromOtel(sessionId, deltas) {
|
|
38
|
+
this.db.prepare(`
|
|
39
|
+
INSERT INTO sessions (id, started_at, ended_at, total_cost_usd, total_input_tokens,
|
|
40
|
+
total_output_tokens, total_cache_read_tokens, total_cache_creation_tokens,
|
|
41
|
+
total_duration_ms, model)
|
|
42
|
+
VALUES (@id, @ts, @ts, @cost, @input, @output, @cache_read, @cache_creation, @duration_ms, @model)
|
|
43
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
44
|
+
total_cost_usd = total_cost_usd + @cost,
|
|
45
|
+
total_input_tokens = total_input_tokens + @input,
|
|
46
|
+
total_output_tokens = total_output_tokens + @output,
|
|
47
|
+
total_cache_read_tokens = total_cache_read_tokens + @cache_read,
|
|
48
|
+
total_cache_creation_tokens = total_cache_creation_tokens + @cache_creation,
|
|
49
|
+
total_duration_ms = total_duration_ms + @duration_ms,
|
|
50
|
+
ended_at = CASE WHEN @ts > COALESCE(ended_at, '') THEN @ts ELSE ended_at END,
|
|
51
|
+
model = COALESCE(@model, model)
|
|
52
|
+
`).run({
|
|
53
|
+
id: sessionId,
|
|
54
|
+
ts: deltas.latestTimestamp,
|
|
55
|
+
cost: deltas.costDelta,
|
|
56
|
+
input: deltas.inputDelta,
|
|
57
|
+
output: deltas.outputDelta,
|
|
58
|
+
cache_read: deltas.cacheReadDelta,
|
|
59
|
+
cache_creation: deltas.cacheCreationDelta,
|
|
60
|
+
duration_ms: deltas.durationMsDelta,
|
|
61
|
+
model: deltas.model,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
insertTurn(turn) {
|
|
65
|
+
// Use RETURNING so we get the correct id on both INSERT and ON CONFLICT UPDATE
|
|
66
|
+
const row = this.db.prepare(`
|
|
67
|
+
INSERT INTO turns (session_id, turn_index, role, timestamp, input_tokens, output_tokens,
|
|
68
|
+
cache_read_tokens, cache_creation_tokens, cost_usd, duration_ms, model, content_text, tool_calls, is_real_user)
|
|
69
|
+
VALUES (@session_id, @turn_index, @role, @timestamp, @input_tokens, @output_tokens,
|
|
70
|
+
@cache_read_tokens, @cache_creation_tokens, @cost_usd, @duration_ms, @model, @content_text, @tool_calls, @is_real_user)
|
|
71
|
+
ON CONFLICT(session_id, turn_index) DO UPDATE SET
|
|
72
|
+
content_text = @content_text,
|
|
73
|
+
tool_calls = @tool_calls,
|
|
74
|
+
input_tokens = @input_tokens,
|
|
75
|
+
output_tokens = @output_tokens,
|
|
76
|
+
cost_usd = MAX(cost_usd, @cost_usd),
|
|
77
|
+
duration_ms = MAX(duration_ms, @duration_ms),
|
|
78
|
+
is_real_user = @is_real_user
|
|
79
|
+
RETURNING id
|
|
80
|
+
`).get(turn);
|
|
81
|
+
return row?.id ?? 0;
|
|
82
|
+
}
|
|
83
|
+
insertToolUse(toolUse) {
|
|
84
|
+
this.db.prepare(`
|
|
85
|
+
INSERT INTO tool_uses (session_id, turn_id, tool_name, tool_input, tool_result, success, duration_ms, timestamp)
|
|
86
|
+
VALUES (@session_id, @turn_id, @tool_name, @tool_input, @tool_result, @success, @duration_ms, @timestamp)
|
|
87
|
+
`).run(toolUse);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Replace all tool uses for a given turn in a single transaction.
|
|
91
|
+
* Deletes existing rows first to prevent duplication on re-ingest.
|
|
92
|
+
*/
|
|
93
|
+
replaceToolUsesForTurn(turnId, toolUses) {
|
|
94
|
+
const del = this.db.prepare(`DELETE FROM tool_uses WHERE turn_id = ?`);
|
|
95
|
+
const ins = this.db.prepare(`
|
|
96
|
+
INSERT INTO tool_uses (session_id, turn_id, tool_name, tool_input, tool_result, success, duration_ms, timestamp)
|
|
97
|
+
VALUES (@session_id, @turn_id, @tool_name, @tool_input, @tool_result, @success, @duration_ms, @timestamp)
|
|
98
|
+
`);
|
|
99
|
+
this.db.transaction(() => {
|
|
100
|
+
del.run(turnId);
|
|
101
|
+
for (const row of toolUses)
|
|
102
|
+
ins.run(row);
|
|
103
|
+
})();
|
|
104
|
+
}
|
|
105
|
+
insertHookEvent(event) {
|
|
106
|
+
this.db.prepare(`
|
|
107
|
+
INSERT INTO hook_events (session_id, event_name, timestamp, payload)
|
|
108
|
+
VALUES (@session_id, @event_name, @timestamp, @payload)
|
|
109
|
+
`).run(event);
|
|
110
|
+
}
|
|
111
|
+
listSessions(limit = 50, offset = 0) {
|
|
112
|
+
return this.db.prepare(`
|
|
113
|
+
SELECT * FROM sessions ORDER BY started_at DESC LIMIT ? OFFSET ?
|
|
114
|
+
`).all(limit, offset);
|
|
115
|
+
}
|
|
116
|
+
countSessions() {
|
|
117
|
+
const row = this.db.prepare(`SELECT COUNT(*) as n FROM sessions`).get();
|
|
118
|
+
return row.n;
|
|
119
|
+
}
|
|
120
|
+
getSession(id) {
|
|
121
|
+
return this.db.prepare(`SELECT * FROM sessions WHERE id = ?`).get(id);
|
|
122
|
+
}
|
|
123
|
+
getSessionTurns(sessionId) {
|
|
124
|
+
return this.db.prepare(`
|
|
125
|
+
SELECT * FROM turns WHERE session_id = ? ORDER BY turn_index ASC
|
|
126
|
+
`).all(sessionId);
|
|
127
|
+
}
|
|
128
|
+
getSessionToolUses(sessionId) {
|
|
129
|
+
return this.db.prepare(`
|
|
130
|
+
SELECT * FROM tool_uses WHERE session_id = ? ORDER BY timestamp ASC
|
|
131
|
+
`).all(sessionId);
|
|
132
|
+
}
|
|
133
|
+
getSessionHookEvents(sessionId) {
|
|
134
|
+
return this.db.prepare(`
|
|
135
|
+
SELECT * FROM hook_events WHERE session_id = ? ORDER BY timestamp ASC
|
|
136
|
+
`).all(sessionId);
|
|
137
|
+
}
|
|
138
|
+
getAggregateStats() {
|
|
139
|
+
return this.db.prepare(`
|
|
140
|
+
SELECT
|
|
141
|
+
(SELECT COUNT(*) FROM sessions) as total_sessions,
|
|
142
|
+
(SELECT SUM(total_input_tokens) FROM sessions) as total_input_tokens,
|
|
143
|
+
(SELECT SUM(total_output_tokens) FROM sessions) as total_output_tokens,
|
|
144
|
+
(SELECT SUM(total_cache_read_tokens) FROM sessions) as total_cache_read_tokens,
|
|
145
|
+
(SELECT SUM(total_cost_usd) FROM sessions) as total_cost_usd,
|
|
146
|
+
(SELECT SUM(total_turns) FROM sessions) as total_turns,
|
|
147
|
+
(SELECT SUM(total_duration_ms) FROM sessions) as total_duration_ms,
|
|
148
|
+
(SELECT COUNT(*) FROM hook_events WHERE event_name = 'UserPromptSubmit') as total_user_prompts,
|
|
149
|
+
(SELECT COUNT(*) FROM hook_events WHERE event_name = 'Stop') as total_interruptions
|
|
150
|
+
`).get();
|
|
151
|
+
}
|
|
152
|
+
// ── OTEL data ──
|
|
153
|
+
insertOtelMetric(metric) {
|
|
154
|
+
this.db.prepare(`
|
|
155
|
+
INSERT INTO otel_metrics (name, value, attributes, session_id, model, timestamp)
|
|
156
|
+
VALUES (@name, @value, @attributes, @session_id, @model, @timestamp)
|
|
157
|
+
`).run(metric);
|
|
158
|
+
}
|
|
159
|
+
insertOtelMetricBatch(metrics) {
|
|
160
|
+
const stmt = this.db.prepare(`
|
|
161
|
+
INSERT INTO otel_metrics (name, value, attributes, session_id, model, timestamp)
|
|
162
|
+
VALUES (@name, @value, @attributes, @session_id, @model, @timestamp)
|
|
163
|
+
`);
|
|
164
|
+
const tx = this.db.transaction((rows) => {
|
|
165
|
+
for (const row of rows)
|
|
166
|
+
stmt.run(row);
|
|
167
|
+
});
|
|
168
|
+
tx(metrics);
|
|
169
|
+
}
|
|
170
|
+
insertOtelEvent(event) {
|
|
171
|
+
this.db.prepare(`
|
|
172
|
+
INSERT INTO otel_events (event_name, attributes, session_id, prompt_id, timestamp)
|
|
173
|
+
VALUES (@event_name, @attributes, @session_id, @prompt_id, @timestamp)
|
|
174
|
+
`).run(event);
|
|
175
|
+
}
|
|
176
|
+
insertOtelEventBatch(events) {
|
|
177
|
+
const stmt = this.db.prepare(`
|
|
178
|
+
INSERT INTO otel_events (event_name, attributes, session_id, prompt_id, timestamp)
|
|
179
|
+
VALUES (@event_name, @attributes, @session_id, @prompt_id, @timestamp)
|
|
180
|
+
`);
|
|
181
|
+
const tx = this.db.transaction((rows) => {
|
|
182
|
+
for (const row of rows)
|
|
183
|
+
stmt.run(row);
|
|
184
|
+
});
|
|
185
|
+
tx(events);
|
|
186
|
+
}
|
|
187
|
+
// ── Dashboard queries ──
|
|
188
|
+
getTokenTimeSeries(from, to, stepSeconds) {
|
|
189
|
+
return this.db.prepare(`
|
|
190
|
+
SELECT
|
|
191
|
+
datetime((CAST(strftime('%s', timestamp) AS INTEGER) / CAST(? AS INTEGER)) * CAST(? AS INTEGER), 'unixepoch') as timestamp,
|
|
192
|
+
SUM(CASE WHEN json_extract(attributes, '$.type') = 'input' THEN value ELSE 0 END) as input,
|
|
193
|
+
SUM(CASE WHEN json_extract(attributes, '$.type') = 'output' THEN value ELSE 0 END) as output,
|
|
194
|
+
SUM(CASE WHEN json_extract(attributes, '$.type') = 'cacheRead' THEN value ELSE 0 END) as cache_read
|
|
195
|
+
FROM otel_metrics
|
|
196
|
+
WHERE name = 'claude_code.token.usage'
|
|
197
|
+
AND timestamp >= ? AND timestamp <= ?
|
|
198
|
+
GROUP BY 1
|
|
199
|
+
ORDER BY 1 ASC
|
|
200
|
+
`).all(stepSeconds, stepSeconds, from, to);
|
|
201
|
+
}
|
|
202
|
+
getCostTimeSeries(from, to, stepSeconds) {
|
|
203
|
+
return this.db.prepare(`
|
|
204
|
+
SELECT
|
|
205
|
+
datetime((CAST(strftime('%s', timestamp) AS INTEGER) / CAST(? AS INTEGER)) * CAST(? AS INTEGER), 'unixepoch') as timestamp,
|
|
206
|
+
SUM(value) as cost
|
|
207
|
+
FROM otel_metrics
|
|
208
|
+
WHERE name = 'claude_code.cost.usage'
|
|
209
|
+
AND timestamp >= ? AND timestamp <= ?
|
|
210
|
+
GROUP BY 1
|
|
211
|
+
ORDER BY 1 ASC
|
|
212
|
+
`).all(stepSeconds, stepSeconds, from, to);
|
|
213
|
+
}
|
|
214
|
+
getToolUsageBreakdown() {
|
|
215
|
+
return this.db.prepare(`
|
|
216
|
+
SELECT tool_name, COUNT(*) as count
|
|
217
|
+
FROM tool_uses
|
|
218
|
+
GROUP BY tool_name
|
|
219
|
+
ORDER BY count DESC
|
|
220
|
+
LIMIT 20
|
|
221
|
+
`).all();
|
|
222
|
+
}
|
|
223
|
+
getModelBreakdown() {
|
|
224
|
+
return this.db.prepare(`
|
|
225
|
+
SELECT
|
|
226
|
+
model,
|
|
227
|
+
SUM(CASE WHEN name = 'claude_code.cost.usage' THEN value ELSE 0 END) as cost,
|
|
228
|
+
SUM(CASE WHEN name = 'claude_code.token.usage' THEN value ELSE 0 END) as tokens
|
|
229
|
+
FROM otel_metrics
|
|
230
|
+
WHERE model IS NOT NULL
|
|
231
|
+
GROUP BY model
|
|
232
|
+
ORDER BY cost DESC
|
|
233
|
+
`).all();
|
|
234
|
+
}
|
|
235
|
+
getRecentOtelEvents(limit = 50) {
|
|
236
|
+
return this.db.prepare(`
|
|
237
|
+
SELECT event_name, attributes, session_id, prompt_id, timestamp
|
|
238
|
+
FROM otel_events
|
|
239
|
+
ORDER BY timestamp DESC
|
|
240
|
+
LIMIT ?
|
|
241
|
+
`).all(limit);
|
|
242
|
+
}
|
|
243
|
+
// ── Task tags ──
|
|
244
|
+
tagTurn(turnId, task) {
|
|
245
|
+
this.db.prepare(`
|
|
246
|
+
INSERT OR IGNORE INTO task_tags (turn_id, task, tagged_at)
|
|
247
|
+
VALUES (?, ?, ?)
|
|
248
|
+
`).run(turnId, task, new Date().toISOString());
|
|
249
|
+
}
|
|
250
|
+
tagTurnsBatch(turnIds, task) {
|
|
251
|
+
const stmt = this.db.prepare(`
|
|
252
|
+
INSERT OR IGNORE INTO task_tags (turn_id, task, tagged_at)
|
|
253
|
+
VALUES (?, ?, ?)
|
|
254
|
+
`);
|
|
255
|
+
const taggedAt = new Date().toISOString();
|
|
256
|
+
this.db.transaction(() => {
|
|
257
|
+
for (const turnId of turnIds)
|
|
258
|
+
stmt.run(turnId, task, taggedAt);
|
|
259
|
+
})();
|
|
260
|
+
}
|
|
261
|
+
getTasksForTurn(turnId) {
|
|
262
|
+
const rows = this.db.prepare(`
|
|
263
|
+
SELECT task FROM task_tags WHERE turn_id = ? ORDER BY tagged_at ASC
|
|
264
|
+
`).all(turnId);
|
|
265
|
+
return rows.map(r => r.task);
|
|
266
|
+
}
|
|
267
|
+
getTaskTagsForTurn(turnId) {
|
|
268
|
+
return this.db.prepare(`SELECT * FROM task_tags WHERE turn_id = ? ORDER BY tagged_at ASC`).all(turnId);
|
|
269
|
+
}
|
|
270
|
+
getTurnsByTask(task, limit = 50, offset = 0) {
|
|
271
|
+
return this.db.prepare(`
|
|
272
|
+
SELECT t.* FROM turns t
|
|
273
|
+
JOIN task_tags tt ON tt.turn_id = t.id
|
|
274
|
+
WHERE tt.task = ?
|
|
275
|
+
ORDER BY t.timestamp DESC
|
|
276
|
+
LIMIT ? OFFSET ?
|
|
277
|
+
`).all(task, limit, offset);
|
|
278
|
+
}
|
|
279
|
+
getTaggedTurns(opts = {}) {
|
|
280
|
+
const { tags, mode = "any", from, to, limit = 50, offset = 0 } = opts;
|
|
281
|
+
const conditions = [];
|
|
282
|
+
const params = [];
|
|
283
|
+
if (tags && tags.length > 0) {
|
|
284
|
+
const placeholders = tags.map(() => "?").join(",");
|
|
285
|
+
if (mode === "all" && tags.length > 1) {
|
|
286
|
+
conditions.push(`t.id IN (
|
|
287
|
+
SELECT turn_id FROM task_tags WHERE task IN (${placeholders})
|
|
288
|
+
GROUP BY turn_id HAVING COUNT(DISTINCT task) = ?
|
|
289
|
+
)`);
|
|
290
|
+
params.push(...tags, tags.length);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
conditions.push(`t.id IN (SELECT DISTINCT turn_id FROM task_tags WHERE task IN (${placeholders}))`);
|
|
294
|
+
params.push(...tags);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
conditions.push("t.id IN (SELECT DISTINCT turn_id FROM task_tags)");
|
|
299
|
+
}
|
|
300
|
+
if (from && to) {
|
|
301
|
+
conditions.push("t.timestamp >= ? AND t.timestamp <= ?");
|
|
302
|
+
params.push(from, to);
|
|
303
|
+
}
|
|
304
|
+
// Only show real user turns (each row = one interaction block)
|
|
305
|
+
conditions.push("t.is_real_user = 1");
|
|
306
|
+
const where = conditions.length > 0 ? "WHERE " + conditions.join(" AND ") : "";
|
|
307
|
+
params.push(limit, offset);
|
|
308
|
+
// Aggregate block stats: sum tokens/cost from this turn to the next real user turn
|
|
309
|
+
return this.db.prepare(`
|
|
310
|
+
SELECT t.*,
|
|
311
|
+
(SELECT GROUP_CONCAT(tt2.task, ', ') FROM task_tags tt2 WHERE tt2.turn_id = t.id) as tags,
|
|
312
|
+
(SELECT COALESCE(SUM(b.input_tokens), 0) FROM turns b
|
|
313
|
+
WHERE b.session_id = t.session_id AND b.turn_index >= t.turn_index
|
|
314
|
+
AND b.turn_index < COALESCE(
|
|
315
|
+
(SELECT MIN(n.turn_index) FROM turns n WHERE n.session_id = t.session_id AND n.turn_index > t.turn_index AND n.is_real_user = 1),
|
|
316
|
+
999999)
|
|
317
|
+
) as block_input_tokens,
|
|
318
|
+
(SELECT COALESCE(SUM(b.output_tokens), 0) FROM turns b
|
|
319
|
+
WHERE b.session_id = t.session_id AND b.turn_index >= t.turn_index
|
|
320
|
+
AND b.turn_index < COALESCE(
|
|
321
|
+
(SELECT MIN(n.turn_index) FROM turns n WHERE n.session_id = t.session_id AND n.turn_index > t.turn_index AND n.is_real_user = 1),
|
|
322
|
+
999999)
|
|
323
|
+
) as block_output_tokens,
|
|
324
|
+
(SELECT COALESCE(SUM(b.cost_usd), 0) FROM turns b
|
|
325
|
+
WHERE b.session_id = t.session_id AND b.turn_index >= t.turn_index
|
|
326
|
+
AND b.turn_index < COALESCE(
|
|
327
|
+
(SELECT MIN(n.turn_index) FROM turns n WHERE n.session_id = t.session_id AND n.turn_index > t.turn_index AND n.is_real_user = 1),
|
|
328
|
+
999999)
|
|
329
|
+
) as block_cost_usd
|
|
330
|
+
FROM turns t
|
|
331
|
+
${where}
|
|
332
|
+
ORDER BY t.timestamp DESC
|
|
333
|
+
LIMIT ? OFFSET ?
|
|
334
|
+
`).all(...params);
|
|
335
|
+
}
|
|
336
|
+
getTurnBlock(turnId) {
|
|
337
|
+
const turn = this.db.prepare(`SELECT * FROM turns WHERE id = ?`).get(turnId);
|
|
338
|
+
if (!turn)
|
|
339
|
+
return [];
|
|
340
|
+
// Find the next real user turn in the same session
|
|
341
|
+
const nextRealUser = this.db.prepare(`
|
|
342
|
+
SELECT turn_index FROM turns
|
|
343
|
+
WHERE session_id = ? AND turn_index > ? AND is_real_user = 1
|
|
344
|
+
ORDER BY turn_index ASC LIMIT 1
|
|
345
|
+
`).get(turn.session_id, turn.turn_index);
|
|
346
|
+
const maxIndex = nextRealUser ? nextRealUser.turn_index : 999999;
|
|
347
|
+
return this.db.prepare(`
|
|
348
|
+
SELECT * FROM turns
|
|
349
|
+
WHERE session_id = ? AND turn_index >= ? AND turn_index < ?
|
|
350
|
+
ORDER BY turn_index ASC
|
|
351
|
+
`).all(turn.session_id, turn.turn_index, maxIndex);
|
|
352
|
+
}
|
|
353
|
+
getStatsByTask(task, from, to) {
|
|
354
|
+
const timeFilter = from && to ? " AND t.timestamp >= ? AND t.timestamp <= ?" : "";
|
|
355
|
+
const params = [task];
|
|
356
|
+
if (from && to)
|
|
357
|
+
params.push(from, to);
|
|
358
|
+
return this.db.prepare(`
|
|
359
|
+
SELECT
|
|
360
|
+
COUNT(*) as total_turns,
|
|
361
|
+
SUM(t.is_real_user) as user_turns,
|
|
362
|
+
SUM(t.input_tokens) as total_input_tokens,
|
|
363
|
+
SUM(t.output_tokens) as total_output_tokens,
|
|
364
|
+
SUM(t.cache_read_tokens) as total_cache_read_tokens,
|
|
365
|
+
SUM(t.cost_usd) as total_cost_usd,
|
|
366
|
+
SUM(t.duration_ms) as total_duration_ms
|
|
367
|
+
FROM turns t
|
|
368
|
+
JOIN task_tags tt ON tt.turn_id = t.id
|
|
369
|
+
WHERE tt.task = ?${timeFilter}
|
|
370
|
+
`).get(...params);
|
|
371
|
+
}
|
|
372
|
+
getStatsByTasks(tasks, mode = "all", from, to) {
|
|
373
|
+
if (tasks.length === 0)
|
|
374
|
+
return null;
|
|
375
|
+
if (tasks.length === 1)
|
|
376
|
+
return this.getStatsByTask(tasks[0], from, to);
|
|
377
|
+
const placeholders = tasks.map(() => "?").join(",");
|
|
378
|
+
const timeFilter = from && to ? " AND t.timestamp >= ? AND t.timestamp <= ?" : "";
|
|
379
|
+
const timeParams = from && to ? [from, to] : [];
|
|
380
|
+
if (mode === "any") {
|
|
381
|
+
return this.db.prepare(`
|
|
382
|
+
SELECT
|
|
383
|
+
COUNT(DISTINCT t.id) as total_turns,
|
|
384
|
+
SUM(t.is_real_user) as user_turns,
|
|
385
|
+
SUM(t.input_tokens) as total_input_tokens,
|
|
386
|
+
SUM(t.output_tokens) as total_output_tokens,
|
|
387
|
+
SUM(t.cache_read_tokens) as total_cache_read_tokens,
|
|
388
|
+
SUM(t.cost_usd) as total_cost_usd,
|
|
389
|
+
SUM(t.duration_ms) as total_duration_ms
|
|
390
|
+
FROM turns t
|
|
391
|
+
WHERE t.id IN (
|
|
392
|
+
SELECT DISTINCT turn_id FROM task_tags WHERE task IN (${placeholders})
|
|
393
|
+
)${timeFilter}
|
|
394
|
+
`).get(...tasks, ...timeParams);
|
|
395
|
+
}
|
|
396
|
+
return this.db.prepare(`
|
|
397
|
+
SELECT
|
|
398
|
+
COUNT(*) as total_turns,
|
|
399
|
+
SUM(t.is_real_user) as user_turns,
|
|
400
|
+
SUM(t.input_tokens) as total_input_tokens,
|
|
401
|
+
SUM(t.output_tokens) as total_output_tokens,
|
|
402
|
+
SUM(t.cache_read_tokens) as total_cache_read_tokens,
|
|
403
|
+
SUM(t.cost_usd) as total_cost_usd,
|
|
404
|
+
SUM(t.duration_ms) as total_duration_ms
|
|
405
|
+
FROM turns t
|
|
406
|
+
WHERE t.id IN (
|
|
407
|
+
SELECT turn_id FROM task_tags
|
|
408
|
+
WHERE task IN (${placeholders})
|
|
409
|
+
GROUP BY turn_id
|
|
410
|
+
HAVING COUNT(DISTINCT task) = ?
|
|
411
|
+
)${timeFilter}
|
|
412
|
+
`).get(...tasks, tasks.length, ...timeParams);
|
|
413
|
+
}
|
|
414
|
+
listTasks() {
|
|
415
|
+
return this.db.prepare(`
|
|
416
|
+
SELECT task, COUNT(*) as turn_count,
|
|
417
|
+
MIN(tagged_at) as first_tagged, MAX(tagged_at) as last_tagged
|
|
418
|
+
FROM task_tags
|
|
419
|
+
GROUP BY task
|
|
420
|
+
ORDER BY last_tagged DESC
|
|
421
|
+
`).all();
|
|
422
|
+
}
|
|
423
|
+
// ── Sync watermarks ──
|
|
424
|
+
getSyncWatermark(tableName) {
|
|
425
|
+
const row = this.db.prepare(`SELECT last_synced_id FROM sync_watermarks WHERE table_name = ?`).get(tableName);
|
|
426
|
+
return row?.last_synced_id ?? 0;
|
|
427
|
+
}
|
|
428
|
+
setSyncWatermark(tableName, lastSyncedId) {
|
|
429
|
+
this.db.prepare(`
|
|
430
|
+
INSERT INTO sync_watermarks (table_name, last_synced_id, last_synced_at)
|
|
431
|
+
VALUES (?, ?, datetime('now'))
|
|
432
|
+
ON CONFLICT(table_name) DO UPDATE SET
|
|
433
|
+
last_synced_id = excluded.last_synced_id,
|
|
434
|
+
last_synced_at = excluded.last_synced_at
|
|
435
|
+
`).run(tableName, lastSyncedId);
|
|
436
|
+
}
|
|
437
|
+
// ── Bulk reads for sync ──
|
|
438
|
+
getUnsyncedSessions(minRowid) {
|
|
439
|
+
return this.db.prepare(`
|
|
440
|
+
SELECT *, rowid as _rowid FROM sessions WHERE rowid > ? ORDER BY rowid ASC
|
|
441
|
+
`).all(minRowid);
|
|
442
|
+
}
|
|
443
|
+
getSessionsByIds(ids) {
|
|
444
|
+
if (ids.length === 0)
|
|
445
|
+
return [];
|
|
446
|
+
const placeholders = ids.map(() => "?").join(",");
|
|
447
|
+
return this.db.prepare(`SELECT * FROM sessions WHERE id IN (${placeholders})`).all(...ids);
|
|
448
|
+
}
|
|
449
|
+
getTurnsAfter(minId, limit) {
|
|
450
|
+
return this.db.prepare(`SELECT * FROM turns WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
451
|
+
}
|
|
452
|
+
getToolUsesAfter(minId, limit) {
|
|
453
|
+
return this.db.prepare(`SELECT * FROM tool_uses WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
454
|
+
}
|
|
455
|
+
getHookEventsAfter(minId, limit) {
|
|
456
|
+
return this.db.prepare(`SELECT * FROM hook_events WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
457
|
+
}
|
|
458
|
+
getOtelMetricsAfter(minId, limit) {
|
|
459
|
+
return this.db.prepare(`SELECT * FROM otel_metrics WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
460
|
+
}
|
|
461
|
+
getOtelEventsAfter(minId, limit) {
|
|
462
|
+
return this.db.prepare(`SELECT * FROM otel_events WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
463
|
+
}
|
|
464
|
+
getTaskTagsAfter(minId, limit) {
|
|
465
|
+
return this.db.prepare(`SELECT * FROM task_tags WHERE id > ? ORDER BY id ASC LIMIT ?`).all(minId, limit);
|
|
466
|
+
}
|
|
467
|
+
getTurnLookup() {
|
|
468
|
+
const rows = this.db.prepare(`SELECT id, session_id, turn_index FROM turns`).all();
|
|
469
|
+
return new Map(rows.map(r => [r.id, { session_id: r.session_id, turn_index: r.turn_index }]));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
//# sourceMappingURL=repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repo.js","sourceRoot":"","sources":["../../src/storage/repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { SessionSyncPayload, ApiOtelMetric, ApiOtelEvent } from "./transform.js";
|
|
2
|
+
export interface SyncClientConfig {
|
|
3
|
+
apiUrl: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SessionSyncResponse {
|
|
8
|
+
session_id: string;
|
|
9
|
+
turns_synced: number;
|
|
10
|
+
tool_uses_synced: number;
|
|
11
|
+
task_tags_synced: number;
|
|
12
|
+
hook_events_synced: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class ZozulApiClient {
|
|
15
|
+
private baseUrl;
|
|
16
|
+
private apiKey;
|
|
17
|
+
private timeout;
|
|
18
|
+
constructor(config: SyncClientConfig);
|
|
19
|
+
syncSession(sessionId: string, payload: SessionSyncPayload): Promise<SessionSyncResponse>;
|
|
20
|
+
postOtelMetricsBulk(metrics: ApiOtelMetric[]): Promise<void>;
|
|
21
|
+
postOtelEventsBulk(events: ApiOtelEvent[]): Promise<void>;
|
|
22
|
+
private post;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/sync/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEtF,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,gBAAgB;IAM9B,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIzF,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;YAIjD,IAAI;CAuBnB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export class ZozulApiClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
timeout;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.baseUrl = config.apiUrl.replace(/\/+$/, "");
|
|
7
|
+
this.apiKey = config.apiKey;
|
|
8
|
+
this.timeout = config.timeout ?? 30_000;
|
|
9
|
+
}
|
|
10
|
+
async syncSession(sessionId, payload) {
|
|
11
|
+
return this.post(`/api/v1/sessions/${sessionId}/sync`, payload);
|
|
12
|
+
}
|
|
13
|
+
async postOtelMetricsBulk(metrics) {
|
|
14
|
+
await this.post("/api/v1/otel/metrics/bulk", metrics);
|
|
15
|
+
}
|
|
16
|
+
async postOtelEventsBulk(events) {
|
|
17
|
+
await this.post("/api/v1/otel/events/bulk", events);
|
|
18
|
+
}
|
|
19
|
+
async post(path, body) {
|
|
20
|
+
const url = `${this.baseUrl}${path}`;
|
|
21
|
+
const res = await fetch(url, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
"X-API-Key": this.apiKey,
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(body),
|
|
28
|
+
signal: AbortSignal.timeout(this.timeout),
|
|
29
|
+
});
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const text = await res.text().catch(() => "");
|
|
32
|
+
throw new Error(`POST ${path} failed: ${res.status} ${res.statusText} — ${text}`);
|
|
33
|
+
}
|
|
34
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
35
|
+
if (contentType.includes("application/json")) {
|
|
36
|
+
return (await res.json());
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/sync/client.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,cAAc;IACjB,OAAO,CAAS;IAChB,MAAM,CAAS;IACf,OAAO,CAAS;IAExB,YAAY,MAAwB;QAClC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,SAAiB,EAAE,OAA2B;QAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,SAAS,OAAO,EAAE,OAAO,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,mBAAmB,CAAC,OAAwB;QAChD,MAAM,IAAI,CAAC,IAAI,CAAC,2BAA2B,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,kBAAkB,CAAC,MAAsB;QAC7C,MAAM,IAAI,CAAC,IAAI,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;IAEO,KAAK,CAAC,IAAI,CAAW,IAAY,EAAE,IAAa;QACtD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC3B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,WAAW,EAAE,IAAI,CAAC,MAAM;aACzB;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAC1B,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC;SAC1C,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,IAAI,KAAK,CAAC,QAAQ,IAAI,YAAY,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,MAAM,IAAI,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC1D,IAAI,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAM,CAAC;QACjC,CAAC;QACD,OAAO,SAAc,CAAC;IACxB,CAAC;CACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SessionRepo } from "../storage/repo.js";
|
|
2
|
+
import type { ZozulApiClient } from "./client.js";
|
|
3
|
+
export interface SyncCounts {
|
|
4
|
+
synced: number;
|
|
5
|
+
failed: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SyncResult {
|
|
8
|
+
sessions: SyncCounts;
|
|
9
|
+
otel_metrics: SyncCounts;
|
|
10
|
+
otel_events: SyncCounts;
|
|
11
|
+
}
|
|
12
|
+
interface SyncOptions {
|
|
13
|
+
verbose?: boolean;
|
|
14
|
+
dryRun?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function runSync(repo: SessionRepo, client: ZozulApiClient, opts?: SyncOptions): Promise<SyncResult>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sync/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAUlD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,UAAU,CAAC;IACrB,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,UAAU,CAAC;CACzB;AAED,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAqCD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,cAAc,EACtB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,UAAU,CAAC,CA6FrB"}
|