@tinycloud/cli 1.5.0-beta.0 → 1.5.0-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.
@@ -16456,7 +16456,7 @@ function formatSpace(space) {
16456
16456
  init_profile_gate();
16457
16457
  init_dist();
16458
16458
  init_profiles();
16459
- import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
16459
+ import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
16460
16460
  import { get as httpGet } from "http";
16461
16461
  import { get as httpsGet } from "https";
16462
16462
  import { spawn } from "child_process";
@@ -31318,6 +31318,7 @@ init_errors();
31318
31318
  init_constants();
31319
31319
  import { NodeWasmBindings, signedCompactUcanExpiry as signedCompactUcanExpiry2 } from "@tinycloud/node-sdk";
31320
31320
  import { isLocalOwnerProfile as isLocalOwnerProfile2, ownerSessionLacksProof as ownerSessionLacksProof2 } from "@tinycloud/operations/profile";
31321
+ import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
31321
31322
 
31322
31323
  // src/lib/duration.ts
31323
31324
  function parseDuration(input) {
@@ -31353,6 +31354,87 @@ init_manifest();
31353
31354
  init_raw_encryption();
31354
31355
  init_owner_did();
31355
31356
  init_caveat_refusal();
31357
+
31358
+ // src/lib/named-keys.ts
31359
+ init_constants();
31360
+ init_errors();
31361
+ import { extractRecapAttenuations } from "@tinycloud/sdk-core";
31362
+ import {
31363
+ EncryptionResourceError,
31364
+ KEY_CREATE_ABILITY,
31365
+ formatPrefixResource,
31366
+ nodeSupportsNamedKeys
31367
+ } from "@tinycloud/sdk-services/encryption/resource";
31368
+ import { DECRYPT_ACTION } from "@tinycloud/sdk-services";
31369
+ var NAMED_KEYS_NETWORK = "default";
31370
+ function namedKeysNetworkId(ownerDid) {
31371
+ return `urn:tinycloud:encryption:${ownerDid}:${NAMED_KEYS_NETWORK}`;
31372
+ }
31373
+ function parseKeysPrefix(input, ownerDid) {
31374
+ const prefix = input.endsWith("/") ? input.slice(0, -1) : input;
31375
+ const invalid3 = (reason) => new CLIError("INVALID_KEY_PREFIX", `Invalid \`--keys\` folder "${input}": ${reason}`, ExitCode.USAGE_ERROR);
31376
+ if (prefix.length === 0) throw invalid3("the folder can't be empty.");
31377
+ if (prefix === "apps" || prefix.startsWith("apps/")) {
31378
+ throw invalid3("folders under `apps/` belong to apps and can't be used from the CLI.");
31379
+ }
31380
+ try {
31381
+ return { prefix, resource: formatPrefixResource(namedKeysNetworkId(ownerDid), prefix) };
31382
+ } catch (error) {
31383
+ if (error instanceof EncryptionResourceError) throw invalid3(error.message);
31384
+ throw error;
31385
+ }
31386
+ }
31387
+ function keyAbilitiesFor(keys) {
31388
+ return { [keys.resource]: [KEY_CREATE_ABILITY, DECRYPT_ACTION] };
31389
+ }
31390
+ function keyPathInPrefix(prefix, path) {
31391
+ return path.startsWith(`${prefix}/`) && path.length > prefix.length + 1;
31392
+ }
31393
+ async function assertNodeSupportsNamedKeys(host, fetchImpl = fetch) {
31394
+ let info = null;
31395
+ try {
31396
+ const response = await fetchImpl(`${host}/info`);
31397
+ if (response.ok) info = await response.json();
31398
+ } catch {
31399
+ info = null;
31400
+ }
31401
+ if (!nodeSupportsNamedKeys(info)) {
31402
+ throw new CLIError(
31403
+ "NAMED_KEYS_UNAVAILABLE",
31404
+ `${host} doesn't support named keys, so \`--keys\` can't be used there. Nothing was signed.`,
31405
+ ExitCode.NODE_ERROR
31406
+ );
31407
+ }
31408
+ }
31409
+ function partialKeysGrantError() {
31410
+ return new CLIError(
31411
+ "KEYS_PARTIAL_GRANT",
31412
+ "Named keys need both permissions (create and use), but only one was approved, so this sign-in wasn't saved. Sign in again and approve both.",
31413
+ ExitCode.PERMISSION_DENIED
31414
+ );
31415
+ }
31416
+ function namedKeysGrantIn(siwe, keys) {
31417
+ let recap = {};
31418
+ try {
31419
+ recap = siwe === void 0 ? {} : extractRecapAttenuations(siwe);
31420
+ } catch {
31421
+ return "none";
31422
+ }
31423
+ const row = recap[keys.resource] ?? {};
31424
+ const wanted = keyAbilitiesFor(keys)[keys.resource];
31425
+ const granted = wanted.filter((ability) => ability in row);
31426
+ if (granted.length === 0) return "none";
31427
+ return granted.length === wanted.length && Object.keys(row).length === wanted.length ? "both" : "partial";
31428
+ }
31429
+ function withVerifiedNamedKeys(profile, session) {
31430
+ if (profile.namedKeys === void 0) return profile;
31431
+ const siwe = typeof session === "object" && session !== null ? session.siwe : void 0;
31432
+ if (typeof siwe === "string" && namedKeysGrantIn(siwe, profile.namedKeys) === "both") return profile;
31433
+ const { namedKeys: _dropped, ...rest } = profile;
31434
+ return rest;
31435
+ }
31436
+
31437
+ // src/auth/scoped-login.ts
31356
31438
  var CAPABILITIES_READ = "tinycloud.capabilities/read";
31357
31439
  var CLOCK_SKEW_MS = 3e4;
31358
31440
  var SIGNED_RECAP = "signed-recap";
@@ -31605,6 +31687,35 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
31605
31687
  }
31606
31688
  return { ownerDid, permissions, expiresAt };
31607
31689
  }
31690
+ function withoutRequestedKeyRow(signed, keys, signerDid) {
31691
+ const mismatch = () => new CLIError(
31692
+ "OPENKEY_SCOPE_MISMATCH",
31693
+ `The key permission in the approval didn't match what was asked for "<folder>/", so this sign-in wasn't saved. Sign in again with \`${CLI_NAME12} auth login --keys <folder>\`.`,
31694
+ ExitCode.PERMISSION_DENIED
31695
+ );
31696
+ let expectedResource;
31697
+ try {
31698
+ expectedResource = parseKeysPrefix(keys.prefix, canonicalOwnerDid(signerDid)).resource;
31699
+ } catch {
31700
+ throw mismatch();
31701
+ }
31702
+ if (keys.resource !== expectedResource) throw mismatch();
31703
+ const rest = [];
31704
+ const actions = /* @__PURE__ */ new Set();
31705
+ for (const permission of signed) {
31706
+ if (!isVerifiedRawEncryptionPermission(permission) || permission.path !== expectedResource) {
31707
+ rest.push(permission);
31708
+ continue;
31709
+ }
31710
+ if (permission.caveats?.some((caveat) => Object.keys(caveat).length > 0)) throw mismatch();
31711
+ for (const action of permission.actions) actions.add(action);
31712
+ }
31713
+ if (actions.size === 0) return rest;
31714
+ const wanted = keyAbilitiesFor(keys)[expectedResource];
31715
+ if ([...actions].some((action) => !wanted.includes(action))) throw mismatch();
31716
+ if (actions.size !== wanted.length) throw partialKeysGrantError();
31717
+ return rest;
31718
+ }
31608
31719
  function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
31609
31720
  const notStored = expected.purpose === "grant" ? "No grant was stored." : "No scoped session was saved.";
31610
31721
  let signed;
@@ -31630,10 +31741,11 @@ function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
31630
31741
  }
31631
31742
  const legacyNested = [];
31632
31743
  const approved = [];
31633
- for (const permission of signed.permissions) {
31744
+ const keyRowRest = expected.namedKeyRow === void 0 ? signed.permissions : withoutRequestedKeyRow(signed.permissions, expected.namedKeyRow, signed.ownerDid);
31745
+ for (const permission of keyRowRest) {
31634
31746
  (isLegacyNestedDecrypt(permission, requested, signed.ownerDid, spaceId) ? legacyNested : approved).push(permission);
31635
31747
  }
31636
- for (const permission of signed.permissions) {
31748
+ for (const permission of keyRowRest) {
31637
31749
  if (isVerifiedRawEncryptionPermission(permission) && !rawEncryptionOwnerMatches(permission.path, signed.ownerDid)) {
31638
31750
  throw new CLIError("OPENKEY_SCOPE_MISMATCH", `OpenKey signed decrypt for a network not owned by the approving identity. ${notStored}`, ExitCode.PERMISSION_DENIED);
31639
31751
  }
@@ -31650,80 +31762,6 @@ function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
31650
31762
 
31651
31763
  // src/auth/login-commit.ts
31652
31764
  import { lstat as lstat6 } from "fs/promises";
31653
-
31654
- // src/lib/named-keys.ts
31655
- init_constants();
31656
- init_errors();
31657
- import { extractRecapAttenuations } from "@tinycloud/sdk-core";
31658
- import {
31659
- EncryptionResourceError,
31660
- KEY_CREATE_ABILITY,
31661
- formatPrefixResource,
31662
- nodeSupportsNamedKeys
31663
- } from "@tinycloud/sdk-services/encryption/resource";
31664
- import { DECRYPT_ACTION } from "@tinycloud/sdk-services";
31665
- var NAMED_KEYS_NETWORK = "default";
31666
- function namedKeysNetworkId(ownerDid) {
31667
- return `urn:tinycloud:encryption:${ownerDid}:${NAMED_KEYS_NETWORK}`;
31668
- }
31669
- function parseKeysPrefix(input, ownerDid) {
31670
- const prefix = input.endsWith("/") ? input.slice(0, -1) : input;
31671
- const invalid3 = (reason) => new CLIError("INVALID_KEY_PREFIX", `Invalid \`--keys\` folder "${input}": ${reason}`, ExitCode.USAGE_ERROR);
31672
- if (prefix.length === 0) throw invalid3("the folder can't be empty.");
31673
- if (prefix === "apps" || prefix.startsWith("apps/")) {
31674
- throw invalid3("folders under `apps/` belong to apps and can't be used from the CLI.");
31675
- }
31676
- try {
31677
- return { prefix, resource: formatPrefixResource(namedKeysNetworkId(ownerDid), prefix) };
31678
- } catch (error) {
31679
- if (error instanceof EncryptionResourceError) throw invalid3(error.message);
31680
- throw error;
31681
- }
31682
- }
31683
- function keyAbilitiesFor(keys) {
31684
- return { [keys.resource]: [KEY_CREATE_ABILITY, DECRYPT_ACTION] };
31685
- }
31686
- function keyPathInPrefix(prefix, path) {
31687
- return path.startsWith(`${prefix}/`) && path.length > prefix.length + 1;
31688
- }
31689
- async function assertNodeSupportsNamedKeys(host, fetchImpl = fetch) {
31690
- let info = null;
31691
- try {
31692
- const response = await fetchImpl(`${host}/info`);
31693
- if (response.ok) info = await response.json();
31694
- } catch {
31695
- info = null;
31696
- }
31697
- if (!nodeSupportsNamedKeys(info)) {
31698
- throw new CLIError(
31699
- "NAMED_KEYS_UNAVAILABLE",
31700
- `${host} doesn't support named keys, so \`--keys\` can't be used there. Nothing was signed.`,
31701
- ExitCode.NODE_ERROR
31702
- );
31703
- }
31704
- }
31705
- function namedKeysGrantIn(siwe, keys) {
31706
- let recap = {};
31707
- try {
31708
- recap = siwe === void 0 ? {} : extractRecapAttenuations(siwe);
31709
- } catch {
31710
- return "none";
31711
- }
31712
- const row = recap[keys.resource] ?? {};
31713
- const wanted = keyAbilitiesFor(keys)[keys.resource];
31714
- const granted = wanted.filter((ability) => ability in row);
31715
- if (granted.length === 0) return "none";
31716
- return granted.length === wanted.length && Object.keys(row).length === wanted.length ? "both" : "partial";
31717
- }
31718
- function withVerifiedNamedKeys(profile, session) {
31719
- if (profile.namedKeys === void 0) return profile;
31720
- const siwe = typeof session === "object" && session !== null ? session.siwe : void 0;
31721
- if (typeof siwe === "string" && namedKeysGrantIn(siwe, profile.namedKeys) === "both") return profile;
31722
- const { namedKeys: _dropped, ...rest } = profile;
31723
- return rest;
31724
- }
31725
-
31726
- // src/auth/login-commit.ts
31727
31765
  init_constants();
31728
31766
  init_profiles();
31729
31767
  init_storage();
@@ -31731,14 +31769,14 @@ init_errors();
31731
31769
  init_space();
31732
31770
  import { join as join13 } from "path";
31733
31771
  import { REPLICA_LOGOUT_PENDING_FILE as REPLICA_LOGOUT_PENDING_FILE3 } from "@tinycloud/node-sdk";
31734
- import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
31772
+ import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
31735
31773
  import { isLocalOwnerProfile as isLocalOwnerProfile3, ownerSessionLacksProof as ownerSessionLacksProof3 } from "@tinycloud/operations/profile";
31736
31774
  import { ProfileLockTimeoutError as ProfileLockTimeoutError2, profilePath as profilePath7 } from "@tinycloud/operations/state";
31737
31775
  function assertNotLocalOwner(profileName2, profile, flow) {
31738
31776
  if (profile === null || !isLocalOwnerProfile3(profile)) return;
31739
31777
  throw new CLIError(
31740
31778
  "LOCAL_OWNER_PROFILE",
31741
- `Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${CLI_NAME12} init --name publisher --key-only\`, then \`${CLI_NAME12} --profile publisher auth login --device --manifest ...\`.`,
31779
+ `Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${CLI_NAME13} init --name publisher --key-only\`, then \`${CLI_NAME13} --profile publisher auth login --device --manifest ...\`.`,
31742
31780
  ExitCode.USAGE_ERROR
31743
31781
  );
31744
31782
  }
@@ -31773,7 +31811,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
31773
31811
  if (problem !== void 0) {
31774
31812
  throw new CLIError(
31775
31813
  "PROFILE_STATE_INCONSISTENT",
31776
- `Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${CLI_NAME12} --profile ${profileName2} context\`, then pass --replace-session to replace this state, or use a new profile.`,
31814
+ `Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${CLI_NAME13} --profile ${profileName2} context\`, then pass --replace-session to replace this state, or use a new profile.`,
31777
31815
  ExitCode.ERROR
31778
31816
  );
31779
31817
  }
@@ -31789,7 +31827,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
31789
31827
  const space = typeof session.spaceId === "string" ? session.spaceId : "an unknown space";
31790
31828
  throw new CLIError(
31791
31829
  "SESSION_IN_USE",
31792
- `Profile "${profileName2}" has a live session for ${space}${expiresAt ? ` until ${expiresAt}` : ""} that this login would ${keepsScope ? "shorten" : "narrow or replace"}, dropping that authority. Nothing was saved. Keep the user's existing profiles: use a new profile name (\`${CLI_NAME12} init --name publisher --key-only\`, then \`${CLI_NAME12} --profile publisher auth login --device --manifest ...\`), or pass --replace-session to replace this session.`,
31830
+ `Profile "${profileName2}" has a live session for ${space}${expiresAt ? ` until ${expiresAt}` : ""} that this login would ${keepsScope ? "shorten" : "narrow or replace"}, dropping that authority. Nothing was saved. Keep the user's existing profiles: use a new profile name (\`${CLI_NAME13} init --name publisher --key-only\`, then \`${CLI_NAME13} --profile publisher auth login --device --manifest ...\`), or pass --replace-session to replace this session.`,
31793
31831
  ExitCode.USAGE_ERROR
31794
31832
  );
31795
31833
  }
@@ -31823,7 +31861,7 @@ async function commitLogin(profileName2, snapshot, commit) {
31823
31861
  if (canonicalJson(current.profile) !== canonicalJson(snapshot.profile) || canonicalJson(current.key) !== canonicalJson(snapshot.key) || canonicalJson(current.session) !== canonicalJson(snapshot.session)) {
31824
31862
  throw new CLIError(
31825
31863
  "PROFILE_CHANGED_DURING_LOGIN",
31826
- `Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${CLI_NAME12} --profile ${profileName2} context\` and run the login again if it is still needed.`,
31864
+ `Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${CLI_NAME13} --profile ${profileName2} context\` and run the login again if it is still needed.`,
31827
31865
  ExitCode.ERROR
31828
31866
  );
31829
31867
  }
@@ -31856,7 +31894,7 @@ async function commitLogin(profileName2, snapshot, commit) {
31856
31894
  if (failures.length === 0) throw error;
31857
31895
  throw new CLIError(
31858
31896
  "PROFILE_STATE_INCONSISTENT",
31859
- `Saving the login for profile "${profileName2}" failed (${errorMessage(error)}), and restoring its previous state failed too (${failures.map(errorMessage).join("; ")}). The profile's key, session and settings may not match. Check \`${CLI_NAME12} --profile ${profileName2} context\`, then run the login again with --replace-session, or use a new profile.`,
31897
+ `Saving the login for profile "${profileName2}" failed (${errorMessage(error)}), and restoring its previous state failed too (${failures.map(errorMessage).join("; ")}). The profile's key, session and settings may not match. Check \`${CLI_NAME13} --profile ${profileName2} context\`, then run the login again with --replace-session, or use a new profile.`,
31860
31898
  ExitCode.ERROR
31861
31899
  );
31862
31900
  }
@@ -31872,7 +31910,7 @@ async function commitLogin(profileName2, snapshot, commit) {
31872
31910
 
31873
31911
  // src/auth/owner-key.ts
31874
31912
  init_theme();
31875
- import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
31913
+ import { CLI_NAME as CLI_NAME14 } from "@tinycloud/operations/cli-name";
31876
31914
  function openKeyPrimaryFlag(delegation) {
31877
31915
  return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
31878
31916
  }
@@ -31883,7 +31921,7 @@ function withOwnerKeyPrimary(profile, primary) {
31883
31921
  function nonPrimaryKeyWarning(ownerDid) {
31884
31922
  const address = ownerDid.split(":")[4] ?? ownerDid;
31885
31923
  return `${theme.warn("Warning:")} you signed in with OpenKey key ${address} (${ownerDid}), which is not your account's primary OpenKey key. This key is a separate owner with its own spaces and data, so this profile does not see the data stored under your primary key.
31886
- To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${CLI_NAME13} init --name <profile>
31924
+ To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${CLI_NAME14} init --name <profile>
31887
31925
  `;
31888
31926
  }
31889
31927
  function warnIfNotPrimaryKey(ownerDid, primary) {
@@ -31892,7 +31930,7 @@ function warnIfNotPrimaryKey(ownerDid, primary) {
31892
31930
  }
31893
31931
  function announceFirstOwner(profileName2, expectedOwner, ownerDid) {
31894
31932
  if (expectedOwner !== void 0 || ownerDid === void 0) return;
31895
- process.stderr.write(`Profile "${profileName2}" now belongs to ${ownerDid}. If that isn't your OpenKey account, stop using this profile and create a new one with ${CLI_NAME13} init.
31933
+ process.stderr.write(`Profile "${profileName2}" now belongs to ${ownerDid}. If that isn't your OpenKey account, stop using this profile and create a new one with ${CLI_NAME14} init.
31896
31934
  `);
31897
31935
  }
31898
31936
  function ownerLoginPermissions(owner) {
@@ -32723,7 +32761,7 @@ init_types();
32723
32761
  init_constants();
32724
32762
  init_permissions();
32725
32763
  init_sdk();
32726
- import { CLI_NAME as CLI_NAME15 } from "@tinycloud/operations/cli-name";
32764
+ import { CLI_NAME as CLI_NAME16 } from "@tinycloud/operations/cli-name";
32727
32765
  import { TinyCloudNode as TinyCloudNode2 } from "@tinycloud/node-sdk";
32728
32766
  import { DelegationRequestBindingSchema, PermissionEntrySchema } from "@tinycloud/operations/artifacts";
32729
32767
  import {
@@ -32738,7 +32776,7 @@ import {
32738
32776
  // src/lib/revoke-delegation.ts
32739
32777
  init_constants();
32740
32778
  init_errors();
32741
- import { CLI_NAME as CLI_NAME14 } from "@tinycloud/operations/cli-name";
32779
+ import { CLI_NAME as CLI_NAME15 } from "@tinycloud/operations/cli-name";
32742
32780
  import { parseSignedCompactUcanAttenuation as parseSignedCompactUcanAttenuation3 } from "@tinycloud/sdk-core";
32743
32781
  import { inspectStoredDelegation, withoutBearer as withoutBearer3 } from "@tinycloud/operations/delegation-binding";
32744
32782
  init_permissions();
@@ -33002,7 +33040,7 @@ async function revokeDelegationOnNode(params) {
33002
33040
  "REVOKE_UNAUTHORIZED",
33003
33041
  "The node rejected this revocation: Unauthorized Revoker.",
33004
33042
  ExitCode.PERMISSION_DENIED,
33005
- { status: 403, hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`${CLI_NAME14} --profile <profile-name> delegation revoke ${cid}\`.` }
33043
+ { status: 403, hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`${CLI_NAME15} --profile <profile-name> delegation revoke ${cid}\`.` }
33006
33044
  );
33007
33045
  }
33008
33046
  throw cliErrorFromService(result.error);
@@ -33085,7 +33123,7 @@ function renderList(result) {
33085
33123
  }
33086
33124
  process.stdout.write(`Profile: ${result.profile}
33087
33125
  `);
33088
- process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${CLI_NAME15} delegation revoke ${result.session.cid})
33126
+ process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${CLI_NAME16} delegation revoke ${result.session.cid})
33089
33127
  ` : "\n"));
33090
33128
  process.stdout.write(`Local verification: ${result.localVerification}; node validation and revocation are not checked.
33091
33129
  `);
@@ -33183,7 +33221,7 @@ function registerAuthGrantsCommand(auth, options = {}) {
33183
33221
  ExitCode.ERROR,
33184
33222
  {
33185
33223
  causeCode: cause.code,
33186
- hint: `Do not use --revoke again. After fixing local profile storage, run: ${CLI_NAME15} --profile ${ctx.profile} auth grants remove ${cid}`
33224
+ hint: `Do not use --revoke again. After fixing local profile storage, run: ${CLI_NAME16} --profile ${ctx.profile} auth grants remove ${cid}`
33187
33225
  }
33188
33226
  );
33189
33227
  }
@@ -33248,7 +33286,7 @@ init_safe_text();
33248
33286
  init_raw_encryption();
33249
33287
  init_space();
33250
33288
  import { DEFAULT_EXPIRY as DEFAULT_EXPIRY2, parseExpiry as parseExpiry3, SERVICE_LONG_TO_SHORT as SERVICE_LONG_TO_SHORT3, VAULT_PERMISSION_SERVICE as VAULT_PERMISSION_SERVICE2 } from "@tinycloud/sdk-core";
33251
- import { CLI_NAME as CLI_NAME16 } from "@tinycloud/operations/cli-name";
33289
+ import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
33252
33290
  var APPS_AGENT_PRESET = "apps-agent";
33253
33291
  var APPS_AGENT_SPACE = "applications";
33254
33292
  var APPS_AGENT_EXPIRY_DAYS = 7;
@@ -33330,7 +33368,7 @@ function lifetimePhrase(ms2) {
33330
33368
  return `${hours} hour${hours === 1 ? "" : "s"}`;
33331
33369
  }
33332
33370
  function revokeSentence(lifetimeMs2) {
33333
- return `It lasts up to ${lifetimePhrase(lifetimeMs2)}; revoke it any time with \`${CLI_NAME16} delegation revoke <id>\`.`;
33371
+ return `It lasts up to ${lifetimePhrase(lifetimeMs2)}; revoke it any time with \`${CLI_NAME17} delegation revoke <id>\`.`;
33334
33372
  }
33335
33373
  function appsAgentScopeNotice(mode, appIds = []) {
33336
33374
  const variant = APPS_AGENT_VARIANTS[mode];
@@ -33525,7 +33563,7 @@ function describeGrantScope(requested, requestedExpiry, applicationsSpace, maxLi
33525
33563
  const durations = [lifetimeMs(requestedExpiry), ...entryLifetimes].filter((ms2) => ms2 !== void 0).map((ms2) => Math.min(ms2, maxLifetimeMs ?? Number.POSITIVE_INFINITY));
33526
33564
  const longest = Math.max(...durations);
33527
33565
  lines.push(
33528
- durations.length === 0 || !Number.isFinite(longest) ? "Its lifetime could not be read from the request." : `It lasts up to ${lifetimePhrase(longest)}${requestedExpiry === void 0 ? " (no lifetime was requested; this is the default)" : ""}; revoke it any time with \`${CLI_NAME16} delegation revoke <id>\`.`
33566
+ durations.length === 0 || !Number.isFinite(longest) ? "Its lifetime could not be read from the request." : `It lasts up to ${lifetimePhrase(longest)}${requestedExpiry === void 0 ? " (no lifetime was requested; this is the default)" : ""}; revoke it any time with \`${CLI_NAME17} delegation revoke <id>\`.`
33529
33567
  );
33530
33568
  return { text: lines.join("\n"), entries: requested, extras };
33531
33569
  }
@@ -33833,7 +33871,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
33833
33871
  "REVOCATION_FAILED",
33834
33872
  `Revocation failed for ${cid}: ${result.error.message}. Session, key, and replica files are retained for retry; local reads stay denied.`,
33835
33873
  ExitCode.ERROR,
33836
- { cid, hint: `Retry \`${CLI_NAME17} auth logout --revoke\` when the node is available.` }
33874
+ { cid, hint: `Retry \`${CLI_NAME18} auth logout --revoke\` when the node is available.` }
33837
33875
  );
33838
33876
  }
33839
33877
  if (result.data.cid !== cid || result.data.revoked !== true) {
@@ -33852,7 +33890,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
33852
33890
  "REVOCATION_FAILED",
33853
33891
  `Revocation failed before local logout: ${error instanceof Error ? error.message : String(error)}. Session, key, and replica files are retained for retry; local reads stay denied.`,
33854
33892
  ExitCode.ERROR,
33855
- { hint: `Retry \`${CLI_NAME17} auth logout --revoke\` when online authority is available.` }
33893
+ { hint: `Retry \`${CLI_NAME18} auth logout --revoke\` when online authority is available.` }
33856
33894
  );
33857
33895
  }
33858
33896
  }
@@ -34051,7 +34089,7 @@ async function finishProfileLogout(ctx, options = {}) {
34051
34089
  function registerAuthCommand(program, options) {
34052
34090
  const auth = program.command("auth").description("Authentication management");
34053
34091
  registerAuthGrantsCommand(auth, { openKeyAcquisition: options?.openKeyAcquisition });
34054
- auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${CLI_NAME17} share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Sign in as this owner (did:pkh:eip155:CHAIN:ADDRESS): OpenKey preselects that key, e.g. one that is not the account's primary key, and an approval by any other identity is refused. With --manifest, also names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").option("--force", "Replica-enabled OpenKey login: replace the stored session key even when recorded shares could not be checked or hold unexpired shares that need the current key").option("--keys <folder>", "Also let this sign-in create and use encryption keys in this folder (for example `qa`). The node must support named keys. With OpenKey, the first time on a profile also needs `--owner`.").option("--replication-prefix <prefix>", "Add replication get+sync authority for a folder prefix ending in /, or root (repeatable)", (prefix, values = []) => [...values, prefix]).option("--replication-allow-secrets", "Allow replication of secret material (network-wide decrypt warning)").action(async (options2, cmd) => {
34092
+ auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${CLI_NAME18} share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Sign in as this owner (did:pkh:eip155:CHAIN:ADDRESS): OpenKey preselects that key, e.g. one that is not the account's primary key, and an approval by any other identity is refused. With --manifest, also names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").option("--force", "Replica-enabled OpenKey login: replace the stored session key even when recorded shares could not be checked or hold unexpired shares that need the current key").option("--keys <folder>", "Also let this sign-in create and use encryption keys in this folder (for example `qa`). The node must support named keys. With OpenKey, the first time on a profile also needs `--owner`.").option("--replication-prefix <prefix>", "Add replication get+sync authority for a folder prefix ending in /, or root (repeatable)", (prefix, values = []) => [...values, prefix]).option("--replication-allow-secrets", "Allow replication of secret material (network-wide decrypt warning)").action(async (options2, cmd) => {
34055
34093
  try {
34056
34094
  if (options2.device && options2.paste) {
34057
34095
  throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
@@ -34209,7 +34247,7 @@ function registerAuthCommand(program, options) {
34209
34247
  authenticated: false,
34210
34248
  replicasRemoved,
34211
34249
  replicasKept: options2.keepReplicas === true,
34212
- warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME17} delegation revoke <cid>\` to end access now.`,
34250
+ warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME18} delegation revoke <cid>\` to end access now.`,
34213
34251
  ...online === void 0 ? {} : {
34214
34252
  revokedCids: online.plan.cids,
34215
34253
  revocationExcluded: online.plan.excluded
@@ -34354,7 +34392,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
34354
34392
  if (profile.authMethod === "openkey" || options2.device) {
34355
34393
  const key = await ProfileManager.getKey(ctx.profile);
34356
34394
  if (!key) {
34357
- throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${CLI_NAME17} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
34395
+ throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${CLI_NAME18} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
34358
34396
  }
34359
34397
  const openkeyHost = resolveOpenKeyHost(profile);
34360
34398
  const grants = [];
@@ -34374,7 +34412,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
34374
34412
  if (notices) process.stderr.write(`${notices.prompts[index]}
34375
34413
  `);
34376
34414
  const reason = permissionGrantReason(
34377
- `Grant requested TinyCloud permissions from \`${CLI_NAME17} auth request --grant\`.`,
34415
+ `Grant requested TinyCloud permissions from \`${CLI_NAME18} auth request --grant\`.`,
34378
34416
  group
34379
34417
  );
34380
34418
  const request = grantRequestPermissions(group, profile.spaceId ?? profile.spaceName);
@@ -34484,7 +34522,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
34484
34522
  kind: parsed.kind,
34485
34523
  requestId: parsed.requestId,
34486
34524
  requested: parsed.requested,
34487
- next: `${CLI_NAME17} auth retry ${parsed.requestId}`
34525
+ next: `${CLI_NAME18} auth retry ${parsed.requestId}`
34488
34526
  });
34489
34527
  return;
34490
34528
  }
@@ -35093,7 +35131,7 @@ async function acquireOpenKeyApproval(params) {
35093
35131
  if (!key) {
35094
35132
  throw new CLIError(
35095
35133
  "NO_KEY",
35096
- `No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME17} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
35134
+ `No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME18} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
35097
35135
  ExitCode.AUTH_REQUIRED
35098
35136
  );
35099
35137
  }
@@ -35166,7 +35204,7 @@ async function ensureDelegationAuthority(params) {
35166
35204
  if (!key) {
35167
35205
  throw new CLIError(
35168
35206
  "NO_KEY",
35169
- `No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME17} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
35207
+ `No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME18} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
35170
35208
  ExitCode.AUTH_REQUIRED
35171
35209
  );
35172
35210
  }
@@ -35420,7 +35458,7 @@ async function rotateAuthKey(profileName2, host, options = {}) {
35420
35458
  if (!profile.privateKey) {
35421
35459
  throw new CLIError(
35422
35460
  "LOCAL_OWNER_KEY_REQUIRED",
35423
- `Profile "${profileName2}" does not have a local owner private key. Run \`${CLI_NAME17} auth login --method local\` first.`,
35461
+ `Profile "${profileName2}" does not have a local owner private key. Run \`${CLI_NAME18} auth login --method local\` first.`,
35424
35462
  ExitCode.AUTH_REQUIRED
35425
35463
  );
35426
35464
  }
@@ -35678,6 +35716,8 @@ async function refreshPreparedOpenKeySession(params) {
35678
35716
  if (expectedOwner && normalizePkhIdentifier(owner) !== normalizePkhIdentifier(expectedOwner)) {
35679
35717
  throw new CLIError("OPENKEY_OWNER_MISMATCH", "The verified owner differs from this profile. No approval was started.", ExitCode.PERMISSION_DENIED);
35680
35718
  }
35719
+ const namedKeys = options.keys === void 0 ? void 0 : parseKeysPrefix(options.keys, canonicalOwnerDid(owner));
35720
+ if (namedKeys !== void 0) await assertNodeSupportsNamedKeys(host);
35681
35721
  if (options.force !== true) await assertShareHistoryAllowsKeyDrop(profileName2, "login");
35682
35722
  const replicationConfig = createCliReplicationConfig(profileName2, replication, { debug: false, quiet: true }, true);
35683
35723
  if (!replicationConfig) throw new CLIError("V2_REQUIRED", "Replica configuration is unavailable; no approval was started.", ExitCode.AUTH_REQUIRED);
@@ -35698,12 +35738,15 @@ async function refreshPreparedOpenKeySession(params) {
35698
35738
  capabilityRequest,
35699
35739
  includeAccountRegistryPermissions: false,
35700
35740
  autoBootstrapAccount: false,
35701
- replication: replicationConfig
35741
+ replication: replicationConfig,
35742
+ ...namedKeys === void 0 ? {} : { additionalRawAbilities: keyAbilitiesFor(namedKeys) }
35702
35743
  });
35703
35744
  let handoff;
35704
35745
  let preparedDid;
35746
+ let handedBackSiwe;
35705
35747
  const gateWasActive = await readGateSnapshot(profilePath8(profileName2)).then((gate) => gate !== void 0 && gate.principalHash !== null, () => true);
35706
35748
  try {
35749
+ if (namedKeys !== void 0) process.stderr.write(KEYS_APPROVAL_NOTE(namedKeys.prefix));
35707
35750
  await node.signInWithOpenKey(async (prepared) => {
35708
35751
  preparedDid = prepared.verificationMethod;
35709
35752
  handoff = await startPreparedOpenKeyHandoff({
@@ -35712,14 +35755,23 @@ async function refreshPreparedOpenKeySession(params) {
35712
35755
  noPopup: options.noPopup,
35713
35756
  paste: options.paste
35714
35757
  });
35715
- return handoff.result;
35758
+ return handoff.result.then((result) => {
35759
+ handedBackSiwe = result.signedMessage;
35760
+ return result;
35761
+ });
35716
35762
  });
35717
35763
  const active = node.restorableSession;
35718
35764
  if (!active || !preparedDid || node.sessionDid !== preparedDid || active.verificationMethod !== preparedDid) {
35719
35765
  throw new CLIError("OPENKEY_SESSION_MISMATCH", "OpenKey signed a different SDK session key; no profile or session was saved.", ExitCode.PERMISSION_DENIED);
35720
35766
  }
35721
35767
  const key = active.jwk;
35722
- const verified = verifyScopedLogin({ ...active }, key, preparedDid, permissions, { expectedOwner: owner, expiry });
35768
+ const verified = verifyScopedLogin({ ...active }, key, preparedDid, permissions, {
35769
+ expectedOwner: owner,
35770
+ expiry,
35771
+ ...namedKeys === void 0 ? {} : { namedKeyRow: namedKeys }
35772
+ });
35773
+ const grant = namedKeys === void 0 ? "none" : namedKeysGrantIn(active.siwe, namedKeys);
35774
+ if (grant === "partial") throw partialKeysGrantError();
35723
35775
  const session = {
35724
35776
  ...verified.session,
35725
35777
  ...node.replicaStamp ? { replicaStamp: node.replicaStamp } : {},
@@ -35740,6 +35792,10 @@ async function refreshPreparedOpenKeySession(params) {
35740
35792
  ...replication.allowSecrets === true ? { allowSecrets: true } : {}
35741
35793
  }
35742
35794
  };
35795
+ if (namedKeys !== void 0) {
35796
+ delete updatedProfile.namedKeys;
35797
+ if (grant === "both") updatedProfile.namedKeys = namedKeys;
35798
+ }
35743
35799
  await commitLogin(profileName2, snapshot, {
35744
35800
  key,
35745
35801
  session,
@@ -35749,6 +35805,10 @@ async function refreshPreparedOpenKeySession(params) {
35749
35805
  });
35750
35806
  await handoff?.complete(true);
35751
35807
  announceFirstOwner(profileName2, expectedOwner, owner);
35808
+ if (namedKeys !== void 0) {
35809
+ if (grant === "both") await readyNamedKeysNetwork(node);
35810
+ else process.stderr.write(keysNotGrantedAfterLogin(profileName2));
35811
+ }
35752
35812
  return {
35753
35813
  profile: updatedProfile,
35754
35814
  delegationData: session,
@@ -35758,14 +35818,14 @@ async function refreshPreparedOpenKeySession(params) {
35758
35818
  } catch (error) {
35759
35819
  await handoff?.complete(false);
35760
35820
  await undoPreparedAttempt(profileName2, node, owner, gateWasActive).catch(() => void 0);
35761
- throw error;
35821
+ throw namedKeys !== void 0 && isNarrowedRecapMismatch(error) && namedKeysGrantIn(handedBackSiwe, namedKeys) === "partial" ? partialKeysGrantError() : error;
35762
35822
  }
35763
35823
  }
35764
35824
  var KEYS_APPROVAL_NOTE = (folder) => `On the OpenKey page, the row that mentions tinycloud.encryption lets this profile create and use keys in the folder "${folder}/". Keep every row ticked. To sign in without keys, untick that whole row and nothing else; any other change makes OpenKey refuse the sign-in.
35765
35825
  `;
35766
- var keysNotGrantedAfterLogin = (profileName2) => `You're signed in, but without named keys: the tinycloud.encryption permission wasn't approved. To add them, sign in again with \`${CLI_NAME17} --profile ${profileName2} auth login --keys <folder>\` and leave the row that mentions tinycloud.encryption ticked.
35826
+ var keysNotGrantedAfterLogin = (profileName2) => `You're signed in, but without named keys: the tinycloud.encryption permission wasn't approved. To add them, sign in again with \`${CLI_NAME18} --profile ${profileName2} auth login --keys <folder>\` and leave the row that mentions tinycloud.encryption ticked.
35767
35827
  `;
35768
- var KEYS_FOLDER_NOT_READY = `You're signed in, but your encryption keys aren't set up yet. Try \`${CLI_NAME17} keys ensure <folder>/<name>\` again in a minute.
35828
+ var KEYS_FOLDER_NOT_READY = `You're signed in, but your encryption keys aren't set up yet. Try \`${CLI_NAME18} keys ensure <folder>/<name>\` again in a minute.
35769
35829
  `;
35770
35830
  async function readyNamedKeysNetwork(node) {
35771
35831
  try {
@@ -35792,7 +35852,7 @@ async function refreshPreparedKeysSession(params) {
35792
35852
  if (expectedOwner && normalizePkhIdentifier(owner) !== normalizePkhIdentifier(expectedOwner)) {
35793
35853
  throw new CLIError("OPENKEY_OWNER_MISMATCH", "The verified owner differs from this profile. No approval was started.", ExitCode.PERMISSION_DENIED);
35794
35854
  }
35795
- const namedKeys = parseKeysPrefix(options.keys, owner);
35855
+ const namedKeys = parseKeysPrefix(options.keys, canonicalOwnerDid(owner));
35796
35856
  await assertNodeSupportsNamedKeys(host);
35797
35857
  if (options.force !== true) await assertShareHistoryAllowsKeyDrop(profileName2, "login");
35798
35858
  const node = new TinyCloudNode3({
@@ -35805,6 +35865,7 @@ async function refreshPreparedKeysSession(params) {
35805
35865
  });
35806
35866
  let handoff;
35807
35867
  let preparedDid;
35868
+ let handedBackSiwe;
35808
35869
  try {
35809
35870
  process.stderr.write(KEYS_APPROVAL_NOTE(namedKeys.prefix));
35810
35871
  await node.signInWithOpenKey(async (prepared) => {
@@ -35815,7 +35876,10 @@ async function refreshPreparedKeysSession(params) {
35815
35876
  noPopup: options.noPopup,
35816
35877
  paste: options.paste
35817
35878
  });
35818
- return handoff.result;
35879
+ return handoff.result.then((result) => {
35880
+ handedBackSiwe = result.signedMessage;
35881
+ return result;
35882
+ });
35819
35883
  });
35820
35884
  const active = node.restorableSession;
35821
35885
  if (!active || !preparedDid || node.sessionDid !== preparedDid || active.verificationMethod !== preparedDid) {
@@ -35849,16 +35913,9 @@ async function refreshPreparedKeysSession(params) {
35849
35913
  } catch (error) {
35850
35914
  await handoff?.complete(false);
35851
35915
  await node.replication?.close();
35852
- throw isNarrowedRecapMismatch(error) ? partialKeysGrantError() : error;
35916
+ throw isNarrowedRecapMismatch(error) && namedKeysGrantIn(handedBackSiwe, namedKeys) === "partial" ? partialKeysGrantError() : error;
35853
35917
  }
35854
35918
  }
35855
- function partialKeysGrantError() {
35856
- return new CLIError(
35857
- "KEYS_PARTIAL_GRANT",
35858
- "Named keys need both permissions (create and use), but only one was approved, so this sign-in wasn't saved. Sign in again and approve both.",
35859
- ExitCode.PERMISSION_DENIED
35860
- );
35861
- }
35862
35919
  async function undoPreparedAttempt(profileName2, node, owner, gateWasActive) {
35863
35920
  if (!gateWasActive) {
35864
35921
  const undone = await ProfileManager.withLock(profileName2, async () => {
@@ -35897,14 +35954,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
35897
35954
  const estimatedExpiry = expiry === void 0 ? void 0 : new Date(Date.now() + expiry.durationMs).toISOString();
35898
35955
  assertSessionReplaceable(profileName2, snapshot, expectedOwner, permissions, estimatedExpiry);
35899
35956
  }
35900
- if (options.keys !== void 0) {
35901
- if (replication.prefixes.length > 0) {
35902
- throw new CLIError(
35903
- "INVALID_ARGUMENT",
35904
- `With OpenKey sign-in, \`--keys\` can't be used on a profile that has replication prefixes (given with \`--replication-prefix\` or saved from an earlier sign-in). Nothing was started. Use a separate profile for named keys: \`${CLI_NAME17} --profile <name> auth login --owner did:pkh:eip155:<chain>:<address> --keys <folder>\`.`,
35905
- ExitCode.USAGE_ERROR
35906
- );
35907
- }
35957
+ if (options.keys !== void 0 && replication.prefixes.length === 0) {
35908
35958
  return refreshPreparedKeysSession({ profileName: profileName2, host, profile, snapshot, options, expectedOwner, expiry });
35909
35959
  }
35910
35960
  if (replication.prefixes.length > 0) {
@@ -35982,7 +36032,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
35982
36032
  }
35983
36033
 
35984
36034
  // src/commands/completion.ts
35985
- import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
36035
+ import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
35986
36036
  function registerCompletionCommand(program) {
35987
36037
  const completion = program.command("completion").description("Generate shell completions");
35988
36038
  completion.command("bash").description("Output bash completions").action(() => {
@@ -35999,7 +36049,7 @@ function registerCompletionCommand(program) {
35999
36049
  });
36000
36050
  }
36001
36051
  function generateBashCompletion() {
36002
- return `# ${CLI_NAME18} bash completion
36052
+ return `# ${CLI_NAME19} bash completion
36003
36053
  _tc_completions() {
36004
36054
  local cur prev commands subcommands
36005
36055
  COMPREPLY=()
@@ -36024,11 +36074,11 @@ _tc_completions() {
36024
36074
  COMPREPLY=( $(compgen -W "\${subcommands}" -- "\${cur}") )
36025
36075
  fi
36026
36076
  }
36027
- complete -F _tc_completions ${CLI_NAME18}
36077
+ complete -F _tc_completions ${CLI_NAME19}
36028
36078
  `;
36029
36079
  }
36030
36080
  function generateZshCompletion() {
36031
- return `#compdef ${CLI_NAME18}
36081
+ return `#compdef ${CLI_NAME19}
36032
36082
 
36033
36083
  _tc() {
36034
36084
  local -a commands
@@ -36079,50 +36129,50 @@ _tc
36079
36129
  `;
36080
36130
  }
36081
36131
  function generateFishCompletion() {
36082
- return `# ${CLI_NAME18} fish completion
36132
+ return `# ${CLI_NAME19} fish completion
36083
36133
  set -l commands init auth kv space delegation share node profile completion
36084
36134
 
36085
36135
  # Disable file completion by default
36086
- complete -c ${CLI_NAME18} -f
36136
+ complete -c ${CLI_NAME19} -f
36087
36137
 
36088
36138
  # Top-level commands
36089
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize a new TinyCloud profile"
36090
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a auth -d "Authentication management"
36091
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a kv -d "Key-value store operations"
36092
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a space -d "Space management"
36093
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a delegation -d "Manage delegations"
36094
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a share -d "Share data with others"
36095
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a node -d "Node health and info"
36096
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a profile -d "Profile management"
36097
- complete -c ${CLI_NAME18} -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
36139
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize a new TinyCloud profile"
36140
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a auth -d "Authentication management"
36141
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a kv -d "Key-value store operations"
36142
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a space -d "Space management"
36143
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a delegation -d "Manage delegations"
36144
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a share -d "Share data with others"
36145
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a node -d "Node health and info"
36146
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a profile -d "Profile management"
36147
+ complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
36098
36148
 
36099
36149
  # Subcommands
36100
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
36101
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
36102
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from space" -a "list create info switch"
36103
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
36104
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from share" -a "create receive list revoke"
36105
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from node" -a "health version status"
36106
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from profile" -a "list create show switch delete"
36107
- complete -c ${CLI_NAME18} -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
36150
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
36151
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
36152
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from space" -a "list create info switch"
36153
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
36154
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from share" -a "create receive list revoke"
36155
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from node" -a "health version status"
36156
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from profile" -a "list create show switch delete"
36157
+ complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
36108
36158
 
36109
36159
  # Global options
36110
- complete -c ${CLI_NAME18} -l profile -s p -d "Profile to use"
36111
- complete -c ${CLI_NAME18} -l host -s H -d "TinyCloud node URL"
36112
- complete -c ${CLI_NAME18} -l verbose -s v -d "Enable verbose output"
36113
- complete -c ${CLI_NAME18} -l no-cache -d "Disable caching"
36114
- complete -c ${CLI_NAME18} -l quiet -s q -d "Suppress non-essential output"
36115
- complete -c ${CLI_NAME18} -l replication -d "Enable read-through replication"
36116
- complete -c ${CLI_NAME18} -l no-replication -d "Disable replication"
36117
- complete -c ${CLI_NAME18} -l replication-debug -d "Write replication diagnostics to stderr"
36160
+ complete -c ${CLI_NAME19} -l profile -s p -d "Profile to use"
36161
+ complete -c ${CLI_NAME19} -l host -s H -d "TinyCloud node URL"
36162
+ complete -c ${CLI_NAME19} -l verbose -s v -d "Enable verbose output"
36163
+ complete -c ${CLI_NAME19} -l no-cache -d "Disable caching"
36164
+ complete -c ${CLI_NAME19} -l quiet -s q -d "Suppress non-essential output"
36165
+ complete -c ${CLI_NAME19} -l replication -d "Enable read-through replication"
36166
+ complete -c ${CLI_NAME19} -l no-replication -d "Disable replication"
36167
+ complete -c ${CLI_NAME19} -l replication-debug -d "Write replication diagnostics to stderr"
36118
36168
  `;
36119
36169
  }
36120
36170
 
36121
36171
  // src/commands/courier.ts
36122
- import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
36123
- var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME19} courier takes no ${CLI_NAME19} options and does not use ${CLI_NAME19} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME19} courier init --host <url>.`;
36172
+ import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
36173
+ var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME20} courier takes no ${CLI_NAME20} options and does not use ${CLI_NAME20} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME20} courier init --host <url>.`;
36124
36174
  function registerCourierCommand(program) {
36125
- const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${CLI_NAME19} courier --help\` (beta)`).argument("[args...]").allowUnknownOption().helpOption(false);
36175
+ const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${CLI_NAME20} courier --help\` (beta)`).argument("[args...]").allowUnknownOption().helpOption(false);
36126
36176
  courier.action(() => courier.error(TC_OPTIONS_BEFORE_COURIER, { exitCode: 2, code: "tc.courier.options" }));
36127
36177
  }
36128
36178
 
@@ -36167,7 +36217,7 @@ init_formatter();
36167
36217
  init_errors();
36168
36218
  init_constants();
36169
36219
  init_sdk();
36170
- import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
36220
+ import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
36171
36221
  import { PermissionNotInManifestError } from "@tinycloud/node-sdk";
36172
36222
  async function queryDelegations(node, options, until) {
36173
36223
  const found = [];
@@ -36178,7 +36228,7 @@ async function queryDelegations(node, options, until) {
36178
36228
  if (page.error.code === "PERMISSION_DENIED") {
36179
36229
  throw new CLIError(
36180
36230
  "PERMISSION_DENIED",
36181
- `This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${CLI_NAME20} auth login\` again to grant it.`,
36231
+ `This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${CLI_NAME21} auth login\` again to grant it.`,
36182
36232
  ExitCode.PERMISSION_DENIED
36183
36233
  );
36184
36234
  }
@@ -36353,7 +36403,7 @@ init_constants();
36353
36403
  init_formatter();
36354
36404
  init_theme();
36355
36405
  init_errors();
36356
- import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
36406
+ import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
36357
36407
  init_session_expired();
36358
36408
  init_types();
36359
36409
  function registerDoctorCommand(program) {
@@ -36387,7 +36437,7 @@ function registerDoctorCommand(program) {
36387
36437
  const profile = await ProfileManager.getProfile(profileName2);
36388
36438
  keyDetail = profile.did ? `${profile.did.slice(0, 20)}...` : "key found";
36389
36439
  } else {
36390
- keyDetail = `no key \u2014 run ${CLI_NAME21} init`;
36440
+ keyDetail = `no key \u2014 run ${CLI_NAME22} init`;
36391
36441
  }
36392
36442
  } catch {
36393
36443
  keyDetail = "error reading key";
@@ -36409,7 +36459,7 @@ function registerDoctorCommand(program) {
36409
36459
  } else if (state2 === "unverified") {
36410
36460
  sessionDetail = `not usable (no signed OpenKey proof) \u2014 ${signInAgainHint(profileName2, resolveProfilePosture(profile))}`;
36411
36461
  } else {
36412
- sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME21} auth login`;
36462
+ sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME22} auth login`;
36413
36463
  }
36414
36464
  } catch {
36415
36465
  sessionDetail = "error reading session";
@@ -36437,7 +36487,7 @@ function registerDoctorCommand(program) {
36437
36487
  try {
36438
36488
  const profile = await ProfileManager.getProfile(profileName2);
36439
36489
  spaceOk = Boolean(profile.spaceId);
36440
- spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME21} space create`;
36490
+ spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME22} space create`;
36441
36491
  } catch {
36442
36492
  spaceDetail = "error checking space";
36443
36493
  }
@@ -36627,7 +36677,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
36627
36677
  }
36628
36678
 
36629
36679
  // src/commands/enable.ts
36630
- import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
36680
+ import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
36631
36681
  init_constants();
36632
36682
  init_profiles();
36633
36683
  init_errors();
@@ -36635,7 +36685,7 @@ init_formatter();
36635
36685
  init_publishing_manifest();
36636
36686
  function registerEnableCommand(program) {
36637
36687
  const enable = program.command("enable").description("Enable a narrowly scoped TinyCloud service");
36638
- enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME22} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME22} init --name publisher --key-only && ${CLI_NAME22} --profile publisher enable share\`; keep existing profiles for their apps.`).option("--replace-session", "Replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, command) => {
36688
+ enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME23} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME23} init --name publisher --key-only && ${CLI_NAME23} --profile publisher enable share\`; keep existing profiles for their apps.`).option("--replace-session", "Replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, command) => {
36639
36689
  try {
36640
36690
  const globalOptions = command.optsWithGlobals();
36641
36691
  const context = await ProfileManager.resolveContext(globalOptions, { storedSession: "unused" });
@@ -36670,7 +36720,7 @@ function registerEnableCommand(program) {
36670
36720
  init_profiles();
36671
36721
  init_constants();
36672
36722
  init_sdk();
36673
- import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
36723
+ import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
36674
36724
  import { operationSpaceResolver as operationSpaceResolver2 } from "@tinycloud/operations/delegation-binding";
36675
36725
 
36676
36726
  // src/lib/knowledge-input.ts
@@ -36936,7 +36986,7 @@ function registerAppsCommand(program) {
36936
36986
  "after",
36937
36987
  `
36938
36988
  Examples:
36939
- $ ${CLI_NAME23} apps install xyz.tinycloud.sample.healthlog
36989
+ $ ${CLI_NAME24} apps install xyz.tinycloud.sample.healthlog
36940
36990
  `
36941
36991
  ).action(async (appIdInput, _options, cmd) => {
36942
36992
  try {
@@ -37028,7 +37078,7 @@ init_profiles();
37028
37078
  init_formatter();
37029
37079
  init_errors();
37030
37080
  init_constants();
37031
- import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
37081
+ import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
37032
37082
  function registerInitCommand(program) {
37033
37083
  program.command("init").description("Initialize a new TinyCloud profile").option("--name <profile>", "Profile name", "default").option("--key-only", "Only generate key, skip authentication").option("--host <url>", "TinyCloud node URL").option("--paste", "Use manual paste mode for authentication").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--default-space <name>", "Default space used when --space is omitted (e.g. applications)").action(async (options, cmd) => {
37034
37084
  try {
@@ -37046,7 +37096,7 @@ function registerInitCommand(program) {
37046
37096
  if (await ProfileManager.profileExists(profileName2)) {
37047
37097
  throw new CLIError(
37048
37098
  "PROFILE_EXISTS",
37049
- `Profile "${profileName2}" already exists. Use \`${CLI_NAME24} profile delete ${profileName2}\` first or choose a different name.`,
37099
+ `Profile "${profileName2}" already exists. Use \`${CLI_NAME25} profile delete ${profileName2}\` first or choose a different name.`,
37050
37100
  ExitCode.ERROR
37051
37101
  );
37052
37102
  }
@@ -37102,7 +37152,7 @@ init_session_expired();
37102
37152
  init_constants();
37103
37153
  init_profiles();
37104
37154
  import { invokeOperation as invokeOperation2 } from "@tinycloud/operations";
37105
- import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
37155
+ import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
37106
37156
  init_permissions();
37107
37157
  init_errors();
37108
37158
  init_formatter();
@@ -37205,7 +37255,7 @@ function knowledgeFailure(result, profile, appId) {
37205
37255
  case "error": {
37206
37256
  if (result.error.code === "SESSION_NOT_FOUND" || result.error.code === "PROFILE_POSTURE_NOT_ALLOWED" && result.context.posture === "unauthenticated") {
37207
37257
  return new CLIError("AUTH_REQUIRED", "Not signed in to TinyCloud.", ExitCode.AUTH_REQUIRED, {
37208
- hint: `Sign in with: ${CLI_NAME25} --profile ${result.context.profile} auth login`
37258
+ hint: `Sign in with: ${CLI_NAME26} --profile ${result.context.profile} auth login`
37209
37259
  });
37210
37260
  }
37211
37261
  if (result.error.code === "NODE_UNREACHABLE") {
@@ -37235,7 +37285,7 @@ function knowledgeFailure(result, profile, appId) {
37235
37285
  }
37236
37286
  }
37237
37287
  function missingGrantHint(profile, appId) {
37238
- const request = `${CLI_NAME25} --profile ${profile} auth request`;
37288
+ const request = `${CLI_NAME26} --profile ${profile} auth request`;
37239
37289
  const lines = appId === void 0 ? [
37240
37290
  ` ${request} --preset ${APPS_AGENT_PRESET} --emit req.json (read-only, all installed apps)`,
37241
37291
  ` ${request} --cap tinycloud.kv:${APPS_AGENT_SPACE}::get,list --emit req.json (all files, no table access)`,
@@ -37248,7 +37298,7 @@ function missingGrantHint(profile, appId) {
37248
37298
  return [
37249
37299
  "Ask the owner for a grant, narrowest first. Create the request:",
37250
37300
  ...lines,
37251
- `The owner runs \`${CLI_NAME25} auth grant req.json > grant.json\`; then import the result with \`${CLI_NAME25} --profile ${profile} auth import grant.json\`.`
37301
+ `The owner runs \`${CLI_NAME26} auth grant req.json > grant.json\`; then import the result with \`${CLI_NAME26} --profile ${profile} auth import grant.json\`.`
37252
37302
  ].join("\n");
37253
37303
  }
37254
37304
 
@@ -37397,9 +37447,12 @@ function registerKvCommand(program) {
37397
37447
  return handleError(error);
37398
37448
  }
37399
37449
  });
37400
- kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
37450
+ kv.command("put <key> [value]").description("Set a value").option("--file <path>", "Read value from file").option("--stdin", "Read value from stdin").option("--if-none-match <value>", "`--if-none-match '*'`: Write only if the key doesn't exist yet; never overwrite").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, value, options, cmd) => {
37401
37451
  try {
37402
37452
  assertAddressableKey(key);
37453
+ if (options.ifNoneMatch !== void 0 && options.ifNoneMatch !== "*") {
37454
+ throw new CLIError("USAGE_ERROR", "`--if-none-match` only accepts `'*'`.", ExitCode.USAGE_ERROR);
37455
+ }
37403
37456
  const globalOpts = cmd.optsWithGlobals();
37404
37457
  const ctx = await ProfileManager.resolveContext(globalOpts);
37405
37458
  const node = await ensureAuthenticated(ctx);
@@ -37423,8 +37476,15 @@ function registerKvCommand(program) {
37423
37476
  }
37424
37477
  }
37425
37478
  const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
37426
- const result = await withSpinner(`Writing ${key}...`, () => kv2.put(key, putValue));
37479
+ const result = await withSpinner(`Writing ${key}...`, () => kv2.put(
37480
+ key,
37481
+ putValue,
37482
+ options.ifNoneMatch === "*" ? { ifNoneMatch: "*" } : void 0
37483
+ ));
37427
37484
  if (!result.ok) {
37485
+ if (options.ifNoneMatch === "*" && result.error.code === "KV_PRECONDITION_FAILED") {
37486
+ throw new CLIError("KV_ALREADY_EXISTS", `Key "${key}" already exists, so nothing was written.`, ExitCode.PERMISSION_DENIED);
37487
+ }
37428
37488
  await throwKvError(result.error, spaceUri, ctx.profile);
37429
37489
  }
37430
37490
  outputJson({ key, written: true, ...retryFields(result.data?.retry) });
@@ -37555,7 +37615,7 @@ init_errors();
37555
37615
  init_sdk();
37556
37616
  import { readFile as readFile12 } from "fs/promises";
37557
37617
  import { NamedKeyError, createNamedKeyClient } from "@tinycloud/node-sdk/encryption-keys";
37558
- import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
37618
+ import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
37559
37619
  async function readStdin3() {
37560
37620
  const chunks = [];
37561
37621
  for await (const chunk of process.stdin) {
@@ -37579,7 +37639,7 @@ function cliErrorFromNamedKey(error, command, path) {
37579
37639
  case "KEY_NOT_FOUND":
37580
37640
  return new CLIError(
37581
37641
  "NOT_FOUND",
37582
- command === "decrypt" ? "The key this envelope needs doesn't exist on this node, so it can't be decrypted here." : `There's no key at ${path} yet. Create it with \`${CLI_NAME26} keys ensure ${path}\`.`,
37642
+ command === "decrypt" ? "The key this envelope needs doesn't exist on this node, so it can't be decrypted here." : `There's no key at ${path} yet. Create it with \`${CLI_NAME27} keys ensure ${path}\`.`,
37583
37643
  ExitCode.NOT_FOUND
37584
37644
  );
37585
37645
  case "KEY_BLOCKED":
@@ -37589,7 +37649,7 @@ function cliErrorFromNamedKey(error, command, path) {
37589
37649
  }
37590
37650
  if (error.status === 401 || error.status === 403) {
37591
37651
  return new CLIError("PERMISSION_DENIED", error.message, ExitCode.PERMISSION_DENIED, {
37592
- hint: `Sign in with \`${CLI_NAME26} auth login --keys <folder>\` for this key's folder.`
37652
+ hint: `Sign in with \`${CLI_NAME27} auth login --keys <folder>\` for this key's folder.`
37593
37653
  });
37594
37654
  }
37595
37655
  return new CLIError(error.code, error.message, error.status === void 0 ? ExitCode.ERROR : ExitCode.NODE_ERROR);
@@ -37609,7 +37669,7 @@ async function grantedPrefix(deps, profileName2) {
37609
37669
  if (keys === void 0) {
37610
37670
  throw new CLIError(
37611
37671
  "KEYS_NOT_GRANTED",
37612
- `Profile "${profileName2}" can't use named keys yet. Sign in with \`${CLI_NAME26} --profile ${profileName2} auth login --keys <folder>\`.`,
37672
+ `Profile "${profileName2}" can't use named keys yet. Sign in with \`${CLI_NAME27} --profile ${profileName2} auth login --keys <folder>\`.`,
37613
37673
  ExitCode.PERMISSION_DENIED
37614
37674
  );
37615
37675
  }
@@ -37619,7 +37679,7 @@ function assertInGrantedPrefix(prefix, path, profileName2) {
37619
37679
  if (!keyPathInPrefix(prefix, path)) {
37620
37680
  throw new CLIError(
37621
37681
  "KEY_OUTSIDE_GRANTED_PREFIX",
37622
- `Key "${path}" is outside the folder "${prefix}/" this sign-in covers. To use it, sign in with \`${CLI_NAME26} --profile ${profileName2} auth login --method local --keys <folder>\`.`,
37682
+ `Key "${path}" is outside the folder "${prefix}/" this sign-in covers. To use it, sign in with \`${CLI_NAME27} --profile ${profileName2} auth login --method local --keys <folder>\`.`,
37623
37683
  ExitCode.PERMISSION_DENIED
37624
37684
  );
37625
37685
  }
@@ -37647,7 +37707,7 @@ var defaultDeps = {
37647
37707
  };
37648
37708
  function registerKeysCommand(program, deps = defaultDeps) {
37649
37709
  const { outputJson: outputJson2, handleError: handleError2 } = deps;
37650
- const keys = program.command("keys").description(`Named encryption keys (sign in with \`${CLI_NAME26} auth login --keys <folder>\` first)`);
37710
+ const keys = program.command("keys").description(`Named encryption keys (sign in with \`${CLI_NAME27} auth login --keys <folder>\` first)`);
37651
37711
  keys.command("ensure <path>").description("Create the named key if it doesn't exist yet, then show it").action(async (path, _options, cmd) => {
37652
37712
  try {
37653
37713
  const ctx = await deps.resolveContext(cmd.optsWithGlobals());
@@ -37725,7 +37785,7 @@ init_errors();
37725
37785
  init_constants();
37726
37786
  init_theme();
37727
37787
  import { resolveManifestKnowledgeRoot as resolveManifestKnowledgeRoot2 } from "@tinycloud/sdk-core";
37728
- import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
37788
+ import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
37729
37789
  import { readFile as readFile13 } from "fs/promises";
37730
37790
  var DEFAULT_APP_SPACE = "applications";
37731
37791
  function registerManifestCommand(program) {
@@ -37733,8 +37793,8 @@ function registerManifestCommand(program) {
37733
37793
  manifest.command("resolve <source>").description("Resolve a manifest file or URL to its effective space, paths, and DB basenames").addHelpText("after", `
37734
37794
 
37735
37795
  Examples:
37736
- $ ${CLI_NAME27} manifest resolve ./manifest.json
37737
- $ ${CLI_NAME27} manifest resolve https://app.example.com/manifest.json --json
37796
+ $ ${CLI_NAME28} manifest resolve ./manifest.json
37797
+ $ ${CLI_NAME28} manifest resolve https://app.example.com/manifest.json --json
37738
37798
 
37739
37799
  What it shows:
37740
37800
  - app_id, name, manifest_version
@@ -37744,7 +37804,7 @@ What it shows:
37744
37804
 
37745
37805
  This command is read-only and does NOT contact the node \u2014 it just resolves
37746
37806
  the manifest against the active profile's address/chain so you know which
37747
- \`--space\` and \`--db\` values to pass to other ${CLI_NAME27} commands.
37807
+ \`--space\` and \`--db\` values to pass to other ${CLI_NAME28} commands.
37748
37808
  `).action(async (source, _options, cmd) => {
37749
37809
  try {
37750
37810
  const globalOpts = cmd.optsWithGlobals();
@@ -37813,7 +37873,7 @@ ${theme.heading("SQL databases")}
37813
37873
  `);
37814
37874
  }
37815
37875
  process.stdout.write(theme.muted(`
37816
- Use with: ${CLI_NAME27} sql query --space ${spaceName} --db <db> "..."
37876
+ Use with: ${CLI_NAME28} sql query --space ${spaceName} --db <db> "..."
37817
37877
  `));
37818
37878
  }
37819
37879
  if (permissions.length > 0) {
@@ -37933,7 +37993,7 @@ init_profiles();
37933
37993
  init_formatter();
37934
37994
  init_errors();
37935
37995
  init_constants();
37936
- import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
37996
+ import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
37937
37997
  import { createInterface as createInterface5 } from "readline";
37938
37998
  init_theme();
37939
37999
  init_types();
@@ -38079,9 +38139,9 @@ profile at command time. Precedence for every kv/sql command:
38079
38139
  explicit --space flag > profile defaultSpace > primary space.
38080
38140
 
38081
38141
  Examples:
38082
- $ ${CLI_NAME28} profile set-default-space applications
38083
- $ ${CLI_NAME28} profile set-default-space applications --profile cli-test
38084
- $ ${CLI_NAME28} profile set-default-space --unset
38142
+ $ ${CLI_NAME29} profile set-default-space applications
38143
+ $ ${CLI_NAME29} profile set-default-space applications --profile cli-test
38144
+ $ ${CLI_NAME29} profile set-default-space --unset
38085
38145
  `).action(async (name, options, cmd) => {
38086
38146
  try {
38087
38147
  const globalOpts = cmd.optsWithGlobals();
@@ -38093,7 +38153,7 @@ Examples:
38093
38153
  if (!options.unset && (name === void 0 || name === "")) {
38094
38154
  throw new CLIError(
38095
38155
  "USAGE_ERROR",
38096
- `Provide a space name (e.g. \`${CLI_NAME28} profile set-default-space applications\`) or pass --unset.`,
38156
+ `Provide a space name (e.g. \`${CLI_NAME29} profile set-default-space applications\`) or pass --unset.`,
38097
38157
  ExitCode.USAGE_ERROR
38098
38158
  );
38099
38159
  }
@@ -38152,7 +38212,7 @@ function parseOperatorType(raw) {
38152
38212
 
38153
38213
  // src/commands/replica.ts
38154
38214
  init_profile_replicas();
38155
- import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
38215
+ import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
38156
38216
  import { randomBytes as randomBytes7 } from "crypto";
38157
38217
  import { constants as fsConstants5 } from "fs";
38158
38218
  import { mkdir as mkdir5, open as open11, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
@@ -38293,11 +38353,11 @@ var EXIT_BY_CODE = {
38293
38353
  var READ_FAILURE = {
38294
38354
  absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
38295
38355
  deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
38296
- content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME29} replica sync` },
38356
+ content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME30} replica sync` },
38297
38357
  coverage_incomplete: {
38298
38358
  code: "COVERAGE_INCOMPLETE",
38299
38359
  exit: ExitCode.NOT_FOUND,
38300
- what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME29} replica sync`
38360
+ what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME30} replica sync`
38301
38361
  },
38302
38362
  not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
38303
38363
  };
@@ -38415,7 +38475,7 @@ async function existingReplicaName(profile, option) {
38415
38475
  const names = await replicaNames(profile);
38416
38476
  if (names.length === 1) return names[0];
38417
38477
  if (names.length === 0) {
38418
- throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME29} replica sync --prefix <prefix>`);
38478
+ throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME30} replica sync --prefix <prefix>`);
38419
38479
  }
38420
38480
  throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
38421
38481
  }
@@ -38523,9 +38583,9 @@ async function storedGrants(profile) {
38523
38583
  function grantHint(space, prefix) {
38524
38584
  return [
38525
38585
  "A replica needs a device grant with get and sync on its prefix:",
38526
- ` device: ${CLI_NAME29} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
38527
- ` owner: ${CLI_NAME29} auth grant req.json > grant.json`,
38528
- ` device: ${CLI_NAME29} auth import grant.json`
38586
+ ` device: ${CLI_NAME30} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
38587
+ ` owner: ${CLI_NAME30} auth grant req.json > grant.json`,
38588
+ ` device: ${CLI_NAME30} auth import grant.json`
38529
38589
  ].join("\n");
38530
38590
  }
38531
38591
  function resolveSpace(input, prefix, grants) {
@@ -38616,7 +38676,7 @@ function describeStatus(status) {
38616
38676
  formatField("Size", formatBytes(status.bytes)),
38617
38677
  formatField("Grant", status.device.delegationCid),
38618
38678
  formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
38619
- formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME29} replica command retries` : null)
38679
+ formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME30} replica command retries` : null)
38620
38680
  ].join("\n");
38621
38681
  }
38622
38682
  async function writeFileAtomic(path, bytes) {
@@ -38780,7 +38840,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
38780
38840
  (options, cmd) => run(async () => {
38781
38841
  const profile = await profileName(cmd);
38782
38842
  if (options.purge) {
38783
- throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME29} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
38843
+ throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME30} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
38784
38844
  }
38785
38845
  if (options.clearPending) {
38786
38846
  throw new CLIError("REPORT_READ_ONLY", "Clearing pending replica writes has no equivalent yet; see TC-1309 for a safe replacement command.", ExitCode.USAGE_ERROR);
@@ -38882,7 +38942,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
38882
38942
  if (differs !== void 0) {
38883
38943
  throw new ReplicaError(
38884
38944
  ReplicaErrorCode.CONFIG_MISMATCH,
38885
- `Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME29} replica reset --replica ${name} --purge first.`
38945
+ `Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME30} replica reset --replica ${name} --purge first.`
38886
38946
  );
38887
38947
  }
38888
38948
  }
@@ -39047,7 +39107,7 @@ init_theme();
39047
39107
  init_errors();
39048
39108
  init_warnings();
39049
39109
  init_constants();
39050
- import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
39110
+ import { CLI_NAME as CLI_NAME32 } from "@tinycloud/operations/cli-name";
39051
39111
  import { readFile as readFile14 } from "fs/promises";
39052
39112
  import { join as join17 } from "path";
39053
39113
  import { homedir, hostname, userInfo } from "os";
@@ -39258,7 +39318,7 @@ function parseStatusEnvelope(line) {
39258
39318
  // src/lib/secrets-wait.ts
39259
39319
  init_constants();
39260
39320
  init_errors();
39261
- import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
39321
+ import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
39262
39322
  var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
39263
39323
  var WAIT_STEADY_INTERVAL_MS = 15e3;
39264
39324
  var DEFAULT_WAIT_TIMEOUT = "5m";
@@ -39266,7 +39326,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
39266
39326
  var MAX_CONSECUTIVE_FAILURES = 5;
39267
39327
  var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
39268
39328
  var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
39269
- var EXPLICIT_KEY_HINT = `${CLI_NAME30} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME30} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
39329
+ var EXPLICIT_KEY_HINT = `${CLI_NAME31} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME31} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
39270
39330
  function abortableSleep(ms2, signal) {
39271
39331
  return new Promise((resolve9) => {
39272
39332
  if (signal.aborted) {
@@ -39302,7 +39362,7 @@ async function runSecretsWait(request, deps) {
39302
39362
  let step = 0;
39303
39363
  const cancelled = () => new CLIError(
39304
39364
  "CANCELLED",
39305
- `Interrupted: ${CLI_NAME30} secrets wait stopped before the secrets were ready.`,
39365
+ `Interrupted: ${CLI_NAME31} secrets wait stopped before the secrets were ready.`,
39306
39366
  ExitCode.CANCELLED,
39307
39367
  progress === void 0 ? {} : { progress }
39308
39368
  );
@@ -39446,13 +39506,13 @@ function statusError(error, profile, warnings) {
39446
39506
  if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
39447
39507
  const recovery = recoveryOf(error, "sign_in");
39448
39508
  if (recovery !== void 0) {
39449
- const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME30} auth login, then retry.`;
39509
+ const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME31} auth login, then retry.`;
39450
39510
  return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
39451
39511
  }
39452
39512
  }
39453
39513
  if (error.code === "PROFILE_MIGRATION_REQUIRED") {
39454
39514
  const recovery = recoveryOf(error, "complete_migration");
39455
- const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME30} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
39515
+ const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME31} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
39456
39516
  return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
39457
39517
  hint,
39458
39518
  ...recovery === void 0 ? {} : { recovery },
@@ -39644,11 +39704,11 @@ async function runSecretOperation(params) {
39644
39704
  }
39645
39705
  function secretPermissionReason(action, name) {
39646
39706
  const target = name ? ` secret "${name}"` : " secrets";
39647
- return `Allow \`${CLI_NAME31} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
39707
+ return `Allow \`${CLI_NAME32} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
39648
39708
  }
39649
39709
  function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
39650
39710
  if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
39651
- const command = `${CLI_NAME31} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
39711
+ const command = `${CLI_NAME32} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
39652
39712
  throw new CLIError(
39653
39713
  "PERMISSION_DENIED",
39654
39714
  action === "get" ? missing.length > 0 && missing.every((permission) => permission.service === "tinycloud.encryption" && Array.isArray(permission.actions) && permission.actions.length > 0 && permission.actions.every((capability) => capability === SECRET_DECRYPT_CAPABILITY)) ? `Profile "${profileName2}" lacks the scoped decrypt authority (${SECRET_DECRYPT_CAPABILITY}) needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" lacks a scoped read or decrypt grant needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" holds no grant for \`${command}\`, and requesting one needs an interactive browser approval.`,
@@ -39659,7 +39719,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
39659
39719
  );
39660
39720
  }
39661
39721
  function scopedSecretLoginHint(profileName2) {
39662
- return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME31} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
39722
+ return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME32} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
39663
39723
  }
39664
39724
  async function runSecretOperationAttempt(label, operation) {
39665
39725
  try {
@@ -39793,7 +39853,7 @@ ${message}`,
39793
39853
  "AUTH_REQUIRED",
39794
39854
  "Not signed in to TinyCloud.",
39795
39855
  ExitCode.AUTH_REQUIRED,
39796
- { hint: `Sign in with: ${CLI_NAME31} --profile ${result.context.profile} auth login` }
39856
+ { hint: `Sign in with: ${CLI_NAME32} --profile ${result.context.profile} auth login` }
39797
39857
  );
39798
39858
  }
39799
39859
  if (result.error.code === "NODE_UNREACHABLE") {
@@ -40270,7 +40330,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40270
40330
  name: "Encryption network",
40271
40331
  ok: false,
40272
40332
  detail: `${networkName} not found`,
40273
- hint: `${CLI_NAME31} secrets network init ${networkName}`
40333
+ hint: `${CLI_NAME32} secrets network init ${networkName}`
40274
40334
  }
40275
40335
  ];
40276
40336
  let secret;
@@ -40316,7 +40376,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40316
40376
  name: "Secret access",
40317
40377
  ok: false,
40318
40378
  detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
40319
- hint: notFound ? local ? `${CLI_NAME31} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME31} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
40379
+ hint: notFound ? local ? `${CLI_NAME32} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME32} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
40320
40380
  });
40321
40381
  }
40322
40382
  } else {
@@ -40560,7 +40620,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40560
40620
  if (effectiveLocal(profile, options)) {
40561
40621
  throw new CLIError(
40562
40622
  "LOCAL_OWNER_PROFILE",
40563
- `Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME31} secrets put NAME --stdin instead of requesting them.`,
40623
+ `Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME32} secrets put NAME --stdin instead of requesting them.`,
40564
40624
  ExitCode.USAGE_ERROR
40565
40625
  );
40566
40626
  }
@@ -40683,7 +40743,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
40683
40743
 
40684
40744
  // src/commands/share.ts
40685
40745
  import { Option } from "commander";
40686
- import { CLI_NAME as CLI_NAME32 } from "@tinycloud/operations/cli-name";
40746
+ import { CLI_NAME as CLI_NAME33 } from "@tinycloud/operations/cli-name";
40687
40747
  import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
40688
40748
  init_errors();
40689
40749
 
@@ -42144,7 +42204,7 @@ function shareCliError(error, operation = "publish") {
42144
42204
  "SHARE_HISTORY_UNREADABLE",
42145
42205
  "sender history cannot be read with the current session key",
42146
42206
  1,
42147
- { hint: `Run \`${CLI_NAME32} share history recover\` to move the unreadable file aside. The old file is kept, not deleted.` }
42207
+ { hint: `Run \`${CLI_NAME33} share history recover\` to move the unreadable file aside. The old file is kept, not deleted.` }
42148
42208
  );
42149
42209
  }
42150
42210
  if (error instanceof SharePublishAuthorityError) {
@@ -42152,12 +42212,12 @@ function shareCliError(error, operation = "publish") {
42152
42212
  const profileName2 = "profileName" in failure ? failure.profileName : void 0;
42153
42213
  const localKey = "localKey" in failure && failure.localKey === true;
42154
42214
  const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
42155
- const loginHint = localKey ? `\`${CLI_NAME32} ${profileHint}auth login --method local\`` : `\`${CLI_NAME32} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME32} ${profileHint}enable share\`)`;
42215
+ const loginHint = localKey ? `\`${CLI_NAME33} ${profileHint}auth login --method local\`` : `\`${CLI_NAME33} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME33} ${profileHint}enable share\`)`;
42156
42216
  if (failure.kind === "caveated-session") {
42157
42217
  const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
42158
42218
  return new CLIError(
42159
42219
  "PERMISSION_DENIED",
42160
- `${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_NAME32} init --name publisher --key-only && ${CLI_NAME32} --profile publisher enable share\``,
42220
+ `${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_NAME33} init --name publisher --key-only && ${CLI_NAME33} --profile publisher enable share\``,
42161
42221
  5
42162
42222
  );
42163
42223
  }
@@ -42258,7 +42318,7 @@ function shareCliError(error, operation = "publish") {
42258
42318
  if (nodeCode === "ENOENT") return new CLIError("INVALID_ARGUMENT", "share input was not found", 2);
42259
42319
  if (nodeCode === "EISDIR") return new CLIError("INVALID_ARGUMENT", "share input must be a Markdown file", 2);
42260
42320
  const mapped = typeof nodeCode === "string" && known[nodeCode] !== void 0 ? known[nodeCode] : known[message];
42261
- const classified = mapped === void 0 ? classifyShareFailure(error, `\`${CLI_NAME32} auth login\``) : void 0;
42321
+ const classified = mapped === void 0 ? classifyShareFailure(error, `\`${CLI_NAME33} auth login\``) : void 0;
42262
42322
  if (classified !== void 0) return Object.assign(new CLIError(classified.code, classified.reason, classified.exitCode), { cause: error });
42263
42323
  if (error instanceof TypeError) return new CLIError("INVALID_ARGUMENT", "share input is invalid", 2);
42264
42324
  return new CLIError(mapped?.code ?? "INVALID_ARGUMENT", mapped ? mapped.code : "share operation failed", mapped?.exit ?? 2);
@@ -42668,7 +42728,7 @@ init_constants();
42668
42728
  init_sdk();
42669
42729
  init_host();
42670
42730
  init_theme();
42671
- import { CLI_NAME as CLI_NAME33 } from "@tinycloud/operations/cli-name";
42731
+ import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
42672
42732
  import { ACCOUNT_REGISTRY_SPACE } from "@tinycloud/node-sdk";
42673
42733
  import { randomBytes as randomBytes9 } from "crypto";
42674
42734
  import { mkdir as mkdir7, writeFile as writeFile4 } from "fs/promises";
@@ -42715,7 +42775,7 @@ function registerSpaceCommand(program) {
42715
42775
  } else {
42716
42776
  const profile = await ProfileManager.getProfile(ctx.profile);
42717
42777
  const cap = "tinycloud.kv:account:spaces/:get,list";
42718
- const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME33} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME33} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
42778
+ const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME34} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME34} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
42719
42779
  throw new CLIError(
42720
42780
  "PERMISSION_DENIED",
42721
42781
  `Listing spaces needs tinycloud.kv/get and tinycloud.kv/list on the account space's spaces/ registry (${accountSpaceId ?? ACCOUNT_REGISTRY_SPACE}), or tinycloud.space/list. This session holds neither.`,
@@ -42752,10 +42812,10 @@ function registerSpaceCommand(program) {
42752
42812
 
42753
42813
  A delegate cannot host a space \u2014 only its owner (root authority) can. This
42754
42814
  emits a tinycloud.host.request artifact naming the space and its owner so you
42755
- can hand it to the owner; they run \`${CLI_NAME33} space host <name>\` and confirm.
42815
+ can hand it to the owner; they run \`${CLI_NAME34} space host <name>\` and confirm.
42756
42816
 
42757
42817
  If you ARE the owner of the resolved space, this refuses and tells you to host
42758
- it directly with \`${CLI_NAME33} space host <name>\` (no request needed).
42818
+ it directly with \`${CLI_NAME34} space host <name>\` (no request needed).
42759
42819
  `).action(async (name, options, cmd) => {
42760
42820
  try {
42761
42821
  const globalOpts = cmd.optsWithGlobals();
@@ -42766,7 +42826,7 @@ it directly with \`${CLI_NAME33} space host <name>\` (no request needed).
42766
42826
  if (await isRootAuthority(spaceId, ctx.profile)) {
42767
42827
  throw new CLIError(
42768
42828
  "ALREADY_ROOT_AUTHORITY",
42769
- `You are the owner of ${spaceId}. Host it directly: ${CLI_NAME33} space host ${spaceName}`,
42829
+ `You are the owner of ${spaceId}. Host it directly: ${CLI_NAME34} space host ${spaceName}`,
42770
42830
  ExitCode.USAGE_ERROR
42771
42831
  );
42772
42832
  }
@@ -42852,7 +42912,7 @@ init_sdk();
42852
42912
  init_space();
42853
42913
  init_host();
42854
42914
  init_theme();
42855
- import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
42915
+ import { CLI_NAME as CLI_NAME35 } from "@tinycloud/operations/cli-name";
42856
42916
  import { writeFile as writeFile5 } from "fs/promises";
42857
42917
  import { resolve as resolve8 } from "path";
42858
42918
  async function dbHandle(node, dbName, spaceInput, profileName2) {
@@ -42876,12 +42936,12 @@ database for simple apps, or pass --db to target a named database. Pass
42876
42936
  --space to target a non-primary space (e.g. the manifest "applications" space).
42877
42937
 
42878
42938
  Common workflows:
42879
- $ ${CLI_NAME34} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
42880
- $ ${CLI_NAME34} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
42881
- $ ${CLI_NAME34} sql query "SELECT id, body FROM notes ORDER BY id"
42882
- $ ${CLI_NAME34} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
42883
- $ ${CLI_NAME34} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
42884
- $ ${CLI_NAME34} sql export --db analytics --output analytics.db
42939
+ $ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
42940
+ $ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
42941
+ $ ${CLI_NAME35} sql query "SELECT id, body FROM notes ORDER BY id"
42942
+ $ ${CLI_NAME35} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
42943
+ $ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
42944
+ $ ${CLI_NAME35} sql export --db analytics --output analytics.db
42885
42945
 
42886
42946
  Commands:
42887
42947
  query Read rows with SELECT statements
@@ -42898,10 +42958,10 @@ Tips:
42898
42958
  sql.command("query <sql>").description("Run a read-only SELECT query").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
42899
42959
 
42900
42960
  Examples:
42901
- $ ${CLI_NAME34} sql query "SELECT * FROM notes ORDER BY id"
42902
- $ ${CLI_NAME34} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
42903
- $ ${CLI_NAME34} sql query "SELECT count(*) AS total FROM events" --db analytics --json
42904
- $ ${CLI_NAME34} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
42961
+ $ ${CLI_NAME35} sql query "SELECT * FROM notes ORDER BY id"
42962
+ $ ${CLI_NAME35} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
42963
+ $ ${CLI_NAME35} sql query "SELECT count(*) AS total FROM events" --db analytics --json
42964
+ $ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
42905
42965
 
42906
42966
  Output:
42907
42967
  Human output is formatted as a table. Piped output or --json returns
@@ -42942,11 +43002,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
42942
43002
  sql.command("execute <sql>").description("Run a write or schema statement").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
42943
43003
 
42944
43004
  Examples:
42945
- $ ${CLI_NAME34} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
42946
- $ ${CLI_NAME34} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
42947
- $ ${CLI_NAME34} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
42948
- $ ${CLI_NAME34} sql execute "DROP TABLE old_notes" --db archive
42949
- $ ${CLI_NAME34} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
43005
+ $ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
43006
+ $ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
43007
+ $ ${CLI_NAME35} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
43008
+ $ ${CLI_NAME35} sql execute "DROP TABLE old_notes" --db archive
43009
+ $ ${CLI_NAME35} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
42950
43010
 
42951
43011
  Output:
42952
43012
  Returns JSON with the changed row count and last inserted row id when available.
@@ -42975,9 +43035,9 @@ Output:
42975
43035
  sql.command("export").description("Export a SQLite database as a binary .db file").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("-o, --output <file>", "Output file path", "export.db").addHelpText("after", `
42976
43036
 
42977
43037
  Examples:
42978
- $ ${CLI_NAME34} sql export
42979
- $ ${CLI_NAME34} sql export --db analytics --output analytics.db
42980
- $ ${CLI_NAME34} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
43038
+ $ ${CLI_NAME35} sql export
43039
+ $ ${CLI_NAME35} sql export --db analytics --output analytics.db
43040
+ $ ${CLI_NAME35} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
42981
43041
 
42982
43042
  Output:
42983
43043
  Writes the database file locally and returns JSON with the path and size.
@@ -43010,19 +43070,19 @@ Output:
43010
43070
  sql.command("copy").description("Copy rows between SQL databases (optionally across spaces)").requiredOption("--from-db <name>", "Source database name").requiredOption("--to-db <name>", "Destination database name").option("--from-space <name|uri>", "Source space (defaults to primary)").option("--to-space <name|uri>", "Destination space (defaults to primary)").option("--table <name...>", "Restrict copy to specific tables (repeat or comma-separated)").option("--dry-run", "Print the plan without writing", false).addHelpText("after", `
43011
43071
 
43012
43072
  Examples:
43013
- $ ${CLI_NAME34} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
43073
+ $ ${CLI_NAME35} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
43014
43074
  --to-db xyz.tinycloud.listen/conversations \\
43015
43075
  --space applications --dry-run
43016
- $ ${CLI_NAME34} sql copy --from-space applications --from-db com.foo/data \\
43076
+ $ ${CLI_NAME35} sql copy --from-space applications --from-db com.foo/data \\
43017
43077
  --to-space applications --to-db com.bar/data \\
43018
43078
  --table conversation --table participant
43019
43079
 
43020
43080
  Notes:
43021
43081
  - Refuses to run when (resolved space, db) is identical for source and destination.
43022
- - Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME34} sql execute\`)
43082
+ - Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME35} sql execute\`)
43023
43083
  to materialize the schema before copying.
43024
43084
  - One row at a time; suitable for small/medium datasets. Large copies should
43025
- use \`${CLI_NAME34} sql export\` + bulk import.
43085
+ use \`${CLI_NAME35} sql export\` + bulk import.
43026
43086
  - Authorization: the active session/delegation must cover sql/read on source
43027
43087
  AND sql/write on destination. Otherwise the relevant operation will fail.
43028
43088
  `).action(async (options, cmd) => {
@@ -43122,7 +43182,7 @@ init_errors();
43122
43182
  init_formatter();
43123
43183
  init_theme();
43124
43184
  init_permissions();
43125
- import { CLI_NAME as CLI_NAME35 } from "@tinycloud/operations/cli-name";
43185
+ import { CLI_NAME as CLI_NAME36 } from "@tinycloud/operations/cli-name";
43126
43186
  import {
43127
43187
  NodeWasmBindings as NodeWasmBindings2
43128
43188
  } from "@tinycloud/node-sdk";
@@ -43396,7 +43456,7 @@ function formatStatus(summary) {
43396
43456
  lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
43397
43457
  lines.push("");
43398
43458
  if (summary.profiles.length === 0) {
43399
- lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME35} init`));
43459
+ lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME36} init`));
43400
43460
  return `${lines.join("\n")}
43401
43461
  `;
43402
43462
  }