@tinycloud/operations 0.5.0-beta.0 → 0.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.
Files changed (43) hide show
  1. package/coverage.json +9 -1
  2. package/dist/artifacts.d.cts +1 -1
  3. package/dist/artifacts.d.ts +1 -1
  4. package/dist/cli-runtime.cjs +788 -180
  5. package/dist/cli-runtime.cjs.map +1 -1
  6. package/dist/cli-runtime.d.cts +1 -1
  7. package/dist/cli-runtime.d.ts +1 -1
  8. package/dist/cli-runtime.js +786 -179
  9. package/dist/cli-runtime.js.map +1 -1
  10. package/dist/{contract-DfyrluLB.d.cts → contract-zRklonbR.d.cts} +6 -3
  11. package/dist/{contract-DfyrluLB.d.ts → contract-zRklonbR.d.ts} +6 -3
  12. package/dist/delegation-binding.cjs +7 -0
  13. package/dist/delegation-binding.cjs.map +1 -1
  14. package/dist/delegation-binding.d.cts +15 -3
  15. package/dist/delegation-binding.d.ts +15 -3
  16. package/dist/delegation-binding.js +6 -0
  17. package/dist/delegation-binding.js.map +1 -1
  18. package/dist/index.cjs +816 -181
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +1 -1
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +812 -178
  23. package/dist/index.js.map +1 -1
  24. package/dist/profile.cjs +5 -0
  25. package/dist/profile.cjs.map +1 -1
  26. package/dist/profile.d.cts +14 -2
  27. package/dist/profile.d.ts +14 -2
  28. package/dist/profile.js +4 -0
  29. package/dist/profile.js.map +1 -1
  30. package/dist/recovery.cjs +67 -0
  31. package/dist/recovery.cjs.map +1 -0
  32. package/dist/recovery.d.cts +38 -0
  33. package/dist/recovery.d.ts +38 -0
  34. package/dist/recovery.js +39 -0
  35. package/dist/recovery.js.map +1 -0
  36. package/dist/secret-request.cjs +271 -0
  37. package/dist/secret-request.cjs.map +1 -0
  38. package/dist/secret-request.d.cts +119 -0
  39. package/dist/secret-request.d.ts +119 -0
  40. package/dist/secret-request.js +222 -0
  41. package/dist/secret-request.js.map +1 -0
  42. package/generated/operations.json +1838 -89
  43. package/package.json +15 -5
package/dist/index.js CHANGED
@@ -298,6 +298,7 @@ var init_errors2 = __esm({
298
298
  "PROFILE_NOT_FOUND",
299
299
  "PROFILE_POSTURE_NOT_ALLOWED",
300
300
  "PROFILE_OWNER_OPT_IN_REQUIRED",
301
+ "PROFILE_MIGRATION_REQUIRED",
301
302
  "AUTH_REQUIRED",
302
303
  "PERMISSION_DENIED",
303
304
  "SESSION_NOT_FOUND",
@@ -5243,6 +5244,45 @@ var init_artifacts = __esm({
5243
5244
  }
5244
5245
  });
5245
5246
 
5247
+ // src/recovery.ts
5248
+ function signInRecovery(profile, posture) {
5249
+ if (!SAFE_PROFILE_TOKEN.test(profile)) return { action: "sign_in" };
5250
+ const template = posture === "local-owner-key" ? SIGN_IN_TEMPLATES.local : posture === "delegate-session" ? SIGN_IN_TEMPLATES.scoped : SIGN_IN_TEMPLATES.openkey;
5251
+ return { action: "sign_in", command: template(profile) };
5252
+ }
5253
+ function migrationRecovery(profile) {
5254
+ return SAFE_PROFILE_TOKEN.test(profile) ? { action: "complete_migration", command: migrationCommand(profile), operationId: MIGRATION_OPERATION_ID } : { action: "complete_migration", operationId: MIGRATION_OPERATION_ID };
5255
+ }
5256
+ function isSafeRecovery(value, action) {
5257
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
5258
+ const record = value;
5259
+ if (record.action !== action) return false;
5260
+ const keys = Object.keys(record);
5261
+ const allowed = action === "sign_in" ? ["action", "command"] : ["action", "command", "operationId"];
5262
+ if (!keys.every((key) => allowed.includes(key))) return false;
5263
+ if (action === "complete_migration" && record.operationId !== MIGRATION_OPERATION_ID) return false;
5264
+ if (!("command" in record)) return true;
5265
+ const command = record.command;
5266
+ if (typeof command !== "string") return false;
5267
+ const profile = /^tc --profile (\S+) /.exec(command)?.[1];
5268
+ if (profile === void 0 || !SAFE_PROFILE_TOKEN.test(profile)) return false;
5269
+ return action === "sign_in" ? Object.values(SIGN_IN_TEMPLATES).some((template) => template(profile) === command) : migrationCommand(profile) === command;
5270
+ }
5271
+ var SAFE_PROFILE_TOKEN, MIGRATION_OPERATION_ID, SIGN_IN_TEMPLATES, migrationCommand;
5272
+ var init_recovery = __esm({
5273
+ "src/recovery.ts"() {
5274
+ "use strict";
5275
+ SAFE_PROFILE_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
5276
+ MIGRATION_OPERATION_ID = "tinycloud.auth.capabilities";
5277
+ SIGN_IN_TEMPLATES = {
5278
+ local: (profile) => `tc --profile ${profile} auth login --method local`,
5279
+ openkey: (profile) => `tc --profile ${profile} auth login --method openkey`,
5280
+ scoped: (profile) => `tc --profile ${profile} auth login --method openkey --paste --manifest <manifest.json>`
5281
+ };
5282
+ migrationCommand = (profile) => `tc --profile ${profile} auth caps`;
5283
+ }
5284
+ });
5285
+
5246
5286
  // src/secrets.ts
5247
5287
  import * as nodeSdk from "@tinycloud/node-sdk";
5248
5288
  function normalizeSecretsGetInput(input) {
@@ -5330,15 +5370,6 @@ function canonicalizeSpaceUri(space) {
5330
5370
  }
5331
5371
  return space;
5332
5372
  }
5333
- function buildSecretSetupUrl(reference) {
5334
- const parameters = [
5335
- ["secret", reference.name],
5336
- ...reference.scope === void 0 ? [] : [["scope", reference.scope]],
5337
- ["space", reference.space]
5338
- ];
5339
- const query = parameters.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
5340
- return `${SECRET_MANAGER_BASE_URL}/app?${query}`;
5341
- }
5342
5373
  function canonicalizeSecretScope(scope) {
5343
5374
  if (scope === void 0 || scope === "") return void 0;
5344
5375
  const trimmed = scope.trim();
@@ -5366,13 +5397,12 @@ function invalidSecretInput() {
5366
5397
  operationError("INPUT_INVALID", "The secret reference is invalid.")
5367
5398
  );
5368
5399
  }
5369
- var DEFAULT_SECRETS_SPACE, SECRET_MANAGER_BASE_URL, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
5400
+ var DEFAULT_SECRETS_SPACE, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
5370
5401
  var init_secrets = __esm({
5371
5402
  "src/secrets.ts"() {
5372
5403
  "use strict";
5373
5404
  init_errors2();
5374
5405
  DEFAULT_SECRETS_SPACE = "secrets";
5375
- SECRET_MANAGER_BASE_URL = "https://secrets.tinycloud.xyz";
5376
5406
  SECRET_NAME_RE2 = /^[A-Z][A-Z0-9_]*$/;
5377
5407
  RESERVED_SECRET_SCOPES = /* @__PURE__ */ new Set(["default", "global"]);
5378
5408
  }
@@ -5529,7 +5559,10 @@ async function importRequestBoundDelegation(context, input) {
5529
5559
  delegation,
5530
5560
  { host }
5531
5561
  );
5532
- } catch {
5562
+ } catch (error) {
5563
+ if (typeof error === "object" && error !== null && "reason" in error && error.reason === "expired") {
5564
+ return expiredDelegation();
5565
+ }
5533
5566
  return operationFailure(
5534
5567
  "DELEGATION_REJECTED",
5535
5568
  "The delegation was rejected by the TinyCloud runtime."
@@ -5547,55 +5580,67 @@ async function importRequestBoundDelegation(context, input) {
5547
5580
  );
5548
5581
  }
5549
5582
  try {
5550
- return await updateProfileStore(
5551
- context.summary.profile,
5552
- "additional-delegations",
5553
- async (records) => {
5554
- const state = await importState(context, input.requestId);
5555
- if (!state.ok) return { records, result: state.result };
5556
- if (state.sessionDid !== sessionDid || normalizeHost(state.host) !== normalizeHost(host)) {
5557
- return {
5558
- records,
5559
- result: audienceMismatch(state.sessionDid, sessionDid)
5583
+ return await withProfileLock(context.summary.profile, async () => {
5584
+ const previousRecords = await readAdditionalDelegations(context.summary.profile);
5585
+ const result = await updateProfileStore(
5586
+ context.summary.profile,
5587
+ "additional-delegations",
5588
+ async (records) => {
5589
+ const state = await importState(context, input.requestId);
5590
+ if (!state.ok) return { records, result: state.result };
5591
+ if (state.sessionDid !== sessionDid || normalizeHost(state.host) !== normalizeHost(host)) {
5592
+ return {
5593
+ records,
5594
+ result: audienceMismatch(state.sessionDid, sessionDid)
5595
+ };
5596
+ }
5597
+ if (state.request.profile !== context.summary.profile || !samePrincipal(state.request.sessionDid, sessionDid) || normalizeHost(state.request.host) !== normalizeHost(host) || JSON.stringify(canonicalizeOperationCapabilities(state.request.requested, resolveSpace2)) !== JSON.stringify(canonicalizeOperationCapabilities(request.requested, resolveSpace2))) {
5598
+ return {
5599
+ records,
5600
+ result: operationFailure(
5601
+ "DELEGATION_ARTIFACT_INVALID",
5602
+ "The delegation does not reference a current stored request."
5603
+ )
5604
+ };
5605
+ }
5606
+ const record = {
5607
+ delegation: activated.delegation,
5608
+ permissions: activated.effectivePermissions,
5609
+ authorityRequest: {
5610
+ requestId: request.requestId,
5611
+ requested: request.requested
5612
+ }
5560
5613
  };
5561
- }
5562
- if (state.request.profile !== context.summary.profile || !samePrincipal(state.request.sessionDid, sessionDid) || normalizeHost(state.request.host) !== normalizeHost(host) || JSON.stringify(canonicalizeOperationCapabilities(state.request.requested, resolveSpace2)) !== JSON.stringify(canonicalizeOperationCapabilities(request.requested, resolveSpace2))) {
5614
+ const position = records.findIndex((entry) => delegationCid(entry) === activated.cid);
5615
+ const alreadyPresent = position !== -1;
5616
+ const others = records.filter((entry) => delegationCid(entry) !== activated.cid);
5563
5617
  return {
5564
- records,
5565
- result: operationFailure(
5566
- "DELEGATION_ARTIFACT_INVALID",
5567
- "The delegation does not reference a current stored request."
5568
- )
5618
+ records: alreadyPresent ? [...others.slice(0, position), record, ...others.slice(position)] : [...records, record],
5619
+ result: {
5620
+ status: "ok",
5621
+ output: {
5622
+ cid: activated.cid,
5623
+ effectivePermissions,
5624
+ expiry: activated.expiry.toISOString(),
5625
+ audience: activated.audience,
5626
+ host: activated.host,
5627
+ activated: true,
5628
+ alreadyPresent
5629
+ }
5630
+ }
5569
5631
  };
5570
5632
  }
5571
- const record = {
5572
- delegation: activated.delegation,
5573
- permissions: activated.effectivePermissions,
5574
- authorityRequest: {
5575
- requestId: request.requestId,
5576
- requested: request.requested
5577
- }
5578
- };
5579
- const position = records.findIndex((entry) => delegationCid(entry) === activated.cid);
5580
- const alreadyPresent = position !== -1;
5581
- const others = records.filter((entry) => delegationCid(entry) !== activated.cid);
5582
- return {
5583
- records: alreadyPresent ? [...others.slice(0, position), record, ...others.slice(position)] : [...records, record],
5584
- result: {
5585
- status: "ok",
5586
- output: {
5587
- cid: activated.cid,
5588
- effectivePermissions,
5589
- expiry: activated.expiry.toISOString(),
5590
- audience: activated.audience,
5591
- host: activated.host,
5592
- activated: true,
5593
- alreadyPresent
5594
- }
5595
- }
5596
- };
5633
+ );
5634
+ if (activated.expiry.getTime() <= Date.now()) {
5635
+ await updateProfileStore(
5636
+ context.summary.profile,
5637
+ "additional-delegations",
5638
+ () => ({ records: previousRecords, result: void 0 })
5639
+ );
5640
+ return expiredDelegation();
5597
5641
  }
5598
- );
5642
+ return result;
5643
+ });
5599
5644
  } catch {
5600
5645
  return internalImportFailure();
5601
5646
  }
@@ -5842,8 +5887,33 @@ var init_auth = __esm({
5842
5887
  }
5843
5888
  });
5844
5889
 
5845
- // src/operations/exploration.ts
5890
+ // src/kv-failure.ts
5846
5891
  import { authorizationVerdictOf, validatedCapabilityOf } from "@tinycloud/sdk-services";
5892
+ function kvRefusal(error) {
5893
+ const verdict = authorizationVerdictOf(error);
5894
+ if (verdict === "unauthenticated") {
5895
+ return validatedCapabilityOf(error) === void 0 ? "auth_required" : "permission_denied";
5896
+ }
5897
+ return verdict === "forbidden" ? "permission_denied" : void 0;
5898
+ }
5899
+ function authorizationFailure(error, action) {
5900
+ switch (kvRefusal(error)) {
5901
+ case "auth_required":
5902
+ return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
5903
+ case "permission_denied":
5904
+ return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
5905
+ case void 0:
5906
+ return void 0;
5907
+ }
5908
+ }
5909
+ var init_kv_failure = __esm({
5910
+ "src/kv-failure.ts"() {
5911
+ "use strict";
5912
+ init_errors2();
5913
+ }
5914
+ });
5915
+
5916
+ // src/operations/exploration.ts
5847
5917
  import { z as z4 } from "zod";
5848
5918
  async function planAccountRegistryRead(context, path) {
5849
5919
  return [{
@@ -6211,19 +6281,6 @@ function caughtKvFailure(error, action) {
6211
6281
  if (error instanceof OperationInvocationError) return { status: "error", error: error.operationError };
6212
6282
  return authorizationFailure(error, action) ?? nodeFailure(action);
6213
6283
  }
6214
- function authorizationFailure(error, action) {
6215
- const verdict = authorizationVerdictOf(error);
6216
- if (verdict === "unauthenticated") {
6217
- if (validatedCapabilityOf(error) !== void 0) {
6218
- return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
6219
- }
6220
- return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
6221
- }
6222
- if (verdict === "forbidden") {
6223
- return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
6224
- }
6225
- return void 0;
6226
- }
6227
6284
  function accountRegistryOutput(output) {
6228
6285
  if (Buffer.byteLength(JSON.stringify(output), "utf8") > MAX_ACCOUNT_REGISTRY_BYTES) {
6229
6286
  return {
@@ -6276,6 +6333,7 @@ var MAX_KV_VALUE_BYTES, DEFAULT_KV_LIST_LIMIT, MAX_ACCOUNT_REGISTRY_RECORDS, MAX
6276
6333
  var init_exploration = __esm({
6277
6334
  "src/operations/exploration.ts"() {
6278
6335
  "use strict";
6336
+ init_kv_failure();
6279
6337
  init_errors2();
6280
6338
  init_secrets();
6281
6339
  MAX_KV_VALUE_BYTES = 1024 * 1024;
@@ -6576,6 +6634,191 @@ var init_exploration = __esm({
6576
6634
  }
6577
6635
  });
6578
6636
 
6637
+ // src/secret-request.ts
6638
+ import * as nodeSdk3 from "@tinycloud/node-sdk";
6639
+ function canonicalSecretRequestOwner(did) {
6640
+ const match = typeof did === "string" ? OWNER_RE.exec(did) : null;
6641
+ if (match === null || !Number.isSafeInteger(Number(match[1]))) throw invalidOwner();
6642
+ const address = match[2];
6643
+ let checksummed;
6644
+ try {
6645
+ checksummed = nodeSdk3.canonicalizeAddress(address);
6646
+ } catch {
6647
+ throw invalidOwner();
6648
+ }
6649
+ if (/[A-F]/.test(address.slice(2)) && address !== checksummed) throw invalidOwner();
6650
+ return `did:pkh:eip155:${match[1]}:${checksummed}`;
6651
+ }
6652
+ function buildSecretRequestUrl(input) {
6653
+ const owner = canonicalSecretRequestOwner(input.owner);
6654
+ const names = validSecretNames(input.names);
6655
+ if (input.scope !== void 0) assertSecretRequestScope(input.scope);
6656
+ const requester = input.requester;
6657
+ if (requester !== void 0 && (typeof requester !== "string" || requester.length === 0 || requester.length > MAX_REQUESTER_LENGTH || CONTROL_OR_FORMAT.test(requester))) {
6658
+ throw new SecretRequestInputError("INVALID_REQUESTER", "The requester label must be 1-64 characters with no control or format characters.");
6659
+ }
6660
+ if (input.returnTo !== void 0 && !isAllowedReturnTo(input.returnTo)) {
6661
+ throw new SecretRequestInputError("INVALID_RETURN_TO", "The return address is not an allowed Secret Manager callback.");
6662
+ }
6663
+ if (input.state !== void 0 && !STATE_RE.test(input.state)) {
6664
+ throw new SecretRequestInputError("INVALID_STATE", "The state must be 1-128 characters of A-Z, a-z, 0-9, '.', '_', '~' or '-'.");
6665
+ }
6666
+ const parameters = [
6667
+ ["owner", owner],
6668
+ ...names.map((name) => ["name", name]),
6669
+ ...input.scope === void 0 ? [] : [["scope", input.scope]],
6670
+ ...input.requester === void 0 ? [] : [["requester", input.requester]],
6671
+ ...input.returnTo === void 0 ? [] : [["return_to", input.returnTo]],
6672
+ ...input.state === void 0 ? [] : [["state", input.state]],
6673
+ ["v", "1"]
6674
+ ];
6675
+ const url = `${SECRET_MANAGER_ORIGIN}/request?${parameters.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}`;
6676
+ if (url.length > SECRET_REQUEST_MAX_URL_LENGTH) {
6677
+ throw new SecretRequestInputError("REQUEST_LINK_TOO_LONG", `The request link is longer than ${SECRET_REQUEST_MAX_URL_LENGTH} characters.`);
6678
+ }
6679
+ return { url, owner, names, ...input.scope === void 0 ? {} : { scope: input.scope } };
6680
+ }
6681
+ function secretSetupFor(link2) {
6682
+ return {
6683
+ url: link2.url,
6684
+ names: [...link2.names],
6685
+ ...link2.scope === void 0 ? {} : { scope: link2.scope },
6686
+ owner: link2.owner,
6687
+ action: "add_secret"
6688
+ };
6689
+ }
6690
+ function resolveSecretsOwner(profile, requestedOwner) {
6691
+ const resolved = profile.local ? ownerOrUndefined(profile.principalDid) : ownerOrUndefined(profile.ownerDid) ?? (profile.posture === "delegate-session" ? ownerOfSpace(profile.space) : void 0) ?? ownerOrUndefined(profile.principalDid);
6692
+ const requested = requestedOwner === void 0 ? void 0 : ownerOrUndefined(requestedOwner);
6693
+ if (resolved !== void 0) {
6694
+ if (requestedOwner !== void 0 && requested !== resolved) {
6695
+ return {
6696
+ ok: false,
6697
+ code: "OWNER_MISMATCH",
6698
+ message: "The requested owner is not the owner of this identity's secrets."
6699
+ };
6700
+ }
6701
+ return { ok: true, owner: resolved, local: profile.local };
6702
+ }
6703
+ if (requested !== void 0 && !profile.local) return { ok: true, owner: requested, local: false };
6704
+ return {
6705
+ ok: false,
6706
+ code: "OWNER_DID_UNKNOWN",
6707
+ message: "The owner of this identity's secrets is unknown. Pass the owner's did:pkh with --owner."
6708
+ };
6709
+ }
6710
+ function missingSecretRecovery(input) {
6711
+ if (input.local) {
6712
+ return { kind: "put_locally", hint: `Store it with: ${putCommands(input.names, input.scope)}` };
6713
+ }
6714
+ if (input.owner !== void 0) {
6715
+ try {
6716
+ return {
6717
+ kind: "add_secret",
6718
+ setup: secretSetupFor(buildSecretRequestUrl({
6719
+ owner: input.owner,
6720
+ names: input.names,
6721
+ ...input.scope === void 0 ? {} : { scope: input.scope }
6722
+ }))
6723
+ };
6724
+ } catch {
6725
+ }
6726
+ }
6727
+ return {
6728
+ kind: "not_requestable",
6729
+ hint: `No Secret Manager request link can express this secret. The owner can store it with: ${putCommands(input.names, input.scope)}`
6730
+ };
6731
+ }
6732
+ function isValidSecretRequestScope(scope) {
6733
+ return scope.length <= MAX_SCOPE_LENGTH && SCOPE_RE.test(scope) && !RESERVED_SCOPES.includes(scope);
6734
+ }
6735
+ function isValidSecretName(name) {
6736
+ return name.length <= MAX_NAME_LENGTH && NAME_RE.test(name);
6737
+ }
6738
+ function validSecretNames(names) {
6739
+ for (const name of names) assertSecretName(name);
6740
+ const unique = dedupe(names);
6741
+ if (unique.length === 0) {
6742
+ throw new SecretRequestInputError("INVALID_SECRET_NAME", "At least one secret name is required.");
6743
+ }
6744
+ if (unique.length > SECRET_REQUEST_MAX_NAMES) {
6745
+ throw new SecretRequestInputError("TOO_MANY_SECRET_NAMES", `At most ${SECRET_REQUEST_MAX_NAMES} secret names can be requested at once.`);
6746
+ }
6747
+ return unique;
6748
+ }
6749
+ function assertSecretName(name) {
6750
+ if (typeof name !== "string" || !isValidSecretName(name)) {
6751
+ throw new SecretRequestInputError("INVALID_SECRET_NAME", "Secret names are 1-64 characters of A-Z, 0-9 and '_', starting with a letter.");
6752
+ }
6753
+ }
6754
+ function assertSecretRequestScope(scope) {
6755
+ if (typeof scope !== "string" || !isValidSecretRequestScope(scope)) {
6756
+ throw new SecretRequestInputError("INVALID_SECRET_SCOPE", "The scope must be a canonical scope of at most 64 characters, not default, global or scoped.");
6757
+ }
6758
+ }
6759
+ function isAllowedReturnTo(returnTo) {
6760
+ if (typeof returnTo !== "string" || returnTo.length > MAX_RETURN_TO_LENGTH) return false;
6761
+ let url;
6762
+ try {
6763
+ url = new URL(returnTo);
6764
+ } catch {
6765
+ return false;
6766
+ }
6767
+ if (url.username !== "" || url.password !== "") return false;
6768
+ return RETURN_TO_ORIGINS.includes(url.origin) || RETURN_TO_PREVIEW.test(url.origin);
6769
+ }
6770
+ function ownerOrUndefined(did) {
6771
+ if (did === void 0) return void 0;
6772
+ try {
6773
+ return canonicalSecretRequestOwner(did);
6774
+ } catch {
6775
+ return void 0;
6776
+ }
6777
+ }
6778
+ function ownerOfSpace(space) {
6779
+ const match = space === void 0 ? null : PKH_SPACE_RE.exec(space);
6780
+ if (match === null) return void 0;
6781
+ return ownerOrUndefined(`did:pkh:eip155:${match[1]}:${match[2].toLowerCase()}`);
6782
+ }
6783
+ function putCommands(names, scope) {
6784
+ return dedupe(names).map((name) => `tc secrets put ${name}${scope === void 0 ? "" : ` --scope ${scope}`} --stdin`).join("; ");
6785
+ }
6786
+ function dedupe(names) {
6787
+ return [...new Set(names)];
6788
+ }
6789
+ function invalidOwner() {
6790
+ return new SecretRequestInputError("INVALID_OWNER", "The owner must be a did:pkh:eip155 account with a valid EIP-55 address.");
6791
+ }
6792
+ var SECRET_MANAGER_ORIGIN, SECRET_REQUEST_MAX_NAMES, SECRET_REQUEST_MAX_URL_LENGTH, OWNER_RE, NAME_RE, MAX_NAME_LENGTH, SCOPE_RE, MAX_SCOPE_LENGTH, RESERVED_SCOPES, MAX_REQUESTER_LENGTH, CONTROL_OR_FORMAT, STATE_RE, MAX_RETURN_TO_LENGTH, RETURN_TO_ORIGINS, RETURN_TO_PREVIEW, PKH_SPACE_RE, SecretRequestInputError;
6793
+ var init_secret_request = __esm({
6794
+ "src/secret-request.ts"() {
6795
+ "use strict";
6796
+ SECRET_MANAGER_ORIGIN = "https://secrets.tinycloud.xyz";
6797
+ SECRET_REQUEST_MAX_NAMES = 10;
6798
+ SECRET_REQUEST_MAX_URL_LENGTH = 8192;
6799
+ OWNER_RE = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/;
6800
+ NAME_RE = /^[A-Z][A-Z0-9_]*$/;
6801
+ MAX_NAME_LENGTH = 64;
6802
+ SCOPE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
6803
+ MAX_SCOPE_LENGTH = 64;
6804
+ RESERVED_SCOPES = ["default", "global", "scoped"];
6805
+ MAX_REQUESTER_LENGTH = 64;
6806
+ CONTROL_OR_FORMAT = /[\p{Cc}\p{Cf}]/u;
6807
+ STATE_RE = /^[A-Za-z0-9._~-]{1,128}$/;
6808
+ MAX_RETURN_TO_LENGTH = 2048;
6809
+ RETURN_TO_ORIGINS = ["https://feed.tinycloud.xyz"];
6810
+ RETURN_TO_PREVIEW = /^https:\/\/[a-z0-9-]+\.tinyfeed\.pages\.dev$/;
6811
+ PKH_SPACE_RE = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40}):[^:]+$/;
6812
+ SecretRequestInputError = class extends Error {
6813
+ constructor(code, message) {
6814
+ super(message);
6815
+ this.name = "SecretRequestInputError";
6816
+ this.code = code;
6817
+ }
6818
+ };
6819
+ }
6820
+ });
6821
+
6579
6822
  // src/operations/secrets-get.ts
6580
6823
  import { z as z5 } from "zod";
6581
6824
  async function planSecretGet(context, input) {
@@ -6643,27 +6886,14 @@ async function executeSecretGet(context, input) {
6643
6886
  )
6644
6887
  };
6645
6888
  }
6646
- return classifySecretRead(reference, result);
6889
+ return classifySecretRead(reference, result, context.runtime?.localOwner === true);
6647
6890
  }
6648
- function classifySecretRead(reference, result) {
6891
+ function classifySecretRead(reference, result, localOwner) {
6649
6892
  switch (result.status) {
6650
6893
  case "ok":
6651
6894
  return { status: "ok", output: { value: result.value } };
6652
6895
  case "not_found":
6653
- return {
6654
- status: "setup_required",
6655
- setup: {
6656
- kind: "secret_manager",
6657
- secret: {
6658
- name: reference.name,
6659
- ...reference.scope === void 0 ? {} : { scope: reference.scope },
6660
- space: reference.space
6661
- },
6662
- url: buildSecretSetupUrl(reference),
6663
- message: "Enter this secret in Secret Manager, then retry the operation."
6664
- },
6665
- requiresCallerInput: true
6666
- };
6896
+ return missingSecretOutcome(reference, localOwner);
6667
6897
  case "permission_required":
6668
6898
  return {
6669
6899
  status: "authority_required",
@@ -6708,6 +6938,47 @@ function classifySecretRead(reference, result) {
6708
6938
  error: operationError("SECRET_READ_FAILED", "The secret read failed.")
6709
6939
  };
6710
6940
  }
6941
+ function missingSecretOutcome(reference, localOwner) {
6942
+ const owner = ownerOfSpace(reference.space);
6943
+ const recovery = missingSecretRecovery({
6944
+ owner,
6945
+ local: localOwner,
6946
+ names: [reference.name],
6947
+ ...reference.scope === void 0 ? {} : { scope: reference.scope }
6948
+ });
6949
+ if (recovery.kind === "add_secret") {
6950
+ const { setup } = recovery;
6951
+ return {
6952
+ status: "setup_required",
6953
+ setup: {
6954
+ kind: "secret_manager",
6955
+ action: setup.action,
6956
+ url: setup.url,
6957
+ names: setup.names,
6958
+ ...setup.scope === void 0 ? {} : { scope: setup.scope },
6959
+ owner: setup.owner,
6960
+ secret: {
6961
+ name: reference.name,
6962
+ ...reference.scope === void 0 ? {} : { scope: reference.scope },
6963
+ space: reference.space
6964
+ },
6965
+ message: "Ask the owner to add this secret in Secret Manager, then retry the operation."
6966
+ },
6967
+ requiresCallerInput: true
6968
+ };
6969
+ }
6970
+ return {
6971
+ status: "error",
6972
+ error: operationError("KV_NOT_FOUND", recovery.hint, {
6973
+ details: {
6974
+ names: [reference.name],
6975
+ ...reference.scope === void 0 ? {} : { scope: reference.scope },
6976
+ ...owner === void 0 ? {} : { owner },
6977
+ recoveryKind: recovery.kind
6978
+ }
6979
+ })
6980
+ };
6981
+ }
6711
6982
  function resolveEncryptionNetworkId(node, space) {
6712
6983
  const candidate = node;
6713
6984
  let scoped;
@@ -6734,6 +7005,7 @@ var init_secrets_get = __esm({
6734
7005
  "src/operations/secrets-get.ts"() {
6735
7006
  "use strict";
6736
7007
  init_errors2();
7008
+ init_secret_request();
6737
7009
  init_secrets();
6738
7010
  SecretsGetInputSchema = z5.object({
6739
7011
  name: z5.string(),
@@ -6785,9 +7057,197 @@ var init_secrets_get = __esm({
6785
7057
  }
6786
7058
  });
6787
7059
 
7060
+ // src/operations/secrets-status.ts
7061
+ import { z as z6 } from "zod";
7062
+ import * as nodeSdk4 from "@tinycloud/node-sdk";
7063
+ async function executeSecretsStatus(context, input) {
7064
+ const runtime = context.runtime;
7065
+ const node = runtime?.node;
7066
+ if (runtime === void 0 || typeof node?.kvForSpace !== "function") {
7067
+ return {
7068
+ status: "error",
7069
+ error: operationError("NODE_ERROR", "The TinyCloud node could not check the secrets.", { retryable: true })
7070
+ };
7071
+ }
7072
+ const local = runtime.localOwner === true;
7073
+ const resolution = resolveSecretsOwner({
7074
+ local,
7075
+ posture: context.summary.posture,
7076
+ ...context.summary.ownerDid === void 0 ? {} : { ownerDid: context.summary.ownerDid },
7077
+ ...context.summary.principalDid === void 0 ? {} : { principalDid: context.summary.principalDid },
7078
+ ...context.summary.space === void 0 ? {} : { space: context.summary.space }
7079
+ }, input.owner);
7080
+ if (!resolution.ok) {
7081
+ return {
7082
+ status: "error",
7083
+ error: operationError("INPUT_INVALID", resolution.message, { details: { reason: resolution.code } })
7084
+ };
7085
+ }
7086
+ const owner = resolution.owner;
7087
+ const skipped2 = runtime.skippedGrantReasons ?? [];
7088
+ const pkh = nodeSdk4.parsePkhDid(owner);
7089
+ const kv = node.kvForSpace(nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets"));
7090
+ const outcomes = await Promise.all(input.names.map(async (name) => {
7091
+ const path = input.scope === void 0 ? `vault/secrets/${name}` : `vault/secrets/scoped/${input.scope}/${name}`;
7092
+ try {
7093
+ const result = await kv.get(path, { raw: true, prefix: "", timeout: PROBE_TIMEOUT_MS });
7094
+ if (result.ok) return "present";
7095
+ const error = result.error;
7096
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "KV_NOT_FOUND") {
7097
+ return "pending";
7098
+ }
7099
+ const refusal = kvRefusal(error);
7100
+ if (refusal === "auth_required") return "auth_required";
7101
+ if (refusal === "permission_denied") return "denied";
7102
+ return "unreachable";
7103
+ } catch {
7104
+ return "unreachable";
7105
+ }
7106
+ }));
7107
+ const namesWith = (outcome) => input.names.filter((_, index) => outcomes[index] === outcome);
7108
+ const present = namesWith("present");
7109
+ const pending = namesWith("pending");
7110
+ const denied = namesWith("denied");
7111
+ if (present.length + pending.length < input.names.length) {
7112
+ if (outcomes.includes("auth_required") || skipped2.includes("activation_unauthenticated")) {
7113
+ return {
7114
+ status: "error",
7115
+ error: operationError(
7116
+ "AUTH_REQUIRED",
7117
+ "The node did not accept this profile's stored session. Sign in again, then retry.",
7118
+ { retryable: false, details: { recovery: signInRecovery(context.summary.profile, context.summary.posture) } }
7119
+ )
7120
+ };
7121
+ }
7122
+ if (outcomes.includes("unreachable") || skipped2.includes("activation_unavailable")) {
7123
+ return {
7124
+ status: "error",
7125
+ error: operationError("NODE_UNREACHABLE", "The TinyCloud node could not be reached.", { retryable: true })
7126
+ };
7127
+ }
7128
+ }
7129
+ const recovery = pending.length === 0 ? void 0 : missingSecretRecovery({
7130
+ owner,
7131
+ local,
7132
+ names: pending,
7133
+ ...input.scope === void 0 ? {} : { scope: input.scope }
7134
+ });
7135
+ return {
7136
+ status: "ok",
7137
+ output: {
7138
+ status: denied.length > 0 ? "consent_required" : pending.length > 0 ? "pending" : "ready",
7139
+ owner,
7140
+ names: [...input.names],
7141
+ ...input.scope === void 0 ? {} : { scope: input.scope },
7142
+ present,
7143
+ pending,
7144
+ denied,
7145
+ ...recovery === void 0 ? {} : recovery.kind === "add_secret" ? { setup: recovery.setup } : { hint: recovery.hint }
7146
+ }
7147
+ };
7148
+ }
7149
+ var PROBE_TIMEOUT_MS, SECRETS_STATUS_DEADLINE_MS, SecretsStatusInputSchema, SecretSetupSchema, SecretsStatusOutputSchema, SECRETS_STATUS_POSTURES, SECRETS_STATUS_EXPOSURE, SECRETS_STATUS_SENSITIVITY, secretsStatusOperationDefinition, secretsStatusOperationDefinitions;
7150
+ var init_secrets_status = __esm({
7151
+ "src/operations/secrets-status.ts"() {
7152
+ "use strict";
7153
+ init_errors2();
7154
+ init_kv_failure();
7155
+ init_recovery();
7156
+ init_secret_request();
7157
+ init_secrets();
7158
+ PROBE_TIMEOUT_MS = 15e3;
7159
+ SECRETS_STATUS_DEADLINE_MS = 18e3;
7160
+ SecretsStatusInputSchema = z6.object({
7161
+ names: z6.array(z6.string()),
7162
+ scope: z6.string().optional(),
7163
+ owner: z6.string().optional()
7164
+ }).strict().superRefine((input, context) => {
7165
+ try {
7166
+ validSecretNames(input.names);
7167
+ } catch {
7168
+ context.addIssue({ code: z6.ZodIssueCode.custom, path: ["names"], message: "Invalid secret names." });
7169
+ }
7170
+ try {
7171
+ const scope = canonicalizeSecretScope(input.scope);
7172
+ if (scope !== void 0 && !isValidSecretRequestScope(scope)) throw new Error("invalid scope");
7173
+ } catch {
7174
+ context.addIssue({ code: z6.ZodIssueCode.custom, path: ["scope"], message: "Invalid secret scope." });
7175
+ }
7176
+ if (input.owner !== void 0) {
7177
+ try {
7178
+ canonicalSecretRequestOwner(input.owner);
7179
+ } catch {
7180
+ context.addIssue({ code: z6.ZodIssueCode.custom, path: ["owner"], message: "Invalid owner." });
7181
+ }
7182
+ }
7183
+ }).transform((input) => {
7184
+ const scope = canonicalizeSecretScope(input.scope);
7185
+ return {
7186
+ names: validSecretNames(input.names),
7187
+ ...scope === void 0 ? {} : { scope },
7188
+ ...input.owner === void 0 ? {} : { owner: canonicalSecretRequestOwner(input.owner) }
7189
+ };
7190
+ });
7191
+ SecretSetupSchema = z6.object({
7192
+ url: z6.string().url(),
7193
+ names: z6.array(z6.string()).min(1),
7194
+ scope: z6.string().optional(),
7195
+ owner: z6.string(),
7196
+ action: z6.literal("add_secret")
7197
+ }).strict();
7198
+ SecretsStatusOutputSchema = z6.object({
7199
+ status: z6.enum(["ready", "pending", "consent_required"]),
7200
+ owner: z6.string(),
7201
+ names: z6.array(z6.string()).min(1),
7202
+ scope: z6.string().optional(),
7203
+ present: z6.array(z6.string()),
7204
+ pending: z6.array(z6.string()),
7205
+ denied: z6.array(z6.string()),
7206
+ setup: SecretSetupSchema.optional(),
7207
+ hint: z6.string().optional()
7208
+ }).strict();
7209
+ SECRETS_STATUS_POSTURES = [
7210
+ "owner-openkey",
7211
+ "delegate-session",
7212
+ "local-owner-key"
7213
+ ];
7214
+ SECRETS_STATUS_EXPOSURE = {
7215
+ cli: { status: "required" },
7216
+ mcp: { status: "required" },
7217
+ skill: { status: "required" },
7218
+ docs: { status: "required" }
7219
+ };
7220
+ SECRETS_STATUS_SENSITIVITY = {
7221
+ input: [],
7222
+ output: []
7223
+ };
7224
+ secretsStatusOperationDefinition = {
7225
+ id: "tinycloud.secrets.status",
7226
+ version: 1,
7227
+ title: "Check whether TinyCloud secrets exist",
7228
+ description: "Report, without reading any value, which named secrets exist in the owner's secrets space: ready, pending (with a Secret Manager request link), or consent_required for names this identity may not read. Each name needs tinycloud.kv/get; this operation never requests it. It never signs in: it uses only the profile's stored session, and writes no profile state.",
7229
+ input: SecretsStatusInputSchema,
7230
+ output: SecretsStatusOutputSchema,
7231
+ effects: ["read"],
7232
+ runtime: "restored",
7233
+ postures: SECRETS_STATUS_POSTURES,
7234
+ exposure: SECRETS_STATUS_EXPOSURE,
7235
+ sensitivity: SECRETS_STATUS_SENSITIVITY,
7236
+ deadlineMs: SECRETS_STATUS_DEADLINE_MS,
7237
+ // Never evaluated against the session: a missing grant is reported as
7238
+ // `consent_required`, never saved as an authority request.
7239
+ authority: async () => [],
7240
+ execute: executeSecretsStatus
7241
+ };
7242
+ secretsStatusOperationDefinitions = [
7243
+ secretsStatusOperationDefinition
7244
+ ];
7245
+ }
7246
+ });
7247
+
6788
7248
  // src/operations/sql.ts
6789
7249
  import nodeSqlParser from "node-sql-parser";
6790
- import { z as z6 } from "zod";
7250
+ import { z as z7 } from "zod";
6791
7251
  async function planSqlRead(context, input) {
6792
7252
  const space = resolveSqlSpace(context, input.space);
6793
7253
  return [{
@@ -7105,7 +7565,7 @@ var init_sql = __esm({
7105
7565
  "use strict";
7106
7566
  init_errors2();
7107
7567
  init_secrets();
7108
- init_exploration();
7568
+ init_kv_failure();
7109
7569
  DEFAULT_MAX_ROWS = 100;
7110
7570
  MAX_MAX_ROWS = 1e3;
7111
7571
  DEFAULT_MAX_BYTES = 1024 * 1024;
@@ -7121,29 +7581,29 @@ WHERE type IN ('table', 'view', 'index', 'trigger')
7121
7581
  AND name NOT LIKE 'sqlite_%'
7122
7582
  ORDER BY type, name`;
7123
7583
  sqlParser = new nodeSqlParser.Parser();
7124
- SpaceSchema2 = z6.string().min(1).refine(
7584
+ SpaceSchema2 = z7.string().min(1).refine(
7125
7585
  (space) => (/^[A-Za-z0-9_-]+$/.test(space) || space.startsWith("tinycloud:")) && !space.includes("*"),
7126
7586
  "Invalid TinyCloud space."
7127
7587
  );
7128
- DatabaseSchema = z6.string().min(1).max(256).refine(
7588
+ DatabaseSchema = z7.string().min(1).max(256).refine(
7129
7589
  (database) => database !== "/" && !database.endsWith("/") && !database.includes("*") && !/[\u0000-\u001f\u007f]/.test(database),
7130
7590
  "Invalid SQLite database name."
7131
7591
  );
7132
- SafeNumberSchema = z6.number().finite().refine(
7592
+ SafeNumberSchema = z7.number().finite().refine(
7133
7593
  (value) => !Number.isInteger(value) || Number.isSafeInteger(value),
7134
7594
  "SQLite integer parameters must be JavaScript safe integers."
7135
7595
  );
7136
- Base64Schema = z6.string().max(Math.ceil(MAX_MAX_BYTES / 3) * 4).refine(
7596
+ Base64Schema = z7.string().max(Math.ceil(MAX_MAX_BYTES / 3) * 4).refine(
7137
7597
  isCanonicalBase642,
7138
7598
  "Invalid base64 BLOB value."
7139
7599
  );
7140
- SqlInputValueSchema = z6.union([
7141
- z6.null(),
7600
+ SqlInputValueSchema = z7.union([
7601
+ z7.null(),
7142
7602
  SafeNumberSchema,
7143
- z6.string().max(MAX_MAX_BYTES),
7144
- z6.object({ type: z6.literal("blob"), base64: Base64Schema }).strict()
7603
+ z7.string().max(MAX_MAX_BYTES),
7604
+ z7.object({ type: z7.literal("blob"), base64: Base64Schema }).strict()
7145
7605
  ]);
7146
- SqlParamsSchema = z6.array(SqlInputValueSchema).max(MAX_PARAMETERS).superRefine(
7606
+ SqlParamsSchema = z7.array(SqlInputValueSchema).max(MAX_PARAMETERS).superRefine(
7147
7607
  (params, context) => {
7148
7608
  const totalBytes = params.reduce(
7149
7609
  (total, value) => total + sqlInputValueBytes(value),
@@ -7151,13 +7611,13 @@ ORDER BY type, name`;
7151
7611
  );
7152
7612
  if (totalBytes > MAX_MAX_BYTES) {
7153
7613
  context.addIssue({
7154
- code: z6.ZodIssueCode.custom,
7614
+ code: z7.ZodIssueCode.custom,
7155
7615
  message: "SQLite query parameters exceed the 4 MiB input limit."
7156
7616
  });
7157
7617
  }
7158
7618
  }
7159
7619
  );
7160
- RequiredSqlParamsSchema = z6.array(SqlInputValueSchema).min(1).max(MAX_PARAMETERS).superRefine(
7620
+ RequiredSqlParamsSchema = z7.array(SqlInputValueSchema).min(1).max(MAX_PARAMETERS).superRefine(
7161
7621
  (params, context) => {
7162
7622
  const totalBytes = params.reduce(
7163
7623
  (total, value) => total + sqlInputValueBytes(value),
@@ -7165,88 +7625,88 @@ ORDER BY type, name`;
7165
7625
  );
7166
7626
  if (totalBytes > MAX_MAX_BYTES) {
7167
7627
  context.addIssue({
7168
- code: z6.ZodIssueCode.custom,
7628
+ code: z7.ZodIssueCode.custom,
7169
7629
  message: "SQLite query parameters exceed the 4 MiB input limit."
7170
7630
  });
7171
7631
  }
7172
7632
  }
7173
7633
  );
7174
- SqlOutputValueSchema = z6.union([
7175
- z6.null(),
7634
+ SqlOutputValueSchema = z7.union([
7635
+ z7.null(),
7176
7636
  SafeNumberSchema,
7177
- z6.string(),
7178
- z6.object({
7179
- type: z6.literal("blob"),
7180
- base64: z6.string(),
7181
- byteLength: z6.number().int().nonnegative()
7637
+ z7.string(),
7638
+ z7.object({
7639
+ type: z7.literal("blob"),
7640
+ base64: z7.string(),
7641
+ byteLength: z7.number().int().nonnegative()
7182
7642
  }).strict()
7183
7643
  ]);
7184
- SqlTargetInputSchema = z6.object({
7644
+ SqlTargetInputSchema = z7.object({
7185
7645
  space: SpaceSchema2,
7186
7646
  database: DatabaseSchema
7187
7647
  }).strict();
7188
- SqlQueryInputSchema = z6.object({
7648
+ SqlQueryInputSchema = z7.object({
7189
7649
  space: SpaceSchema2,
7190
7650
  database: DatabaseSchema,
7191
- sql: z6.string().min(1).max(MAX_SQL_LENGTH).superRefine((sql, context) => {
7651
+ sql: z7.string().min(1).max(MAX_SQL_LENGTH).superRefine((sql, context) => {
7192
7652
  if (!isSingleReadQuery(sql)) {
7193
7653
  context.addIssue({
7194
- code: z6.ZodIssueCode.custom,
7654
+ code: z7.ZodIssueCode.custom,
7195
7655
  message: "SQL must parse as exactly one SQLite SELECT statement."
7196
7656
  });
7197
7657
  }
7198
7658
  }),
7199
7659
  params: SqlParamsSchema.optional(),
7200
- maxRows: z6.number().int().positive().max(MAX_MAX_ROWS).optional(),
7201
- maxBytes: z6.number().int().positive().max(MAX_MAX_BYTES).optional()
7660
+ maxRows: z7.number().int().positive().max(MAX_MAX_ROWS).optional(),
7661
+ maxBytes: z7.number().int().positive().max(MAX_MAX_BYTES).optional()
7202
7662
  }).strict();
7203
- SqlExecuteInputSchema = z6.object({
7663
+ SqlExecuteInputSchema = z7.object({
7204
7664
  space: SpaceSchema2,
7205
7665
  database: DatabaseSchema,
7206
- sql: z6.string().min(1).max(MAX_SQL_LENGTH),
7666
+ sql: z7.string().min(1).max(MAX_SQL_LENGTH),
7207
7667
  params: RequiredSqlParamsSchema,
7208
- acknowledgeDatabaseWideAuthority: z6.literal(true).describe(SQL_WRITE_AUTHORITY_NOTICE)
7668
+ acknowledgeDatabaseWideAuthority: z7.literal(true).describe(SQL_WRITE_AUTHORITY_NOTICE)
7209
7669
  }).strict().superRefine((input, context) => {
7210
7670
  if (parseSingleDmlStatement(input.sql, input.params.length) === void 0) {
7211
7671
  context.addIssue({
7212
- code: z6.ZodIssueCode.custom,
7672
+ code: z7.ZodIssueCode.custom,
7213
7673
  path: ["sql"],
7214
7674
  message: "SQL must parse as exactly one parameterized INSERT, UPDATE, or DELETE with one value per positional placeholder."
7215
7675
  });
7216
7676
  }
7217
7677
  });
7218
- SqlLimitsOutputSchema = z6.object({
7219
- maxRows: z6.number().int().positive(),
7220
- maxBytes: z6.number().int().positive(),
7221
- enforcement: z6.literal("node-requested-client-verified")
7678
+ SqlLimitsOutputSchema = z7.object({
7679
+ maxRows: z7.number().int().positive(),
7680
+ maxBytes: z7.number().int().positive(),
7681
+ enforcement: z7.literal("node-requested-client-verified")
7222
7682
  }).strict();
7223
- SqlSchemaInspectOutputSchema = z6.object({
7224
- space: z6.string().min(1),
7225
- database: z6.string().min(1),
7226
- objects: z6.array(z6.object({
7227
- type: z6.enum(["table", "view", "index", "trigger"]),
7228
- name: z6.string().min(1),
7229
- tableName: z6.string().min(1),
7230
- sql: z6.string().optional()
7683
+ SqlSchemaInspectOutputSchema = z7.object({
7684
+ space: z7.string().min(1),
7685
+ database: z7.string().min(1),
7686
+ objects: z7.array(z7.object({
7687
+ type: z7.enum(["table", "view", "index", "trigger"]),
7688
+ name: z7.string().min(1),
7689
+ tableName: z7.string().min(1),
7690
+ sql: z7.string().optional()
7231
7691
  }).strict()),
7232
- count: z6.number().int().nonnegative(),
7692
+ count: z7.number().int().nonnegative(),
7233
7693
  limits: SqlLimitsOutputSchema
7234
7694
  }).strict();
7235
- SqlQueryOutputSchema = z6.object({
7236
- space: z6.string().min(1),
7237
- database: z6.string().min(1),
7238
- columns: z6.array(z6.string()),
7239
- rows: z6.array(z6.array(SqlOutputValueSchema)),
7240
- rowCount: z6.number().int().nonnegative(),
7695
+ SqlQueryOutputSchema = z7.object({
7696
+ space: z7.string().min(1),
7697
+ database: z7.string().min(1),
7698
+ columns: z7.array(z7.string()),
7699
+ rows: z7.array(z7.array(SqlOutputValueSchema)),
7700
+ rowCount: z7.number().int().nonnegative(),
7241
7701
  limits: SqlLimitsOutputSchema
7242
7702
  }).strict();
7243
- SqlExecuteOutputSchema = z6.object({
7244
- space: z6.string().min(1),
7245
- database: z6.string().min(1),
7246
- statementType: z6.enum(["insert", "update", "delete"]),
7247
- changes: z6.number().int().nonnegative(),
7248
- lastInsertRowId: z6.number().int().nullable(),
7249
- authorityNotice: z6.literal(SQL_WRITE_AUTHORITY_NOTICE)
7703
+ SqlExecuteOutputSchema = z7.object({
7704
+ space: z7.string().min(1),
7705
+ database: z7.string().min(1),
7706
+ statementType: z7.enum(["insert", "update", "delete"]),
7707
+ changes: z7.number().int().nonnegative(),
7708
+ lastInsertRowId: z7.number().int().nullable(),
7709
+ authorityNotice: z7.literal(SQL_WRITE_AUTHORITY_NOTICE)
7250
7710
  }).strict();
7251
7711
  AUTHENTICATED_POSTURES2 = [
7252
7712
  "owner-openkey",
@@ -7346,6 +7806,8 @@ var init_contract = __esm({
7346
7806
  "resources_mismatch",
7347
7807
  "unsupported",
7348
7808
  "activation_rejected",
7809
+ "activation_unavailable",
7810
+ "activation_unauthenticated",
7349
7811
  "invalid"
7350
7812
  ];
7351
7813
  DELEGATION_CID_PATTERN = "^bafkr4i[a-z2-7]{52}$";
@@ -7354,7 +7816,7 @@ var init_contract = __esm({
7354
7816
 
7355
7817
  // src/delegation-binding.ts
7356
7818
  import { join as join2 } from "path";
7357
- import * as nodeSdk3 from "@tinycloud/node-sdk";
7819
+ import * as nodeSdk5 from "@tinycloud/node-sdk";
7358
7820
  function bindingMigrationPath(profile) {
7359
7821
  return join2(profilePath(profile), "delegation-binding-migration.json");
7360
7822
  }
@@ -7450,6 +7912,9 @@ function skipped(reason) {
7450
7912
  }
7451
7913
  async function replayProfileDelegations(profile, node, options) {
7452
7914
  await prepareStoredDelegationReplay(profile, node, { host: options.host, migrate: options.migrate });
7915
+ return replayStoredDelegationRecords(profile, node, options);
7916
+ }
7917
+ async function replayStoredDelegationRecords(profile, node, options) {
7453
7918
  const installed = [];
7454
7919
  const warnings = [];
7455
7920
  const seen = /* @__PURE__ */ new Set();
@@ -7490,7 +7955,7 @@ function storedDelegationForAnotherKey(entry, node) {
7490
7955
  const audience = verifiedCompactAudience(node, delegation);
7491
7956
  if (audience === void 0) return false;
7492
7957
  try {
7493
- return nodeSdk3.principalDidEquals(delegation.delegateDID, audience) && !nodeSdk3.principalDidEquals(audience, node.sessionDid);
7958
+ return nodeSdk5.principalDidEquals(delegation.delegateDID, audience) && !nodeSdk5.principalDidEquals(audience, node.sessionDid);
7494
7959
  } catch {
7495
7960
  return false;
7496
7961
  }
@@ -7524,7 +7989,7 @@ async function assess(node, entry, options, mode) {
7524
7989
  let outsideRequest = false;
7525
7990
  let effectivePermissions;
7526
7991
  try {
7527
- const installed = await nodeSdk3.activateValidatedRuntimeDelegation(node, toActivate, {
7992
+ const installed = await nodeSdk5.activateValidatedRuntimeDelegation(node, toActivate, {
7528
7993
  host: options.host,
7529
7994
  authorize: (effective) => {
7530
7995
  verifyCompactProof(toActivate);
@@ -7556,7 +8021,7 @@ function verifyCompactProof(delegation) {
7556
8021
  try {
7557
8022
  parseSignedCompactUcanAttenuation(withoutBearer(authorization), delegation.cid);
7558
8023
  } catch {
7559
- throw new nodeSdk3.RuntimeDelegationRejectedError("proof_invalid", "The compact delegation proof is invalid.");
8024
+ throw new nodeSdk5.RuntimeDelegationRejectedError("proof_invalid", "The compact delegation proof is invalid.");
7560
8025
  }
7561
8026
  }
7562
8027
  function rejectionReason(error) {
@@ -7573,7 +8038,7 @@ async function bindingMigrationRecorded(profile) {
7573
8038
  async function signedCapabilities(node, delegation, host) {
7574
8039
  let signed;
7575
8040
  try {
7576
- await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, {
8041
+ await nodeSdk5.activateValidatedRuntimeDelegation(node, delegation, {
7577
8042
  // The transport host is not signed authority; check it at replay instead.
7578
8043
  host: delegation.host ?? host,
7579
8044
  authorize: (effective) => {
@@ -7636,7 +8101,7 @@ var init_delegation_binding = __esm({
7636
8101
  });
7637
8102
 
7638
8103
  // src/operations/status.ts
7639
- import { z as z7 } from "zod";
8104
+ import { z as z8 } from "zod";
7640
8105
  function createStatusDefinition(params) {
7641
8106
  return {
7642
8107
  id: params.id,
@@ -7761,27 +8226,27 @@ var init_status = __esm({
7761
8226
  init_delegation_binding();
7762
8227
  init_errors2();
7763
8228
  init_state();
7764
- StatusInputSchema = z7.object({}).strict();
7765
- StatusOutputSchema = z7.object({
7766
- profile: z7.string().min(1),
7767
- host: z7.string().min(1),
7768
- posture: z7.enum([
8229
+ StatusInputSchema = z8.object({}).strict();
8230
+ StatusOutputSchema = z8.object({
8231
+ profile: z8.string().min(1),
8232
+ host: z8.string().min(1),
8233
+ posture: z8.enum([
7769
8234
  "owner-openkey",
7770
8235
  "delegate-session",
7771
8236
  "local-owner-key",
7772
8237
  "unauthenticated"
7773
8238
  ]),
7774
- operatorType: z7.enum(["human", "agent"]).optional(),
7775
- principalDid: z7.string().min(1).optional(),
7776
- sessionDid: z7.string().min(1).optional(),
7777
- ownerDid: z7.string().min(1).optional(),
7778
- space: z7.string().min(1).optional(),
7779
- session: z7.object({
7780
- present: z7.boolean(),
7781
- expired: z7.boolean().nullable(),
7782
- expiresAt: z7.string().datetime({ offset: true }).nullable()
8239
+ operatorType: z8.enum(["human", "agent"]).optional(),
8240
+ principalDid: z8.string().min(1).optional(),
8241
+ sessionDid: z8.string().min(1).optional(),
8242
+ ownerDid: z8.string().min(1).optional(),
8243
+ space: z8.string().min(1).optional(),
8244
+ session: z8.object({
8245
+ present: z8.boolean(),
8246
+ expired: z8.boolean().nullable(),
8247
+ expiresAt: z8.string().datetime({ offset: true }).nullable()
7783
8248
  }).strict(),
7784
- liveAdditionalDelegationCount: z7.number().int().nonnegative().describe(
8249
+ liveAdditionalDelegationCount: z8.number().int().nonnegative().describe(
7785
8250
  "Stored, unexpired compact-UCAN or signed-login delegations with a valid request binding. Each still requires local proof validation and node activation before it grants authority."
7786
8251
  )
7787
8252
  }).strict();
@@ -7825,7 +8290,8 @@ function registeredOperationDefinitions() {
7825
8290
  ...authOperationDefinitions,
7826
8291
  ...explorationOperationDefinitions,
7827
8292
  ...sqlOperationDefinitions,
7828
- ...secretsGetOperationDefinitions
8293
+ ...secretsGetOperationDefinitions,
8294
+ ...secretsStatusOperationDefinitions
7829
8295
  ]);
7830
8296
  return operationDefinitions;
7831
8297
  }
@@ -7857,6 +8323,7 @@ var init_registry = __esm({
7857
8323
  init_auth();
7858
8324
  init_exploration();
7859
8325
  init_secrets_get();
8326
+ init_secrets_status();
7860
8327
  init_sql();
7861
8328
  init_status();
7862
8329
  }
@@ -7909,6 +8376,9 @@ function resolvePosture(profile) {
7909
8376
  if (profile.authMethod === "local") return "local-owner-key";
7910
8377
  return "owner-openkey";
7911
8378
  }
8379
+ function isLocalOwnerProfile(profile) {
8380
+ return resolvePosture(profile) === "local-owner-key" || profile.authMethod === "local" || typeof profile.privateKey === "string";
8381
+ }
7912
8382
  function profileNotFound2(profile) {
7913
8383
  return {
7914
8384
  ok: false,
@@ -7980,7 +8450,7 @@ var runtime_exports = {};
7980
8450
  __export(runtime_exports, {
7981
8451
  createInvocationRuntime: () => createInvocationRuntime
7982
8452
  });
7983
- import * as nodeSdk4 from "@tinycloud/node-sdk";
8453
+ import * as nodeSdk6 from "@tinycloud/node-sdk";
7984
8454
  async function createInvocationRuntime(target, requirement = "authenticated") {
7985
8455
  const resolved = await resolveInvocationProfile(target);
7986
8456
  if (!resolved.ok) {
@@ -7994,11 +8464,14 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
7994
8464
  if (requirement === "inspection") {
7995
8465
  return { ok: true, context: { summary } };
7996
8466
  }
8467
+ if (requirement === "restored") {
8468
+ return createRestoredRuntime(target, resolved);
8469
+ }
7997
8470
  const profileName = summary.profile;
7998
8471
  try {
7999
8472
  const {
8000
8473
  TinyCloudNode: TinyCloudNodeConstructor
8001
- } = nodeSdk4;
8474
+ } = nodeSdk6;
8002
8475
  const explicitPrivateKeyOverride = typeof target.privateKey === "string";
8003
8476
  const [session, key] = explicitPrivateKeyOverride ? [null, null] : await Promise.all([
8004
8477
  readSession(profileName),
@@ -8076,7 +8549,9 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
8076
8549
  const runtime = {
8077
8550
  node,
8078
8551
  granted: canonicalizeCapabilities(livePermissions),
8079
- ...warnings.length === 0 ? {} : { warnings }
8552
+ ...warnings.length === 0 ? {} : { warnings },
8553
+ localOwner: explicitPrivateKeyOverride || isLocalOwnerProfile(profile),
8554
+ skippedGrantReasons: warnings.map((warning) => warning.reason)
8080
8555
  };
8081
8556
  return {
8082
8557
  ok: true,
@@ -8100,11 +8575,112 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
8100
8575
  ));
8101
8576
  }
8102
8577
  }
8578
+ async function createRestoredRuntime(target, resolved) {
8579
+ const summary = resolved.context;
8580
+ const profileName = summary.profile;
8581
+ const profile = resolved.profile;
8582
+ if (typeof target.privateKey === "string") {
8583
+ return failed(summary, operationError(
8584
+ "AUTH_REQUIRED",
8585
+ "This operation never signs in; an explicit private key has no stored session.",
8586
+ { details: { reason: "explicit_key" } }
8587
+ ));
8588
+ }
8589
+ try {
8590
+ const actualPosture = resolvePosture(profile);
8591
+ const selectedPosture = profile.authMethod === "local" ? "local-owner-key" : summary.posture;
8592
+ if (actualPosture !== summary.posture || selectedPosture !== summary.posture) {
8593
+ return failed(
8594
+ { ...summary, posture: actualPosture !== summary.posture ? actualPosture : selectedPosture },
8595
+ operationError(
8596
+ "PROFILE_POSTURE_NOT_ALLOWED",
8597
+ "The selected profile changed posture during runtime resolution."
8598
+ )
8599
+ );
8600
+ }
8601
+ const [session, key] = await Promise.all([
8602
+ readSession(profileName),
8603
+ readJson(`${profilePath(profileName)}/key.json`)
8604
+ ]);
8605
+ const activeSession = session === null ? void 0 : normalizeSession(session, key);
8606
+ if (activeSession === void 0) {
8607
+ return failed(summary, operationError(
8608
+ "SESSION_NOT_FOUND",
8609
+ "The selected profile does not have an active session. This operation never signs in.",
8610
+ { details: { recovery: signInRecovery(profileName, summary.posture) } }
8611
+ ));
8612
+ }
8613
+ if (!await bindingMigrationRecorded(profileName)) {
8614
+ const records = await readAdditionalDelegations(profileName);
8615
+ if (records.some(
8616
+ (entry) => storedDelegationKind(entry) === "compact" && !("authorityRequest" in entry)
8617
+ )) {
8618
+ return failed(summary, operationError(
8619
+ "PROFILE_MIGRATION_REQUIRED",
8620
+ `Profile "${profileName}" has a one-time stored-grant upgrade to finish. Run one authenticated command on it (tc --profile <profile> auth caps), then retry.`,
8621
+ { retryable: false, details: { recovery: migrationRecovery(profileName) } }
8622
+ ));
8623
+ }
8624
+ }
8625
+ const { TinyCloudNode: TinyCloudNodeConstructor } = nodeSdk6;
8626
+ const node = new TinyCloudNodeConstructor({
8627
+ host: summary.host,
8628
+ ...typeof profile.privateKey === "string" ? { privateKey: profile.privateKey } : {}
8629
+ });
8630
+ try {
8631
+ await node.restoreSession(activeSession);
8632
+ } catch (error) {
8633
+ if (isRecord7(error) && error.code === "AUTH_EXPIRED") {
8634
+ return failed(summary, operationError(
8635
+ "AUTH_REQUIRED",
8636
+ "The stored session has expired or is no longer valid. Sign in again.",
8637
+ { retryable: false, details: { recovery: signInRecovery(profileName, summary.posture) } }
8638
+ ));
8639
+ }
8640
+ throw error;
8641
+ }
8642
+ const activeSessionDid = normalizeDid(node.sessionDid);
8643
+ const livePrincipalDid = normalizeDid(node.did);
8644
+ const livePermissions = [...node.getVerifiedSessionCapabilities()];
8645
+ const replay = await replayStoredDelegationRecords(
8646
+ profileName,
8647
+ node,
8648
+ { host: summary.host, resolveSpace: operationSpaceResolver(node, summary.space) }
8649
+ );
8650
+ for (const installed of replay.installed) {
8651
+ livePermissions.push(...installed.effectivePermissions);
8652
+ }
8653
+ const runtime = {
8654
+ node,
8655
+ granted: canonicalizeCapabilities(livePermissions),
8656
+ ...replay.warnings.length === 0 ? {} : { warnings: replay.warnings },
8657
+ localOwner: isLocalOwnerProfile(profile),
8658
+ skippedGrantReasons: replay.warnings.map((warning) => warning.reason)
8659
+ };
8660
+ return {
8661
+ ok: true,
8662
+ context: {
8663
+ summary: {
8664
+ ...summary,
8665
+ ...livePrincipalDid === void 0 ? {} : { principalDid: livePrincipalDid },
8666
+ ...activeSessionDid === void 0 ? {} : { sessionDid: activeSessionDid }
8667
+ },
8668
+ runtime
8669
+ }
8670
+ };
8671
+ } catch {
8672
+ return failed(summary, operationError(
8673
+ "NODE_ERROR",
8674
+ "The selected profile runtime could not be initialized.",
8675
+ { retryable: true }
8676
+ ));
8677
+ }
8678
+ }
8103
8679
  function spaceForAuthenticatedPrincipal(principal2) {
8104
8680
  if (principal2 === void 0) return void 0;
8105
8681
  try {
8106
- const pkh = nodeSdk4.parsePkhDid(principal2);
8107
- return pkh === null ? void 0 : nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
8682
+ const pkh = nodeSdk6.parsePkhDid(principal2);
8683
+ return pkh === null ? void 0 : nodeSdk6.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
8108
8684
  } catch {
8109
8685
  return void 0;
8110
8686
  }
@@ -8154,6 +8730,7 @@ var init_runtime = __esm({
8154
8730
  init_authority();
8155
8731
  init_errors2();
8156
8732
  init_profile();
8733
+ init_recovery();
8157
8734
  init_secrets();
8158
8735
  init_state();
8159
8736
  init_delegation_binding();
@@ -8168,11 +8745,14 @@ init_artifacts();
8168
8745
  import { createHash } from "crypto";
8169
8746
  import { isAbsolute as isAbsolute2 } from "path";
8170
8747
  import {
8171
- makePkhSpaceId as makePkhSpaceId3,
8172
- parsePkhDid as parsePkhDid3,
8748
+ makePkhSpaceId as makePkhSpaceId4,
8749
+ parsePkhDid as parsePkhDid4,
8173
8750
  parseSpaceUri as parseSpaceUri2
8174
8751
  } from "@tinycloud/node-sdk";
8175
8752
 
8753
+ // src/redaction.ts
8754
+ init_recovery();
8755
+
8176
8756
  // src/safe-values.ts
8177
8757
  function safeOriginHost(value) {
8178
8758
  if (typeof value !== "string" || value.length === 0) return void 0;
@@ -8249,7 +8829,25 @@ function redactOperationError(_definition, error) {
8249
8829
  }
8250
8830
  var SAFE_ERROR_DETAIL_POLICIES = {
8251
8831
  INPUT_INVALID: {
8252
- issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0
8832
+ issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0,
8833
+ reason: (value) => value === "OWNER_MISMATCH" || value === "OWNER_DID_UNKNOWN" ? value : void 0
8834
+ },
8835
+ AUTH_REQUIRED: {
8836
+ recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0,
8837
+ reason: (value) => value === "explicit_key" ? value : void 0
8838
+ },
8839
+ SESSION_NOT_FOUND: {
8840
+ recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0
8841
+ },
8842
+ PROFILE_MIGRATION_REQUIRED: {
8843
+ recovery: (value) => isSafeRecovery(value, "complete_migration") ? { ...value } : void 0
8844
+ },
8845
+ // `secrets.get` not found for an identity no request link serves.
8846
+ KV_NOT_FOUND: {
8847
+ recoveryKind: (value) => value === "put_locally" || value === "not_requestable" ? value : void 0,
8848
+ names: (value) => Array.isArray(value) && value.length >= 1 && value.length <= 10 && value.every((name) => typeof name === "string" && name.length <= 256 && /^[A-Z][A-Z0-9_]*$/.test(name)) ? [...value] : void 0,
8849
+ scope: (value) => typeof value === "string" && value.length <= 256 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(value) ? value : void 0,
8850
+ owner: (value) => typeof value === "string" && /^did:pkh:eip155:[1-9][0-9]*:0x[0-9a-fA-F]{40}$/.test(value) ? value : void 0
8253
8851
  },
8254
8852
  OPERATION_VERSION_UNSUPPORTED: {
8255
8853
  supportedVersions: (value) => Array.isArray(value) && value.every((item) => Number.isInteger(item)) ? value : void 0
@@ -8331,7 +8929,7 @@ async function invokeOperation(operationId, operationVersion, invocationTarget,
8331
8929
  operationError("INPUT_INVALID", "The invocation target is invalid.")
8332
8930
  );
8333
8931
  }
8334
- return withTinyCloudStateRoot(target.stateRoot, async () => {
8932
+ const run = withTinyCloudStateRoot(target.stateRoot, async () => {
8335
8933
  const prepared = await prepareInvocation(
8336
8934
  operationId,
8337
8935
  operationVersion,
@@ -8341,6 +8939,33 @@ async function invokeOperation(operationId, operationVersion, invocationTarget,
8341
8939
  if (isPreparedError(prepared)) return prepared.result;
8342
8940
  return withRuntimeWarnings(await executePreparedInvocation(prepared), prepared);
8343
8941
  });
8942
+ const { lookupOperation: lookupOperation2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
8943
+ const lookup = lookupOperation2(operationId, operationVersion);
8944
+ const deadlineMs = lookup.status === "found" ? lookup.definition.deadlineMs : void 0;
8945
+ if (deadlineMs === void 0) return run;
8946
+ return withResponseDeadline(run, deadlineMs, () => errorResult(
8947
+ { operationId, operationVersion },
8948
+ unresolvedContextSummary(target),
8949
+ operationError(
8950
+ "NODE_UNREACHABLE",
8951
+ `The operation did not finish within ${deadlineMs / 1e3} s.`,
8952
+ { retryable: true }
8953
+ )
8954
+ ));
8955
+ }
8956
+ async function withResponseDeadline(run, deadlineMs, expired) {
8957
+ run.catch(() => {
8958
+ });
8959
+ let timer;
8960
+ const deadline = new Promise((resolve2) => {
8961
+ timer = setTimeout(() => resolve2(expired()), deadlineMs);
8962
+ timer.unref();
8963
+ });
8964
+ try {
8965
+ return await Promise.race([run, deadline]);
8966
+ } finally {
8967
+ clearTimeout(timer);
8968
+ }
8344
8969
  }
8345
8970
  function withRuntimeWarnings(result, prepared) {
8346
8971
  const warnings = prepared.runtimeResolution.context.runtime?.warnings;
@@ -8415,7 +9040,7 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
8415
9040
  )
8416
9041
  ) };
8417
9042
  }
8418
- if (target.privateKey === void 0 && definition.runtime === "authenticated" && isOwnerPosture(inspectionResolution.context.summary.posture) && target.allowOwnerProfile !== true) {
9043
+ if (target.privateKey === void 0 && requiresRuntime(definition.runtime) && isOwnerPosture(inspectionResolution.context.summary.posture) && target.allowOwnerProfile !== true) {
8419
9044
  return { status: "error", result: errorResult(
8420
9045
  operation,
8421
9046
  inspectionResolution.context.summary,
@@ -8425,15 +9050,15 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
8425
9050
  )
8426
9051
  ) };
8427
9052
  }
8428
- const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target, "authenticated");
9053
+ const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target, definition.runtime);
8429
9054
  if (!runtimeResolution.ok) {
8430
9055
  return { status: "error", result: errorResult(
8431
9056
  operation,
8432
9057
  runtimeResolution.context,
8433
- runtimeResolution.error
9058
+ redactOperationError(definition, runtimeResolution.error)
8434
9059
  ) };
8435
9060
  }
8436
- if (definition.runtime === "authenticated") {
9061
+ if (requiresRuntime(definition.runtime)) {
8437
9062
  const actualPosture = runtimeResolution.context.summary.posture;
8438
9063
  if (!definition.postures.includes(actualPosture)) {
8439
9064
  return { status: "error", result: errorResult(
@@ -8820,6 +9445,15 @@ function exactCapabilities2(value) {
8820
9445
  function isOwnerPosture(posture) {
8821
9446
  return posture === "owner-openkey" || posture === "local-owner-key";
8822
9447
  }
9448
+ function requiresRuntime(requirement) {
9449
+ switch (requirement) {
9450
+ case "authenticated":
9451
+ case "restored":
9452
+ return true;
9453
+ case "inspection":
9454
+ return false;
9455
+ }
9456
+ }
8823
9457
  function zodIssueDetails(error) {
8824
9458
  return {
8825
9459
  // The canonical error channel carries only schema-owned issue codes.