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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3615,6 +3615,32 @@ ORDER BY type, name`;
3615
3615
  }
3616
3616
  });
3617
3617
 
3618
+ // src/contract.ts
3619
+ var STORED_GRANT_SKIP_REASONS, DELEGATION_CID_PATTERN;
3620
+ var init_contract = __esm({
3621
+ "src/contract.ts"() {
3622
+ "use strict";
3623
+ STORED_GRANT_SKIP_REASONS = [
3624
+ "malformed",
3625
+ "unbound",
3626
+ "outside_request",
3627
+ "host_mismatch",
3628
+ "expired",
3629
+ "cid_mismatch",
3630
+ "authority_invalid",
3631
+ "proof_missing",
3632
+ "proof_invalid",
3633
+ "owner_mismatch",
3634
+ "audience_mismatch",
3635
+ "resources_mismatch",
3636
+ "unsupported",
3637
+ "activation_rejected",
3638
+ "invalid"
3639
+ ];
3640
+ DELEGATION_CID_PATTERN = "^bafkr4i[a-z2-7]{52}$";
3641
+ }
3642
+ });
3643
+
3618
3644
  // src/delegation-binding.ts
3619
3645
  function bindingMigrationPath(profile) {
3620
3646
  return (0, import_node_path2.join)(profilePath(profile), "delegation-binding-migration.json");
@@ -3638,12 +3664,12 @@ function replayLimit(entry, migrated) {
3638
3664
  if (binding.success) return binding.data.requested;
3639
3665
  return !migrated && kind === "compact" && !("authorityRequest" in entry) ? "signed" : void 0;
3640
3666
  }
3641
- function bindToSignedCapabilities(record, signed, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
3667
+ function bindToRequest(record, requested, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
3642
3668
  return {
3643
3669
  ...record,
3644
3670
  authorityRequest: DelegationRequestBindingSchema.parse({
3645
3671
  requestId: `${REQUEST_ID_PREFIX[source]}:${record.delegation.cid}`,
3646
- requested: structuredClone(signed)
3672
+ requested: structuredClone(requested)
3647
3673
  }),
3648
3674
  authorityRequestAudit: { source, recordedAt, note: BINDING_NOTES[source] }
3649
3675
  };
@@ -3699,23 +3725,40 @@ async function migratedRecord(node, entry, host, recordedAt) {
3699
3725
  const signed = await signedCapabilities(node, delegation, host);
3700
3726
  if (signed === void 0) return void 0;
3701
3727
  try {
3702
- return bindToSignedCapabilities({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
3728
+ return bindToRequest({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
3703
3729
  } catch {
3704
3730
  return void 0;
3705
3731
  }
3706
3732
  }
3733
+ function skipped(reason) {
3734
+ return { status: "skipped", reason };
3735
+ }
3707
3736
  async function replayStoredDelegation(node, entry, options) {
3737
+ const kind = storedDelegationKind(entry);
3708
3738
  const delegation = normalizeStoredDelegation(entry);
3709
- if (delegation === void 0 || delegation.expiry.getTime() <= Date.now()) return void 0;
3739
+ if (kind === "refused" || delegation === void 0) return skipped("malformed");
3740
+ if (delegation.expiry.getTime() <= Date.now()) return NOT_REPLAYED;
3741
+ if (kind === "other") return skipped("proof_missing");
3710
3742
  const limit = replayLimit(entry, options.migrated);
3711
- if (limit === void 0) return void 0;
3712
- const authorize = limit === "signed" ? () => true : (effective) => delegationWithinRequest(limit, effective, options.resolveSpace);
3743
+ if (limit === void 0) return skipped("unbound");
3744
+ let outsideRequest = false;
3745
+ const authorize = limit === "signed" ? () => true : (effective) => {
3746
+ outsideRequest = !delegationWithinRequest(limit, effective, options.resolveSpace);
3747
+ return !outsideRequest;
3748
+ };
3713
3749
  try {
3714
- return await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize });
3715
- } catch {
3716
- return void 0;
3750
+ return {
3751
+ status: "installed",
3752
+ delegation: await nodeSdk3.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize })
3753
+ };
3754
+ } catch (error) {
3755
+ return skipped(outsideRequest ? "outside_request" : rejectionReason(error));
3717
3756
  }
3718
3757
  }
3758
+ function rejectionReason(error) {
3759
+ const reason = isRecord4(error) ? error.reason : void 0;
3760
+ return typeof reason === "string" && STORED_GRANT_SKIP_REASONS.includes(reason) ? reason : "invalid";
3761
+ }
3719
3762
  async function bindingMigrationRecorded(profile) {
3720
3763
  try {
3721
3764
  return await readJson(bindingMigrationPath(profile)) !== null;
@@ -3760,7 +3803,7 @@ function recordKey(entry) {
3760
3803
  function isRecord4(value) {
3761
3804
  return typeof value === "object" && value !== null && !Array.isArray(value);
3762
3805
  }
3763
- var import_node_path2, nodeSdk3, REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS;
3806
+ var import_node_path2, nodeSdk3, REQUEST_ID_PREFIX, BINDING_NOTES, MIGRATION_LOCK_TIMEOUT_MS, NOT_REPLAYED;
3764
3807
  var init_delegation_binding = __esm({
3765
3808
  "src/delegation-binding.ts"() {
3766
3809
  "use strict";
@@ -3768,17 +3811,21 @@ var init_delegation_binding = __esm({
3768
3811
  nodeSdk3 = __toESM(require("@tinycloud/node-sdk"), 1);
3769
3812
  init_artifacts();
3770
3813
  init_authority();
3814
+ init_contract();
3771
3815
  init_state();
3772
3816
  init_secrets();
3773
3817
  REQUEST_ID_PREFIX = {
3774
3818
  migration: "migrated",
3775
- "unbound-import": "unbound-import"
3819
+ "unbound-import": "unbound-import",
3820
+ "cli-grant": "cli-grant"
3776
3821
  };
3777
3822
  BINDING_NOTES = {
3778
3823
  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.",
3779
- "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."
3824
+ "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.",
3825
+ "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."
3780
3826
  };
3781
3827
  MIGRATION_LOCK_TIMEOUT_MS = 250;
3828
+ NOT_REPLAYED = { status: "not-replayed" };
3782
3829
  }
3783
3830
  });
3784
3831
 
@@ -4190,7 +4237,18 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4190
4237
  ));
4191
4238
  }
4192
4239
  } else {
4193
- await node.restoreSession(activeSession);
4240
+ try {
4241
+ await node.restoreSession(activeSession);
4242
+ } catch (error) {
4243
+ if (isRecord7(error) && error.code === "AUTH_EXPIRED") {
4244
+ return failed(summary, operationError(
4245
+ "AUTH_REQUIRED",
4246
+ "The stored session has expired or is no longer valid. Sign in again.",
4247
+ { retryable: false }
4248
+ ));
4249
+ }
4250
+ throw error;
4251
+ }
4194
4252
  }
4195
4253
  const activeSessionDid = normalizeDid(node.sessionDid);
4196
4254
  const livePrincipalDid = normalizeDid(node.did);
@@ -4198,6 +4256,7 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4198
4256
  const livePermissions = [...node.getVerifiedSessionCapabilities()];
4199
4257
  const resolveSpace2 = operationSpaceResolver(node, authenticatedSpace ?? summary.space);
4200
4258
  const seenCids = /* @__PURE__ */ new Set();
4259
+ const warnings = [];
4201
4260
  if (!explicitPrivateKeyOverride) {
4202
4261
  const activator = node;
4203
4262
  const migrated = await prepareStoredDelegationReplay(profileName, activator, {
@@ -4207,20 +4266,25 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4207
4266
  migrate: activeSession !== void 0
4208
4267
  });
4209
4268
  for (const entry of await readAdditionalDelegations(profileName)) {
4210
- const activated = await replayStoredDelegation(activator, entry, {
4269
+ const replay = await replayStoredDelegation(activator, entry, {
4211
4270
  host: summary.host,
4212
4271
  migrated,
4213
4272
  resolveSpace: resolveSpace2
4214
4273
  });
4215
- if (activated !== void 0 && !seenCids.has(activated.cid)) {
4216
- seenCids.add(activated.cid);
4217
- livePermissions.push(...activated.effectivePermissions);
4274
+ if (replay.status === "installed") {
4275
+ if (!seenCids.has(replay.delegation.cid)) {
4276
+ seenCids.add(replay.delegation.cid);
4277
+ livePermissions.push(...replay.delegation.effectivePermissions);
4278
+ }
4279
+ } else if (replay.status === "skipped") {
4280
+ warnings.push(skippedGrantWarning(node, entry, replay.reason));
4218
4281
  }
4219
4282
  }
4220
4283
  }
4221
4284
  const runtime = {
4222
4285
  node,
4223
- granted: canonicalizeCapabilities(livePermissions)
4286
+ granted: canonicalizeCapabilities(livePermissions),
4287
+ ...warnings.length === 0 ? {} : { warnings }
4224
4288
  };
4225
4289
  return {
4226
4290
  ok: true,
@@ -4244,6 +4308,24 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
4244
4308
  ));
4245
4309
  }
4246
4310
  }
4311
+ function skippedGrantWarning(node, entry, reason) {
4312
+ const grantCid = verifiedStoredCid(node, entry);
4313
+ return { code: "STORED_GRANT_SKIPPED", reason, ...grantCid === void 0 ? {} : { grantCid } };
4314
+ }
4315
+ function verifiedStoredCid(node, entry) {
4316
+ const delegation = isRecord7(entry) && isRecord7(entry.delegation) ? entry.delegation : void 0;
4317
+ const header = isRecord7(delegation?.delegationHeader) ? delegation.delegationHeader : void 0;
4318
+ const cid = delegation?.cid;
4319
+ const computeDelegationCid = isRecord7(node) ? node.computeDelegationCid : void 0;
4320
+ if (typeof cid !== "string" || typeof header?.Authorization !== "string" || typeof computeDelegationCid !== "function") {
4321
+ return void 0;
4322
+ }
4323
+ try {
4324
+ return computeDelegationCid.call(node, header.Authorization) === cid && DELEGATION_CID.test(cid) ? cid : void 0;
4325
+ } catch {
4326
+ return void 0;
4327
+ }
4328
+ }
4247
4329
  function spaceForAuthenticatedPrincipal(principal2) {
4248
4330
  if (principal2 === void 0) return void 0;
4249
4331
  try {
@@ -4292,17 +4374,19 @@ function unresolvedSummary(target) {
4292
4374
  posture: "unauthenticated"
4293
4375
  };
4294
4376
  }
4295
- var nodeSdk4;
4377
+ var nodeSdk4, DELEGATION_CID;
4296
4378
  var init_runtime = __esm({
4297
4379
  "src/runtime.ts"() {
4298
4380
  "use strict";
4299
4381
  nodeSdk4 = __toESM(require("@tinycloud/node-sdk"), 1);
4382
+ init_contract();
4300
4383
  init_authority();
4301
4384
  init_errors();
4302
4385
  init_profile();
4303
4386
  init_secrets();
4304
4387
  init_state();
4305
4388
  init_delegation_binding();
4389
+ DELEGATION_CID = new RegExp(DELEGATION_CID_PATTERN);
4306
4390
  }
4307
4391
  });
4308
4392
 
@@ -4685,9 +4769,13 @@ async function invokeOperation(operationId, operationVersion, invocationTarget,
4685
4769
  unknownInput
4686
4770
  );
4687
4771
  if (isPreparedError(prepared)) return prepared.result;
4688
- return executePreparedInvocation(prepared);
4772
+ return withRuntimeWarnings(await executePreparedInvocation(prepared), prepared);
4689
4773
  });
4690
4774
  }
4775
+ function withRuntimeWarnings(result, prepared) {
4776
+ const warnings = prepared.runtimeResolution.context.runtime?.warnings;
4777
+ return warnings === void 0 || warnings.length === 0 ? result : { ...result, warnings: [...warnings] };
4778
+ }
4691
4779
  function isPreparedError(prepared) {
4692
4780
  return "status" in prepared;
4693
4781
  }