@nocoo/pew 0.2.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/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +5 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +221 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/login.d.ts +34 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +108 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/status.d.ts +19 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +35 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +49 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +267 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/upload.d.ts +53 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +185 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config/manager.d.ts +14 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +32 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/discovery/sources.d.ts +37 -0
- package/dist/discovery/sources.d.ts.map +1 -0
- package/dist/discovery/sources.js +110 -0
- package/dist/discovery/sources.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/claude.d.ts +34 -0
- package/dist/parsers/claude.d.ts.map +1 -0
- package/dist/parsers/claude.js +101 -0
- package/dist/parsers/claude.js.map +1 -0
- package/dist/parsers/gemini.d.ts +38 -0
- package/dist/parsers/gemini.d.ts.map +1 -0
- package/dist/parsers/gemini.js +142 -0
- package/dist/parsers/gemini.js.map +1 -0
- package/dist/parsers/openclaw.d.ts +23 -0
- package/dist/parsers/openclaw.d.ts.map +1 -0
- package/dist/parsers/openclaw.js +85 -0
- package/dist/parsers/openclaw.js.map +1 -0
- package/dist/parsers/opencode.d.ts +35 -0
- package/dist/parsers/opencode.d.ts.map +1 -0
- package/dist/parsers/opencode.js +118 -0
- package/dist/parsers/opencode.js.map +1 -0
- package/dist/storage/cursor-store.d.ts +14 -0
- package/dist/storage/cursor-store.d.ts.map +1 -0
- package/dist/storage/cursor-store.js +34 -0
- package/dist/storage/cursor-store.js.map +1 -0
- package/dist/storage/local-queue.d.ts +30 -0
- package/dist/storage/local-queue.d.ts.map +1 -0
- package/dist/storage/local-queue.js +70 -0
- package/dist/storage/local-queue.js.map +1 -0
- package/dist/utils/buckets.d.ts +17 -0
- package/dist/utils/buckets.d.ts.map +1 -0
- package/dist/utils/buckets.js +38 -0
- package/dist/utils/buckets.js.map +1 -0
- package/dist/utils/paths.d.ts +17 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +21 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { stat } from "node:fs/promises";
|
|
2
|
+
import { CursorStore } from "../storage/cursor-store.js";
|
|
3
|
+
import { LocalQueue } from "../storage/local-queue.js";
|
|
4
|
+
import { discoverClaudeFiles, discoverGeminiFiles, discoverOpenCodeFiles, discoverOpenClawFiles, } from "../discovery/sources.js";
|
|
5
|
+
import { parseClaudeFile } from "../parsers/claude.js";
|
|
6
|
+
import { parseGeminiFile } from "../parsers/gemini.js";
|
|
7
|
+
import { parseOpenCodeFile } from "../parsers/opencode.js";
|
|
8
|
+
import { parseOpenClawFile } from "../parsers/openclaw.js";
|
|
9
|
+
import { toUtcHalfHourStart, bucketKey, addTokens, emptyTokenDelta } from "../utils/buckets.js";
|
|
10
|
+
/**
|
|
11
|
+
* Execute the sync operation: discover files, parse incrementally,
|
|
12
|
+
* aggregate into half-hour buckets, and write to local queue.
|
|
13
|
+
*
|
|
14
|
+
* Pure logic — no CLI I/O. Receives all dependencies via options.
|
|
15
|
+
*/
|
|
16
|
+
export async function executeSync(opts) {
|
|
17
|
+
const { stateDir, onProgress } = opts;
|
|
18
|
+
const cursorStore = new CursorStore(stateDir);
|
|
19
|
+
const queue = new LocalQueue(stateDir);
|
|
20
|
+
const cursors = await cursorStore.load();
|
|
21
|
+
const allDeltas = [];
|
|
22
|
+
const sourceCounts = { claude: 0, gemini: 0, opencode: 0, openclaw: 0 };
|
|
23
|
+
const filesScanned = { claude: 0, gemini: 0, opencode: 0, openclaw: 0 };
|
|
24
|
+
// ---------- Claude Code ----------
|
|
25
|
+
if (opts.claudeDir) {
|
|
26
|
+
onProgress?.({
|
|
27
|
+
source: "claude-code",
|
|
28
|
+
phase: "discover",
|
|
29
|
+
message: "Discovering Claude Code files...",
|
|
30
|
+
});
|
|
31
|
+
const files = await discoverClaudeFiles(opts.claudeDir);
|
|
32
|
+
filesScanned.claude = files.length;
|
|
33
|
+
onProgress?.({ source: "claude-code",
|
|
34
|
+
phase: "parse",
|
|
35
|
+
total: files.length,
|
|
36
|
+
message: `Parsing ${files.length} Claude files...`,
|
|
37
|
+
});
|
|
38
|
+
for (let i = 0; i < files.length; i++) {
|
|
39
|
+
const filePath = files[i];
|
|
40
|
+
const prev = cursors.files[filePath];
|
|
41
|
+
const st = await stat(filePath).catch(() => null);
|
|
42
|
+
if (!st)
|
|
43
|
+
continue;
|
|
44
|
+
const inode = st.ino;
|
|
45
|
+
const startOffset = prev && prev.inode === inode ? (prev.offset ?? 0) : 0;
|
|
46
|
+
const result = await parseClaudeFile({ filePath, startOffset });
|
|
47
|
+
cursors.files[filePath] = {
|
|
48
|
+
inode,
|
|
49
|
+
offset: result.endOffset,
|
|
50
|
+
updatedAt: new Date().toISOString(),
|
|
51
|
+
};
|
|
52
|
+
allDeltas.push(...result.deltas);
|
|
53
|
+
sourceCounts.claude += result.deltas.length;
|
|
54
|
+
onProgress?.({
|
|
55
|
+
source: "claude-code",
|
|
56
|
+
phase: "parse",
|
|
57
|
+
current: i + 1,
|
|
58
|
+
total: files.length,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ---------- Gemini CLI ----------
|
|
63
|
+
if (opts.geminiDir) {
|
|
64
|
+
onProgress?.({
|
|
65
|
+
source: "gemini-cli",
|
|
66
|
+
phase: "discover",
|
|
67
|
+
message: "Discovering Gemini CLI files...",
|
|
68
|
+
});
|
|
69
|
+
const files = await discoverGeminiFiles(opts.geminiDir);
|
|
70
|
+
filesScanned.gemini = files.length;
|
|
71
|
+
onProgress?.({
|
|
72
|
+
source: "gemini-cli",
|
|
73
|
+
phase: "parse",
|
|
74
|
+
total: files.length,
|
|
75
|
+
message: `Parsing ${files.length} Gemini files...`,
|
|
76
|
+
});
|
|
77
|
+
for (let i = 0; i < files.length; i++) {
|
|
78
|
+
const filePath = files[i];
|
|
79
|
+
const prev = cursors.files[filePath];
|
|
80
|
+
const st = await stat(filePath).catch(() => null);
|
|
81
|
+
if (!st)
|
|
82
|
+
continue;
|
|
83
|
+
const inode = st.ino;
|
|
84
|
+
const startIndex = prev && prev.inode === inode ? (prev.lastIndex ?? -1) : -1;
|
|
85
|
+
const lastTotals = prev && prev.inode === inode ? (prev.lastTotals ?? null) : null;
|
|
86
|
+
const result = await parseGeminiFile({
|
|
87
|
+
filePath,
|
|
88
|
+
startIndex,
|
|
89
|
+
lastTotals,
|
|
90
|
+
});
|
|
91
|
+
cursors.files[filePath] = {
|
|
92
|
+
inode,
|
|
93
|
+
lastIndex: result.lastIndex,
|
|
94
|
+
lastTotals: result.lastTotals,
|
|
95
|
+
lastModel: result.lastModel,
|
|
96
|
+
updatedAt: new Date().toISOString(),
|
|
97
|
+
};
|
|
98
|
+
allDeltas.push(...result.deltas);
|
|
99
|
+
sourceCounts.gemini += result.deltas.length;
|
|
100
|
+
onProgress?.({
|
|
101
|
+
source: "gemini-cli",
|
|
102
|
+
phase: "parse",
|
|
103
|
+
current: i + 1,
|
|
104
|
+
total: files.length,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// ---------- OpenCode ----------
|
|
109
|
+
if (opts.openCodeMessageDir) {
|
|
110
|
+
onProgress?.({
|
|
111
|
+
source: "opencode",
|
|
112
|
+
phase: "discover",
|
|
113
|
+
message: "Discovering OpenCode files...",
|
|
114
|
+
});
|
|
115
|
+
const discovery = await discoverOpenCodeFiles(opts.openCodeMessageDir, cursors.dirMtimes);
|
|
116
|
+
const files = discovery.files;
|
|
117
|
+
// Count includes files in skipped dirs (already tracked in cursors)
|
|
118
|
+
filesScanned.opencode = files.length;
|
|
119
|
+
onProgress?.({
|
|
120
|
+
source: "opencode",
|
|
121
|
+
phase: "parse",
|
|
122
|
+
total: files.length,
|
|
123
|
+
message: `Parsing ${files.length} OpenCode files (${discovery.skippedDirs} dirs skipped)...`,
|
|
124
|
+
});
|
|
125
|
+
for (let i = 0; i < files.length; i++) {
|
|
126
|
+
const filePath = files[i];
|
|
127
|
+
const prev = cursors.files[filePath];
|
|
128
|
+
const st = await stat(filePath).catch(() => null);
|
|
129
|
+
if (!st)
|
|
130
|
+
continue;
|
|
131
|
+
const inode = st.ino;
|
|
132
|
+
// Triple-check unchanged optimization
|
|
133
|
+
if (prev &&
|
|
134
|
+
prev.inode === inode &&
|
|
135
|
+
prev.size === st.size &&
|
|
136
|
+
prev.mtimeMs === st.mtimeMs) {
|
|
137
|
+
onProgress?.({
|
|
138
|
+
source: "opencode",
|
|
139
|
+
phase: "parse",
|
|
140
|
+
current: i + 1,
|
|
141
|
+
total: files.length,
|
|
142
|
+
});
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const lastTotals = prev && prev.inode === inode ? (prev.lastTotals ?? null) : null;
|
|
146
|
+
const result = await parseOpenCodeFile({ filePath, lastTotals });
|
|
147
|
+
cursors.files[filePath] = {
|
|
148
|
+
inode,
|
|
149
|
+
size: st.size,
|
|
150
|
+
mtimeMs: st.mtimeMs,
|
|
151
|
+
lastTotals: result.lastTotals,
|
|
152
|
+
messageKey: result.messageKey,
|
|
153
|
+
updatedAt: new Date().toISOString(),
|
|
154
|
+
};
|
|
155
|
+
if (result.delta) {
|
|
156
|
+
allDeltas.push(result.delta);
|
|
157
|
+
sourceCounts.opencode += 1;
|
|
158
|
+
}
|
|
159
|
+
onProgress?.({
|
|
160
|
+
source: "opencode",
|
|
161
|
+
phase: "parse",
|
|
162
|
+
current: i + 1,
|
|
163
|
+
total: files.length,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// Persist directory mtimes for next run
|
|
167
|
+
cursors.dirMtimes = discovery.dirMtimes;
|
|
168
|
+
}
|
|
169
|
+
// ---------- OpenClaw ----------
|
|
170
|
+
if (opts.openclawDir) {
|
|
171
|
+
onProgress?.({
|
|
172
|
+
source: "openclaw",
|
|
173
|
+
phase: "discover",
|
|
174
|
+
message: "Discovering OpenClaw files...",
|
|
175
|
+
});
|
|
176
|
+
const files = await discoverOpenClawFiles(opts.openclawDir);
|
|
177
|
+
filesScanned.openclaw = files.length;
|
|
178
|
+
onProgress?.({
|
|
179
|
+
source: "openclaw",
|
|
180
|
+
phase: "parse",
|
|
181
|
+
total: files.length,
|
|
182
|
+
message: `Parsing ${files.length} OpenClaw files...`,
|
|
183
|
+
});
|
|
184
|
+
for (let i = 0; i < files.length; i++) {
|
|
185
|
+
const filePath = files[i];
|
|
186
|
+
const prev = cursors.files[filePath];
|
|
187
|
+
const st = await stat(filePath).catch(() => null);
|
|
188
|
+
if (!st)
|
|
189
|
+
continue;
|
|
190
|
+
const inode = st.ino;
|
|
191
|
+
const startOffset = prev && prev.inode === inode ? (prev.offset ?? 0) : 0;
|
|
192
|
+
const result = await parseOpenClawFile({ filePath, startOffset });
|
|
193
|
+
cursors.files[filePath] = {
|
|
194
|
+
inode,
|
|
195
|
+
offset: result.endOffset,
|
|
196
|
+
updatedAt: new Date().toISOString(),
|
|
197
|
+
};
|
|
198
|
+
allDeltas.push(...result.deltas);
|
|
199
|
+
sourceCounts.openclaw += result.deltas.length;
|
|
200
|
+
onProgress?.({
|
|
201
|
+
source: "openclaw",
|
|
202
|
+
phase: "parse",
|
|
203
|
+
current: i + 1,
|
|
204
|
+
total: files.length,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ---------- Aggregate into half-hour buckets ----------
|
|
209
|
+
onProgress?.({
|
|
210
|
+
source: "all",
|
|
211
|
+
phase: "aggregate",
|
|
212
|
+
message: `Aggregating ${allDeltas.length} deltas into buckets...`,
|
|
213
|
+
});
|
|
214
|
+
const buckets = new Map();
|
|
215
|
+
for (const delta of allDeltas) {
|
|
216
|
+
const hourStart = toUtcHalfHourStart(delta.timestamp);
|
|
217
|
+
if (!hourStart)
|
|
218
|
+
continue;
|
|
219
|
+
const key = bucketKey(delta.source, delta.model, hourStart);
|
|
220
|
+
let bucket = buckets.get(key);
|
|
221
|
+
if (!bucket) {
|
|
222
|
+
bucket = {
|
|
223
|
+
source: delta.source,
|
|
224
|
+
model: delta.model,
|
|
225
|
+
hourStart,
|
|
226
|
+
tokens: emptyTokenDelta(),
|
|
227
|
+
};
|
|
228
|
+
buckets.set(key, bucket);
|
|
229
|
+
}
|
|
230
|
+
addTokens(bucket.tokens, delta.tokens);
|
|
231
|
+
}
|
|
232
|
+
// ---------- Write to queue ----------
|
|
233
|
+
const records = [];
|
|
234
|
+
for (const bucket of buckets.values()) {
|
|
235
|
+
const totalTokens = bucket.tokens.inputTokens +
|
|
236
|
+
bucket.tokens.outputTokens +
|
|
237
|
+
bucket.tokens.reasoningOutputTokens;
|
|
238
|
+
records.push({
|
|
239
|
+
source: bucket.source,
|
|
240
|
+
model: bucket.model,
|
|
241
|
+
hour_start: bucket.hourStart,
|
|
242
|
+
input_tokens: bucket.tokens.inputTokens,
|
|
243
|
+
cached_input_tokens: bucket.tokens.cachedInputTokens,
|
|
244
|
+
output_tokens: bucket.tokens.outputTokens,
|
|
245
|
+
reasoning_output_tokens: bucket.tokens.reasoningOutputTokens,
|
|
246
|
+
total_tokens: totalTokens,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
if (records.length > 0) {
|
|
250
|
+
await queue.appendBatch(records);
|
|
251
|
+
}
|
|
252
|
+
// ---------- Save cursor state ----------
|
|
253
|
+
cursors.updatedAt = new Date().toISOString();
|
|
254
|
+
await cursorStore.save(cursors);
|
|
255
|
+
onProgress?.({
|
|
256
|
+
source: "all",
|
|
257
|
+
phase: "done",
|
|
258
|
+
message: `Synced ${allDeltas.length} events → ${records.length} records`,
|
|
259
|
+
});
|
|
260
|
+
return {
|
|
261
|
+
totalDeltas: allDeltas.length,
|
|
262
|
+
totalRecords: records.length,
|
|
263
|
+
sources: sourceCounts,
|
|
264
|
+
filesScanned,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI upload command — sends local queue records to the Pew SaaS.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Load API key from config
|
|
6
|
+
* 2. Read un-uploaded records from queue (using saved offset)
|
|
7
|
+
* 3. Split into batches of ≤50 (D1 Free plan: 50 queries/Worker invocation)
|
|
8
|
+
* 4. POST each batch to /api/ingest with Bearer token
|
|
9
|
+
* 5. Persist offset after each successful batch (for resume on failure)
|
|
10
|
+
* 6. Retry on 5xx with exponential backoff
|
|
11
|
+
*/
|
|
12
|
+
import type { QueueRecord } from "@pew/core";
|
|
13
|
+
export interface UploadOptions {
|
|
14
|
+
/** Directory for config file and queue state */
|
|
15
|
+
stateDir: string;
|
|
16
|
+
/** Base URL of the Pew SaaS */
|
|
17
|
+
apiUrl: string;
|
|
18
|
+
/** Whether dev mode is active (uses config.dev.json) */
|
|
19
|
+
dev?: boolean;
|
|
20
|
+
/** Injected fetch (for testing) */
|
|
21
|
+
fetch: typeof globalThis.fetch;
|
|
22
|
+
/** Max records per API request (default: 50) */
|
|
23
|
+
batchSize?: number;
|
|
24
|
+
/** Max retries per batch on 5xx (default: 2) */
|
|
25
|
+
maxRetries?: number;
|
|
26
|
+
/** Base retry delay in ms (default: 1000, doubled each retry) */
|
|
27
|
+
retryDelayMs?: number;
|
|
28
|
+
/** Progress callback */
|
|
29
|
+
onProgress?: (event: UploadProgressEvent) => void;
|
|
30
|
+
}
|
|
31
|
+
export interface UploadProgressEvent {
|
|
32
|
+
phase: "uploading" | "done";
|
|
33
|
+
batch?: number;
|
|
34
|
+
totalBatches?: number;
|
|
35
|
+
total?: number;
|
|
36
|
+
message?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface UploadResult {
|
|
39
|
+
success: boolean;
|
|
40
|
+
uploaded: number;
|
|
41
|
+
batches: number;
|
|
42
|
+
error?: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Aggregate QueueRecords by (source, model, hour_start), summing token fields.
|
|
46
|
+
*
|
|
47
|
+
* This ensures that when combined with server-side overwrite upsert, the
|
|
48
|
+
* pipeline is fully idempotent: re-scanning and re-uploading produces the
|
|
49
|
+
* same final result in D1.
|
|
50
|
+
*/
|
|
51
|
+
export declare function aggregateRecords(records: QueueRecord[]): QueueRecord[];
|
|
52
|
+
export declare function executeUpload(opts: UploadOptions): Promise<UploadResult>;
|
|
53
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAM7C,MAAM,WAAW,aAAa;IAC5B,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,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAED,MAAM,WAAW,mBAAmB;IAClC,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,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAcD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoBtE;AAMD,wBAAsB,aAAa,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAiG9E"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI upload command — sends local queue records to the Pew SaaS.
|
|
3
|
+
*
|
|
4
|
+
* Flow:
|
|
5
|
+
* 1. Load API key from config
|
|
6
|
+
* 2. Read un-uploaded records from queue (using saved offset)
|
|
7
|
+
* 3. Split into batches of ≤50 (D1 Free plan: 50 queries/Worker invocation)
|
|
8
|
+
* 4. POST each batch to /api/ingest with Bearer token
|
|
9
|
+
* 5. Persist offset after each successful batch (for resume on failure)
|
|
10
|
+
* 6. Retry on 5xx with exponential backoff
|
|
11
|
+
*/
|
|
12
|
+
import { ConfigManager } from "../config/manager.js";
|
|
13
|
+
import { LocalQueue } from "../storage/local-queue.js";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Constants
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const DEFAULT_BATCH_SIZE = 50;
|
|
18
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
19
|
+
const DEFAULT_RETRY_DELAY_MS = 1000;
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Pre-aggregation — merge QueueRecords with the same (source, model, hour_start)
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/**
|
|
24
|
+
* Aggregate QueueRecords by (source, model, hour_start), summing token fields.
|
|
25
|
+
*
|
|
26
|
+
* This ensures that when combined with server-side overwrite upsert, the
|
|
27
|
+
* pipeline is fully idempotent: re-scanning and re-uploading produces the
|
|
28
|
+
* same final result in D1.
|
|
29
|
+
*/
|
|
30
|
+
export function aggregateRecords(records) {
|
|
31
|
+
if (records.length === 0)
|
|
32
|
+
return [];
|
|
33
|
+
const map = new Map();
|
|
34
|
+
for (const r of records) {
|
|
35
|
+
const key = `${r.source}|${r.model}|${r.hour_start}`;
|
|
36
|
+
const existing = map.get(key);
|
|
37
|
+
if (existing) {
|
|
38
|
+
existing.input_tokens += r.input_tokens;
|
|
39
|
+
existing.cached_input_tokens += r.cached_input_tokens;
|
|
40
|
+
existing.output_tokens += r.output_tokens;
|
|
41
|
+
existing.reasoning_output_tokens += r.reasoning_output_tokens;
|
|
42
|
+
existing.total_tokens += r.total_tokens;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
map.set(key, { ...r });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return [...map.values()];
|
|
49
|
+
}
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Implementation
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
export async function executeUpload(opts) {
|
|
54
|
+
const { stateDir, apiUrl, dev = false, fetch: fetchFn, batchSize = DEFAULT_BATCH_SIZE, maxRetries = DEFAULT_MAX_RETRIES, retryDelayMs = DEFAULT_RETRY_DELAY_MS, onProgress, } = opts;
|
|
55
|
+
// 1. Load API key
|
|
56
|
+
const configManager = new ConfigManager(stateDir, dev);
|
|
57
|
+
const config = await configManager.load();
|
|
58
|
+
if (!config.token) {
|
|
59
|
+
return {
|
|
60
|
+
success: false,
|
|
61
|
+
uploaded: 0,
|
|
62
|
+
batches: 0,
|
|
63
|
+
error: "Not logged in. Run `pew login` first.",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// 2. Read un-uploaded records
|
|
67
|
+
const queue = new LocalQueue(stateDir);
|
|
68
|
+
const currentOffset = await queue.loadOffset();
|
|
69
|
+
const { records: rawRecords, newOffset } = await queue.readFromOffset(currentOffset);
|
|
70
|
+
if (rawRecords.length === 0) {
|
|
71
|
+
return { success: true, uploaded: 0, batches: 0 };
|
|
72
|
+
}
|
|
73
|
+
// 2b. Pre-aggregate by (source, model, hour_start) for idempotent upsert
|
|
74
|
+
const records = aggregateRecords(rawRecords);
|
|
75
|
+
// 3. Split into batches
|
|
76
|
+
const batches = [];
|
|
77
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
78
|
+
batches.push(records.slice(i, i + batchSize));
|
|
79
|
+
}
|
|
80
|
+
// 4. Upload each batch
|
|
81
|
+
const endpoint = `${apiUrl}/api/ingest`;
|
|
82
|
+
let totalUploaded = 0;
|
|
83
|
+
let batchesCompleted = 0;
|
|
84
|
+
for (let batchIdx = 0; batchIdx < batches.length; batchIdx++) {
|
|
85
|
+
const batch = batches[batchIdx];
|
|
86
|
+
onProgress?.({
|
|
87
|
+
phase: "uploading",
|
|
88
|
+
batch: batchIdx + 1,
|
|
89
|
+
totalBatches: batches.length,
|
|
90
|
+
total: records.length,
|
|
91
|
+
message: `Uploading batch ${batchIdx + 1}/${batches.length} (${batch.length} records)...`,
|
|
92
|
+
});
|
|
93
|
+
const result = await sendBatchWithRetry({
|
|
94
|
+
endpoint,
|
|
95
|
+
token: config.token,
|
|
96
|
+
batch,
|
|
97
|
+
fetchFn,
|
|
98
|
+
maxRetries,
|
|
99
|
+
retryDelayMs,
|
|
100
|
+
});
|
|
101
|
+
if (!result.ok) {
|
|
102
|
+
// With idempotent overwrite upsert, don't save partial offset.
|
|
103
|
+
// Next retry will re-aggregate and re-send everything safely.
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
uploaded: totalUploaded,
|
|
107
|
+
batches: batchesCompleted,
|
|
108
|
+
error: result.error,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
totalUploaded += batch.length;
|
|
112
|
+
batchesCompleted++;
|
|
113
|
+
}
|
|
114
|
+
// 5. All batches succeeded — save final offset
|
|
115
|
+
await queue.saveOffset(newOffset);
|
|
116
|
+
onProgress?.({
|
|
117
|
+
phase: "done",
|
|
118
|
+
total: totalUploaded,
|
|
119
|
+
message: `Uploaded ${totalUploaded} records in ${batchesCompleted} batch(es).`,
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
success: true,
|
|
123
|
+
uploaded: totalUploaded,
|
|
124
|
+
batches: batchesCompleted,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function sendBatchWithRetry(opts) {
|
|
128
|
+
const { endpoint, token, batch, fetchFn, maxRetries, retryDelayMs } = opts;
|
|
129
|
+
let lastError = "";
|
|
130
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
131
|
+
if (attempt > 0 && retryDelayMs > 0) {
|
|
132
|
+
await sleep(retryDelayMs * 2 ** (attempt - 1));
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const resp = await fetchFn(endpoint, {
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: {
|
|
138
|
+
"Content-Type": "application/json",
|
|
139
|
+
Authorization: `Bearer ${token}`,
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify(batch),
|
|
142
|
+
});
|
|
143
|
+
if (resp.ok) {
|
|
144
|
+
return { ok: true };
|
|
145
|
+
}
|
|
146
|
+
// 429 — rate limited, retry with Retry-After if available
|
|
147
|
+
if (resp.status === 429) {
|
|
148
|
+
const retryAfter = resp.headers.get("Retry-After");
|
|
149
|
+
const retryMs = retryAfter
|
|
150
|
+
? Math.max(Number(retryAfter) * 1000, retryDelayMs)
|
|
151
|
+
: retryDelayMs * 2 ** attempt;
|
|
152
|
+
if (attempt < maxRetries && retryMs > 0) {
|
|
153
|
+
await sleep(retryMs);
|
|
154
|
+
}
|
|
155
|
+
const body = await resp.json().catch(() => ({}));
|
|
156
|
+
lastError = `429: ${body.error ?? "Too Many Requests"}`;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// 4xx — client error, don't retry
|
|
160
|
+
if (resp.status >= 400 && resp.status < 500) {
|
|
161
|
+
const body = await resp.json().catch(() => ({}));
|
|
162
|
+
const msg = body.error ?? `HTTP ${resp.status}`;
|
|
163
|
+
return { ok: false, error: `${resp.status}: ${msg}` };
|
|
164
|
+
}
|
|
165
|
+
// 5xx — server error, retry
|
|
166
|
+
const body = await resp.json().catch(() => ({}));
|
|
167
|
+
lastError = `${resp.status}: ${body.error ?? "Server Error"}`;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
lastError = String(err.message ?? err);
|
|
171
|
+
// Network errors — don't retry if maxRetries is 0
|
|
172
|
+
if (attempt >= maxRetries) {
|
|
173
|
+
return { ok: false, error: lastError };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
ok: false,
|
|
179
|
+
error: `Upload failed after ${maxRetries + 1} attempts: ${lastError}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function sleep(ms) {
|
|
183
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PewConfig } from "@pew/core";
|
|
2
|
+
/**
|
|
3
|
+
* Manages the CLI configuration file.
|
|
4
|
+
* Stored at ~/.config/pew/config.json (prod) or config.dev.json (dev).
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConfigManager {
|
|
7
|
+
readonly configPath: string;
|
|
8
|
+
constructor(configDir: string, dev?: boolean);
|
|
9
|
+
/** Load config from disk. Returns empty config if file doesn't exist or is corrupted. */
|
|
10
|
+
load(): Promise<PewConfig>;
|
|
11
|
+
/** Save config to disk, creating the directory if needed. */
|
|
12
|
+
save(config: PewConfig): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/config/manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAK3C;;;GAGG;AACH,qBAAa,aAAa;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,SAAS,EAAE,MAAM,EAAE,GAAG,UAAQ;IAK1C,yFAAyF;IACnF,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC;IAShC,6DAA6D;IACvD,IAAI,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAK7C"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
const PROD_CONFIG = "config.json";
|
|
4
|
+
const DEV_CONFIG = "config.dev.json";
|
|
5
|
+
/**
|
|
6
|
+
* Manages the CLI configuration file.
|
|
7
|
+
* Stored at ~/.config/pew/config.json (prod) or config.dev.json (dev).
|
|
8
|
+
*/
|
|
9
|
+
export class ConfigManager {
|
|
10
|
+
configPath;
|
|
11
|
+
constructor(configDir, dev = false) {
|
|
12
|
+
const filename = dev ? DEV_CONFIG : PROD_CONFIG;
|
|
13
|
+
this.configPath = join(configDir, filename);
|
|
14
|
+
}
|
|
15
|
+
/** Load config from disk. Returns empty config if file doesn't exist or is corrupted. */
|
|
16
|
+
async load() {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readFile(this.configPath, "utf-8");
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** Save config to disk, creating the directory if needed. */
|
|
26
|
+
async save(config) {
|
|
27
|
+
const dir = dirname(this.configPath);
|
|
28
|
+
await mkdir(dir, { recursive: true });
|
|
29
|
+
await writeFile(this.configPath, JSON.stringify(config, null, 2) + "\n");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/config/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,WAAW,GAAG,aAAa,CAAC;AAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC;AAErC;;;GAGG;AACH,MAAM,OAAO,aAAa;IACf,UAAU,CAAS;IAE5B,YAAY,SAAiB,EAAE,GAAG,GAAG,KAAK;QACxC,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC;QAChD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,yFAAyF;IACzF,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,IAAI,CAAC,MAAiB;QAC1B,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3E,CAAC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discover Claude Code JSONL files.
|
|
3
|
+
* Path pattern: ~/.claude/projects/\*\*\/*.jsonl
|
|
4
|
+
*/
|
|
5
|
+
export declare function discoverClaudeFiles(claudeDir: string): Promise<string[]>;
|
|
6
|
+
/**
|
|
7
|
+
* Discover Gemini CLI session files.
|
|
8
|
+
* Path pattern: ~/.gemini/tmp/\*\/chats/session-*.json
|
|
9
|
+
*/
|
|
10
|
+
export declare function discoverGeminiFiles(geminiDir: string): Promise<string[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Result of OpenCode discovery with directory-level mtime tracking.
|
|
13
|
+
*/
|
|
14
|
+
export interface OpenCodeDiscoveryResult {
|
|
15
|
+
/** Files in changed directories (only these need parsing) */
|
|
16
|
+
files: string[];
|
|
17
|
+
/** Updated directory mtimes (all directories, for persisting) */
|
|
18
|
+
dirMtimes: Record<string, number>;
|
|
19
|
+
/** Number of directories skipped due to unchanged mtime */
|
|
20
|
+
skippedDirs: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Discover OpenCode message files with directory-level mtime optimization.
|
|
24
|
+
*
|
|
25
|
+
* Instead of stat()-ing all 66K+ message files, we stat() only the ~3K
|
|
26
|
+
* session directories. If a directory's mtime hasn't changed since last
|
|
27
|
+
* sync, we skip the entire directory (no readdir, no file stat).
|
|
28
|
+
*
|
|
29
|
+
* Path pattern: ~/.local/share/opencode/storage/message/ses_*\/msg_*.json
|
|
30
|
+
*/
|
|
31
|
+
export declare function discoverOpenCodeFiles(messageDir: string, knownDirMtimes?: Record<string, number>): Promise<OpenCodeDiscoveryResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Discover OpenClaw session files.
|
|
34
|
+
* Path pattern: ~/.openclaw/agents/\*\/sessions/*.jsonl
|
|
35
|
+
*/
|
|
36
|
+
export declare function discoverOpenClawFiles(openclawDir: string): Promise<string[]>;
|
|
37
|
+
//# sourceMappingURL=sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../src/discovery/sources.ts"],"names":[],"mappings":"AAoCA;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACtC,OAAO,CAAC,uBAAuB,CAAC,CAiDlC;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,EAAE,CAAC,CAGnB"}
|