@nocoo/pew 0.4.0 → 0.6.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +117 -8
- package/dist/cli.js.map +1 -1
- package/dist/commands/session-sync.d.ts +62 -0
- package/dist/commands/session-sync.d.ts.map +1 -0
- package/dist/commands/session-sync.js +443 -0
- package/dist/commands/session-sync.js.map +1 -0
- package/dist/commands/session-upload.d.ts +55 -0
- package/dist/commands/session-upload.d.ts.map +1 -0
- package/dist/commands/session-upload.js +177 -0
- package/dist/commands/session-upload.js.map +1 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +22 -11
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts +12 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +159 -3
- package/dist/commands/sync.js.map +1 -1
- package/dist/discovery/sources.d.ts +5 -0
- package/dist/discovery/sources.d.ts.map +1 -1
- package/dist/discovery/sources.js +7 -0
- package/dist/discovery/sources.js.map +1 -1
- package/dist/parsers/claude-session.d.ts +19 -0
- package/dist/parsers/claude-session.d.ts.map +1 -0
- package/dist/parsers/claude-session.js +131 -0
- package/dist/parsers/claude-session.js.map +1 -0
- package/dist/parsers/codex-session.d.ts +24 -0
- package/dist/parsers/codex-session.d.ts.map +1 -0
- package/dist/parsers/codex-session.js +140 -0
- package/dist/parsers/codex-session.js.map +1 -0
- package/dist/parsers/codex.d.ts +37 -0
- package/dist/parsers/codex.d.ts.map +1 -0
- package/dist/parsers/codex.js +136 -0
- package/dist/parsers/codex.js.map +1 -0
- package/dist/parsers/gemini-session.d.ts +19 -0
- package/dist/parsers/gemini-session.d.ts.map +1 -0
- package/dist/parsers/gemini-session.js +103 -0
- package/dist/parsers/gemini-session.js.map +1 -0
- package/dist/parsers/openclaw-session.d.ts +20 -0
- package/dist/parsers/openclaw-session.d.ts.map +1 -0
- package/dist/parsers/openclaw-session.js +122 -0
- package/dist/parsers/openclaw-session.js.map +1 -0
- package/dist/parsers/opencode-session.d.ts +15 -0
- package/dist/parsers/opencode-session.d.ts.map +1 -0
- package/dist/parsers/opencode-session.js +131 -0
- package/dist/parsers/opencode-session.js.map +1 -0
- package/dist/parsers/opencode-sqlite-db.d.ts +29 -0
- package/dist/parsers/opencode-sqlite-db.d.ts.map +1 -0
- package/dist/parsers/opencode-sqlite-db.js +71 -0
- package/dist/parsers/opencode-sqlite-db.js.map +1 -0
- package/dist/parsers/opencode-sqlite-session.d.ts +32 -0
- package/dist/parsers/opencode-sqlite-session.d.ts.map +1 -0
- package/dist/parsers/opencode-sqlite-session.js +121 -0
- package/dist/parsers/opencode-sqlite-session.js.map +1 -0
- package/dist/parsers/opencode-sqlite.d.ts +53 -0
- package/dist/parsers/opencode-sqlite.d.ts.map +1 -0
- package/dist/parsers/opencode-sqlite.js +104 -0
- package/dist/parsers/opencode-sqlite.js.map +1 -0
- package/dist/storage/session-cursor-store.d.ts +14 -0
- package/dist/storage/session-cursor-store.d.ts.map +1 -0
- package/dist/storage/session-cursor-store.js +34 -0
- package/dist/storage/session-cursor-store.js.map +1 -0
- package/dist/storage/session-queue.d.ts +28 -0
- package/dist/storage/session-queue.d.ts.map +1 -0
- package/dist/storage/session-queue.js +65 -0
- package/dist/storage/session-queue.js.map +1 -0
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +5 -0
- package/dist/utils/paths.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session sync orchestrator.
|
|
3
|
+
*
|
|
4
|
+
* Discovers AI tool session files, full-scans changed files (mtime+size
|
|
5
|
+
* dual-check), collects SessionSnapshots, converts to SessionQueueRecords,
|
|
6
|
+
* deduplicates, and writes to session queue.
|
|
7
|
+
*
|
|
8
|
+
* Fully independent from the token sync pipeline — separate cursors,
|
|
9
|
+
* separate queue, separate files.
|
|
10
|
+
*/
|
|
11
|
+
import { readdir, stat } from "node:fs/promises";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { SessionCursorStore } from "../storage/session-cursor-store.js";
|
|
14
|
+
import { SessionQueue } from "../storage/session-queue.js";
|
|
15
|
+
import { discoverClaudeFiles, discoverCodexFiles, discoverGeminiFiles, discoverOpenClawFiles, } from "../discovery/sources.js";
|
|
16
|
+
import { collectClaudeSessions } from "../parsers/claude-session.js";
|
|
17
|
+
import { collectCodexSessions } from "../parsers/codex-session.js";
|
|
18
|
+
import { collectGeminiSessions } from "../parsers/gemini-session.js";
|
|
19
|
+
import { collectOpenCodeSessions } from "../parsers/opencode-session.js";
|
|
20
|
+
import { collectOpenClawSessions } from "../parsers/openclaw-session.js";
|
|
21
|
+
import { collectOpenCodeSqliteSessions } from "../parsers/opencode-sqlite-session.js";
|
|
22
|
+
import { deduplicateSessionRecords } from "./session-upload.js";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Helpers
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/** Convert camelCase SessionSnapshot to snake_case SessionQueueRecord */
|
|
27
|
+
function toQueueRecord(snap) {
|
|
28
|
+
return {
|
|
29
|
+
session_key: snap.sessionKey,
|
|
30
|
+
source: snap.source,
|
|
31
|
+
kind: snap.kind,
|
|
32
|
+
started_at: snap.startedAt,
|
|
33
|
+
last_message_at: snap.lastMessageAt,
|
|
34
|
+
duration_seconds: snap.durationSeconds,
|
|
35
|
+
user_messages: snap.userMessages,
|
|
36
|
+
assistant_messages: snap.assistantMessages,
|
|
37
|
+
total_messages: snap.totalMessages,
|
|
38
|
+
project_ref: snap.projectRef,
|
|
39
|
+
model: snap.model,
|
|
40
|
+
snapshot_at: snap.snapshotAt,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Check if a file has changed since the last cursor.
|
|
45
|
+
* Returns true if the file should be re-scanned (full-scan).
|
|
46
|
+
*/
|
|
47
|
+
function fileChanged(cursor, mtimeMs, size) {
|
|
48
|
+
if (!cursor)
|
|
49
|
+
return true;
|
|
50
|
+
return cursor.mtimeMs !== mtimeMs || cursor.size !== size;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Discover OpenCode session directories.
|
|
54
|
+
*
|
|
55
|
+
* Lists subdirectories under the message dir (e.g. ses_xxx/).
|
|
56
|
+
* Returns absolute paths to session directories.
|
|
57
|
+
*/
|
|
58
|
+
async function discoverOpenCodeSessionDirs(messageDir) {
|
|
59
|
+
let entries;
|
|
60
|
+
try {
|
|
61
|
+
entries = await readdir(messageDir, { withFileTypes: true });
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
return entries
|
|
67
|
+
.filter((e) => e.isDirectory())
|
|
68
|
+
.map((e) => join(messageDir, e.name))
|
|
69
|
+
.sort();
|
|
70
|
+
}
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Main orchestrator
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
/**
|
|
75
|
+
* Execute session sync: discover files, full-scan changed files,
|
|
76
|
+
* collect snapshots, deduplicate, and write to session queue.
|
|
77
|
+
*/
|
|
78
|
+
export async function executeSessionSync(opts) {
|
|
79
|
+
const { stateDir, onProgress } = opts;
|
|
80
|
+
const cursorStore = new SessionCursorStore(stateDir);
|
|
81
|
+
const queue = new SessionQueue(stateDir);
|
|
82
|
+
const cursors = await cursorStore.load();
|
|
83
|
+
const allSnapshots = [];
|
|
84
|
+
const sourceCounts = { claude: 0, codex: 0, gemini: 0, opencode: 0, openclaw: 0 };
|
|
85
|
+
// ---------- Claude Code ----------
|
|
86
|
+
if (opts.claudeDir) {
|
|
87
|
+
onProgress?.({
|
|
88
|
+
source: "claude-code",
|
|
89
|
+
phase: "discover",
|
|
90
|
+
message: "Discovering Claude Code session files...",
|
|
91
|
+
});
|
|
92
|
+
const files = await discoverClaudeFiles(opts.claudeDir);
|
|
93
|
+
onProgress?.({
|
|
94
|
+
source: "claude-code",
|
|
95
|
+
phase: "parse",
|
|
96
|
+
total: files.length,
|
|
97
|
+
message: `Scanning ${files.length} Claude session files...`,
|
|
98
|
+
});
|
|
99
|
+
for (let i = 0; i < files.length; i++) {
|
|
100
|
+
const filePath = files[i];
|
|
101
|
+
const st = await stat(filePath).catch(() => null);
|
|
102
|
+
if (!st)
|
|
103
|
+
continue;
|
|
104
|
+
const cursor = cursors.files[filePath];
|
|
105
|
+
if (!fileChanged(cursor, st.mtimeMs, st.size)) {
|
|
106
|
+
onProgress?.({
|
|
107
|
+
source: "claude-code",
|
|
108
|
+
phase: "parse",
|
|
109
|
+
current: i + 1,
|
|
110
|
+
total: files.length,
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const snapshots = await collectClaudeSessions(filePath).catch((err) => {
|
|
115
|
+
onProgress?.({
|
|
116
|
+
source: "claude-code",
|
|
117
|
+
phase: "warn",
|
|
118
|
+
message: `Skipping ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
119
|
+
});
|
|
120
|
+
return [];
|
|
121
|
+
});
|
|
122
|
+
// Update cursor with new mtime+size
|
|
123
|
+
cursors.files[filePath] = { mtimeMs: st.mtimeMs, size: st.size };
|
|
124
|
+
allSnapshots.push(...snapshots);
|
|
125
|
+
sourceCounts.claude += snapshots.length;
|
|
126
|
+
onProgress?.({
|
|
127
|
+
source: "claude-code",
|
|
128
|
+
phase: "parse",
|
|
129
|
+
current: i + 1,
|
|
130
|
+
total: files.length,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// ---------- Gemini CLI ----------
|
|
135
|
+
if (opts.geminiDir) {
|
|
136
|
+
onProgress?.({
|
|
137
|
+
source: "gemini-cli",
|
|
138
|
+
phase: "discover",
|
|
139
|
+
message: "Discovering Gemini CLI session files...",
|
|
140
|
+
});
|
|
141
|
+
const files = await discoverGeminiFiles(opts.geminiDir);
|
|
142
|
+
onProgress?.({
|
|
143
|
+
source: "gemini-cli",
|
|
144
|
+
phase: "parse",
|
|
145
|
+
total: files.length,
|
|
146
|
+
message: `Scanning ${files.length} Gemini session files...`,
|
|
147
|
+
});
|
|
148
|
+
for (let i = 0; i < files.length; i++) {
|
|
149
|
+
const filePath = files[i];
|
|
150
|
+
const st = await stat(filePath).catch(() => null);
|
|
151
|
+
if (!st)
|
|
152
|
+
continue;
|
|
153
|
+
const cursor = cursors.files[filePath];
|
|
154
|
+
if (!fileChanged(cursor, st.mtimeMs, st.size)) {
|
|
155
|
+
onProgress?.({
|
|
156
|
+
source: "gemini-cli",
|
|
157
|
+
phase: "parse",
|
|
158
|
+
current: i + 1,
|
|
159
|
+
total: files.length,
|
|
160
|
+
});
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const snapshots = await collectGeminiSessions(filePath).catch((err) => {
|
|
164
|
+
onProgress?.({
|
|
165
|
+
source: "gemini-cli",
|
|
166
|
+
phase: "warn",
|
|
167
|
+
message: `Skipping ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
168
|
+
});
|
|
169
|
+
return [];
|
|
170
|
+
});
|
|
171
|
+
cursors.files[filePath] = { mtimeMs: st.mtimeMs, size: st.size };
|
|
172
|
+
allSnapshots.push(...snapshots);
|
|
173
|
+
sourceCounts.gemini += snapshots.length;
|
|
174
|
+
onProgress?.({
|
|
175
|
+
source: "gemini-cli",
|
|
176
|
+
phase: "parse",
|
|
177
|
+
current: i + 1,
|
|
178
|
+
total: files.length,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// ---------- OpenCode ----------
|
|
183
|
+
if (opts.openCodeMessageDir) {
|
|
184
|
+
onProgress?.({
|
|
185
|
+
source: "opencode",
|
|
186
|
+
phase: "discover",
|
|
187
|
+
message: "Discovering OpenCode session directories...",
|
|
188
|
+
});
|
|
189
|
+
const dirs = await discoverOpenCodeSessionDirs(opts.openCodeMessageDir);
|
|
190
|
+
onProgress?.({
|
|
191
|
+
source: "opencode",
|
|
192
|
+
phase: "parse",
|
|
193
|
+
total: dirs.length,
|
|
194
|
+
message: `Scanning ${dirs.length} OpenCode session directories...`,
|
|
195
|
+
});
|
|
196
|
+
for (let i = 0; i < dirs.length; i++) {
|
|
197
|
+
const dirPath = dirs[i];
|
|
198
|
+
const st = await stat(dirPath).catch(() => null);
|
|
199
|
+
if (!st)
|
|
200
|
+
continue;
|
|
201
|
+
// For directories, use mtimeMs as proxy for content changes.
|
|
202
|
+
// Size for dirs isn't reliable across filesystems, so we use
|
|
203
|
+
// mtimeMs only and set size to 0 as a sentinel.
|
|
204
|
+
const cursor = cursors.files[dirPath];
|
|
205
|
+
if (cursor && cursor.mtimeMs === st.mtimeMs) {
|
|
206
|
+
onProgress?.({
|
|
207
|
+
source: "opencode",
|
|
208
|
+
phase: "parse",
|
|
209
|
+
current: i + 1,
|
|
210
|
+
total: dirs.length,
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const snapshots = await collectOpenCodeSessions(dirPath).catch((err) => {
|
|
215
|
+
onProgress?.({
|
|
216
|
+
source: "opencode",
|
|
217
|
+
phase: "warn",
|
|
218
|
+
message: `Skipping ${dirPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
219
|
+
});
|
|
220
|
+
return [];
|
|
221
|
+
});
|
|
222
|
+
cursors.files[dirPath] = { mtimeMs: st.mtimeMs, size: 0 };
|
|
223
|
+
allSnapshots.push(...snapshots);
|
|
224
|
+
sourceCounts.opencode += snapshots.length;
|
|
225
|
+
onProgress?.({
|
|
226
|
+
source: "opencode",
|
|
227
|
+
phase: "parse",
|
|
228
|
+
current: i + 1,
|
|
229
|
+
total: dirs.length,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// ---------- OpenCode SQLite Sessions ----------
|
|
234
|
+
if (opts.openCodeDbPath) {
|
|
235
|
+
onProgress?.({
|
|
236
|
+
source: "opencode-sqlite",
|
|
237
|
+
phase: "discover",
|
|
238
|
+
message: "Checking OpenCode SQLite database for sessions...",
|
|
239
|
+
});
|
|
240
|
+
const dbStat = await stat(opts.openCodeDbPath).catch(() => null);
|
|
241
|
+
if (dbStat && !opts.openSessionDb) {
|
|
242
|
+
// DB file exists but adapter is missing (bun:sqlite not available)
|
|
243
|
+
onProgress?.({
|
|
244
|
+
source: "opencode-sqlite",
|
|
245
|
+
phase: "warn",
|
|
246
|
+
message: `OpenCode SQLite database found at ${opts.openCodeDbPath} but bun:sqlite is not available — SQLite session data will NOT be synced`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
else if (dbStat && opts.openSessionDb) {
|
|
250
|
+
const dbInode = dbStat.ino;
|
|
251
|
+
const prevSqlite = cursors.openCodeSqlite;
|
|
252
|
+
// If inode changed (DB recreated), reset cursor
|
|
253
|
+
const lastTimeUpdated = prevSqlite && prevSqlite.inode === dbInode
|
|
254
|
+
? prevSqlite.lastTimeUpdated
|
|
255
|
+
: 0;
|
|
256
|
+
const prevProcessedIds = new Set(prevSqlite && prevSqlite.inode === dbInode
|
|
257
|
+
? (prevSqlite.lastProcessedIds ?? [])
|
|
258
|
+
: []);
|
|
259
|
+
const handle = opts.openSessionDb(opts.openCodeDbPath);
|
|
260
|
+
if (handle) {
|
|
261
|
+
try {
|
|
262
|
+
// Query uses >= to avoid missing same-millisecond rows.
|
|
263
|
+
// We dedup previously-processed IDs from the prior batch.
|
|
264
|
+
const rawSessions = handle.querySessions(lastTimeUpdated);
|
|
265
|
+
const sessions = prevProcessedIds.size > 0
|
|
266
|
+
? rawSessions.filter((s) => !prevProcessedIds.has(s.id))
|
|
267
|
+
: rawSessions;
|
|
268
|
+
if (sessions.length > 0) {
|
|
269
|
+
const sessionIds = sessions.map((s) => s.id);
|
|
270
|
+
const messages = handle.querySessionMessages(sessionIds);
|
|
271
|
+
const snapshots = collectOpenCodeSqliteSessions(sessions, messages);
|
|
272
|
+
onProgress?.({
|
|
273
|
+
source: "opencode-sqlite",
|
|
274
|
+
phase: "parse",
|
|
275
|
+
message: `Collected ${snapshots.length} sessions from ${rawSessions.length} SQLite session rows`,
|
|
276
|
+
});
|
|
277
|
+
allSnapshots.push(...snapshots);
|
|
278
|
+
sourceCounts.opencode += snapshots.length;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
onProgress?.({
|
|
282
|
+
source: "opencode-sqlite",
|
|
283
|
+
phase: "parse",
|
|
284
|
+
message: "No new SQLite sessions found",
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Update session cursor — advance past ALL queried sessions.
|
|
288
|
+
// Sessions are ORDER BY time_updated ASC, so last has the max.
|
|
289
|
+
// Track IDs at the max timestamp for same-millisecond dedup
|
|
290
|
+
// on the next query.
|
|
291
|
+
const maxTimeUpdated = rawSessions.length > 0
|
|
292
|
+
? rawSessions[rawSessions.length - 1].time_updated
|
|
293
|
+
: lastTimeUpdated;
|
|
294
|
+
const idsAtMax = rawSessions
|
|
295
|
+
.filter((s) => s.time_updated === maxTimeUpdated)
|
|
296
|
+
.map((s) => s.id);
|
|
297
|
+
cursors.openCodeSqlite = {
|
|
298
|
+
lastTimeUpdated: maxTimeUpdated,
|
|
299
|
+
lastProcessedIds: idsAtMax,
|
|
300
|
+
inode: dbInode,
|
|
301
|
+
updatedAt: new Date().toISOString(),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
handle.close();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
// openSessionDb returned null — DB exists but couldn't be opened
|
|
310
|
+
onProgress?.({
|
|
311
|
+
source: "opencode-sqlite",
|
|
312
|
+
phase: "warn",
|
|
313
|
+
message: `Failed to open OpenCode SQLite database at ${opts.openCodeDbPath} — SQLite session data will NOT be synced`,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// ---------- OpenClaw ----------
|
|
319
|
+
if (opts.openclawDir) {
|
|
320
|
+
onProgress?.({
|
|
321
|
+
source: "openclaw",
|
|
322
|
+
phase: "discover",
|
|
323
|
+
message: "Discovering OpenClaw session files...",
|
|
324
|
+
});
|
|
325
|
+
const files = await discoverOpenClawFiles(opts.openclawDir);
|
|
326
|
+
onProgress?.({
|
|
327
|
+
source: "openclaw",
|
|
328
|
+
phase: "parse",
|
|
329
|
+
total: files.length,
|
|
330
|
+
message: `Scanning ${files.length} OpenClaw session files...`,
|
|
331
|
+
});
|
|
332
|
+
for (let i = 0; i < files.length; i++) {
|
|
333
|
+
const filePath = files[i];
|
|
334
|
+
const st = await stat(filePath).catch(() => null);
|
|
335
|
+
if (!st)
|
|
336
|
+
continue;
|
|
337
|
+
const cursor = cursors.files[filePath];
|
|
338
|
+
if (!fileChanged(cursor, st.mtimeMs, st.size)) {
|
|
339
|
+
onProgress?.({
|
|
340
|
+
source: "openclaw",
|
|
341
|
+
phase: "parse",
|
|
342
|
+
current: i + 1,
|
|
343
|
+
total: files.length,
|
|
344
|
+
});
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const snapshots = await collectOpenClawSessions(filePath).catch((err) => {
|
|
348
|
+
onProgress?.({
|
|
349
|
+
source: "openclaw",
|
|
350
|
+
phase: "warn",
|
|
351
|
+
message: `Skipping ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
352
|
+
});
|
|
353
|
+
return [];
|
|
354
|
+
});
|
|
355
|
+
cursors.files[filePath] = { mtimeMs: st.mtimeMs, size: st.size };
|
|
356
|
+
allSnapshots.push(...snapshots);
|
|
357
|
+
sourceCounts.openclaw += snapshots.length;
|
|
358
|
+
onProgress?.({
|
|
359
|
+
source: "openclaw",
|
|
360
|
+
phase: "parse",
|
|
361
|
+
current: i + 1,
|
|
362
|
+
total: files.length,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
// ---------- Codex CLI ----------
|
|
367
|
+
if (opts.codexSessionsDir) {
|
|
368
|
+
onProgress?.({
|
|
369
|
+
source: "codex",
|
|
370
|
+
phase: "discover",
|
|
371
|
+
message: "Discovering Codex CLI session files...",
|
|
372
|
+
});
|
|
373
|
+
const files = await discoverCodexFiles(opts.codexSessionsDir);
|
|
374
|
+
onProgress?.({
|
|
375
|
+
source: "codex",
|
|
376
|
+
phase: "parse",
|
|
377
|
+
total: files.length,
|
|
378
|
+
message: `Scanning ${files.length} Codex session files...`,
|
|
379
|
+
});
|
|
380
|
+
for (let i = 0; i < files.length; i++) {
|
|
381
|
+
const filePath = files[i];
|
|
382
|
+
const st = await stat(filePath).catch(() => null);
|
|
383
|
+
if (!st)
|
|
384
|
+
continue;
|
|
385
|
+
const cursor = cursors.files[filePath];
|
|
386
|
+
if (!fileChanged(cursor, st.mtimeMs, st.size)) {
|
|
387
|
+
onProgress?.({
|
|
388
|
+
source: "codex",
|
|
389
|
+
phase: "parse",
|
|
390
|
+
current: i + 1,
|
|
391
|
+
total: files.length,
|
|
392
|
+
});
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const snapshots = await collectCodexSessions(filePath).catch((err) => {
|
|
396
|
+
onProgress?.({
|
|
397
|
+
source: "codex",
|
|
398
|
+
phase: "warn",
|
|
399
|
+
message: `Skipping ${filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
400
|
+
});
|
|
401
|
+
return [];
|
|
402
|
+
});
|
|
403
|
+
cursors.files[filePath] = { mtimeMs: st.mtimeMs, size: st.size };
|
|
404
|
+
allSnapshots.push(...snapshots);
|
|
405
|
+
sourceCounts.codex += snapshots.length;
|
|
406
|
+
onProgress?.({
|
|
407
|
+
source: "codex",
|
|
408
|
+
phase: "parse",
|
|
409
|
+
current: i + 1,
|
|
410
|
+
total: files.length,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
// ---------- Convert snapshots to queue records ----------
|
|
415
|
+
const records = allSnapshots.map(toQueueRecord);
|
|
416
|
+
// ---------- Deduplicate: keep latest snapshot per session_key ----------
|
|
417
|
+
onProgress?.({
|
|
418
|
+
source: "all",
|
|
419
|
+
phase: "dedup",
|
|
420
|
+
message: `Deduplicating ${records.length} session records...`,
|
|
421
|
+
});
|
|
422
|
+
const deduped = deduplicateSessionRecords(records);
|
|
423
|
+
// ---------- Save cursor state FIRST (before queue) ----------
|
|
424
|
+
// Same safety invariant as token sync: cursor saved before queue
|
|
425
|
+
// so a crash never causes duplicate writes.
|
|
426
|
+
cursors.updatedAt = new Date().toISOString();
|
|
427
|
+
await cursorStore.save(cursors);
|
|
428
|
+
// ---------- Write to session queue ----------
|
|
429
|
+
if (deduped.length > 0) {
|
|
430
|
+
await queue.appendBatch(deduped);
|
|
431
|
+
}
|
|
432
|
+
onProgress?.({
|
|
433
|
+
source: "all",
|
|
434
|
+
phase: "done",
|
|
435
|
+
message: `Synced ${allSnapshots.length} snapshots → ${deduped.length} records`,
|
|
436
|
+
});
|
|
437
|
+
return {
|
|
438
|
+
totalSnapshots: allSnapshots.length,
|
|
439
|
+
totalRecords: deduped.length,
|
|
440
|
+
sources: sourceCounts,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=session-sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-sync.js","sourceRoot":"","sources":["../../src/commands/session-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,uCAAuC,CAAC;AAEtF,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAsDhE,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,yEAAyE;AACzE,SAAS,aAAa,CAAC,IAAqB;IAC1C,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,UAAU;QAC5B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,UAAU,EAAE,IAAI,CAAC,SAAS;QAC1B,eAAe,EAAE,IAAI,CAAC,aAAa;QACnC,gBAAgB,EAAE,IAAI,CAAC,eAAe;QACtC,aAAa,EAAE,IAAI,CAAC,YAAY;QAChC,kBAAkB,EAAE,IAAI,CAAC,iBAAiB;QAC1C,cAAc,EAAE,IAAI,CAAC,aAAa;QAClC,WAAW,EAAE,IAAI,CAAC,UAAU;QAC5B,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,WAAW,EAAE,IAAI,CAAC,UAAU;KAC7B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAClB,MAAqC,EACrC,OAAe,EACf,IAAY;IAEZ,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,MAAM,CAAC,OAAO,KAAK,OAAO,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,2BAA2B,CACxC,UAAkB;IAElB,IAAI,OAAmC,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,OAAO;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;SACpC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAwB;IAExB,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAEtC,MAAM,WAAW,GAAG,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;IAEzC,MAAM,YAAY,GAAsB,EAAE,CAAC;IAC3C,MAAM,YAAY,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IAElF,oCAAoC;IACpC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,aAAa;YACrB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,0CAA0C;SACpD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxD,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,aAAa;YACrB,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,OAAO,EAAE,YAAY,KAAK,CAAC,MAAM,0BAA0B;SAC5D,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAkC,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,aAAa;oBACrB,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC3D,CAAC,GAAY,EAAE,EAAE;gBACf,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,aAAa;oBACrB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,YAAY,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACrF,CAAC,CAAC;gBACH,OAAO,EAAuB,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,oCAAoC;YACpC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;YAEjE,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;YAChC,YAAY,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC;YAExC,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,GAAG,CAAC;gBACd,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mCAAmC;IACnC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,yCAAyC;SACnD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACxD,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,OAAO,EAAE,YAAY,KAAK,CAAC,MAAM,0BAA0B;SAC5D,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAkC,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,YAAY;oBACpB,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC3D,CAAC,GAAY,EAAE,EAAE;gBACf,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,YAAY;oBACpB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,YAAY,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACrF,CAAC,CAAC;gBACH,OAAO,EAAuB,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;YAEjE,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;YAChC,YAAY,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC;YAExC,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,YAAY;gBACpB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,GAAG,CAAC;gBACd,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC5B,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,6CAA6C;SACvD,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,2BAA2B,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QACxE,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,IAAI,CAAC,MAAM;YAClB,OAAO,EAAE,YAAY,IAAI,CAAC,MAAM,kCAAkC;SACnE,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,6DAA6D;YAC7D,6DAA6D;YAC7D,gDAAgD;YAChD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAkC,CAAC;YACvE,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,EAAE,CAAC,OAAO,EAAE,CAAC;gBAC5C,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,IAAI,CAAC,MAAM;iBACnB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,OAAO,CAAC,CAAC,KAAK,CAC5D,CAAC,GAAY,EAAE,EAAE;gBACf,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,YAAY,OAAO,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACpF,CAAC,CAAC;gBACH,OAAO,EAAuB,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAE1D,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;YAChC,YAAY,CAAC,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC;YAE1C,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,GAAG,CAAC;gBACd,KAAK,EAAE,IAAI,CAAC,MAAM;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,iBAAiB;YACzB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,mDAAmD;SAC7D,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAEjE,IAAI,MAAM,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YAClC,mEAAmE;YACnE,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,iBAAiB;gBACzB,KAAK,EAAE,MAAM;gBACb,OAAO,EAAE,qCAAqC,IAAI,CAAC,cAAc,2EAA2E;aAC7I,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,MAAM,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACxC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC;YAC3B,MAAM,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC;YAE1C,gDAAgD;YAChD,MAAM,eAAe,GACnB,UAAU,IAAI,UAAU,CAAC,KAAK,KAAK,OAAO;gBACxC,CAAC,CAAC,UAAU,CAAC,eAAe;gBAC5B,CAAC,CAAC,CAAC,CAAC;YACR,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,UAAU,IAAI,UAAU,CAAC,KAAK,KAAK,OAAO;gBACxC,CAAC,CAAC,CAAC,UAAU,CAAC,gBAAgB,IAAI,EAAE,CAAC;gBACrC,CAAC,CAAC,EAAE,CACP,CAAC;YAEF,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACvD,IAAI,MAAM,EAAE,CAAC;gBACX,IAAI,CAAC;oBACH,wDAAwD;oBACxD,0DAA0D;oBAC1D,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;oBAC1D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,GAAG,CAAC;wBACxC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBACxD,CAAC,CAAC,WAAW,CAAC;oBAEhB,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACxB,MAAM,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;wBAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;wBACzD,MAAM,SAAS,GAAG,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;wBAEpE,UAAU,EAAE,CAAC;4BACX,MAAM,EAAE,iBAAiB;4BACzB,KAAK,EAAE,OAAO;4BACd,OAAO,EAAE,aAAa,SAAS,CAAC,MAAM,kBAAkB,WAAW,CAAC,MAAM,sBAAsB;yBACjG,CAAC,CAAC;wBAEH,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;wBAChC,YAAY,CAAC,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC;oBAC5C,CAAC;yBAAM,CAAC;wBACN,UAAU,EAAE,CAAC;4BACX,MAAM,EAAE,iBAAiB;4BACzB,KAAK,EAAE,OAAO;4BACd,OAAO,EAAE,8BAA8B;yBACxC,CAAC,CAAC;oBACL,CAAC;oBAED,6DAA6D;oBAC7D,+DAA+D;oBAC/D,4DAA4D;oBAC5D,qBAAqB;oBACrB,MAAM,cAAc,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC;wBAC3C,CAAC,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,YAAY;wBAClD,CAAC,CAAC,eAAe,CAAC;oBACpB,MAAM,QAAQ,GAAG,WAAW;yBACzB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,KAAK,cAAc,CAAC;yBAChD,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;oBACpB,OAAO,CAAC,cAAc,GAAG;wBACvB,eAAe,EAAE,cAAc;wBAC/B,gBAAgB,EAAE,QAAQ;wBAC1B,KAAK,EAAE,OAAO;wBACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;qBACE,CAAC;gBAC1C,CAAC;wBAAS,CAAC;oBACT,MAAM,CAAC,KAAK,EAAE,CAAC;gBACjB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,iEAAiE;gBACjE,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,iBAAiB;oBACzB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,8CAA8C,IAAI,CAAC,cAAc,2CAA2C;iBACtH,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;QACrB,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,uCAAuC;SACjD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC5D,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,UAAU;YAClB,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,OAAO,EAAE,YAAY,KAAK,CAAC,MAAM,4BAA4B;SAC9D,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAkC,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC7D,CAAC,GAAY,EAAE,EAAE;gBACf,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,UAAU;oBAClB,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,YAAY,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACrF,CAAC,CAAC;gBACH,OAAO,EAAuB,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;YAEjE,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;YAChC,YAAY,CAAC,QAAQ,IAAI,SAAS,CAAC,MAAM,CAAC;YAE1C,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,UAAU;gBAClB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,GAAG,CAAC;gBACd,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC1B,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,UAAU;YACjB,OAAO,EAAE,wCAAwC;SAClD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC9D,UAAU,EAAE,CAAC;YACX,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,OAAO;YACd,KAAK,EAAE,KAAK,CAAC,MAAM;YACnB,OAAO,EAAE,YAAY,KAAK,CAAC,MAAM,yBAAyB;SAC3D,CAAC,CAAC;QAEH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,EAAE;gBAAE,SAAS;YAElB,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAkC,CAAC;YACxE,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,OAAO;oBACf,KAAK,EAAE,OAAO;oBACd,OAAO,EAAE,CAAC,GAAG,CAAC;oBACd,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,MAAM,SAAS,GAAG,MAAM,oBAAoB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAC1D,CAAC,GAAY,EAAE,EAAE;gBACf,UAAU,EAAE,CAAC;oBACX,MAAM,EAAE,OAAO;oBACf,KAAK,EAAE,MAAM;oBACb,OAAO,EAAE,YAAY,QAAQ,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACrF,CAAC,CAAC;gBACH,OAAO,EAAuB,CAAC;YACjC,CAAC,CACF,CAAC;YAEF,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;YAEjE,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;YAChC,YAAY,CAAC,KAAK,IAAI,SAAS,CAAC,MAAM,CAAC;YAEvC,UAAU,EAAE,CAAC;gBACX,MAAM,EAAE,OAAO;gBACf,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,CAAC,GAAG,CAAC;gBACd,KAAK,EAAE,KAAK,CAAC,MAAM;aACpB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,2DAA2D;IAC3D,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAEhD,0EAA0E;IAC1E,UAAU,EAAE,CAAC;QACX,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,OAAO;QACd,OAAO,EAAE,iBAAiB,OAAO,CAAC,MAAM,qBAAqB;KAC9D,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAEnD,+DAA+D;IAC/D,iEAAiE;IACjE,4CAA4C;IAC5C,OAAO,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAEhC,+CAA+C;IAC/C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAED,UAAU,EAAE,CAAC;QACX,MAAM,EAAE,KAAK;QACb,KAAK,EAAE,MAAM;QACb,OAAO,EAAE,UAAU,YAAY,CAAC,MAAM,gBAAgB,OAAO,CAAC,MAAM,UAAU;KAC/E,CAAC,CAAC;IAEH,OAAO;QACL,cAAc,EAAE,YAAY,CAAC,MAAM;QACnC,YAAY,EAAE,OAAO,CAAC,MAAM;QAC5B,OAAO,EAAE,YAAY;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session upload command — sends local session queue records to the Pew SaaS.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Load API key from config
|
|
6
|
+
* 2. Read un-uploaded session records from queue (using saved offset)
|
|
7
|
+
* 3. Deduplicate: keep only latest snapshot per session_key
|
|
8
|
+
* 4. Split into batches of ≤50 (D1 Free plan limit)
|
|
9
|
+
* 5. POST each batch to /api/ingest/sessions with Bearer token
|
|
10
|
+
* 6. Persist offset after all batches succeed
|
|
11
|
+
* 7. Retry on 5xx/429 with exponential backoff
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionQueueRecord } from "@pew/core";
|
|
14
|
+
export interface SessionUploadOptions {
|
|
15
|
+
/** Directory for config file and queue state */
|
|
16
|
+
stateDir: string;
|
|
17
|
+
/** Base URL of the Pew SaaS */
|
|
18
|
+
apiUrl: string;
|
|
19
|
+
/** Whether dev mode is active (uses config.dev.json) */
|
|
20
|
+
dev?: boolean;
|
|
21
|
+
/** Injected fetch (for testing) */
|
|
22
|
+
fetch: typeof globalThis.fetch;
|
|
23
|
+
/** Max records per API request (default: 50) */
|
|
24
|
+
batchSize?: number;
|
|
25
|
+
/** Max retries per batch on 5xx (default: 2) */
|
|
26
|
+
maxRetries?: number;
|
|
27
|
+
/** Base retry delay in ms (default: 1000, doubled each retry) */
|
|
28
|
+
retryDelayMs?: number;
|
|
29
|
+
/** Progress callback */
|
|
30
|
+
onProgress?: (event: SessionUploadProgressEvent) => void;
|
|
31
|
+
}
|
|
32
|
+
export interface SessionUploadProgressEvent {
|
|
33
|
+
phase: "uploading" | "done";
|
|
34
|
+
batch?: number;
|
|
35
|
+
totalBatches?: number;
|
|
36
|
+
total?: number;
|
|
37
|
+
message?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SessionUploadResult {
|
|
40
|
+
success: boolean;
|
|
41
|
+
uploaded: number;
|
|
42
|
+
batches: number;
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Unlike token's aggregateRecords() which SUMS, session dedup
|
|
47
|
+
* keeps only the LATEST snapshot per session_key.
|
|
48
|
+
*
|
|
49
|
+
* This ensures idempotent uploads: re-scanning the same session
|
|
50
|
+
* files produces the same final result after server-side monotonic
|
|
51
|
+
* upsert (WHERE excluded.snapshot_at >= session_records.snapshot_at).
|
|
52
|
+
*/
|
|
53
|
+
export declare function deduplicateSessionRecords(records: SessionQueueRecord[]): SessionQueueRecord[];
|
|
54
|
+
export declare function executeSessionUpload(opts: SessionUploadOptions): Promise<SessionUploadResult>;
|
|
55
|
+
//# sourceMappingURL=session-upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-upload.d.ts","sourceRoot":"","sources":["../../src/commands/session-upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAQpD,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,mCAAmC;IACnC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;CAC1D;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,WAAW,GAAG,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAcD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,kBAAkB,EAAE,GAC5B,kBAAkB,EAAE,CAWtB;AAMD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,oBAAoB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CAgG9B"}
|