@tinycloud/sdk-core 3.1.0-beta.8 → 3.1.0

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
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  ACCOUNT_MANIFEST_PERMISSIONS: () => import_bootstrap4.ACCOUNT_MANIFEST_PERMISSIONS,
44
44
  ACCOUNT_REGISTRY_PATH: () => ACCOUNT_REGISTRY_PATH,
45
45
  ACCOUNT_REGISTRY_SPACE: () => ACCOUNT_REGISTRY_SPACE,
46
+ AGENTS_SPACE: () => import_bootstrap4.AGENTS_SPACE,
46
47
  ATTESTED_ENFORCER_BINDING_V2_SCHEMA: () => ATTESTED_ENFORCER_BINDING_V2_SCHEMA,
47
48
  AccountService: () => AccountService,
48
49
  AutoApproveSpaceCreationHandler: () => AutoApproveSpaceCreationHandler,
@@ -215,6 +216,7 @@ __export(index_exports, {
215
216
  SpaceErrorCodes: () => SpaceErrorCodes,
216
217
  SpaceService: () => SpaceService,
217
218
  TINYCLOUD_ACCOUNT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_ACCOUNT_SPACE_MANIFEST,
219
+ TINYCLOUD_AGENTS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_AGENTS_SPACE_MANIFEST,
218
220
  TINYCLOUD_APPLICATIONS_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_APPLICATIONS_SPACE_MANIFEST,
219
221
  TINYCLOUD_CANONICAL_IDENTITY_CLAIM: () => TINYCLOUD_CANONICAL_IDENTITY_CLAIM,
220
222
  TINYCLOUD_DEFAULT_SPACE_MANIFEST: () => import_bootstrap4.TINYCLOUD_DEFAULT_SPACE_MANIFEST,
@@ -359,6 +361,7 @@ __export(index_exports, {
359
361
  isCapabilitySubset: () => isCapabilitySubset,
360
362
  isDigest: () => isDigest,
361
363
  isEvmAddress: () => isEvmAddress,
364
+ isExplicitOnlyAction: () => isExplicitOnlyAction,
362
365
  isNetworkId: () => import_sdk_services9.isNetworkId,
363
366
  isPlausibleOpenKeyActionId: () => isPlausibleOpenKeyActionId,
364
367
  isStorageFullError: () => import_sdk_services9.isStorageFullError,
@@ -397,6 +400,7 @@ __export(index_exports, {
397
400
  parseSecretCatalogKey: () => import_sdk_services9.parseSecretCatalogKey,
398
401
  parseShareArtifact: () => parseShareArtifact,
399
402
  parseShareUrl: () => parseShareUrl,
403
+ parseSignedCompactUcanAttenuation: () => parseSignedCompactUcanAttenuation,
400
404
  parseSpaceUri: () => parseSpaceUri,
401
405
  pkhDid: () => pkhDid,
402
406
  policyCidFromCanonicalBytes: () => policyCidFromCanonicalBytes,
@@ -4679,6 +4683,32 @@ function parseCompactUcanAuthorization(input, expectedCid) {
4679
4683
  throw new Error("compact UCAN CID does not match exact Authorization bytes");
4680
4684
  return { authorization: input, cid: cid2, header, payload };
4681
4685
  }
4686
+ var SignedCompactAttenuationSchema = import_zod2.z.record(import_zod2.z.record(import_zod2.z.array(import_zod2.z.unknown()).min(1)));
4687
+ function parseSignedCompactUcanAttenuation(input, expectedCid) {
4688
+ if (typeof input !== "string" || /\s/.test(input))
4689
+ throw new Error("Authorization must be compact UCAN bytes");
4690
+ const segments = input.split(".");
4691
+ if (segments.length !== 3 || segments.some((segment) => segment.length === 0))
4692
+ throw new Error("Authorization must have three compact segments");
4693
+ const [headerSegment, payloadSegment, signatureSegment] = segments;
4694
+ const headerValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(decodeBase64Url2(headerSegment)));
4695
+ const payloadValue = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(decodeBase64Url2(payloadSegment)));
4696
+ if (!isRecordValue(headerValue) || !isRecordValue(headerValue.jwk) || headerValue.alg !== "EdDSA" || headerValue.typ !== "JWT" || headerValue.ucv !== "0.10.0" || headerValue.jwk.kty !== "OKP" || headerValue.jwk.crv !== "Ed25519" || !isRecordValue(payloadValue) || typeof payloadValue.iss !== "string")
4697
+ throw new Error("Authorization has an invalid signed UCAN header or issuer");
4698
+ const att = SignedCompactAttenuationSchema.parse(payloadValue.att);
4699
+ const principal = payloadValue.iss.split("#", 1)[0];
4700
+ const didMaterial = principal.startsWith("did:key:") ? base58btc.decode(principal.slice("did:key:".length)) : new Uint8Array();
4701
+ const publicKey = didMaterial.length === 34 && didMaterial[0] === 237 && didMaterial[1] === 1 ? didMaterial.slice(2) : new Uint8Array();
4702
+ const jwkKey = typeof headerValue.jwk.x === "string" ? decodeBase64Url2(headerValue.jwk.x) : new Uint8Array();
4703
+ if (publicKey.length !== 32 || !equalBytes(publicKey, jwkKey))
4704
+ throw new Error("compact UCAN JWK does not bind issuer");
4705
+ const signature = decodeBase64Url2(signatureSegment);
4706
+ if (!import_ed255193.ed25519.verify(signature, new TextEncoder().encode(`${headerSegment}.${payloadSegment}`), publicKey))
4707
+ throw new Error("compact UCAN signature is invalid");
4708
+ const cid2 = CID.createV1(85, create(30, (0, import_blake3.blake3)(new TextEncoder().encode(input)))).toString();
4709
+ if (expectedCid !== void 0 && cid2 !== expectedCid) throw new Error("compact UCAN CID does not match exact Authorization bytes");
4710
+ return { cid: cid2, payload: { att } };
4711
+ }
4682
4712
  function parsePolicySessionUcan(authorization, expectedProofs) {
4683
4713
  const parsed = parseCompactUcanAuthorization(authorization);
4684
4714
  const fact = parsed.payload.fct[0];
@@ -7166,7 +7196,7 @@ var DelegationManager = class {
7166
7196
  * }
7167
7197
  * ```
7168
7198
  */
7169
- async revoke(cid2) {
7199
+ async revoke(cid2, options2) {
7170
7200
  if (!cid2) {
7171
7201
  return {
7172
7202
  ok: false,
@@ -7186,15 +7216,20 @@ var DelegationManager = class {
7186
7216
  )
7187
7217
  };
7188
7218
  }
7189
- const headers = this.invokeAny(
7190
- this.session,
7191
- [{
7192
- resource: `urn:cid:${cid2}`,
7193
- service: "delegation",
7194
- path: "",
7195
- action: DelegationAction.REVOKE
7196
- }]
7197
- );
7219
+ const entry = {
7220
+ resource: `urn:cid:${cid2}`,
7221
+ ...options2?.targetSpaceId === void 0 ? {} : { spaceId: options2.targetSpaceId },
7222
+ service: "delegation",
7223
+ path: "",
7224
+ action: DelegationAction.REVOKE
7225
+ };
7226
+ if (options2?.authorityCid !== void 0) {
7227
+ Object.defineProperty(entry, "revokeAuthorityCid", {
7228
+ value: options2.authorityCid,
7229
+ enumerable: false
7230
+ });
7231
+ }
7232
+ const headers = this.invokeAny(this.session, [entry]);
7198
7233
  const response = await this.fetchFn(`${this.host}/revoke`, {
7199
7234
  method: "POST",
7200
7235
  headers
@@ -7352,7 +7387,8 @@ var DelegationManager = class {
7352
7387
  )
7353
7388
  };
7354
7389
  }
7355
- if (!this.accountSpaceId) {
7390
+ const accountSpaceId = this.accountSpaceId;
7391
+ if (!accountSpaceId) {
7356
7392
  return {
7357
7393
  ok: false,
7358
7394
  error: createError(
@@ -7363,7 +7399,7 @@ var DelegationManager = class {
7363
7399
  }
7364
7400
  try {
7365
7401
  const invocationHeaders = this.invokeAny(this.session, [{
7366
- spaceId: this.accountSpaceId,
7402
+ spaceId: accountSpaceId,
7367
7403
  service: "delegation",
7368
7404
  path: "",
7369
7405
  action: DelegationAction.LIST
@@ -8661,8 +8697,17 @@ function canonicalizeRecapCaveats(caveats) {
8661
8697
  }
8662
8698
  return `[${branches.join(",")}]`;
8663
8699
  }
8700
+ var EXPLICIT_ONLY_ACTIONS = {
8701
+ "tinycloud.kv/sync": true,
8702
+ "tinycloud.kv/retain": true
8703
+ };
8704
+ function isExplicitOnlyAction(action) {
8705
+ return Object.prototype.hasOwnProperty.call(EXPLICIT_ONLY_ACTIONS, action);
8706
+ }
8664
8707
  function actionContains(grantedAction, requestedAction) {
8665
- if (grantedAction === "*" || grantedAction === requestedAction) return true;
8708
+ if (grantedAction === requestedAction) return true;
8709
+ if (isExplicitOnlyAction(requestedAction)) return false;
8710
+ if (grantedAction === "*") return true;
8666
8711
  if (!grantedAction.endsWith("/*")) return false;
8667
8712
  const service = grantedAction.slice(0, -2);
8668
8713
  return requestedAction === service || requestedAction.startsWith(`${service}/`);
@@ -11345,7 +11390,9 @@ var SharingService = class {
11345
11390
  if (delegation.allowSubDelegation === false) continue;
11346
11391
  if (!this.pathMatches(delegation.path || "", path)) continue;
11347
11392
  const delegationActions = delegation.actions || [];
11348
- if (!actions.every((action) => delegationActions.includes(action) || delegationActions.includes("*"))) {
11393
+ if (!actions.every(
11394
+ (action) => delegationActions.some((granted) => actionContains(granted, action))
11395
+ )) {
11349
11396
  continue;
11350
11397
  }
11351
11398
  if ((delegation.caveats?.length ?? 0) > 0) {
@@ -14396,6 +14443,56 @@ var AccountService = class {
14396
14443
  await this.upsertSpacesIndexQuietly(registered);
14397
14444
  return (0, import_sdk_services7.ok)({ spaces: registered, recoveredFromBatchError: batchResult.error });
14398
14445
  },
14446
+ /**
14447
+ * Register only spaces without a registry record (TC-800). Existing
14448
+ * records, including renamed or archived ones, are kept byte-for-byte.
14449
+ * Bootstrap repair uses this on populated accounts.
14450
+ */
14451
+ registerMissing: async (spaces) => {
14452
+ if (spaces.length === 0) return (0, import_sdk_services7.ok)([]);
14453
+ await this.config.ensureAccountSpaceHosted?.();
14454
+ const kvResult = this.accountKV();
14455
+ if (!kvResult.ok) return kvResult;
14456
+ const kv = kvResult.data;
14457
+ const stored = spaces.map((space) => spaceRecordFromInput(space));
14458
+ const keys = stored.map((record2) => spaceKey(record2.space_id));
14459
+ const read2 = await kv.batchGet(keys);
14460
+ if (!read2.ok) return accountErr(read2.error);
14461
+ const byKey = new Map(read2.data.results.map((result) => [result.key, result.result]));
14462
+ const final = new Array(keys.length);
14463
+ const missing = [];
14464
+ for (let i = 0; i < keys.length; i++) {
14465
+ const key = keys[i];
14466
+ const result = byKey.get(key);
14467
+ if (!result) {
14468
+ return (0, import_sdk_services7.err)((0, import_sdk_services7.serviceError)(
14469
+ import_sdk_services7.ErrorCodes.NETWORK_ERROR,
14470
+ `KV batch read omitted requested key ${JSON.stringify(key)}`,
14471
+ SERVICE_NAME2
14472
+ ));
14473
+ }
14474
+ if (result.ok) {
14475
+ final[i] = spaceFromRecord(key, result.data.data);
14476
+ } else if (result.error.code === import_sdk_services7.ErrorCodes.KV_NOT_FOUND) {
14477
+ missing.push({ index: i, key, record: stored[i] });
14478
+ } else {
14479
+ return accountErr(result.error);
14480
+ }
14481
+ }
14482
+ const creates = await Promise.all(missing.map(async ({ index, key, record: record2 }) => {
14483
+ const written = await kv.put(key, record2, { ifNoneMatch: "*" });
14484
+ if (written.ok) return { index, result: (0, import_sdk_services7.ok)(spaceFromRecord(key, record2)) };
14485
+ const raced = await kv.get(key);
14486
+ if (raced.ok) return { index, result: (0, import_sdk_services7.ok)(spaceFromRecord(key, raced.data.data)) };
14487
+ return { index, result: (0, import_sdk_services7.err)(written.error) };
14488
+ }));
14489
+ for (const { index, result } of creates) {
14490
+ if (!result.ok) return accountErr(result.error);
14491
+ final[index] = result.data;
14492
+ }
14493
+ await this.upsertSpacesIndexQuietly(final);
14494
+ return (0, import_sdk_services7.ok)(final);
14495
+ },
14399
14496
  syncAccessible: async () => {
14400
14497
  const synced = await this.syncAccessibleSpaces({ tolerateStorageFull: false });
14401
14498
  return synced.ok ? (0, import_sdk_services7.ok)(synced.data.spaces) : synced;