@tinycloud/cli 1.1.0-beta.8 → 1.1.0-beta.9

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.
@@ -163,7 +163,7 @@ import ora from "ora";
163
163
  function outputJson(data) {
164
164
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
165
165
  }
166
- function outputError(code3, message, hint) {
166
+ function outputError(code3, message, hint, meta) {
167
167
  if (isInteractive()) {
168
168
  process.stderr.write(
169
169
  `${theme.error("\u2717")} ${theme.label(code3)}: ${message}
@@ -180,6 +180,7 @@ function outputError(code3, message, hint) {
180
180
  error: { code: code3, message }
181
181
  };
182
182
  if (hint) payload.error.hint = hint;
183
+ if (meta) payload.error.meta = meta;
183
184
  process.stderr.write(JSON.stringify(payload, null, 2) + "\n");
184
185
  }
185
186
  }
@@ -261,23 +262,42 @@ var init_formatter = __esm({
261
262
  import { readFileSync, readdirSync } from "fs";
262
263
  import { join as join2 } from "path";
263
264
  import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
265
+ import { authorizationVerdictOf, parseCapabilityResource, SERVICE_LONG_TO_SHORT, validatedCapabilityOf } from "@tinycloud/sdk-core";
264
266
  function setActiveProfileName(name) {
265
267
  activeProfileName = name;
266
268
  }
269
+ function cliErrorFromService(error, message = error.message) {
270
+ if (error instanceof CLIError) return error;
271
+ const meta = { ...error.meta };
272
+ delete meta.hint;
273
+ const status = error.status ?? error.statusCode;
274
+ if (typeof status === "number" && Number.isInteger(status) && status >= 400 && status <= 599) meta.status = status;
275
+ const verdict = authorizationVerdictOf({ ...error, meta });
276
+ if (verdict === void 0 && message.includes("Missing private key parameter in JWK")) {
277
+ return missingPrivateKeyError();
278
+ }
279
+ const missingCapability = validatedCapabilityOf({ ...error, meta }) !== void 0;
280
+ const denied = verdict === "forbidden" || verdict === "unauthenticated" && missingCapability;
281
+ return new CLIError(
282
+ denied ? "PERMISSION_DENIED" : verdict === "unauthenticated" ? "AUTH_REQUIRED" : error.code,
283
+ message,
284
+ denied ? ExitCode.PERMISSION_DENIED : verdict === "unauthenticated" ? ExitCode.AUTH_REQUIRED : ExitCode.ERROR,
285
+ meta
286
+ );
287
+ }
267
288
  function wrapError(error) {
289
+ if (error instanceof CLIError) return error;
268
290
  const message = error instanceof Error ? error.message : String(error);
269
- if (message.includes("Missing private key parameter in JWK")) {
270
- const profileName = activeProfileName ?? process.env.TC_PROFILE ?? DEFAULT_PROFILE;
271
- return new CLIError(
272
- "AUTH_REQUIRED",
273
- `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
274
- ExitCode.AUTH_REQUIRED,
275
- {
276
- hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey`
277
- }
278
- );
291
+ const verdict = authorizationVerdictOf(error);
292
+ if (verdict === "unauthenticated") {
293
+ return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
294
+ }
295
+ if (verdict === "forbidden") {
296
+ return new CLIError("PERMISSION_DENIED", message, ExitCode.PERMISSION_DENIED);
297
+ }
298
+ if (verdict === void 0 && message.includes("Missing private key parameter in JWK")) {
299
+ return missingPrivateKeyError();
279
300
  }
280
- if (error instanceof CLIError) return error;
281
301
  if (error instanceof ProfileLockTimeoutError) {
282
302
  return new CLIError(
283
303
  "PROFILE_LOCK_TIMEOUT",
@@ -289,13 +309,13 @@ function wrapError(error) {
289
309
  if (error instanceof ProfileDeletedError) {
290
310
  return new CLIError("PROFILE_NOT_FOUND", message);
291
311
  }
292
- if (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired")) {
312
+ if (verdict === void 0 && (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired"))) {
293
313
  return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
294
314
  }
295
315
  if (message.includes("NOT_FOUND") || message.includes("KV_NOT_FOUND")) {
296
316
  return new CLIError("NOT_FOUND", message, ExitCode.NOT_FOUND);
297
317
  }
298
- if (message.includes("PERMISSION_DENIED")) {
318
+ if (verdict === void 0 && message.includes("PERMISSION_DENIED")) {
299
319
  return new CLIError("PERMISSION_DENIED", message, ExitCode.PERMISSION_DENIED);
300
320
  }
301
321
  if (message.includes("ECONNREFUSED") || message.includes("ETIMEDOUT") || message.includes("fetch failed")) {
@@ -303,39 +323,51 @@ function wrapError(error) {
303
323
  }
304
324
  return new CLIError("ERROR", message, ExitCode.ERROR);
305
325
  }
326
+ function missingPrivateKeyError() {
327
+ const profileName = activeProfileName ?? process.env.TC_PROFILE ?? DEFAULT_PROFILE;
328
+ return new CLIError(
329
+ "AUTH_REQUIRED",
330
+ `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
331
+ ExitCode.AUTH_REQUIRED,
332
+ { hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey` }
333
+ );
334
+ }
306
335
  function handleError(error) {
307
336
  const cliError = wrapError(error);
308
337
  const prebuilt = typeof cliError.metadata?.hint === "string" ? cliError.metadata.hint : void 0;
309
338
  const hint = prebuilt ?? buildAuthHint(cliError) ?? (cliError.code === "NETWORK_ERROR" ? buildNetworkHint() : void 0);
310
- outputError(cliError.code, cliError.message, hint);
339
+ const authMeta = cliError.code === "AUTH_REQUIRED" || cliError.code === "PERMISSION_DENIED" ? cliError.metadata : void 0;
340
+ const meta = {};
341
+ if (cliError.status !== void 0 && authMeta !== void 0) meta.status = cliError.status;
342
+ const capability = validatedCapabilityOf({ meta: authMeta });
343
+ if (capability) {
344
+ meta.resource = capability.resource;
345
+ meta.requiredAction = capability.requiredAction;
346
+ }
347
+ outputError(cliError.code, cliError.message, hint, Object.keys(meta).length ? meta : void 0);
311
348
  process.exit(cliError.exitCode);
312
349
  }
313
350
  function buildAuthHint(error) {
314
- const resource = error.metadata?.resource;
315
- const requiredAction = error.metadata?.requiredAction;
316
- if (typeof resource !== "string" || typeof requiredAction !== "string") {
317
- return void 0;
318
- }
319
- const spec = capSpecFromAuthMeta(resource, requiredAction);
351
+ const capability = validatedCapabilityOf({ meta: error.metadata });
352
+ if (!capability) return void 0;
353
+ const spec = capSpecFromAuthMeta(capability.resource, capability.requiredAction);
320
354
  if (!spec) return void 0;
321
355
  return [
322
356
  "The active session is missing a TinyCloud capability.",
323
- `Request it with: tc auth request --cap "${spec}"`,
357
+ `Request it with: tc auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
324
358
  "Then retry the original command."
325
359
  ].join("\n");
326
360
  }
327
361
  function capSpecFromAuthMeta(resource, action) {
328
- const slash = resource.indexOf("/");
329
- if (slash <= 0 || slash === resource.length - 1) return void 0;
330
- const spaceUri = resource.slice(0, slash);
331
- const rest = resource.slice(slash + 1);
332
- const nextSlash = rest.indexOf("/");
333
- if (nextSlash <= 0) return void 0;
334
- const serviceShort = rest.slice(0, nextSlash);
335
- const path = rest.slice(nextSlash + 1);
336
- const actionName = action.includes("/") ? action.slice(action.indexOf("/") + 1) : action;
337
- const spaceName = spaceUri.startsWith("tinycloud:") ? spaceUri.slice(spaceUri.lastIndexOf(":") + 1) : spaceUri;
338
- return `tinycloud.${serviceShort}:${spaceName}:${path}:${actionName}`;
362
+ const slash = action.indexOf("/");
363
+ if (slash < 0) return void 0;
364
+ const longService = action.slice(0, slash);
365
+ const serviceShort = SERVICE_LONG_TO_SHORT[longService];
366
+ if (!serviceShort) return void 0;
367
+ const parsed = parseCapabilityResource(resource, serviceShort);
368
+ if (!parsed) return void 0;
369
+ const spaceName = parsed.space.slice(parsed.space.lastIndexOf(":") + 1);
370
+ return `${longService}:${spaceName}:${parsed.path}:${action.slice(slash + 1)}`;
339
371
  }
340
372
  function buildNetworkHint() {
341
373
  const readHost = (name) => {
@@ -389,7 +421,9 @@ var init_errors = __esm({
389
421
  this.exitCode = exitCode;
390
422
  this.metadata = metadata;
391
423
  this.name = "CLIError";
424
+ if (typeof metadata?.status === "number" && Number.isInteger(metadata.status) && metadata.status >= 400 && metadata.status <= 599) this.status = metadata.status;
392
425
  }
426
+ status;
393
427
  };
394
428
  }
395
429
  });
@@ -1880,6 +1914,7 @@ var CAPABILITIES2 = deriveServiceConstants("capabilities");
1880
1914
  var HOOKS = deriveServiceConstants("hooks");
1881
1915
  var ENCRYPTION = deriveServiceConstants("encryption");
1882
1916
  var SPACE = deriveServiceConstants("space");
1917
+ var CAPABILITY_REGISTRY = CAPABILITIES;
1883
1918
  var DEFAULT_MANIFEST_SPACE = "applications";
1884
1919
  var ACCOUNT_REGISTRY_SPACE = "account";
1885
1920
  var ACCOUNT_REGISTRY_PATH = "applications/";
@@ -6820,6 +6855,75 @@ var ServiceSessionSchema = external_exports.object({
6820
6855
  /** The session key JWK (required for invoke) */
6821
6856
  jwk: external_exports.object({}).passthrough()
6822
6857
  });
6858
+ var URN_PREFIX = "urn:tinycloud:encryption:";
6859
+ var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
6860
+ var PKH_EIP155_DID_RE = /^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/;
6861
+ var NetworkIdError = class extends Error {
6862
+ constructor(message) {
6863
+ super(message);
6864
+ this.name = "NetworkIdError";
6865
+ }
6866
+ };
6867
+ function parseNetworkId(networkId) {
6868
+ if (typeof networkId !== "string" || networkId.length === 0) {
6869
+ throw new NetworkIdError("networkId must be a non-empty string");
6870
+ }
6871
+ if (!networkId.startsWith(URN_PREFIX)) {
6872
+ throw new NetworkIdError(
6873
+ `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
6874
+ );
6875
+ }
6876
+ const body = networkId.slice(URN_PREFIX.length);
6877
+ const lastColon = body.lastIndexOf(":");
6878
+ if (lastColon <= 0 || lastColon === body.length - 1) {
6879
+ throw new NetworkIdError(
6880
+ `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`
6881
+ );
6882
+ }
6883
+ const ownerDid = body.slice(0, lastColon);
6884
+ const name = body.slice(lastColon + 1);
6885
+ if (!ownerDid.startsWith("did:")) {
6886
+ throw new NetworkIdError(
6887
+ `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`
6888
+ );
6889
+ }
6890
+ const didParts = ownerDid.split(":");
6891
+ if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
6892
+ throw new NetworkIdError(
6893
+ `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`
6894
+ );
6895
+ }
6896
+ if (!NETWORK_NAME_RE.test(name)) {
6897
+ throw new NetworkIdError(
6898
+ `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
6899
+ );
6900
+ }
6901
+ return { networkId, ownerDid, name };
6902
+ }
6903
+ function parsePkhOwnerDid(ownerDid) {
6904
+ const match = ownerDid.match(PKH_EIP155_DID_RE);
6905
+ if (!match) return null;
6906
+ return {
6907
+ chainId: match[1],
6908
+ address: match[2].toLowerCase()
6909
+ };
6910
+ }
6911
+ function ownerDidMatches(a, b) {
6912
+ const aPkh = parsePkhOwnerDid(a);
6913
+ const bPkh = parsePkhOwnerDid(b);
6914
+ if (aPkh && bPkh) {
6915
+ return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;
6916
+ }
6917
+ return a === b;
6918
+ }
6919
+ function networkDiscoveryKey(name) {
6920
+ if (!NETWORK_NAME_RE.test(name)) {
6921
+ throw new NetworkIdError(
6922
+ `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
6923
+ );
6924
+ }
6925
+ return `.well-known/encryption/network/${name}`;
6926
+ }
6823
6927
  function parsePermissionHint(value) {
6824
6928
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
6825
6929
  const candidate = value;
@@ -6887,6 +6991,101 @@ function parseAuthError(responseText) {
6887
6991
  function authUnauthorizedError(service, message, meta) {
6888
6992
  return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });
6889
6993
  }
6994
+ var grantableActions = new Set(
6995
+ CAPABILITY_REGISTRY.map(({ urn }) => urn).filter((urn) => !urn.endsWith("/*"))
6996
+ );
6997
+ var capabilityServices = new Set(
6998
+ CAPABILITY_REGISTRY.map(({ service }) => service.slice("tinycloud.".length))
6999
+ );
7000
+ function parseCapabilityResource2(resource, service) {
7001
+ if (resource.length === 0 || resource.length > 1024 || !capabilityServices.has(service) || /[\s\p{C}*#]/u.test(resource) || resource.includes("://")) return void 0;
7002
+ const segments = resource.split("/");
7003
+ if (!/^tinycloud:[A-Za-z0-9][A-Za-z0-9:._-]*$/.test(segments[0])) return void 0;
7004
+ const serviceIndex = segments.findIndex(
7005
+ (segment, index) => index > 0 && capabilityServices.has(segment)
7006
+ );
7007
+ if (serviceIndex < 1 || segments[serviceIndex] !== service) return void 0;
7008
+ if (segments.slice(1, serviceIndex).some(
7009
+ (segment) => segment.length === 0 || segment === "." || segment === ".."
7010
+ )) return void 0;
7011
+ const pathSegments = segments.slice(serviceIndex + 1);
7012
+ if (pathSegments.some((segment) => segment === "." || segment === "..")) return void 0;
7013
+ if (service !== "kv" && (pathSegments.length === 0 || pathSegments[pathSegments.length - 1] === "")) return void 0;
7014
+ return {
7015
+ space: segments.slice(0, serviceIndex).join("/"),
7016
+ path: pathSegments.join("/")
7017
+ };
7018
+ }
7019
+ function safeCapabilityResource(value, service, action) {
7020
+ if (typeof value !== "string" || value.length === 0 || value.length > 1024) return false;
7021
+ if (service === "encryption" && value.startsWith("urn:tinycloud:encryption:")) {
7022
+ if (!/^[A-Za-z0-9_][A-Za-z0-9_.:/-]*$/.test(value) || value.includes("//") || value.split("/").some((segment) => segment === "." || segment === "..")) return false;
7023
+ try {
7024
+ parseNetworkId(value);
7025
+ return true;
7026
+ } catch {
7027
+ return false;
7028
+ }
7029
+ }
7030
+ const parsed = parseCapabilityResource2(value, service);
7031
+ return parsed !== void 0 && (!parsed.path.endsWith("/") || service === "kv" && action === "tinycloud.kv/list") && (parsed.path !== "" || service === "kv" && action === "tinycloud.kv/list");
7032
+ }
7033
+ function validatedCapabilityOf2(error) {
7034
+ const verdict = authorizationVerdictOf2(error);
7035
+ if (verdict !== "unauthenticated" && verdict !== "forbidden") return void 0;
7036
+ const seen = /* @__PURE__ */ new Set();
7037
+ let node = error;
7038
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
7039
+ seen.add(node);
7040
+ const record = node;
7041
+ const status = errorStatusOf(record);
7042
+ const meta = record.meta;
7043
+ if ((status === 401 || status === 403 || status === void 0 && record.code === ErrorCodes.AUTH_UNAUTHORIZED) && typeof meta === "object" && meta !== null && !Array.isArray(meta)) {
7044
+ const metadata = meta;
7045
+ const { resource, requiredAction } = metadata;
7046
+ if (typeof requiredAction === "string" && grantableActions.has(requiredAction)) {
7047
+ const actionService = requiredAction.slice("tinycloud.".length, requiredAction.indexOf("/"));
7048
+ const explicitService = record.service === void 0 ? metadata.service : record.service;
7049
+ if (safeCapabilityResource(resource, actionService, requiredAction) && (explicitService === void 0 || explicitService === actionService || explicitService === `tinycloud.${actionService}`)) {
7050
+ return { resource, requiredAction };
7051
+ }
7052
+ }
7053
+ }
7054
+ node = record.cause;
7055
+ }
7056
+ return void 0;
7057
+ }
7058
+ var MAX_CAUSE_DEPTH = 8;
7059
+ function errorStatusOf(node) {
7060
+ const meta = node.meta;
7061
+ const candidates = [
7062
+ node.status,
7063
+ node.statusCode,
7064
+ typeof meta === "object" && meta !== null ? meta.status : void 0
7065
+ ];
7066
+ for (const candidate of candidates) {
7067
+ if (typeof candidate === "number" && Number.isInteger(candidate) && candidate >= 400 && candidate <= 599) {
7068
+ return candidate;
7069
+ }
7070
+ }
7071
+ return void 0;
7072
+ }
7073
+ function authorizationVerdictOf2(error) {
7074
+ const seen = /* @__PURE__ */ new Set();
7075
+ let sawUnauthorizedCode = false;
7076
+ let node = error;
7077
+ for (let depth = 0; depth < MAX_CAUSE_DEPTH && typeof node === "object" && node !== null && !seen.has(node); depth += 1) {
7078
+ seen.add(node);
7079
+ const record = node;
7080
+ const status = errorStatusOf(record);
7081
+ if (status === 401) return "unauthenticated";
7082
+ if (status === 403) return "forbidden";
7083
+ if (status !== void 0) return "other";
7084
+ if (record.code === ErrorCodes.AUTH_UNAUTHORIZED) sawUnauthorizedCode = true;
7085
+ node = record.cause;
7086
+ }
7087
+ return sawUnauthorizedCode ? "forbidden" : void 0;
7088
+ }
6890
7089
  function storageQuotaExceededError(service, message, meta) {
6891
7090
  return {
6892
7091
  code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
@@ -7582,12 +7781,14 @@ var KVService = class extends BaseService {
7582
7781
  signal: this.combineSignals(options?.signal)
7583
7782
  });
7584
7783
  if (!response.ok) {
7585
- const errorText = await response.text();
7784
+ const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
7586
7785
  if (response.status === 401 || response.status === 403) {
7587
7786
  return this.authorizationFailure(
7588
7787
  `Failed to batch read ${keys.length} key(s)`,
7589
7788
  response,
7590
- errorText
7789
+ errorText,
7790
+ paths,
7791
+ action
7591
7792
  );
7592
7793
  }
7593
7794
  if (response.status === 413) {
@@ -7677,7 +7878,7 @@ var KVService = class extends BaseService {
7677
7878
  return text;
7678
7879
  }
7679
7880
  }
7680
- async createSignedReadUrlError(response, key) {
7881
+ async createSignedReadUrlError(response, key, path) {
7681
7882
  let errorText = response.statusText;
7682
7883
  try {
7683
7884
  const text = await response.text();
@@ -7690,7 +7891,9 @@ var KVService = class extends BaseService {
7690
7891
  return this.authorizationFailure(
7691
7892
  `Failed to create signed read URL for key ${JSON.stringify(key)}`,
7692
7893
  response,
7693
- errorText
7894
+ errorText,
7895
+ [path],
7896
+ KVAction.GET
7694
7897
  );
7695
7898
  }
7696
7899
  const code3 = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
@@ -7703,21 +7906,42 @@ var KVService = class extends BaseService {
7703
7906
  )
7704
7907
  );
7705
7908
  }
7909
+ /** A known 401/403 keeps its typed status even if reading its body fails. */
7910
+ async readAuthorizationText(response) {
7911
+ try {
7912
+ return await response.text();
7913
+ } catch {
7914
+ return "";
7915
+ }
7916
+ }
7706
7917
  /**
7707
7918
  * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
7708
7919
  * status and the server text (`<context>: <status> - <text>`, the same
7709
7920
  * shape as every other KV failure) so callers that rethrow only the message
7710
7921
  * still see the status; `meta.status` carries it for typed callers.
7711
- * Resource/ability hints are parsed from the raw body, never the message.
7922
+ * Server hints must match the requested path/action (whether the node sends
7923
+ * a relative path or full resource); metadata uses only the request-derived
7924
+ * canonical resource under the authenticated session's space.
7712
7925
  */
7713
- authorizationFailure(context, response, errorText, extraMeta = {}) {
7926
+ authorizationFailure(context, response, errorText, paths, expectedAction, extraMeta = {}) {
7714
7927
  const { resource, action } = parseAuthError(errorText);
7928
+ const requestedPath = paths.find(
7929
+ (path) => resource === path || resource === `${this.context.session.spaceId}/kv/${path}`
7930
+ );
7931
+ const capability = requestedPath !== void 0 && action === expectedAction ? validatedCapabilityOf2({
7932
+ service: "kv",
7933
+ code: ErrorCodes.AUTH_UNAUTHORIZED,
7934
+ meta: {
7935
+ status: response.status,
7936
+ resource: `${this.context.session.spaceId}/kv/${requestedPath}`,
7937
+ requiredAction: action
7938
+ }
7939
+ }) : void 0;
7715
7940
  const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
7716
7941
  return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
7717
7942
  status: response.status,
7718
- ...action && { requiredAction: action },
7719
- ...resource && { resource },
7720
- ...extraMeta
7943
+ ...extraMeta,
7944
+ ...capability
7721
7945
  }));
7722
7946
  }
7723
7947
  normalizeSignedReadUrlResponse(data) {
@@ -7761,13 +7985,24 @@ var KVService = class extends BaseService {
7761
7985
  );
7762
7986
  if (!response.ok) {
7763
7987
  if (response.status === 401 || response.status === 403) {
7764
- const errorText2 = await response.text();
7988
+ const errorText2 = await this.readAuthorizationText(response);
7765
7989
  const permissionHint = parsePermissionHintFromErrorText(errorText2);
7990
+ const structuredCapability = permissionHint?.service === "tinycloud.kv" && permissionHint.space === this.context.session.spaceId && permissionHint.path === path ? validatedCapabilityOf2({
7991
+ service: "kv",
7992
+ code: ErrorCodes.AUTH_UNAUTHORIZED,
7993
+ meta: {
7994
+ status: response.status,
7995
+ resource: `${this.context.session.spaceId}/kv/${path}`,
7996
+ requiredAction: KVAction.GET
7997
+ }
7998
+ }) : void 0;
7766
7999
  return this.authorizationFailure(
7767
8000
  `Failed to get key ${JSON.stringify(key)}`,
7768
8001
  response,
7769
8002
  errorText2,
7770
- permissionHint === void 0 ? {} : { permissionHint }
8003
+ [path],
8004
+ KVAction.GET,
8005
+ structuredCapability ? { permissionHint, ...structuredCapability } : {}
7771
8006
  );
7772
8007
  }
7773
8008
  if (response.status === 404) {
@@ -7844,7 +8079,9 @@ var KVService = class extends BaseService {
7844
8079
  return this.authorizationFailure(
7845
8080
  `Failed to put key ${JSON.stringify(key)}`,
7846
8081
  response,
7847
- await response.text()
8082
+ await this.readAuthorizationText(response),
8083
+ [path],
8084
+ KVAction.PUT
7848
8085
  );
7849
8086
  }
7850
8087
  if (!response.ok) {
@@ -7973,7 +8210,9 @@ var KVService = class extends BaseService {
7973
8210
  return this.authorizationFailure(
7974
8211
  `Failed to batch put ${items.length} key(s)`,
7975
8212
  response,
7976
- errorText
8213
+ errorText,
8214
+ paths,
8215
+ KVAction.PUT
7977
8216
  );
7978
8217
  }
7979
8218
  const quotaError = this.handleQuotaErrorResponse(
@@ -8094,11 +8333,13 @@ var KVService = class extends BaseService {
8094
8333
  }
8095
8334
  );
8096
8335
  if (!response.ok) {
8097
- if (response.status === 401) {
8336
+ if (response.status === 401 || response.status === 403) {
8098
8337
  return this.authorizationFailure(
8099
8338
  "Failed to list keys",
8100
8339
  response,
8101
- await response.text()
8340
+ await this.readAuthorizationText(response),
8341
+ [listPath],
8342
+ KVAction.LIST
8102
8343
  );
8103
8344
  }
8104
8345
  const errorText = await response.text();
@@ -8147,11 +8388,13 @@ var KVService = class extends BaseService {
8147
8388
  options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
8148
8389
  );
8149
8390
  if (!response.ok) {
8150
- if (response.status === 401) {
8391
+ if (response.status === 401 || response.status === 403) {
8151
8392
  return this.authorizationFailure(
8152
8393
  `Failed to delete key ${JSON.stringify(key)}`,
8153
8394
  response,
8154
- await response.text()
8395
+ await this.readAuthorizationText(response),
8396
+ [path],
8397
+ KVAction.DELETE
8155
8398
  );
8156
8399
  }
8157
8400
  if (response.status === 404) {
@@ -8209,11 +8452,13 @@ var KVService = class extends BaseService {
8209
8452
  options?.signal
8210
8453
  );
8211
8454
  if (!response.ok) {
8212
- if (response.status === 401) {
8455
+ if (response.status === 401 || response.status === 403) {
8213
8456
  return this.authorizationFailure(
8214
8457
  `Failed to get metadata for key ${JSON.stringify(key)}`,
8215
8458
  response,
8216
- await response.text()
8459
+ await this.readAuthorizationText(response),
8460
+ [path],
8461
+ KVAction.HEAD
8217
8462
  );
8218
8463
  }
8219
8464
  if (response.status === 404) {
@@ -8280,7 +8525,7 @@ var KVService = class extends BaseService {
8280
8525
  signal: this.combineSignals(options?.signal)
8281
8526
  });
8282
8527
  if (!response.ok) {
8283
- return this.createSignedReadUrlError(response, key);
8528
+ return this.createSignedReadUrlError(response, key, path);
8284
8529
  }
8285
8530
  const signedUrl = this.normalizeSignedReadUrlResponse(
8286
8531
  await response.json()
@@ -8504,14 +8749,15 @@ var SQLService = class extends BaseService {
8504
8749
  };
8505
8750
  if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
8506
8751
  if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
8752
+ const action = this.actionForSql(sql, SQLAction.READ);
8507
8753
  const response = await this.invokeSQL(
8508
8754
  dbName,
8509
- this.actionForSql(sql, SQLAction.READ),
8755
+ action,
8510
8756
  body,
8511
8757
  options?.signal
8512
8758
  );
8513
8759
  if (!response.ok) {
8514
- return this.handleErrorResponse(response, "query");
8760
+ return this.handleErrorResponse(response, "query", dbName, [action]);
8515
8761
  }
8516
8762
  const data = await response.json();
8517
8763
  return ok(data);
@@ -8540,14 +8786,15 @@ var SQLService = class extends BaseService {
8540
8786
  (statement) => this.actionForSql(statement, SQLAction.SCHEMA)
8541
8787
  )
8542
8788
  ];
8789
+ const requestedActions2 = this.dedupeActions(actions);
8543
8790
  const response = await this.invokeSQL(
8544
8791
  dbName,
8545
- this.dedupeActions(actions),
8792
+ requestedActions2,
8546
8793
  body,
8547
8794
  options?.signal
8548
8795
  );
8549
8796
  if (!response.ok) {
8550
- return this.handleErrorResponse(response, "execute");
8797
+ return this.handleErrorResponse(response, "execute", dbName, requestedActions2);
8551
8798
  }
8552
8799
  const data = await response.json();
8553
8800
  return ok(data);
@@ -8562,14 +8809,15 @@ var SQLService = class extends BaseService {
8562
8809
  return err(authRequiredError("sql"));
8563
8810
  }
8564
8811
  try {
8812
+ const requestedActions2 = this.actionsForSqlBatch(statements);
8565
8813
  const response = await this.invokeSQL(
8566
8814
  dbName,
8567
- this.actionsForSqlBatch(statements),
8815
+ requestedActions2,
8568
8816
  { action: "batch", statements },
8569
8817
  options?.signal
8570
8818
  );
8571
8819
  if (!response.ok) {
8572
- return this.handleErrorResponse(response, "batch");
8820
+ return this.handleErrorResponse(response, "batch", dbName, requestedActions2);
8573
8821
  }
8574
8822
  const data = await response.json();
8575
8823
  return ok(data);
@@ -8591,7 +8839,7 @@ var SQLService = class extends BaseService {
8591
8839
  options?.signal
8592
8840
  );
8593
8841
  if (!response.ok) {
8594
- return this.handleErrorResponse(response, "executeStatement");
8842
+ return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
8595
8843
  }
8596
8844
  const data = await response.json();
8597
8845
  return ok(data);
@@ -8613,7 +8861,7 @@ var SQLService = class extends BaseService {
8613
8861
  options?.signal
8614
8862
  );
8615
8863
  if (!response.ok) {
8616
- return this.handleErrorResponse(response, "export");
8864
+ return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
8617
8865
  }
8618
8866
  const resp = response;
8619
8867
  if (typeof resp.blob === "function") {
@@ -8747,7 +8995,7 @@ var SQLService = class extends BaseService {
8747
8995
  }))
8748
8996
  );
8749
8997
  }
8750
- async handleErrorResponse(response, operation) {
8998
+ async handleErrorResponse(response, operation, dbName, requestedActions2) {
8751
8999
  const errorText = await response.text();
8752
9000
  const meta = responseErrorMeta(response.status, response.statusText, errorText);
8753
9001
  if (response.status === 402) {
@@ -8766,9 +9014,11 @@ var SQLService = class extends BaseService {
8766
9014
  errorBody
8767
9015
  );
8768
9016
  if (response.status === 401) {
8769
- const { resource, action } = parseAuthError(errorText);
8770
- if (action) meta.requiredAction = action;
8771
- if (resource) meta.resource = resource;
9017
+ const { action } = parseAuthError(errorText);
9018
+ if (action && requestedActions2.includes(action)) {
9019
+ meta.requiredAction = action;
9020
+ meta.resource = `${this.context.session.spaceId}/sql/${dbName}`;
9021
+ }
8772
9022
  }
8773
9023
  return err(
8774
9024
  serviceError(errorCode2, message, "sql", { meta })
@@ -11517,75 +11767,6 @@ function canonicalHashHex(sha2563, value) {
11517
11767
  const canonical = canonicalize(value);
11518
11768
  return hexEncode2(sha2563(utf8Encode(canonical)));
11519
11769
  }
11520
- var URN_PREFIX = "urn:tinycloud:encryption:";
11521
- var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
11522
- var PKH_EIP155_DID_RE = /^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/;
11523
- var NetworkIdError = class extends Error {
11524
- constructor(message) {
11525
- super(message);
11526
- this.name = "NetworkIdError";
11527
- }
11528
- };
11529
- function parseNetworkId(networkId) {
11530
- if (typeof networkId !== "string" || networkId.length === 0) {
11531
- throw new NetworkIdError("networkId must be a non-empty string");
11532
- }
11533
- if (!networkId.startsWith(URN_PREFIX)) {
11534
- throw new NetworkIdError(
11535
- `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
11536
- );
11537
- }
11538
- const body = networkId.slice(URN_PREFIX.length);
11539
- const lastColon = body.lastIndexOf(":");
11540
- if (lastColon <= 0 || lastColon === body.length - 1) {
11541
- throw new NetworkIdError(
11542
- `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`
11543
- );
11544
- }
11545
- const ownerDid = body.slice(0, lastColon);
11546
- const name = body.slice(lastColon + 1);
11547
- if (!ownerDid.startsWith("did:")) {
11548
- throw new NetworkIdError(
11549
- `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`
11550
- );
11551
- }
11552
- const didParts = ownerDid.split(":");
11553
- if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
11554
- throw new NetworkIdError(
11555
- `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`
11556
- );
11557
- }
11558
- if (!NETWORK_NAME_RE.test(name)) {
11559
- throw new NetworkIdError(
11560
- `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
11561
- );
11562
- }
11563
- return { networkId, ownerDid, name };
11564
- }
11565
- function parsePkhOwnerDid(ownerDid) {
11566
- const match = ownerDid.match(PKH_EIP155_DID_RE);
11567
- if (!match) return null;
11568
- return {
11569
- chainId: match[1],
11570
- address: match[2].toLowerCase()
11571
- };
11572
- }
11573
- function ownerDidMatches(a, b) {
11574
- const aPkh = parsePkhOwnerDid(a);
11575
- const bPkh = parsePkhOwnerDid(b);
11576
- if (aPkh && bPkh) {
11577
- return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;
11578
- }
11579
- return a === b;
11580
- }
11581
- function networkDiscoveryKey(name) {
11582
- if (!NETWORK_NAME_RE.test(name)) {
11583
- throw new NetworkIdError(
11584
- `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
11585
- );
11586
- }
11587
- return `.well-known/encryption/network/${name}`;
11588
- }
11589
11770
  var DEFAULT_ENCRYPTION_ALG = "x25519-aes256gcm/v1";
11590
11771
  var ENVELOPE_VERSION = 1;
11591
11772
  var DEFAULT_KEY_VERSION = 1;
@@ -12421,7 +12602,7 @@ var SERVICE_SHORT_TO_LONG = Object.freeze({
12421
12602
  });
12422
12603
  var ENCRYPTION_PERMISSION_SERVICE2 = "tinycloud.encryption";
12423
12604
  var ENCRYPTION_MANIFEST_SPACE2 = "encryption";
12424
- var SERVICE_LONG_TO_SHORT = Object.freeze(
12605
+ var SERVICE_LONG_TO_SHORT2 = Object.freeze(
12425
12606
  Object.fromEntries(
12426
12607
  Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
12427
12608
  )
@@ -13955,7 +14136,7 @@ async function authenticatedNode(cmd) {
13955
14136
  }
13956
14137
  function assertOk(result) {
13957
14138
  if (!result.ok) {
13958
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
14139
+ throw cliErrorFromService(result.error);
13959
14140
  }
13960
14141
  }
13961
14142
  async function loadManifestSource(source) {
@@ -15967,7 +16148,7 @@ async function importRequestBoundDelegation(ctx, artifact) {
15967
16148
  ExitCode.ERROR
15968
16149
  );
15969
16150
  case "error":
15970
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16151
+ throw cliErrorFromService(result.error);
15971
16152
  }
15972
16153
  }
15973
16154
  function isStoredDelegationLike(value) {
@@ -16702,7 +16883,7 @@ function registerDelegationCommand(program) {
16702
16883
  expiry
16703
16884
  });
16704
16885
  if (!result.ok) {
16705
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16886
+ throw cliErrorFromService(result.error);
16706
16887
  }
16707
16888
  outputJson({
16708
16889
  cid: result.data.cid,
@@ -16722,7 +16903,7 @@ function registerDelegationCommand(program) {
16722
16903
  const node = await ensureAuthenticated(ctx);
16723
16904
  const result = await node.delegationManager.list();
16724
16905
  if (!result.ok) {
16725
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16906
+ throw cliErrorFromService(result.error);
16726
16907
  }
16727
16908
  let delegations = result.data;
16728
16909
  if (options.granted) {
@@ -16768,7 +16949,7 @@ function registerDelegationCommand(program) {
16768
16949
  const node = await ensureAuthenticated(ctx);
16769
16950
  const result = await node.delegationManager.revoke(cid);
16770
16951
  if (!result.ok) {
16771
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16952
+ throw cliErrorFromService(result.error);
16772
16953
  }
16773
16954
  outputJson({ cid, revoked: true });
16774
16955
  } catch (error) {
@@ -16893,7 +17074,6 @@ function registerDoctorCommand(program) {
16893
17074
  init_profiles();
16894
17075
  init_formatter();
16895
17076
  init_errors();
16896
- init_constants();
16897
17077
  import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
16898
17078
  import { resolve } from "path";
16899
17079
  init_theme();
@@ -16910,7 +17090,7 @@ function registerDuckdbCommand(program) {
16910
17090
  () => node.duckdb.db(options.db).query(sqlStr, params)
16911
17091
  );
16912
17092
  if (!result.ok) {
16913
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17093
+ throw cliErrorFromService(result.error);
16914
17094
  }
16915
17095
  const { columns, rows, rowCount } = result.data;
16916
17096
  if (shouldOutputJson()) {
@@ -16942,7 +17122,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16942
17122
  () => node.duckdb.db(options.db).execute(sqlStr, params)
16943
17123
  );
16944
17124
  if (!result.ok) {
16945
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17125
+ throw cliErrorFromService(result.error);
16946
17126
  }
16947
17127
  outputJson({ changes: result.data.changes });
16948
17128
  } catch (error) {
@@ -16959,7 +17139,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16959
17139
  () => node.duckdb.db(options.db).describe()
16960
17140
  );
16961
17141
  if (!result.ok) {
16962
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17142
+ throw cliErrorFromService(result.error);
16963
17143
  }
16964
17144
  const schema = result.data;
16965
17145
  if (shouldOutputJson()) {
@@ -17004,7 +17184,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
17004
17184
  () => node.duckdb.db(options.db).export()
17005
17185
  );
17006
17186
  if (!result.ok) {
17007
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17187
+ throw cliErrorFromService(result.error);
17008
17188
  }
17009
17189
  const blob = result.data;
17010
17190
  const buffer = Buffer.from(await blob.arrayBuffer());
@@ -17031,7 +17211,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
17031
17211
  () => node.duckdb.db(options.db).import(bytes)
17032
17212
  );
17033
17213
  if (!result.ok) {
17034
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17214
+ throw cliErrorFromService(result.error);
17035
17215
  }
17036
17216
  outputJson({
17037
17217
  file: filePath,
@@ -17168,7 +17348,7 @@ init_theme();
17168
17348
  async function throwKvError(error, spaceUri, profileName) {
17169
17349
  const hosted = await unhostedSpaceError(error, spaceUri, profileName);
17170
17350
  if (hosted) throw hosted;
17171
- throw new CLIError(error.code, error.message, ExitCode.ERROR);
17351
+ throw cliErrorFromService(error);
17172
17352
  }
17173
17353
  function isByteCount(value) {
17174
17354
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -18685,7 +18865,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18685
18865
  operation: () => secrets2.list(scopeOptions)
18686
18866
  });
18687
18867
  if (!result.ok) {
18688
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
18868
+ throw cliErrorFromService(result.error);
18689
18869
  }
18690
18870
  const secretNames = Array.isArray(result.data) ? result.data : [];
18691
18871
  outputJson({
@@ -18802,7 +18982,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18802
18982
  operation: () => secrets2.put(name, secretValue, scopeOptions)
18803
18983
  });
18804
18984
  if (!result.ok) {
18805
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
18985
+ throw cliErrorFromService(result.error);
18806
18986
  }
18807
18987
  outputJson({ name, written: true });
18808
18988
  } catch (error) {
@@ -18828,7 +19008,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18828
19008
  operation: () => secrets2.delete(name, scopeOptions)
18829
19009
  });
18830
19010
  if (!result.ok) {
18831
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
19011
+ throw cliErrorFromService(result.error);
18832
19012
  }
18833
19013
  outputJson({ name, deleted: true });
18834
19014
  } catch (error) {
@@ -27511,7 +27691,6 @@ async function notifyShare(input) {
27511
27691
  };
27512
27692
  }
27513
27693
  let attempts = 0;
27514
- let lastError;
27515
27694
  while (attempts < attemptsLimit) {
27516
27695
  if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
27517
27696
  attempts += 1;
@@ -27538,10 +27717,19 @@ async function notifyShare(input) {
27538
27717
  reason: "delivery-window-expired"
27539
27718
  };
27540
27719
  }
27541
- lastError = error;
27720
+ if (error !== null && typeof error === "object" && "status" in error && (error.status === 401 || error.status === 403)) {
27721
+ return {
27722
+ protocol: "tinycloud-share",
27723
+ version: 1,
27724
+ shareId: input.shareId,
27725
+ state: "partial-failure",
27726
+ idempotencyKey,
27727
+ attempts,
27728
+ retryable: false
27729
+ };
27730
+ }
27542
27731
  }
27543
27732
  }
27544
- void lastError;
27545
27733
  return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state: "partial-failure", idempotencyKey, attempts, retryable: true };
27546
27734
  }
27547
27735
  async function defaultIdempotencyKey(shareId, recipient) {
@@ -29543,7 +29731,7 @@ function registerSpaceCommand(program) {
29543
29731
  const node = await ensureAuthenticated(ctx);
29544
29732
  const result = await node.spaces.list();
29545
29733
  if (!result.ok) {
29546
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
29734
+ throw cliErrorFromService(result.error);
29547
29735
  }
29548
29736
  if (shouldOutputJson()) {
29549
29737
  outputJson({ spaces: result.data, count: result.data.length });
@@ -29688,7 +29876,7 @@ async function throwSqlError(error, spaceUri, profileName, prefix) {
29688
29876
  const hosted = await unhostedSpaceError(error, spaceUri, profileName);
29689
29877
  if (hosted) throw hosted;
29690
29878
  const message = prefix ? `${prefix}${error.message}` : error.message;
29691
- throw new CLIError(error.code, message, ExitCode.ERROR, error.meta);
29879
+ throw cliErrorFromService(error, message);
29692
29880
  }
29693
29881
  function registerSqlCommand(program) {
29694
29882
  const sql = program.command("sql").description("SQLite database operations for your TinyCloud space").addHelpText("after", `
@@ -30400,7 +30588,7 @@ async function unlockVault(node, privateKey) {
30400
30588
  const signer = new PrivateKeySigner2(privateKey);
30401
30589
  const result = await node.vault.unlock(signer);
30402
30590
  if (result && !result.ok) {
30403
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30591
+ throw cliErrorFromService(result.error);
30404
30592
  }
30405
30593
  }
30406
30594
  function registerVaultCommand(program) {
@@ -30441,7 +30629,7 @@ function registerVaultCommand(program) {
30441
30629
  }
30442
30630
  const result = await withSpinner(`Writing ${key}...`, () => node.vault.put(key, putValue));
30443
30631
  if (!result.ok) {
30444
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30632
+ throw cliErrorFromService(result.error);
30445
30633
  }
30446
30634
  outputJson({ key, written: true });
30447
30635
  } catch (error) {
@@ -30460,7 +30648,7 @@ function registerVaultCommand(program) {
30460
30648
  if (result.error.code === "NOT_FOUND") {
30461
30649
  throw new CLIError("NOT_FOUND", `Key "${key}" not found`, ExitCode.NOT_FOUND);
30462
30650
  }
30463
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30651
+ throw cliErrorFromService(result.error);
30464
30652
  }
30465
30653
  const data = result.data.data ?? result.data;
30466
30654
  if (options.output) {
@@ -30491,7 +30679,7 @@ function registerVaultCommand(program) {
30491
30679
  await withSpinner("Unlocking vault...", () => unlockVault(node, privateKey));
30492
30680
  const result = await withSpinner(`Deleting ${key}...`, () => node.vault.delete(key));
30493
30681
  if (!result.ok) {
30494
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30682
+ throw cliErrorFromService(result.error);
30495
30683
  }
30496
30684
  outputJson({ key, deleted: true });
30497
30685
  } catch (error) {
@@ -30508,7 +30696,7 @@ function registerVaultCommand(program) {
30508
30696
  const listOptions = options.prefix ? { prefix: options.prefix } : void 0;
30509
30697
  const result = await withSpinner("Listing vault keys...", () => node.vault.list(listOptions));
30510
30698
  if (!result.ok) {
30511
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30699
+ throw cliErrorFromService(result.error);
30512
30700
  }
30513
30701
  const keys = result.data.data ?? result.data;
30514
30702
  const keyList = Array.isArray(keys) ? keys : [];
@@ -30534,7 +30722,7 @@ function registerVaultCommand(program) {
30534
30722
  outputJson({ key, exists: false, metadata: {} });
30535
30723
  return;
30536
30724
  }
30537
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30725
+ throw cliErrorFromService(result.error);
30538
30726
  }
30539
30727
  outputJson({
30540
30728
  key,
@@ -30584,7 +30772,7 @@ function registerVarsCommand(program) {
30584
30772
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
30585
30773
  const result = await withSpinner("Listing variables...", () => prefixedKv.list());
30586
30774
  if (!result.ok) {
30587
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30775
+ throw cliErrorFromService(result.error);
30588
30776
  }
30589
30777
  const rawData = result.data.data ?? result.data;
30590
30778
  const keyList = Array.isArray(rawData) ? rawData : rawData?.keys ?? [];
@@ -30608,7 +30796,7 @@ function registerVarsCommand(program) {
30608
30796
  if (result.error.code === "KV_NOT_FOUND" || result.error.code === "NOT_FOUND") {
30609
30797
  throw new CLIError("NOT_FOUND", `Variable "${name}" not found`, ExitCode.NOT_FOUND);
30610
30798
  }
30611
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30799
+ throw cliErrorFromService(result.error);
30612
30800
  }
30613
30801
  const data = result.data.data;
30614
30802
  let value;
@@ -30666,7 +30854,7 @@ function registerVarsCommand(program) {
30666
30854
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
30667
30855
  const result = await withSpinner(`Setting variable ${name}...`, () => prefixedKv.put(name, payload));
30668
30856
  if (!result.ok) {
30669
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30857
+ throw cliErrorFromService(result.error);
30670
30858
  }
30671
30859
  outputJson({ name, written: true });
30672
30860
  } catch (error) {
@@ -30682,7 +30870,7 @@ function registerVarsCommand(program) {
30682
30870
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
30683
30871
  const result = await withSpinner(`Deleting variable ${name}...`, () => prefixedKv.delete(name));
30684
30872
  if (!result.ok) {
30685
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30873
+ throw cliErrorFromService(result.error);
30686
30874
  }
30687
30875
  outputJson({ name, deleted: true });
30688
30876
  } catch (error) {