@vibe-cafe/vibe-usage 0.10.26 → 0.10.27
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/package.json +1 -1
- package/src/sync.js +54 -8
package/package.json
CHANGED
package/src/sync.js
CHANGED
|
@@ -15,6 +15,28 @@ import { success, failure, warn, arrow, link, dim } from './output.js';
|
|
|
15
15
|
const BATCH_SIZE = 100;
|
|
16
16
|
const SESSION_BATCH_SIZE = 500;
|
|
17
17
|
|
|
18
|
+
/** Coarse human duration: "45s" / "2m10s" / "1h 20m". */
|
|
19
|
+
export function formatDuration(seconds) {
|
|
20
|
+
const secs = Math.max(0, Math.round(seconds));
|
|
21
|
+
if (secs < 60) return `${secs}s`;
|
|
22
|
+
const h = Math.floor(secs / 3600);
|
|
23
|
+
const m = Math.floor((secs % 3600) / 60);
|
|
24
|
+
const s = secs % 60;
|
|
25
|
+
if (h > 0) return m > 0 ? `${h}h ${m}m` : `${h}h`;
|
|
26
|
+
return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Remaining upload time from batches already finished. Measured, never guessed:
|
|
31
|
+
* returns null until at least one batch has completed, because a first-sync
|
|
32
|
+
* backlog and a steady-state trickle differ by three orders of magnitude and
|
|
33
|
+
* any a-priori rate would be wrong for one of them.
|
|
34
|
+
*/
|
|
35
|
+
export function estimateRemainingSeconds({ elapsedMs, doneBatches, totalBatches }) {
|
|
36
|
+
if (!(elapsedMs > 0) || doneBatches < 1 || totalBatches <= doneBatches) return null;
|
|
37
|
+
return ((elapsedMs / doneBatches) * (totalBatches - doneBatches)) / 1000;
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
function formatBytes(bytes) {
|
|
19
41
|
if (bytes < 1024) return `${bytes}B`;
|
|
20
42
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
@@ -321,6 +343,20 @@ export async function runSync({
|
|
|
321
343
|
const totalBatches = Math.max(bucketBatches, sessionBatches, 1);
|
|
322
344
|
const syncClient = createSyncClient({ defaultSurface: surface, hostname: host });
|
|
323
345
|
|
|
346
|
+
// Say up front how much is about to go up. A first sync (or one that
|
|
347
|
+
// backfills after a parser was broken) can be thousands of batches, and with
|
|
348
|
+
// only a per-batch progress line the user cannot tell a long upload from a
|
|
349
|
+
// hung one -- which is exactly how a silently failing parser stayed hidden.
|
|
350
|
+
if (!quiet) {
|
|
351
|
+
const pending = [`${allBucketsToSend.length} buckets`];
|
|
352
|
+
if (allSessionsToSend.length > 0) pending.push(`${allSessionsToSend.length} sessions`);
|
|
353
|
+
const batchNote = totalBatches > 1 ? `,分 ${totalBatches} 批` : '';
|
|
354
|
+
console.log(dim(` 待上传 ${pending.join(' · ')}${batchNote}`));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
let uploadedBytes = 0;
|
|
358
|
+
const uploadStartedAt = Date.now();
|
|
359
|
+
|
|
324
360
|
try {
|
|
325
361
|
for (let batchIdx = 0; batchIdx < totalBatches; batchIdx++) {
|
|
326
362
|
const batch = allBucketsToSend.slice(batchIdx * BATCH_SIZE, (batchIdx + 1) * BATCH_SIZE);
|
|
@@ -328,13 +364,25 @@ export async function runSync({
|
|
|
328
364
|
const batchNum = batchIdx + 1;
|
|
329
365
|
const prefix = totalBatches > 1 ? ` ${dim(`[${batchNum}/${totalBatches}]`)} 上传中 ` : ' 上传中 ';
|
|
330
366
|
|
|
367
|
+
// Only measured batches feed the estimate, so the first batch shows no
|
|
368
|
+
// ETA rather than a made-up one.
|
|
369
|
+
const remaining = estimateRemainingSeconds({
|
|
370
|
+
elapsedMs: Date.now() - uploadStartedAt,
|
|
371
|
+
doneBatches: batchIdx,
|
|
372
|
+
totalBatches,
|
|
373
|
+
});
|
|
374
|
+
const etaNote = remaining === null ? '' : ` · 预计还需 ${formatDuration(remaining)}`;
|
|
375
|
+
|
|
376
|
+
let batchBytes = 0;
|
|
331
377
|
const result = await ingest(apiUrl, config.apiKey, batch, {
|
|
332
378
|
client: forBatch(syncClient, batchIdx, totalBatches),
|
|
333
379
|
onProgress(sent, total) {
|
|
380
|
+
batchBytes = total;
|
|
334
381
|
const pct = Math.round((sent / total) * 100);
|
|
335
|
-
process.stdout.write(`\r${prefix}${dim(`${formatBytes(sent)}/${formatBytes(total)} (${pct}%)`)}\x1b[K`);
|
|
382
|
+
process.stdout.write(`\r${prefix}${dim(`${formatBytes(sent)}/${formatBytes(total)} (${pct}%)${etaNote}`)}\x1b[K`);
|
|
336
383
|
},
|
|
337
384
|
}, batchSessions.length > 0 ? batchSessions : undefined);
|
|
385
|
+
uploadedBytes += batchBytes;
|
|
338
386
|
totalIngested += result.ingested ?? batch.length;
|
|
339
387
|
totalSessionsSynced += result.sessions ?? 0;
|
|
340
388
|
const batchUnknownSources = new Set(result.dropped?.unknownSources || []);
|
|
@@ -379,6 +427,10 @@ export async function runSync({
|
|
|
379
427
|
const syncParts = [`${totalIngested} buckets`];
|
|
380
428
|
if (totalSessionsSynced > 0) syncParts.push(`${totalSessionsSynced} sessions`);
|
|
381
429
|
console.log(success(`已同步 ${syncParts.join(' · ')}`));
|
|
430
|
+
if (!quiet && uploadedBytes > 0) {
|
|
431
|
+
const elapsed = (Date.now() - uploadStartedAt) / 1000;
|
|
432
|
+
console.log(dim(` 上传 ${formatBytes(uploadedBytes)}(已压缩),用时 ${formatDuration(elapsed)}`));
|
|
433
|
+
}
|
|
382
434
|
|
|
383
435
|
if (totalDroppedBuckets > 0) {
|
|
384
436
|
const reasons = [];
|
|
@@ -399,13 +451,7 @@ export async function runSync({
|
|
|
399
451
|
const totalActive = allSessionsToSend.reduce((s, x) => s + x.activeSeconds, 0);
|
|
400
452
|
const totalDuration = allSessionsToSend.reduce((s, x) => s + x.durationSeconds, 0);
|
|
401
453
|
const totalMsgs = allSessionsToSend.reduce((s, x) => s + x.messageCount, 0);
|
|
402
|
-
|
|
403
|
-
if (secs < 60) return `${secs}s`;
|
|
404
|
-
const h = Math.floor(secs / 3600);
|
|
405
|
-
const m = Math.floor((secs % 3600) / 60);
|
|
406
|
-
return h > 0 ? (m > 0 ? `${h}h ${m}m` : `${h}h`) : `${m}m`;
|
|
407
|
-
};
|
|
408
|
-
console.log(dim(` 活跃 ${fmtTime(totalActive)} / 总时长 ${fmtTime(totalDuration)} · ${totalMsgs} 条消息`));
|
|
454
|
+
console.log(dim(` 活跃 ${formatDuration(totalActive)} / 总时长 ${formatDuration(totalDuration)} · ${totalMsgs} 条消息`));
|
|
409
455
|
}
|
|
410
456
|
|
|
411
457
|
if (!quiet) {
|