@tinycloud/cli 1.0.0-beta.17 → 1.0.0-beta.18

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.
@@ -16907,6 +16907,23 @@ async function throwKvError(error, spaceUri, profileName) {
16907
16907
  if (hosted) throw hosted;
16908
16908
  throw new CLIError(error.code, error.message, ExitCode.ERROR);
16909
16909
  }
16910
+ function isByteCount(value) {
16911
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
16912
+ }
16913
+ function storageQuotaError(error) {
16914
+ if (error.code !== "STORAGE_QUOTA_EXCEEDED") return void 0;
16915
+ const { usedBytes, limitBytes } = typeof error.meta === "object" && error.meta !== null ? error.meta : {};
16916
+ const sizes = isByteCount(usedBytes) && isByteCount(limitBytes) ? ` (${formatBytes(usedBytes)} used of ${formatBytes(limitBytes)} limit)` : "";
16917
+ return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was written`, ExitCode.ERROR);
16918
+ }
16919
+ function assertAddressableKey(key) {
16920
+ for (let i = 0; i < key.length; i++) {
16921
+ const code2 = key.charCodeAt(i);
16922
+ if (code2 <= 32 || code2 === 127) {
16923
+ throw new CLIError("USAGE_ERROR", "KV keys cannot contain spaces or control characters; use a URL-safe key", ExitCode.USAGE_ERROR);
16924
+ }
16925
+ }
16926
+ }
16910
16927
  async function readStdin2() {
16911
16928
  const chunks = [];
16912
16929
  for await (const chunk of process.stdin) {
@@ -16923,6 +16940,7 @@ function registerKvCommand(program) {
16923
16940
  const kv = program.command("kv").description("Key-value store operations");
16924
16941
  kv.command("get <key>").description("Get a value by key").option("--raw", "Output raw value (no JSON wrapping)").option("-o, --output <file>", "Write value to file").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
16925
16942
  try {
16943
+ assertAddressableKey(key);
16926
16944
  const globalOpts = cmd.optsWithGlobals();
16927
16945
  const ctx = await ProfileManager.resolveContext(globalOpts);
16928
16946
  const node = await ensureAuthenticated(ctx);
@@ -16967,6 +16985,7 @@ function registerKvCommand(program) {
16967
16985
  });
16968
16986
  kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
16969
16987
  try {
16988
+ assertAddressableKey(key);
16970
16989
  const globalOpts = cmd.optsWithGlobals();
16971
16990
  const ctx = await ProfileManager.resolveContext(globalOpts);
16972
16991
  const node = await ensureAuthenticated(ctx);
@@ -16992,6 +17011,8 @@ function registerKvCommand(program) {
16992
17011
  const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
16993
17012
  const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
16994
17013
  if (!result.ok) {
17014
+ const quota = storageQuotaError(result.error);
17015
+ if (quota) throw quota;
16995
17016
  await throwKvError(result.error, spaceUri, ctx.profile);
16996
17017
  }
16997
17018
  outputJson({ key, written: true });
@@ -17001,6 +17022,7 @@ function registerKvCommand(program) {
17001
17022
  });
17002
17023
  kv.command("delete <key>").description("Delete a key").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
17003
17024
  try {
17025
+ assertAddressableKey(key);
17004
17026
  const globalOpts = cmd.optsWithGlobals();
17005
17027
  const ctx = await ProfileManager.resolveContext(globalOpts);
17006
17028
  const node = await ensureAuthenticated(ctx);
@@ -17016,6 +17038,7 @@ function registerKvCommand(program) {
17016
17038
  });
17017
17039
  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) => {
17018
17040
  try {
17041
+ if (options.prefix) assertAddressableKey(options.prefix);
17019
17042
  const globalOpts = cmd.optsWithGlobals();
17020
17043
  const ctx = await ProfileManager.resolveContext(globalOpts);
17021
17044
  const node = await ensureAuthenticated(ctx);
@@ -17051,6 +17074,7 @@ function registerKvCommand(program) {
17051
17074
  });
17052
17075
  kv.command("head <key>").description("Get metadata for a key (no body)").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
17053
17076
  try {
17077
+ assertAddressableKey(key);
17054
17078
  const globalOpts = cmd.optsWithGlobals();
17055
17079
  const ctx = await ProfileManager.resolveContext(globalOpts);
17056
17080
  const node = await ensureAuthenticated(ctx);
@@ -27204,6 +27228,7 @@ function parseNativeShareUrl(value) {
27204
27228
  }
27205
27229
 
27206
27230
  // src/commands/share.ts
27231
+ init_formatter();
27207
27232
  init_errors();
27208
27233
 
27209
27234
  // src/share/output.ts
@@ -27403,7 +27428,7 @@ function shareCliError(error) {
27403
27428
  }
27404
27429
  if (failure.kind === "scope-denied") {
27405
27430
  const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
27406
- const renew = localKey ? `renew it with ${loginHint} using the required capability` : `request the builtin:share-publishing scope with ${loginHint}`;
27431
+ const renew = localKey ? `renew it with ${loginHint} using the required capability` : `verify the session includes the builtin:share-publishing scope; if it does not, request it with ${loginHint}`;
27407
27432
  return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; ${renew}`, 5);
27408
27433
  }
27409
27434
  if (failure.kind === "lifetime-exceeds-session") {
@@ -27428,6 +27453,13 @@ function shareCliError(error) {
27428
27453
  if (failure.kind === "registry-rejected") {
27429
27454
  return new CLIError("REGISTRY_REJECTED", "the TinyCloud location registry rejected this session's location record, so nothing was shared; retrying will not help. Log in again, and report the problem if it persists", 6);
27430
27455
  }
27456
+ if (failure.kind === "storage-quota-exceeded") {
27457
+ const sizes = failure.usedBytes === void 0 || failure.limitBytes === void 0 ? "" : ` (${formatBytes(failure.usedBytes)} used of ${formatBytes(failure.limitBytes)} limit)`;
27458
+ return new CLIError("STORAGE_QUOTA_EXCEEDED", `storage quota exceeded${sizes}; nothing was shared`, 4);
27459
+ }
27460
+ if (failure.kind === "upload-failed") {
27461
+ return new CLIError("UPLOAD_FAILED", "share source upload failed; nothing was shared", 4);
27462
+ }
27431
27463
  }
27432
27464
  if (error instanceof ShareNotifyError) {
27433
27465
  return new CLIError("INVALID_ARGUMENT", error.message, 2);