@nocoo/pew 0.4.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +117 -8
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/session-sync.d.ts +62 -0
  5. package/dist/commands/session-sync.d.ts.map +1 -0
  6. package/dist/commands/session-sync.js +443 -0
  7. package/dist/commands/session-sync.js.map +1 -0
  8. package/dist/commands/session-upload.d.ts +39 -0
  9. package/dist/commands/session-upload.d.ts.map +1 -0
  10. package/dist/commands/session-upload.js +45 -0
  11. package/dist/commands/session-upload.js.map +1 -0
  12. package/dist/commands/status.d.ts +9 -0
  13. package/dist/commands/status.d.ts.map +1 -1
  14. package/dist/commands/status.js +22 -11
  15. package/dist/commands/status.js.map +1 -1
  16. package/dist/commands/sync.d.ts +12 -0
  17. package/dist/commands/sync.d.ts.map +1 -1
  18. package/dist/commands/sync.js +159 -3
  19. package/dist/commands/sync.js.map +1 -1
  20. package/dist/commands/upload-engine.d.ts +63 -0
  21. package/dist/commands/upload-engine.d.ts.map +1 -0
  22. package/dist/commands/upload-engine.js +164 -0
  23. package/dist/commands/upload-engine.js.map +1 -0
  24. package/dist/commands/upload.d.ts +5 -21
  25. package/dist/commands/upload.d.ts.map +1 -1
  26. package/dist/commands/upload.js +11 -144
  27. package/dist/commands/upload.js.map +1 -1
  28. package/dist/discovery/sources.d.ts +5 -0
  29. package/dist/discovery/sources.d.ts.map +1 -1
  30. package/dist/discovery/sources.js +7 -0
  31. package/dist/discovery/sources.js.map +1 -1
  32. package/dist/parsers/claude-session.d.ts +19 -0
  33. package/dist/parsers/claude-session.d.ts.map +1 -0
  34. package/dist/parsers/claude-session.js +131 -0
  35. package/dist/parsers/claude-session.js.map +1 -0
  36. package/dist/parsers/codex-session.d.ts +24 -0
  37. package/dist/parsers/codex-session.d.ts.map +1 -0
  38. package/dist/parsers/codex-session.js +140 -0
  39. package/dist/parsers/codex-session.js.map +1 -0
  40. package/dist/parsers/codex.d.ts +37 -0
  41. package/dist/parsers/codex.d.ts.map +1 -0
  42. package/dist/parsers/codex.js +136 -0
  43. package/dist/parsers/codex.js.map +1 -0
  44. package/dist/parsers/gemini-session.d.ts +19 -0
  45. package/dist/parsers/gemini-session.d.ts.map +1 -0
  46. package/dist/parsers/gemini-session.js +103 -0
  47. package/dist/parsers/gemini-session.js.map +1 -0
  48. package/dist/parsers/openclaw-session.d.ts +20 -0
  49. package/dist/parsers/openclaw-session.d.ts.map +1 -0
  50. package/dist/parsers/openclaw-session.js +122 -0
  51. package/dist/parsers/openclaw-session.js.map +1 -0
  52. package/dist/parsers/opencode-session.d.ts +15 -0
  53. package/dist/parsers/opencode-session.d.ts.map +1 -0
  54. package/dist/parsers/opencode-session.js +131 -0
  55. package/dist/parsers/opencode-session.js.map +1 -0
  56. package/dist/parsers/opencode-sqlite-db.d.ts +29 -0
  57. package/dist/parsers/opencode-sqlite-db.d.ts.map +1 -0
  58. package/dist/parsers/opencode-sqlite-db.js +71 -0
  59. package/dist/parsers/opencode-sqlite-db.js.map +1 -0
  60. package/dist/parsers/opencode-sqlite-session.d.ts +32 -0
  61. package/dist/parsers/opencode-sqlite-session.d.ts.map +1 -0
  62. package/dist/parsers/opencode-sqlite-session.js +121 -0
  63. package/dist/parsers/opencode-sqlite-session.js.map +1 -0
  64. package/dist/parsers/opencode-sqlite.d.ts +53 -0
  65. package/dist/parsers/opencode-sqlite.d.ts.map +1 -0
  66. package/dist/parsers/opencode-sqlite.js +104 -0
  67. package/dist/parsers/opencode-sqlite.js.map +1 -0
  68. package/dist/storage/base-queue.d.ts +40 -0
  69. package/dist/storage/base-queue.d.ts.map +1 -0
  70. package/dist/storage/base-queue.js +89 -0
  71. package/dist/storage/base-queue.js.map +1 -0
  72. package/dist/storage/local-queue.d.ts +4 -24
  73. package/dist/storage/local-queue.d.ts.map +1 -1
  74. package/dist/storage/local-queue.js +5 -64
  75. package/dist/storage/local-queue.js.map +1 -1
  76. package/dist/storage/session-cursor-store.d.ts +14 -0
  77. package/dist/storage/session-cursor-store.d.ts.map +1 -0
  78. package/dist/storage/session-cursor-store.js +34 -0
  79. package/dist/storage/session-cursor-store.js.map +1 -0
  80. package/dist/storage/session-queue.d.ts +10 -0
  81. package/dist/storage/session-queue.d.ts.map +1 -0
  82. package/dist/storage/session-queue.js +11 -0
  83. package/dist/storage/session-queue.js.map +1 -0
  84. package/dist/utils/paths.d.ts +4 -0
  85. package/dist/utils/paths.d.ts.map +1 -1
  86. package/dist/utils/paths.js +5 -0
  87. package/dist/utils/paths.js.map +1 -1
  88. 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":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CLI session upload command — sends local session queue records to the Pew SaaS.
3
+ *
4
+ * Thin wrapper around the generic upload engine with session-specific
5
+ * preprocessing (deduplication: keep only latest snapshot per session_key).
6
+ */
7
+ import type { UploadResult, UploadProgressEvent } from "./upload-engine.js";
8
+ import type { SessionQueueRecord } from "@pew/core";
9
+ export interface SessionUploadOptions {
10
+ /** Directory for config file and queue state */
11
+ stateDir: string;
12
+ /** Base URL of the Pew SaaS */
13
+ apiUrl: string;
14
+ /** Whether dev mode is active (uses config.dev.json) */
15
+ dev?: boolean;
16
+ /** Injected fetch (for testing) */
17
+ fetch: typeof globalThis.fetch;
18
+ /** Max records per API request (default: 50) */
19
+ batchSize?: number;
20
+ /** Max retries per batch on 5xx (default: 2) */
21
+ maxRetries?: number;
22
+ /** Base retry delay in ms (default: 1000, doubled each retry) */
23
+ retryDelayMs?: number;
24
+ /** Progress callback */
25
+ onProgress?: (event: SessionUploadProgressEvent) => void;
26
+ }
27
+ export type SessionUploadProgressEvent = UploadProgressEvent;
28
+ export type SessionUploadResult = UploadResult;
29
+ /**
30
+ * Unlike token's aggregateRecords() which SUMS, session dedup
31
+ * keeps only the LATEST snapshot per session_key.
32
+ *
33
+ * This ensures idempotent uploads: re-scanning the same session
34
+ * files produces the same final result after server-side monotonic
35
+ * upsert (WHERE excluded.snapshot_at >= session_records.snapshot_at).
36
+ */
37
+ export declare function deduplicateSessionRecords(records: SessionQueueRecord[]): SessionQueueRecord[];
38
+ export declare function executeSessionUpload(opts: SessionUploadOptions): Promise<SessionUploadResult>;
39
+ //# 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;;;;;GAKG;AAIH,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EAEpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAMpD,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,MAAM,0BAA0B,GAAG,mBAAmB,CAAC;AAC7D,MAAM,MAAM,mBAAmB,GAAG,YAAY,CAAC;AAM/C;;;;;;;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,CAW9B"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * CLI session upload command — sends local session queue records to the Pew SaaS.
3
+ *
4
+ * Thin wrapper around the generic upload engine with session-specific
5
+ * preprocessing (deduplication: keep only latest snapshot per session_key).
6
+ */
7
+ import { SessionQueue } from "../storage/session-queue.js";
8
+ import { createUploadEngine } from "./upload-engine.js";
9
+ // ---------------------------------------------------------------------------
10
+ // Pre-dedup — keep only the latest snapshot per session_key
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Unlike token's aggregateRecords() which SUMS, session dedup
14
+ * keeps only the LATEST snapshot per session_key.
15
+ *
16
+ * This ensures idempotent uploads: re-scanning the same session
17
+ * files produces the same final result after server-side monotonic
18
+ * upsert (WHERE excluded.snapshot_at >= session_records.snapshot_at).
19
+ */
20
+ export function deduplicateSessionRecords(records) {
21
+ if (records.length === 0)
22
+ return [];
23
+ const map = new Map();
24
+ for (const r of records) {
25
+ const existing = map.get(r.session_key);
26
+ if (!existing || r.snapshot_at > existing.snapshot_at) {
27
+ map.set(r.session_key, r);
28
+ }
29
+ }
30
+ return [...map.values()];
31
+ }
32
+ // ---------------------------------------------------------------------------
33
+ // Implementation
34
+ // ---------------------------------------------------------------------------
35
+ export async function executeSessionUpload(opts) {
36
+ const queue = new SessionQueue(opts.stateDir);
37
+ const engine = createUploadEngine({
38
+ queue,
39
+ endpoint: "/api/ingest/sessions",
40
+ entityName: "session records",
41
+ preprocess: deduplicateSessionRecords,
42
+ });
43
+ return engine.execute(opts);
44
+ }
45
+ //# sourceMappingURL=session-upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-upload.js","sourceRoot":"","sources":["../../src/commands/session-upload.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAkCxD,8EAA8E;AAC9E,4DAA4D;AAC5D,8EAA8E;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAA6B;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,MAAM,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;YACtD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,IAA0B;IAE1B,MAAM,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,kBAAkB,CAAqB;QACpD,KAAK;QACL,QAAQ,EAAE,sBAAsB;QAChC,UAAU,EAAE,iBAAiB;QAC7B,UAAU,EAAE,yBAAyB;KACtC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
@@ -1,3 +1,11 @@
1
+ /** Resolved source directory paths used for file classification */
2
+ export interface SourceDirs {
3
+ claudeDir: string;
4
+ codexSessionsDir: string;
5
+ geminiDir: string;
6
+ openCodeMessageDir: string;
7
+ openclawDir: string;
8
+ }
1
9
  /** Status summary for display */
2
10
  export interface StatusResult {
3
11
  /** Number of tracked files */
@@ -15,5 +23,6 @@ export interface StatusResult {
15
23
  */
16
24
  export declare function executeStatus(opts: {
17
25
  stateDir: string;
26
+ sourceDirs: SourceDirs;
18
27
  }): Promise<StatusResult>;
19
28
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAKA,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,YAAY,CAAC,CA4BxB"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,8BAA8B;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAiBD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB,GAAG,OAAO,CAAC,YAAY,CAAC,CAuBxB"}
@@ -1,28 +1,39 @@
1
1
  import { CursorStore } from "../storage/cursor-store.js";
2
2
  import { LocalQueue } from "../storage/local-queue.js";
3
+ /**
4
+ * Classify a cursor file path into a source label.
5
+ *
6
+ * Uses resolved source directories (startsWith) so that custom paths
7
+ * like $CODEX_HOME are classified correctly.
8
+ */
9
+ function classifySource(filePath, dirs) {
10
+ if (filePath.startsWith(dirs.claudeDir))
11
+ return "claude-code";
12
+ if (filePath.startsWith(dirs.codexSessionsDir))
13
+ return "codex";
14
+ if (filePath.startsWith(dirs.geminiDir))
15
+ return "gemini-cli";
16
+ if (filePath.startsWith(dirs.openCodeMessageDir))
17
+ return "opencode";
18
+ if (filePath.startsWith(dirs.openclawDir))
19
+ return "openclaw";
20
+ return "unknown";
21
+ }
3
22
  /**
4
23
  * Compute the current sync status.
5
24
  * Pure logic — no CLI I/O.
6
25
  */
7
26
  export async function executeStatus(opts) {
8
- const { stateDir } = opts;
27
+ const { stateDir, sourceDirs } = opts;
9
28
  const cursorStore = new CursorStore(stateDir);
10
29
  const queue = new LocalQueue(stateDir);
11
30
  const cursors = await cursorStore.load();
12
31
  const offset = await queue.loadOffset();
13
32
  const { records } = await queue.readFromOffset(offset);
14
- // Count files by source based on path patterns
33
+ // Count files by source using resolved directory paths
15
34
  const sources = {};
16
35
  for (const filePath of Object.keys(cursors.files)) {
17
- let source = "unknown";
18
- if (filePath.includes(".claude"))
19
- source = "claude-code";
20
- else if (filePath.includes(".gemini"))
21
- source = "gemini-cli";
22
- else if (filePath.includes("opencode"))
23
- source = "opencode";
24
- else if (filePath.includes(".openclaw"))
25
- source = "openclaw";
36
+ const source = classifySource(filePath, sourceDirs);
26
37
  sources[source] = (sources[source] || 0) + 1;
27
38
  }
28
39
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAevD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAEnC;IACC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAE1B,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEvC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IACxC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvD,+CAA+C;IAC/C,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,IAAI,MAAM,GAAG,SAAS,CAAC;QACvB,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,MAAM,GAAG,aAAa,CAAC;aACpD,IAAI,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,MAAM,GAAG,YAAY,CAAC;aACxD,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC;YAAE,MAAM,GAAG,UAAU,CAAC;aACvD,IAAI,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,MAAM,GAAG,UAAU,CAAC;QAE7D,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM;QAC/C,QAAQ,EAAE,OAAO,CAAC,SAAS;QAC3B,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,OAAO;KACR,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAuBvD;;;;;GAKG;AACH,SAAS,cAAc,CAAC,QAAgB,EAAE,IAAgB;IACxD,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,aAAa,CAAC;IAC9D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,gBAAgB,CAAC;QAAE,OAAO,OAAO,CAAC;IAC/D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,YAAY,CAAC;IAC7D,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,kBAAkB,CAAC;QAAE,OAAO,UAAU,CAAC;IACpE,IAAI,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC;QAAE,OAAO,UAAU,CAAC;IAC7D,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAGnC;IACC,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IAEtC,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEvC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IACxC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvD,uDAAuD;IACvD,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;QACpD,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO;QACL,YAAY,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM;QAC/C,QAAQ,EAAE,OAAO,CAAC,SAAS;QAC3B,cAAc,EAAE,OAAO,CAAC,MAAM;QAC9B,OAAO;KACR,CAAC;AACJ,CAAC"}