cutsio 0.2.4 → 0.2.6
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/index.js +241 -185
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -137,6 +137,18 @@ var ApiError = class extends Error {
|
|
|
137
137
|
this.status = status;
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
|
+
var MAX_REQUEST_RETRIES = 3;
|
|
141
|
+
var BASE_RETRY_DELAY_MS = 1e3;
|
|
142
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
143
|
+
function retryDelayMs(attempt, retryAfter) {
|
|
144
|
+
const headerSeconds = retryAfter ? Number(retryAfter) : Number.NaN;
|
|
145
|
+
const headerMs = Number.isFinite(headerSeconds) && headerSeconds >= 0 ? headerSeconds * 1e3 : null;
|
|
146
|
+
const backoffMs = Math.min(BASE_RETRY_DELAY_MS * 2 ** attempt, MAX_RETRY_DELAY_MS);
|
|
147
|
+
return Math.min(headerMs ?? backoffMs, MAX_RETRY_DELAY_MS) + Math.random() * 500;
|
|
148
|
+
}
|
|
149
|
+
function sleep(ms) {
|
|
150
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
151
|
+
}
|
|
140
152
|
var CutsioClient = class {
|
|
141
153
|
base;
|
|
142
154
|
apiKey;
|
|
@@ -181,17 +193,21 @@ var CutsioClient = class {
|
|
|
181
193
|
...init?.body ? { "Content-Type": "application/json" } : {},
|
|
182
194
|
...init?.headers ?? {}
|
|
183
195
|
};
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
196
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
197
|
+
const response = await fetch(`${this.base}${path}`, { ...init, headers });
|
|
198
|
+
if (response.ok) return response.json();
|
|
199
|
+
const retryable = response.status === 429 || response.status === 502 || response.status === 503 || response.status === 504;
|
|
200
|
+
if (!retryable || attempt >= MAX_REQUEST_RETRIES) {
|
|
201
|
+
let message = `HTTP ${response.status}`;
|
|
202
|
+
try {
|
|
203
|
+
const body = await response.json();
|
|
204
|
+
if (typeof body.error === "string") message = body.error;
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
throw new ApiError(message, response.status);
|
|
191
208
|
}
|
|
192
|
-
|
|
209
|
+
await sleep(retryDelayMs(attempt, response.headers.get("retry-after")));
|
|
193
210
|
}
|
|
194
|
-
return response.json();
|
|
195
211
|
}
|
|
196
212
|
};
|
|
197
213
|
|
|
@@ -350,6 +366,195 @@ function resolveSelection(manifest, selection) {
|
|
|
350
366
|
return { videos: [...selected.values()], errors };
|
|
351
367
|
}
|
|
352
368
|
|
|
369
|
+
// src/ui/format.ts
|
|
370
|
+
function formatBytes(bytes) {
|
|
371
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
372
|
+
let value = bytes;
|
|
373
|
+
let unit = 0;
|
|
374
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
375
|
+
value /= 1024;
|
|
376
|
+
unit += 1;
|
|
377
|
+
}
|
|
378
|
+
return `${value.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// src/ui/theme.ts
|
|
382
|
+
var colorEnabled = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
383
|
+
function paint(code) {
|
|
384
|
+
return colorEnabled ? (text) => `\x1B[${code}m${text}\x1B[0m` : (text) => text;
|
|
385
|
+
}
|
|
386
|
+
var color = {
|
|
387
|
+
dim: paint(2),
|
|
388
|
+
bold: paint(1),
|
|
389
|
+
red: paint(31),
|
|
390
|
+
green: paint(32),
|
|
391
|
+
yellow: paint(33),
|
|
392
|
+
blue: paint(34),
|
|
393
|
+
magenta: paint(35),
|
|
394
|
+
cyan: paint(36)
|
|
395
|
+
};
|
|
396
|
+
var isInteractive = colorEnabled;
|
|
397
|
+
var animationsEnabled = colorEnabled && process.env.CUTSIO_NO_ANIM !== "1";
|
|
398
|
+
function sleep2(ms) {
|
|
399
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/ui/progress.ts
|
|
403
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
404
|
+
var BAR_WIDTH = 22;
|
|
405
|
+
var ProgressView = class {
|
|
406
|
+
active = /* @__PURE__ */ new Map();
|
|
407
|
+
interactive = isInteractive;
|
|
408
|
+
statusLine = null;
|
|
409
|
+
renderedLines = 0;
|
|
410
|
+
frame = 0;
|
|
411
|
+
timer = null;
|
|
412
|
+
dirty = false;
|
|
413
|
+
constructor() {
|
|
414
|
+
if (!this.interactive) return;
|
|
415
|
+
this.timer = setInterval(() => {
|
|
416
|
+
if (this.active.size === 0 && !this.statusLine) return;
|
|
417
|
+
this.frame += 1;
|
|
418
|
+
this.render();
|
|
419
|
+
}, 120);
|
|
420
|
+
this.timer.unref();
|
|
421
|
+
}
|
|
422
|
+
status(text) {
|
|
423
|
+
if (!this.interactive) return;
|
|
424
|
+
this.statusLine = text;
|
|
425
|
+
this.dirty = true;
|
|
426
|
+
this.render();
|
|
427
|
+
}
|
|
428
|
+
/** Print a line above the live area without tearing it. */
|
|
429
|
+
log(message) {
|
|
430
|
+
if (!this.interactive) {
|
|
431
|
+
console.log(message);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
this.clear();
|
|
435
|
+
process.stdout.write(`${message}
|
|
436
|
+
`);
|
|
437
|
+
this.render();
|
|
438
|
+
}
|
|
439
|
+
error(message) {
|
|
440
|
+
if (!this.interactive) {
|
|
441
|
+
console.error(message);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.clear();
|
|
445
|
+
process.stderr.write(`${message}
|
|
446
|
+
`);
|
|
447
|
+
this.render();
|
|
448
|
+
}
|
|
449
|
+
start(id, label) {
|
|
450
|
+
if (!this.interactive) {
|
|
451
|
+
console.log(` fetch ${label}`);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
const now = Date.now();
|
|
455
|
+
this.active.set(id, {
|
|
456
|
+
label,
|
|
457
|
+
bytes: 0,
|
|
458
|
+
total: null,
|
|
459
|
+
lastBytes: 0,
|
|
460
|
+
lastAt: now,
|
|
461
|
+
speed: 0
|
|
462
|
+
});
|
|
463
|
+
this.dirty = true;
|
|
464
|
+
this.render();
|
|
465
|
+
}
|
|
466
|
+
progress(id, bytes, total) {
|
|
467
|
+
if (!this.interactive) return;
|
|
468
|
+
const item = this.active.get(id);
|
|
469
|
+
if (!item) return;
|
|
470
|
+
const now = Date.now();
|
|
471
|
+
const elapsed = (now - item.lastAt) / 1e3;
|
|
472
|
+
if (elapsed >= 0.3) {
|
|
473
|
+
const delta = bytes - item.lastBytes;
|
|
474
|
+
if (delta >= 0) item.speed = delta / elapsed;
|
|
475
|
+
item.lastBytes = bytes;
|
|
476
|
+
item.lastAt = now;
|
|
477
|
+
}
|
|
478
|
+
item.bytes = bytes;
|
|
479
|
+
if (total !== null) item.total = total;
|
|
480
|
+
this.dirty = true;
|
|
481
|
+
}
|
|
482
|
+
finish(id, ok, text) {
|
|
483
|
+
if (!this.interactive) {
|
|
484
|
+
if (ok) console.log(` done ${text}`);
|
|
485
|
+
else console.error(` FAILED ${text}`);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.clear();
|
|
489
|
+
const line = ok ? ` ${color.green("\u2713")} ${text}` : ` ${color.red("\u2717")} ${text}`;
|
|
490
|
+
(ok ? process.stdout : process.stderr).write(`${line}
|
|
491
|
+
`);
|
|
492
|
+
this.active.delete(id);
|
|
493
|
+
this.render();
|
|
494
|
+
}
|
|
495
|
+
stop() {
|
|
496
|
+
if (this.timer) clearInterval(this.timer);
|
|
497
|
+
this.timer = null;
|
|
498
|
+
if (!this.interactive) return;
|
|
499
|
+
this.clear();
|
|
500
|
+
this.active.clear();
|
|
501
|
+
this.statusLine = null;
|
|
502
|
+
}
|
|
503
|
+
/** Run `work` while a spinner status shows; cleared on exit, plain line when piped. */
|
|
504
|
+
async withStatus(text, work) {
|
|
505
|
+
if (!this.interactive) {
|
|
506
|
+
console.log(text);
|
|
507
|
+
try {
|
|
508
|
+
return await work();
|
|
509
|
+
} finally {
|
|
510
|
+
this.stop();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
this.status(text);
|
|
514
|
+
try {
|
|
515
|
+
return await work();
|
|
516
|
+
} finally {
|
|
517
|
+
this.statusLine = null;
|
|
518
|
+
this.render();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
clear() {
|
|
522
|
+
if (this.renderedLines === 0) return;
|
|
523
|
+
process.stdout.write(`\x1B[${this.renderedLines}A\x1B[J`);
|
|
524
|
+
this.renderedLines = 0;
|
|
525
|
+
}
|
|
526
|
+
render() {
|
|
527
|
+
if (!this.interactive) return;
|
|
528
|
+
if (this.active.size === 0 && !this.statusLine) {
|
|
529
|
+
this.clear();
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const lines = [];
|
|
533
|
+
if (this.statusLine) {
|
|
534
|
+
const spinner = FRAMES[this.frame % FRAMES.length] ?? FRAMES[0];
|
|
535
|
+
lines.push(` ${color.cyan(spinner ?? "")} ${color.dim(this.statusLine)}`);
|
|
536
|
+
}
|
|
537
|
+
for (const item of this.active.values()) {
|
|
538
|
+
lines.push(` ${color.bold(item.label)}`);
|
|
539
|
+
lines.push(` ${this.bar(item)}`);
|
|
540
|
+
}
|
|
541
|
+
this.clear();
|
|
542
|
+
process.stdout.write(`${lines.join("\n")}
|
|
543
|
+
`);
|
|
544
|
+
this.renderedLines = lines.length;
|
|
545
|
+
this.dirty = false;
|
|
546
|
+
}
|
|
547
|
+
bar(item) {
|
|
548
|
+
const ratio = item.total && item.total > 0 ? Math.min(1, item.bytes / item.total) : null;
|
|
549
|
+
const filled = ratio === null ? 0 : Math.round(ratio * BAR_WIDTH);
|
|
550
|
+
const track = color.cyan("\u2588".repeat(filled)) + color.dim("\u2591".repeat(BAR_WIDTH - filled));
|
|
551
|
+
const percent = ratio === null ? " ? " : `${Math.floor(ratio * 100)}%`.padStart(4);
|
|
552
|
+
const size = item.total ? `${formatBytes(item.bytes)} / ${formatBytes(item.total)}` : formatBytes(item.bytes);
|
|
553
|
+
const speed = item.speed > 0 ? `${formatBytes(item.speed)}/s` : "";
|
|
554
|
+
return `${track} ${percent} ${size} ${color.dim(speed)}`.trimEnd();
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
|
|
353
558
|
// src/commands/list.ts
|
|
354
559
|
async function listCommand(flags) {
|
|
355
560
|
const { apiKey, migrated } = await resolveApiKey();
|
|
@@ -359,12 +564,15 @@ async function listCommand(flags) {
|
|
|
359
564
|
return 1;
|
|
360
565
|
}
|
|
361
566
|
const client = new CutsioClient(resolveApiUrl(flags.url), apiKey);
|
|
567
|
+
const view = new ProgressView();
|
|
362
568
|
let manifest;
|
|
363
569
|
try {
|
|
364
|
-
manifest = await client.fetchManifest();
|
|
570
|
+
manifest = await view.withStatus("Fetching workspace\u2026", () => client.fetchManifest());
|
|
365
571
|
} catch (error) {
|
|
366
572
|
console.error(error instanceof Error ? error.message : String(error));
|
|
367
573
|
return 1;
|
|
574
|
+
} finally {
|
|
575
|
+
view.stop();
|
|
368
576
|
}
|
|
369
577
|
if (flags.json === "true") {
|
|
370
578
|
console.log(JSON.stringify(manifest, null, 2));
|
|
@@ -446,7 +654,16 @@ async function loginCommand(flags) {
|
|
|
446
654
|
continue;
|
|
447
655
|
}
|
|
448
656
|
try {
|
|
449
|
-
const
|
|
657
|
+
const view = new ProgressView();
|
|
658
|
+
let who;
|
|
659
|
+
try {
|
|
660
|
+
who = await view.withStatus(
|
|
661
|
+
"Verifying key\u2026",
|
|
662
|
+
() => new CutsioClient(apiUrl, apiKey).whoami()
|
|
663
|
+
);
|
|
664
|
+
} finally {
|
|
665
|
+
view.stop();
|
|
666
|
+
}
|
|
450
667
|
if (!who.orgId) {
|
|
451
668
|
console.error("That key is not attached to an organization. Create an organization API key.\n");
|
|
452
669
|
continue;
|
|
@@ -751,12 +968,16 @@ async function sync(client, options) {
|
|
|
751
968
|
while (queue.size > 0) {
|
|
752
969
|
const ready = [];
|
|
753
970
|
const ids = [...queue.keys()];
|
|
971
|
+
let serverDelayMs = 0;
|
|
754
972
|
for (let index = 0; index < ids.length; index += PREPARE_BATCH_SIZE) {
|
|
755
973
|
const batch = ids.slice(index, index + PREPARE_BATCH_SIZE);
|
|
756
974
|
const results = await client.prepare(batch);
|
|
757
975
|
for (const result of results) {
|
|
758
976
|
const item = queue.get(result.id);
|
|
759
977
|
if (!item) continue;
|
|
978
|
+
if (typeof result.retryAfterSeconds === "number" && result.retryAfterSeconds > 0) {
|
|
979
|
+
serverDelayMs = Math.max(serverDelayMs, result.retryAfterSeconds * 1e3);
|
|
980
|
+
}
|
|
760
981
|
switch (result.status) {
|
|
761
982
|
case "ready":
|
|
762
983
|
if (result.url) {
|
|
@@ -850,7 +1071,7 @@ async function sync(client, options) {
|
|
|
850
1071
|
}
|
|
851
1072
|
break;
|
|
852
1073
|
}
|
|
853
|
-
await
|
|
1074
|
+
await sleep3(Math.min(Math.max(serverDelayMs, prepareDelayMs), MAX_PREPARE_DELAY_MS));
|
|
854
1075
|
prepareDelayMs = Math.min(prepareDelayMs * 2, MAX_PREPARE_DELAY_MS);
|
|
855
1076
|
}
|
|
856
1077
|
return { downloaded, unchanged, skipped, failed };
|
|
@@ -870,180 +1091,10 @@ async function runPool(items, concurrency, worker) {
|
|
|
870
1091
|
})
|
|
871
1092
|
);
|
|
872
1093
|
}
|
|
873
|
-
function
|
|
874
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
// src/ui/format.ts
|
|
878
|
-
function formatBytes(bytes) {
|
|
879
|
-
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
880
|
-
let value = bytes;
|
|
881
|
-
let unit = 0;
|
|
882
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
883
|
-
value /= 1024;
|
|
884
|
-
unit += 1;
|
|
885
|
-
}
|
|
886
|
-
return `${value.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// src/ui/theme.ts
|
|
890
|
-
var colorEnabled = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
891
|
-
function paint(code) {
|
|
892
|
-
return colorEnabled ? (text) => `\x1B[${code}m${text}\x1B[0m` : (text) => text;
|
|
893
|
-
}
|
|
894
|
-
var color = {
|
|
895
|
-
dim: paint(2),
|
|
896
|
-
bold: paint(1),
|
|
897
|
-
red: paint(31),
|
|
898
|
-
green: paint(32),
|
|
899
|
-
yellow: paint(33),
|
|
900
|
-
blue: paint(34),
|
|
901
|
-
magenta: paint(35),
|
|
902
|
-
cyan: paint(36)
|
|
903
|
-
};
|
|
904
|
-
var isInteractive = colorEnabled;
|
|
905
|
-
var animationsEnabled = colorEnabled && process.env.CUTSIO_NO_ANIM !== "1";
|
|
906
|
-
function sleep2(ms) {
|
|
1094
|
+
function sleep3(ms) {
|
|
907
1095
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
908
1096
|
}
|
|
909
1097
|
|
|
910
|
-
// src/ui/progress.ts
|
|
911
|
-
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
912
|
-
var BAR_WIDTH = 22;
|
|
913
|
-
var ProgressView = class {
|
|
914
|
-
active = /* @__PURE__ */ new Map();
|
|
915
|
-
interactive = isInteractive;
|
|
916
|
-
statusLine = null;
|
|
917
|
-
renderedLines = 0;
|
|
918
|
-
frame = 0;
|
|
919
|
-
timer = null;
|
|
920
|
-
dirty = false;
|
|
921
|
-
constructor() {
|
|
922
|
-
if (!this.interactive) return;
|
|
923
|
-
this.timer = setInterval(() => {
|
|
924
|
-
if (this.active.size === 0) return;
|
|
925
|
-
this.frame += 1;
|
|
926
|
-
this.render();
|
|
927
|
-
}, 120);
|
|
928
|
-
this.timer.unref();
|
|
929
|
-
}
|
|
930
|
-
status(text) {
|
|
931
|
-
if (!this.interactive) return;
|
|
932
|
-
this.statusLine = text;
|
|
933
|
-
this.dirty = true;
|
|
934
|
-
}
|
|
935
|
-
/** Print a line above the live area without tearing it. */
|
|
936
|
-
log(message) {
|
|
937
|
-
if (!this.interactive) {
|
|
938
|
-
console.log(message);
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
this.clear();
|
|
942
|
-
process.stdout.write(`${message}
|
|
943
|
-
`);
|
|
944
|
-
this.render();
|
|
945
|
-
}
|
|
946
|
-
error(message) {
|
|
947
|
-
if (!this.interactive) {
|
|
948
|
-
console.error(message);
|
|
949
|
-
return;
|
|
950
|
-
}
|
|
951
|
-
this.clear();
|
|
952
|
-
process.stderr.write(`${message}
|
|
953
|
-
`);
|
|
954
|
-
this.render();
|
|
955
|
-
}
|
|
956
|
-
start(id, label) {
|
|
957
|
-
if (!this.interactive) {
|
|
958
|
-
console.log(` fetch ${label}`);
|
|
959
|
-
return;
|
|
960
|
-
}
|
|
961
|
-
const now = Date.now();
|
|
962
|
-
this.active.set(id, {
|
|
963
|
-
label,
|
|
964
|
-
bytes: 0,
|
|
965
|
-
total: null,
|
|
966
|
-
lastBytes: 0,
|
|
967
|
-
lastAt: now,
|
|
968
|
-
speed: 0
|
|
969
|
-
});
|
|
970
|
-
this.dirty = true;
|
|
971
|
-
this.render();
|
|
972
|
-
}
|
|
973
|
-
progress(id, bytes, total) {
|
|
974
|
-
if (!this.interactive) return;
|
|
975
|
-
const item = this.active.get(id);
|
|
976
|
-
if (!item) return;
|
|
977
|
-
const now = Date.now();
|
|
978
|
-
const elapsed = (now - item.lastAt) / 1e3;
|
|
979
|
-
if (elapsed >= 0.3) {
|
|
980
|
-
const delta = bytes - item.lastBytes;
|
|
981
|
-
if (delta >= 0) item.speed = delta / elapsed;
|
|
982
|
-
item.lastBytes = bytes;
|
|
983
|
-
item.lastAt = now;
|
|
984
|
-
}
|
|
985
|
-
item.bytes = bytes;
|
|
986
|
-
if (total !== null) item.total = total;
|
|
987
|
-
this.dirty = true;
|
|
988
|
-
}
|
|
989
|
-
finish(id, ok, text) {
|
|
990
|
-
if (!this.interactive) {
|
|
991
|
-
if (ok) console.log(` done ${text}`);
|
|
992
|
-
else console.error(` FAILED ${text}`);
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
this.clear();
|
|
996
|
-
const line = ok ? ` ${color.green("\u2713")} ${text}` : ` ${color.red("\u2717")} ${text}`;
|
|
997
|
-
(ok ? process.stdout : process.stderr).write(`${line}
|
|
998
|
-
`);
|
|
999
|
-
this.active.delete(id);
|
|
1000
|
-
this.render();
|
|
1001
|
-
}
|
|
1002
|
-
stop() {
|
|
1003
|
-
if (this.timer) clearInterval(this.timer);
|
|
1004
|
-
this.timer = null;
|
|
1005
|
-
if (!this.interactive) return;
|
|
1006
|
-
this.clear();
|
|
1007
|
-
this.active.clear();
|
|
1008
|
-
this.statusLine = null;
|
|
1009
|
-
}
|
|
1010
|
-
clear() {
|
|
1011
|
-
if (this.renderedLines === 0) return;
|
|
1012
|
-
process.stdout.write(`\x1B[${this.renderedLines}A\x1B[J`);
|
|
1013
|
-
this.renderedLines = 0;
|
|
1014
|
-
}
|
|
1015
|
-
render() {
|
|
1016
|
-
if (!this.interactive) return;
|
|
1017
|
-
if (this.active.size === 0 && !this.statusLine) {
|
|
1018
|
-
this.clear();
|
|
1019
|
-
return;
|
|
1020
|
-
}
|
|
1021
|
-
const lines = [];
|
|
1022
|
-
if (this.statusLine) {
|
|
1023
|
-
const spinner = FRAMES[this.frame % FRAMES.length] ?? FRAMES[0];
|
|
1024
|
-
lines.push(` ${color.cyan(spinner ?? "")} ${color.dim(this.statusLine)}`);
|
|
1025
|
-
}
|
|
1026
|
-
for (const item of this.active.values()) {
|
|
1027
|
-
lines.push(` ${color.bold(item.label)}`);
|
|
1028
|
-
lines.push(` ${this.bar(item)}`);
|
|
1029
|
-
}
|
|
1030
|
-
this.clear();
|
|
1031
|
-
process.stdout.write(`${lines.join("\n")}
|
|
1032
|
-
`);
|
|
1033
|
-
this.renderedLines = lines.length;
|
|
1034
|
-
this.dirty = false;
|
|
1035
|
-
}
|
|
1036
|
-
bar(item) {
|
|
1037
|
-
const ratio = item.total && item.total > 0 ? Math.min(1, item.bytes / item.total) : null;
|
|
1038
|
-
const filled = ratio === null ? 0 : Math.round(ratio * BAR_WIDTH);
|
|
1039
|
-
const track = color.cyan("\u2588".repeat(filled)) + color.dim("\u2591".repeat(BAR_WIDTH - filled));
|
|
1040
|
-
const percent = ratio === null ? " ? " : `${Math.floor(ratio * 100)}%`.padStart(4);
|
|
1041
|
-
const size = item.total ? `${formatBytes(item.bytes)} / ${formatBytes(item.total)}` : formatBytes(item.bytes);
|
|
1042
|
-
const speed = item.speed > 0 ? `${formatBytes(item.speed)}/s` : "";
|
|
1043
|
-
return `${track} ${percent} ${size} ${color.dim(speed)}`.trimEnd();
|
|
1044
|
-
}
|
|
1045
|
-
};
|
|
1046
|
-
|
|
1047
1098
|
// src/commands/selection-prompt.ts
|
|
1048
1099
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
1049
1100
|
import { createInterface } from "node:readline/promises";
|
|
@@ -1172,7 +1223,7 @@ async function syncCommand(flags) {
|
|
|
1172
1223
|
try {
|
|
1173
1224
|
if (flags.interactive === "true") {
|
|
1174
1225
|
view.stop();
|
|
1175
|
-
manifest = await client.fetchManifest();
|
|
1226
|
+
manifest = await view.withStatus("Fetching workspace\u2026", () => client.fetchManifest());
|
|
1176
1227
|
const prompted = await promptSelection(manifest);
|
|
1177
1228
|
if (!prompted) {
|
|
1178
1229
|
console.log("Cancelled. Nothing downloaded.");
|
|
@@ -1280,12 +1331,17 @@ async function whoamiCommand(flags) {
|
|
|
1280
1331
|
return 1;
|
|
1281
1332
|
}
|
|
1282
1333
|
const client = new CutsioClient(resolveApiUrl(flags.url), apiKey);
|
|
1334
|
+
const view = new ProgressView();
|
|
1283
1335
|
try {
|
|
1284
|
-
console.log(
|
|
1336
|
+
console.log(
|
|
1337
|
+
JSON.stringify(await view.withStatus("Checking key\u2026", () => client.whoami()), null, 2)
|
|
1338
|
+
);
|
|
1285
1339
|
return 0;
|
|
1286
1340
|
} catch (error) {
|
|
1287
1341
|
console.error(error instanceof Error ? error.message : String(error));
|
|
1288
1342
|
return 1;
|
|
1343
|
+
} finally {
|
|
1344
|
+
view.stop();
|
|
1289
1345
|
}
|
|
1290
1346
|
}
|
|
1291
1347
|
|