vibeusage 0.2.20 → 0.2.22
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/README.md +306 -173
- package/README.old.md +324 -0
- package/README.zh-CN.md +304 -188
- package/package.json +32 -30
- package/src/cli.js +41 -37
- package/src/commands/activate-if-needed.js +41 -0
- package/src/commands/diagnostics.js +8 -9
- package/src/commands/doctor.js +31 -26
- package/src/commands/init.js +324 -208
- package/src/commands/status.js +86 -80
- package/src/commands/sync.js +182 -130
- package/src/commands/uninstall.js +69 -58
- package/src/lib/activation-check.js +290 -0
- package/src/lib/browser-auth.js +52 -54
- package/src/lib/claude-config.js +25 -25
- package/src/lib/cli-ui.js +35 -35
- package/src/lib/codex-config.js +40 -36
- package/src/lib/debug-flags.js +2 -2
- package/src/lib/diagnostics.js +73 -55
- package/src/lib/doctor.js +139 -132
- package/src/lib/fs.js +17 -17
- package/src/lib/gemini-config.js +44 -40
- package/src/lib/init-flow.js +16 -22
- package/src/lib/insforge-client.js +10 -10
- package/src/lib/insforge.js +9 -3
- package/src/lib/openclaw-hook.js +91 -67
- package/src/lib/openclaw-session-plugin.js +520 -0
- package/src/lib/opencode-config.js +31 -32
- package/src/lib/opencode-usage-audit.js +34 -31
- package/src/lib/progress.js +12 -13
- package/src/lib/project-usage-purge.js +23 -17
- package/src/lib/prompt.js +8 -4
- package/src/lib/rollout.js +342 -241
- package/src/lib/runtime-config.js +34 -22
- package/src/lib/subscriptions.js +94 -92
- package/src/lib/tracker-paths.js +6 -6
- package/src/lib/upload-throttle.js +35 -16
- package/src/lib/uploader.js +33 -29
- package/src/lib/vibeusage-api.js +72 -56
- package/src/lib/vibeusage-public-repo.js +41 -24
package/src/lib/rollout.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const fssync = require(
|
|
3
|
-
const path = require(
|
|
4
|
-
const readline = require(
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
const fssync = require("node:fs");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const readline = require("node:readline");
|
|
5
5
|
|
|
6
|
-
const { ensureDir } = require(
|
|
7
|
-
const { hashRepoRoot, resolveGitHubPublicStatus } = require(
|
|
6
|
+
const { ensureDir } = require("./fs");
|
|
7
|
+
const { hashRepoRoot, resolveGitHubPublicStatus } = require("./vibeusage-public-repo");
|
|
8
8
|
|
|
9
|
-
const DEFAULT_SOURCE =
|
|
10
|
-
const DEFAULT_MODEL =
|
|
11
|
-
const BUCKET_SEPARATOR =
|
|
9
|
+
const DEFAULT_SOURCE = "codex";
|
|
10
|
+
const DEFAULT_MODEL = "unknown";
|
|
11
|
+
const BUCKET_SEPARATOR = "|";
|
|
12
12
|
|
|
13
13
|
async function listRolloutFiles(sessionsDir) {
|
|
14
14
|
const out = [];
|
|
@@ -27,7 +27,7 @@ async function listRolloutFiles(sessionsDir) {
|
|
|
27
27
|
const files = await safeReadDir(dayDir);
|
|
28
28
|
for (const f of files) {
|
|
29
29
|
if (!f.isFile()) continue;
|
|
30
|
-
if (!f.name.startsWith(
|
|
30
|
+
if (!f.name.startsWith("rollout-") || !f.name.endsWith(".jsonl")) continue;
|
|
31
31
|
out.push(path.join(dayDir, f.name));
|
|
32
32
|
}
|
|
33
33
|
}
|
|
@@ -50,11 +50,11 @@ async function listGeminiSessionFiles(tmpDir) {
|
|
|
50
50
|
const roots = await safeReadDir(tmpDir);
|
|
51
51
|
for (const root of roots) {
|
|
52
52
|
if (!root.isDirectory()) continue;
|
|
53
|
-
const chatsDir = path.join(tmpDir, root.name,
|
|
53
|
+
const chatsDir = path.join(tmpDir, root.name, "chats");
|
|
54
54
|
const chats = await safeReadDir(chatsDir);
|
|
55
55
|
for (const entry of chats) {
|
|
56
56
|
if (!entry.isFile()) continue;
|
|
57
|
-
if (!entry.name.startsWith(
|
|
57
|
+
if (!entry.name.startsWith("session-") || !entry.name.endsWith(".json")) continue;
|
|
58
58
|
out.push(path.join(chatsDir, entry.name));
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -64,7 +64,7 @@ async function listGeminiSessionFiles(tmpDir) {
|
|
|
64
64
|
|
|
65
65
|
async function listOpencodeMessageFiles(storageDir) {
|
|
66
66
|
const out = [];
|
|
67
|
-
const messageDir = path.join(storageDir,
|
|
67
|
+
const messageDir = path.join(storageDir, "message");
|
|
68
68
|
await walkOpencodeMessages(messageDir, out);
|
|
69
69
|
out.sort((a, b) => a.localeCompare(b));
|
|
70
70
|
return out;
|
|
@@ -77,16 +77,16 @@ async function parseRolloutIncremental({
|
|
|
77
77
|
projectQueuePath,
|
|
78
78
|
onProgress,
|
|
79
79
|
source,
|
|
80
|
-
publicRepoResolver
|
|
80
|
+
publicRepoResolver,
|
|
81
81
|
}) {
|
|
82
82
|
await ensureDir(path.dirname(queuePath));
|
|
83
83
|
let filesProcessed = 0;
|
|
84
84
|
let eventsAggregated = 0;
|
|
85
85
|
|
|
86
|
-
const cb = typeof onProgress ===
|
|
86
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
87
87
|
const totalFiles = Array.isArray(rolloutFiles) ? rolloutFiles.length : 0;
|
|
88
88
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
89
|
-
const projectEnabled = typeof projectQueuePath ===
|
|
89
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
90
90
|
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
91
91
|
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
92
92
|
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
@@ -94,16 +94,18 @@ async function parseRolloutIncremental({
|
|
|
94
94
|
const touchedBuckets = new Set();
|
|
95
95
|
const defaultSource = normalizeSourceInput(source) || DEFAULT_SOURCE;
|
|
96
96
|
|
|
97
|
-
if (!cursors.files || typeof cursors.files !==
|
|
97
|
+
if (!cursors.files || typeof cursors.files !== "object") {
|
|
98
98
|
cursors.files = {};
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
for (let idx = 0; idx < rolloutFiles.length; idx++) {
|
|
102
102
|
const entry = rolloutFiles[idx];
|
|
103
|
-
const filePath = typeof entry ===
|
|
103
|
+
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
104
104
|
if (!filePath) continue;
|
|
105
105
|
const fileSource =
|
|
106
|
-
typeof entry ===
|
|
106
|
+
typeof entry === "string"
|
|
107
|
+
? defaultSource
|
|
108
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
107
109
|
const st = await fs.stat(filePath).catch(() => null);
|
|
108
110
|
if (!st || !st.isFile()) continue;
|
|
109
111
|
|
|
@@ -120,7 +122,7 @@ async function parseRolloutIncremental({
|
|
|
120
122
|
projectMetaCache,
|
|
121
123
|
publicRepoCache,
|
|
122
124
|
publicRepoResolver,
|
|
123
|
-
projectState
|
|
125
|
+
projectState,
|
|
124
126
|
})
|
|
125
127
|
: null;
|
|
126
128
|
const projectRef = projectContext?.projectRef || null;
|
|
@@ -140,7 +142,7 @@ async function parseRolloutIncremental({
|
|
|
140
142
|
projectKey,
|
|
141
143
|
projectMetaCache,
|
|
142
144
|
publicRepoCache,
|
|
143
|
-
publicRepoResolver
|
|
145
|
+
publicRepoResolver,
|
|
144
146
|
});
|
|
145
147
|
|
|
146
148
|
cursors.files[key] = {
|
|
@@ -148,7 +150,7 @@ async function parseRolloutIncremental({
|
|
|
148
150
|
offset: result.endOffset,
|
|
149
151
|
lastTotal: result.lastTotal,
|
|
150
152
|
lastModel: result.lastModel,
|
|
151
|
-
updatedAt: new Date().toISOString()
|
|
153
|
+
updatedAt: new Date().toISOString(),
|
|
152
154
|
};
|
|
153
155
|
|
|
154
156
|
filesProcessed += 1;
|
|
@@ -161,7 +163,7 @@ async function parseRolloutIncremental({
|
|
|
161
163
|
filePath,
|
|
162
164
|
filesProcessed,
|
|
163
165
|
eventsAggregated,
|
|
164
|
-
bucketsQueued: touchedBuckets.size
|
|
166
|
+
bucketsQueued: touchedBuckets.size,
|
|
165
167
|
});
|
|
166
168
|
}
|
|
167
169
|
}
|
|
@@ -187,34 +189,36 @@ async function parseClaudeIncremental({
|
|
|
187
189
|
projectQueuePath,
|
|
188
190
|
onProgress,
|
|
189
191
|
source,
|
|
190
|
-
publicRepoResolver
|
|
192
|
+
publicRepoResolver,
|
|
191
193
|
}) {
|
|
192
194
|
await ensureDir(path.dirname(queuePath));
|
|
193
195
|
let filesProcessed = 0;
|
|
194
196
|
let eventsAggregated = 0;
|
|
195
197
|
|
|
196
|
-
const cb = typeof onProgress ===
|
|
198
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
197
199
|
const files = Array.isArray(projectFiles) ? projectFiles : [];
|
|
198
200
|
const totalFiles = files.length;
|
|
199
201
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
200
|
-
const projectEnabled = typeof projectQueuePath ===
|
|
202
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
201
203
|
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
202
204
|
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
203
205
|
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
204
206
|
const publicRepoCache = projectEnabled ? new Map() : null;
|
|
205
207
|
const touchedBuckets = new Set();
|
|
206
|
-
const defaultSource = normalizeSourceInput(source) ||
|
|
208
|
+
const defaultSource = normalizeSourceInput(source) || "claude";
|
|
207
209
|
|
|
208
|
-
if (!cursors.files || typeof cursors.files !==
|
|
210
|
+
if (!cursors.files || typeof cursors.files !== "object") {
|
|
209
211
|
cursors.files = {};
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
for (let idx = 0; idx < files.length; idx++) {
|
|
213
215
|
const entry = files[idx];
|
|
214
|
-
const filePath = typeof entry ===
|
|
216
|
+
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
215
217
|
if (!filePath) continue;
|
|
216
218
|
const fileSource =
|
|
217
|
-
typeof entry ===
|
|
219
|
+
typeof entry === "string"
|
|
220
|
+
? defaultSource
|
|
221
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
218
222
|
const st = await fs.stat(filePath).catch(() => null);
|
|
219
223
|
if (!st || !st.isFile()) continue;
|
|
220
224
|
|
|
@@ -229,7 +233,7 @@ async function parseClaudeIncremental({
|
|
|
229
233
|
projectMetaCache,
|
|
230
234
|
publicRepoCache,
|
|
231
235
|
publicRepoResolver,
|
|
232
|
-
projectState
|
|
236
|
+
projectState,
|
|
233
237
|
})
|
|
234
238
|
: null;
|
|
235
239
|
const projectRef = projectContext?.projectRef || null;
|
|
@@ -244,13 +248,13 @@ async function parseClaudeIncremental({
|
|
|
244
248
|
projectState,
|
|
245
249
|
projectTouchedBuckets,
|
|
246
250
|
projectRef,
|
|
247
|
-
projectKey
|
|
251
|
+
projectKey,
|
|
248
252
|
});
|
|
249
253
|
|
|
250
254
|
cursors.files[key] = {
|
|
251
255
|
inode,
|
|
252
256
|
offset: result.endOffset,
|
|
253
|
-
updatedAt: new Date().toISOString()
|
|
257
|
+
updatedAt: new Date().toISOString(),
|
|
254
258
|
};
|
|
255
259
|
|
|
256
260
|
filesProcessed += 1;
|
|
@@ -263,7 +267,7 @@ async function parseClaudeIncremental({
|
|
|
263
267
|
filePath,
|
|
264
268
|
filesProcessed,
|
|
265
269
|
eventsAggregated,
|
|
266
|
-
bucketsQueued: touchedBuckets.size
|
|
270
|
+
bucketsQueued: touchedBuckets.size,
|
|
267
271
|
});
|
|
268
272
|
}
|
|
269
273
|
}
|
|
@@ -289,34 +293,36 @@ async function parseGeminiIncremental({
|
|
|
289
293
|
projectQueuePath,
|
|
290
294
|
onProgress,
|
|
291
295
|
source,
|
|
292
|
-
publicRepoResolver
|
|
296
|
+
publicRepoResolver,
|
|
293
297
|
}) {
|
|
294
298
|
await ensureDir(path.dirname(queuePath));
|
|
295
299
|
let filesProcessed = 0;
|
|
296
300
|
let eventsAggregated = 0;
|
|
297
301
|
|
|
298
|
-
const cb = typeof onProgress ===
|
|
302
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
299
303
|
const files = Array.isArray(sessionFiles) ? sessionFiles : [];
|
|
300
304
|
const totalFiles = files.length;
|
|
301
305
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
302
|
-
const projectEnabled = typeof projectQueuePath ===
|
|
306
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
303
307
|
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
304
308
|
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
305
309
|
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
306
310
|
const publicRepoCache = projectEnabled ? new Map() : null;
|
|
307
311
|
const touchedBuckets = new Set();
|
|
308
|
-
const defaultSource = normalizeSourceInput(source) ||
|
|
312
|
+
const defaultSource = normalizeSourceInput(source) || "gemini";
|
|
309
313
|
|
|
310
|
-
if (!cursors.files || typeof cursors.files !==
|
|
314
|
+
if (!cursors.files || typeof cursors.files !== "object") {
|
|
311
315
|
cursors.files = {};
|
|
312
316
|
}
|
|
313
317
|
|
|
314
318
|
for (let idx = 0; idx < files.length; idx++) {
|
|
315
319
|
const entry = files[idx];
|
|
316
|
-
const filePath = typeof entry ===
|
|
320
|
+
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
317
321
|
if (!filePath) continue;
|
|
318
322
|
const fileSource =
|
|
319
|
-
typeof entry ===
|
|
323
|
+
typeof entry === "string"
|
|
324
|
+
? defaultSource
|
|
325
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
320
326
|
const st = await fs.stat(filePath).catch(() => null);
|
|
321
327
|
if (!st || !st.isFile()) continue;
|
|
322
328
|
|
|
@@ -333,7 +339,7 @@ async function parseGeminiIncremental({
|
|
|
333
339
|
projectMetaCache,
|
|
334
340
|
publicRepoCache,
|
|
335
341
|
publicRepoResolver,
|
|
336
|
-
projectState
|
|
342
|
+
projectState,
|
|
337
343
|
})
|
|
338
344
|
: null;
|
|
339
345
|
const projectRef = projectContext?.projectRef || null;
|
|
@@ -350,7 +356,7 @@ async function parseGeminiIncremental({
|
|
|
350
356
|
projectState,
|
|
351
357
|
projectTouchedBuckets,
|
|
352
358
|
projectRef,
|
|
353
|
-
projectKey
|
|
359
|
+
projectKey,
|
|
354
360
|
});
|
|
355
361
|
|
|
356
362
|
cursors.files[key] = {
|
|
@@ -358,7 +364,7 @@ async function parseGeminiIncremental({
|
|
|
358
364
|
lastIndex: result.lastIndex,
|
|
359
365
|
lastTotals: result.lastTotals,
|
|
360
366
|
lastModel: result.lastModel,
|
|
361
|
-
updatedAt: new Date().toISOString()
|
|
367
|
+
updatedAt: new Date().toISOString(),
|
|
362
368
|
};
|
|
363
369
|
|
|
364
370
|
filesProcessed += 1;
|
|
@@ -371,7 +377,7 @@ async function parseGeminiIncremental({
|
|
|
371
377
|
filePath,
|
|
372
378
|
filesProcessed,
|
|
373
379
|
eventsAggregated,
|
|
374
|
-
bucketsQueued: touchedBuckets.size
|
|
380
|
+
bucketsQueued: touchedBuckets.size,
|
|
375
381
|
});
|
|
376
382
|
}
|
|
377
383
|
}
|
|
@@ -397,17 +403,17 @@ async function parseOpencodeIncremental({
|
|
|
397
403
|
projectQueuePath,
|
|
398
404
|
onProgress,
|
|
399
405
|
source,
|
|
400
|
-
publicRepoResolver
|
|
406
|
+
publicRepoResolver,
|
|
401
407
|
}) {
|
|
402
408
|
await ensureDir(path.dirname(queuePath));
|
|
403
409
|
let filesProcessed = 0;
|
|
404
410
|
let eventsAggregated = 0;
|
|
405
411
|
|
|
406
|
-
const cb = typeof onProgress ===
|
|
412
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
407
413
|
const files = Array.isArray(messageFiles) ? messageFiles : [];
|
|
408
414
|
const totalFiles = files.length;
|
|
409
415
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
410
|
-
const projectEnabled = typeof projectQueuePath ===
|
|
416
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
411
417
|
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
412
418
|
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
413
419
|
const projectMetaCache = projectEnabled ? new Map() : null;
|
|
@@ -415,18 +421,20 @@ async function parseOpencodeIncremental({
|
|
|
415
421
|
const opencodeState = normalizeOpencodeState(cursors?.opencode);
|
|
416
422
|
const messageIndex = opencodeState.messages;
|
|
417
423
|
const touchedBuckets = new Set();
|
|
418
|
-
const defaultSource = normalizeSourceInput(source) ||
|
|
424
|
+
const defaultSource = normalizeSourceInput(source) || "opencode";
|
|
419
425
|
|
|
420
|
-
if (!cursors.files || typeof cursors.files !==
|
|
426
|
+
if (!cursors.files || typeof cursors.files !== "object") {
|
|
421
427
|
cursors.files = {};
|
|
422
428
|
}
|
|
423
429
|
|
|
424
430
|
for (let idx = 0; idx < files.length; idx++) {
|
|
425
431
|
const entry = files[idx];
|
|
426
|
-
const filePath = typeof entry ===
|
|
432
|
+
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
427
433
|
if (!filePath) continue;
|
|
428
434
|
const fileSource =
|
|
429
|
-
typeof entry ===
|
|
435
|
+
typeof entry === "string"
|
|
436
|
+
? defaultSource
|
|
437
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
430
438
|
const st = await fs.stat(filePath).catch(() => null);
|
|
431
439
|
if (!st || !st.isFile()) continue;
|
|
432
440
|
|
|
@@ -435,7 +443,8 @@ async function parseOpencodeIncremental({
|
|
|
435
443
|
const inode = st.ino || 0;
|
|
436
444
|
const size = Number.isFinite(st.size) ? st.size : 0;
|
|
437
445
|
const mtimeMs = Number.isFinite(st.mtimeMs) ? st.mtimeMs : 0;
|
|
438
|
-
const unchanged =
|
|
446
|
+
const unchanged =
|
|
447
|
+
prev && prev.inode === inode && prev.size === size && prev.mtimeMs === mtimeMs;
|
|
439
448
|
if (unchanged) {
|
|
440
449
|
filesProcessed += 1;
|
|
441
450
|
if (cb) {
|
|
@@ -445,22 +454,24 @@ async function parseOpencodeIncremental({
|
|
|
445
454
|
filePath,
|
|
446
455
|
filesProcessed,
|
|
447
456
|
eventsAggregated,
|
|
448
|
-
bucketsQueued: touchedBuckets.size
|
|
457
|
+
bucketsQueued: touchedBuckets.size,
|
|
449
458
|
});
|
|
450
459
|
}
|
|
451
460
|
continue;
|
|
452
461
|
}
|
|
453
462
|
|
|
454
|
-
const fallbackTotals = prev && typeof prev.lastTotals ===
|
|
463
|
+
const fallbackTotals = prev && typeof prev.lastTotals === "object" ? prev.lastTotals : null;
|
|
455
464
|
const fallbackMessageKey =
|
|
456
|
-
prev && typeof prev.messageKey ===
|
|
465
|
+
prev && typeof prev.messageKey === "string" && prev.messageKey.trim()
|
|
466
|
+
? prev.messageKey.trim()
|
|
467
|
+
: null;
|
|
457
468
|
const projectContext = projectEnabled
|
|
458
469
|
? await resolveProjectContextForFile({
|
|
459
470
|
filePath,
|
|
460
471
|
projectMetaCache,
|
|
461
472
|
publicRepoCache,
|
|
462
473
|
publicRepoResolver,
|
|
463
|
-
projectState
|
|
474
|
+
projectState,
|
|
464
475
|
})
|
|
465
476
|
: null;
|
|
466
477
|
const projectRef = projectContext?.projectRef || null;
|
|
@@ -477,7 +488,7 @@ async function parseOpencodeIncremental({
|
|
|
477
488
|
projectState,
|
|
478
489
|
projectTouchedBuckets,
|
|
479
490
|
projectRef,
|
|
480
|
-
projectKey
|
|
491
|
+
projectKey,
|
|
481
492
|
});
|
|
482
493
|
|
|
483
494
|
cursors.files[key] = {
|
|
@@ -486,7 +497,7 @@ async function parseOpencodeIncremental({
|
|
|
486
497
|
mtimeMs,
|
|
487
498
|
lastTotals: result.lastTotals,
|
|
488
499
|
messageKey: result.messageKey || null,
|
|
489
|
-
updatedAt: new Date().toISOString()
|
|
500
|
+
updatedAt: new Date().toISOString(),
|
|
490
501
|
};
|
|
491
502
|
|
|
492
503
|
filesProcessed += 1;
|
|
@@ -495,7 +506,7 @@ async function parseOpencodeIncremental({
|
|
|
495
506
|
if (result.messageKey && result.shouldUpdate) {
|
|
496
507
|
messageIndex[result.messageKey] = {
|
|
497
508
|
lastTotals: result.lastTotals,
|
|
498
|
-
updatedAt: new Date().toISOString()
|
|
509
|
+
updatedAt: new Date().toISOString(),
|
|
499
510
|
};
|
|
500
511
|
}
|
|
501
512
|
|
|
@@ -506,7 +517,7 @@ async function parseOpencodeIncremental({
|
|
|
506
517
|
filePath,
|
|
507
518
|
filesProcessed,
|
|
508
519
|
eventsAggregated,
|
|
509
|
-
bucketsQueued: touchedBuckets.size
|
|
520
|
+
bucketsQueued: touchedBuckets.size,
|
|
510
521
|
});
|
|
511
522
|
}
|
|
512
523
|
}
|
|
@@ -533,32 +544,34 @@ async function parseOpenclawIncremental({
|
|
|
533
544
|
queuePath,
|
|
534
545
|
projectQueuePath,
|
|
535
546
|
onProgress,
|
|
536
|
-
source
|
|
547
|
+
source,
|
|
537
548
|
}) {
|
|
538
549
|
await ensureDir(path.dirname(queuePath));
|
|
539
550
|
let filesProcessed = 0;
|
|
540
551
|
let eventsAggregated = 0;
|
|
541
552
|
|
|
542
|
-
const cb = typeof onProgress ===
|
|
553
|
+
const cb = typeof onProgress === "function" ? onProgress : null;
|
|
543
554
|
const files = Array.isArray(sessionFiles) ? sessionFiles : [];
|
|
544
555
|
const totalFiles = files.length;
|
|
545
556
|
const hourlyState = normalizeHourlyState(cursors?.hourly);
|
|
546
|
-
const projectEnabled = typeof projectQueuePath ===
|
|
557
|
+
const projectEnabled = typeof projectQueuePath === "string" && projectQueuePath.length > 0;
|
|
547
558
|
const projectState = projectEnabled ? normalizeProjectState(cursors?.projectHourly) : null;
|
|
548
559
|
const projectTouchedBuckets = projectEnabled ? new Set() : null;
|
|
549
560
|
const touchedBuckets = new Set();
|
|
550
|
-
const defaultSource = normalizeSourceInput(source) ||
|
|
561
|
+
const defaultSource = normalizeSourceInput(source) || "openclaw";
|
|
551
562
|
|
|
552
|
-
if (!cursors.files || typeof cursors.files !==
|
|
563
|
+
if (!cursors.files || typeof cursors.files !== "object") {
|
|
553
564
|
cursors.files = {};
|
|
554
565
|
}
|
|
555
566
|
|
|
556
567
|
for (let idx = 0; idx < files.length; idx++) {
|
|
557
568
|
const entry = files[idx];
|
|
558
|
-
const filePath = typeof entry ===
|
|
569
|
+
const filePath = typeof entry === "string" ? entry : entry?.path;
|
|
559
570
|
if (!filePath) continue;
|
|
560
571
|
const fileSource =
|
|
561
|
-
typeof entry ===
|
|
572
|
+
typeof entry === "string"
|
|
573
|
+
? defaultSource
|
|
574
|
+
: normalizeSourceInput(entry?.source) || defaultSource;
|
|
562
575
|
const st = await fs.stat(filePath).catch(() => null);
|
|
563
576
|
if (!st || !st.isFile()) continue;
|
|
564
577
|
|
|
@@ -574,13 +587,13 @@ async function parseOpenclawIncremental({
|
|
|
574
587
|
touchedBuckets,
|
|
575
588
|
source: fileSource,
|
|
576
589
|
projectState,
|
|
577
|
-
projectTouchedBuckets
|
|
590
|
+
projectTouchedBuckets,
|
|
578
591
|
});
|
|
579
592
|
|
|
580
593
|
cursors.files[key] = {
|
|
581
594
|
inode,
|
|
582
595
|
offset: result.endOffset,
|
|
583
|
-
updatedAt: new Date().toISOString()
|
|
596
|
+
updatedAt: new Date().toISOString(),
|
|
584
597
|
};
|
|
585
598
|
|
|
586
599
|
filesProcessed += 1;
|
|
@@ -593,7 +606,7 @@ async function parseOpenclawIncremental({
|
|
|
593
606
|
filePath,
|
|
594
607
|
filesProcessed,
|
|
595
608
|
eventsAggregated,
|
|
596
|
-
bucketsQueued: touchedBuckets.size
|
|
609
|
+
bucketsQueued: touchedBuckets.size,
|
|
597
610
|
});
|
|
598
611
|
}
|
|
599
612
|
}
|
|
@@ -619,20 +632,20 @@ async function parseOpenclawSessionFile({
|
|
|
619
632
|
touchedBuckets,
|
|
620
633
|
source,
|
|
621
634
|
projectState,
|
|
622
|
-
projectTouchedBuckets
|
|
635
|
+
projectTouchedBuckets,
|
|
623
636
|
}) {
|
|
624
637
|
const st = await fs.stat(filePath);
|
|
625
638
|
const endOffset = st.size;
|
|
626
639
|
if (startOffset >= endOffset) return { endOffset, eventsAggregated: 0 };
|
|
627
640
|
|
|
628
|
-
const stream = fssync.createReadStream(filePath, { encoding:
|
|
641
|
+
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
|
|
629
642
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
630
643
|
|
|
631
644
|
let eventsAggregated = 0;
|
|
632
645
|
for await (const line of rl) {
|
|
633
646
|
if (!line) continue;
|
|
634
647
|
// Fast-path filter: OpenClaw assistant messages include message.usage.totalTokens.
|
|
635
|
-
if (!line.includes('"usage"') || !line.includes(
|
|
648
|
+
if (!line.includes('"usage"') || !line.includes("totalTokens")) continue;
|
|
636
649
|
|
|
637
650
|
let obj;
|
|
638
651
|
try {
|
|
@@ -641,14 +654,14 @@ async function parseOpenclawSessionFile({
|
|
|
641
654
|
continue;
|
|
642
655
|
}
|
|
643
656
|
|
|
644
|
-
if (obj?.type !==
|
|
657
|
+
if (obj?.type !== "message") continue;
|
|
645
658
|
const msg = obj?.message;
|
|
646
|
-
if (!msg || typeof msg !==
|
|
659
|
+
if (!msg || typeof msg !== "object") continue;
|
|
647
660
|
|
|
648
661
|
const usage = msg.usage;
|
|
649
|
-
if (!usage || typeof usage !==
|
|
662
|
+
if (!usage || typeof usage !== "object") continue;
|
|
650
663
|
|
|
651
|
-
const tokenTimestamp = typeof obj?.timestamp ===
|
|
664
|
+
const tokenTimestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
|
|
652
665
|
if (!tokenTimestamp) continue;
|
|
653
666
|
|
|
654
667
|
const model = normalizeModelInput(msg.model) || DEFAULT_MODEL;
|
|
@@ -658,7 +671,7 @@ async function parseOpenclawSessionFile({
|
|
|
658
671
|
cached_input_tokens: Number((usage.cacheRead || 0) + (usage.cacheWrite || 0)),
|
|
659
672
|
output_tokens: Number(usage.output || 0),
|
|
660
673
|
reasoning_output_tokens: 0,
|
|
661
|
-
total_tokens: Number(usage.totalTokens || 0)
|
|
674
|
+
total_tokens: Number(usage.totalTokens || 0),
|
|
662
675
|
};
|
|
663
676
|
|
|
664
677
|
if (isAllZeroUsage(delta)) continue;
|
|
@@ -694,7 +707,7 @@ async function parseRolloutFile({
|
|
|
694
707
|
projectKey,
|
|
695
708
|
projectMetaCache,
|
|
696
709
|
publicRepoCache,
|
|
697
|
-
publicRepoResolver
|
|
710
|
+
publicRepoResolver,
|
|
698
711
|
}) {
|
|
699
712
|
const st = await fs.stat(filePath);
|
|
700
713
|
const endOffset = st.size;
|
|
@@ -702,11 +715,11 @@ async function parseRolloutFile({
|
|
|
702
715
|
return { endOffset, lastTotal, lastModel, eventsAggregated: 0 };
|
|
703
716
|
}
|
|
704
717
|
|
|
705
|
-
const stream = fssync.createReadStream(filePath, { encoding:
|
|
718
|
+
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
|
|
706
719
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
707
720
|
|
|
708
|
-
let model = typeof lastModel ===
|
|
709
|
-
let totals = lastTotal && typeof lastTotal ===
|
|
721
|
+
let model = typeof lastModel === "string" ? lastModel : null;
|
|
722
|
+
let totals = lastTotal && typeof lastTotal === "object" ? lastTotal : null;
|
|
710
723
|
let currentCwd = null;
|
|
711
724
|
let currentProjectRef = projectRef || null;
|
|
712
725
|
let currentProjectKey = projectKey || null;
|
|
@@ -729,14 +742,14 @@ async function parseRolloutFile({
|
|
|
729
742
|
}
|
|
730
743
|
|
|
731
744
|
if (
|
|
732
|
-
(obj?.type ===
|
|
745
|
+
(obj?.type === "turn_context" || obj?.type === "session_meta") &&
|
|
733
746
|
obj?.payload &&
|
|
734
|
-
typeof obj.payload ===
|
|
747
|
+
typeof obj.payload === "object"
|
|
735
748
|
) {
|
|
736
|
-
if (typeof obj.payload.model ===
|
|
749
|
+
if (typeof obj.payload.model === "string") {
|
|
737
750
|
model = obj.payload.model;
|
|
738
751
|
}
|
|
739
|
-
if (projectState && typeof obj.payload.cwd ===
|
|
752
|
+
if (projectState && typeof obj.payload.cwd === "string") {
|
|
740
753
|
const nextCwd = obj.payload.cwd.trim();
|
|
741
754
|
if (nextCwd && nextCwd !== currentCwd) {
|
|
742
755
|
const context = await resolveProjectContextForPath({
|
|
@@ -744,7 +757,7 @@ async function parseRolloutFile({
|
|
|
744
757
|
projectMetaCache,
|
|
745
758
|
publicRepoCache,
|
|
746
759
|
publicRepoResolver,
|
|
747
|
-
projectState
|
|
760
|
+
projectState,
|
|
748
761
|
});
|
|
749
762
|
currentCwd = nextCwd;
|
|
750
763
|
currentProjectRef = context?.projectRef || null;
|
|
@@ -758,9 +771,9 @@ async function parseRolloutFile({
|
|
|
758
771
|
if (!token) continue;
|
|
759
772
|
|
|
760
773
|
const info = token.info;
|
|
761
|
-
if (!info || typeof info !==
|
|
774
|
+
if (!info || typeof info !== "object") continue;
|
|
762
775
|
|
|
763
|
-
const tokenTimestamp = typeof token.timestamp ===
|
|
776
|
+
const tokenTimestamp = typeof token.timestamp === "string" ? token.timestamp : null;
|
|
764
777
|
if (!tokenTimestamp) continue;
|
|
765
778
|
|
|
766
779
|
const lastUsage = info.last_token_usage;
|
|
@@ -769,7 +782,7 @@ async function parseRolloutFile({
|
|
|
769
782
|
const delta = pickDelta(lastUsage, totalUsage, totals);
|
|
770
783
|
if (!delta) continue;
|
|
771
784
|
|
|
772
|
-
if (totalUsage && typeof totalUsage ===
|
|
785
|
+
if (totalUsage && typeof totalUsage === "object") {
|
|
773
786
|
totals = totalUsage;
|
|
774
787
|
}
|
|
775
788
|
|
|
@@ -785,7 +798,7 @@ async function parseRolloutFile({
|
|
|
785
798
|
currentProjectKey,
|
|
786
799
|
source,
|
|
787
800
|
bucketStart,
|
|
788
|
-
currentProjectRef
|
|
801
|
+
currentProjectRef,
|
|
789
802
|
);
|
|
790
803
|
addTotals(projectBucket.totals, delta);
|
|
791
804
|
projectTouchedBuckets.add(projectBucketKey(currentProjectKey, source, bucketStart));
|
|
@@ -805,7 +818,7 @@ async function parseClaudeFile({
|
|
|
805
818
|
projectState,
|
|
806
819
|
projectTouchedBuckets,
|
|
807
820
|
projectRef,
|
|
808
|
-
projectKey
|
|
821
|
+
projectKey,
|
|
809
822
|
}) {
|
|
810
823
|
const st = await fs.stat(filePath).catch(() => null);
|
|
811
824
|
if (!st || !st.isFile()) return { endOffset: startOffset, eventsAggregated: 0 };
|
|
@@ -813,7 +826,7 @@ async function parseClaudeFile({
|
|
|
813
826
|
const endOffset = st.size;
|
|
814
827
|
if (startOffset >= endOffset) return { endOffset, eventsAggregated: 0 };
|
|
815
828
|
|
|
816
|
-
const stream = fssync.createReadStream(filePath, { encoding:
|
|
829
|
+
const stream = fssync.createReadStream(filePath, { encoding: "utf8", start: startOffset });
|
|
817
830
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
818
831
|
|
|
819
832
|
let eventsAggregated = 0;
|
|
@@ -827,10 +840,10 @@ async function parseClaudeFile({
|
|
|
827
840
|
}
|
|
828
841
|
|
|
829
842
|
const usage = obj?.message?.usage || obj?.usage;
|
|
830
|
-
if (!usage || typeof usage !==
|
|
843
|
+
if (!usage || typeof usage !== "object") continue;
|
|
831
844
|
|
|
832
845
|
const model = normalizeModelInput(obj?.message?.model || obj?.model) || DEFAULT_MODEL;
|
|
833
|
-
const tokenTimestamp = typeof obj?.timestamp ===
|
|
846
|
+
const tokenTimestamp = typeof obj?.timestamp === "string" ? obj.timestamp : null;
|
|
834
847
|
if (!tokenTimestamp) continue;
|
|
835
848
|
|
|
836
849
|
const delta = normalizeClaudeUsage(usage);
|
|
@@ -843,7 +856,13 @@ async function parseClaudeFile({
|
|
|
843
856
|
addTotals(bucket.totals, delta);
|
|
844
857
|
touchedBuckets.add(bucketKey(source, model, bucketStart));
|
|
845
858
|
if (projectKey && projectState && projectTouchedBuckets) {
|
|
846
|
-
const projectBucket = getProjectBucket(
|
|
859
|
+
const projectBucket = getProjectBucket(
|
|
860
|
+
projectState,
|
|
861
|
+
projectKey,
|
|
862
|
+
source,
|
|
863
|
+
bucketStart,
|
|
864
|
+
projectRef,
|
|
865
|
+
);
|
|
847
866
|
addTotals(projectBucket.totals, delta);
|
|
848
867
|
projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
|
|
849
868
|
}
|
|
@@ -866,9 +885,9 @@ async function parseGeminiFile({
|
|
|
866
885
|
projectState,
|
|
867
886
|
projectTouchedBuckets,
|
|
868
887
|
projectRef,
|
|
869
|
-
projectKey
|
|
888
|
+
projectKey,
|
|
870
889
|
}) {
|
|
871
|
-
const raw = await fs.readFile(filePath,
|
|
890
|
+
const raw = await fs.readFile(filePath, "utf8").catch(() => "");
|
|
872
891
|
if (!raw.trim()) return { lastIndex: startIndex, lastTotals, lastModel, eventsAggregated: 0 };
|
|
873
892
|
|
|
874
893
|
let session;
|
|
@@ -886,18 +905,18 @@ async function parseGeminiFile({
|
|
|
886
905
|
}
|
|
887
906
|
|
|
888
907
|
let eventsAggregated = 0;
|
|
889
|
-
let model = typeof lastModel ===
|
|
890
|
-
let totals = lastTotals && typeof lastTotals ===
|
|
908
|
+
let model = typeof lastModel === "string" ? lastModel : null;
|
|
909
|
+
let totals = lastTotals && typeof lastTotals === "object" ? lastTotals : null;
|
|
891
910
|
const begin = Number.isFinite(startIndex) ? startIndex + 1 : 0;
|
|
892
911
|
|
|
893
912
|
for (let idx = begin; idx < messages.length; idx++) {
|
|
894
913
|
const msg = messages[idx];
|
|
895
|
-
if (!msg || typeof msg !==
|
|
914
|
+
if (!msg || typeof msg !== "object") continue;
|
|
896
915
|
|
|
897
916
|
const normalizedModel = normalizeModelInput(msg.model);
|
|
898
917
|
if (normalizedModel) model = normalizedModel;
|
|
899
918
|
|
|
900
|
-
const timestamp = typeof msg.timestamp ===
|
|
919
|
+
const timestamp = typeof msg.timestamp === "string" ? msg.timestamp : null;
|
|
901
920
|
const currentTotals = normalizeGeminiTokens(msg.tokens);
|
|
902
921
|
if (!timestamp || !currentTotals) {
|
|
903
922
|
totals = currentTotals || totals;
|
|
@@ -920,7 +939,13 @@ async function parseGeminiFile({
|
|
|
920
939
|
addTotals(bucket.totals, delta);
|
|
921
940
|
touchedBuckets.add(bucketKey(source, model, bucketStart));
|
|
922
941
|
if (projectKey && projectState && projectTouchedBuckets) {
|
|
923
|
-
const projectBucket = getProjectBucket(
|
|
942
|
+
const projectBucket = getProjectBucket(
|
|
943
|
+
projectState,
|
|
944
|
+
projectKey,
|
|
945
|
+
source,
|
|
946
|
+
bucketStart,
|
|
947
|
+
projectRef,
|
|
948
|
+
);
|
|
924
949
|
addTotals(projectBucket.totals, delta);
|
|
925
950
|
projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
|
|
926
951
|
}
|
|
@@ -932,7 +957,7 @@ async function parseGeminiFile({
|
|
|
932
957
|
lastIndex: messages.length - 1,
|
|
933
958
|
lastTotals: totals,
|
|
934
959
|
lastModel: model,
|
|
935
|
-
eventsAggregated
|
|
960
|
+
eventsAggregated,
|
|
936
961
|
};
|
|
937
962
|
}
|
|
938
963
|
|
|
@@ -947,22 +972,26 @@ async function parseOpencodeMessageFile({
|
|
|
947
972
|
projectState,
|
|
948
973
|
projectTouchedBuckets,
|
|
949
974
|
projectRef,
|
|
950
|
-
projectKey
|
|
975
|
+
projectKey,
|
|
951
976
|
}) {
|
|
952
977
|
const fallbackKey =
|
|
953
|
-
typeof fallbackMessageKey ===
|
|
954
|
-
|
|
978
|
+
typeof fallbackMessageKey === "string" && fallbackMessageKey.trim()
|
|
979
|
+
? fallbackMessageKey.trim()
|
|
980
|
+
: null;
|
|
981
|
+
const legacyTotals = fallbackTotals && typeof fallbackTotals === "object" ? fallbackTotals : null;
|
|
955
982
|
const fallbackEntry = messageIndex && fallbackKey ? messageIndex[fallbackKey] : null;
|
|
956
983
|
const fallbackLastTotals =
|
|
957
|
-
fallbackEntry && typeof fallbackEntry.lastTotals ===
|
|
984
|
+
fallbackEntry && typeof fallbackEntry.lastTotals === "object"
|
|
985
|
+
? fallbackEntry.lastTotals
|
|
986
|
+
: legacyTotals;
|
|
958
987
|
|
|
959
|
-
const raw = await fs.readFile(filePath,
|
|
988
|
+
const raw = await fs.readFile(filePath, "utf8").catch(() => "");
|
|
960
989
|
if (!raw.trim()) {
|
|
961
990
|
return {
|
|
962
991
|
messageKey: fallbackKey,
|
|
963
992
|
lastTotals: fallbackLastTotals,
|
|
964
993
|
eventsAggregated: 0,
|
|
965
|
-
shouldUpdate: false
|
|
994
|
+
shouldUpdate: false,
|
|
966
995
|
};
|
|
967
996
|
}
|
|
968
997
|
|
|
@@ -974,13 +1003,13 @@ async function parseOpencodeMessageFile({
|
|
|
974
1003
|
messageKey: fallbackKey,
|
|
975
1004
|
lastTotals: fallbackLastTotals,
|
|
976
1005
|
eventsAggregated: 0,
|
|
977
|
-
shouldUpdate: false
|
|
1006
|
+
shouldUpdate: false,
|
|
978
1007
|
};
|
|
979
1008
|
}
|
|
980
1009
|
|
|
981
1010
|
const messageKey = deriveOpencodeMessageKey(msg, filePath);
|
|
982
1011
|
const prev = messageIndex && messageKey ? messageIndex[messageKey] : null;
|
|
983
|
-
const indexTotals = prev && typeof prev.lastTotals ===
|
|
1012
|
+
const indexTotals = prev && typeof prev.lastTotals === "object" ? prev.lastTotals : null;
|
|
984
1013
|
const fallbackMatch = !fallbackKey || fallbackKey === messageKey;
|
|
985
1014
|
const lastTotals = indexTotals || (fallbackMatch ? fallbackLastTotals : null);
|
|
986
1015
|
|
|
@@ -1000,7 +1029,7 @@ async function parseOpencodeMessageFile({
|
|
|
1000
1029
|
messageKey,
|
|
1001
1030
|
lastTotals,
|
|
1002
1031
|
eventsAggregated: 0,
|
|
1003
|
-
shouldUpdate: Boolean(lastTotals)
|
|
1032
|
+
shouldUpdate: Boolean(lastTotals),
|
|
1004
1033
|
};
|
|
1005
1034
|
}
|
|
1006
1035
|
|
|
@@ -1011,7 +1040,7 @@ async function parseOpencodeMessageFile({
|
|
|
1011
1040
|
messageKey,
|
|
1012
1041
|
lastTotals,
|
|
1013
1042
|
eventsAggregated: 0,
|
|
1014
|
-
shouldUpdate: Boolean(lastTotals)
|
|
1043
|
+
shouldUpdate: Boolean(lastTotals),
|
|
1015
1044
|
};
|
|
1016
1045
|
}
|
|
1017
1046
|
|
|
@@ -1020,7 +1049,13 @@ async function parseOpencodeMessageFile({
|
|
|
1020
1049
|
addTotals(bucket.totals, delta);
|
|
1021
1050
|
touchedBuckets.add(bucketKey(source, model, bucketStart));
|
|
1022
1051
|
if (projectKey && projectState && projectTouchedBuckets) {
|
|
1023
|
-
const projectBucket = getProjectBucket(
|
|
1052
|
+
const projectBucket = getProjectBucket(
|
|
1053
|
+
projectState,
|
|
1054
|
+
projectKey,
|
|
1055
|
+
source,
|
|
1056
|
+
bucketStart,
|
|
1057
|
+
projectRef,
|
|
1058
|
+
);
|
|
1024
1059
|
addTotals(projectBucket.totals, delta);
|
|
1025
1060
|
projectTouchedBuckets.add(projectBucketKey(projectKey, source, bucketStart));
|
|
1026
1061
|
}
|
|
@@ -1039,7 +1074,10 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1039
1074
|
}
|
|
1040
1075
|
if (touchedGroups.size === 0) return 0;
|
|
1041
1076
|
|
|
1042
|
-
const groupQueued =
|
|
1077
|
+
const groupQueued =
|
|
1078
|
+
hourlyState.groupQueued && typeof hourlyState.groupQueued === "object"
|
|
1079
|
+
? hourlyState.groupQueued
|
|
1080
|
+
: {};
|
|
1043
1081
|
let codexTouched = false;
|
|
1044
1082
|
const legacyGroups = new Set();
|
|
1045
1083
|
for (const groupKey of touchedGroups) {
|
|
@@ -1068,7 +1106,7 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1068
1106
|
groupedBuckets.set(groupKey, group);
|
|
1069
1107
|
}
|
|
1070
1108
|
|
|
1071
|
-
if (bucket.queuedKey != null && typeof bucket.queuedKey !==
|
|
1109
|
+
if (bucket.queuedKey != null && typeof bucket.queuedKey !== "string") {
|
|
1072
1110
|
bucket.queuedKey = null;
|
|
1073
1111
|
}
|
|
1074
1112
|
group.buckets.set(model, bucket);
|
|
@@ -1082,7 +1120,7 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1082
1120
|
const hourStart = parsed.hourStart;
|
|
1083
1121
|
if (!hourStart) continue;
|
|
1084
1122
|
const source = normalizeSourceInput(parsed.source) || DEFAULT_SOURCE;
|
|
1085
|
-
if (source !==
|
|
1123
|
+
if (source !== "every-code") continue;
|
|
1086
1124
|
const groupKey = groupBucketKey(source, hourStart);
|
|
1087
1125
|
if (legacyGroups.has(groupKey) || groupedBuckets.has(groupKey)) continue;
|
|
1088
1126
|
const model = normalizeModelInput(parsed.model) || DEFAULT_MODEL;
|
|
@@ -1104,7 +1142,7 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1104
1142
|
group = { source, hourStart, buckets: new Map() };
|
|
1105
1143
|
groupedBuckets.set(groupKey, group);
|
|
1106
1144
|
}
|
|
1107
|
-
if (bucket.queuedKey != null && typeof bucket.queuedKey !==
|
|
1145
|
+
if (bucket.queuedKey != null && typeof bucket.queuedKey !== "string") {
|
|
1108
1146
|
bucket.queuedKey = null;
|
|
1109
1147
|
}
|
|
1110
1148
|
const model = normalizeModelInput(parsed.model) || DEFAULT_MODEL;
|
|
@@ -1138,11 +1176,16 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1138
1176
|
cached_input_tokens: zeroTotals.cached_input_tokens,
|
|
1139
1177
|
output_tokens: zeroTotals.output_tokens,
|
|
1140
1178
|
reasoning_output_tokens: zeroTotals.reasoning_output_tokens,
|
|
1141
|
-
total_tokens: zeroTotals.total_tokens
|
|
1142
|
-
})
|
|
1179
|
+
total_tokens: zeroTotals.total_tokens,
|
|
1180
|
+
}),
|
|
1143
1181
|
);
|
|
1144
1182
|
}
|
|
1145
|
-
if (
|
|
1183
|
+
if (
|
|
1184
|
+
unknownBucket &&
|
|
1185
|
+
!alignedModel &&
|
|
1186
|
+
unknownBucket.queuedKey &&
|
|
1187
|
+
unknownBucket.queuedKey !== zeroKey
|
|
1188
|
+
) {
|
|
1146
1189
|
if (unknownBucket.retractedUnknownKey !== zeroKey) {
|
|
1147
1190
|
toAppend.push(
|
|
1148
1191
|
JSON.stringify({
|
|
@@ -1153,8 +1196,8 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1153
1196
|
cached_input_tokens: zeroTotals.cached_input_tokens,
|
|
1154
1197
|
output_tokens: zeroTotals.output_tokens,
|
|
1155
1198
|
reasoning_output_tokens: zeroTotals.reasoning_output_tokens,
|
|
1156
|
-
total_tokens: zeroTotals.total_tokens
|
|
1157
|
-
})
|
|
1199
|
+
total_tokens: zeroTotals.total_tokens,
|
|
1200
|
+
}),
|
|
1158
1201
|
);
|
|
1159
1202
|
unknownBucket.retractedUnknownKey = zeroKey;
|
|
1160
1203
|
}
|
|
@@ -1178,8 +1221,8 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1178
1221
|
cached_input_tokens: totals.cached_input_tokens,
|
|
1179
1222
|
output_tokens: totals.output_tokens,
|
|
1180
1223
|
reasoning_output_tokens: totals.reasoning_output_tokens,
|
|
1181
|
-
total_tokens: totals.total_tokens
|
|
1182
|
-
})
|
|
1224
|
+
total_tokens: totals.total_tokens,
|
|
1225
|
+
}),
|
|
1183
1226
|
);
|
|
1184
1227
|
bucket.queuedKey = key;
|
|
1185
1228
|
}
|
|
@@ -1188,7 +1231,7 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1188
1231
|
|
|
1189
1232
|
if (!unknownBucket?.totals) continue;
|
|
1190
1233
|
let outputModel = DEFAULT_MODEL;
|
|
1191
|
-
if (group.source ===
|
|
1234
|
+
if (group.source === "every-code") {
|
|
1192
1235
|
const aligned = findNearestCodexModel(group.hourStart, codexDominants);
|
|
1193
1236
|
if (aligned) outputModel = aligned;
|
|
1194
1237
|
}
|
|
@@ -1203,11 +1246,16 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1203
1246
|
cached_input_tokens: zeroTotals.cached_input_tokens,
|
|
1204
1247
|
output_tokens: zeroTotals.output_tokens,
|
|
1205
1248
|
reasoning_output_tokens: zeroTotals.reasoning_output_tokens,
|
|
1206
|
-
total_tokens: zeroTotals.total_tokens
|
|
1207
|
-
})
|
|
1249
|
+
total_tokens: zeroTotals.total_tokens,
|
|
1250
|
+
}),
|
|
1208
1251
|
);
|
|
1209
1252
|
}
|
|
1210
|
-
if (
|
|
1253
|
+
if (
|
|
1254
|
+
!alignedModel &&
|
|
1255
|
+
nextAligned &&
|
|
1256
|
+
unknownBucket.queuedKey &&
|
|
1257
|
+
unknownBucket.queuedKey !== zeroKey
|
|
1258
|
+
) {
|
|
1211
1259
|
if (unknownBucket.retractedUnknownKey !== zeroKey) {
|
|
1212
1260
|
toAppend.push(
|
|
1213
1261
|
JSON.stringify({
|
|
@@ -1218,8 +1266,8 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1218
1266
|
cached_input_tokens: zeroTotals.cached_input_tokens,
|
|
1219
1267
|
output_tokens: zeroTotals.output_tokens,
|
|
1220
1268
|
reasoning_output_tokens: zeroTotals.reasoning_output_tokens,
|
|
1221
|
-
total_tokens: zeroTotals.total_tokens
|
|
1222
|
-
})
|
|
1269
|
+
total_tokens: zeroTotals.total_tokens,
|
|
1270
|
+
}),
|
|
1223
1271
|
);
|
|
1224
1272
|
unknownBucket.retractedUnknownKey = zeroKey;
|
|
1225
1273
|
}
|
|
@@ -1237,8 +1285,8 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1237
1285
|
cached_input_tokens: unknownBucket.totals.cached_input_tokens,
|
|
1238
1286
|
output_tokens: unknownBucket.totals.output_tokens,
|
|
1239
1287
|
reasoning_output_tokens: unknownBucket.totals.reasoning_output_tokens,
|
|
1240
|
-
total_tokens: unknownBucket.totals.total_tokens
|
|
1241
|
-
})
|
|
1288
|
+
total_tokens: unknownBucket.totals.total_tokens,
|
|
1289
|
+
}),
|
|
1242
1290
|
);
|
|
1243
1291
|
unknownBucket.queuedKey = outputKey;
|
|
1244
1292
|
}
|
|
@@ -1259,7 +1307,7 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1259
1307
|
source: normalizeSourceInput(parsed.source) || DEFAULT_SOURCE,
|
|
1260
1308
|
hourStart,
|
|
1261
1309
|
models: new Set(),
|
|
1262
|
-
totals: initTotals()
|
|
1310
|
+
totals: initTotals(),
|
|
1263
1311
|
};
|
|
1264
1312
|
grouped.set(groupKey, group);
|
|
1265
1313
|
}
|
|
@@ -1281,8 +1329,8 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1281
1329
|
cached_input_tokens: group.totals.cached_input_tokens,
|
|
1282
1330
|
output_tokens: group.totals.output_tokens,
|
|
1283
1331
|
reasoning_output_tokens: group.totals.reasoning_output_tokens,
|
|
1284
|
-
total_tokens: group.totals.total_tokens
|
|
1285
|
-
})
|
|
1332
|
+
total_tokens: group.totals.total_tokens,
|
|
1333
|
+
}),
|
|
1286
1334
|
);
|
|
1287
1335
|
groupQueued[groupKey] = key;
|
|
1288
1336
|
}
|
|
@@ -1291,14 +1339,24 @@ async function enqueueTouchedBuckets({ queuePath, hourlyState, touchedBuckets })
|
|
|
1291
1339
|
hourlyState.groupQueued = groupQueued;
|
|
1292
1340
|
|
|
1293
1341
|
if (toAppend.length > 0) {
|
|
1294
|
-
await fs.appendFile(queuePath, toAppend.join(
|
|
1342
|
+
await fs.appendFile(queuePath, toAppend.join("\n") + "\n", "utf8");
|
|
1295
1343
|
}
|
|
1296
1344
|
|
|
1297
1345
|
return toAppend.length;
|
|
1298
1346
|
}
|
|
1299
1347
|
|
|
1300
|
-
async function enqueueTouchedProjectBuckets({
|
|
1301
|
-
|
|
1348
|
+
async function enqueueTouchedProjectBuckets({
|
|
1349
|
+
projectQueuePath,
|
|
1350
|
+
projectState,
|
|
1351
|
+
projectTouchedBuckets,
|
|
1352
|
+
}) {
|
|
1353
|
+
if (
|
|
1354
|
+
!projectQueuePath ||
|
|
1355
|
+
!projectState ||
|
|
1356
|
+
!projectTouchedBuckets ||
|
|
1357
|
+
projectTouchedBuckets.size === 0
|
|
1358
|
+
)
|
|
1359
|
+
return 0;
|
|
1302
1360
|
|
|
1303
1361
|
await ensureDir(path.dirname(projectQueuePath));
|
|
1304
1362
|
|
|
@@ -1309,8 +1367,8 @@ async function enqueueTouchedProjectBuckets({ projectQueuePath, projectState, pr
|
|
|
1309
1367
|
const totals = bucket.totals;
|
|
1310
1368
|
const queuedKey = totalsKey(totals);
|
|
1311
1369
|
if (bucket.queuedKey === queuedKey) continue;
|
|
1312
|
-
const projectRef = typeof bucket.project_ref ===
|
|
1313
|
-
const projectKey = typeof bucket.project_key ===
|
|
1370
|
+
const projectRef = typeof bucket.project_ref === "string" ? bucket.project_ref : null;
|
|
1371
|
+
const projectKey = typeof bucket.project_key === "string" ? bucket.project_key : null;
|
|
1314
1372
|
if (!projectRef || !projectKey) continue;
|
|
1315
1373
|
|
|
1316
1374
|
toAppend.push(
|
|
@@ -1323,14 +1381,14 @@ async function enqueueTouchedProjectBuckets({ projectQueuePath, projectState, pr
|
|
|
1323
1381
|
cached_input_tokens: totals.cached_input_tokens,
|
|
1324
1382
|
output_tokens: totals.output_tokens,
|
|
1325
1383
|
reasoning_output_tokens: totals.reasoning_output_tokens,
|
|
1326
|
-
total_tokens: totals.total_tokens
|
|
1327
|
-
})
|
|
1384
|
+
total_tokens: totals.total_tokens,
|
|
1385
|
+
}),
|
|
1328
1386
|
);
|
|
1329
1387
|
bucket.queuedKey = queuedKey;
|
|
1330
1388
|
}
|
|
1331
1389
|
|
|
1332
1390
|
if (toAppend.length > 0) {
|
|
1333
|
-
await fs.appendFile(projectQueuePath, toAppend.join(
|
|
1391
|
+
await fs.appendFile(projectQueuePath, toAppend.join("\n") + "\n", "utf8");
|
|
1334
1392
|
}
|
|
1335
1393
|
|
|
1336
1394
|
return toAppend.length;
|
|
@@ -1422,9 +1480,9 @@ function findNearestCodexModel(hourStart, dominants) {
|
|
|
1422
1480
|
}
|
|
1423
1481
|
|
|
1424
1482
|
function normalizeHourlyState(raw) {
|
|
1425
|
-
const state = raw && typeof raw ===
|
|
1483
|
+
const state = raw && typeof raw === "object" ? raw : {};
|
|
1426
1484
|
const version = Number(state.version || 1);
|
|
1427
|
-
const rawBuckets = state.buckets && typeof state.buckets ===
|
|
1485
|
+
const rawBuckets = state.buckets && typeof state.buckets === "object" ? state.buckets : {};
|
|
1428
1486
|
const buckets = {};
|
|
1429
1487
|
const groupQueued = {};
|
|
1430
1488
|
|
|
@@ -1444,7 +1502,7 @@ function normalizeHourlyState(raw) {
|
|
|
1444
1502
|
version: 3,
|
|
1445
1503
|
buckets,
|
|
1446
1504
|
groupQueued,
|
|
1447
|
-
updatedAt: typeof state.updatedAt ===
|
|
1505
|
+
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
|
|
1448
1506
|
};
|
|
1449
1507
|
}
|
|
1450
1508
|
|
|
@@ -1457,21 +1515,21 @@ function normalizeHourlyState(raw) {
|
|
|
1457
1515
|
}
|
|
1458
1516
|
|
|
1459
1517
|
const existingGroupQueued =
|
|
1460
|
-
state.groupQueued && typeof state.groupQueued ===
|
|
1518
|
+
state.groupQueued && typeof state.groupQueued === "object" ? state.groupQueued : {};
|
|
1461
1519
|
|
|
1462
1520
|
return {
|
|
1463
1521
|
version: 3,
|
|
1464
1522
|
buckets,
|
|
1465
1523
|
groupQueued: version >= 3 ? existingGroupQueued : {},
|
|
1466
|
-
updatedAt: typeof state.updatedAt ===
|
|
1524
|
+
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
|
|
1467
1525
|
};
|
|
1468
1526
|
}
|
|
1469
1527
|
|
|
1470
1528
|
function normalizeProjectState(raw) {
|
|
1471
|
-
const state = raw && typeof raw ===
|
|
1472
|
-
const rawBuckets = state.buckets && typeof state.buckets ===
|
|
1529
|
+
const state = raw && typeof raw === "object" ? raw : {};
|
|
1530
|
+
const rawBuckets = state.buckets && typeof state.buckets === "object" ? state.buckets : {};
|
|
1473
1531
|
const buckets = {};
|
|
1474
|
-
const rawProjects = state.projects && typeof state.projects ===
|
|
1532
|
+
const rawProjects = state.projects && typeof state.projects === "object" ? state.projects : {};
|
|
1475
1533
|
const projects = {};
|
|
1476
1534
|
|
|
1477
1535
|
for (const [key, value] of Object.entries(rawBuckets)) {
|
|
@@ -1480,7 +1538,7 @@ function normalizeProjectState(raw) {
|
|
|
1480
1538
|
}
|
|
1481
1539
|
|
|
1482
1540
|
for (const [key, value] of Object.entries(rawProjects)) {
|
|
1483
|
-
if (!key || !value || typeof value !==
|
|
1541
|
+
if (!key || !value || typeof value !== "object") continue;
|
|
1484
1542
|
projects[key] = { ...value };
|
|
1485
1543
|
}
|
|
1486
1544
|
|
|
@@ -1488,21 +1546,21 @@ function normalizeProjectState(raw) {
|
|
|
1488
1546
|
version: 2,
|
|
1489
1547
|
buckets,
|
|
1490
1548
|
projects,
|
|
1491
|
-
updatedAt: typeof state.updatedAt ===
|
|
1549
|
+
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
|
|
1492
1550
|
};
|
|
1493
1551
|
}
|
|
1494
1552
|
|
|
1495
1553
|
function normalizeOpencodeState(raw) {
|
|
1496
|
-
const state = raw && typeof raw ===
|
|
1497
|
-
const messages = state.messages && typeof state.messages ===
|
|
1554
|
+
const state = raw && typeof raw === "object" ? raw : {};
|
|
1555
|
+
const messages = state.messages && typeof state.messages === "object" ? state.messages : {};
|
|
1498
1556
|
return {
|
|
1499
1557
|
messages,
|
|
1500
|
-
updatedAt: typeof state.updatedAt ===
|
|
1558
|
+
updatedAt: typeof state.updatedAt === "string" ? state.updatedAt : null,
|
|
1501
1559
|
};
|
|
1502
1560
|
}
|
|
1503
1561
|
|
|
1504
1562
|
function normalizeMessageKeyPart(value) {
|
|
1505
|
-
if (typeof value !==
|
|
1563
|
+
if (typeof value !== "string") return "";
|
|
1506
1564
|
return value.trim();
|
|
1507
1565
|
}
|
|
1508
1566
|
|
|
@@ -1519,17 +1577,17 @@ function getHourlyBucket(state, source, model, hourStart) {
|
|
|
1519
1577
|
const normalizedModel = normalizeModelInput(model) || DEFAULT_MODEL;
|
|
1520
1578
|
const key = bucketKey(normalizedSource, normalizedModel, hourStart);
|
|
1521
1579
|
let bucket = buckets[key];
|
|
1522
|
-
if (!bucket || typeof bucket !==
|
|
1580
|
+
if (!bucket || typeof bucket !== "object") {
|
|
1523
1581
|
bucket = { totals: initTotals(), queuedKey: null };
|
|
1524
1582
|
buckets[key] = bucket;
|
|
1525
1583
|
return bucket;
|
|
1526
1584
|
}
|
|
1527
1585
|
|
|
1528
|
-
if (!bucket.totals || typeof bucket.totals !==
|
|
1586
|
+
if (!bucket.totals || typeof bucket.totals !== "object") {
|
|
1529
1587
|
bucket.totals = initTotals();
|
|
1530
1588
|
}
|
|
1531
1589
|
|
|
1532
|
-
if (bucket.queuedKey != null && typeof bucket.queuedKey !==
|
|
1590
|
+
if (bucket.queuedKey != null && typeof bucket.queuedKey !== "string") {
|
|
1533
1591
|
bucket.queuedKey = null;
|
|
1534
1592
|
}
|
|
1535
1593
|
|
|
@@ -1541,24 +1599,24 @@ function getProjectBucket(state, projectKey, source, hourStart, projectRef) {
|
|
|
1541
1599
|
const normalizedSource = normalizeSourceInput(source) || DEFAULT_SOURCE;
|
|
1542
1600
|
const key = projectBucketKey(projectKey, normalizedSource, hourStart);
|
|
1543
1601
|
let bucket = buckets[key];
|
|
1544
|
-
if (!bucket || typeof bucket !==
|
|
1602
|
+
if (!bucket || typeof bucket !== "object") {
|
|
1545
1603
|
bucket = {
|
|
1546
1604
|
totals: initTotals(),
|
|
1547
1605
|
queuedKey: null,
|
|
1548
1606
|
project_key: projectKey,
|
|
1549
1607
|
project_ref: projectRef,
|
|
1550
1608
|
source: normalizedSource,
|
|
1551
|
-
hour_start: hourStart
|
|
1609
|
+
hour_start: hourStart,
|
|
1552
1610
|
};
|
|
1553
1611
|
buckets[key] = bucket;
|
|
1554
1612
|
return bucket;
|
|
1555
1613
|
}
|
|
1556
1614
|
|
|
1557
|
-
if (!bucket.totals || typeof bucket.totals !==
|
|
1615
|
+
if (!bucket.totals || typeof bucket.totals !== "object") {
|
|
1558
1616
|
bucket.totals = initTotals();
|
|
1559
1617
|
}
|
|
1560
1618
|
|
|
1561
|
-
if (bucket.queuedKey != null && typeof bucket.queuedKey !==
|
|
1619
|
+
if (bucket.queuedKey != null && typeof bucket.queuedKey !== "string") {
|
|
1562
1620
|
bucket.queuedKey = null;
|
|
1563
1621
|
}
|
|
1564
1622
|
|
|
@@ -1576,7 +1634,7 @@ function initTotals() {
|
|
|
1576
1634
|
cached_input_tokens: 0,
|
|
1577
1635
|
output_tokens: 0,
|
|
1578
1636
|
reasoning_output_tokens: 0,
|
|
1579
|
-
total_tokens: 0
|
|
1637
|
+
total_tokens: 0,
|
|
1580
1638
|
};
|
|
1581
1639
|
}
|
|
1582
1640
|
|
|
@@ -1594,8 +1652,8 @@ function totalsKey(totals) {
|
|
|
1594
1652
|
totals.cached_input_tokens || 0,
|
|
1595
1653
|
totals.output_tokens || 0,
|
|
1596
1654
|
totals.reasoning_output_tokens || 0,
|
|
1597
|
-
totals.total_tokens || 0
|
|
1598
|
-
].join(
|
|
1655
|
+
totals.total_tokens || 0,
|
|
1656
|
+
].join("|");
|
|
1599
1657
|
}
|
|
1600
1658
|
|
|
1601
1659
|
function toUtcHalfHourStart(ts) {
|
|
@@ -1611,8 +1669,8 @@ function toUtcHalfHourStart(ts) {
|
|
|
1611
1669
|
dt.getUTCHours(),
|
|
1612
1670
|
halfMinute,
|
|
1613
1671
|
0,
|
|
1614
|
-
0
|
|
1615
|
-
)
|
|
1672
|
+
0,
|
|
1673
|
+
),
|
|
1616
1674
|
);
|
|
1617
1675
|
return bucketStart.toISOString();
|
|
1618
1676
|
}
|
|
@@ -1634,7 +1692,8 @@ function groupBucketKey(source, hourStart) {
|
|
|
1634
1692
|
}
|
|
1635
1693
|
|
|
1636
1694
|
function parseBucketKey(key) {
|
|
1637
|
-
if (typeof key !==
|
|
1695
|
+
if (typeof key !== "string")
|
|
1696
|
+
return { source: DEFAULT_SOURCE, model: DEFAULT_MODEL, hourStart: "" };
|
|
1638
1697
|
const first = key.indexOf(BUCKET_SEPARATOR);
|
|
1639
1698
|
if (first <= 0) return { source: DEFAULT_SOURCE, model: DEFAULT_MODEL, hourStart: key };
|
|
1640
1699
|
const second = key.indexOf(BUCKET_SEPARATOR, first + 1);
|
|
@@ -1644,24 +1703,24 @@ function parseBucketKey(key) {
|
|
|
1644
1703
|
return {
|
|
1645
1704
|
source: key.slice(0, first),
|
|
1646
1705
|
model: key.slice(first + 1, second),
|
|
1647
|
-
hourStart: key.slice(second + 1)
|
|
1706
|
+
hourStart: key.slice(second + 1),
|
|
1648
1707
|
};
|
|
1649
1708
|
}
|
|
1650
1709
|
|
|
1651
1710
|
function normalizeSourceInput(value) {
|
|
1652
|
-
if (typeof value !==
|
|
1711
|
+
if (typeof value !== "string") return null;
|
|
1653
1712
|
const trimmed = value.trim().toLowerCase();
|
|
1654
1713
|
return trimmed.length > 0 ? trimmed : null;
|
|
1655
1714
|
}
|
|
1656
1715
|
|
|
1657
1716
|
function normalizeModelInput(value) {
|
|
1658
|
-
if (typeof value !==
|
|
1717
|
+
if (typeof value !== "string") return null;
|
|
1659
1718
|
const trimmed = value.trim();
|
|
1660
1719
|
return trimmed.length > 0 ? trimmed : null;
|
|
1661
1720
|
}
|
|
1662
1721
|
|
|
1663
1722
|
async function resolveProjectMetaForPath(startDir, cache) {
|
|
1664
|
-
if (!startDir || typeof startDir !==
|
|
1723
|
+
if (!startDir || typeof startDir !== "string") return null;
|
|
1665
1724
|
if (cache && cache.has(startDir)) return cache.get(startDir);
|
|
1666
1725
|
|
|
1667
1726
|
const visited = [];
|
|
@@ -1701,11 +1760,11 @@ async function resolveProjectMetaForPath(startDir, cache) {
|
|
|
1701
1760
|
async function defaultPublicRepoResolver({ projectRef, repoRoot, cache }) {
|
|
1702
1761
|
if (!projectRef) {
|
|
1703
1762
|
return {
|
|
1704
|
-
status:
|
|
1763
|
+
status: "blocked",
|
|
1705
1764
|
projectKey: null,
|
|
1706
1765
|
projectRef: null,
|
|
1707
1766
|
repoRootHash: repoRoot ? hashRepoRoot(repoRoot) : null,
|
|
1708
|
-
reason:
|
|
1767
|
+
reason: "missing_ref",
|
|
1709
1768
|
};
|
|
1710
1769
|
}
|
|
1711
1770
|
|
|
@@ -1714,15 +1773,20 @@ async function defaultPublicRepoResolver({ projectRef, repoRoot, cache }) {
|
|
|
1714
1773
|
if (cache && !cached) cache.set(projectRef, base);
|
|
1715
1774
|
return {
|
|
1716
1775
|
...base,
|
|
1717
|
-
repoRootHash: repoRoot ? hashRepoRoot(repoRoot) : null
|
|
1776
|
+
repoRootHash: repoRoot ? hashRepoRoot(repoRoot) : null,
|
|
1718
1777
|
};
|
|
1719
1778
|
}
|
|
1720
1779
|
|
|
1721
1780
|
function recordProjectMeta(projectState, meta) {
|
|
1722
|
-
if (!projectState || !meta || typeof meta !==
|
|
1723
|
-
const repoRootHash = typeof meta.repoRootHash ===
|
|
1724
|
-
let projectKey = typeof meta.projectKey ===
|
|
1725
|
-
if (
|
|
1781
|
+
if (!projectState || !meta || typeof meta !== "object") return;
|
|
1782
|
+
const repoRootHash = typeof meta.repoRootHash === "string" ? meta.repoRootHash : null;
|
|
1783
|
+
let projectKey = typeof meta.projectKey === "string" ? meta.projectKey : null;
|
|
1784
|
+
if (
|
|
1785
|
+
!projectKey &&
|
|
1786
|
+
repoRootHash &&
|
|
1787
|
+
projectState.projects &&
|
|
1788
|
+
typeof projectState.projects === "object"
|
|
1789
|
+
) {
|
|
1726
1790
|
for (const [key, entry] of Object.entries(projectState.projects)) {
|
|
1727
1791
|
if (entry && entry.repo_root_hash === repoRootHash) {
|
|
1728
1792
|
projectKey = key;
|
|
@@ -1731,22 +1795,22 @@ function recordProjectMeta(projectState, meta) {
|
|
|
1731
1795
|
}
|
|
1732
1796
|
}
|
|
1733
1797
|
if (!projectKey) return;
|
|
1734
|
-
if (!projectState.projects || typeof projectState.projects !==
|
|
1798
|
+
if (!projectState.projects || typeof projectState.projects !== "object") {
|
|
1735
1799
|
projectState.projects = {};
|
|
1736
1800
|
}
|
|
1737
1801
|
const prev = projectState.projects[projectKey] || {};
|
|
1738
|
-
const status = typeof meta.status ===
|
|
1739
|
-
const projectRef = typeof meta.projectRef ===
|
|
1802
|
+
const status = typeof meta.status === "string" ? meta.status : null;
|
|
1803
|
+
const projectRef = typeof meta.projectRef === "string" ? meta.projectRef : null;
|
|
1740
1804
|
const next = {
|
|
1741
1805
|
...prev,
|
|
1742
1806
|
project_ref: projectRef || prev.project_ref || null,
|
|
1743
1807
|
status: status || prev.status || null,
|
|
1744
1808
|
repo_root_hash: repoRootHash || prev.repo_root_hash || null,
|
|
1745
|
-
updated_at: new Date().toISOString()
|
|
1809
|
+
updated_at: new Date().toISOString(),
|
|
1746
1810
|
};
|
|
1747
|
-
if (status ===
|
|
1811
|
+
if (status === "blocked" && prev.status !== "blocked") {
|
|
1748
1812
|
next.purge_pending = true;
|
|
1749
|
-
} else if (status && status !==
|
|
1813
|
+
} else if (status && status !== "blocked") {
|
|
1750
1814
|
next.purge_pending = false;
|
|
1751
1815
|
}
|
|
1752
1816
|
projectState.projects[projectKey] = next;
|
|
@@ -1757,7 +1821,7 @@ async function resolveProjectContextForFile({
|
|
|
1757
1821
|
projectMetaCache,
|
|
1758
1822
|
publicRepoCache,
|
|
1759
1823
|
publicRepoResolver,
|
|
1760
|
-
projectState
|
|
1824
|
+
projectState,
|
|
1761
1825
|
}) {
|
|
1762
1826
|
if (!filePath) return null;
|
|
1763
1827
|
return resolveProjectContextForPath({
|
|
@@ -1765,7 +1829,7 @@ async function resolveProjectContextForFile({
|
|
|
1765
1829
|
projectMetaCache,
|
|
1766
1830
|
publicRepoCache,
|
|
1767
1831
|
publicRepoResolver,
|
|
1768
|
-
projectState
|
|
1832
|
+
projectState,
|
|
1769
1833
|
});
|
|
1770
1834
|
}
|
|
1771
1835
|
|
|
@@ -1774,43 +1838,48 @@ async function resolveProjectContextForPath({
|
|
|
1774
1838
|
projectMetaCache,
|
|
1775
1839
|
publicRepoCache,
|
|
1776
1840
|
publicRepoResolver,
|
|
1777
|
-
projectState
|
|
1841
|
+
projectState,
|
|
1778
1842
|
}) {
|
|
1779
1843
|
if (!startDir) return null;
|
|
1780
1844
|
const projectMeta = await resolveProjectMetaForPath(startDir, projectMetaCache);
|
|
1781
1845
|
if (!projectMeta) return null;
|
|
1782
|
-
const resolver =
|
|
1846
|
+
const resolver =
|
|
1847
|
+
typeof publicRepoResolver === "function" ? publicRepoResolver : defaultPublicRepoResolver;
|
|
1783
1848
|
const meta = await resolver({
|
|
1784
1849
|
projectRef: projectMeta.projectRef,
|
|
1785
1850
|
repoRoot: projectMeta.repoRoot,
|
|
1786
|
-
cache: publicRepoCache
|
|
1851
|
+
cache: publicRepoCache,
|
|
1787
1852
|
});
|
|
1788
1853
|
const repoRootHash = projectMeta.repoRoot ? hashRepoRoot(projectMeta.repoRoot) : null;
|
|
1789
1854
|
const normalized = {
|
|
1790
1855
|
...(meta || {}),
|
|
1791
1856
|
projectRef: meta?.projectRef || projectMeta.projectRef,
|
|
1792
1857
|
projectKey: meta?.projectKey || null,
|
|
1793
|
-
status: meta?.status ||
|
|
1794
|
-
repoRootHash: meta?.repoRootHash || repoRootHash
|
|
1858
|
+
status: meta?.status || "blocked",
|
|
1859
|
+
repoRootHash: meta?.repoRootHash || repoRootHash,
|
|
1795
1860
|
};
|
|
1796
1861
|
recordProjectMeta(projectState, normalized);
|
|
1797
|
-
if (normalized.status !==
|
|
1862
|
+
if (normalized.status !== "public_verified") {
|
|
1798
1863
|
return { projectRef: normalized.projectRef, projectKey: null, status: normalized.status };
|
|
1799
1864
|
}
|
|
1800
|
-
return {
|
|
1865
|
+
return {
|
|
1866
|
+
projectRef: normalized.projectRef,
|
|
1867
|
+
projectKey: normalized.projectKey,
|
|
1868
|
+
status: normalized.status,
|
|
1869
|
+
};
|
|
1801
1870
|
}
|
|
1802
1871
|
|
|
1803
1872
|
async function resolveGitConfigPath(rootDir) {
|
|
1804
|
-
const gitPath = path.join(rootDir,
|
|
1873
|
+
const gitPath = path.join(rootDir, ".git");
|
|
1805
1874
|
const st = await fs.stat(gitPath).catch(() => null);
|
|
1806
1875
|
if (!st) return null;
|
|
1807
1876
|
if (st.isDirectory()) {
|
|
1808
|
-
const configPath = path.join(gitPath,
|
|
1877
|
+
const configPath = path.join(gitPath, "config");
|
|
1809
1878
|
const cfg = await fs.stat(configPath).catch(() => null);
|
|
1810
1879
|
return cfg && cfg.isFile() ? configPath : null;
|
|
1811
1880
|
}
|
|
1812
1881
|
if (st.isFile()) {
|
|
1813
|
-
const content = await fs.readFile(gitPath,
|
|
1882
|
+
const content = await fs.readFile(gitPath, "utf8").catch(() => "");
|
|
1814
1883
|
const match = content.match(/gitdir:\s*(.+)/i);
|
|
1815
1884
|
if (!match) return null;
|
|
1816
1885
|
let gitDir = match[1].trim();
|
|
@@ -1818,18 +1887,18 @@ async function resolveGitConfigPath(rootDir) {
|
|
|
1818
1887
|
if (!path.isAbsolute(gitDir)) {
|
|
1819
1888
|
gitDir = path.resolve(rootDir, gitDir);
|
|
1820
1889
|
}
|
|
1821
|
-
const configPath = path.join(gitDir,
|
|
1890
|
+
const configPath = path.join(gitDir, "config");
|
|
1822
1891
|
const cfg = await fs.stat(configPath).catch(() => null);
|
|
1823
1892
|
if (cfg && cfg.isFile()) return configPath;
|
|
1824
1893
|
|
|
1825
|
-
const commonDirRaw = await fs.readFile(path.join(gitDir,
|
|
1894
|
+
const commonDirRaw = await fs.readFile(path.join(gitDir, "commondir"), "utf8").catch(() => "");
|
|
1826
1895
|
const commonDirRel = commonDirRaw.trim();
|
|
1827
1896
|
if (!commonDirRel) return null;
|
|
1828
1897
|
let commonDir = commonDirRel;
|
|
1829
1898
|
if (!path.isAbsolute(commonDir)) {
|
|
1830
1899
|
commonDir = path.resolve(gitDir, commonDir);
|
|
1831
1900
|
}
|
|
1832
|
-
const commonConfigPath = path.join(commonDir,
|
|
1901
|
+
const commonConfigPath = path.join(commonDir, "config");
|
|
1833
1902
|
const commonCfg = await fs.stat(commonConfigPath).catch(() => null);
|
|
1834
1903
|
return commonCfg && commonCfg.isFile() ? commonConfigPath : null;
|
|
1835
1904
|
}
|
|
@@ -1837,7 +1906,7 @@ async function resolveGitConfigPath(rootDir) {
|
|
|
1837
1906
|
}
|
|
1838
1907
|
|
|
1839
1908
|
async function readGitRemoteUrl(configPath) {
|
|
1840
|
-
const raw = await fs.readFile(configPath,
|
|
1909
|
+
const raw = await fs.readFile(configPath, "utf8").catch(() => "");
|
|
1841
1910
|
if (!raw.trim()) return null;
|
|
1842
1911
|
|
|
1843
1912
|
const remotes = new Map();
|
|
@@ -1856,34 +1925,34 @@ async function readGitRemoteUrl(configPath) {
|
|
|
1856
1925
|
}
|
|
1857
1926
|
}
|
|
1858
1927
|
|
|
1859
|
-
if (remotes.has(
|
|
1928
|
+
if (remotes.has("origin")) return remotes.get("origin");
|
|
1860
1929
|
const first = remotes.values().next();
|
|
1861
1930
|
return first.done ? null : first.value;
|
|
1862
1931
|
}
|
|
1863
1932
|
|
|
1864
1933
|
function canonicalizeProjectRef(remoteUrl) {
|
|
1865
|
-
if (typeof remoteUrl !==
|
|
1934
|
+
if (typeof remoteUrl !== "string") return null;
|
|
1866
1935
|
let ref = remoteUrl.trim();
|
|
1867
1936
|
if (!ref) return null;
|
|
1868
1937
|
|
|
1869
|
-
if (ref.startsWith(
|
|
1938
|
+
if (ref.startsWith("file://")) return null;
|
|
1870
1939
|
if (path.isAbsolute(ref) || path.win32.isAbsolute(ref)) return null;
|
|
1871
1940
|
|
|
1872
1941
|
const gitAtMatch = ref.match(/^git@([^:]+):(.+)$/i);
|
|
1873
1942
|
if (gitAtMatch) {
|
|
1874
1943
|
ref = `https://${gitAtMatch[1]}/${gitAtMatch[2]}`;
|
|
1875
|
-
} else if (ref.startsWith(
|
|
1944
|
+
} else if (ref.startsWith("ssh://")) {
|
|
1876
1945
|
try {
|
|
1877
1946
|
const parsed = new URL(ref);
|
|
1878
1947
|
ref = `https://${parsed.hostname}${parsed.pathname}`;
|
|
1879
1948
|
} catch (_e) {
|
|
1880
1949
|
return null;
|
|
1881
1950
|
}
|
|
1882
|
-
} else if (ref.startsWith(
|
|
1883
|
-
ref = `https://${ref.slice(
|
|
1884
|
-
} else if (ref.startsWith(
|
|
1885
|
-
ref = `https://${ref.slice(
|
|
1886
|
-
} else if (!ref.startsWith(
|
|
1951
|
+
} else if (ref.startsWith("git://")) {
|
|
1952
|
+
ref = `https://${ref.slice("git://".length)}`;
|
|
1953
|
+
} else if (ref.startsWith("http://")) {
|
|
1954
|
+
ref = `https://${ref.slice("http://".length)}`;
|
|
1955
|
+
} else if (!ref.startsWith("https://")) {
|
|
1887
1956
|
return null;
|
|
1888
1957
|
}
|
|
1889
1958
|
|
|
@@ -1895,13 +1964,13 @@ function canonicalizeProjectRef(remoteUrl) {
|
|
|
1895
1964
|
return null;
|
|
1896
1965
|
}
|
|
1897
1966
|
|
|
1898
|
-
ref = ref.replace(/\.git$/i,
|
|
1899
|
-
ref = ref.replace(/\/+$/,
|
|
1967
|
+
ref = ref.replace(/\.git$/i, "");
|
|
1968
|
+
ref = ref.replace(/\/+$/, "");
|
|
1900
1969
|
return ref || null;
|
|
1901
1970
|
}
|
|
1902
1971
|
|
|
1903
1972
|
function normalizeGeminiTokens(tokens) {
|
|
1904
|
-
if (!tokens || typeof tokens !==
|
|
1973
|
+
if (!tokens || typeof tokens !== "object") return null;
|
|
1905
1974
|
const input = toNonNegativeInt(tokens.input);
|
|
1906
1975
|
const cached = toNonNegativeInt(tokens.cached);
|
|
1907
1976
|
const output = toNonNegativeInt(tokens.output);
|
|
@@ -1914,12 +1983,12 @@ function normalizeGeminiTokens(tokens) {
|
|
|
1914
1983
|
cached_input_tokens: cached,
|
|
1915
1984
|
output_tokens: output + tool,
|
|
1916
1985
|
reasoning_output_tokens: thoughts,
|
|
1917
|
-
total_tokens: total
|
|
1986
|
+
total_tokens: total,
|
|
1918
1987
|
};
|
|
1919
1988
|
}
|
|
1920
1989
|
|
|
1921
1990
|
function normalizeOpencodeTokens(tokens) {
|
|
1922
|
-
if (!tokens || typeof tokens !==
|
|
1991
|
+
if (!tokens || typeof tokens !== "object") return null;
|
|
1923
1992
|
const input = toNonNegativeInt(tokens.input);
|
|
1924
1993
|
const output = toNonNegativeInt(tokens.output);
|
|
1925
1994
|
const reasoning = toNonNegativeInt(tokens.reasoning);
|
|
@@ -1933,7 +2002,7 @@ function normalizeOpencodeTokens(tokens) {
|
|
|
1933
2002
|
cached_input_tokens: cached,
|
|
1934
2003
|
output_tokens: output,
|
|
1935
2004
|
reasoning_output_tokens: reasoning,
|
|
1936
|
-
total_tokens: total
|
|
2005
|
+
total_tokens: total,
|
|
1937
2006
|
};
|
|
1938
2007
|
}
|
|
1939
2008
|
|
|
@@ -1949,8 +2018,8 @@ function sameGeminiTotals(a, b) {
|
|
|
1949
2018
|
}
|
|
1950
2019
|
|
|
1951
2020
|
function diffGeminiTotals(current, previous) {
|
|
1952
|
-
if (!current || typeof current !==
|
|
1953
|
-
if (!previous || typeof previous !==
|
|
2021
|
+
if (!current || typeof current !== "object") return null;
|
|
2022
|
+
if (!previous || typeof previous !== "object") return current;
|
|
1954
2023
|
if (sameGeminiTotals(current, previous)) return null;
|
|
1955
2024
|
|
|
1956
2025
|
const totalReset = (current.total_tokens || 0) < (previous.total_tokens || 0);
|
|
@@ -1958,10 +2027,16 @@ function diffGeminiTotals(current, previous) {
|
|
|
1958
2027
|
|
|
1959
2028
|
const delta = {
|
|
1960
2029
|
input_tokens: Math.max(0, (current.input_tokens || 0) - (previous.input_tokens || 0)),
|
|
1961
|
-
cached_input_tokens: Math.max(
|
|
2030
|
+
cached_input_tokens: Math.max(
|
|
2031
|
+
0,
|
|
2032
|
+
(current.cached_input_tokens || 0) - (previous.cached_input_tokens || 0),
|
|
2033
|
+
),
|
|
1962
2034
|
output_tokens: Math.max(0, (current.output_tokens || 0) - (previous.output_tokens || 0)),
|
|
1963
|
-
reasoning_output_tokens: Math.max(
|
|
1964
|
-
|
|
2035
|
+
reasoning_output_tokens: Math.max(
|
|
2036
|
+
0,
|
|
2037
|
+
(current.reasoning_output_tokens || 0) - (previous.reasoning_output_tokens || 0),
|
|
2038
|
+
),
|
|
2039
|
+
total_tokens: Math.max(0, (current.total_tokens || 0) - (previous.total_tokens || 0)),
|
|
1965
2040
|
};
|
|
1966
2041
|
|
|
1967
2042
|
return isAllZeroUsage(delta) ? null : delta;
|
|
@@ -1970,11 +2045,11 @@ function diffGeminiTotals(current, previous) {
|
|
|
1970
2045
|
function extractTokenCount(obj) {
|
|
1971
2046
|
const payload = obj?.payload;
|
|
1972
2047
|
if (!payload) return null;
|
|
1973
|
-
if (payload.type ===
|
|
2048
|
+
if (payload.type === "token_count") {
|
|
1974
2049
|
return { info: payload.info, timestamp: obj?.timestamp || null };
|
|
1975
2050
|
}
|
|
1976
2051
|
const msg = payload.msg;
|
|
1977
|
-
if (msg && msg.type ===
|
|
2052
|
+
if (msg && msg.type === "token_count") {
|
|
1978
2053
|
return { info: msg.info, timestamp: obj?.timestamp || null };
|
|
1979
2054
|
}
|
|
1980
2055
|
return null;
|
|
@@ -2002,7 +2077,13 @@ function pickDelta(lastUsage, totalUsage, prevTotals) {
|
|
|
2002
2077
|
|
|
2003
2078
|
if (hasTotal && hasPrevTotals) {
|
|
2004
2079
|
const delta = {};
|
|
2005
|
-
for (const k of [
|
|
2080
|
+
for (const k of [
|
|
2081
|
+
"input_tokens",
|
|
2082
|
+
"cached_input_tokens",
|
|
2083
|
+
"output_tokens",
|
|
2084
|
+
"reasoning_output_tokens",
|
|
2085
|
+
"total_tokens",
|
|
2086
|
+
]) {
|
|
2006
2087
|
const a = Number(totalUsage[k]);
|
|
2007
2088
|
const b = Number(prevTotals[k]);
|
|
2008
2089
|
if (Number.isFinite(a) && Number.isFinite(b)) delta[k] = Math.max(0, a - b);
|
|
@@ -2021,7 +2102,13 @@ function pickDelta(lastUsage, totalUsage, prevTotals) {
|
|
|
2021
2102
|
|
|
2022
2103
|
function normalizeUsage(u) {
|
|
2023
2104
|
const out = {};
|
|
2024
|
-
for (const k of [
|
|
2105
|
+
for (const k of [
|
|
2106
|
+
"input_tokens",
|
|
2107
|
+
"cached_input_tokens",
|
|
2108
|
+
"output_tokens",
|
|
2109
|
+
"reasoning_output_tokens",
|
|
2110
|
+
"total_tokens",
|
|
2111
|
+
]) {
|
|
2025
2112
|
const n = Number(u[k] || 0);
|
|
2026
2113
|
out[k] = Number.isFinite(n) && n >= 0 ? Math.floor(n) : 0;
|
|
2027
2114
|
}
|
|
@@ -2029,33 +2116,46 @@ function normalizeUsage(u) {
|
|
|
2029
2116
|
}
|
|
2030
2117
|
|
|
2031
2118
|
function normalizeClaudeUsage(u) {
|
|
2032
|
-
const inputTokens =
|
|
2119
|
+
const inputTokens =
|
|
2120
|
+
toNonNegativeInt(u?.input_tokens) + toNonNegativeInt(u?.cache_creation_input_tokens);
|
|
2033
2121
|
const outputTokens = toNonNegativeInt(u?.output_tokens);
|
|
2034
|
-
const hasTotal = u && Object.prototype.hasOwnProperty.call(u,
|
|
2122
|
+
const hasTotal = u && Object.prototype.hasOwnProperty.call(u, "total_tokens");
|
|
2035
2123
|
const totalTokens = hasTotal ? toNonNegativeInt(u?.total_tokens) : inputTokens + outputTokens;
|
|
2036
2124
|
return {
|
|
2037
2125
|
input_tokens: inputTokens,
|
|
2038
2126
|
cached_input_tokens: toNonNegativeInt(u?.cache_read_input_tokens),
|
|
2039
2127
|
output_tokens: outputTokens,
|
|
2040
2128
|
reasoning_output_tokens: 0,
|
|
2041
|
-
total_tokens: totalTokens
|
|
2129
|
+
total_tokens: totalTokens,
|
|
2042
2130
|
};
|
|
2043
2131
|
}
|
|
2044
2132
|
|
|
2045
2133
|
function isNonEmptyObject(v) {
|
|
2046
|
-
return Boolean(v && typeof v ===
|
|
2134
|
+
return Boolean(v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length > 0);
|
|
2047
2135
|
}
|
|
2048
2136
|
|
|
2049
2137
|
function isAllZeroUsage(u) {
|
|
2050
|
-
if (!u || typeof u !==
|
|
2051
|
-
for (const k of [
|
|
2138
|
+
if (!u || typeof u !== "object") return true;
|
|
2139
|
+
for (const k of [
|
|
2140
|
+
"input_tokens",
|
|
2141
|
+
"cached_input_tokens",
|
|
2142
|
+
"output_tokens",
|
|
2143
|
+
"reasoning_output_tokens",
|
|
2144
|
+
"total_tokens",
|
|
2145
|
+
]) {
|
|
2052
2146
|
if (Number(u[k] || 0) !== 0) return false;
|
|
2053
2147
|
}
|
|
2054
2148
|
return true;
|
|
2055
2149
|
}
|
|
2056
2150
|
|
|
2057
2151
|
function sameUsage(a, b) {
|
|
2058
|
-
for (const k of [
|
|
2152
|
+
for (const k of [
|
|
2153
|
+
"input_tokens",
|
|
2154
|
+
"cached_input_tokens",
|
|
2155
|
+
"output_tokens",
|
|
2156
|
+
"reasoning_output_tokens",
|
|
2157
|
+
"total_tokens",
|
|
2158
|
+
]) {
|
|
2059
2159
|
if (toNonNegativeInt(a?.[k]) !== toNonNegativeInt(b?.[k])) return false;
|
|
2060
2160
|
}
|
|
2061
2161
|
return true;
|
|
@@ -2069,7 +2169,7 @@ function totalsReset(curr, prev) {
|
|
|
2069
2169
|
}
|
|
2070
2170
|
|
|
2071
2171
|
function isFiniteNumber(v) {
|
|
2072
|
-
return typeof v ===
|
|
2172
|
+
return typeof v === "number" && Number.isFinite(v);
|
|
2073
2173
|
}
|
|
2074
2174
|
|
|
2075
2175
|
function toNonNegativeInt(v) {
|
|
@@ -2101,7 +2201,7 @@ async function walkClaudeProjects(dir, out) {
|
|
|
2101
2201
|
await walkClaudeProjects(fullPath, out);
|
|
2102
2202
|
continue;
|
|
2103
2203
|
}
|
|
2104
|
-
if (entry.isFile() && entry.name.endsWith(
|
|
2204
|
+
if (entry.isFile() && entry.name.endsWith(".jsonl")) out.push(fullPath);
|
|
2105
2205
|
}
|
|
2106
2206
|
}
|
|
2107
2207
|
|
|
@@ -2113,7 +2213,8 @@ async function walkOpencodeMessages(dir, out) {
|
|
|
2113
2213
|
await walkOpencodeMessages(fullPath, out);
|
|
2114
2214
|
continue;
|
|
2115
2215
|
}
|
|
2116
|
-
if (entry.isFile() && entry.name.startsWith(
|
|
2216
|
+
if (entry.isFile() && entry.name.startsWith("msg_") && entry.name.endsWith(".json"))
|
|
2217
|
+
out.push(fullPath);
|
|
2117
2218
|
}
|
|
2118
2219
|
}
|
|
2119
2220
|
|
|
@@ -2126,5 +2227,5 @@ module.exports = {
|
|
|
2126
2227
|
parseClaudeIncremental,
|
|
2127
2228
|
parseGeminiIncremental,
|
|
2128
2229
|
parseOpencodeIncremental,
|
|
2129
|
-
parseOpenclawIncremental
|
|
2230
|
+
parseOpenclawIncremental,
|
|
2130
2231
|
};
|