@tinycloud/cli 1.0.1-beta.5 → 1.1.0-beta.10

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.
@@ -14,11 +14,11 @@ var __export = (target, all) => {
14
14
  for (var name in all)
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
- var __copyProps = (to, from3, except, desc) => {
18
- if (from3 && typeof from3 === "object" || typeof from3 === "function") {
19
- for (let key of __getOwnPropNames(from3))
17
+ var __copyProps = (to, from4, except, desc) => {
18
+ if (from4 && typeof from4 === "object" || typeof from4 === "function") {
19
+ for (let key of __getOwnPropNames(from4))
20
20
  if (!__hasOwnProp.call(to, key) && key !== except)
21
- __defProp(to, key, { get: () => from3[key], enumerable: !(desc = __getOwnPropDesc(from3, key)) || desc.enumerable });
21
+ __defProp(to, key, { get: () => from4[key], enumerable: !(desc = __getOwnPropDesc(from4, key)) || desc.enumerable });
22
22
  }
23
23
  return to;
24
24
  };
@@ -163,10 +163,10 @@ 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(code2, message, hint) {
166
+ function outputError(code3, message, hint, meta) {
167
167
  if (isInteractive()) {
168
168
  process.stderr.write(
169
- `${theme.error("\u2717")} ${theme.label(code2)}: ${message}
169
+ `${theme.error("\u2717")} ${theme.label(code3)}: ${message}
170
170
  `
171
171
  );
172
172
  if (hint) {
@@ -177,9 +177,10 @@ function outputError(code2, message, hint) {
177
177
  }
178
178
  } else {
179
179
  const payload = {
180
- error: { code: code2, message }
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
  }
@@ -260,24 +261,43 @@ var init_formatter = __esm({
260
261
  // src/output/errors.ts
261
262
  import { readFileSync, readdirSync } from "fs";
262
263
  import { join as join2 } from "path";
263
- import { ProfileLockTimeoutError } from "@tinycloud/operations/state";
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",
@@ -286,13 +306,16 @@ function wrapError(error) {
286
306
  { hint: "Wait for the other command to finish and retry. A crashed process's lock is reclaimed automatically after 30 s." }
287
307
  );
288
308
  }
289
- if (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired")) {
309
+ if (error instanceof ProfileDeletedError) {
310
+ return new CLIError("PROFILE_NOT_FOUND", message);
311
+ }
312
+ if (verdict === void 0 && (message.includes("Not signed in") || message.includes("AUTH_EXPIRED") || message.includes("Session expired"))) {
290
313
  return new CLIError("AUTH_REQUIRED", message, ExitCode.AUTH_REQUIRED);
291
314
  }
292
315
  if (message.includes("NOT_FOUND") || message.includes("KV_NOT_FOUND")) {
293
316
  return new CLIError("NOT_FOUND", message, ExitCode.NOT_FOUND);
294
317
  }
295
- if (message.includes("PERMISSION_DENIED")) {
318
+ if (verdict === void 0 && message.includes("PERMISSION_DENIED")) {
296
319
  return new CLIError("PERMISSION_DENIED", message, ExitCode.PERMISSION_DENIED);
297
320
  }
298
321
  if (message.includes("ECONNREFUSED") || message.includes("ETIMEDOUT") || message.includes("fetch failed")) {
@@ -300,39 +323,51 @@ function wrapError(error) {
300
323
  }
301
324
  return new CLIError("ERROR", message, ExitCode.ERROR);
302
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
+ }
303
335
  function handleError(error) {
304
336
  const cliError = wrapError(error);
305
337
  const prebuilt = typeof cliError.metadata?.hint === "string" ? cliError.metadata.hint : void 0;
306
338
  const hint = prebuilt ?? buildAuthHint(cliError) ?? (cliError.code === "NETWORK_ERROR" ? buildNetworkHint() : void 0);
307
- 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);
308
348
  process.exit(cliError.exitCode);
309
349
  }
310
350
  function buildAuthHint(error) {
311
- const resource = error.metadata?.resource;
312
- const requiredAction = error.metadata?.requiredAction;
313
- if (typeof resource !== "string" || typeof requiredAction !== "string") {
314
- return void 0;
315
- }
316
- 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);
317
354
  if (!spec) return void 0;
318
355
  return [
319
356
  "The active session is missing a TinyCloud capability.",
320
- `Request it with: tc auth request --cap "${spec}"`,
357
+ `Request it with: tc auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
321
358
  "Then retry the original command."
322
359
  ].join("\n");
323
360
  }
324
361
  function capSpecFromAuthMeta(resource, action) {
325
- const slash = resource.indexOf("/");
326
- if (slash <= 0 || slash === resource.length - 1) return void 0;
327
- const spaceUri = resource.slice(0, slash);
328
- const rest = resource.slice(slash + 1);
329
- const nextSlash = rest.indexOf("/");
330
- if (nextSlash <= 0) return void 0;
331
- const serviceShort = rest.slice(0, nextSlash);
332
- const path = rest.slice(nextSlash + 1);
333
- const actionName = action.includes("/") ? action.slice(action.indexOf("/") + 1) : action;
334
- const spaceName = spaceUri.startsWith("tinycloud:") ? spaceUri.slice(spaceUri.lastIndexOf(":") + 1) : spaceUri;
335
- 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)}`;
336
371
  }
337
372
  function buildNetworkHint() {
338
373
  const readHost = (name) => {
@@ -380,13 +415,15 @@ var init_errors = __esm({
380
415
  init_constants();
381
416
  init_formatter();
382
417
  CLIError = class extends Error {
383
- constructor(code2, message, exitCode = ExitCode.ERROR, metadata) {
418
+ constructor(code3, message, exitCode = ExitCode.ERROR, metadata) {
384
419
  super(message);
385
- this.code = code2;
420
+ this.code = code3;
386
421
  this.exitCode = exitCode;
387
422
  this.metadata = metadata;
388
423
  this.name = "CLIError";
424
+ if (typeof metadata?.status === "number" && Number.isInteger(metadata.status) && metadata.status >= 400 && metadata.status <= 599) this.status = metadata.status;
389
425
  }
426
+ status;
390
427
  };
391
428
  }
392
429
  });
@@ -644,6 +681,8 @@ import { join as join3 } from "path";
644
681
  import {
645
682
  profilePath,
646
683
  readSession,
684
+ recordProfileDeletion,
685
+ refuseWriteToDeletedProfile,
647
686
  removeSession,
648
687
  withProfileLock,
649
688
  writeSession
@@ -723,7 +762,7 @@ var init_profiles = __esm({
723
762
  if (!profile) {
724
763
  throw new CLIError(
725
764
  "PROFILE_NOT_FOUND",
726
- `Profile "${name}" does not exist. Run \`tc init\` or \`tc profile create ${name}\` first.`
765
+ `Profile "${name}" does not exist. Create it with \`tc init --name ${name}\`.`
727
766
  );
728
767
  }
729
768
  return profile;
@@ -758,10 +797,12 @@ var init_profiles = __esm({
758
797
  * removed while holding the profile lock, so another writer's critical
759
798
  * section never sees them vanish midway: session and key first, settings
760
799
  * last, so a crash midway never leaves a session or key without its
761
- * profile. `.lock` itself is left to the lock's release; the then-empty
762
- * directory is removed afterwards unless another writer took the lock (or
763
- * wrote) meanwhile. A profile directory that is a symlink is unlinked, its
764
- * target left alone.
800
+ * profile. The deletion is then recorded, so a store write that waited for
801
+ * the lock meanwhile refuses rather than recreating a profile with only a
802
+ * session in it. `.lock` itself is left to the lock's release; the
803
+ * then-empty directory is removed afterwards unless another writer took
804
+ * the lock (or wrote) meanwhile. A profile directory that is a symlink is
805
+ * unlinked, its target left alone.
765
806
  * Throws if the name is not one path segment or names the default profile.
766
807
  */
767
808
  static async deleteProfile(name) {
@@ -782,11 +823,12 @@ var init_profiles = __esm({
782
823
  `Cannot delete the default profile "${name}". Change the default first with \`tc profile default <other>\`.`
783
824
  );
784
825
  }
785
- const isLink = await lstat(profileDir).then((stats) => stats.isSymbolicLink(), (error) => {
786
- if (error.code === "ENOENT") return false;
826
+ const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
827
+ if (error.code === "ENOENT") return "missing";
787
828
  throw error;
788
829
  });
789
- if (isLink) {
830
+ if (kind === "missing") return;
831
+ if (kind === "link") {
790
832
  await rm2(profileDir, { force: true });
791
833
  return;
792
834
  }
@@ -796,6 +838,7 @@ var init_profiles = __esm({
796
838
  if (entry !== ".lock" && entry !== "profile.json") await rm2(join3(profileDir, entry), { recursive: true, force: true });
797
839
  }
798
840
  await rm2(join3(profileDir, "profile.json"), { force: true });
841
+ await recordProfileDeletion(name);
799
842
  });
800
843
  await rmdir(profileDir).catch((error) => {
801
844
  if (error.code !== "ENOENT" && error.code !== "ENOTEMPTY" && error.code !== "EEXIST") throw error;
@@ -808,9 +851,14 @@ var init_profiles = __esm({
808
851
  static async getKey(name) {
809
852
  return readJson(join3(PROFILES_DIR, name, "key.json"));
810
853
  }
811
- /** Saves a JWK key under the profile lock (0600, in an owner-only profile directory). */
854
+ /**
855
+ * Saves a JWK key under the profile lock (0600, in an owner-only profile
856
+ * directory). Refused (PROFILE_NOT_FOUND) if the profile was deleted while
857
+ * this waited for the lock, rather than leaving a key-only profile.
858
+ */
812
859
  static async setKey(name, jwk) {
813
860
  await _ProfileManager.withLock(name, async () => {
861
+ await refuseWriteToDeletedProfile(name);
814
862
  await writeJson(join3(await _ProfileManager.ensureProfileDir(name), "key.json"), jwk);
815
863
  });
816
864
  }
@@ -1325,12 +1373,12 @@ var init_md = __esm({
1325
1373
  _cloneInto(to) {
1326
1374
  to || (to = new this.constructor());
1327
1375
  to.set(...this.get());
1328
- const { blockLen, buffer, length: length3, finished, destroyed, pos } = this;
1376
+ const { blockLen, buffer, length: length4, finished, destroyed, pos } = this;
1329
1377
  to.destroyed = destroyed;
1330
1378
  to.finished = finished;
1331
- to.length = length3;
1379
+ to.length = length4;
1332
1380
  to.pos = pos;
1333
- if (length3 % blockLen)
1381
+ if (length4 % blockLen)
1334
1382
  to.buffer.set(buffer);
1335
1383
  return to;
1336
1384
  }
@@ -1728,11 +1776,11 @@ var require_decrypt_transport_response_error = __commonJS({
1728
1776
  for (var name in all)
1729
1777
  __defProp3(target, name, { get: all[name], enumerable: true });
1730
1778
  };
1731
- var __copyProps2 = (to, from3, except, desc) => {
1732
- if (from3 && typeof from3 === "object" || typeof from3 === "function") {
1733
- for (let key of __getOwnPropNames2(from3))
1779
+ var __copyProps2 = (to, from4, except, desc) => {
1780
+ if (from4 && typeof from4 === "object" || typeof from4 === "function") {
1781
+ for (let key of __getOwnPropNames2(from4))
1734
1782
  if (!__hasOwnProp2.call(to, key) && key !== except)
1735
- __defProp3(to, key, { get: () => from3[key], enumerable: !(desc = __getOwnPropDesc2(from3, key)) || desc.enumerable });
1783
+ __defProp3(to, key, { get: () => from4[key], enumerable: !(desc = __getOwnPropDesc2(from4, key)) || desc.enumerable });
1736
1784
  }
1737
1785
  return to;
1738
1786
  };
@@ -1779,7 +1827,8 @@ import {
1779
1827
  profileStoreMetadataPath,
1780
1828
  readAdditionalDelegations,
1781
1829
  readAuthRequests,
1782
- upsertProfileRecord,
1830
+ refuseWriteToDeletedProfile as refuseWriteToDeletedProfile2,
1831
+ updateProfileStore,
1783
1832
  withProfileLock as withProfileLock2,
1784
1833
  writeJsonAtomic
1785
1834
  } from "@tinycloud/operations/state";
@@ -1865,6 +1914,7 @@ var CAPABILITIES2 = deriveServiceConstants("capabilities");
1865
1914
  var HOOKS = deriveServiceConstants("hooks");
1866
1915
  var ENCRYPTION = deriveServiceConstants("encryption");
1867
1916
  var SPACE = deriveServiceConstants("space");
1917
+ var CAPABILITY_REGISTRY = CAPABILITIES;
1868
1918
  var DEFAULT_MANIFEST_SPACE = "applications";
1869
1919
  var ACCOUNT_REGISTRY_SPACE = "account";
1870
1920
  var ACCOUNT_REGISTRY_PATH = "applications/";
@@ -3322,8 +3372,8 @@ function isValidJWT(jwt, alg) {
3322
3372
  const [header] = jwt.split(".");
3323
3373
  if (!header)
3324
3374
  return false;
3325
- const base642 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
3326
- const decoded = JSON.parse(atob(base642));
3375
+ const base643 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
3376
+ const decoded = JSON.parse(atob(base643));
3327
3377
  if (typeof decoded !== "object" || decoded === null)
3328
3378
  return false;
3329
3379
  if ("typ" in decoded && decoded?.typ !== "JWT")
@@ -5977,23 +6027,23 @@ var ZodEffects = class extends ZodType {
5977
6027
  }
5978
6028
  if (effect.type === "transform") {
5979
6029
  if (ctx.common.async === false) {
5980
- const base3 = this._def.schema._parseSync({
6030
+ const base4 = this._def.schema._parseSync({
5981
6031
  data: ctx.data,
5982
6032
  path: ctx.path,
5983
6033
  parent: ctx
5984
6034
  });
5985
- if (!isValid(base3))
6035
+ if (!isValid(base4))
5986
6036
  return INVALID;
5987
- const result = effect.transform(base3.value, checkCtx);
6037
+ const result = effect.transform(base4.value, checkCtx);
5988
6038
  if (result instanceof Promise) {
5989
6039
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
5990
6040
  }
5991
6041
  return { status: status.value, value: result };
5992
6042
  } else {
5993
- return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base3) => {
5994
- if (!isValid(base3))
6043
+ return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base4) => {
6044
+ if (!isValid(base4))
5995
6045
  return INVALID;
5996
- return Promise.resolve(effect.transform(base3.value, checkCtx)).then((result) => ({
6046
+ return Promise.resolve(effect.transform(base4.value, checkCtx)).then((result) => ({
5997
6047
  status: status.value,
5998
6048
  value: result
5999
6049
  }));
@@ -6432,9 +6482,9 @@ function ok(data) {
6432
6482
  function err(error) {
6433
6483
  return { ok: false, error };
6434
6484
  }
6435
- function serviceError(code2, message, service, options) {
6485
+ function serviceError(code3, message, service, options) {
6436
6486
  return {
6437
- code: code2,
6487
+ code: code3,
6438
6488
  message,
6439
6489
  service,
6440
6490
  cause: options?.cause,
@@ -6805,6 +6855,75 @@ var ServiceSessionSchema = external_exports.object({
6805
6855
  /** The session key JWK (required for invoke) */
6806
6856
  jwk: external_exports.object({}).passthrough()
6807
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
+ }
6808
6927
  function parsePermissionHint(value) {
6809
6928
  if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
6810
6929
  const candidate = value;
@@ -6872,6 +6991,101 @@ function parseAuthError(responseText) {
6872
6991
  function authUnauthorizedError(service, message, meta) {
6873
6992
  return serviceError(ErrorCodes.AUTH_UNAUTHORIZED, message, service, { meta });
6874
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
+ }
6875
7089
  function storageQuotaExceededError(service, message, meta) {
6876
7090
  return {
6877
7091
  code: ErrorCodes.STORAGE_QUOTA_EXCEEDED,
@@ -7567,12 +7781,14 @@ var KVService = class extends BaseService {
7567
7781
  signal: this.combineSignals(options?.signal)
7568
7782
  });
7569
7783
  if (!response.ok) {
7570
- const errorText = await response.text();
7784
+ const errorText = response.status === 401 || response.status === 403 ? await this.readAuthorizationText(response) : await response.text();
7571
7785
  if (response.status === 401 || response.status === 403) {
7572
7786
  return this.authorizationFailure(
7573
7787
  `Failed to batch read ${keys.length} key(s)`,
7574
7788
  response,
7575
- errorText
7789
+ errorText,
7790
+ paths,
7791
+ action
7576
7792
  );
7577
7793
  }
7578
7794
  if (response.status === 413) {
@@ -7662,7 +7878,7 @@ var KVService = class extends BaseService {
7662
7878
  return text;
7663
7879
  }
7664
7880
  }
7665
- async createSignedReadUrlError(response, key) {
7881
+ async createSignedReadUrlError(response, key, path) {
7666
7882
  let errorText = response.statusText;
7667
7883
  try {
7668
7884
  const text = await response.text();
@@ -7675,34 +7891,57 @@ var KVService = class extends BaseService {
7675
7891
  return this.authorizationFailure(
7676
7892
  `Failed to create signed read URL for key ${JSON.stringify(key)}`,
7677
7893
  response,
7678
- errorText
7894
+ errorText,
7895
+ [path],
7896
+ KVAction.GET
7679
7897
  );
7680
7898
  }
7681
- const code2 = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
7899
+ const code3 = response.status === 400 ? ErrorCodes.INVALID_INPUT : ErrorCodes.NETWORK_ERROR;
7682
7900
  return err(
7683
7901
  serviceError(
7684
- code2,
7902
+ code3,
7685
7903
  `Failed to create signed read URL for key ${JSON.stringify(key)}: ${response.status} - ${errorText}`,
7686
7904
  "kv",
7687
7905
  { meta: { status: response.status, statusText: response.statusText } }
7688
7906
  )
7689
7907
  );
7690
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
+ }
7691
7917
  /**
7692
7918
  * AUTH_UNAUTHORIZED for a 401/403 response. The message keeps the HTTP
7693
7919
  * status and the server text (`<context>: <status> - <text>`, the same
7694
7920
  * shape as every other KV failure) so callers that rethrow only the message
7695
7921
  * still see the status; `meta.status` carries it for typed callers.
7696
- * 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.
7697
7925
  */
7698
- authorizationFailure(context, response, errorText, extraMeta = {}) {
7926
+ authorizationFailure(context, response, errorText, paths, expectedAction, extraMeta = {}) {
7699
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;
7700
7940
  const detail = errorText.trim().length > 0 ? errorText : response.statusText || "authorization failed";
7701
7941
  return err(authUnauthorizedError("kv", `${context}: ${response.status} - ${detail}`, {
7702
7942
  status: response.status,
7703
- ...action && { requiredAction: action },
7704
- ...resource && { resource },
7705
- ...extraMeta
7943
+ ...extraMeta,
7944
+ ...capability
7706
7945
  }));
7707
7946
  }
7708
7947
  normalizeSignedReadUrlResponse(data) {
@@ -7746,13 +7985,24 @@ var KVService = class extends BaseService {
7746
7985
  );
7747
7986
  if (!response.ok) {
7748
7987
  if (response.status === 401 || response.status === 403) {
7749
- const errorText2 = await response.text();
7988
+ const errorText2 = await this.readAuthorizationText(response);
7750
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;
7751
7999
  return this.authorizationFailure(
7752
8000
  `Failed to get key ${JSON.stringify(key)}`,
7753
8001
  response,
7754
8002
  errorText2,
7755
- permissionHint === void 0 ? {} : { permissionHint }
8003
+ [path],
8004
+ KVAction.GET,
8005
+ structuredCapability ? { permissionHint, ...structuredCapability } : {}
7756
8006
  );
7757
8007
  }
7758
8008
  if (response.status === 404) {
@@ -7829,7 +8079,9 @@ var KVService = class extends BaseService {
7829
8079
  return this.authorizationFailure(
7830
8080
  `Failed to put key ${JSON.stringify(key)}`,
7831
8081
  response,
7832
- await response.text()
8082
+ await this.readAuthorizationText(response),
8083
+ [path],
8084
+ KVAction.PUT
7833
8085
  );
7834
8086
  }
7835
8087
  if (!response.ok) {
@@ -7958,7 +8210,9 @@ var KVService = class extends BaseService {
7958
8210
  return this.authorizationFailure(
7959
8211
  `Failed to batch put ${items.length} key(s)`,
7960
8212
  response,
7961
- errorText
8213
+ errorText,
8214
+ paths,
8215
+ KVAction.PUT
7962
8216
  );
7963
8217
  }
7964
8218
  const quotaError = this.handleQuotaErrorResponse(
@@ -8079,11 +8333,13 @@ var KVService = class extends BaseService {
8079
8333
  }
8080
8334
  );
8081
8335
  if (!response.ok) {
8082
- if (response.status === 401) {
8336
+ if (response.status === 401 || response.status === 403) {
8083
8337
  return this.authorizationFailure(
8084
8338
  "Failed to list keys",
8085
8339
  response,
8086
- await response.text()
8340
+ await this.readAuthorizationText(response),
8341
+ [listPath],
8342
+ KVAction.LIST
8087
8343
  );
8088
8344
  }
8089
8345
  const errorText = await response.text();
@@ -8132,11 +8388,13 @@ var KVService = class extends BaseService {
8132
8388
  options?.ifMatch === void 0 ? void 0 : { "if-match": options.ifMatch }
8133
8389
  );
8134
8390
  if (!response.ok) {
8135
- if (response.status === 401) {
8391
+ if (response.status === 401 || response.status === 403) {
8136
8392
  return this.authorizationFailure(
8137
8393
  `Failed to delete key ${JSON.stringify(key)}`,
8138
8394
  response,
8139
- await response.text()
8395
+ await this.readAuthorizationText(response),
8396
+ [path],
8397
+ KVAction.DELETE
8140
8398
  );
8141
8399
  }
8142
8400
  if (response.status === 404) {
@@ -8194,11 +8452,13 @@ var KVService = class extends BaseService {
8194
8452
  options?.signal
8195
8453
  );
8196
8454
  if (!response.ok) {
8197
- if (response.status === 401) {
8455
+ if (response.status === 401 || response.status === 403) {
8198
8456
  return this.authorizationFailure(
8199
8457
  `Failed to get metadata for key ${JSON.stringify(key)}`,
8200
8458
  response,
8201
- await response.text()
8459
+ await this.readAuthorizationText(response),
8460
+ [path],
8461
+ KVAction.HEAD
8202
8462
  );
8203
8463
  }
8204
8464
  if (response.status === 404) {
@@ -8265,7 +8525,7 @@ var KVService = class extends BaseService {
8265
8525
  signal: this.combineSignals(options?.signal)
8266
8526
  });
8267
8527
  if (!response.ok) {
8268
- return this.createSignedReadUrlError(response, key);
8528
+ return this.createSignedReadUrlError(response, key, path);
8269
8529
  }
8270
8530
  const signedUrl = this.normalizeSignedReadUrlResponse(
8271
8531
  await response.json()
@@ -8489,14 +8749,15 @@ var SQLService = class extends BaseService {
8489
8749
  };
8490
8750
  if (options?.maxRows !== void 0) body.maxRows = options.maxRows;
8491
8751
  if (options?.maxBytes !== void 0) body.maxBytes = options.maxBytes;
8752
+ const action = this.actionForSql(sql, SQLAction.READ);
8492
8753
  const response = await this.invokeSQL(
8493
8754
  dbName,
8494
- this.actionForSql(sql, SQLAction.READ),
8755
+ action,
8495
8756
  body,
8496
8757
  options?.signal
8497
8758
  );
8498
8759
  if (!response.ok) {
8499
- return this.handleErrorResponse(response, "query");
8760
+ return this.handleErrorResponse(response, "query", dbName, [action]);
8500
8761
  }
8501
8762
  const data = await response.json();
8502
8763
  return ok(data);
@@ -8525,14 +8786,15 @@ var SQLService = class extends BaseService {
8525
8786
  (statement) => this.actionForSql(statement, SQLAction.SCHEMA)
8526
8787
  )
8527
8788
  ];
8789
+ const requestedActions2 = this.dedupeActions(actions);
8528
8790
  const response = await this.invokeSQL(
8529
8791
  dbName,
8530
- this.dedupeActions(actions),
8792
+ requestedActions2,
8531
8793
  body,
8532
8794
  options?.signal
8533
8795
  );
8534
8796
  if (!response.ok) {
8535
- return this.handleErrorResponse(response, "execute");
8797
+ return this.handleErrorResponse(response, "execute", dbName, requestedActions2);
8536
8798
  }
8537
8799
  const data = await response.json();
8538
8800
  return ok(data);
@@ -8547,14 +8809,15 @@ var SQLService = class extends BaseService {
8547
8809
  return err(authRequiredError("sql"));
8548
8810
  }
8549
8811
  try {
8812
+ const requestedActions2 = this.actionsForSqlBatch(statements);
8550
8813
  const response = await this.invokeSQL(
8551
8814
  dbName,
8552
- this.actionsForSqlBatch(statements),
8815
+ requestedActions2,
8553
8816
  { action: "batch", statements },
8554
8817
  options?.signal
8555
8818
  );
8556
8819
  if (!response.ok) {
8557
- return this.handleErrorResponse(response, "batch");
8820
+ return this.handleErrorResponse(response, "batch", dbName, requestedActions2);
8558
8821
  }
8559
8822
  const data = await response.json();
8560
8823
  return ok(data);
@@ -8576,7 +8839,7 @@ var SQLService = class extends BaseService {
8576
8839
  options?.signal
8577
8840
  );
8578
8841
  if (!response.ok) {
8579
- return this.handleErrorResponse(response, "executeStatement");
8842
+ return this.handleErrorResponse(response, "executeStatement", dbName, [SQLAction.WRITE]);
8580
8843
  }
8581
8844
  const data = await response.json();
8582
8845
  return ok(data);
@@ -8598,7 +8861,7 @@ var SQLService = class extends BaseService {
8598
8861
  options?.signal
8599
8862
  );
8600
8863
  if (!response.ok) {
8601
- return this.handleErrorResponse(response, "export");
8864
+ return this.handleErrorResponse(response, "export", dbName, [SQLAction.READ]);
8602
8865
  }
8603
8866
  const resp = response;
8604
8867
  if (typeof resp.blob === "function") {
@@ -8732,7 +8995,7 @@ var SQLService = class extends BaseService {
8732
8995
  }))
8733
8996
  );
8734
8997
  }
8735
- async handleErrorResponse(response, operation) {
8998
+ async handleErrorResponse(response, operation, dbName, requestedActions2) {
8736
8999
  const errorText = await response.text();
8737
9000
  const meta = responseErrorMeta(response.status, response.statusText, errorText);
8738
9001
  if (response.status === 402) {
@@ -8751,9 +9014,11 @@ var SQLService = class extends BaseService {
8751
9014
  errorBody
8752
9015
  );
8753
9016
  if (response.status === 401) {
8754
- const { resource, action } = parseAuthError(errorText);
8755
- if (action) meta.requiredAction = action;
8756
- 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
+ }
8757
9022
  }
8758
9023
  return err(
8759
9024
  serviceError(errorCode2, message, "sql", { meta })
@@ -11502,75 +11767,6 @@ function canonicalHashHex(sha2563, value) {
11502
11767
  const canonical = canonicalize(value);
11503
11768
  return hexEncode2(sha2563(utf8Encode(canonical)));
11504
11769
  }
11505
- var URN_PREFIX = "urn:tinycloud:encryption:";
11506
- var NETWORK_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
11507
- var PKH_EIP155_DID_RE = /^did:pkh:eip155:(\d+):(0x[a-fA-F0-9]{40})$/;
11508
- var NetworkIdError = class extends Error {
11509
- constructor(message) {
11510
- super(message);
11511
- this.name = "NetworkIdError";
11512
- }
11513
- };
11514
- function parseNetworkId(networkId) {
11515
- if (typeof networkId !== "string" || networkId.length === 0) {
11516
- throw new NetworkIdError("networkId must be a non-empty string");
11517
- }
11518
- if (!networkId.startsWith(URN_PREFIX)) {
11519
- throw new NetworkIdError(
11520
- `networkId must start with ${URN_PREFIX} (got ${JSON.stringify(networkId)})`
11521
- );
11522
- }
11523
- const body = networkId.slice(URN_PREFIX.length);
11524
- const lastColon = body.lastIndexOf(":");
11525
- if (lastColon <= 0 || lastColon === body.length - 1) {
11526
- throw new NetworkIdError(
11527
- `networkId missing ownerDid or name segment (got ${JSON.stringify(networkId)})`
11528
- );
11529
- }
11530
- const ownerDid = body.slice(0, lastColon);
11531
- const name = body.slice(lastColon + 1);
11532
- if (!ownerDid.startsWith("did:")) {
11533
- throw new NetworkIdError(
11534
- `networkId ownerDid must be a DID (got ${JSON.stringify(ownerDid)})`
11535
- );
11536
- }
11537
- const didParts = ownerDid.split(":");
11538
- if (didParts.length < 3 || didParts.some((p) => p.length === 0)) {
11539
- throw new NetworkIdError(
11540
- `networkId ownerDid is not a well-formed DID (got ${JSON.stringify(ownerDid)})`
11541
- );
11542
- }
11543
- if (!NETWORK_NAME_RE.test(name)) {
11544
- throw new NetworkIdError(
11545
- `networkId name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
11546
- );
11547
- }
11548
- return { networkId, ownerDid, name };
11549
- }
11550
- function parsePkhOwnerDid(ownerDid) {
11551
- const match = ownerDid.match(PKH_EIP155_DID_RE);
11552
- if (!match) return null;
11553
- return {
11554
- chainId: match[1],
11555
- address: match[2].toLowerCase()
11556
- };
11557
- }
11558
- function ownerDidMatches(a, b) {
11559
- const aPkh = parsePkhOwnerDid(a);
11560
- const bPkh = parsePkhOwnerDid(b);
11561
- if (aPkh && bPkh) {
11562
- return aPkh.chainId === bPkh.chainId && aPkh.address === bPkh.address;
11563
- }
11564
- return a === b;
11565
- }
11566
- function networkDiscoveryKey(name) {
11567
- if (!NETWORK_NAME_RE.test(name)) {
11568
- throw new NetworkIdError(
11569
- `network name ${JSON.stringify(name)} must match ${NETWORK_NAME_RE.source}`
11570
- );
11571
- }
11572
- return `.well-known/encryption/network/${name}`;
11573
- }
11574
11770
  var DEFAULT_ENCRYPTION_ALG = "x25519-aes256gcm/v1";
11575
11771
  var ENVELOPE_VERSION = 1;
11576
11772
  var DEFAULT_KEY_VERSION = 1;
@@ -12406,7 +12602,7 @@ var SERVICE_SHORT_TO_LONG = Object.freeze({
12406
12602
  });
12407
12603
  var ENCRYPTION_PERMISSION_SERVICE2 = "tinycloud.encryption";
12408
12604
  var ENCRYPTION_MANIFEST_SPACE2 = "encryption";
12409
- var SERVICE_LONG_TO_SHORT = Object.freeze(
12605
+ var SERVICE_LONG_TO_SHORT2 = Object.freeze(
12410
12606
  Object.fromEntries(
12411
12607
  Object.entries(SERVICE_SHORT_TO_LONG).map(([s, l]) => [l, s])
12412
12608
  )
@@ -12896,18 +13092,18 @@ function expandVaultPermissionEntry(entry) {
12896
13092
  const byBase = /* @__PURE__ */ new Map();
12897
13093
  for (const action of entry.actions) {
12898
13094
  const expansion = vaultActionExpansion(action);
12899
- for (const base3 of expansion.bases) {
12900
- const actions = byBase.get(base3) ?? [];
13095
+ for (const base4 of expansion.bases) {
13096
+ const actions = byBase.get(base4) ?? [];
12901
13097
  if (!actions.includes(expansion.action)) {
12902
13098
  actions.push(expansion.action);
12903
13099
  }
12904
- byBase.set(base3, actions);
13100
+ byBase.set(base4, actions);
12905
13101
  }
12906
13102
  }
12907
- return [...byBase.entries()].map(([base3, actions]) => ({
13103
+ return [...byBase.entries()].map(([base4, actions]) => ({
12908
13104
  ...entry,
12909
13105
  service: "tinycloud.kv",
12910
- path: vaultKVPath(base3, entry.path),
13106
+ path: vaultKVPath(base4, entry.path),
12911
13107
  actions,
12912
13108
  skipPrefix: true
12913
13109
  }));
@@ -12950,9 +13146,9 @@ function normalizeVaultAction(action) {
12950
13146
  }
12951
13147
  return action;
12952
13148
  }
12953
- function vaultKVPath(base3, path) {
13149
+ function vaultKVPath(base4, path) {
12954
13150
  const normalized = path.startsWith("/") ? path.slice(1) : path;
12955
- return `${base3}/${normalized}`;
13151
+ return `${base4}/${normalized}`;
12956
13152
  }
12957
13153
  function cloneResourceCapability(entry) {
12958
13154
  return {
@@ -13123,17 +13319,15 @@ function didWithoutFragment(did) {
13123
13319
  async function loadAdditionalDelegations(profile) {
13124
13320
  return readAdditionalDelegations(profile);
13125
13321
  }
13126
- async function saveAdditionalDelegations(profile, entries) {
13127
- await replaceSharedRecords(profile, "additional-delegations", entries);
13322
+ async function appendAdditionalDelegations(profile, entries) {
13323
+ const { mergeDelegationsWithoutRequest } = await import("@tinycloud/operations/delegation-binding");
13324
+ await updateProfileStore(profile, "additional-delegations", (stored) => ({
13325
+ records: mergeDelegationsWithoutRequest(stored, entries.map((entry) => ({ ...entry }))),
13326
+ result: void 0
13327
+ }));
13128
13328
  }
13129
13329
  async function appendAdditionalDelegation(profile, entry) {
13130
- await upsertProfileRecord(
13131
- profile,
13132
- "additional-delegations",
13133
- entry.delegation.cid,
13134
- entry,
13135
- (candidate) => candidate.delegation.cid
13136
- );
13330
+ await appendAdditionalDelegations(profile, [entry]);
13137
13331
  }
13138
13332
  async function loadPermissionRequestArtifacts(profile) {
13139
13333
  const raw = await readAuthRequests(profile);
@@ -13147,10 +13341,8 @@ async function appendPermissionRequestArtifact(profile, artifact) {
13147
13341
  await writeSharedRecords(profile, "auth-requests", next);
13148
13342
  });
13149
13343
  }
13150
- async function replaceSharedRecords(profile, store, entries) {
13151
- await withProfileLock2(profile, () => writeSharedRecords(profile, store, entries));
13152
- }
13153
13344
  async function writeSharedRecords(profile, store, entries) {
13345
+ await refuseWriteToDeletedProfile2(profile);
13154
13346
  const path = store === "additional-delegations" ? additionalDelegationsPath(profile) : permissionRequestsPath(profile);
13155
13347
  await writeJsonAtomic(path, entries);
13156
13348
  await writeJsonAtomic(profileStoreMetadataPath(profile, store), { formatVersion: 1 });
@@ -13163,13 +13355,48 @@ async function getLastPermissionRequestArtifact(profile) {
13163
13355
  const existing = await loadPermissionRequestArtifacts(profile);
13164
13356
  return existing.at(-1) ?? null;
13165
13357
  }
13166
- async function replayAdditionalDelegations(node, profile) {
13358
+ async function replayAdditionalDelegations(node, profile, options) {
13359
+ const {
13360
+ operationSpaceResolver,
13361
+ prepareStoredDelegationReplay,
13362
+ replayStoredDelegation,
13363
+ storedDelegationKind
13364
+ } = await import("@tinycloud/operations/delegation-binding");
13365
+ const activator = node;
13366
+ const migrated = await prepareStoredDelegationReplay(profile, activator, {
13367
+ host: options.host,
13368
+ migrate: options.migrate
13369
+ });
13370
+ const resolveSpace = operationSpaceResolver(node, options.ownerSpace);
13167
13371
  const entries = await loadAdditionalDelegations(profile);
13168
- for (const entry of entries) {
13372
+ for (const stored of entries) {
13373
+ const kind = storedDelegationKind(stored);
13374
+ if (kind === "compact" || kind === "signed-login") {
13375
+ const installed = await replayStoredDelegation(activator, stored, {
13376
+ host: options.host,
13377
+ migrated,
13378
+ resolveSpace
13379
+ });
13380
+ if (installed === void 0 && process.env.TC_DEBUG_REPLAY === "1") {
13381
+ process.stderr.write("[replay] skipping a stored delegation refused by validation or its request binding\n");
13382
+ }
13383
+ continue;
13384
+ }
13385
+ if (kind === "refused") {
13386
+ if (process.env.TC_DEBUG_REPLAY === "1") {
13387
+ process.stderr.write("[replay] skipping a malformed stored record or one bound to a request it cannot be held to\n");
13388
+ }
13389
+ continue;
13390
+ }
13391
+ const entry = stored;
13169
13392
  const expiry = entry.delegation.expiry instanceof Date ? entry.delegation.expiry : new Date(entry.delegation.expiry);
13170
13393
  if (expiry.getTime() <= Date.now()) continue;
13171
13394
  try {
13172
- await node.useRuntimeDelegation({ ...entry.delegation, expiry });
13395
+ await node.useRuntimeDelegation({
13396
+ ...entry.delegation,
13397
+ delegationHeader: { Authorization: entry.delegation.delegationHeader.Authorization },
13398
+ expiry
13399
+ });
13173
13400
  } catch (err2) {
13174
13401
  if (process.env.TC_DEBUG_REPLAY === "1") {
13175
13402
  process.stderr.write(`[replay] skipping ${entry.delegation.cid}: ${err2.message}
@@ -13465,7 +13692,7 @@ async function createSDKInstance(ctx, options) {
13465
13692
  if (!key && !effectivePrivateKey && !(profile?.authMethod === "openkey" && session !== null)) {
13466
13693
  throw new CLIError(
13467
13694
  "AUTH_REQUIRED",
13468
- `No key found for profile "${ctx.profile}". Run \`tc init\` first.`,
13695
+ `No key found for profile "${ctx.profile}". Run \`tc --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
13469
13696
  ExitCode.AUTH_REQUIRED
13470
13697
  );
13471
13698
  }
@@ -13474,6 +13701,7 @@ async function createSDKInstance(ctx, options) {
13474
13701
  host: ctx.host,
13475
13702
  privateKey: effectivePrivateKey
13476
13703
  });
13704
+ let restoredOwnSession2 = false;
13477
13705
  if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
13478
13706
  await node2.restoreSession({
13479
13707
  delegationHeader: session.delegationHeader,
@@ -13486,16 +13714,22 @@ async function createSDKInstance(ctx, options) {
13486
13714
  siwe: session.siwe,
13487
13715
  signature: session.signature
13488
13716
  });
13717
+ restoredOwnSession2 = true;
13489
13718
  } else {
13490
13719
  await node2.signIn();
13491
13720
  }
13492
- await replayAdditionalDelegations(node2, ctx.profile);
13721
+ await replayAdditionalDelegations(node2, ctx.profile, {
13722
+ host: ctx.host,
13723
+ ownerSpace: profile.spaceId,
13724
+ migrate: restoredOwnSession2 && options?.privateKey === void 0
13725
+ });
13493
13726
  return node2;
13494
13727
  }
13495
13728
  const node = new TinyCloudNode({
13496
13729
  host: ctx.host,
13497
13730
  privateKey: options?.privateKey
13498
13731
  });
13732
+ let restoredOwnSession = false;
13499
13733
  if (options?.privateKey) {
13500
13734
  await node.signIn();
13501
13735
  } else if (session && session.delegationHeader && session.delegationCid && session.spaceId) {
@@ -13510,10 +13744,16 @@ async function createSDKInstance(ctx, options) {
13510
13744
  siwe: session.siwe,
13511
13745
  signature: session.signature
13512
13746
  });
13747
+ restoredOwnSession = true;
13513
13748
  }
13514
- await replayAdditionalDelegations(node, ctx.profile);
13749
+ await replayAdditionalDelegations(node, ctx.profile, {
13750
+ host: ctx.host,
13751
+ ownerSpace: profile?.spaceId,
13752
+ migrate: restoredOwnSession
13753
+ });
13515
13754
  return node;
13516
13755
  }
13756
+ var BOOTSTRAP_PROFILE_FIELDS = ["sessionDid", "spaceId"];
13517
13757
  async function bootstrapDelegatedSession(ctx, delegation) {
13518
13758
  const written = await ProfileManager.withLock(ctx.profile, async () => {
13519
13759
  const previousProfile = await ProfileManager.getProfile(ctx.profile);
@@ -13563,13 +13803,13 @@ async function bootstrapDelegatedSession(ctx, delegation) {
13563
13803
  throw error;
13564
13804
  });
13565
13805
  const session = await ProfileManager.getSession(ctx.profile);
13566
- if (JSON.stringify(profile) !== JSON.stringify(written.profile) || JSON.stringify(session) !== JSON.stringify(written.session)) {
13806
+ if (profile === null || BOOTSTRAP_PROFILE_FIELDS.some((field) => profile[field] !== written.profile[field]) || JSON.stringify(session) !== JSON.stringify(written.session)) {
13567
13807
  return `Profile "${ctx.profile}" changed while the import was pending (another login, logout or profile update), so its newer state was kept and the provisional session was not rolled back.`;
13568
13808
  }
13569
13809
  return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
13570
13810
  }, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
13571
13811
  // The lock or a read failed: the import's error stays the one reported.
13572
- `Rolling back the provisional session of profile "${ctx.profile}" could not run (${error instanceof Error ? error.message : String(error)}); check \`tc --profile ${ctx.profile} context\`.`
13812
+ `Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`tc --profile ${ctx.profile} context\`.`
13573
13813
  ));
13574
13814
  throw annotate(cause, note);
13575
13815
  };
@@ -13585,15 +13825,27 @@ async function restoreBeforeBootstrap(profileName, previousProfile) {
13585
13825
  const failures = [];
13586
13826
  for (const write of [
13587
13827
  () => ProfileManager.clearSession(profileName),
13588
- () => ProfileManager.setProfile(profileName, previousProfile)
13828
+ () => ProfileManager.updateProfile(profileName, (current) => {
13829
+ const restored = { ...current };
13830
+ for (const field of BOOTSTRAP_PROFILE_FIELDS) {
13831
+ if (previousProfile[field] === void 0) delete restored[field];
13832
+ else restored[field] = previousProfile[field];
13833
+ }
13834
+ return restored;
13835
+ })
13589
13836
  ]) {
13590
13837
  await write().catch((error) => {
13591
- failures.push(error instanceof Error ? error.message : String(error));
13838
+ failures.push(failureName(error));
13592
13839
  });
13593
13840
  }
13594
13841
  if (failures.length === 0) return void 0;
13595
13842
  return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`tc --profile ${profileName} context\`.`;
13596
13843
  }
13844
+ function failureName(error) {
13845
+ if (error instanceof SyntaxError) return "a profile file is not valid JSON";
13846
+ if (typeof error === "object" && error !== null && "code" in error && typeof error.code === "string") return error.code;
13847
+ return error instanceof Error ? error.name : "unknown error";
13848
+ }
13597
13849
  function annotate(error, note) {
13598
13850
  if (note === void 0) return error;
13599
13851
  const cause = wrapError(error);
@@ -13605,7 +13857,7 @@ async function ensureAuthenticated(ctx, options) {
13605
13857
  }
13606
13858
  const profile = await ProfileManager.getProfile(ctx.profile).catch(() => null);
13607
13859
  if (profile?.authMethod === "local" && profile.privateKey) {
13608
- return createSDKInstance(ctx, { privateKey: profile.privateKey });
13860
+ return createSDKInstance(ctx);
13609
13861
  }
13610
13862
  const session = await ProfileManager.getSession(ctx.profile);
13611
13863
  if (!session) {
@@ -13884,7 +14136,7 @@ async function authenticatedNode(cmd) {
13884
14136
  }
13885
14137
  function assertOk(result) {
13886
14138
  if (!result.ok) {
13887
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
14139
+ throw cliErrorFromService(result.error);
13888
14140
  }
13889
14141
  }
13890
14142
  async function loadManifestSource(source) {
@@ -14018,8 +14270,8 @@ function buildAuthUrl(did, options = {}) {
14018
14270
  params.set("expiry", String(options.expiry));
14019
14271
  }
14020
14272
  params.set("protocolVersion", "1");
14021
- const base3 = options.openkeyHost ?? DEFAULT_OPENKEY_HOST;
14022
- return `${base3}/delegate?${params.toString()}`;
14273
+ const base4 = options.openkeyHost ?? DEFAULT_OPENKEY_HOST;
14274
+ return `${base4}/delegate?${params.toString()}`;
14023
14275
  }
14024
14276
  function validateDelegationCallbackPayload(value) {
14025
14277
  if (!value || typeof value !== "object") return "expected an object";
@@ -14989,20 +15241,20 @@ async function acquireDeviceDelegation(input) {
14989
15241
  }
14990
15242
  const startValue = await responseJson(startResponse);
14991
15243
  if (!startResponse.ok) {
14992
- const code2 = errorCode(startValue);
15244
+ const code3 = errorCode(startValue);
14993
15245
  const description = errorDescription(startValue);
14994
- if (startResponse.status === 429 && code2 === "rate_limited") {
15246
+ if (startResponse.status === 429 && code3 === "rate_limited") {
14995
15247
  throw deviceAuthRateLimit(startResponse);
14996
15248
  }
14997
- if (code2 === "invalid_scope") {
15249
+ if (code3 === "invalid_scope") {
14998
15250
  throw new CLIError(
14999
15251
  "SCOPE_REJECTED",
15000
15252
  `OpenKey rejected the requested scope${description ? `: ${description}` : "."} Remove that capability from the manifest or use another approval flow.`,
15001
15253
  ExitCode.PERMISSION_DENIED,
15002
- { openkeyError: code2, ...description ? { capability: description } : {} }
15254
+ { openkeyError: code3, ...description ? { capability: description } : {} }
15003
15255
  );
15004
15256
  }
15005
- throw new CLIError("DEVICE_AUTH_FAILED", `OpenKey device authorization failed: ${code2 ?? `HTTP ${startResponse.status}`}${description ? ` (${description})` : ""}`, ExitCode.ERROR);
15257
+ throw new CLIError("DEVICE_AUTH_FAILED", `OpenKey device authorization failed: ${code3 ?? `HTTP ${startResponse.status}`}${description ? ` (${description})` : ""}`, ExitCode.ERROR);
15006
15258
  }
15007
15259
  const started = validateStart(startValue, openkeyHost);
15008
15260
  const deadline = Date.now() + started.expiresIn * 1e3;
@@ -15030,20 +15282,20 @@ async function acquireDeviceDelegation(input) {
15030
15282
  continue;
15031
15283
  }
15032
15284
  const value = await responseJson(response);
15033
- const code2 = errorCode(value);
15034
- if (response.status >= 500 || response.status === 429 && code2 !== "slow_down") continue;
15035
- if (code2 === "slow_down") {
15285
+ const code3 = errorCode(value);
15286
+ if (response.status >= 500 || response.status === 429 && code3 !== "slow_down") continue;
15287
+ if (code3 === "slow_down") {
15036
15288
  interval += 5;
15037
15289
  continue;
15038
15290
  }
15039
- if (code2 === "authorization_pending") continue;
15040
- if (code2 === "access_denied") {
15291
+ if (code3 === "authorization_pending") continue;
15292
+ if (code3 === "access_denied") {
15041
15293
  throw new CLIError("DEVICE_AUTH_DENIED", "The owner denied the OpenKey device authorization. No session was saved.", ExitCode.PERMISSION_DENIED);
15042
15294
  }
15043
- if (code2 === "expired_token") {
15295
+ if (code3 === "expired_token") {
15044
15296
  throw new CLIError("DEVICE_AUTH_EXPIRED", "OpenKey device authorization expired_token: the approval window closed. Run the command again.", ExitCode.AUTH_REQUIRED);
15045
15297
  }
15046
- if (!response.ok) throw new CLIError("DEVICE_AUTH_FAILED", `OpenKey device authorization failed: ${code2 ?? `HTTP ${response.status}`}`, ExitCode.ERROR);
15298
+ if (!response.ok) throw new CLIError("DEVICE_AUTH_FAILED", `OpenKey device authorization failed: ${code3 ?? `HTTP ${response.status}`}`, ExitCode.ERROR);
15047
15299
  const result = value;
15048
15300
  if (result?.status === "pending") {
15049
15301
  interval = Math.max(interval, Number.isSafeInteger(result.interval) ? result.interval : interval);
@@ -15252,6 +15504,7 @@ function registerAuthCommand(program) {
15252
15504
  try {
15253
15505
  const globalOpts = cmd.optsWithGlobals();
15254
15506
  const ctx = await ProfileManager.resolveContext(globalOpts);
15507
+ await ProfileManager.getProfile(ctx.profile);
15255
15508
  await ProfileManager.clearSession(ctx.profile);
15256
15509
  outputJson({ profile: ctx.profile, authenticated: false });
15257
15510
  } catch (error) {
@@ -15364,7 +15617,7 @@ function registerAuthCommand(program) {
15364
15617
  if (profile.authMethod === "openkey" || options.device) {
15365
15618
  const key = await ProfileManager.getKey(ctx.profile);
15366
15619
  if (!key) {
15367
- throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`tc init\` first.`, ExitCode.AUTH_REQUIRED);
15620
+ throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`tc --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
15368
15621
  }
15369
15622
  const openkeyHost = resolveOpenKeyHost(profile);
15370
15623
  const grants = [];
@@ -15496,6 +15749,15 @@ function registerAuthCommand(program) {
15496
15749
  return;
15497
15750
  }
15498
15751
  const imported = normalizeDelegationImport(parsed);
15752
+ const { activateUnboundCompactImport, storedDelegationKind } = await import("@tinycloud/operations/delegation-binding");
15753
+ const kind = storedDelegationKind({ delegation: imported.delegation });
15754
+ if (kind === "refused") {
15755
+ throw new CLIError(
15756
+ "INVALID_AUTH_IMPORT",
15757
+ "Imported delegation must carry exactly one string Authorization header.",
15758
+ ExitCode.USAGE_ERROR
15759
+ );
15760
+ }
15499
15761
  let node;
15500
15762
  try {
15501
15763
  node = await ensureAuthenticated(ctx);
@@ -15505,18 +15767,33 @@ function registerAuthCommand(program) {
15505
15767
  if (session || resolveProfilePosture(profile) !== "delegate-session") throw error;
15506
15768
  node = (await bootstrapDelegatedSession(ctx, imported.delegation)).node;
15507
15769
  }
15508
- await appendAdditionalDelegation(ctx.profile, storedAdditionalDelegation(
15509
- imported.delegation,
15510
- imported.permissions
15511
- ));
15512
15770
  const targetsSessionKey = typeof imported.delegation.delegateDID === "string" && principalDidEquals(imported.delegation.delegateDID, node.sessionDid);
15513
15771
  let activated = false;
15514
- if (targetsSessionKey) {
15515
- await node.useRuntimeDelegation(imported.delegation);
15772
+ let permissions = imported.permissions;
15773
+ if (targetsSessionKey && kind === "compact") {
15774
+ const record = await activateUnboundCompactImport(
15775
+ node,
15776
+ imported.delegation,
15777
+ ctx.host
15778
+ );
15779
+ await appendAdditionalDelegation(ctx.profile, record);
15780
+ permissions = record.permissions;
15516
15781
  activated = true;
15782
+ } else {
15783
+ await appendAdditionalDelegation(ctx.profile, storedAdditionalDelegation(
15784
+ imported.delegation,
15785
+ imported.permissions
15786
+ ));
15787
+ if (targetsSessionKey) {
15788
+ await node.useRuntimeDelegation({
15789
+ ...imported.delegation,
15790
+ delegationHeader: { Authorization: imported.delegation.delegationHeader.Authorization }
15791
+ });
15792
+ activated = true;
15793
+ }
15517
15794
  }
15518
15795
  await appendGrantHistory(ctx.profile, {
15519
- addedCaps: imported.permissions,
15796
+ addedCaps: permissions,
15520
15797
  source: "cli",
15521
15798
  delegationCid: imported.delegation.cid,
15522
15799
  expiry: imported.delegation.expiry.toISOString()
@@ -15527,7 +15804,7 @@ function registerAuthCommand(program) {
15527
15804
  kind: "tinycloud.auth.delegation",
15528
15805
  requestId: imported.requestId ?? null,
15529
15806
  delegationCid: imported.delegation.cid,
15530
- permissions: imported.permissions,
15807
+ permissions,
15531
15808
  expiry: imported.delegation.expiry.toISOString()
15532
15809
  });
15533
15810
  } catch (error) {
@@ -15872,7 +16149,7 @@ async function importRequestBoundDelegation(ctx, artifact) {
15872
16149
  ExitCode.ERROR
15873
16150
  );
15874
16151
  case "error":
15875
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16152
+ throw cliErrorFromService(result.error);
15876
16153
  }
15877
16154
  }
15878
16155
  function isStoredDelegationLike(value) {
@@ -15901,8 +16178,6 @@ async function activateAndStoreOpenKeyGrants(profileName, node, grants, source)
15901
16178
  for (const { delegation } of grants) await node.useRuntimeDelegation(delegation);
15902
16179
  if (grants.length === 0) return;
15903
16180
  await ProfileManager.withLock(profileName, async () => {
15904
- const existing = await loadAdditionalDelegations(profileName);
15905
- const replacing = new Set(grants.map(({ delegation }) => delegation.cid));
15906
16181
  for (const { delegation, effective } of grants) {
15907
16182
  await appendGrantHistory(profileName, {
15908
16183
  addedCaps: effective,
@@ -15911,10 +16186,10 @@ async function activateAndStoreOpenKeyGrants(profileName, node, grants, source)
15911
16186
  expiry: delegation.expiry.toISOString()
15912
16187
  });
15913
16188
  }
15914
- await saveAdditionalDelegations(profileName, [
15915
- ...existing.filter(({ delegation }) => !replacing.has(delegation.cid)),
15916
- ...grants.map(({ delegation, effective }) => storedAdditionalDelegation(delegation, effective))
15917
- ]);
16189
+ await appendAdditionalDelegations(
16190
+ profileName,
16191
+ grants.map(({ delegation, effective }) => storedAdditionalDelegation(delegation, effective))
16192
+ );
15918
16193
  });
15919
16194
  }
15920
16195
  async function ensureDelegationAuthority(params) {
@@ -15924,7 +16199,7 @@ async function ensureDelegationAuthority(params) {
15924
16199
  if (!key) {
15925
16200
  throw new CLIError(
15926
16201
  "NO_KEY",
15927
- `No key found for profile "${params.ctx.profile}". Run \`tc init\` first.`,
16202
+ `No key found for profile "${params.ctx.profile}". Run \`tc --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
15928
16203
  ExitCode.AUTH_REQUIRED
15929
16204
  );
15930
16205
  }
@@ -15996,7 +16271,7 @@ function execCapturedCommand(command) {
15996
16271
  stdio: "inherit"
15997
16272
  });
15998
16273
  child.on("error", reject);
15999
- child.on("exit", (code2, signal) => {
16274
+ child.on("exit", (code3, signal) => {
16000
16275
  if (signal) {
16001
16276
  reject(new CLIError(
16002
16277
  "COMMAND_SIGNAL",
@@ -16005,8 +16280,8 @@ function execCapturedCommand(command) {
16005
16280
  ));
16006
16281
  return;
16007
16282
  }
16008
- if (code2 && code2 !== 0) {
16009
- process.exitCode = code2;
16283
+ if (code3 && code3 !== 0) {
16284
+ process.exitCode = code3;
16010
16285
  }
16011
16286
  resolve4();
16012
16287
  });
@@ -16333,15 +16608,15 @@ ${declined.map((permission) => ` ${compactPermission(permission)}`).join("\n")}
16333
16608
  }
16334
16609
  async function refreshOpenKeySession(profileName, host, options = {}) {
16335
16610
  const snapshot = await readProfileSnapshot(profileName);
16611
+ const profile = snapshot.profile ?? await ProfileManager.getProfile(profileName);
16336
16612
  const key = snapshot.key;
16337
16613
  if (!key) {
16338
16614
  throw new CLIError(
16339
16615
  "NO_KEY",
16340
- `No key found for profile "${profileName}". Run \`tc init\` first.`,
16616
+ `No key found for profile "${profileName}". Run \`tc --profile ${profileName} auth rotate\` to create a new key and sign in.`,
16341
16617
  ExitCode.AUTH_REQUIRED
16342
16618
  );
16343
16619
  }
16344
- const profile = snapshot.profile ?? await ProfileManager.getProfile(profileName);
16345
16620
  if (options.permissions !== void 0) validateLoginPermissions(options.permissions);
16346
16621
  const permissions = options.permissions === void 0 ? void 0 : scopedLoginPermissions(options.permissions);
16347
16622
  if (permissions !== void 0) {
@@ -16609,7 +16884,7 @@ function registerDelegationCommand(program) {
16609
16884
  expiry
16610
16885
  });
16611
16886
  if (!result.ok) {
16612
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16887
+ throw cliErrorFromService(result.error);
16613
16888
  }
16614
16889
  outputJson({
16615
16890
  cid: result.data.cid,
@@ -16629,7 +16904,7 @@ function registerDelegationCommand(program) {
16629
16904
  const node = await ensureAuthenticated(ctx);
16630
16905
  const result = await node.delegationManager.list();
16631
16906
  if (!result.ok) {
16632
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16907
+ throw cliErrorFromService(result.error);
16633
16908
  }
16634
16909
  let delegations = result.data;
16635
16910
  if (options.granted) {
@@ -16675,7 +16950,7 @@ function registerDelegationCommand(program) {
16675
16950
  const node = await ensureAuthenticated(ctx);
16676
16951
  const result = await node.delegationManager.revoke(cid);
16677
16952
  if (!result.ok) {
16678
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
16953
+ throw cliErrorFromService(result.error);
16679
16954
  }
16680
16955
  outputJson({ cid, revoked: true });
16681
16956
  } catch (error) {
@@ -16800,7 +17075,6 @@ function registerDoctorCommand(program) {
16800
17075
  init_profiles();
16801
17076
  init_formatter();
16802
17077
  init_errors();
16803
- init_constants();
16804
17078
  import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
16805
17079
  import { resolve } from "path";
16806
17080
  init_theme();
@@ -16817,7 +17091,7 @@ function registerDuckdbCommand(program) {
16817
17091
  () => node.duckdb.db(options.db).query(sqlStr, params)
16818
17092
  );
16819
17093
  if (!result.ok) {
16820
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17094
+ throw cliErrorFromService(result.error);
16821
17095
  }
16822
17096
  const { columns, rows, rowCount } = result.data;
16823
17097
  if (shouldOutputJson()) {
@@ -16849,7 +17123,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16849
17123
  () => node.duckdb.db(options.db).execute(sqlStr, params)
16850
17124
  );
16851
17125
  if (!result.ok) {
16852
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17126
+ throw cliErrorFromService(result.error);
16853
17127
  }
16854
17128
  outputJson({ changes: result.data.changes });
16855
17129
  } catch (error) {
@@ -16866,7 +17140,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16866
17140
  () => node.duckdb.db(options.db).describe()
16867
17141
  );
16868
17142
  if (!result.ok) {
16869
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17143
+ throw cliErrorFromService(result.error);
16870
17144
  }
16871
17145
  const schema = result.data;
16872
17146
  if (shouldOutputJson()) {
@@ -16911,7 +17185,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16911
17185
  () => node.duckdb.db(options.db).export()
16912
17186
  );
16913
17187
  if (!result.ok) {
16914
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17188
+ throw cliErrorFromService(result.error);
16915
17189
  }
16916
17190
  const blob = result.data;
16917
17191
  const buffer = Buffer.from(await blob.arrayBuffer());
@@ -16938,7 +17212,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
16938
17212
  () => node.duckdb.db(options.db).import(bytes)
16939
17213
  );
16940
17214
  if (!result.ok) {
16941
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
17215
+ throw cliErrorFromService(result.error);
16942
17216
  }
16943
17217
  outputJson({
16944
17218
  file: filePath,
@@ -17075,7 +17349,7 @@ init_theme();
17075
17349
  async function throwKvError(error, spaceUri, profileName) {
17076
17350
  const hosted = await unhostedSpaceError(error, spaceUri, profileName);
17077
17351
  if (hosted) throw hosted;
17078
- throw new CLIError(error.code, error.message, ExitCode.ERROR);
17352
+ throw cliErrorFromService(error);
17079
17353
  }
17080
17354
  function isByteCount(value) {
17081
17355
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
@@ -17088,8 +17362,8 @@ function storageQuotaError(error) {
17088
17362
  }
17089
17363
  function assertAddressableKey(key) {
17090
17364
  for (let i = 0; i < key.length; i++) {
17091
- const code2 = key.charCodeAt(i);
17092
- if (code2 <= 32 || code2 === 127) {
17365
+ const code3 = key.charCodeAt(i);
17366
+ if (code3 <= 32 || code3 === 127) {
17093
17367
  throw new CLIError("USAGE_ERROR", "KV keys cannot contain spaces or control characters; use a URL-safe key", ExitCode.USAGE_ERROR);
17094
17368
  }
17095
17369
  }
@@ -17919,8 +18193,8 @@ async function writeSecretFile(path, value) {
17919
18193
  if (created) {
17920
18194
  await rm3(temp, { force: true }).catch(() => void 0);
17921
18195
  }
17922
- const code2 = error instanceof Error && "code" in error && typeof error.code === "string" ? ` (${error.code})` : "";
17923
- throw new CLIError("ERROR", `Could not write secret output "${path}"${code2}.`, ExitCode.ERROR);
18196
+ const code3 = error instanceof Error && "code" in error && typeof error.code === "string" ? ` (${error.code})` : "";
18197
+ throw new CLIError("ERROR", `Could not write secret output "${path}"${code3}.`, ExitCode.ERROR);
17924
18198
  }
17925
18199
  try {
17926
18200
  const parent = await open2(parentPath, "r");
@@ -18058,8 +18332,8 @@ function shouldRequestSecretPermissions(error) {
18058
18332
  function thrownPermissionError(error) {
18059
18333
  const record = error;
18060
18334
  const message = typeof record?.message === "string" ? record.message : String(error);
18061
- const code2 = typeof record?.code === "string" ? record.code : "PERMISSION_DENIED";
18062
- if (code2 !== "PERMISSION_DENIED" && !/permission|session expired|autosign|capabilit/i.test(message)) {
18335
+ const code3 = typeof record?.code === "string" ? record.code : "PERMISSION_DENIED";
18336
+ if (code3 !== "PERMISSION_DENIED" && !/permission|session expired|autosign|capabilit/i.test(message)) {
18063
18337
  return null;
18064
18338
  }
18065
18339
  return {
@@ -18267,9 +18541,9 @@ async function resolveDelegatedSecretSource(source, secretPath, space = SECRETS_
18267
18541
  return { ...selected, source };
18268
18542
  }
18269
18543
  function mapEncryptionResultError(error) {
18270
- const code2 = error.code || "DECRYPTION_FAILED";
18271
- const exitCode = code2 === "PERMISSION_DENIED" ? ExitCode.PERMISSION_DENIED : code2 === "NOT_FOUND" ? ExitCode.NOT_FOUND : code2 === "NETWORK_ERROR" || code2 === "TRANSPORT_ERROR" ? ExitCode.NETWORK_ERROR : ExitCode.ERROR;
18272
- return new CLIError(code2, error.message, exitCode);
18544
+ const code3 = error.code || "DECRYPTION_FAILED";
18545
+ const exitCode = code3 === "PERMISSION_DENIED" ? ExitCode.PERMISSION_DENIED : code3 === "NOT_FOUND" ? ExitCode.NOT_FOUND : code3 === "NETWORK_ERROR" || code3 === "TRANSPORT_ERROR" ? ExitCode.NETWORK_ERROR : ExitCode.ERROR;
18546
+ return new CLIError(code3, error.message, exitCode);
18273
18547
  }
18274
18548
  function parseDecryptedSecretPayload(data, secretPath) {
18275
18549
  const text = new TextDecoder().decode(data);
@@ -18592,7 +18866,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18592
18866
  operation: () => secrets2.list(scopeOptions)
18593
18867
  });
18594
18868
  if (!result.ok) {
18595
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
18869
+ throw cliErrorFromService(result.error);
18596
18870
  }
18597
18871
  const secretNames = Array.isArray(result.data) ? result.data : [];
18598
18872
  outputJson({
@@ -18709,7 +18983,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18709
18983
  operation: () => secrets2.put(name, secretValue, scopeOptions)
18710
18984
  });
18711
18985
  if (!result.ok) {
18712
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
18986
+ throw cliErrorFromService(result.error);
18713
18987
  }
18714
18988
  outputJson({ name, written: true });
18715
18989
  } catch (error) {
@@ -18735,7 +19009,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
18735
19009
  operation: () => secrets2.delete(name, scopeOptions)
18736
19010
  });
18737
19011
  if (!result.ok) {
18738
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
19012
+ throw cliErrorFromService(result.error);
18739
19013
  }
18740
19014
  outputJson({ name, deleted: true });
18741
19015
  } catch (error) {
@@ -18803,13 +19077,13 @@ function _abool2(value, title = "") {
18803
19077
  }
18804
19078
  return value;
18805
19079
  }
18806
- function _abytes2(value, length3, title = "") {
19080
+ function _abytes2(value, length4, title = "") {
18807
19081
  const bytes = isBytes(value);
18808
19082
  const len = value?.length;
18809
- const needsLen = length3 !== void 0;
18810
- if (!bytes || needsLen && len !== length3) {
19083
+ const needsLen = length4 !== void 0;
19084
+ if (!bytes || needsLen && len !== length4) {
18811
19085
  const prefix = title && `"${title}" `;
18812
- const ofLen = needsLen ? ` of length ${length3}` : "";
19086
+ const ofLen = needsLen ? ` of length ${length4}` : "";
18813
19087
  const got = bytes ? `length=${len}` : `type=${typeof value}`;
18814
19088
  throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
18815
19089
  }
@@ -19332,15 +19606,15 @@ var wNAF = class {
19332
19606
  const { windows, windowSize } = calcWOpts(W, this.bits);
19333
19607
  const points = [];
19334
19608
  let p = point;
19335
- let base3 = p;
19609
+ let base4 = p;
19336
19610
  for (let window = 0; window < windows; window++) {
19337
- base3 = p;
19338
- points.push(base3);
19611
+ base4 = p;
19612
+ points.push(base4);
19339
19613
  for (let i = 1; i < windowSize; i++) {
19340
- base3 = base3.add(p);
19341
- points.push(base3);
19614
+ base4 = base4.add(p);
19615
+ points.push(base4);
19342
19616
  }
19343
- p = base3.double();
19617
+ p = base4.double();
19344
19618
  }
19345
19619
  return points;
19346
19620
  }
@@ -20360,13 +20634,13 @@ var BLAKE2 = class extends Hash {
20360
20634
  return res;
20361
20635
  }
20362
20636
  _cloneInto(to) {
20363
- const { buffer, length: length3, finished, destroyed, outputLen, pos } = this;
20637
+ const { buffer, length: length4, finished, destroyed, outputLen, pos } = this;
20364
20638
  to || (to = new this.constructor({ dkLen: outputLen }));
20365
20639
  to.set(...this.get());
20366
20640
  to.buffer.set(buffer);
20367
20641
  to.destroyed = destroyed;
20368
20642
  to.finished = finished;
20369
- to.length = length3;
20643
+ to.length = length4;
20370
20644
  to.pos = pos;
20371
20645
  to.outputLen = outputLen;
20372
20646
  return to;
@@ -21600,8 +21874,8 @@ function isValidJWT2(jwt, alg) {
21600
21874
  const [header] = jwt.split(".");
21601
21875
  if (!header)
21602
21876
  return false;
21603
- const base642 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
21604
- const decoded = JSON.parse(atob(base642));
21877
+ const base6422 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
21878
+ const decoded = JSON.parse(atob(base6422));
21605
21879
  if (typeof decoded !== "object" || decoded === null)
21606
21880
  return false;
21607
21881
  if ("typ" in decoded && decoded?.typ !== "JWT")
@@ -24255,23 +24529,23 @@ var ZodEffects2 = class extends ZodType2 {
24255
24529
  }
24256
24530
  if (effect.type === "transform") {
24257
24531
  if (ctx.common.async === false) {
24258
- const base3 = this._def.schema._parseSync({
24532
+ const base33 = this._def.schema._parseSync({
24259
24533
  data: ctx.data,
24260
24534
  path: ctx.path,
24261
24535
  parent: ctx
24262
24536
  });
24263
- if (!isValid2(base3))
24537
+ if (!isValid2(base33))
24264
24538
  return INVALID2;
24265
- const result = effect.transform(base3.value, checkCtx);
24539
+ const result = effect.transform(base33.value, checkCtx);
24266
24540
  if (result instanceof Promise) {
24267
24541
  throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`);
24268
24542
  }
24269
24543
  return { status: status.value, value: result };
24270
24544
  } else {
24271
- return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base3) => {
24272
- if (!isValid2(base3))
24545
+ return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base33) => {
24546
+ if (!isValid2(base33))
24273
24547
  return INVALID2;
24274
- return Promise.resolve(effect.transform(base3.value, checkCtx)).then((result) => ({
24548
+ return Promise.resolve(effect.transform(base33.value, checkCtx)).then((result) => ({
24275
24549
  status: status.value,
24276
24550
  value: result
24277
24551
  }));
@@ -25179,9 +25453,9 @@ var cache = /* @__PURE__ */ new WeakMap();
25179
25453
  function baseCache(cid2) {
25180
25454
  const baseCache22 = cache.get(cid2);
25181
25455
  if (baseCache22 == null) {
25182
- const baseCache3 = /* @__PURE__ */ new Map();
25183
- cache.set(cid2, baseCache3);
25184
- return baseCache3;
25456
+ const baseCache32 = /* @__PURE__ */ new Map();
25457
+ cache.set(cid2, baseCache32);
25458
+ return baseCache32;
25185
25459
  }
25186
25460
  return baseCache22;
25187
25461
  }
@@ -26419,7 +26693,7 @@ function base2(ALPHABET, name) {
26419
26693
  var LEADER = ALPHABET.charAt(0);
26420
26694
  var FACTOR = Math.log(BASE) / Math.log(256);
26421
26695
  var iFACTOR = Math.log(256) / Math.log(BASE);
26422
- function encode5(source) {
26696
+ function encode52(source) {
26423
26697
  if (source instanceof Uint8Array)
26424
26698
  ;
26425
26699
  else if (ArrayBuffer.isView(source)) {
@@ -26434,7 +26708,7 @@ function base2(ALPHABET, name) {
26434
26708
  return "";
26435
26709
  }
26436
26710
  var zeroes = 0;
26437
- var length3 = 0;
26711
+ var length32 = 0;
26438
26712
  var pbegin = 0;
26439
26713
  var pend = source.length;
26440
26714
  while (pbegin !== pend && source[pbegin] === 0) {
@@ -26446,7 +26720,7 @@ function base2(ALPHABET, name) {
26446
26720
  while (pbegin !== pend) {
26447
26721
  var carry = source[pbegin];
26448
26722
  var i2 = 0;
26449
- for (var it1 = size - 1; (carry !== 0 || i2 < length3) && it1 !== -1; it1--, i2++) {
26723
+ for (var it1 = size - 1; (carry !== 0 || i2 < length32) && it1 !== -1; it1--, i2++) {
26450
26724
  carry += 256 * b58[it1] >>> 0;
26451
26725
  b58[it1] = carry % BASE >>> 0;
26452
26726
  carry = carry / BASE >>> 0;
@@ -26454,10 +26728,10 @@ function base2(ALPHABET, name) {
26454
26728
  if (carry !== 0) {
26455
26729
  throw new Error("Non-zero carry");
26456
26730
  }
26457
- length3 = i2;
26731
+ length32 = i2;
26458
26732
  pbegin++;
26459
26733
  }
26460
- var it2 = size - length3;
26734
+ var it2 = size - length32;
26461
26735
  while (it2 !== size && b58[it2] === 0) {
26462
26736
  it2++;
26463
26737
  }
@@ -26479,7 +26753,7 @@ function base2(ALPHABET, name) {
26479
26753
  return;
26480
26754
  }
26481
26755
  var zeroes = 0;
26482
- var length3 = 0;
26756
+ var length32 = 0;
26483
26757
  while (source[psz] === LEADER) {
26484
26758
  zeroes++;
26485
26759
  psz++;
@@ -26492,7 +26766,7 @@ function base2(ALPHABET, name) {
26492
26766
  return;
26493
26767
  }
26494
26768
  var i2 = 0;
26495
- for (var it3 = size - 1; (carry !== 0 || i2 < length3) && it3 !== -1; it3--, i2++) {
26769
+ for (var it3 = size - 1; (carry !== 0 || i2 < length32) && it3 !== -1; it3--, i2++) {
26496
26770
  carry += BASE * b256[it3] >>> 0;
26497
26771
  b256[it3] = carry % 256 >>> 0;
26498
26772
  carry = carry / 256 >>> 0;
@@ -26500,13 +26774,13 @@ function base2(ALPHABET, name) {
26500
26774
  if (carry !== 0) {
26501
26775
  throw new Error("Non-zero carry");
26502
26776
  }
26503
- length3 = i2;
26777
+ length32 = i2;
26504
26778
  psz++;
26505
26779
  }
26506
26780
  if (source[psz] === " ") {
26507
26781
  return;
26508
26782
  }
26509
- var it4 = size - length3;
26783
+ var it4 = size - length32;
26510
26784
  while (it4 !== size && b256[it4] === 0) {
26511
26785
  it4++;
26512
26786
  }
@@ -26517,7 +26791,7 @@ function base2(ALPHABET, name) {
26517
26791
  }
26518
26792
  return vch;
26519
26793
  }
26520
- function decode9(string) {
26794
+ function decode92(string) {
26521
26795
  var buffer = decodeUnsafe(string);
26522
26796
  if (buffer) {
26523
26797
  return buffer;
@@ -26525,9 +26799,9 @@ function base2(ALPHABET, name) {
26525
26799
  throw new Error(`Non-${name} character`);
26526
26800
  }
26527
26801
  return {
26528
- encode: encode5,
26802
+ encode: encode52,
26529
26803
  decodeUnsafe,
26530
- decode: decode9
26804
+ decode: decode92
26531
26805
  };
26532
26806
  }
26533
26807
  var src2 = base2;
@@ -26625,16 +26899,16 @@ var Codec2 = class {
26625
26899
  return this.decoder.decode(input);
26626
26900
  }
26627
26901
  };
26628
- function from2({ name, prefix, encode: encode5, decode: decode9 }) {
26629
- return new Codec2(name, prefix, encode5, decode9);
26902
+ function from2({ name, prefix, encode: encode52, decode: decode92 }) {
26903
+ return new Codec2(name, prefix, encode52, decode92);
26630
26904
  }
26631
26905
  function baseX2({ name, prefix, alphabet }) {
26632
- const { encode: encode5, decode: decode9 } = base_x_default2(alphabet, name);
26906
+ const { encode: encode52, decode: decode92 } = base_x_default2(alphabet, name);
26633
26907
  return from2({
26634
26908
  prefix,
26635
26909
  name,
26636
- encode: encode5,
26637
- decode: (text) => coerce3(decode9(text))
26910
+ encode: encode52,
26911
+ decode: (text) => coerce3(decode92(text))
26638
26912
  });
26639
26913
  }
26640
26914
  function decode5(string, alphabetIdx, bitsPerChar, name) {
@@ -27004,24 +27278,24 @@ var Digest2 = class {
27004
27278
  this.bytes = bytes3;
27005
27279
  }
27006
27280
  };
27007
- function format2(link2, base3) {
27281
+ function format2(link2, base33) {
27008
27282
  const { bytes: bytes3, version } = link2;
27009
27283
  switch (version) {
27010
27284
  case 0:
27011
- return toStringV02(bytes3, baseCache2(link2), base3 ?? base58btc2.encoder);
27285
+ return toStringV02(bytes3, baseCache2(link2), base33 ?? base58btc2.encoder);
27012
27286
  default:
27013
- return toStringV12(bytes3, baseCache2(link2), base3 ?? base322.encoder);
27287
+ return toStringV12(bytes3, baseCache2(link2), base33 ?? base322.encoder);
27014
27288
  }
27015
27289
  }
27016
27290
  var cache2 = /* @__PURE__ */ new WeakMap();
27017
27291
  function baseCache2(cid2) {
27018
- const baseCache3 = cache2.get(cid2);
27019
- if (baseCache3 == null) {
27292
+ const baseCache32 = cache2.get(cid2);
27293
+ if (baseCache32 == null) {
27020
27294
  const baseCache4 = /* @__PURE__ */ new Map();
27021
27295
  cache2.set(cid2, baseCache4);
27022
27296
  return baseCache4;
27023
27297
  }
27024
- return baseCache3;
27298
+ return baseCache32;
27025
27299
  }
27026
27300
  var CID2 = class _CID2 {
27027
27301
  code;
@@ -27100,8 +27374,8 @@ var CID2 = class _CID2 {
27100
27374
  const unknown = other;
27101
27375
  return unknown != null && self.code === unknown.code && self.version === unknown.version && equals4(self.multihash, unknown.multihash);
27102
27376
  }
27103
- toString(base3) {
27104
- return format2(this, base3);
27377
+ toString(base33) {
27378
+ return format2(this, base33);
27105
27379
  }
27106
27380
  toJSON() {
27107
27381
  return { "/": format2(this) };
@@ -27233,8 +27507,8 @@ var CID2 = class _CID2 {
27233
27507
  static inspectBytes(initialBytes) {
27234
27508
  let offset = 0;
27235
27509
  const next = () => {
27236
- const [i, length3] = decode7(initialBytes.subarray(offset));
27237
- offset += length3;
27510
+ const [i, length32] = decode7(initialBytes.subarray(offset));
27511
+ offset += length32;
27238
27512
  return i;
27239
27513
  };
27240
27514
  let version = next();
@@ -27261,8 +27535,8 @@ var CID2 = class _CID2 {
27261
27535
  * throw an error if encoding of the CID is not compatible with supplied (or
27262
27536
  * a default decoder).
27263
27537
  */
27264
- static parse(source, base3) {
27265
- const [prefix, bytes3] = parseCIDtoBytes2(source, base3);
27538
+ static parse(source, base33) {
27539
+ const [prefix, bytes3] = parseCIDtoBytes2(source, base33);
27266
27540
  const cid2 = _CID2.decode(bytes3);
27267
27541
  if (cid2.version === 0 && source[0] !== "Q") {
27268
27542
  throw Error("Version 0 CID string must not include multibase prefix");
@@ -27271,56 +27545,56 @@ var CID2 = class _CID2 {
27271
27545
  return cid2;
27272
27546
  }
27273
27547
  };
27274
- function parseCIDtoBytes2(source, base3) {
27548
+ function parseCIDtoBytes2(source, base33) {
27275
27549
  switch (source[0]) {
27276
27550
  // CIDv0 is parsed differently
27277
27551
  case "Q": {
27278
- const decoder = base3 ?? base58btc2;
27552
+ const decoder = base33 ?? base58btc2;
27279
27553
  return [
27280
27554
  base58btc2.prefix,
27281
27555
  decoder.decode(`${base58btc2.prefix}${source}`)
27282
27556
  ];
27283
27557
  }
27284
27558
  case base58btc2.prefix: {
27285
- const decoder = base3 ?? base58btc2;
27559
+ const decoder = base33 ?? base58btc2;
27286
27560
  return [base58btc2.prefix, decoder.decode(source)];
27287
27561
  }
27288
27562
  case base322.prefix: {
27289
- const decoder = base3 ?? base322;
27563
+ const decoder = base33 ?? base322;
27290
27564
  return [base322.prefix, decoder.decode(source)];
27291
27565
  }
27292
27566
  case base362.prefix: {
27293
- const decoder = base3 ?? base362;
27567
+ const decoder = base33 ?? base362;
27294
27568
  return [base362.prefix, decoder.decode(source)];
27295
27569
  }
27296
27570
  default: {
27297
- if (base3 == null) {
27571
+ if (base33 == null) {
27298
27572
  throw Error("To parse non base32, base36 or base58btc encoded CID multibase decoder must be provided");
27299
27573
  }
27300
- return [source[0], base3.decode(source)];
27574
+ return [source[0], base33.decode(source)];
27301
27575
  }
27302
27576
  }
27303
27577
  }
27304
- function toStringV02(bytes3, cache3, base3) {
27305
- const { prefix } = base3;
27578
+ function toStringV02(bytes3, cache32, base33) {
27579
+ const { prefix } = base33;
27306
27580
  if (prefix !== base58btc2.prefix) {
27307
- throw Error(`Cannot string encode V0 in ${base3.name} encoding`);
27581
+ throw Error(`Cannot string encode V0 in ${base33.name} encoding`);
27308
27582
  }
27309
- const cid2 = cache3.get(prefix);
27583
+ const cid2 = cache32.get(prefix);
27310
27584
  if (cid2 == null) {
27311
- const cid3 = base3.encode(bytes3).slice(1);
27312
- cache3.set(prefix, cid3);
27585
+ const cid3 = base33.encode(bytes3).slice(1);
27586
+ cache32.set(prefix, cid3);
27313
27587
  return cid3;
27314
27588
  } else {
27315
27589
  return cid2;
27316
27590
  }
27317
27591
  }
27318
- function toStringV12(bytes3, cache3, base3) {
27319
- const { prefix } = base3;
27320
- const cid2 = cache3.get(prefix);
27592
+ function toStringV12(bytes3, cache32, base33) {
27593
+ const { prefix } = base33;
27594
+ const cid2 = cache32.get(prefix);
27321
27595
  if (cid2 == null) {
27322
- const cid3 = base3.encode(bytes3);
27323
- cache3.set(prefix, cid3);
27596
+ const cid3 = base33.encode(bytes3);
27597
+ cache32.set(prefix, cid3);
27324
27598
  return cid3;
27325
27599
  } else {
27326
27600
  return cid2;
@@ -27374,11 +27648,12 @@ function historyRecordForPublishedShare(result, now = /* @__PURE__ */ new Date()
27374
27648
  };
27375
27649
  }
27376
27650
  var ShareNotifyError = class extends Error {
27377
- code = "delivery-failed";
27378
- constructor(message = "share delivery did not complete") {
27651
+ constructor(message = "share delivery did not complete", reason) {
27379
27652
  super(message);
27653
+ this.reason = reason;
27380
27654
  this.name = "ShareNotifyError";
27381
27655
  }
27656
+ code = "delivery-failed";
27382
27657
  };
27383
27658
  function recipientMatchesShareRecord(record, recipient) {
27384
27659
  const matcher = record.recipientMatcher;
@@ -27388,6 +27663,11 @@ function recipientMatchesShareRecord(record, recipient) {
27388
27663
  if (matcher.kind === "emailDomain") return mailbox.domain === matcher.value;
27389
27664
  return false;
27390
27665
  }
27666
+ function shareDeliveryWindowExpiresAt(record) {
27667
+ const expiresAt = Math.min(Date.parse(record.expiresAt), Date.parse(record.registeredAt) + 5 * 60 * 1e3);
27668
+ if (!Number.isFinite(expiresAt)) throw new ShareNotifyError("share delivery history has invalid timestamps");
27669
+ return expiresAt;
27670
+ }
27391
27671
  async function notifyShare(input) {
27392
27672
  const mailbox = canonicalMailbox(input.recipient);
27393
27673
  if (!input.shareId || mailbox === void 0) throw new ShareNotifyError("recipient is invalid");
@@ -27398,19 +27678,59 @@ async function notifyShare(input) {
27398
27678
  const idempotencyKey = input.idempotencyKey ?? await defaultIdempotencyKey(input.shareId, recipient);
27399
27679
  const attemptsLimit = input.maxAttempts ?? 3;
27400
27680
  if (!Number.isSafeInteger(attemptsLimit) || attemptsLimit < 1 || attemptsLimit > 8) throw new ShareNotifyError("maxAttempts is invalid");
27681
+ if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
27682
+ if (input.checkDeliveryWindow && input.record !== void 0 && shareDeliveryWindowExpiresAt(input.record) <= Date.now()) {
27683
+ return {
27684
+ protocol: "tinycloud-share",
27685
+ version: 1,
27686
+ shareId: input.shareId,
27687
+ state: "partial-failure",
27688
+ idempotencyKey,
27689
+ attempts: 1,
27690
+ retryable: false,
27691
+ reason: "delivery-window-expired"
27692
+ };
27693
+ }
27401
27694
  let attempts = 0;
27402
- let lastError;
27403
27695
  while (attempts < attemptsLimit) {
27404
27696
  if (input.signal?.aborted) throw new ShareNotifyError("share delivery was cancelled");
27405
27697
  attempts += 1;
27406
27698
  try {
27407
27699
  const state = await input.adapter.deliver({ shareId: input.shareId, recipient, idempotencyKey, ...input.record === void 0 ? {} : { record: input.record }, ...input.signal === void 0 ? {} : { signal: input.signal } });
27408
- return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state, idempotencyKey, attempts };
27700
+ return {
27701
+ protocol: "tinycloud-share",
27702
+ version: 1,
27703
+ shareId: input.shareId,
27704
+ state: state === "delivered" && input.record?.deliveredRecipients?.includes(recipient) ? "already-delivered" : state,
27705
+ idempotencyKey,
27706
+ attempts
27707
+ };
27409
27708
  } catch (error) {
27410
- lastError = error;
27709
+ if (error instanceof ShareNotifyError && error.reason === "delivery-window-expired") {
27710
+ return {
27711
+ protocol: "tinycloud-share",
27712
+ version: 1,
27713
+ shareId: input.shareId,
27714
+ state: "partial-failure",
27715
+ idempotencyKey,
27716
+ attempts,
27717
+ retryable: false,
27718
+ reason: "delivery-window-expired"
27719
+ };
27720
+ }
27721
+ if (error !== null && typeof error === "object" && "status" in error && (error.status === 401 || error.status === 403)) {
27722
+ return {
27723
+ protocol: "tinycloud-share",
27724
+ version: 1,
27725
+ shareId: input.shareId,
27726
+ state: "partial-failure",
27727
+ idempotencyKey,
27728
+ attempts,
27729
+ retryable: false
27730
+ };
27731
+ }
27411
27732
  }
27412
27733
  }
27413
- void lastError;
27414
27734
  return { protocol: "tinycloud-share", version: 1, shareId: input.shareId, state: "partial-failure", idempotencyKey, attempts, retryable: true };
27415
27735
  }
27416
27736
  async function defaultIdempotencyKey(shareId, recipient) {
@@ -27454,7 +27774,11 @@ async function revokeShare(input) {
27454
27774
  });
27455
27775
  }
27456
27776
  const revokedAt = (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
27457
- if (input.records !== void 0) await input.records.put({ ...input.record, revokedAt });
27777
+ if (input.records?.update !== void 0) {
27778
+ await input.records.update(input.record.shareId, (record) => ({ ...record, revokedAt }));
27779
+ } else if (input.records !== void 0) {
27780
+ await input.records.put({ ...input.record, revokedAt });
27781
+ }
27458
27782
  return { state: "revoked", target, delegationCid, revokedAt };
27459
27783
  }
27460
27784
  function redactRecord(record, revealLink, link2) {
@@ -27502,225 +27826,1548 @@ function parseNativeShareUrl(value) {
27502
27826
  return token;
27503
27827
  }
27504
27828
 
27505
- // src/commands/share.ts
27506
- init_formatter();
27507
- init_errors();
27508
-
27509
- // src/share/output.ts
27510
- function writeJson2(value) {
27511
- process.stdout.write(`${JSON.stringify(value, null, 2)}
27512
- `);
27513
- }
27514
- function authorizationRequiredJson(result) {
27515
- return {
27516
- state: "authorization-required",
27517
- method: result.method,
27518
- next: "complete authorization through the configured authority adapter, then retry with the required proof"
27519
- };
27520
- }
27521
- function publishHuman(result) {
27522
- process.stdout.write(`${result.url}
27523
- `);
27524
- }
27525
- function inspectHuman(result) {
27526
- const metadata = result.metadata;
27527
- process.stdout.write([
27528
- `Share ${metadata.shareId}`,
27529
- `File: ${metadata.display.filename ?? "unnamed"}`,
27530
- `Target: ${metadata.target.kind === "bearer" ? "bearer (anyone with the complete link can read)" : metadata.target.kind}`,
27531
- `Expires: ${metadata.expiresAt}`,
27532
- `Resource: ${metadata.resource.path}`,
27533
- `Link format: ${result.link.kind}`
27534
- ].join("\n") + "\n");
27535
- }
27536
- function receiveHuman(path) {
27537
- process.stdout.write(`${path}
27538
- `);
27539
- }
27540
- function receiveJson(result, path) {
27541
- writeJson2({ protocol: "tinycloud-share", version: 1, path, metadata: result.metadata });
27542
- }
27543
-
27544
- // src/share/io.ts
27545
- import { constants } from "fs";
27546
- import { lstat as lstat3, mkdir as mkdir3, mkdtemp, open as open4, readFile as readFile9, realpath, stat as stat2, link, rename as rename3, rm as rm4, unlink } from "fs/promises";
27547
- import { randomBytes as randomBytes4 } from "crypto";
27548
- import { basename as basename3, join as join6, resolve as resolve2, sep } from "path";
27549
- init_errors();
27550
- var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
27551
- var MAX_SHARE_URL_BYTES = 64 * 1024;
27552
- async function readBoundedStdin(limit = MAX_SHARE_STDIN_BYTES) {
27553
- if (!Number.isSafeInteger(limit) || limit <= 0) throw new Error("MAX_BYTES_EXCEEDED");
27554
- const chunks = [];
27555
- let total = 0;
27556
- for await (const chunk of process.stdin) {
27557
- const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
27558
- total += bytes.byteLength;
27559
- if (total > limit) throw new Error("MAX_BYTES_EXCEEDED");
27560
- chunks.push(bytes);
27829
+ // ../share-envelope/dist/index.js
27830
+ var empty3 = new Uint8Array(0);
27831
+ function equals5(aa, bb) {
27832
+ if (aa === bb) {
27833
+ return true;
27561
27834
  }
27562
- return new Uint8Array(Buffer.concat(chunks, total));
27563
- }
27564
- async function readBoundedUrlStdin() {
27565
- const bytes = await readBoundedStdin(MAX_SHARE_URL_BYTES);
27566
- const value = new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim();
27567
- if (value.length === 0 || /\s/.test(value)) throw new Error("INVALID_ARGUMENT");
27568
- return value;
27569
- }
27570
- function safeFilename(value) {
27571
- return shareFilename(value);
27572
- }
27573
- function shareFilename(value) {
27574
- try {
27575
- return canonicalShareFilename(value);
27576
- } catch {
27577
- throw new CLIError(
27578
- "UNSAFE_FILENAME",
27579
- hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment",
27580
- 8
27581
- );
27835
+ if (aa.byteLength !== bb.byteLength) {
27836
+ return false;
27582
27837
  }
27583
- }
27584
- function shareInputFilename(input, name) {
27585
- return shareFilename(name ?? (input === "-" ? "stdin.md" : basename3(resolve2(input))));
27586
- }
27587
- async function readShareInput(input, name, limit = MAX_SHARE_STDIN_BYTES) {
27588
- const filename = shareInputFilename(input, name);
27589
- if (input === "-") return { bytes: await readBoundedStdin(limit), filename };
27590
- const path = resolve2(input);
27591
- const info = await stat2(path);
27592
- if (!info.isFile() || info.size > limit) throw new Error("MAX_BYTES_EXCEEDED");
27593
- const bytes = new Uint8Array(await readFile9(path));
27594
- if (bytes.byteLength > limit) throw new Error("MAX_BYTES_EXCEEDED");
27595
- return { bytes, filename };
27596
- }
27597
- async function assertDirectory(path) {
27598
- const absolute = resolve2(path);
27599
- const segments = absolute.split(sep).filter(Boolean);
27600
- let current = absolute.startsWith(sep) ? sep : "";
27601
- for (const segment of segments) {
27602
- current = current === sep ? join6(current, segment) : join6(current, segment);
27603
- try {
27604
- const info = await lstat3(current);
27605
- if (info.isSymbolicLink()) {
27606
- const canonical = await realpath(current);
27607
- if (current !== "/tmp" && current !== "/var") throw new Error("OUTPUT_EXISTS");
27608
- if (canonical !== `/private${current}`) throw new Error("OUTPUT_EXISTS");
27609
- } else if (!info.isDirectory()) throw new Error("OUTPUT_EXISTS");
27610
- } catch (error) {
27611
- if (error.code !== "ENOENT") throw error;
27612
- await mkdir3(current, { mode: 448 });
27613
- const created = await lstat3(current);
27614
- if (created.isSymbolicLink() || !created.isDirectory()) throw new Error("OUTPUT_EXISTS");
27838
+ for (let ii = 0; ii < aa.byteLength; ii++) {
27839
+ if (aa[ii] !== bb[ii]) {
27840
+ return false;
27615
27841
  }
27616
27842
  }
27843
+ return true;
27617
27844
  }
27618
- async function writeShareOutput(directory, filename, bytes, force) {
27619
- const outputDirectory = resolve2(directory);
27620
- await assertDirectory(outputDirectory);
27621
- const safeName = safeFilename(filename);
27622
- const directoryHandle = await open4(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
27623
- const stableDirectory = await realpath(outputDirectory);
27624
- const outputPath = join6(stableDirectory, safeName);
27625
- const directoryIdentity = await directoryHandle.stat();
27626
- const assertStableDirectory = async () => {
27627
- const current = await stat2(stableDirectory);
27628
- if (current.dev !== directoryIdentity.dev || current.ino !== directoryIdentity.ino) throw new Error("OUTPUT_EXISTS");
27629
- };
27630
- await assertStableDirectory();
27631
- const stagingDirectory = await mkdtemp(join6(stableDirectory, ".tinycloud-share-stage-"));
27632
- const stagingInfo = await lstat3(stagingDirectory);
27633
- if (!stagingInfo.isDirectory() || (stagingInfo.mode & 511) !== 448) throw new Error("OUTPUT_EXISTS");
27634
- const stagingPath = join6(stagingDirectory, `.tinycloud-share-${randomBytes4(16).toString("hex")}.tmp`);
27635
- let temporaryPath;
27636
- let handle;
27637
- try {
27638
- await assertStableDirectory();
27639
- try {
27640
- const existing = await lstat3(outputPath);
27641
- if (existing.isSymbolicLink()) throw new Error("UNSAFE_FILENAME");
27642
- } catch (error) {
27643
- if (error.code !== "ENOENT") throw error;
27644
- }
27645
- temporaryPath = stagingPath;
27646
- handle = await open4(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
27647
- await handle.writeFile(bytes);
27648
- await handle.close();
27649
- handle = void 0;
27650
- await assertStableDirectory();
27651
- if (force) {
27652
- await rename3(temporaryPath, outputPath);
27653
- } else {
27654
- await link(temporaryPath, outputPath);
27655
- await unlink(temporaryPath);
27656
- }
27657
- await assertStableDirectory();
27658
- } catch (error) {
27659
- if (error.code === "EEXIST") throw new Error("OUTPUT_EXISTS");
27660
- if (error.code === "ELOOP") throw new Error("UNSAFE_FILENAME");
27661
- throw error;
27662
- } finally {
27663
- await handle?.close();
27664
- try {
27665
- if (temporaryPath !== void 0) await unlink(temporaryPath);
27666
- } catch {
27667
- }
27668
- await rm4(stagingDirectory, { recursive: true, force: true });
27669
- await directoryHandle.close();
27845
+ function coerce4(o) {
27846
+ if (o instanceof Uint8Array && o.constructor.name === "Uint8Array") {
27847
+ return o;
27670
27848
  }
27671
- return join6(outputDirectory, safeName);
27672
- }
27673
-
27674
- // src/share/errors.ts
27675
- var SharePublishAuthorityError = class extends Error {
27676
- constructor(failure) {
27677
- super(failure.kind);
27678
- this.failure = failure;
27679
- this.name = "SharePublishAuthorityError";
27849
+ if (o instanceof ArrayBuffer) {
27850
+ return new Uint8Array(o);
27680
27851
  }
27681
- };
27682
-
27683
- // src/commands/share.ts
27684
- var SHARE_ORIGIN = "https://share.tinycloud.xyz";
27685
- var shareServices = {};
27686
- function parseShareTarget(value) {
27687
- if (value === "anyone" || value === "bearer") return { kind: "bearer" };
27688
- if (value.startsWith("did:")) return { kind: "recipientDid", did: value };
27689
- if (value.startsWith("domain:")) return canonicalMailboxTarget({ kind: "emailDomain", domain: value.slice("domain:".length) });
27690
- if (value.startsWith("email:")) return canonicalMailboxTarget({ kind: "email", address: value.slice("email:".length) });
27691
- if (value.includes("@")) return canonicalMailboxTarget({ kind: "email", address: value });
27692
- throw new CLIError("INVALID_ARGUMENT", "--to must be anyone, a did:, an email address, or domain:example.com", 2);
27693
- }
27694
- function canonicalMailboxTarget(target) {
27695
- try {
27696
- return normalizeShareTarget(target);
27697
- } catch (error) {
27698
- throw new CLIError("INVALID_ARGUMENT", error instanceof TypeError ? error.message : "share recipient is invalid", 2);
27852
+ if (ArrayBuffer.isView(o)) {
27853
+ return new Uint8Array(o.buffer, o.byteOffset, o.byteLength);
27699
27854
  }
27855
+ throw new Error("Unknown type, must be binary type");
27700
27856
  }
27701
- function shareCliError(error) {
27702
- if (error instanceof CLIError) return error;
27703
- if (error instanceof SharePublishAuthorityError) {
27704
- const failure = error.failure;
27705
- const profileName = "profileName" in failure ? failure.profileName : void 0;
27706
- const localKey = "localKey" in failure && failure.localKey === true;
27707
- const profileHint = profileName === void 0 ? "" : `--profile ${profileName} `;
27708
- const loginHint = localKey ? `\`tc ${profileHint}auth login --method local\`` : `\`tc ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`tc ${profileHint}enable share\`)`;
27709
- if (failure.kind === "caveated-session") {
27710
- const holder = profileName === void 0 ? "this session" : `profile ${profileName}'s session`;
27711
- return new CLIError(
27712
- "PERMISSION_DENIED",
27713
- `${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`tc init --name publisher --key-only && tc --profile publisher enable share\``,
27714
- 5
27715
- );
27716
- }
27717
- if (failure.kind === "owner-space-unresolved") {
27718
- return new CLIError("AUTH_REQUIRED", `a valid signed TinyCloud session is required; run ${loginHint}`, 3);
27719
- }
27720
- if (failure.kind === "scope-denied") {
27721
- const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
27722
- 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}`;
27723
- return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; ${renew}`, 5);
27857
+ function base3(ALPHABET, name) {
27858
+ if (ALPHABET.length >= 255) {
27859
+ throw new TypeError("Alphabet too long");
27860
+ }
27861
+ var BASE_MAP = new Uint8Array(256);
27862
+ for (var j = 0; j < BASE_MAP.length; j++) {
27863
+ BASE_MAP[j] = 255;
27864
+ }
27865
+ for (var i = 0; i < ALPHABET.length; i++) {
27866
+ var x = ALPHABET.charAt(i);
27867
+ var xc = x.charCodeAt(0);
27868
+ if (BASE_MAP[xc] !== 255) {
27869
+ throw new TypeError(x + " is ambiguous");
27870
+ }
27871
+ BASE_MAP[xc] = i;
27872
+ }
27873
+ var BASE = ALPHABET.length;
27874
+ var LEADER = ALPHABET.charAt(0);
27875
+ var FACTOR = Math.log(BASE) / Math.log(256);
27876
+ var iFACTOR = Math.log(256) / Math.log(BASE);
27877
+ function encode32(source) {
27878
+ if (source instanceof Uint8Array)
27879
+ ;
27880
+ else if (ArrayBuffer.isView(source)) {
27881
+ source = new Uint8Array(source.buffer, source.byteOffset, source.byteLength);
27882
+ } else if (Array.isArray(source)) {
27883
+ source = Uint8Array.from(source);
27884
+ }
27885
+ if (!(source instanceof Uint8Array)) {
27886
+ throw new TypeError("Expected Uint8Array");
27887
+ }
27888
+ if (source.length === 0) {
27889
+ return "";
27890
+ }
27891
+ var zeroes = 0;
27892
+ var length22 = 0;
27893
+ var pbegin = 0;
27894
+ var pend = source.length;
27895
+ while (pbegin !== pend && source[pbegin] === 0) {
27896
+ pbegin++;
27897
+ zeroes++;
27898
+ }
27899
+ var size = (pend - pbegin) * iFACTOR + 1 >>> 0;
27900
+ var b58 = new Uint8Array(size);
27901
+ while (pbegin !== pend) {
27902
+ var carry = source[pbegin];
27903
+ var i2 = 0;
27904
+ for (var it1 = size - 1; (carry !== 0 || i2 < length22) && it1 !== -1; it1--, i2++) {
27905
+ carry += 256 * b58[it1] >>> 0;
27906
+ b58[it1] = carry % BASE >>> 0;
27907
+ carry = carry / BASE >>> 0;
27908
+ }
27909
+ if (carry !== 0) {
27910
+ throw new Error("Non-zero carry");
27911
+ }
27912
+ length22 = i2;
27913
+ pbegin++;
27914
+ }
27915
+ var it2 = size - length22;
27916
+ while (it2 !== size && b58[it2] === 0) {
27917
+ it2++;
27918
+ }
27919
+ var str = LEADER.repeat(zeroes);
27920
+ for (; it2 < size; ++it2) {
27921
+ str += ALPHABET.charAt(b58[it2]);
27922
+ }
27923
+ return str;
27924
+ }
27925
+ function decodeUnsafe(source) {
27926
+ if (typeof source !== "string") {
27927
+ throw new TypeError("Expected String");
27928
+ }
27929
+ if (source.length === 0) {
27930
+ return new Uint8Array();
27931
+ }
27932
+ var psz = 0;
27933
+ if (source[psz] === " ") {
27934
+ return;
27935
+ }
27936
+ var zeroes = 0;
27937
+ var length22 = 0;
27938
+ while (source[psz] === LEADER) {
27939
+ zeroes++;
27940
+ psz++;
27941
+ }
27942
+ var size = (source.length - psz) * FACTOR + 1 >>> 0;
27943
+ var b256 = new Uint8Array(size);
27944
+ while (source[psz]) {
27945
+ var carry = BASE_MAP[source.charCodeAt(psz)];
27946
+ if (carry === 255) {
27947
+ return;
27948
+ }
27949
+ var i2 = 0;
27950
+ for (var it3 = size - 1; (carry !== 0 || i2 < length22) && it3 !== -1; it3--, i2++) {
27951
+ carry += BASE * b256[it3] >>> 0;
27952
+ b256[it3] = carry % 256 >>> 0;
27953
+ carry = carry / 256 >>> 0;
27954
+ }
27955
+ if (carry !== 0) {
27956
+ throw new Error("Non-zero carry");
27957
+ }
27958
+ length22 = i2;
27959
+ psz++;
27960
+ }
27961
+ if (source[psz] === " ") {
27962
+ return;
27963
+ }
27964
+ var it4 = size - length22;
27965
+ while (it4 !== size && b256[it4] === 0) {
27966
+ it4++;
27967
+ }
27968
+ var vch = new Uint8Array(zeroes + (size - it4));
27969
+ var j2 = zeroes;
27970
+ while (it4 !== size) {
27971
+ vch[j2++] = b256[it4++];
27972
+ }
27973
+ return vch;
27974
+ }
27975
+ function decode52(string) {
27976
+ var buffer = decodeUnsafe(string);
27977
+ if (buffer) {
27978
+ return buffer;
27979
+ }
27980
+ throw new Error(`Non-${name} character`);
27981
+ }
27982
+ return {
27983
+ encode: encode32,
27984
+ decodeUnsafe,
27985
+ decode: decode52
27986
+ };
27987
+ }
27988
+ var src3 = base3;
27989
+ var _brrp__multiformats_scope_baseX3 = src3;
27990
+ var base_x_default3 = _brrp__multiformats_scope_baseX3;
27991
+ var Encoder3 = class {
27992
+ name;
27993
+ prefix;
27994
+ baseEncode;
27995
+ constructor(name, prefix, baseEncode) {
27996
+ this.name = name;
27997
+ this.prefix = prefix;
27998
+ this.baseEncode = baseEncode;
27999
+ }
28000
+ encode(bytes) {
28001
+ if (bytes instanceof Uint8Array) {
28002
+ return `${this.prefix}${this.baseEncode(bytes)}`;
28003
+ } else {
28004
+ throw Error("Unknown type, must be binary type");
28005
+ }
28006
+ }
28007
+ };
28008
+ var Decoder3 = class {
28009
+ name;
28010
+ prefix;
28011
+ baseDecode;
28012
+ prefixCodePoint;
28013
+ constructor(name, prefix, baseDecode) {
28014
+ this.name = name;
28015
+ this.prefix = prefix;
28016
+ const prefixCodePoint = prefix.codePointAt(0);
28017
+ if (prefixCodePoint === void 0) {
28018
+ throw new Error("Invalid prefix character");
28019
+ }
28020
+ this.prefixCodePoint = prefixCodePoint;
28021
+ this.baseDecode = baseDecode;
28022
+ }
28023
+ decode(text) {
28024
+ if (typeof text === "string") {
28025
+ if (text.codePointAt(0) !== this.prefixCodePoint) {
28026
+ throw Error(`Unable to decode multibase string ${JSON.stringify(text)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);
28027
+ }
28028
+ return this.baseDecode(text.slice(this.prefix.length));
28029
+ } else {
28030
+ throw Error("Can only multibase decode strings");
28031
+ }
28032
+ }
28033
+ or(decoder) {
28034
+ return or3(this, decoder);
28035
+ }
28036
+ };
28037
+ var ComposedDecoder3 = class {
28038
+ decoders;
28039
+ constructor(decoders) {
28040
+ this.decoders = decoders;
28041
+ }
28042
+ or(decoder) {
28043
+ return or3(this, decoder);
28044
+ }
28045
+ decode(input) {
28046
+ const prefix = input[0];
28047
+ const decoder = this.decoders[prefix];
28048
+ if (decoder != null) {
28049
+ return decoder.decode(input);
28050
+ } else {
28051
+ throw RangeError(`Unable to decode multibase string ${JSON.stringify(input)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`);
28052
+ }
28053
+ }
28054
+ };
28055
+ function or3(left, right) {
28056
+ return new ComposedDecoder3({
28057
+ ...left.decoders ?? { [left.prefix]: left },
28058
+ ...right.decoders ?? { [right.prefix]: right }
28059
+ });
28060
+ }
28061
+ var Codec3 = class {
28062
+ name;
28063
+ prefix;
28064
+ baseEncode;
28065
+ baseDecode;
28066
+ encoder;
28067
+ decoder;
28068
+ constructor(name, prefix, baseEncode, baseDecode) {
28069
+ this.name = name;
28070
+ this.prefix = prefix;
28071
+ this.baseEncode = baseEncode;
28072
+ this.baseDecode = baseDecode;
28073
+ this.encoder = new Encoder3(name, prefix, baseEncode);
28074
+ this.decoder = new Decoder3(name, prefix, baseDecode);
28075
+ }
28076
+ encode(input) {
28077
+ return this.encoder.encode(input);
28078
+ }
28079
+ decode(input) {
28080
+ return this.decoder.decode(input);
28081
+ }
28082
+ };
28083
+ function from3({ name, prefix, encode: encode32, decode: decode52 }) {
28084
+ return new Codec3(name, prefix, encode32, decode52);
28085
+ }
28086
+ function baseX3({ name, prefix, alphabet }) {
28087
+ const { encode: encode32, decode: decode52 } = base_x_default3(alphabet, name);
28088
+ return from3({
28089
+ prefix,
28090
+ name,
28091
+ encode: encode32,
28092
+ decode: (text) => coerce4(decode52(text))
28093
+ });
28094
+ }
28095
+ function decode9(string, alphabetIdx, bitsPerChar, name) {
28096
+ let end = string.length;
28097
+ while (string[end - 1] === "=") {
28098
+ --end;
28099
+ }
28100
+ const out = new Uint8Array(end * bitsPerChar / 8 | 0);
28101
+ let bits = 0;
28102
+ let buffer = 0;
28103
+ let written = 0;
28104
+ for (let i = 0; i < end; ++i) {
28105
+ const value = alphabetIdx[string[i]];
28106
+ if (value === void 0) {
28107
+ throw new SyntaxError(`Non-${name} character`);
28108
+ }
28109
+ buffer = buffer << bitsPerChar | value;
28110
+ bits += bitsPerChar;
28111
+ if (bits >= 8) {
28112
+ bits -= 8;
28113
+ out[written++] = 255 & buffer >> bits;
28114
+ }
28115
+ }
28116
+ if (bits >= bitsPerChar || (255 & buffer << 8 - bits) !== 0) {
28117
+ throw new SyntaxError("Unexpected end of data");
28118
+ }
28119
+ return out;
28120
+ }
28121
+ function encode5(data, alphabet, bitsPerChar) {
28122
+ const pad = alphabet[alphabet.length - 1] === "=";
28123
+ const mask = (1 << bitsPerChar) - 1;
28124
+ let out = "";
28125
+ let bits = 0;
28126
+ let buffer = 0;
28127
+ for (let i = 0; i < data.length; ++i) {
28128
+ buffer = buffer << 8 | data[i];
28129
+ bits += 8;
28130
+ while (bits > bitsPerChar) {
28131
+ bits -= bitsPerChar;
28132
+ out += alphabet[mask & buffer >> bits];
28133
+ }
28134
+ }
28135
+ if (bits !== 0) {
28136
+ out += alphabet[mask & buffer << bitsPerChar - bits];
28137
+ }
28138
+ if (pad) {
28139
+ while ((out.length * bitsPerChar & 7) !== 0) {
28140
+ out += "=";
28141
+ }
28142
+ }
28143
+ return out;
28144
+ }
28145
+ function createAlphabetIdx3(alphabet) {
28146
+ const alphabetIdx = {};
28147
+ for (let i = 0; i < alphabet.length; ++i) {
28148
+ alphabetIdx[alphabet[i]] = i;
28149
+ }
28150
+ return alphabetIdx;
28151
+ }
28152
+ function rfc46483({ name, prefix, bitsPerChar, alphabet }) {
28153
+ const alphabetIdx = createAlphabetIdx3(alphabet);
28154
+ return from3({
28155
+ prefix,
28156
+ name,
28157
+ encode(input) {
28158
+ return encode5(input, alphabet, bitsPerChar);
28159
+ },
28160
+ decode(input) {
28161
+ return decode9(input, alphabetIdx, bitsPerChar, name);
28162
+ }
28163
+ });
28164
+ }
28165
+ var base323 = rfc46483({
28166
+ prefix: "b",
28167
+ name: "base32",
28168
+ alphabet: "abcdefghijklmnopqrstuvwxyz234567",
28169
+ bitsPerChar: 5
28170
+ });
28171
+ var base32upper3 = rfc46483({
28172
+ prefix: "B",
28173
+ name: "base32upper",
28174
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
28175
+ bitsPerChar: 5
28176
+ });
28177
+ var base32pad3 = rfc46483({
28178
+ prefix: "c",
28179
+ name: "base32pad",
28180
+ alphabet: "abcdefghijklmnopqrstuvwxyz234567=",
28181
+ bitsPerChar: 5
28182
+ });
28183
+ var base32padupper3 = rfc46483({
28184
+ prefix: "C",
28185
+ name: "base32padupper",
28186
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
28187
+ bitsPerChar: 5
28188
+ });
28189
+ var base32hex3 = rfc46483({
28190
+ prefix: "v",
28191
+ name: "base32hex",
28192
+ alphabet: "0123456789abcdefghijklmnopqrstuv",
28193
+ bitsPerChar: 5
28194
+ });
28195
+ var base32hexupper3 = rfc46483({
28196
+ prefix: "V",
28197
+ name: "base32hexupper",
28198
+ alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUV",
28199
+ bitsPerChar: 5
28200
+ });
28201
+ var base32hexpad3 = rfc46483({
28202
+ prefix: "t",
28203
+ name: "base32hexpad",
28204
+ alphabet: "0123456789abcdefghijklmnopqrstuv=",
28205
+ bitsPerChar: 5
28206
+ });
28207
+ var base32hexpadupper3 = rfc46483({
28208
+ prefix: "T",
28209
+ name: "base32hexpadupper",
28210
+ alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUV=",
28211
+ bitsPerChar: 5
28212
+ });
28213
+ var base32z3 = rfc46483({
28214
+ prefix: "h",
28215
+ name: "base32z",
28216
+ alphabet: "ybndrfg8ejkmcpqxot1uwisza345h769",
28217
+ bitsPerChar: 5
28218
+ });
28219
+ var base363 = baseX3({
28220
+ prefix: "k",
28221
+ name: "base36",
28222
+ alphabet: "0123456789abcdefghijklmnopqrstuvwxyz"
28223
+ });
28224
+ var base36upper3 = baseX3({
28225
+ prefix: "K",
28226
+ name: "base36upper",
28227
+ alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
28228
+ });
28229
+ var base58btc3 = baseX3({
28230
+ name: "base58btc",
28231
+ prefix: "z",
28232
+ alphabet: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
28233
+ });
28234
+ var base58flickr3 = baseX3({
28235
+ name: "base58flickr",
28236
+ prefix: "Z",
28237
+ alphabet: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
28238
+ });
28239
+ var encode_13 = encode22;
28240
+ var MSB3 = 128;
28241
+ var REST3 = 127;
28242
+ var MSBALL3 = ~REST3;
28243
+ var INT3 = Math.pow(2, 31);
28244
+ function encode22(num, out, offset) {
28245
+ out = out || [];
28246
+ offset = offset || 0;
28247
+ var oldOffset = offset;
28248
+ while (num >= INT3) {
28249
+ out[offset++] = num & 255 | MSB3;
28250
+ num /= 128;
28251
+ }
28252
+ while (num & MSBALL3) {
28253
+ out[offset++] = num & 255 | MSB3;
28254
+ num >>>= 7;
28255
+ }
28256
+ out[offset] = num | 0;
28257
+ encode22.bytes = offset - oldOffset + 1;
28258
+ return out;
28259
+ }
28260
+ var decode22 = read3;
28261
+ var MSB$13 = 128;
28262
+ var REST$13 = 127;
28263
+ function read3(buf, offset) {
28264
+ var res = 0, offset = offset || 0, shift = 0, counter = offset, b, l = buf.length;
28265
+ do {
28266
+ if (counter >= l) {
28267
+ read3.bytes = 0;
28268
+ throw new RangeError("Could not decode varint");
28269
+ }
28270
+ b = buf[counter++];
28271
+ res += shift < 28 ? (b & REST$13) << shift : (b & REST$13) * Math.pow(2, shift);
28272
+ shift += 7;
28273
+ } while (b >= MSB$13);
28274
+ read3.bytes = counter - offset;
28275
+ return res;
28276
+ }
28277
+ var N13 = Math.pow(2, 7);
28278
+ var N23 = Math.pow(2, 14);
28279
+ var N33 = Math.pow(2, 21);
28280
+ var N43 = Math.pow(2, 28);
28281
+ var N53 = Math.pow(2, 35);
28282
+ var N63 = Math.pow(2, 42);
28283
+ var N73 = Math.pow(2, 49);
28284
+ var N83 = Math.pow(2, 56);
28285
+ var N93 = Math.pow(2, 63);
28286
+ var length3 = function(value) {
28287
+ return value < N13 ? 1 : value < N23 ? 2 : value < N33 ? 3 : value < N43 ? 4 : value < N53 ? 5 : value < N63 ? 6 : value < N73 ? 7 : value < N83 ? 8 : value < N93 ? 9 : 10;
28288
+ };
28289
+ var varint3 = {
28290
+ encode: encode_13,
28291
+ decode: decode22,
28292
+ encodingLength: length3
28293
+ };
28294
+ var _brrp_varint3 = varint3;
28295
+ var varint_default3 = _brrp_varint3;
28296
+ function decode32(data, offset = 0) {
28297
+ const code22 = varint_default3.decode(data, offset);
28298
+ return [code22, varint_default3.decode.bytes];
28299
+ }
28300
+ function encodeTo3(int, target, offset = 0) {
28301
+ varint_default3.encode(int, target, offset);
28302
+ return target;
28303
+ }
28304
+ function encodingLength3(int) {
28305
+ return varint_default3.encodingLength(int);
28306
+ }
28307
+ function create3(code22, digest2) {
28308
+ const size = digest2.byteLength;
28309
+ const sizeOffset = encodingLength3(code22);
28310
+ const digestOffset = sizeOffset + encodingLength3(size);
28311
+ const bytes = new Uint8Array(digestOffset + size);
28312
+ encodeTo3(code22, bytes, 0);
28313
+ encodeTo3(size, bytes, sizeOffset);
28314
+ bytes.set(digest2, digestOffset);
28315
+ return new Digest3(code22, size, digest2, bytes);
28316
+ }
28317
+ function decode42(multihash) {
28318
+ const bytes = coerce4(multihash);
28319
+ const [code22, sizeOffset] = decode32(bytes);
28320
+ const [size, digestOffset] = decode32(bytes.subarray(sizeOffset));
28321
+ const digest2 = bytes.subarray(sizeOffset + digestOffset);
28322
+ if (digest2.byteLength !== size) {
28323
+ throw new Error("Incorrect length");
28324
+ }
28325
+ return new Digest3(code22, size, digest2, bytes);
28326
+ }
28327
+ function equals22(a, b) {
28328
+ if (a === b) {
28329
+ return true;
28330
+ } else {
28331
+ const data = b;
28332
+ return a.code === data.code && a.size === data.size && data.bytes instanceof Uint8Array && equals5(a.bytes, data.bytes);
28333
+ }
28334
+ }
28335
+ var Digest3 = class {
28336
+ code;
28337
+ size;
28338
+ digest;
28339
+ bytes;
28340
+ /**
28341
+ * Creates a multihash digest.
28342
+ */
28343
+ constructor(code22, size, digest2, bytes) {
28344
+ this.code = code22;
28345
+ this.size = size;
28346
+ this.digest = digest2;
28347
+ this.bytes = bytes;
28348
+ }
28349
+ };
28350
+ function format3(link2, base22) {
28351
+ const { bytes, version } = link2;
28352
+ switch (version) {
28353
+ case 0:
28354
+ return toStringV03(bytes, baseCache3(link2), base22 ?? base58btc3.encoder);
28355
+ default:
28356
+ return toStringV13(bytes, baseCache3(link2), base22 ?? base323.encoder);
28357
+ }
28358
+ }
28359
+ var cache3 = /* @__PURE__ */ new WeakMap();
28360
+ function baseCache3(cid) {
28361
+ const baseCache22 = cache3.get(cid);
28362
+ if (baseCache22 == null) {
28363
+ const baseCache32 = /* @__PURE__ */ new Map();
28364
+ cache3.set(cid, baseCache32);
28365
+ return baseCache32;
28366
+ }
28367
+ return baseCache22;
28368
+ }
28369
+ var CID3 = class _CID3 {
28370
+ code;
28371
+ version;
28372
+ multihash;
28373
+ bytes;
28374
+ "/";
28375
+ /**
28376
+ * @param version - Version of the CID
28377
+ * @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
28378
+ * @param multihash - (Multi)hash of the of the content.
28379
+ */
28380
+ constructor(version, code22, multihash, bytes) {
28381
+ this.code = code22;
28382
+ this.version = version;
28383
+ this.multihash = multihash;
28384
+ this.bytes = bytes;
28385
+ this["/"] = bytes;
28386
+ }
28387
+ /**
28388
+ * Signalling `cid.asCID === cid` has been replaced with `cid['/'] === cid.bytes`
28389
+ * please either use `CID.asCID(cid)` or switch to new signalling mechanism
28390
+ *
28391
+ * @deprecated
28392
+ */
28393
+ get asCID() {
28394
+ return this;
28395
+ }
28396
+ // ArrayBufferView
28397
+ get byteOffset() {
28398
+ return this.bytes.byteOffset;
28399
+ }
28400
+ // ArrayBufferView
28401
+ get byteLength() {
28402
+ return this.bytes.byteLength;
28403
+ }
28404
+ toV0() {
28405
+ switch (this.version) {
28406
+ case 0: {
28407
+ return this;
28408
+ }
28409
+ case 1: {
28410
+ const { code: code22, multihash } = this;
28411
+ if (code22 !== DAG_PB_CODE3) {
28412
+ throw new Error("Cannot convert a non dag-pb CID to CIDv0");
28413
+ }
28414
+ if (multihash.code !== SHA_256_CODE3) {
28415
+ throw new Error("Cannot convert non sha2-256 multihash CID to CIDv0");
28416
+ }
28417
+ return _CID3.createV0(multihash);
28418
+ }
28419
+ default: {
28420
+ throw Error(`Can not convert CID version ${this.version} to version 0. This is a bug please report`);
28421
+ }
28422
+ }
28423
+ }
28424
+ toV1() {
28425
+ switch (this.version) {
28426
+ case 0: {
28427
+ const { code: code22, digest: digest2 } = this.multihash;
28428
+ const multihash = create3(code22, digest2);
28429
+ return _CID3.createV1(this.code, multihash);
28430
+ }
28431
+ case 1: {
28432
+ return this;
28433
+ }
28434
+ default: {
28435
+ throw Error(`Can not convert CID version ${this.version} to version 1. This is a bug please report`);
28436
+ }
28437
+ }
28438
+ }
28439
+ equals(other) {
28440
+ return _CID3.equals(this, other);
28441
+ }
28442
+ static equals(self, other) {
28443
+ const unknown = other;
28444
+ return unknown != null && self.code === unknown.code && self.version === unknown.version && equals22(self.multihash, unknown.multihash);
28445
+ }
28446
+ toString(base22) {
28447
+ return format3(this, base22);
28448
+ }
28449
+ toJSON() {
28450
+ return { "/": format3(this) };
28451
+ }
28452
+ link() {
28453
+ return this;
28454
+ }
28455
+ [Symbol.toStringTag] = "CID";
28456
+ // Legacy
28457
+ [/* @__PURE__ */ Symbol.for("nodejs.util.inspect.custom")]() {
28458
+ return `CID(${this.toString()})`;
28459
+ }
28460
+ /**
28461
+ * Takes any input `value` and returns a `CID` instance if it was
28462
+ * a `CID` otherwise returns `null`. If `value` is instanceof `CID`
28463
+ * it will return value back. If `value` is not instance of this CID
28464
+ * class, but is compatible CID it will return new instance of this
28465
+ * `CID` class. Otherwise returns null.
28466
+ *
28467
+ * This allows two different incompatible versions of CID library to
28468
+ * co-exist and interop as long as binary interface is compatible.
28469
+ */
28470
+ static asCID(input) {
28471
+ if (input == null) {
28472
+ return null;
28473
+ }
28474
+ const value = input;
28475
+ if (value instanceof _CID3) {
28476
+ return value;
28477
+ } else if (value["/"] != null && value["/"] === value.bytes || value.asCID === value) {
28478
+ const { version, code: code22, multihash, bytes } = value;
28479
+ return new _CID3(version, code22, multihash, bytes ?? encodeCID3(version, code22, multihash.bytes));
28480
+ } else if (value[cidSymbol3] === true) {
28481
+ const { version, multihash, code: code22 } = value;
28482
+ const digest2 = decode42(multihash);
28483
+ return _CID3.create(version, code22, digest2);
28484
+ } else {
28485
+ return null;
28486
+ }
28487
+ }
28488
+ /**
28489
+ * @param version - Version of the CID
28490
+ * @param code - Code of the codec content is encoded in, see https://github.com/multiformats/multicodec/blob/master/table.csv
28491
+ * @param digest - (Multi)hash of the of the content.
28492
+ */
28493
+ static create(version, code22, digest2) {
28494
+ if (typeof code22 !== "number") {
28495
+ throw new Error("String codecs are no longer supported");
28496
+ }
28497
+ if (!(digest2.bytes instanceof Uint8Array)) {
28498
+ throw new Error("Invalid digest");
28499
+ }
28500
+ switch (version) {
28501
+ case 0: {
28502
+ if (code22 !== DAG_PB_CODE3) {
28503
+ throw new Error(`Version 0 CID must use dag-pb (code: ${DAG_PB_CODE3}) block encoding`);
28504
+ } else {
28505
+ return new _CID3(version, code22, digest2, digest2.bytes);
28506
+ }
28507
+ }
28508
+ case 1: {
28509
+ const bytes = encodeCID3(version, code22, digest2.bytes);
28510
+ return new _CID3(version, code22, digest2, bytes);
28511
+ }
28512
+ default: {
28513
+ throw new Error("Invalid version");
28514
+ }
28515
+ }
28516
+ }
28517
+ /**
28518
+ * Simplified version of `create` for CIDv0.
28519
+ */
28520
+ static createV0(digest2) {
28521
+ return _CID3.create(0, DAG_PB_CODE3, digest2);
28522
+ }
28523
+ /**
28524
+ * Simplified version of `create` for CIDv1.
28525
+ *
28526
+ * @param code - Content encoding format code.
28527
+ * @param digest - Multihash of the content.
28528
+ */
28529
+ static createV1(code22, digest2) {
28530
+ return _CID3.create(1, code22, digest2);
28531
+ }
28532
+ /**
28533
+ * Decoded a CID from its binary representation. The byte array must contain
28534
+ * only the CID with no additional bytes.
28535
+ *
28536
+ * An error will be thrown if the bytes provided do not contain a valid
28537
+ * binary representation of a CID.
28538
+ */
28539
+ static decode(bytes) {
28540
+ const [cid, remainder] = _CID3.decodeFirst(bytes);
28541
+ if (remainder.length !== 0) {
28542
+ throw new Error("Incorrect length");
28543
+ }
28544
+ return cid;
28545
+ }
28546
+ /**
28547
+ * Decoded a CID from its binary representation at the beginning of a byte
28548
+ * array.
28549
+ *
28550
+ * Returns an array with the first element containing the CID and the second
28551
+ * element containing the remainder of the original byte array. The remainder
28552
+ * will be a zero-length byte array if the provided bytes only contained a
28553
+ * binary CID representation.
28554
+ */
28555
+ static decodeFirst(bytes) {
28556
+ const specs = _CID3.inspectBytes(bytes);
28557
+ const prefixSize = specs.size - specs.multihashSize;
28558
+ const multihashBytes = coerce4(bytes.subarray(prefixSize, prefixSize + specs.multihashSize));
28559
+ if (multihashBytes.byteLength !== specs.multihashSize) {
28560
+ throw new Error("Incorrect length");
28561
+ }
28562
+ const digestBytes = multihashBytes.subarray(specs.multihashSize - specs.digestSize);
28563
+ const digest2 = new Digest3(specs.multihashCode, specs.digestSize, digestBytes, multihashBytes);
28564
+ const cid = specs.version === 0 ? _CID3.createV0(digest2) : _CID3.createV1(specs.codec, digest2);
28565
+ return [cid, bytes.subarray(specs.size)];
28566
+ }
28567
+ /**
28568
+ * Inspect the initial bytes of a CID to determine its properties.
28569
+ *
28570
+ * Involves decoding up to 4 varints. Typically this will require only 4 to 6
28571
+ * bytes but for larger multicodec code values and larger multihash digest
28572
+ * lengths these varints can be quite large. It is recommended that at least
28573
+ * 10 bytes be made available in the `initialBytes` argument for a complete
28574
+ * inspection.
28575
+ */
28576
+ static inspectBytes(initialBytes) {
28577
+ let offset = 0;
28578
+ const next = () => {
28579
+ const [i, length22] = decode32(initialBytes.subarray(offset));
28580
+ offset += length22;
28581
+ return i;
28582
+ };
28583
+ let version = next();
28584
+ let codec = DAG_PB_CODE3;
28585
+ if (version === 18) {
28586
+ version = 0;
28587
+ offset = 0;
28588
+ } else {
28589
+ codec = next();
28590
+ }
28591
+ if (version !== 0 && version !== 1) {
28592
+ throw new RangeError(`Invalid CID version ${version}`);
28593
+ }
28594
+ const prefixSize = offset;
28595
+ const multihashCode = next();
28596
+ const digestSize = next();
28597
+ const size = offset + digestSize;
28598
+ const multihashSize = size - prefixSize;
28599
+ return { version, codec, multihashCode, digestSize, multihashSize, size };
28600
+ }
28601
+ /**
28602
+ * Takes cid in a string representation and creates an instance. If `base`
28603
+ * decoder is not provided will use a default from the configuration. It will
28604
+ * throw an error if encoding of the CID is not compatible with supplied (or
28605
+ * a default decoder).
28606
+ */
28607
+ static parse(source, base22) {
28608
+ const [prefix, bytes] = parseCIDtoBytes3(source, base22);
28609
+ const cid = _CID3.decode(bytes);
28610
+ if (cid.version === 0 && source[0] !== "Q") {
28611
+ throw Error("Version 0 CID string must not include multibase prefix");
28612
+ }
28613
+ baseCache3(cid).set(prefix, source);
28614
+ return cid;
28615
+ }
28616
+ };
28617
+ function parseCIDtoBytes3(source, base22) {
28618
+ switch (source[0]) {
28619
+ // CIDv0 is parsed differently
28620
+ case "Q": {
28621
+ const decoder = base22 ?? base58btc3;
28622
+ return [
28623
+ base58btc3.prefix,
28624
+ decoder.decode(`${base58btc3.prefix}${source}`)
28625
+ ];
28626
+ }
28627
+ case base58btc3.prefix: {
28628
+ const decoder = base22 ?? base58btc3;
28629
+ return [base58btc3.prefix, decoder.decode(source)];
28630
+ }
28631
+ case base323.prefix: {
28632
+ const decoder = base22 ?? base323;
28633
+ return [base323.prefix, decoder.decode(source)];
28634
+ }
28635
+ case base363.prefix: {
28636
+ const decoder = base22 ?? base363;
28637
+ return [base363.prefix, decoder.decode(source)];
28638
+ }
28639
+ default: {
28640
+ if (base22 == null) {
28641
+ throw Error("To parse non base32, base36 or base58btc encoded CID multibase decoder must be provided");
28642
+ }
28643
+ return [source[0], base22.decode(source)];
28644
+ }
28645
+ }
28646
+ }
28647
+ function toStringV03(bytes, cache22, base22) {
28648
+ const { prefix } = base22;
28649
+ if (prefix !== base58btc3.prefix) {
28650
+ throw Error(`Cannot string encode V0 in ${base22.name} encoding`);
28651
+ }
28652
+ const cid = cache22.get(prefix);
28653
+ if (cid == null) {
28654
+ const cid2 = base22.encode(bytes).slice(1);
28655
+ cache22.set(prefix, cid2);
28656
+ return cid2;
28657
+ } else {
28658
+ return cid;
28659
+ }
28660
+ }
28661
+ function toStringV13(bytes, cache22, base22) {
28662
+ const { prefix } = base22;
28663
+ const cid = cache22.get(prefix);
28664
+ if (cid == null) {
28665
+ const cid2 = base22.encode(bytes);
28666
+ cache22.set(prefix, cid2);
28667
+ return cid2;
28668
+ } else {
28669
+ return cid;
28670
+ }
28671
+ }
28672
+ var DAG_PB_CODE3 = 112;
28673
+ var SHA_256_CODE3 = 18;
28674
+ function encodeCID3(version, code22, multihash) {
28675
+ const codeOffset = encodingLength3(version);
28676
+ const hashOffset = codeOffset + encodingLength3(code22);
28677
+ const bytes = new Uint8Array(hashOffset + multihash.byteLength);
28678
+ encodeTo3(version, bytes, 0);
28679
+ encodeTo3(code22, bytes, codeOffset);
28680
+ bytes.set(multihash, hashOffset);
28681
+ return bytes;
28682
+ }
28683
+ var cidSymbol3 = /* @__PURE__ */ Symbol.for("@ipld/js-cid/CID");
28684
+ var code2 = 85;
28685
+ var SHA256_CODE2 = 18;
28686
+ function isCanonicalRawCid2(cidString) {
28687
+ let cid;
28688
+ try {
28689
+ cid = CID3.parse(cidString);
28690
+ } catch {
28691
+ return false;
28692
+ }
28693
+ return cid.version === 1 && cid.code === code2 && cid.multihash.code === SHA256_CODE2 && cid.toString() === cidString;
28694
+ }
28695
+ var base642 = rfc46483({
28696
+ prefix: "m",
28697
+ name: "base64",
28698
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
28699
+ bitsPerChar: 6
28700
+ });
28701
+ var base64pad2 = rfc46483({
28702
+ prefix: "M",
28703
+ name: "base64pad",
28704
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
28705
+ bitsPerChar: 6
28706
+ });
28707
+ var base64url2 = rfc46483({
28708
+ prefix: "u",
28709
+ name: "base64url",
28710
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
28711
+ bitsPerChar: 6
28712
+ });
28713
+ var base64urlpad2 = rfc46483({
28714
+ prefix: "U",
28715
+ name: "base64urlpad",
28716
+ alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",
28717
+ bitsPerChar: 6
28718
+ });
28719
+ function fromBase64Url2(text) {
28720
+ const bytes = base64url2.baseDecode(text);
28721
+ if (base64url2.baseEncode(bytes) !== text) {
28722
+ throw new TypeError("non-canonical base64url input");
28723
+ }
28724
+ return bytes;
28725
+ }
28726
+ function utf8Bytes2(text) {
28727
+ return new TextEncoder().encode(text);
28728
+ }
28729
+ var ED25519_MULTICODEC_PREFIX2 = Uint8Array.of(237, 1);
28730
+ function decodeBase64UrlOrNull2(value) {
28731
+ try {
28732
+ return fromBase64Url2(value);
28733
+ } catch {
28734
+ return null;
28735
+ }
28736
+ }
28737
+ var base64UrlString2 = () => external_exports.string().refine((value) => decodeBase64UrlOrNull2(value) !== null, {
28738
+ message: "expected strictly-decodable unpadded base64url"
28739
+ });
28740
+ function isCanonicalHttpsOrigin2(value) {
28741
+ let url;
28742
+ try {
28743
+ url = new URL(value);
28744
+ } catch {
28745
+ return false;
28746
+ }
28747
+ return url.protocol === "https:" && url.origin === value;
28748
+ }
28749
+ var sessionJwkCommonFields2 = {
28750
+ alg: external_exports.string().min(1).optional(),
28751
+ use: external_exports.string().min(1).optional(),
28752
+ key_ops: external_exports.array(external_exports.string().min(1)).optional(),
28753
+ kid: external_exports.string().min(1).optional(),
28754
+ ext: external_exports.boolean().optional()
28755
+ };
28756
+ var okpPrivateJwkSchema2 = external_exports.object({
28757
+ kty: external_exports.literal("OKP"),
28758
+ crv: external_exports.string().min(1),
28759
+ x: base64UrlString2(),
28760
+ d: base64UrlString2(),
28761
+ ...sessionJwkCommonFields2
28762
+ }).strict();
28763
+ var ecPrivateJwkSchema2 = external_exports.object({
28764
+ kty: external_exports.literal("EC"),
28765
+ crv: external_exports.string().min(1),
28766
+ x: base64UrlString2(),
28767
+ y: base64UrlString2(),
28768
+ d: base64UrlString2(),
28769
+ ...sessionJwkCommonFields2
28770
+ }).strict();
28771
+ var sessionJwkSchema2 = external_exports.discriminatedUnion("kty", [
28772
+ okpPrivateJwkSchema2,
28773
+ ecPrivateJwkSchema2
28774
+ ]);
28775
+ var policyTargetSchema2 = external_exports.object({
28776
+ kind: external_exports.literal("policy"),
28777
+ policyCid: external_exports.string().min(1),
28778
+ /** Canonical policy bytes, base64url-encoded (bytes are not JSON). */
28779
+ policyBytes: base64UrlString2()
28780
+ }).strict();
28781
+ var bearerKeyTargetSchema2 = external_exports.object({
28782
+ kind: external_exports.literal("bearerKey"),
28783
+ sessionJwk: sessionJwkSchema2
28784
+ }).strict();
28785
+ var recipientDidTargetSchema2 = external_exports.object({
28786
+ kind: external_exports.literal("recipientDid"),
28787
+ did: external_exports.string().regex(/^did:[a-z0-9]+:.+$/, "expected a DID")
28788
+ }).strict();
28789
+ var authorizationTargetSchema2 = external_exports.discriminatedUnion("kind", [
28790
+ policyTargetSchema2,
28791
+ bearerKeyTargetSchema2,
28792
+ recipientDidTargetSchema2
28793
+ ]);
28794
+ function isCanonicalResourcePath2(value) {
28795
+ if (value.length === 0) return false;
28796
+ if (/[\u0000-\u001f\u007f\\]/.test(value)) return false;
28797
+ if (/%2f|%5c|%2e/i.test(value)) return false;
28798
+ return value.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
28799
+ }
28800
+ function isCanonicalPathSegment2(value) {
28801
+ return isCanonicalResourcePath2(value) && !value.includes("/");
28802
+ }
28803
+ var resourceSelectorSchema2 = external_exports.object({
28804
+ kind: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
28805
+ path: external_exports.string().min(1)
28806
+ }).strict().superRefine((selector, ctx) => {
28807
+ const body = selector.kind === "prefix" && selector.path.endsWith("/") ? selector.path.slice(0, -1) : selector.path;
28808
+ if (!isCanonicalResourcePath2(body)) {
28809
+ ctx.addIssue({
28810
+ code: external_exports.ZodIssueCode.custom,
28811
+ path: ["path"],
28812
+ message: "expected a canonical resource path (non-empty segments, no . or .. segments, no //, no backslash, no %2f/%5c/%2e, no control chars)"
28813
+ });
28814
+ }
28815
+ });
28816
+ var targetSchema2 = external_exports.object({
28817
+ origin: external_exports.string().refine(isCanonicalHttpsOrigin2, {
28818
+ message: "expected a canonical https origin (https://host[:port], nothing else)"
28819
+ }),
28820
+ nodeAudience: external_exports.string().min(1),
28821
+ spaceId: external_exports.string().refine(isCanonicalPathSegment2, {
28822
+ message: "expected a single canonical path segment (spaceId joins the resource URI; separators/traversal would alias grants)"
28823
+ }),
28824
+ resource: resourceSelectorSchema2
28825
+ }).strict();
28826
+ var displaySchema2 = external_exports.object({
28827
+ senderName: external_exports.string().optional(),
28828
+ filename: external_exports.string().optional(),
28829
+ recipientHint: external_exports.string().optional(),
28830
+ /**
28831
+ * Presentation preference only (viewer spec §1): may narrow, never widen;
28832
+ * capabilities always win.
28833
+ */
28834
+ mode: external_exports.union([external_exports.literal("document"), external_exports.literal("source"), external_exports.literal("folder")]).optional()
28835
+ }).strict();
28836
+ var contentPointerSchema2 = external_exports.object({
28837
+ cid: external_exports.string().refine(isCanonicalRawCid2, {
28838
+ message: "expected a canonical CIDv1 raw sha2-256 base32 CID"
28839
+ }),
28840
+ key: external_exports.string().refine((value) => decodeBase64UrlOrNull2(value)?.length === 32, {
28841
+ message: "expected base64url decoding to exactly 32 bytes"
28842
+ })
28843
+ }).strict();
28844
+ var signatureSchema2 = external_exports.object({
28845
+ /** did:key of the sender's ed25519 signing key. */
28846
+ signerDid: external_exports.string().regex(/^did:key:z[1-9A-HJ-NP-Za-km-z]+$/, "expected a did:key"),
28847
+ algorithm: external_exports.literal("Ed25519"),
28848
+ /** base64url-encoded ed25519 signature over the JCS bytes of all other fields. */
28849
+ value: external_exports.string().refine((value) => decodeBase64UrlOrNull2(value)?.length === 64, {
28850
+ message: "expected base64url decoding to exactly 64 bytes"
28851
+ })
28852
+ }).strict();
28853
+ var unsignedShareEnvelopeSchema2 = external_exports.object({
28854
+ version: external_exports.literal(1),
28855
+ shareId: external_exports.string().min(1),
28856
+ /** Full signed delegation chain, opaque serialized form. */
28857
+ delegation: external_exports.string().min(1),
28858
+ authorizationTarget: authorizationTargetSchema2,
28859
+ target: targetSchema2,
28860
+ display: displaySchema2,
28861
+ /** ISO 8601 UTC datetime. Advisory here; enforcement is the delegation's. */
28862
+ expiry: external_exports.string().datetime(),
28863
+ /** Bearer-slice sealed-content pointer (see contentPointerSchema). */
28864
+ content: contentPointerSchema2.optional()
28865
+ }).strict();
28866
+ var shareEnvelopeSchema2 = unsignedShareEnvelopeSchema2.extend({ signature: signatureSchema2 }).strict();
28867
+ var recipientMatcherSchema2 = external_exports.discriminatedUnion("kind", [
28868
+ external_exports.object({ kind: external_exports.literal("exactEmail"), value: external_exports.string().min(3).regex(/^[^@\s]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/) }).strict(),
28869
+ external_exports.object({ kind: external_exports.literal("emailDomain"), value: external_exports.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/) }).strict(),
28870
+ external_exports.object({ kind: external_exports.literal("recipientDid"), value: external_exports.string().regex(/^did:[a-z0-9]+:.+$/) }).strict(),
28871
+ external_exports.object({ kind: external_exports.literal("policyDigest"), value: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/) }).strict(),
28872
+ external_exports.object({ kind: external_exports.literal("bearer") }).strict()
28873
+ ]);
28874
+ var shareActionSchema2 = external_exports.union([external_exports.literal("read"), external_exports.literal("list"), external_exports.literal("edit")]);
28875
+ var kvContentSourceSchema2 = external_exports.object({
28876
+ kind: external_exports.literal("kv"),
28877
+ space: external_exports.string().min(1),
28878
+ path: external_exports.string().min(1),
28879
+ action: external_exports.literal("tinycloud.kv/get")
28880
+ }).strict();
28881
+ var sqlContentSourceSchema2 = external_exports.object({
28882
+ kind: external_exports.literal("sql"),
28883
+ space: external_exports.string().min(1),
28884
+ database: external_exports.string().min(1),
28885
+ path: external_exports.string().min(1),
28886
+ statement: external_exports.string().min(1),
28887
+ arguments: external_exports.record(external_exports.string(), external_exports.number().int()),
28888
+ argumentsDigest: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/),
28889
+ action: external_exports.literal("tinycloud.sql/read")
28890
+ }).strict();
28891
+ var contentSourceSchema2 = external_exports.discriminatedUnion("kind", [kvContentSourceSchema2, sqlContentSourceSchema2]);
28892
+ var v2TargetSchema2 = external_exports.object({
28893
+ origin: external_exports.string().refine(isCanonicalHttpsOrigin2, { message: "expected a canonical https origin" }),
28894
+ nodeAudience: external_exports.string().min(1),
28895
+ spaceId: external_exports.string().refine(isCanonicalPathSegment2, { message: "expected a canonical space id" })
28896
+ }).strict();
28897
+ var shareDecryptionSchema2 = external_exports.object({
28898
+ networkId: external_exports.string().min(1),
28899
+ action: external_exports.literal("tinycloud.encryption/decrypt")
28900
+ }).strict();
28901
+ var ownerAuthoritySchema2 = external_exports.object({
28902
+ registrationCid: external_exports.string().min(1),
28903
+ shareCid: external_exports.string().min(1),
28904
+ envelopeCid: external_exports.string().min(1),
28905
+ enforcementDelegation: external_exports.record(external_exports.string(), external_exports.unknown()),
28906
+ outerEnvelope: external_exports.record(external_exports.string(), external_exports.unknown()),
28907
+ /** Node-signed registration evidence; required by trusted policy adapters. */
28908
+ registrationReceipt: external_exports.object({
28909
+ registration: external_exports.record(external_exports.string(), external_exports.unknown()),
28910
+ proof: external_exports.record(external_exports.string(), external_exports.unknown())
28911
+ }).strict().optional()
28912
+ }).strict();
28913
+ var contentMetadataSchema2 = external_exports.object({
28914
+ mediaType: external_exports.string().min(1).max(128).optional(),
28915
+ byteLength: external_exports.number().int().nonnegative().max(100 * 1024 * 1024).optional(),
28916
+ filename: external_exports.string().min(1).max(255).optional(),
28917
+ encoding: external_exports.literal("utf-8").optional(),
28918
+ /** Encrypted presentation discriminator. The fixed entry point is index.html. */
28919
+ artifact: external_exports.literal("html").optional()
28920
+ }).strict();
28921
+ var deliveryEmailSchema2 = external_exports.string().email();
28922
+ var unsignedShareEnvelopeV2BaseSchema2 = external_exports.object({
28923
+ version: external_exports.literal(2),
28924
+ shareId: external_exports.string().min(1),
28925
+ recipientMatcher: recipientMatcherSchema2,
28926
+ deliveryEmail: deliveryEmailSchema2.optional(),
28927
+ actions: external_exports.array(shareActionSchema2).min(1).max(3),
28928
+ resource: resourceSelectorSchema2,
28929
+ target: v2TargetSchema2,
28930
+ delegationCid: external_exports.string().min(1),
28931
+ authorityMaterialHandle: external_exports.string().min(1),
28932
+ authorityMaterialDigest: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/),
28933
+ decryption: shareDecryptionSchema2.optional(),
28934
+ contentSource: contentSourceSchema2,
28935
+ contentSourceDigest: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/),
28936
+ authorizationTarget: authorizationTargetSchema2,
28937
+ display: displaySchema2,
28938
+ expiry: external_exports.string().datetime(),
28939
+ encrypted: external_exports.boolean(),
28940
+ content: contentPointerSchema2.optional(),
28941
+ metadata: contentMetadataSchema2,
28942
+ ownerAuthority: ownerAuthoritySchema2.optional()
28943
+ }).strict();
28944
+ function validateV2Invariants2(value, ctx) {
28945
+ const actions = [...value.actions];
28946
+ if (new Set(actions).size !== actions.length) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["actions"], message: "actions must be unique" });
28947
+ if (actions.some((action, index) => action !== ["read", "list", "edit"].filter((candidate) => actions.includes(candidate))[index])) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["actions"], message: "actions must be canonically ordered" });
28948
+ if (!value.encrypted && value.recipientMatcher.kind !== "policyDigest") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["recipientMatcher"], message: "safe plaintext must carry only a matcher digest" });
28949
+ if (!value.encrypted && value.authorizationTarget.kind !== "policy") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["encrypted"], message: "unencrypted content requires a policy target" });
28950
+ if (!value.encrypted && value.content !== void 0) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["content"], message: "policy-only plaintext cannot carry content" });
28951
+ if (value.encrypted && (value.metadata.mediaType === void 0 || value.metadata.filename === void 0 || value.metadata.byteLength === void 0)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["metadata"], message: "encrypted shares must describe their content" });
28952
+ if (!value.encrypted && Object.keys(value.metadata).length !== 0) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["metadata"], message: "policy-only plaintext cannot describe content" });
28953
+ if (!value.encrypted && value.metadata.encoding !== void 0) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["metadata", "encoding"], message: "policy-only plaintext cannot carry content encoding" });
28954
+ if (value.metadata.artifact === "html" && (!value.encrypted || value.resource.kind !== "prefix" || !value.actions.includes("read") || !value.actions.includes("list"))) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["metadata", "artifact"], message: "html artifacts require an encrypted readable prefix" });
28955
+ if (!value.encrypted && (value.display.senderName !== void 0 || value.display.filename !== void 0 || value.display.recipientHint !== void 0)) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["display"], message: "policy-only plaintext cannot carry display metadata" });
28956
+ if (!value.encrypted && value.deliveryEmail !== void 0) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["deliveryEmail"], message: "policy-only plaintext cannot carry delivery metadata" });
28957
+ if (value.recipientMatcher.kind === "exactEmail" && value.deliveryEmail !== void 0 && value.deliveryEmail !== value.recipientMatcher.value) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["deliveryEmail"], message: "delivery email must match the exact matcher" });
28958
+ if (value.recipientMatcher.kind === "emailDomain" && value.deliveryEmail !== void 0 && value.deliveryEmail.toLowerCase().endsWith(`@${value.recipientMatcher.value}`) === false) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["deliveryEmail"], message: "delivery email must belong to the matcher domain" });
28959
+ if (value.contentSource.kind === "kv" && value.contentSource.action !== "tinycloud.kv/get") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "source action mismatch" });
28960
+ if (value.contentSource.kind === "sql" && value.contentSource.action !== "tinycloud.sql/read") ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "source action mismatch" });
28961
+ }
28962
+ var unsignedShareEnvelopeV2Schema2 = unsignedShareEnvelopeV2BaseSchema2.superRefine(validateV2Invariants2);
28963
+ var shareEnvelopeV2Schema2 = unsignedShareEnvelopeV2BaseSchema2.extend({ signature: signatureSchema2 }).strict().superRefine(validateV2Invariants2);
28964
+ var unifiedResourceSchema2 = external_exports.string().refine((value) => {
28965
+ const marker = value.indexOf("/kv/");
28966
+ if (marker < 1) return false;
28967
+ const space = value.slice(0, marker);
28968
+ const legacy = space.startsWith("tinycloud://");
28969
+ if (legacy ? /[:/?#%]/.test(space.slice("tinycloud://".length)) : !space.startsWith("tinycloud:") || /[/?#%]/.test(space)) return false;
28970
+ const path = value.slice(marker + 4);
28971
+ return !path.startsWith("/") && !path.endsWith("/") && !path.includes("//") && path.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== "..");
28972
+ }, { message: "expected canonical TinyCloud KV resource" });
28973
+ var unifiedEncryptionNetworkSchema2 = external_exports.string().refine((value) => {
28974
+ if (!value.startsWith("urn:tinycloud:encryption:")) return false;
28975
+ const rest = value.slice("urn:tinycloud:encryption:".length);
28976
+ const separator = rest.lastIndexOf(":");
28977
+ const owner = separator < 0 ? "" : rest.slice(0, separator);
28978
+ const network = separator < 0 ? "" : rest.slice(separator + 1);
28979
+ return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
28980
+ }, { message: "expected canonical TinyCloud encryption network" });
28981
+ var unifiedKvCapabilitySchema2 = external_exports.object({
28982
+ kind: external_exports.literal("kv"),
28983
+ resource: unifiedResourceSchema2,
28984
+ selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
28985
+ actions: external_exports.array(external_exports.union([
28986
+ external_exports.literal("tinycloud.kv/get"),
28987
+ external_exports.literal("tinycloud.kv/list"),
28988
+ external_exports.literal("tinycloud.kv/metadata"),
28989
+ external_exports.literal("tinycloud.kv/put")
28990
+ ])).min(1)
28991
+ }).strict();
28992
+ var unifiedEncryptionCapabilitySchema2 = external_exports.object({
28993
+ kind: external_exports.literal("encryption"),
28994
+ resource: unifiedEncryptionNetworkSchema2,
28995
+ action: external_exports.literal("tinycloud.encryption/decrypt")
28996
+ }).strict();
28997
+ var unifiedCapabilitySchema2 = external_exports.union([unifiedKvCapabilitySchema2, unifiedEncryptionCapabilitySchema2]);
28998
+ var unifiedContentSourceSchema2 = external_exports.object({
28999
+ shareId: external_exports.string().min(1),
29000
+ kvResource: unifiedResourceSchema2,
29001
+ selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
29002
+ encryptionNetwork: unifiedEncryptionNetworkSchema2,
29003
+ encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
29004
+ keyVersion: external_exports.number().int().positive(),
29005
+ mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
29006
+ initialCiphertextDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/).optional()
29007
+ }).strict();
29008
+ var unifiedPolicyV1Schema2 = external_exports.object({
29009
+ schema: external_exports.literal("xyz.tinycloud.policy/policy/v1"),
29010
+ policyId: external_exports.string().regex(/^pol_[a-z2-7]+$/),
29011
+ ownerDid: external_exports.string().min(1),
29012
+ createdAt: external_exports.string().datetime({ offset: true }),
29013
+ expiresAt: external_exports.string().datetime({ offset: true }).optional(),
29014
+ contentSource: unifiedContentSourceSchema2,
29015
+ capabilityCeiling: external_exports.array(unifiedCapabilitySchema2).min(2),
29016
+ signature: external_exports.object({ suite: external_exports.string().min(1), signerDid: external_exports.string().min(1), value: external_exports.string().min(1) }).strict()
29017
+ }).strict();
29018
+ var policyCredentialRequirementV1Schema2 = external_exports.object({
29019
+ type: external_exports.literal("TinyCloudPolicyCredentialRequirement"),
29020
+ version: external_exports.literal(1),
29021
+ requirementDigest: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/),
29022
+ descriptorDigest: external_exports.string().regex(/^[A-Za-z0-9_-]{43}$/),
29023
+ issuerDid: external_exports.string().min(1),
29024
+ issuerKid: external_exports.string().min(1),
29025
+ profile: external_exports.object({ id: external_exports.string().min(1), version: external_exports.literal(1) }).strict(),
29026
+ credentialType: external_exports.object({ id: external_exports.string().min(1), version: external_exports.literal(1) }).strict()
29027
+ }).strict();
29028
+ var unifiedPolicyV2Schema2 = external_exports.object({
29029
+ schema: external_exports.literal("xyz.tinycloud.policy/policy/v2"),
29030
+ policyId: external_exports.string().regex(/^pol_[a-z2-7]+$/),
29031
+ ownerDid: external_exports.string().min(1),
29032
+ createdAt: external_exports.string().datetime({ offset: true }),
29033
+ expiresAt: external_exports.string().datetime({ offset: true }).optional(),
29034
+ contentSource: unifiedContentSourceSchema2,
29035
+ capabilityCeiling: external_exports.array(unifiedCapabilitySchema2).min(2),
29036
+ credentialRequirement: policyCredentialRequirementV1Schema2,
29037
+ signature: external_exports.object({ suite: external_exports.literal("Ed25519"), signerDid: external_exports.string().min(1), value: external_exports.string().min(1) }).strict()
29038
+ }).strict();
29039
+ var unifiedPolicySchema2 = external_exports.discriminatedUnion("schema", [
29040
+ unifiedPolicyV1Schema2,
29041
+ unifiedPolicyV2Schema2
29042
+ ]);
29043
+ var unifiedRootSchema2 = external_exports.object({
29044
+ cid: external_exports.string().min(1),
29045
+ authorization: external_exports.string().regex(/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/),
29046
+ role: external_exports.union([external_exports.literal("policy-authority"), external_exports.literal("policy-enforcement")])
29047
+ }).strict();
29048
+ var attestedEnforcerBindingV2Schema2 = external_exports.object({
29049
+ schema: external_exports.literal("xyz.tinycloud.policy/attested-enforcer/v2"),
29050
+ enforcerDid: external_exports.string().regex(/^did:key:z[1-9A-HJ-NP-Za-km-z]+$/),
29051
+ nodeAudience: external_exports.string().min(1),
29052
+ attestationBindingDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
29053
+ issuedAt: external_exports.string().datetime({ offset: true }),
29054
+ expiresAt: external_exports.string().datetime({ offset: true }),
29055
+ signature: external_exports.object({ suite: external_exports.literal("Ed25519"), signerDid: external_exports.string().min(1), value: base64UrlString2() }).strict()
29056
+ }).strict();
29057
+ var v3TargetSchema2 = external_exports.object({
29058
+ origin: external_exports.string().refine(isCanonicalHttpsOrigin2, { message: "expected a canonical https origin" }),
29059
+ nodeAudience: external_exports.string().min(1),
29060
+ spaceId: external_exports.string().refine(isCanonicalPathSegment2, { message: "expected a canonical space id" })
29061
+ }).strict();
29062
+ var unsignedShareEnvelopeV3BaseSchema2 = external_exports.object({
29063
+ version: external_exports.literal(3),
29064
+ shareId: external_exports.string().min(1),
29065
+ recipientMatcher: recipientMatcherSchema2,
29066
+ deliveryEmail: deliveryEmailSchema2.optional(),
29067
+ actions: external_exports.array(shareActionSchema2).min(1).max(3),
29068
+ resource: resourceSelectorSchema2,
29069
+ target: v3TargetSchema2,
29070
+ policy: unifiedPolicySchema2,
29071
+ policyCid: external_exports.string().min(1),
29072
+ policyRoot: unifiedRootSchema2,
29073
+ enforcementRoot: unifiedRootSchema2,
29074
+ attestedEnforcerBinding: attestedEnforcerBindingV2Schema2,
29075
+ contentSource: unifiedContentSourceSchema2,
29076
+ contentSourceDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
29077
+ encryptionNetwork: unifiedEncryptionNetworkSchema2,
29078
+ expiry: external_exports.string().datetime({ offset: true }),
29079
+ display: displaySchema2,
29080
+ encrypted: external_exports.literal(true),
29081
+ metadata: contentMetadataSchema2
29082
+ }).strict();
29083
+ function validateV3Invariants2(value, ctx) {
29084
+ const actions = [...value.actions];
29085
+ if (new Set(actions).size !== actions.length || actions.some((action, index) => action !== ["read", "list", "edit"].filter((candidate) => actions.includes(candidate))[index])) {
29086
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["actions"], message: "actions must be unique and canonically ordered" });
29087
+ }
29088
+ if (value.policyRoot.role !== "policy-authority" || value.enforcementRoot.role !== "policy-enforcement") {
29089
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policyRoot", "role"], message: "root roles are fixed" });
29090
+ }
29091
+ if (value.policyCid.length === 0 || value.policyCid === value.policyRoot.cid || value.policyCid === value.enforcementRoot.cid) {
29092
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policyCid"], message: "policy CID must be distinct from both roots" });
29093
+ }
29094
+ if (value.contentSource.encryptionNetwork !== value.encryptionNetwork) {
29095
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["encryptionNetwork"], message: "encryption network is not bound to the source" });
29096
+ }
29097
+ if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
29098
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
29099
+ }
29100
+ if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
29101
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
29102
+ }
29103
+ if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
29104
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
29105
+ }
29106
+ const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
29107
+ const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === value.encryptionNetwork && capability.action === "tinycloud.encryption/decrypt");
29108
+ if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
29109
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt network" });
29110
+ }
29111
+ }
29112
+ var unsignedShareEnvelopeV3Schema2 = unsignedShareEnvelopeV3BaseSchema2.superRefine(validateV3Invariants2);
29113
+ var shareEnvelopeV3Schema2 = unsignedShareEnvelopeV3BaseSchema2.extend({ signature: signatureSchema2 }).strict().superRefine(validateV3Invariants2);
29114
+ var ENVELOPE_AAD_LABEL2 = "tinycloud-share-envelope-v1";
29115
+ var AAD2 = utf8Bytes2(ENVELOPE_AAD_LABEL2);
29116
+ var LABEL2 = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
29117
+ function isCanonicalEmailDomain2(value) {
29118
+ if (value.length === 0 || value.length > 253) return false;
29119
+ const labels = value.split(".");
29120
+ return labels.length >= 2 && labels.every((label) => LABEL2.test(label)) && !/^[0-9]+$/.test(labels.at(-1));
29121
+ }
29122
+ function canonicalMailbox2(value) {
29123
+ const email = value.trim().toLowerCase();
29124
+ const at = email.indexOf("@");
29125
+ if (at <= 0 || at !== email.lastIndexOf("@")) return void 0;
29126
+ const local = email.slice(0, at);
29127
+ const domain = email.slice(at + 1);
29128
+ if (local.length > 64 || !/^[a-z0-9#$&'*+/=?^_`{|}~-]+(?:\.[a-z0-9#$&'*+/=?^_`{|}~-]+)*$/.test(local) || !isCanonicalEmailDomain2(domain)) return void 0;
29129
+ return Object.freeze({ email, domain });
29130
+ }
29131
+ var MAX_INLINE_BYTES2 = 256 * 1024;
29132
+
29133
+ // src/commands/share.ts
29134
+ import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
29135
+ init_formatter();
29136
+ init_errors();
29137
+
29138
+ // src/share/output.ts
29139
+ function writeJson2(value) {
29140
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
29141
+ `);
29142
+ }
29143
+ function authorizationRequiredJson(result) {
29144
+ return {
29145
+ state: "authorization-required",
29146
+ method: result.method,
29147
+ next: "complete authorization through the configured authority adapter, then retry with the required proof"
29148
+ };
29149
+ }
29150
+ function publishHuman(result) {
29151
+ process.stdout.write(`${result.url}
29152
+ `);
29153
+ }
29154
+ function inspectHuman(result) {
29155
+ const metadata = result.metadata;
29156
+ process.stdout.write([
29157
+ `Share ${metadata.shareId}`,
29158
+ `File: ${metadata.display.filename ?? "unnamed"}`,
29159
+ `Target: ${metadata.target.kind === "bearer" ? "bearer (anyone with the complete link can read)" : metadata.target.kind}`,
29160
+ `Expires: ${metadata.expiresAt}`,
29161
+ `Resource: ${metadata.resource.path}`,
29162
+ `Link format: ${result.link.kind}`
29163
+ ].join("\n") + "\n");
29164
+ }
29165
+ function receiveHuman(path) {
29166
+ process.stdout.write(`${path}
29167
+ `);
29168
+ }
29169
+ function receiveJson(result, path) {
29170
+ writeJson2({ protocol: "tinycloud-share", version: 1, path, metadata: result.metadata });
29171
+ }
29172
+
29173
+ // src/share/io.ts
29174
+ import { constants } from "fs";
29175
+ import { lstat as lstat3, mkdir as mkdir3, mkdtemp, open as open4, readFile as readFile9, realpath, stat as stat2, link, rename as rename3, rm as rm4, unlink } from "fs/promises";
29176
+ import { randomBytes as randomBytes4 } from "crypto";
29177
+ import { basename as basename3, join as join6, resolve as resolve2, sep } from "path";
29178
+ init_errors();
29179
+ var MAX_SHARE_STDIN_BYTES = 100 * 1024 * 1024;
29180
+ var MAX_SHARE_URL_BYTES = 64 * 1024;
29181
+ async function readBoundedStdin(limit = MAX_SHARE_STDIN_BYTES) {
29182
+ if (!Number.isSafeInteger(limit) || limit <= 0) throw new Error("MAX_BYTES_EXCEEDED");
29183
+ const chunks = [];
29184
+ let total = 0;
29185
+ for await (const chunk of process.stdin) {
29186
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
29187
+ total += bytes.byteLength;
29188
+ if (total > limit) throw new Error("MAX_BYTES_EXCEEDED");
29189
+ chunks.push(bytes);
29190
+ }
29191
+ return new Uint8Array(Buffer.concat(chunks, total));
29192
+ }
29193
+ async function readBoundedUrlStdin() {
29194
+ const bytes = await readBoundedStdin(MAX_SHARE_URL_BYTES);
29195
+ const value = new TextDecoder("utf-8", { fatal: true }).decode(bytes).trim();
29196
+ if (value.length === 0 || /\s/.test(value)) throw new Error("INVALID_ARGUMENT");
29197
+ return value;
29198
+ }
29199
+ function safeFilename(value) {
29200
+ return shareFilename(value);
29201
+ }
29202
+ function shareFilename(value) {
29203
+ try {
29204
+ return canonicalShareFilename(value);
29205
+ } catch {
29206
+ throw new CLIError(
29207
+ "UNSAFE_FILENAME",
29208
+ hasUnsafeFilenameCodePoint(value) ? "filename contains control or invisible characters" : "filename must be one safe path segment",
29209
+ 8
29210
+ );
29211
+ }
29212
+ }
29213
+ function shareInputFilename(input, name) {
29214
+ return shareFilename(name ?? (input === "-" ? "stdin.md" : basename3(resolve2(input))));
29215
+ }
29216
+ async function readShareInput(input, name, limit = MAX_SHARE_STDIN_BYTES) {
29217
+ const filename = shareInputFilename(input, name);
29218
+ if (input === "-") return { bytes: await readBoundedStdin(limit), filename };
29219
+ const path = resolve2(input);
29220
+ const info = await stat2(path);
29221
+ if (!info.isFile() || info.size > limit) throw new Error("MAX_BYTES_EXCEEDED");
29222
+ const bytes = new Uint8Array(await readFile9(path));
29223
+ if (bytes.byteLength > limit) throw new Error("MAX_BYTES_EXCEEDED");
29224
+ return { bytes, filename };
29225
+ }
29226
+ async function assertDirectory(path) {
29227
+ const absolute = resolve2(path);
29228
+ const segments = absolute.split(sep).filter(Boolean);
29229
+ let current = absolute.startsWith(sep) ? sep : "";
29230
+ for (const segment of segments) {
29231
+ current = current === sep ? join6(current, segment) : join6(current, segment);
29232
+ try {
29233
+ const info = await lstat3(current);
29234
+ if (info.isSymbolicLink()) {
29235
+ const canonical = await realpath(current);
29236
+ if (current !== "/tmp" && current !== "/var") throw new Error("OUTPUT_EXISTS");
29237
+ if (canonical !== `/private${current}`) throw new Error("OUTPUT_EXISTS");
29238
+ } else if (!info.isDirectory()) throw new Error("OUTPUT_EXISTS");
29239
+ } catch (error) {
29240
+ if (error.code !== "ENOENT") throw error;
29241
+ await mkdir3(current, { mode: 448 });
29242
+ const created = await lstat3(current);
29243
+ if (created.isSymbolicLink() || !created.isDirectory()) throw new Error("OUTPUT_EXISTS");
29244
+ }
29245
+ }
29246
+ }
29247
+ async function writeShareOutput(directory, filename, bytes, force) {
29248
+ const outputDirectory = resolve2(directory);
29249
+ await assertDirectory(outputDirectory);
29250
+ const safeName = safeFilename(filename);
29251
+ const directoryHandle = await open4(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
29252
+ const stableDirectory = await realpath(outputDirectory);
29253
+ const outputPath = join6(stableDirectory, safeName);
29254
+ const directoryIdentity = await directoryHandle.stat();
29255
+ const assertStableDirectory = async () => {
29256
+ const current = await stat2(stableDirectory);
29257
+ if (current.dev !== directoryIdentity.dev || current.ino !== directoryIdentity.ino) throw new Error("OUTPUT_EXISTS");
29258
+ };
29259
+ await assertStableDirectory();
29260
+ const stagingDirectory = await mkdtemp(join6(stableDirectory, ".tinycloud-share-stage-"));
29261
+ const stagingInfo = await lstat3(stagingDirectory);
29262
+ if (!stagingInfo.isDirectory() || (stagingInfo.mode & 511) !== 448) throw new Error("OUTPUT_EXISTS");
29263
+ const stagingPath = join6(stagingDirectory, `.tinycloud-share-${randomBytes4(16).toString("hex")}.tmp`);
29264
+ let temporaryPath;
29265
+ let handle;
29266
+ try {
29267
+ await assertStableDirectory();
29268
+ try {
29269
+ const existing = await lstat3(outputPath);
29270
+ if (existing.isSymbolicLink()) throw new Error("UNSAFE_FILENAME");
29271
+ } catch (error) {
29272
+ if (error.code !== "ENOENT") throw error;
29273
+ }
29274
+ temporaryPath = stagingPath;
29275
+ handle = await open4(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
29276
+ await handle.writeFile(bytes);
29277
+ await handle.close();
29278
+ handle = void 0;
29279
+ await assertStableDirectory();
29280
+ if (force) {
29281
+ await rename3(temporaryPath, outputPath);
29282
+ } else {
29283
+ await link(temporaryPath, outputPath);
29284
+ await unlink(temporaryPath);
29285
+ }
29286
+ await assertStableDirectory();
29287
+ } catch (error) {
29288
+ if (error.code === "EEXIST") throw new Error("OUTPUT_EXISTS");
29289
+ if (error.code === "ELOOP") throw new Error("UNSAFE_FILENAME");
29290
+ throw error;
29291
+ } finally {
29292
+ await handle?.close();
29293
+ try {
29294
+ if (temporaryPath !== void 0) await unlink(temporaryPath);
29295
+ } catch {
29296
+ }
29297
+ await rm4(stagingDirectory, { recursive: true, force: true });
29298
+ await directoryHandle.close();
29299
+ }
29300
+ return join6(outputDirectory, safeName);
29301
+ }
29302
+
29303
+ // src/share/errors.ts
29304
+ var SharePublishAuthorityError = class extends Error {
29305
+ constructor(failure) {
29306
+ super(failure.kind);
29307
+ this.failure = failure;
29308
+ this.name = "SharePublishAuthorityError";
29309
+ }
29310
+ };
29311
+ var ShareHistoryRetryError = class extends Error {
29312
+ constructor(profile) {
29313
+ super("sender history changed during the operation");
29314
+ this.profile = profile;
29315
+ this.name = "ShareHistoryRetryError";
29316
+ }
29317
+ code = "SHARE_HISTORY_RETRY";
29318
+ };
29319
+
29320
+ // src/commands/share.ts
29321
+ var SHARE_ORIGIN = "https://share.tinycloud.xyz";
29322
+ var NOTIFY_WINDOW_MESSAGE = "Share notification authorization expired (at share expiry or 5 minutes after publication); publish a new share and invite the recipient then.\n";
29323
+ var shareServices = {};
29324
+ function parseShareTarget(value) {
29325
+ if (value === "anyone" || value === "bearer") return { kind: "bearer" };
29326
+ if (value.startsWith("did:")) return { kind: "recipientDid", did: value };
29327
+ if (value.startsWith("domain:")) return canonicalMailboxTarget({ kind: "emailDomain", domain: value.slice("domain:".length) });
29328
+ if (value.startsWith("email:")) return canonicalMailboxTarget({ kind: "email", address: value.slice("email:".length) });
29329
+ if (value.includes("@")) return canonicalMailboxTarget({ kind: "email", address: value });
29330
+ throw new CLIError("INVALID_ARGUMENT", "--to must be anyone, a did:, an email address, or domain:example.com", 2);
29331
+ }
29332
+ function canonicalMailboxTarget(target) {
29333
+ try {
29334
+ return normalizeShareTarget(target);
29335
+ } catch (error) {
29336
+ throw new CLIError("INVALID_ARGUMENT", error instanceof TypeError ? error.message : "share recipient is invalid", 2);
29337
+ }
29338
+ }
29339
+ function shareCliError(error, operation = "publish") {
29340
+ if (error instanceof CLIError) return error;
29341
+ if (error instanceof ProfileLockTimeoutError3) return wrapError(error);
29342
+ if (error instanceof ShareHistoryRetryError) {
29343
+ return new CLIError(
29344
+ "SHARE_HISTORY_RETRY",
29345
+ `sender history for profile ${JSON.stringify(error.profile)} changed or disappeared during this command`,
29346
+ 1,
29347
+ { hint: `Retry this command with --profile ${JSON.stringify(error.profile)}. If this was a revoke, check its node state first: revocation may already have succeeded.` }
29348
+ );
29349
+ }
29350
+ if (error instanceof SharePublishAuthorityError) {
29351
+ const failure = error.failure;
29352
+ const profileName = "profileName" in failure ? failure.profileName : void 0;
29353
+ const localKey = "localKey" in failure && failure.localKey === true;
29354
+ const profileHint = profileName === void 0 ? "" : `--profile ${profileName} `;
29355
+ const loginHint = localKey ? `\`tc ${profileHint}auth login --method local\`` : `\`tc ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`tc ${profileHint}enable share\`)`;
29356
+ if (failure.kind === "caveated-session") {
29357
+ const holder = profileName === void 0 ? "this session" : `profile ${profileName}'s session`;
29358
+ return new CLIError(
29359
+ "PERMISSION_DENIED",
29360
+ `${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`tc init --name publisher --key-only && tc --profile publisher enable share\``,
29361
+ 5
29362
+ );
29363
+ }
29364
+ if (failure.kind === "owner-space-unresolved") {
29365
+ return new CLIError("AUTH_REQUIRED", `a valid signed TinyCloud session is required; run ${loginHint}`, 3);
29366
+ }
29367
+ if (failure.kind === "scope-denied") {
29368
+ const requiredAction = failure.requiredAction === void 0 ? "" : ` (${failure.requiredAction})`;
29369
+ 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}`;
29370
+ return new CLIError("PERMISSION_DENIED", `the session lacks ${failure.capability} authority${requiredAction}; ${renew}`, 5);
27724
29371
  }
27725
29372
  if (failure.kind === "lifetime-exceeds-session") {
27726
29373
  const expiresAt = failure.sessionExpiresAt.toISOString();
@@ -27741,6 +29388,10 @@ function shareCliError(error) {
27741
29388
  if (failure.kind === "registry-unavailable") {
27742
29389
  return new CLIError("UNAVAILABLE", "the TinyCloud location registry could not be reached, so nothing was shared; try again shortly", 4);
27743
29390
  }
29391
+ if (failure.kind === "node-info-unavailable") {
29392
+ const outcome = operation === "publish" ? "nothing was shared and no invitation was sent" : "no invitation was sent";
29393
+ return new CLIError("UNAVAILABLE", `could not verify TinyCloud node 1.17.3 domain delivery support; ${outcome}. Check the node and retry`, 4);
29394
+ }
27744
29395
  if (failure.kind === "registry-rejected") {
27745
29396
  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);
27746
29397
  }
@@ -27757,14 +29408,14 @@ function shareCliError(error) {
27757
29408
  }
27758
29409
  if (error instanceof SharePublishError) {
27759
29410
  const exit = error.code === "authority-required" ? 3 : error.code === "max-bytes-exceeded" ? 7 : 2;
27760
- const code2 = error.code === "authority-required" ? "AUTH_REQUIRED" : error.code === "max-bytes-exceeded" ? "MAX_BYTES_EXCEEDED" : "INVALID_ARGUMENT";
27761
- return new CLIError(code2, error.message, exit);
29411
+ const code3 = error.code === "authority-required" ? "AUTH_REQUIRED" : error.code === "max-bytes-exceeded" ? "MAX_BYTES_EXCEEDED" : "INVALID_ARGUMENT";
29412
+ return new CLIError(code3, error.message, exit);
27762
29413
  }
27763
29414
  if (error instanceof ShareReceiveError) {
27764
29415
  const verification = /* @__PURE__ */ new Set(["cid-mismatch", "decrypt-failed", "envelope-invalid", "origin-mismatch", "signature-invalid", "capability-invalid", "content-integrity-failed"]);
27765
29416
  const exit = error.code === "max-bytes-exceeded" ? 7 : verification.has(error.code) ? 5 : error.code === "expired" || error.code === "fetch-failed" ? 4 : error.code === "invalid-link" || error.code === "unsupported-target" ? 2 : 2;
27766
- const code2 = error.code === "fetch-failed" ? "NOT_FOUND" : error.code.replaceAll("-", "_").toUpperCase();
27767
- return new CLIError(code2, error.message, exit);
29417
+ const code3 = error.code === "fetch-failed" ? "NOT_FOUND" : error.code.replaceAll("-", "_").toUpperCase();
29418
+ return new CLIError(code3, error.message, exit);
27768
29419
  }
27769
29420
  const message = error instanceof Error ? error.message : String(error);
27770
29421
  const nodeCode = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
@@ -27809,8 +29460,33 @@ function expires(value) {
27809
29460
  }
27810
29461
  async function rememberPublishedShare(result) {
27811
29462
  const record = historyRecordForPublishedShare(result);
27812
- if (shareServices.records !== void 0) await shareServices.records.put(record);
27813
- return record;
29463
+ if (shareServices.records === void 0) return { record, recorded: false };
29464
+ try {
29465
+ await shareServices.records.put(record);
29466
+ return { record, recorded: true };
29467
+ } catch {
29468
+ process.stderr.write("Warning: share was published but not recorded in sender history; future share list, share notify, and share revoke by ID will not find it.\n");
29469
+ return { record, recorded: false };
29470
+ }
29471
+ }
29472
+ async function notifyRecordedShare(record, recipient, adapter, storage) {
29473
+ const result = await notifyShare({ shareId: record.shareId, recipient, record, adapter, checkDeliveryWindow: true });
29474
+ if (result.state !== "partial-failure" && storage !== void 0) {
29475
+ const mailbox = canonicalMailbox2(recipient).email;
29476
+ if (!record.deliveredRecipients?.includes(mailbox)) {
29477
+ try {
29478
+ if (storage.update === void 0) throw new Error("atomic sender history update is unavailable");
29479
+ const updated = await storage.update(record.shareId, (current) => ({
29480
+ ...current,
29481
+ deliveredRecipients: current.deliveredRecipients?.includes(mailbox) ? current.deliveredRecipients : [...current.deliveredRecipients ?? [], mailbox]
29482
+ }));
29483
+ if (updated === void 0) throw new Error("sender history record was removed");
29484
+ } catch {
29485
+ process.stderr.write("Warning: delivery succeeded, but could not record its confirmation in sender history.\n");
29486
+ }
29487
+ }
29488
+ }
29489
+ return result;
27814
29490
  }
27815
29491
  function byteLimit(value) {
27816
29492
  if (value === void 0) return void 0;
@@ -27837,7 +29513,7 @@ function assertAggregateInputLimit(inputs, maxBytes) {
27837
29513
  }
27838
29514
  function registerShareCommand(program) {
27839
29515
  const share = program.command("share").description("Publish and consume TinyCloud Share links");
27840
- share.command("publish <files...>").description("Publish one or more bounded files as a Share").option("--name <filename>", "Filename for stdin input").option("--to <target>", "Share target", "anyone").option("--notify", "Request idempotent email delivery for addressed targets").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN).action(async (files, options, command) => {
29516
+ share.command("publish <files...>").description("Publish one or more bounded files as a Share").option("--name <filename>", "Filename for stdin input").option("--to <target>", "Share target", "anyone").option("--notify", "Email an exact-email recipient, or a domain mailbox on node 1.17.3+").option("--notify-to <address>", "Mailbox to invite for --to domain:<name> --notify (node 1.17.3+)").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--json", "Print versioned redacted JSON").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN).action(async (files, options, command) => {
27841
29517
  try {
27842
29518
  const json = jsonOutput(options, command);
27843
29519
  const maxBytes = byteLimit(options.maxBytes);
@@ -27851,7 +29527,21 @@ function registerShareCommand(program) {
27851
29527
  if (options.prefix && target.kind === "bearer") throw new CLIError("INVALID_ARGUMENT", "--prefix requires an addressed target", 2);
27852
29528
  if (inputs.length > 1 && target.kind === "bearer") throw new CLIError("UNSUPPORTED_LINK", "multiple files require an addressed target", 2);
27853
29529
  if (inputs.length > 1 && !options.prefix) throw new CLIError("INVALID_ARGUMENT", "multiple files require --prefix", 2);
27854
- if (options.notify === true && target.kind !== "email") throw new CLIError("INVALID_ARGUMENT", "--notify requires an exact email target", 2);
29530
+ if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
29531
+ if (options.notifyTo !== void 0 && (options.notify !== true || target.kind !== "emailDomain")) {
29532
+ throw new CLIError("INVALID_ARGUMENT", "--notify-to requires --to domain:<name> --notify", 2);
29533
+ }
29534
+ let notifyRecipient;
29535
+ if (options.notify === true && target.kind === "email") notifyRecipient = target.address;
29536
+ else if (options.notify === true && target.kind === "emailDomain") {
29537
+ const mailbox = options.notifyTo === void 0 ? void 0 : canonicalMailbox2(options.notifyTo);
29538
+ if (mailbox === void 0 || mailbox.domain !== target.domain) {
29539
+ throw new CLIError("INVALID_ARGUMENT", "--to domain:<name> --notify requires --notify-to <email> at that exact domain (node 1.17.3+)", 2);
29540
+ }
29541
+ notifyRecipient = mailbox.email;
29542
+ } else if (options.notify === true) {
29543
+ throw new CLIError("INVALID_ARGUMENT", "--notify requires an email target, or a domain target with --notify-to on node 1.17.3+", 2);
29544
+ }
27855
29545
  const result = await publishTargetShare({
27856
29546
  source: inputs[0].bytes,
27857
29547
  filename: inputs[0].filename,
@@ -27876,16 +29566,17 @@ function registerShareCommand(program) {
27876
29566
  }
27877
29567
  throw new CLIError(result.method === "openkey-device" ? "DEVICE_AUTH_REQUIRED" : "CLAIM_REQUIRED", "recipient authorization is required; continue through the configured authority adapter", 6);
27878
29568
  }
27879
- const record = await rememberPublishedShare(result);
27880
- if (options.notify === true && target.kind === "email") {
29569
+ const { record, recorded } = await rememberPublishedShare(result);
29570
+ let notification;
29571
+ if (notifyRecipient !== void 0) {
27881
29572
  if (shareServices.delivery === void 0) throw new CLIError("AUTH_REQUIRED", "delivery authority is not configured", 3);
27882
- const recipient = record.recipientMatcher.kind === "exactEmail" ? record.recipientMatcher.value : target.address;
27883
- const delivery = await notifyShare({ shareId: record.shareId, recipient, record, adapter: shareServices.delivery });
27884
- if (delivery.state === "partial-failure") process.exitCode = 9;
29573
+ notification = await notifyRecordedShare(record, notifyRecipient, shareServices.delivery, recorded ? shareServices.records : void 0);
29574
+ if (notification.state === "partial-failure") process.exitCode = 9;
29575
+ if (notification.reason === "delivery-window-expired") process.stderr.write(NOTIFY_WINDOW_MESSAGE);
27885
29576
  }
27886
29577
  if (result.metadata.expiryClamped === true) process.stderr.write(`Share expiry clamped to session expiry (${result.metadata.expiresAt}).
27887
29578
  `);
27888
- if (json) writeJson2({ ...redactPublishedShare(result), expiryClamped: result.metadata.expiryClamped === true });
29579
+ if (json) writeJson2({ ...redactPublishedShare(result), expiryClamped: result.metadata.expiryClamped === true, ...notification === void 0 ? {} : { notification } });
27889
29580
  else publishHuman(result);
27890
29581
  } catch (error) {
27891
29582
  handleError(shareCliError(error));
@@ -27972,20 +29663,31 @@ function registerShareCommand(program) {
27972
29663
  handleError(shareCliError(error));
27973
29664
  }
27974
29665
  });
27975
- share.command("notify <id>").description("Retry idempotent delivery without recreating the share").requiredOption("--to <address>", "Recipient email").option("--json", "Print versioned JSON").action(async (id, options, command) => {
29666
+ share.command("notify <id>").description("Retry idempotent delivery within 5 minutes of publication; after that publish a new share").requiredOption("--to <address>", "Recipient email").option("--json", "Print versioned JSON").action(async (id, options, command) => {
27976
29667
  try {
27977
29668
  const json = jsonOutput(options, command);
27978
29669
  if (shareServices.delivery === void 0) throw new CLIError("AUTH_REQUIRED", "delivery authority is not configured", 3);
27979
29670
  if (shareServices.records === void 0) throw new CLIError("AUTH_REQUIRED", "sender history storage is not configured", 3);
27980
29671
  const record = await shareServices.records.get(id);
27981
29672
  if (record === void 0) throw new CLIError("NOT_FOUND", "share not found", 4);
27982
- const result = await notifyShare({ shareId: id, recipient: options.to, record, adapter: shareServices.delivery });
29673
+ if (!record.actions.includes("tinycloud.kv/get")) {
29674
+ throw new CLIError("INVALID_ARGUMENT", "share notify requires a stored share with the read action; no invitation was sent", 2);
29675
+ }
29676
+ const mailbox = canonicalMailbox2(options.to);
29677
+ if (mailbox === void 0) throw new ShareNotifyError("recipient is invalid");
29678
+ if (!recipientMatchesShareRecord(record, mailbox.email)) throw new ShareNotifyError("recipient does not match the stored share target");
29679
+ if (record.recipientMatcher.kind === "emailDomain" && shareDeliveryWindowExpiresAt(record) > Date.now()) {
29680
+ if (shareServices.assertDomainDelivery === void 0) throw new CLIError("UNAVAILABLE", "could not verify TinyCloud node 1.17.3 domain delivery support; no invitation was sent", 4);
29681
+ await shareServices.assertDomainDelivery();
29682
+ }
29683
+ const result = await notifyRecordedShare(record, options.to, shareServices.delivery, shareServices.records);
27983
29684
  if (json) writeJson2(result);
27984
29685
  else process.stdout.write(`${result.state}
27985
29686
  `);
29687
+ if (result.reason === "delivery-window-expired") process.stderr.write(NOTIFY_WINDOW_MESSAGE);
27986
29688
  if (result.state === "partial-failure") process.exitCode = 9;
27987
29689
  } catch (error) {
27988
- handleError(shareCliError(error));
29690
+ handleError(shareCliError(error, "notify"));
27989
29691
  }
27990
29692
  });
27991
29693
  share.command("revoke <id>").description("Revoke addressed shares; report bearer retention honestly").option("--ancestor", "Revoke the owner delegation ancestry").option("--json", "Print versioned JSON").action(async (id, options, command) => {
@@ -28030,7 +29732,7 @@ function registerSpaceCommand(program) {
28030
29732
  const node = await ensureAuthenticated(ctx);
28031
29733
  const result = await node.spaces.list();
28032
29734
  if (!result.ok) {
28033
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
29735
+ throw cliErrorFromService(result.error);
28034
29736
  }
28035
29737
  if (shouldOutputJson()) {
28036
29738
  outputJson({ spaces: result.data, count: result.data.length });
@@ -28175,7 +29877,7 @@ async function throwSqlError(error, spaceUri, profileName, prefix) {
28175
29877
  const hosted = await unhostedSpaceError(error, spaceUri, profileName);
28176
29878
  if (hosted) throw hosted;
28177
29879
  const message = prefix ? `${prefix}${error.message}` : error.message;
28178
- throw new CLIError(error.code, message, ExitCode.ERROR, error.meta);
29880
+ throw cliErrorFromService(error, message);
28179
29881
  }
28180
29882
  function registerSqlCommand(program) {
28181
29883
  const sql = program.command("sql").description("SQLite database operations for your TinyCloud space").addHelpText("after", `
@@ -28561,7 +30263,13 @@ async function readHasKey(name, issues) {
28561
30263
  }
28562
30264
  async function readDelegations(name, issues) {
28563
30265
  try {
28564
- return await loadAdditionalDelegations(name);
30266
+ const entries = await loadAdditionalDelegations(name);
30267
+ const { storedDelegationKind } = await import("@tinycloud/operations/delegation-binding");
30268
+ const readable = entries.filter((entry) => storedDelegationKind(entry) !== "refused");
30269
+ if (readable.length < entries.length) {
30270
+ issues.push(`delegations: ${entries.length - readable.length} unreadable stored record(s) skipped`);
30271
+ }
30272
+ return readable;
28565
30273
  } catch (error) {
28566
30274
  issues.push(`delegations: ${messageFromError(error)}`);
28567
30275
  return [];
@@ -28881,7 +30589,7 @@ async function unlockVault(node, privateKey) {
28881
30589
  const signer = new PrivateKeySigner2(privateKey);
28882
30590
  const result = await node.vault.unlock(signer);
28883
30591
  if (result && !result.ok) {
28884
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30592
+ throw cliErrorFromService(result.error);
28885
30593
  }
28886
30594
  }
28887
30595
  function registerVaultCommand(program) {
@@ -28922,7 +30630,7 @@ function registerVaultCommand(program) {
28922
30630
  }
28923
30631
  const result = await withSpinner(`Writing ${key}...`, () => node.vault.put(key, putValue));
28924
30632
  if (!result.ok) {
28925
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30633
+ throw cliErrorFromService(result.error);
28926
30634
  }
28927
30635
  outputJson({ key, written: true });
28928
30636
  } catch (error) {
@@ -28941,7 +30649,7 @@ function registerVaultCommand(program) {
28941
30649
  if (result.error.code === "NOT_FOUND") {
28942
30650
  throw new CLIError("NOT_FOUND", `Key "${key}" not found`, ExitCode.NOT_FOUND);
28943
30651
  }
28944
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30652
+ throw cliErrorFromService(result.error);
28945
30653
  }
28946
30654
  const data = result.data.data ?? result.data;
28947
30655
  if (options.output) {
@@ -28972,7 +30680,7 @@ function registerVaultCommand(program) {
28972
30680
  await withSpinner("Unlocking vault...", () => unlockVault(node, privateKey));
28973
30681
  const result = await withSpinner(`Deleting ${key}...`, () => node.vault.delete(key));
28974
30682
  if (!result.ok) {
28975
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30683
+ throw cliErrorFromService(result.error);
28976
30684
  }
28977
30685
  outputJson({ key, deleted: true });
28978
30686
  } catch (error) {
@@ -28989,7 +30697,7 @@ function registerVaultCommand(program) {
28989
30697
  const listOptions = options.prefix ? { prefix: options.prefix } : void 0;
28990
30698
  const result = await withSpinner("Listing vault keys...", () => node.vault.list(listOptions));
28991
30699
  if (!result.ok) {
28992
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30700
+ throw cliErrorFromService(result.error);
28993
30701
  }
28994
30702
  const keys = result.data.data ?? result.data;
28995
30703
  const keyList = Array.isArray(keys) ? keys : [];
@@ -29015,7 +30723,7 @@ function registerVaultCommand(program) {
29015
30723
  outputJson({ key, exists: false, metadata: {} });
29016
30724
  return;
29017
30725
  }
29018
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30726
+ throw cliErrorFromService(result.error);
29019
30727
  }
29020
30728
  outputJson({
29021
30729
  key,
@@ -29065,7 +30773,7 @@ function registerVarsCommand(program) {
29065
30773
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
29066
30774
  const result = await withSpinner("Listing variables...", () => prefixedKv.list());
29067
30775
  if (!result.ok) {
29068
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30776
+ throw cliErrorFromService(result.error);
29069
30777
  }
29070
30778
  const rawData = result.data.data ?? result.data;
29071
30779
  const keyList = Array.isArray(rawData) ? rawData : rawData?.keys ?? [];
@@ -29089,7 +30797,7 @@ function registerVarsCommand(program) {
29089
30797
  if (result.error.code === "KV_NOT_FOUND" || result.error.code === "NOT_FOUND") {
29090
30798
  throw new CLIError("NOT_FOUND", `Variable "${name}" not found`, ExitCode.NOT_FOUND);
29091
30799
  }
29092
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30800
+ throw cliErrorFromService(result.error);
29093
30801
  }
29094
30802
  const data = result.data.data;
29095
30803
  let value;
@@ -29147,7 +30855,7 @@ function registerVarsCommand(program) {
29147
30855
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
29148
30856
  const result = await withSpinner(`Setting variable ${name}...`, () => prefixedKv.put(name, payload));
29149
30857
  if (!result.ok) {
29150
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30858
+ throw cliErrorFromService(result.error);
29151
30859
  }
29152
30860
  outputJson({ name, written: true });
29153
30861
  } catch (error) {
@@ -29163,7 +30871,7 @@ function registerVarsCommand(program) {
29163
30871
  const prefixedKv = node.kv.withPrefix(VARIABLES_PREFIX);
29164
30872
  const result = await withSpinner(`Deleting variable ${name}...`, () => prefixedKv.delete(name));
29165
30873
  if (!result.ok) {
29166
- throw new CLIError(result.error.code, result.error.message, ExitCode.ERROR);
30874
+ throw cliErrorFromService(result.error);
29167
30875
  }
29168
30876
  outputJson({ name, deleted: true });
29169
30877
  } catch (error) {