@tinycloud/cli 1.2.3-beta.0 → 1.2.3-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -225,10 +225,11 @@ var init_warnings = __esm({
225
225
 
226
226
  // src/output/formatter.ts
227
227
  import ora from "ora";
228
+ import { CLI_NAME as CLI_NAME2 } from "@tinycloud/operations/cli-name";
228
229
  function warningLine(warnings) {
229
230
  const which = warnings.map((warning) => `${warning.grantCid ?? "unidentified"}: ${warning.reason}`).join(", ");
230
231
  const subject = warnings.length === 1 ? "A stored grant was" : `${warnings.length} stored grants were`;
231
- return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with tc auth grants remove <cid>.`;
232
+ return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with ${CLI_NAME2} auth grants remove <cid>.`;
232
233
  }
233
234
  function outputError(code3, message, hint, details = {}) {
234
235
  const { meta, warnings = [], setup, recovery, progress } = details;
@@ -383,6 +384,7 @@ import { readFileSync, readdirSync } from "fs";
383
384
  import { join } from "path";
384
385
  import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
385
386
  import { isSafeRecovery } from "@tinycloud/operations/recovery";
387
+ import { CLI_NAME as CLI_NAME3 } from "@tinycloud/operations/cli-name";
386
388
  import {
387
389
  buildSecretRequestUrl,
388
390
  isSafeRequestAccessRecovery,
@@ -439,7 +441,7 @@ function missingPrivateKeyError() {
439
441
  "AUTH_REQUIRED",
440
442
  `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
441
443
  ExitCode.AUTH_REQUIRED,
442
- { hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey` }
444
+ { hint: `Sign in again with: ${CLI_NAME3} --profile ${profileName} auth login --method openkey` }
443
445
  );
444
446
  }
445
447
  function storageFullError(error, progress) {
@@ -530,7 +532,7 @@ function buildAuthHint(error) {
530
532
  if (!spec) return void 0;
531
533
  return [
532
534
  "The active session is missing a TinyCloud capability.",
533
- `Request it with: tc auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
535
+ `Request it with: ${CLI_NAME3} auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
534
536
  "Then retry the original command."
535
537
  ].join("\n");
536
538
  }
@@ -573,12 +575,12 @@ function buildNetworkHint() {
573
575
  const lines = [];
574
576
  lines.push(activeHost ? `Active profile "${activeName}" \u2192 ${activeHost}` : `Active profile "${activeName}"`);
575
577
  if (others.length === 0) {
576
- lines.push(`No other profiles configured. Run \`tc profile create <name>\` or \`tc init\`.`);
578
+ lines.push(`No other profiles configured. Run \`${CLI_NAME3} profile create <name>\` or \`${CLI_NAME3} init\`.`);
577
579
  } else {
578
580
  lines.push(`Switch to a reachable profile:`);
579
581
  const longest = Math.max(...others.map((p) => p.name.length));
580
582
  for (const { name, host } of others) {
581
- lines.push(` tc profile switch ${name.padEnd(longest)} # ${host}`);
583
+ lines.push(` ${CLI_NAME3} profile switch ${name.padEnd(longest)} # ${host}`);
582
584
  }
583
585
  }
584
586
  lines.push(`Or override per-command with --host or TC_HOST.`);
@@ -691,6 +693,7 @@ var init_types = __esm({
691
693
  });
692
694
 
693
695
  // src/lib/space.ts
696
+ import { CLI_NAME as CLI_NAME5 } from "@tinycloud/operations/cli-name";
694
697
  function canonicalizeAddress(address) {
695
698
  const trimmed = address.trim();
696
699
  return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
@@ -732,7 +735,7 @@ function resolveAddress(profile, session) {
732
735
  }
733
736
  throw new CLIError(
734
737
  "ADDRESS_UNKNOWN",
735
- `Cannot determine Ethereum address for profile "${profile.name}". Run \`tc auth login\` to refresh the session.`,
738
+ `Cannot determine Ethereum address for profile "${profile.name}". Run \`${CLI_NAME5} auth login\` to refresh the session.`,
736
739
  ExitCode.AUTH_REQUIRED
737
740
  );
738
741
  }
@@ -5681,6 +5684,7 @@ var init_permissions = __esm({
5681
5684
 
5682
5685
  // src/auth/session-expired.ts
5683
5686
  import { signInRecovery } from "@tinycloud/operations/recovery";
5687
+ import { CLI_NAME as CLI_NAME6 } from "@tinycloud/operations/cli-name";
5684
5688
  function signInAgainHint(profileName, posture) {
5685
5689
  const delegate = posture === "delegate-session";
5686
5690
  const { command } = signInRecovery(
@@ -5688,7 +5692,7 @@ function signInAgainHint(profileName, posture) {
5688
5692
  delegate ? "delegate-session" : posture === "local-owner-key" ? "local-owner-key" : "owner-openkey"
5689
5693
  );
5690
5694
  if (command === void 0) {
5691
- return delegate ? `Have the owner approve a new scoped login for profile ${JSON.stringify(profileName)} with tc auth login --method openkey --paste --manifest <manifest.json>.` : `Sign in again to profile ${JSON.stringify(profileName)} with tc auth login.`;
5695
+ return delegate ? `Have the owner approve a new scoped login for profile ${JSON.stringify(profileName)} with ${CLI_NAME6} auth login --method openkey --paste --manifest <manifest.json>.` : `Sign in again to profile ${JSON.stringify(profileName)} with ${CLI_NAME6} auth login.`;
5692
5696
  }
5693
5697
  return delegate ? `Have the owner approve a new scoped login: ${command}. Pass the owner's code on stdin, newline-terminated.` : `Sign in again with: ${command}`;
5694
5698
  }
@@ -5726,6 +5730,7 @@ var init_delegation_expiry = __esm({
5726
5730
  });
5727
5731
 
5728
5732
  // src/lib/replication-log.ts
5733
+ import { CLI_NAME as CLI_NAME7 } from "@tinycloud/operations/cli-name";
5729
5734
  import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, writeFileSync } from "fs";
5730
5735
  import { dirname as dirname2, join as join4 } from "path";
5731
5736
  function appendReplicationEvent(root, event) {
@@ -5788,7 +5793,7 @@ function createReplicationEventSink(profileRoot, options) {
5788
5793
  }
5789
5794
  if (event.type === "replication.read" && (event.pendingState === "in_flight" || event.pendingState === "ambiguous") && !options.quiet && !pinnedNotices.has(event.key)) {
5790
5795
  pinnedNotices.add(event.key);
5791
- process.stderr.write(`[replication] ${event.key} reads from the network: an earlier write's outcome is unknown (${event.pendingState}${event.code ? `, ${event.code}` : ""}). Review: tc replica report
5796
+ process.stderr.write(`[replication] ${event.key} reads from the network: an earlier write's outcome is unknown (${event.pendingState}${event.code ? `, ${event.code}` : ""}). Review: ${CLI_NAME7} replica report
5792
5797
  `);
5793
5798
  }
5794
5799
  };
@@ -7783,6 +7788,7 @@ var init_dist = __esm({
7783
7788
 
7784
7789
  // src/lib/replication-config.ts
7785
7790
  import { join as join5 } from "path";
7791
+ import { CLI_NAME as CLI_NAME8 } from "@tinycloud/operations/cli-name";
7786
7792
  import { profilePath, withProfileLock as withProfileLock2 } from "@tinycloud/operations/state";
7787
7793
  import { sqliteReplicaStorage } from "@tinycloud/node-sdk";
7788
7794
  function createCliReplicationConfig(profile, settings, output, enabled) {
@@ -7797,7 +7803,7 @@ function createCliReplicationConfig(profile, settings, output, enabled) {
7797
7803
  "INVALID_REPLICATION_PREFIX",
7798
7804
  error instanceof Error ? error.message : String(error),
7799
7805
  ExitCode.USAGE_ERROR,
7800
- { hint: `Replace the saved replication prefix with \`tc auth login --replication-prefix ${replacement}\`; purging a replica alone does not change this profile setting.` }
7806
+ { hint: `Replace the saved replication prefix with \`${CLI_NAME8} auth login --replication-prefix ${replacement}\`; purging a replica alone does not change this profile setting.` }
7801
7807
  );
7802
7808
  }
7803
7809
  }
@@ -7843,6 +7849,7 @@ __export(sdk_exports, {
7843
7849
  storedSessionVerifiesLocally: () => storedSessionVerifiesLocally
7844
7850
  });
7845
7851
  import { TinyCloudNode, signedCompactUcanExpiry } from "@tinycloud/node-sdk";
7852
+ import { CLI_NAME as CLI_NAME9 } from "@tinycloud/operations/cli-name";
7846
7853
  import { ownerSessionLacksProof } from "@tinycloud/operations/profile";
7847
7854
  function jwkHasPrivateParameter(jwk) {
7848
7855
  if (!jwk || typeof jwk !== "object") return false;
@@ -7865,7 +7872,7 @@ function signerJwkForProfile(profileName, sessionJwk, key) {
7865
7872
  `Profile "${profileName}" cannot restore its session because its private key material is missing.`,
7866
7873
  ExitCode.AUTH_REQUIRED,
7867
7874
  {
7868
- hint: `Sign in again with: tc --profile ${profileName} auth login --method openkey`
7875
+ hint: `Sign in again with: ${CLI_NAME9} --profile ${profileName} auth login --method openkey`
7869
7876
  }
7870
7877
  );
7871
7878
  }
@@ -7952,7 +7959,7 @@ async function createSDKInstance(ctx, options) {
7952
7959
  if (!key && !effectivePrivateKey && !(profile?.authMethod === "openkey" && session !== null)) {
7953
7960
  throw new CLIError(
7954
7961
  "AUTH_REQUIRED",
7955
- `No key found for profile "${ctx.profile}". Run \`tc --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
7962
+ `No key found for profile "${ctx.profile}". Run \`${CLI_NAME9} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
7956
7963
  ExitCode.AUTH_REQUIRED
7957
7964
  );
7958
7965
  }
@@ -8071,7 +8078,7 @@ async function bootstrapDelegatedSession(ctx, delegation) {
8071
8078
  return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
8072
8079
  }, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
8073
8080
  // The lock or a read failed: the import's error stays the one reported.
8074
- `Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`tc --profile ${ctx.profile} context\`.`
8081
+ `Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`${CLI_NAME9} --profile ${ctx.profile} context\`.`
8075
8082
  ));
8076
8083
  throw annotate(reported, note);
8077
8084
  };
@@ -8101,7 +8108,7 @@ async function restoreBeforeBootstrap(profileName, previousProfile) {
8101
8108
  });
8102
8109
  }
8103
8110
  if (failures.length === 0) return void 0;
8104
- return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`tc --profile ${profileName} context\`.`;
8111
+ return `Rolling back the provisional session of profile "${profileName}" failed too (${failures.join("; ")}); check \`${CLI_NAME9} --profile ${profileName} context\`.`;
8105
8112
  }
8106
8113
  function failureName(error) {
8107
8114
  if (error instanceof SyntaxError) return "a profile file is not valid JSON";
@@ -8125,7 +8132,7 @@ async function ensureAuthenticated(ctx, options) {
8125
8132
  if (!session) {
8126
8133
  throw new CLIError(
8127
8134
  "AUTH_REQUIRED",
8128
- `Not authenticated. Run \`tc auth login\` or \`tc init\` first.`,
8135
+ `Not authenticated. Run \`${CLI_NAME9} auth login\` or \`${CLI_NAME9} init\` first.`,
8129
8136
  ExitCode.AUTH_REQUIRED
8130
8137
  );
8131
8138
  }
@@ -8162,6 +8169,7 @@ __export(host_exports, {
8162
8169
  spaceNameFromUri: () => spaceNameFromUri,
8163
8170
  unhostedSpaceError: () => unhostedSpaceError
8164
8171
  });
8172
+ import { CLI_NAME as CLI_NAME10 } from "@tinycloud/operations/cli-name";
8165
8173
  import {
8166
8174
  discoverLocalTinyCloudNode
8167
8175
  } from "@tinycloud/sdk-core";
@@ -8255,13 +8263,13 @@ async function unhostedSpaceError(error, spaceUri, profileName) {
8255
8263
  const owner = await isRootAuthority(spaceUri, profileName);
8256
8264
  const hint = owner ? [
8257
8265
  "You are the owner. Host it once:",
8258
- ` tc space host ${spaceName}`,
8266
+ ` ${CLI_NAME10} space host ${spaceName}`,
8259
8267
  "Then retry."
8260
8268
  ].join("\n") : [
8261
8269
  "You are a delegate and CANNOT host this space \u2014 only its owner can.",
8262
8270
  "Emit a host request:",
8263
- ` tc space host-request ${spaceName} --emit ./host-request.json`,
8264
- "Send it to the owner; they run `tc space host` and confirm. Then retry."
8271
+ ` ${CLI_NAME10} space host-request ${spaceName} --emit ./host-request.json`,
8272
+ `Send it to the owner; they run \`${CLI_NAME10} space host\` and confirm. Then retry.`
8265
8273
  ].join("\n");
8266
8274
  const message = owner ? `Space '${spaceName}' (${spaceUri}) is not hosted.` : `Space '${spaceName}' (owner ${ownerDidFromSpaceUri(spaceUri) ?? spaceUri}) is not hosted.`;
8267
8275
  return new CLIError("SPACE_NOT_HOSTED", message, ExitCode.ERROR, { hint });
@@ -8286,6 +8294,7 @@ var init_host = __esm({
8286
8294
 
8287
8295
  // src/config/profiles.ts
8288
8296
  import { chmod as chmod2, lstat, readdir as readdir2, rm as rm2, rmdir } from "fs/promises";
8297
+ import { CLI_NAME as CLI_NAME11 } from "@tinycloud/operations/cli-name";
8289
8298
  import { join as join6 } from "path";
8290
8299
  import {
8291
8300
  profilePath as profilePath2,
@@ -8371,7 +8380,7 @@ var init_profiles = __esm({
8371
8380
  if (!profile) {
8372
8381
  throw new CLIError(
8373
8382
  "PROFILE_NOT_FOUND",
8374
- `Profile "${name}" does not exist. Create it with \`tc init --name ${name}\`.`
8383
+ `Profile "${name}" does not exist. Create it with \`${CLI_NAME11} init --name ${name}\`.`
8375
8384
  );
8376
8385
  }
8377
8386
  return profile;
@@ -8429,7 +8438,7 @@ var init_profiles = __esm({
8429
8438
  if (config.defaultProfile === name) {
8430
8439
  throw new CLIError(
8431
8440
  "PROFILE_DELETE_DEFAULT",
8432
- `Cannot delete the default profile "${name}". Change the default first with \`tc profile default <other>\`.`
8441
+ `Cannot delete the default profile "${name}". Change the default first with \`${CLI_NAME11} profile default <other>\`.`
8433
8442
  );
8434
8443
  }
8435
8444
  const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
@@ -8566,15 +8575,18 @@ var init_profiles = __esm({
8566
8575
  });
8567
8576
 
8568
8577
  // src/commands/courier.ts
8569
- var TC_OPTIONS_BEFORE_COURIER = 'tc courier takes no tc options and does not use tc profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. tc courier init --host <url>.';
8578
+ import { CLI_NAME } from "@tinycloud/operations/cli-name";
8579
+ var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME} courier takes no ${CLI_NAME} options and does not use ${CLI_NAME} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME} courier init --host <url>.`;
8570
8580
 
8571
8581
  // src/index.ts
8572
8582
  init_errors();
8573
8583
  import { readFileSync as readFileSync2 } from "fs";
8584
+ import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
8574
8585
  import { Command } from "commander";
8575
8586
 
8576
8587
  // src/output/banner.ts
8577
8588
  init_formatter();
8589
+ import { CLI_NAME as CLI_NAME4 } from "@tinycloud/operations/cli-name";
8578
8590
 
8579
8591
  // src/output/taglines.ts
8580
8592
  var HOLIDAY_TAGLINES = [
@@ -8645,14 +8657,14 @@ function resolveCommitHash() {
8645
8657
  function formatBannerLine(version2) {
8646
8658
  const commit = resolveCommitHash();
8647
8659
  const tagline = pickTagline();
8648
- const versionPart = `tc v${version2}`;
8660
+ const versionPart = `${CLI_NAME4} v${version2}`;
8649
8661
  const commitPart = commit ? ` (${commit})` : "";
8650
8662
  const separator = " \u2014 ";
8651
8663
  if (!isInteractive()) {
8652
8664
  return `${versionPart}${commitPart}${separator}${tagline}`;
8653
8665
  }
8654
8666
  return [
8655
- theme.brand("\u2601\uFE0F tc"),
8667
+ theme.brand(`\u2601\uFE0F ${CLI_NAME4}`),
8656
8668
  " ",
8657
8669
  theme.muted(`v${version2}`),
8658
8670
  commit ? theme.dim(` (${commit})`) : "",
@@ -8674,6 +8686,9 @@ init_theme();
8674
8686
  init_formatter();
8675
8687
  init_profiles();
8676
8688
 
8689
+ // src/commands/share.ts
8690
+ import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
8691
+
8677
8692
  // ../share-sdk/dist/index.js
8678
8693
  init_sha2();
8679
8694
  init_ed25519();
@@ -15973,7 +15988,7 @@ async function revokeShare(input) {
15973
15988
  if (delegationCid === void 0) return { state: "unsupported", target, reason: "share has no node-enforced delegation receipt", code: "unsupported-target" };
15974
15989
  if (target === "bearer") {
15975
15990
  if (input.adapter.revokeDelegation === void 0) return { state: "unsupported", target, reason: "native delegation revocation authority is required", code: "unsupported-target" };
15976
- await input.adapter.revokeDelegation({ delegationCid, scope });
15991
+ await input.adapter.revokeDelegation({ delegationCid, scope, record: input.record });
15977
15992
  } else {
15978
15993
  if (input.record.ownerDid === void 0) return { state: "unsupported", target, reason: "share has no Policy/v3 owner receipt", code: "unsupported-target" };
15979
15994
  if (input.adapter.revokePolicyRoot === void 0) return { state: "unsupported", target, reason: "Policy/v3 root revocation authority is required", code: "unsupported-target" };
@@ -15985,11 +16000,14 @@ async function revokeShare(input) {
15985
16000
  nodeAudience: policyNodeAudience(input.record)
15986
16001
  });
15987
16002
  }
15988
- const revokedAt = (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
16003
+ let revokedAt = input.record.revokedAt ?? (input.now?.() ?? /* @__PURE__ */ new Date()).toISOString();
15989
16004
  if (input.records?.update !== void 0) {
15990
- await input.records.update(input.record.shareId, (record) => ({ ...record, revokedAt }));
16005
+ const updated = await input.records.update(input.record.shareId, (record) => ({ ...record, revokedAt: record.revokedAt ?? revokedAt }));
16006
+ revokedAt = updated?.revokedAt ?? revokedAt;
15991
16007
  } else if (input.records !== void 0) {
15992
- await input.records.put({ ...input.record, revokedAt });
16008
+ const latest = await input.records.get(input.record.shareId);
16009
+ revokedAt = latest?.revokedAt ?? revokedAt;
16010
+ await input.records.put({ ...latest ?? input.record, revokedAt });
15993
16011
  }
15994
16012
  return { state: "revoked", target, delegationCid, revokedAt };
15995
16013
  }
@@ -17680,12 +17698,12 @@ function shareCliError(error, operation = "publish") {
17680
17698
  const profileName = "profileName" in failure ? failure.profileName : void 0;
17681
17699
  const localKey = "localKey" in failure && failure.localKey === true;
17682
17700
  const profileHint = profileName === void 0 ? "" : `--profile ${profileName} `;
17683
- const loginHint = localKey ? `\`tc ${profileHint}auth login --method local\`` : `\`tc ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`tc ${profileHint}enable share\`)`;
17701
+ const loginHint = localKey ? `\`${CLI_NAME12} ${profileHint}auth login --method local\`` : `\`${CLI_NAME12} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME12} ${profileHint}enable share\`)`;
17684
17702
  if (failure.kind === "caveated-session") {
17685
17703
  const holder = profileName === void 0 ? "this session" : `profile ${profileName}'s session`;
17686
17704
  return new CLIError(
17687
17705
  "PERMISSION_DENIED",
17688
- `${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\``,
17706
+ `${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): \`${CLI_NAME12} init --name publisher --key-only && ${CLI_NAME12} --profile publisher enable share\``,
17689
17707
  5
17690
17708
  );
17691
17709
  }
@@ -18019,7 +18037,7 @@ function registerShareCommand(program2) {
18019
18037
  return handleError(shareCliError(error, "notify"));
18020
18038
  }
18021
18039
  });
18022
- 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) => {
18040
+ share.command("revoke <id>").description("Revoke a bearer or addressed share this profile created").option("--ancestor", "Revoke the owner delegation ancestry").option("--json", "Print versioned JSON").action(async (id, options, command) => {
18023
18041
  try {
18024
18042
  const json = jsonOutput(options, command);
18025
18043
  if (shareServices.records === void 0) throw new CLIError("AUTH_REQUIRED", "sender history storage is not configured", 3);
@@ -18042,12 +18060,21 @@ function registerShareCommand(program2) {
18042
18060
  init_storage();
18043
18061
  init_profiles();
18044
18062
  init_constants();
18063
+ init_errors();
18045
18064
  init_session_expired();
18046
18065
  import { readFile as readFile4 } from "fs/promises";
18047
18066
  import { join as join8 } from "path";
18048
18067
  import { createHash } from "crypto";
18049
18068
  import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
18050
- import { LocationRecordValidationError, LocationRegistryHttpError, revokePolicyRootV3 } from "@tinycloud/sdk-core";
18069
+ import { withoutBearer } from "@tinycloud/operations/delegation-binding";
18070
+ import {
18071
+ LocationRecordValidationError,
18072
+ LocationRegistryHttpError,
18073
+ parseSignedCompactUcanAttenuation,
18074
+ parseSignedSiweCacao,
18075
+ revokePolicyRootV3,
18076
+ signCompactUcanAuthorization
18077
+ } from "@tinycloud/sdk-core";
18051
18078
  import { extractSiweExpiration, InvalidRestoredSessionError } from "@tinycloud/node-sdk";
18052
18079
  function requiredKvAction(meta) {
18053
18080
  if (meta === null || typeof meta !== "object" || !("requiredAction" in meta)) return void 0;
@@ -18641,9 +18668,80 @@ function createShareAuthorityAdapters(input = {}) {
18641
18668
  return "delivered";
18642
18669
  }) };
18643
18670
  const revocation = {
18644
- revokeDelegation: input.revokeDelegation ?? (async (request) => {
18645
- const result = await (await authenticatedNode()).revokeDelegation(request.delegationCid);
18646
- if (!result.ok) throw new Error("share delegation revocation was rejected");
18671
+ // The node authorizes a revocation signed by the delegation's delegator,
18672
+ // or by the owner of a space it targets. A bearer link is minted by the
18673
+ // session key, or by the owner wallet when it outlives the session, so a
18674
+ // proofless session-key revocation covers the first, and a local owner
18675
+ // key covers both. Without either key (a session-only profile that signed
18676
+ // in again since, for example) the link cannot be revoked here.
18677
+ revokeDelegation: input.revokeDelegation ?? (async ({ delegationCid, record }) => {
18678
+ const node = await authenticatedNode();
18679
+ const activeNode = await node.activeNodeIdentity();
18680
+ if (record.target.origin !== activeNode.origin) throw new Error("share delegation revocation is not bound to the active owner node");
18681
+ const linkUnavailable = new CLIError("SHARE_LINK_UNAVAILABLE", "this share's stored link could not be verified; nothing was revoked", ExitCode.NOT_FOUND);
18682
+ const resource = `${record.target.spaceId}/kv/${record.resource.path}`;
18683
+ let sessionIssued;
18684
+ try {
18685
+ const decoded = node.sharing.decodeLink(parseNativeShareUrl(record.link ?? ""));
18686
+ const linkKey = decoded.keyDid.split("#", 1)[0];
18687
+ const authorization2 = withoutBearer(decoded.delegation.authHeader ?? "");
18688
+ if (authorization2.split(".").length === 3) {
18689
+ const signed = parseSignedCompactUcanAttenuation(authorization2, delegationCid);
18690
+ if (signed.payload.aud.split("#", 1)[0] !== linkKey || signed.payload.att[resource] === void 0) throw linkUnavailable;
18691
+ sessionIssued = signed.payload.iss.split("#", 1)[0] === node.credentialHolderDid;
18692
+ } else {
18693
+ const signed = await parseSignedSiweCacao(authorization2, delegationCid);
18694
+ const owner = /^tinycloud:pkh:(eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):/.exec(record.target.spaceId)?.[1];
18695
+ if (owner === void 0 || signed.issuer.toLowerCase() !== `did:pkh:${owner}`.toLowerCase() || signed.audience !== linkKey || signed.attenuation[resource] === void 0) throw linkUnavailable;
18696
+ sessionIssued = false;
18697
+ }
18698
+ } catch {
18699
+ throw linkUnavailable;
18700
+ }
18701
+ if (delegationCid === node.session?.delegationCid) throw linkUnavailable;
18702
+ let authorization;
18703
+ if (sessionIssued) {
18704
+ const now = Math.floor(Date.now() / 1e3);
18705
+ authorization = (await signCompactUcanAuthorization({
18706
+ issuerDid: node.sessionDid,
18707
+ audienceDid: activeNode.nodeDid,
18708
+ attenuation: { [`urn:cid:${delegationCid}`]: { "tinycloud.delegation/revoke": [{}] } },
18709
+ facts: [{}],
18710
+ proofs: [],
18711
+ notBefore: now,
18712
+ expiresAt: now + 60,
18713
+ nonce: `urn:uuid:${crypto.randomUUID()}`,
18714
+ sign: (bytes) => node.signSessionBytes(bytes)
18715
+ })).authorization;
18716
+ } else if (!node.isSessionOnly) {
18717
+ authorization = await node.signOwnerRevocation(delegationCid);
18718
+ } else {
18719
+ throw new CLIError("SHARE_ISSUER_KEY_UNAVAILABLE", "the key that created this share link is not available to this profile; nothing was revoked", ExitCode.PERMISSION_DENIED);
18720
+ }
18721
+ const response = await fetchFn(`${activeNode.origin}/revoke`, { method: "POST", headers: { Authorization: authorization } });
18722
+ const body = await response.text();
18723
+ if (response.ok) {
18724
+ let receipt;
18725
+ try {
18726
+ receipt = JSON.parse(body);
18727
+ } catch {
18728
+ receipt = void 0;
18729
+ }
18730
+ if (typeof receipt === "object" && receipt !== null && "revoked" in receipt && receipt.revoked === true && "cid" in receipt && receipt.cid === delegationCid) return;
18731
+ throw new CLIError(
18732
+ "SHARE_REVOKE_UNCONFIRMED",
18733
+ `the node answered the share revocation with HTTP ${response.status} but did not confirm it; the link may already be revoked. Check whether it still opens before retrying`,
18734
+ ExitCode.ERROR,
18735
+ { status: response.status }
18736
+ );
18737
+ }
18738
+ const nodeMessage = body.replace(/[\u0000-\u001f\u007f]+/g, " ").trim().slice(0, 200);
18739
+ throw new CLIError(
18740
+ "SHARE_REVOKE_REJECTED",
18741
+ `the node rejected the share revocation (HTTP ${response.status}${nodeMessage === "" ? "" : `: ${nodeMessage}`}); nothing was revoked`,
18742
+ ExitCode.NOT_FOUND,
18743
+ { status: response.status }
18744
+ );
18647
18745
  }),
18648
18746
  revokePolicyRoot: input.revokePolicyRoot ?? (async (request) => {
18649
18747
  const node = await authenticatedNode();
@@ -18720,7 +18818,7 @@ function selectedShareProfile() {
18720
18818
  }
18721
18819
  return process.env.TC_PROFILE;
18722
18820
  }
18723
- program.name("tc").description("TinyCloud CLI \u2014 self-sovereign storage from the terminal").version(version).option("-p, --profile <name>", "Profile to use").option("-H, --host <url>", "TinyCloud node URL").option("-v, --verbose", "Enable verbose output").option("--no-cache", "Disable caching").option("-q, --quiet", "Suppress non-essential output").option("--json", "Force JSON output").option("--replication", "Enable read-through replication for this invocation").option("--no-replication", "Disable read-through replication for this invocation").option("--replication-debug", "Write replication diagnostics to stderr");
18821
+ program.name(CLI_NAME13).description("TinyCloud CLI \u2014 self-sovereign storage from the terminal").version(version).option("-p, --profile <name>", "Profile to use").option("-H, --host <url>", "TinyCloud node URL").option("-v, --verbose", "Enable verbose output").option("--no-cache", "Disable caching").option("-q, --quiet", "Suppress non-essential output").option("--json", "Force JSON output").option("--replication", "Enable read-through replication for this invocation").option("--no-replication", "Disable read-through replication for this invocation").option("--replication-debug", "Write replication diagnostics to stderr");
18724
18822
  program.hook("preAction", async (thisCommand) => {
18725
18823
  const opts = thisCommand.optsWithGlobals();
18726
18824
  setJsonOutputRequested(opts.json === true);
@@ -18730,19 +18828,20 @@ program.hook("preAction", async (thisCommand) => {
18730
18828
  emitBanner(version);
18731
18829
  }
18732
18830
  const commandName = thisCommand.name();
18733
- const fullCommand = parentName && parentName !== "tc" ? `${parentName} ${commandName}` : commandName;
18734
- const skipGuard = ["tc", "init", "doctor", "completion", "help", "upgrade", "status"].includes(commandName) || fullCommand === "profile create";
18831
+ const isRootCommand = thisCommand === program;
18832
+ const fullCommand = parentName && thisCommand.parent !== program ? `${parentName} ${commandName}` : commandName;
18833
+ const skipGuard = isRootCommand || ["init", "doctor", "completion", "help", "upgrade", "status"].includes(commandName) || fullCommand === "profile create";
18735
18834
  if (!skipGuard && !opts.quiet && !shouldOutputJson()) {
18736
18835
  try {
18737
18836
  const config = await ProfileManager.getConfig();
18738
18837
  const profileName = opts.profile || config.defaultProfile;
18739
18838
  const hasProfile = await ProfileManager.profileExists(profileName);
18740
18839
  if (!hasProfile) {
18741
- process.stderr.write(theme.warn("\u26A0 No profile configured.") + " " + theme.muted("Run: tc init") + "\n\n");
18840
+ process.stderr.write(theme.warn("\u26A0 No profile configured.") + " " + theme.muted(`Run: ${CLI_NAME13} init`) + "\n\n");
18742
18841
  } else {
18743
18842
  const key = await ProfileManager.getKey(profileName);
18744
18843
  if (!key) {
18745
- process.stderr.write(theme.warn("\u26A0 No key found.") + " " + theme.muted("Run: tc init") + "\n\n");
18844
+ process.stderr.write(theme.warn("\u26A0 No key found.") + " " + theme.muted(`Run: ${CLI_NAME13} init`) + "\n\n");
18746
18845
  }
18747
18846
  }
18748
18847
  } catch {
@@ -18789,14 +18888,14 @@ program.addHelpText("afterAll", () => {
18789
18888
  if (!process.stdout.isTTY) return "";
18790
18889
  return `
18791
18890
  ${theme.heading("Examples:")}
18792
- ${theme.command("tc init")} ${theme.muted("Set up a profile and generate keys")}
18793
- ${theme.command("tc auth login")} ${theme.muted("Authenticate via browser")}
18794
- ${theme.command('tc kv put greeting "Hello"')} ${theme.muted("Store a value")}
18795
- ${theme.command("tc kv list")} ${theme.muted("List all keys")}
18796
- ${theme.command("tc secrets network init")} ${theme.muted("Create the default secrets network")}
18797
- ${theme.command("tc account apps list")} ${theme.muted("List registered account apps")}
18798
- ${theme.command("tc delegation create --to did:pkh:...")} ${theme.muted("Grant access to another user")}
18799
- ${theme.command("tc space list")} ${theme.muted("Show your spaces")}
18891
+ ${theme.command(`${CLI_NAME13} init`)} ${theme.muted("Set up a profile and generate keys")}
18892
+ ${theme.command(`${CLI_NAME13} auth login`)} ${theme.muted("Authenticate via browser")}
18893
+ ${theme.command(`${CLI_NAME13} kv put greeting "Hello"`)} ${theme.muted("Store a value")}
18894
+ ${theme.command(`${CLI_NAME13} kv list`)} ${theme.muted("List all keys")}
18895
+ ${theme.command(`${CLI_NAME13} secrets network init`)} ${theme.muted("Create the default secrets network")}
18896
+ ${theme.command(`${CLI_NAME13} account apps list`)} ${theme.muted("List registered account apps")}
18897
+ ${theme.command(`${CLI_NAME13} delegation create --to did:pkh:...`)} ${theme.muted("Grant access to another user")}
18898
+ ${theme.command(`${CLI_NAME13} space list`)} ${theme.muted("Show your spaces")}
18800
18899
 
18801
18900
  ${theme.muted("Docs:")} ${theme.accent("https://docs.tinycloud.xyz/cli")}
18802
18901
  ${theme.muted("Repo:")} ${theme.accent("https://github.com/tinycloudlabs/web-sdk")}