@tinycloud/operations 0.4.0-beta.11 → 0.4.0-beta.13

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.
@@ -1,4 +1,4 @@
1
- import { I as InvocationTarget, a as OperationResult } from './contract-eWtDSMeI.cjs';
1
+ import { I as InvocationTarget, a as OperationResult } from './contract-BRwWJpPs.cjs';
2
2
 
3
3
  /**
4
4
  * The only published CLI acquisition helper. Its fixed operation identity is
@@ -1,4 +1,4 @@
1
- import { I as InvocationTarget, a as OperationResult } from './contract-eWtDSMeI.js';
1
+ import { I as InvocationTarget, a as OperationResult } from './contract-BRwWJpPs.js';
2
2
 
3
3
  /**
4
4
  * The only published CLI acquisition helper. Its fixed operation identity is
@@ -355,8 +355,13 @@ function canonicalizeRecapCaveats(caveats) {
355
355
  }
356
356
  return `[${branches.join(",")}]`;
357
357
  }
358
+ function isExplicitOnlyAction(action) {
359
+ return Object.prototype.hasOwnProperty.call(EXPLICIT_ONLY_ACTIONS, action);
360
+ }
358
361
  function actionContains(grantedAction, requestedAction) {
359
- if (grantedAction === "*" || grantedAction === requestedAction) return true;
362
+ if (grantedAction === requestedAction) return true;
363
+ if (isExplicitOnlyAction(requestedAction)) return false;
364
+ if (grantedAction === "*") return true;
360
365
  if (!grantedAction.endsWith("/*")) return false;
361
366
  const service = grantedAction.slice(0, -2);
362
367
  return requestedAction === service || requestedAction.startsWith(`${service}/`);
@@ -381,10 +386,15 @@ function cloneEntry(entry) {
381
386
  ...entry.expiry !== void 0 ? { expiry: entry.expiry } : {}
382
387
  };
383
388
  }
389
+ var EXPLICIT_ONLY_ACTIONS;
384
390
  var init_capabilities = __esm({
385
391
  "../sdk-core/src/capabilities.ts"() {
386
392
  "use strict";
387
393
  init_manifest();
394
+ EXPLICIT_ONLY_ACTIONS = {
395
+ "tinycloud.kv/sync": true,
396
+ "tinycloud.kv/retain": true
397
+ };
388
398
  }
389
399
  });
390
400
 
@@ -3626,6 +3636,32 @@ ORDER BY type, name`;
3626
3636
  }
3627
3637
  });
3628
3638
 
3639
+ // src/contract.ts
3640
+ var STORED_GRANT_SKIP_REASONS, DELEGATION_CID_PATTERN;
3641
+ var init_contract = __esm({
3642
+ "src/contract.ts"() {
3643
+ "use strict";
3644
+ STORED_GRANT_SKIP_REASONS = [
3645
+ "malformed",
3646
+ "unbound",
3647
+ "outside_request",
3648
+ "host_mismatch",
3649
+ "expired",
3650
+ "cid_mismatch",
3651
+ "authority_invalid",
3652
+ "proof_missing",
3653
+ "proof_invalid",
3654
+ "owner_mismatch",
3655
+ "audience_mismatch",
3656
+ "resources_mismatch",
3657
+ "unsupported",
3658
+ "activation_rejected",
3659
+ "invalid"
3660
+ ];
3661
+ DELEGATION_CID_PATTERN = "^bafkr4i[a-z2-7]{52}$";
3662
+ }
3663
+ });
3664
+
3629
3665
  // src/delegation-binding.ts
3630
3666
  import { join as join2 } from "path";
3631
3667
  import * as nodeSdk3 from "@tinycloud/node-sdk";
@@ -3651,12 +3687,12 @@ function replayLimit(entry, migrated) {
3651
3687
  if (binding.success) return binding.data.requested;
3652
3688
  return !migrated && kind === "compact" && !("authorityRequest" in entry) ? "signed" : void 0;
3653
3689
  }
3654
- function bindToSignedCapabilities(record, signed, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
3690
+ function bindToRequest(record, requested, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
3655
3691
  return {
3656
3692
  ...record,
3657
3693
  authorityRequest: DelegationRequestBindingSchema.parse({
3658
3694
  requestId: `${REQUEST_ID_PREFIX[source]}:${record.delegation.cid}`,
3659
- requested: structuredClone(signed)
3695
+ requested: structuredClone(requested)
3660
3696
  }),
3661
3697
  authorityRequestAudit: { source, recordedAt, note: BINDING_NOTES[source] }
3662
3698
  };
@@ -3712,23 +3748,40 @@ async function migratedRecord(node, entry, host, recordedAt) {
3712
3748
  const signed = await signedCapabilities(node, delegation, host);
3713
3749
  if (signed === void 0) return void 0;
3714
3750
  try {
3715
- return bindToSignedCapabilities({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
3751
+ return bindToRequest({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
3716
3752
  } catch {
3717
3753
  return void 0;
3718
3754
  }
3719
3755
  }
3756
+ function skipped(reason) {
3757
+ return { status: "skipped", reason };
3758
+ }
3720
3759
  async function replayStoredDelegation(node, entry, options) {
3760
+ const kind = storedDelegationKind(entry);
3721
3761
  const delegation = normalizeStoredDelegation(entry);
3722
- if (delegation === void 0 || delegation.expiry.getTime() <= Date.now()) return void 0;
3762
+ if (kind === "refused" || delegation === void 0) return skipped("malformed");
3763
+ if (delegation.expiry.getTime() <= Date.now()) return NOT_REPLAYED;
3764
+ if (kind === "other") return skipped("proof_missing");
3723
3765
  const limit = replayLimit(entry, options.migrated);
3724
- if (limit === void 0) return void 0;
3725
- const authorize = limit === "signed" ? () => true : (effective) => delegationWithinRequest(limit, effective, options.resolveSpace);
3766
+ if (limit === void 0) return skipped("unbound");
3767
+ let outsideRequest = false;
3768
+ const authorize = limit === "signed" ? () => true : (effective) => {
3769
+ outsideRequest = !delegationWithinRequest(limit, effective, options.resolveSpace);
3770
+ return !outsideRequest;
3771
+ };
3726
3772
  try {
3727
- return await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize });
3728
- } catch {
3729
- return void 0;
3773
+ return {
3774
+ status: "installed",
3775
+ delegation: await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize })
3776
+ };
3777
+ } catch (error) {
3778
+ return skipped(outsideRequest ? "outside_request" : rejectionReason(error));
3730
3779
  }
3731
3780
  }
3781
+ function rejectionReason(error) {
3782
+ const reason = isRecord4(error) ? error.reason : void 0;
3783
+ return typeof reason === "string" && STORED_GRANT_SKIP_REASONS.includes(reason) ? reason : "invalid";
3784
+ }
3732
3785
  async function bindingMigrationRecorded(profile) {
3733
3786
  try {
3734
3787
  return await readJson(bindingMigrationPath(profile)) !== null;
@@ -3773,23 +3826,27 @@ function recordKey(entry) {
3773
3826
  function isRecord4(value) {
3774
3827
  return typeof value === "object" && value !== null && !Array.isArray(value);
3775
3828
  }
3776
- var REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS;
3829
+ var REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS, NOT_REPLAYED;
3777
3830
  var init_delegation_binding = __esm({
3778
3831
  "src/delegation-binding.ts"() {
3779
3832
  "use strict";
3780
3833
  init_artifacts();
3781
3834
  init_authority();
3835
+ init_contract();
3782
3836
  init_state();
3783
3837
  init_secrets();
3784
3838
  REQUEST_ID_PREFIX = {
3785
3839
  migration: "migrated",
3786
- "unbound-import": "unbound-import"
3840
+ "unbound-import": "unbound-import",
3841
+ "cli-grant": "cli-grant"
3787
3842
  };
3788
3843
  BINDING_NOTES = {
3789
3844
  migration: "Stored without a request binding before bindings were required. Bound at migration to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them.",
3790
- "unbound-import": "Imported by `tc auth import` without a stored request. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them."
3845
+ "unbound-import": "Imported by `tc auth import` without a stored request. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them.",
3846
+ "cli-grant": "Granted to this CLI by `tc auth request --grant` or a permission escalation, and checked against the request it answered. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them."
3791
3847
  };
3792
3848
  MIGRATION_LOCK_TIMEOUT_MS = 250;
3849
+ NOT_REPLAYED = { status: "not-replayed" };
3793
3850
  }
3794
3851
  });
3795
3852
 
@@ -4202,7 +4259,18 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4202
4259
  ));
4203
4260
  }
4204
4261
  } else {
4205
- await node.restoreSession(activeSession);
4262
+ try {
4263
+ await node.restoreSession(activeSession);
4264
+ } catch (error) {
4265
+ if (isRecord7(error) && error.code === "AUTH_EXPIRED") {
4266
+ return failed(summary, operationError(
4267
+ "AUTH_REQUIRED",
4268
+ "The stored session has expired or is no longer valid. Sign in again.",
4269
+ { retryable: false }
4270
+ ));
4271
+ }
4272
+ throw error;
4273
+ }
4206
4274
  }
4207
4275
  const activeSessionDid = normalizeDid(node.sessionDid);
4208
4276
  const livePrincipalDid = normalizeDid(node.did);
@@ -4210,6 +4278,7 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4210
4278
  const livePermissions = [...node.getVerifiedSessionCapabilities()];
4211
4279
  const resolveSpace2 = operationSpaceResolver(node, authenticatedSpace ?? summary.space);
4212
4280
  const seenCids = /* @__PURE__ */ new Set();
4281
+ const warnings = [];
4213
4282
  if (!explicitPrivateKeyOverride) {
4214
4283
  const activator = node;
4215
4284
  const migrated = await prepareStoredDelegationReplay(profileName, activator, {
@@ -4219,20 +4288,25 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4219
4288
  migrate: activeSession !== void 0
4220
4289
  });
4221
4290
  for (const entry of await readAdditionalDelegations(profileName)) {
4222
- const activated = await replayStoredDelegation(activator, entry, {
4291
+ const replay = await replayStoredDelegation(activator, entry, {
4223
4292
  host: summary.host,
4224
4293
  migrated,
4225
4294
  resolveSpace: resolveSpace2
4226
4295
  });
4227
- if (activated !== void 0 && !seenCids.has(activated.cid)) {
4228
- seenCids.add(activated.cid);
4229
- livePermissions.push(...activated.effectivePermissions);
4296
+ if (replay.status === "installed") {
4297
+ if (!seenCids.has(replay.delegation.cid)) {
4298
+ seenCids.add(replay.delegation.cid);
4299
+ livePermissions.push(...replay.delegation.effectivePermissions);
4300
+ }
4301
+ } else if (replay.status === "skipped") {
4302
+ warnings.push(skippedGrantWarning(node, entry, replay.reason));
4230
4303
  }
4231
4304
  }
4232
4305
  }
4233
4306
  const runtime = {
4234
4307
  node,
4235
- granted: canonicalizeCapabilities(livePermissions)
4308
+ granted: canonicalizeCapabilities(livePermissions),
4309
+ ...warnings.length === 0 ? {} : { warnings }
4236
4310
  };
4237
4311
  return {
4238
4312
  ok: true,
@@ -4256,6 +4330,24 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4256
4330
  ));
4257
4331
  }
4258
4332
  }
4333
+ function skippedGrantWarning(node, entry, reason) {
4334
+ const grantCid = verifiedStoredCid(node, entry);
4335
+ return { code: "STORED_GRANT_SKIPPED", reason, ...grantCid === void 0 ? {} : { grantCid } };
4336
+ }
4337
+ function verifiedStoredCid(node, entry) {
4338
+ const delegation = isRecord7(entry) && isRecord7(entry.delegation) ? entry.delegation : void 0;
4339
+ const header = isRecord7(delegation?.delegationHeader) ? delegation.delegationHeader : void 0;
4340
+ const cid = delegation?.cid;
4341
+ const computeDelegationCid = isRecord7(node) ? node.computeDelegationCid : void 0;
4342
+ if (typeof cid !== "string" || typeof header?.Authorization !== "string" || typeof computeDelegationCid !== "function") {
4343
+ return void 0;
4344
+ }
4345
+ try {
4346
+ return computeDelegationCid.call(node, header.Authorization) === cid && DELEGATION_CID.test(cid) ? cid : void 0;
4347
+ } catch {
4348
+ return void 0;
4349
+ }
4350
+ }
4259
4351
  function spaceForAuthenticatedPrincipal(principal2) {
4260
4352
  if (principal2 === void 0) return void 0;
4261
4353
  try {
@@ -4304,15 +4396,18 @@ function unresolvedSummary(target) {
4304
4396
  posture: "unauthenticated"
4305
4397
  };
4306
4398
  }
4399
+ var DELEGATION_CID;
4307
4400
  var init_runtime = __esm({
4308
4401
  "src/runtime.ts"() {
4309
4402
  "use strict";
4403
+ init_contract();
4310
4404
  init_authority();
4311
4405
  init_errors();
4312
4406
  init_profile();
4313
4407
  init_secrets();
4314
4408
  init_state();
4315
4409
  init_delegation_binding();
4410
+ DELEGATION_CID = new RegExp(DELEGATION_CID_PATTERN);
4316
4411
  }
4317
4412
  });
4318
4413
 
@@ -4712,6 +4807,9 @@ async function invokeLocalAuthorityRetry(operationId, operationVersion, target,
4712
4807
  unknownInput
4713
4808
  );
4714
4809
  if (isPreparedError(prepared)) return prepared.result;
4810
+ return withRuntimeWarnings(await retryWithLocalAuthority(prepared), prepared);
4811
+ }
4812
+ async function retryWithLocalAuthority(prepared) {
4715
4813
  if (prepared.operation.operationId !== "tinycloud.secrets.get" || prepared.operation.operationVersion !== 1) {
4716
4814
  return errorResult(
4717
4815
  prepared.operation,
@@ -4810,6 +4908,10 @@ async function invokeLocalAuthorityRetry(operationId, operationVersion, target,
4810
4908
  return localAuthorityFailure(prepared);
4811
4909
  }
4812
4910
  }
4911
+ function withRuntimeWarnings(result, prepared) {
4912
+ const warnings = prepared.runtimeResolution.context.runtime?.warnings;
4913
+ return warnings === void 0 || warnings.length === 0 ? result : { ...result, warnings: [...warnings] };
4914
+ }
4813
4915
  function isPreparedError(prepared) {
4814
4916
  return "status" in prepared;
4815
4917
  }