@tinycloud/cli 1.2.2-beta.5 → 1.2.3-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -432,14 +432,6 @@ function formatBytes(bytes) {
432
432
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
433
433
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
434
434
  }
435
- function formatTimeAgo(date) {
436
- const d = typeof date === "string" ? new Date(date) : date;
437
- const seconds = Math.floor((Date.now() - d.getTime()) / 1e3);
438
- if (seconds < 60) return "just now";
439
- if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
440
- if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
441
- return `${Math.floor(seconds / 86400)}d ago`;
442
- }
443
435
  var WARNING_CODE, WARNING_REASON, DELEGATION_CID, JSON_REQUESTED;
444
436
  var init_formatter = __esm({
445
437
  "src/output/formatter.ts"() {
@@ -20310,7 +20302,8 @@ async function startAuthFlow(did, options = {}) {
20310
20302
  }
20311
20303
  try {
20312
20304
  return await callbackFlow(did, options);
20313
- } catch {
20305
+ } catch (error) {
20306
+ if (error instanceof CLIError && error.code === "CANCELLED") throw error;
20314
20307
  if (process.stdin.isTTY) {
20315
20308
  console.error("Could not open browser. Falling back to manual paste mode.");
20316
20309
  return pasteFlow(did, options);
@@ -20555,6 +20548,7 @@ If the browser can't connect back, enter the short code or paste the full delega
20555
20548
  resolving = false;
20556
20549
  }
20557
20550
  });
20551
+ rl.on("SIGINT", () => settle({ error: loginCancelled() }));
20558
20552
  }
20559
20553
  });
20560
20554
  timeout = setTimeout(() => {
@@ -20594,10 +20588,19 @@ Open this URL in a browser to authenticate:
20594
20588
  }
20595
20589
  ));
20596
20590
  });
20591
+ rl.on("SIGINT", () => {
20592
+ if (answered) return;
20593
+ answered = true;
20594
+ rl.close();
20595
+ reject(loginCancelled());
20596
+ });
20597
20597
  rl.setPrompt("Enter short code or paste full delegation code: ");
20598
20598
  rl.prompt();
20599
20599
  });
20600
20600
  }
20601
+ function loginCancelled() {
20602
+ return new CLIError("CANCELLED", "Interrupted: tc auth login stopped before approval. No session was saved.", ExitCode.CANCELLED);
20603
+ }
20601
20604
 
20602
20605
  // src/auth/device-auth.ts
20603
20606
  init_constants();
@@ -24459,37 +24462,64 @@ function registerKvCommand(program) {
24459
24462
  return handleError(error);
24460
24463
  }
24461
24464
  });
24462
- kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (options, cmd) => {
24465
+ kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--limit <n>", "Keys per page, 1-1000 (the node's default otherwise)").option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Follow nextCursor until the listing is complete").action(async (options, cmd) => {
24463
24466
  try {
24464
24467
  if (options.prefix) assertAddressableKey(options.prefix);
24468
+ const limit = options.limit === void 0 ? void 0 : Number(options.limit);
24469
+ if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 1e3)) {
24470
+ throw new CLIError("USAGE_ERROR", "--limit must be an integer from 1 through 1000", ExitCode.USAGE_ERROR);
24471
+ }
24465
24472
  const globalOpts = cmd.optsWithGlobals();
24466
24473
  const ctx = await ProfileManager.resolveContext(globalOpts);
24467
24474
  const node = await ensureAuthenticated(ctx);
24468
24475
  const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
24469
- const listOptions = options.prefix ? { prefix: options.prefix } : void 0;
24470
- const result = await withSpinner("Listing keys...", () => kv2.list(listOptions));
24471
- if (!result.ok) {
24472
- await throwKvError(result.error, spaceUri, ctx.profile);
24476
+ const keyList = [];
24477
+ let cursor = options.cursor;
24478
+ let truncated;
24479
+ const seenCursors = /* @__PURE__ */ new Set();
24480
+ for (; ; ) {
24481
+ const result = await withSpinner("Listing keys...", () => kv2.list({
24482
+ ...options.prefix ? { prefix: options.prefix } : {},
24483
+ ...limit === void 0 ? {} : { limit },
24484
+ ...cursor === void 0 ? {} : { cursor }
24485
+ }));
24486
+ if (!result.ok) {
24487
+ await throwKvError(result.error, spaceUri, ctx.profile);
24488
+ break;
24489
+ }
24490
+ for (const key of result.data.keys) keyList.push(key);
24491
+ truncated = result.data.truncated;
24492
+ cursor = truncated === true ? result.data.nextCursor : void 0;
24493
+ if (options.all && truncated === true && cursor === void 0) {
24494
+ throw new CLIError("KV_LIST_CURSOR_MISSING", "The node reported more keys but no cursor to continue from, so the listing can't be completed.", ExitCode.ERROR);
24495
+ }
24496
+ if (!options.all || cursor === void 0) break;
24497
+ if (seenCursors.has(cursor)) {
24498
+ throw new CLIError("KV_LIST_CURSOR_STALLED", "The node returned a list cursor it had already returned, so the listing can't be completed.", ExitCode.ERROR);
24499
+ }
24500
+ seenCursors.add(cursor);
24473
24501
  }
24474
- const rawData = result.data.data ?? result.data;
24475
- const keyList = Array.isArray(rawData) ? rawData : rawData?.keys ?? [];
24476
24502
  if (shouldOutputJson()) {
24477
24503
  outputJson({
24478
24504
  keys: keyList,
24479
24505
  count: keyList.length,
24480
- prefix: options.prefix ?? null
24506
+ prefix: options.prefix ?? null,
24507
+ // null when the node doesn't report truncation.
24508
+ truncated: truncated ?? null,
24509
+ nextCursor: cursor ?? null
24481
24510
  });
24482
24511
  } else {
24483
24512
  if (keyList.length === 0) {
24484
24513
  process.stdout.write(theme.muted("No keys found.") + "\n");
24485
24514
  } else {
24486
- const rows = keyList.map((e) => [
24487
- e.key || e,
24488
- e.contentLength ? formatBytes(e.contentLength) : "\u2014",
24489
- e.updatedAt ? formatTimeAgo(e.updatedAt) : "\u2014"
24490
- ]);
24515
+ const rows = keyList.map((key) => [key, "\u2014", "\u2014"]);
24491
24516
  process.stdout.write(formatTable(["Key", "Size", "Updated"], rows) + "\n");
24492
24517
  }
24518
+ if (cursor !== void 0) {
24519
+ process.stdout.write(theme.muted(`More keys follow. Run again with --cursor ${cursor}, or use --all.`) + "\n");
24520
+ } else if (truncated === true) {
24521
+ process.stdout.write(theme.muted("More keys follow, but the node gave no cursor to continue from.") + "\n");
24522
+ }
24493
24523
  }
24494
24524
  } catch (error) {
24495
24525
  return handleError(error);