@agentunion/fastaun 0.5.21 → 0.5.23

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 (75) hide show
  1. package/CHANGELOG.md +58 -1
  2. package/_packed_docs/CHANGELOG.md +58 -1
  3. package/_packed_docs/INDEX.md +469 -453
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +169 -164
  5. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +307 -67
  6. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +41 -15
  7. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +5 -5
  8. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +30 -8
  9. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +669 -666
  10. package/_packed_docs/sdk/0.5.22-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +212 -0
  11. package/_packed_docs/sdk/0.5.22-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +199 -0
  12. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +16 -0
  13. package/_packed_docs/sdk/09-group-rpc-manual.md +10 -0
  14. package/_packed_docs/sdk/09-storage-rpc-manual.md +64 -7
  15. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +3 -1
  16. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +2 -0
  17. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +138 -134
  18. package/_packed_docs/sdk/INDEX.md +255 -239
  19. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +939 -930
  20. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +312 -363
  21. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +322 -553
  22. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +4 -4
  23. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +484 -480
  24. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +190 -97
  25. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +158 -127
  26. package/dist/agent-md.d.ts +16 -2
  27. package/dist/agent-md.js +59 -23
  28. package/dist/agent-md.js.map +1 -1
  29. package/dist/aid-store.d.ts +1 -0
  30. package/dist/aid-store.js +43 -8
  31. package/dist/aid-store.js.map +1 -1
  32. package/dist/auth.d.ts +7 -0
  33. package/dist/auth.js +222 -70
  34. package/dist/auth.js.map +1 -1
  35. package/dist/client/delivery.d.ts +28 -0
  36. package/dist/client/delivery.js +399 -44
  37. package/dist/client/delivery.js.map +1 -1
  38. package/dist/client/group-state.js +3 -0
  39. package/dist/client/group-state.js.map +1 -1
  40. package/dist/client/rpc-pipeline.js +28 -2
  41. package/dist/client/rpc-pipeline.js.map +1 -1
  42. package/dist/client/v2-e2ee.js +109 -39
  43. package/dist/client/v2-e2ee.js.map +1 -1
  44. package/dist/client.d.ts +3 -0
  45. package/dist/client.js +114 -26
  46. package/dist/client.js.map +1 -1
  47. package/dist/group-fs.d.ts +1 -0
  48. package/dist/group-fs.js +37 -19
  49. package/dist/group-fs.js.map +1 -1
  50. package/dist/org-protocol.d.ts +11 -0
  51. package/dist/org-protocol.js +154 -19
  52. package/dist/org-protocol.js.map +1 -1
  53. package/dist/ownership-v21.d.ts +21 -3
  54. package/dist/ownership-v21.js +135 -37
  55. package/dist/ownership-v21.js.map +1 -1
  56. package/dist/ownership-workflow.d.ts +7 -2
  57. package/dist/ownership-workflow.js +173 -17
  58. package/dist/ownership-workflow.js.map +1 -1
  59. package/dist/storage/lowlevel.d.ts +6 -0
  60. package/dist/storage/lowlevel.js +24 -7
  61. package/dist/storage/lowlevel.js.map +1 -1
  62. package/dist/storage/read-cache.d.ts +70 -0
  63. package/dist/storage/read-cache.js +625 -0
  64. package/dist/storage/read-cache.js.map +1 -0
  65. package/dist/storage/vfs.d.ts +2 -0
  66. package/dist/storage/vfs.js +50 -15
  67. package/dist/storage/vfs.js.map +1 -1
  68. package/dist/tools/cross-sdk-agent.d.ts +21 -1
  69. package/dist/tools/cross-sdk-agent.js +301 -19
  70. package/dist/tools/cross-sdk-agent.js.map +1 -1
  71. package/dist/transport.js +6 -2
  72. package/dist/transport.js.map +1 -1
  73. package/dist/version.d.ts +1 -1
  74. package/dist/version.js +1 -1
  75. package/package.json +1 -1
@@ -5,6 +5,7 @@ import * as crypto from 'node:crypto';
5
5
  import * as fs from 'node:fs';
6
6
  import * as path from 'node:path';
7
7
  import { tmpdir } from 'node:os';
8
+ import { AsyncLocalStorage } from 'node:async_hooks';
8
9
  import { URL } from 'node:url';
9
10
  import { fileURLToPath } from 'node:url';
10
11
  import { AUNClient } from '../client.js';
@@ -15,6 +16,32 @@ import { VERSION } from '../version.js';
15
16
  import { canonicalJson, digest, buildRequestBinding, verifyMembershipBinding, ProtocolError } from '../org-protocol.js';
16
17
  import { ownershipTaskResult } from '../ownership-workflow.js';
17
18
  import { TimeoutError } from '../errors.js';
19
+ const credentialProcessStartupId = crypto.randomUUID();
20
+ const cacheReadContext = new AsyncLocalStorage();
21
+ const cacheReadObservers = new WeakMap();
22
+ function acquireCacheReadObserver(target, key, wrap) {
23
+ let observers = cacheReadObservers.get(target);
24
+ if (!observers) {
25
+ observers = new Map();
26
+ cacheReadObservers.set(target, observers);
27
+ }
28
+ let observer = observers.get(key);
29
+ if (!observer) {
30
+ const original = target[key];
31
+ target[key] = wrap(original);
32
+ observer = { users: 0, restore: () => { target[key] = original; } };
33
+ observers.set(key, observer);
34
+ }
35
+ observer.users += 1;
36
+ return () => {
37
+ if (--observer.users === 0) {
38
+ observer.restore();
39
+ observers.delete(key);
40
+ if (!observers.size)
41
+ cacheReadObservers.delete(target);
42
+ }
43
+ };
44
+ }
18
45
  function envBool(name, fallback = false) {
19
46
  const raw = process.env[name];
20
47
  if (raw == null)
@@ -136,6 +163,13 @@ export class CrossSdkTsAgent {
136
163
  eventBoundClients = new WeakSet();
137
164
  batchEventClient;
138
165
  batchEventHandlers = [];
166
+ p2pSubscription = true;
167
+ p2pEventClient;
168
+ p2pEventHandler;
169
+ p2pDeliveryPaused = false;
170
+ p2pDeliveryBlocked = 0;
171
+ p2pDeliveryGate = null;
172
+ p2pDeliveryRetiredClients = new WeakSet();
139
173
  rpcSpans = [];
140
174
  observationGeneration = 0;
141
175
  pullBarrier = null;
@@ -155,6 +189,18 @@ export class CrossSdkTsAgent {
155
189
  constructor() {
156
190
  this.client = this.newClient();
157
191
  }
192
+ credentialStartup;
193
+ credentialProcessId = credentialProcessStartupId;
194
+ credentialProcessCheckpoint() {
195
+ const internal = this.client;
196
+ const metadata = internal._tokenStore._loadMetadataStrict(this.aid) ?? {};
197
+ return structuredClone({ pid: process.pid, startup_uuid: this.credentialProcessId, aid: this.aid,
198
+ cert_fingerprint: this.client.currentAid?.certFingerprint, store: this.aunPath,
199
+ records: metadata.credential_renewals_v1 ?? {}, revocations: metadata.credential_revocations_v1 ?? { version: 1, authorities: {} } });
200
+ }
201
+ captureCredentialStartup() {
202
+ this.credentialStartup ??= { ...this.credentialProcessCheckpoint(), phase: 'before_connect' };
203
+ }
158
204
  async start() {
159
205
  this.bindClientEvents();
160
206
  // Cross-SDK runner 需要先由显式 Tail 建立窗口证明;启动阶段不得遗留后台 Pull。
@@ -172,7 +218,7 @@ export class CrossSdkTsAgent {
172
218
  bindClientEvents() {
173
219
  this.eventBoundClients ??= new WeakSet();
174
220
  if (this.eventBoundClients.has(this.client)) {
175
- this.setBatchEventMode(this.batchEventMode ?? false);
221
+ this.setP2pSubscription(this.p2pSubscription ?? true);
176
222
  return;
177
223
  }
178
224
  this.eventBoundClients.add(this.client);
@@ -182,10 +228,6 @@ export class CrossSdkTsAgent {
182
228
  this.client.on('_raw.group.message_created', (data) => this.storeRawPushObservation('_raw.group.message_created', 'group', 'legacy', data));
183
229
  this.client.on('_raw.group.v2.message_created', (data) => this.storeRawPushObservation('_raw.group.v2.message_created', 'group', 'v2', data));
184
230
  this.client.on('_raw.group.changed', (data) => this.storeRawGroupChangedObservation(data));
185
- this.client.on('message.received', (msg) => {
186
- this.countSingleEvent('message.received');
187
- void this.storeApplicationEvent('message.received', msg);
188
- });
189
231
  this.client.on('message.undecryptable', (msg) => {
190
232
  void this.storeInboxItem(this.normalizeMessage(msg, false, 'undecryptable'));
191
233
  });
@@ -219,7 +261,7 @@ export class CrossSdkTsAgent {
219
261
  this.recordTrace(textOf(payload.org_id), { stage: event, payload });
220
262
  });
221
263
  }
222
- this.setBatchEventMode(this.batchEventMode ?? false);
264
+ this.setP2pSubscription(this.p2pSubscription ?? true);
223
265
  }
224
266
  countSingleEvent(event) {
225
267
  this.singleEventCounts ??= { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
@@ -237,18 +279,78 @@ export class CrossSdkTsAgent {
237
279
  else if (event === 'group.changed')
238
280
  this.storeGroupChangedPublicEvent(value);
239
281
  }
282
+ setP2pSubscription(enabled) {
283
+ this.p2pSubscription = enabled;
284
+ if (this.p2pEventHandler && (this.p2pEventClient !== this.client || !enabled)) {
285
+ this.p2pEventClient?.off('message.received', this.p2pEventHandler);
286
+ this.p2pEventHandler = undefined;
287
+ }
288
+ this.p2pEventClient = this.client;
289
+ if (enabled && !this.p2pEventHandler) {
290
+ const client = this.client;
291
+ this.p2pEventHandler = async (msg) => {
292
+ if ((this.p2pDeliveryPaused || this.p2pDeliveryRetiredClients?.has(client)) && !await this.waitForP2pDelivery(client))
293
+ return;
294
+ this.countSingleEvent('message.received');
295
+ await this.storeApplicationEvent('message.received', msg);
296
+ };
297
+ this.client.on('message.received', this.p2pEventHandler);
298
+ }
299
+ this.setBatchEventMode(this.batchEventMode ?? false);
300
+ }
301
+ setP2pDeliveryPaused(paused) {
302
+ this.p2pDeliveryPaused = paused;
303
+ if (!paused) {
304
+ const gate = this.p2pDeliveryGate;
305
+ this.p2pDeliveryGate = null;
306
+ gate?.release();
307
+ }
308
+ }
309
+ async waitForP2pDelivery(client) {
310
+ if (this.p2pDeliveryRetiredClients?.has(client))
311
+ return false;
312
+ this.p2pDeliveryBlocked = (this.p2pDeliveryBlocked ?? 0) + 1;
313
+ try {
314
+ while (this.p2pDeliveryPaused && !this.p2pDeliveryRetiredClients?.has(client)) {
315
+ if (!this.p2pDeliveryGate) {
316
+ let release;
317
+ const promise = new Promise((resolve) => { release = resolve; });
318
+ this.p2pDeliveryGate = { promise, release };
319
+ }
320
+ await this.p2pDeliveryGate.promise;
321
+ }
322
+ return !this.p2pDeliveryRetiredClients?.has(client);
323
+ }
324
+ finally {
325
+ this.p2pDeliveryBlocked -= 1;
326
+ }
327
+ }
328
+ retireP2pDelivery(client) {
329
+ this.p2pDeliveryRetiredClients ??= new WeakSet();
330
+ this.p2pDeliveryRetiredClients.add(client);
331
+ const gate = this.p2pDeliveryGate;
332
+ this.p2pDeliveryGate = null;
333
+ gate?.release();
334
+ }
335
+ applicationDeliveryState() {
336
+ const dispatcher = this.client._dispatcher;
337
+ return { subscribed: this.p2pSubscription ?? true, paused: this.p2pDeliveryPaused ?? false, blocked: this.p2pDeliveryBlocked ?? 0,
338
+ capacity: dispatcher?._messageQueueCapacity?.() ?? 0, used: dispatcher?._queuedMessageCount ?? 0, waiting: dispatcher?._messageAdmissionWaiters ?? 0 };
339
+ }
240
340
  setBatchEventMode(enabled) {
241
341
  this.batchEventMode = enabled;
242
- if (enabled && this.batchEventClient === this.client && this.batchEventHandlers?.length)
342
+ const events = enabled ? ['message.batch', 'group.message_batch', 'group.event_batch'].filter((event) => event !== 'message.batch' || this.p2pSubscription !== false) : [];
343
+ if (this.batchEventClient === this.client && this.batchEventHandlers?.length === events.length && this.batchEventHandlers.every((item, index) => item.event === events[index]))
243
344
  return;
244
345
  for (const { event, handler } of this.batchEventHandlers ?? [])
245
346
  this.batchEventClient?.off(event, handler);
246
347
  this.batchEventClient = this.client;
247
348
  this.batchEventHandlers = [];
248
- if (!enabled)
249
- return;
250
- for (const event of ['message.batch', 'group.message_batch', 'group.event_batch']) {
349
+ const client = this.client;
350
+ for (const event of events) {
251
351
  const handler = async (value) => {
352
+ if (event === 'message.batch' && (this.p2pDeliveryPaused || this.p2pDeliveryRetiredClients?.has(client)) && !await this.waitForP2pDelivery(client))
353
+ return;
252
354
  const data = jsonObjectOf(value) ?? {};
253
355
  const items = JSON.parse(JSON.stringify(jsonSafe(Array.isArray(data.items) ? data.items : [])));
254
356
  this.batchEvents ??= [];
@@ -617,6 +719,7 @@ export class CrossSdkTsAgent {
617
719
  }
618
720
  async close() {
619
721
  this.resetObservationLedgers();
722
+ this.retireP2pDelivery(this.client);
620
723
  try {
621
724
  await this.client.close();
622
725
  }
@@ -628,7 +731,7 @@ export class CrossSdkTsAgent {
628
731
  const internal = this.client;
629
732
  const store = new AIDStore({
630
733
  aunPath: this.aunPath,
631
- encryptionSeed: '',
734
+ encryptionSeed: process.env.AUN_TEST_ENCRYPTION_SEED || process.env.AUN_ENCRYPTION_SEED || '',
632
735
  slotId: this.slotId,
633
736
  debug: this.debug,
634
737
  });
@@ -661,6 +764,7 @@ export class CrossSdkTsAgent {
661
764
  throw new Error(`load identity failed: ${loaded.error.code}: ${loaded.error.message}`);
662
765
  }
663
766
  this.client.loadIdentity(loaded.data.aid);
767
+ this.captureCredentialStartup();
664
768
  const checked = await store.checkAgentMd(this.aid);
665
769
  const localEtag = checked.ok ? String(checked.data.local_etag ?? '').trim() : '';
666
770
  const remoteEtag = checked.ok ? String(checked.data.remote_etag ?? '').trim() : '';
@@ -886,10 +990,23 @@ export class CrossSdkTsAgent {
886
990
  if ((body.pull_barrier_count !== undefined && (typeof body.pull_barrier_count !== 'number' || !Number.isSafeInteger(body.pull_barrier_count) || body.pull_barrier_count < 0)) ||
887
991
  (body.pull_barrier_scope !== undefined && !['all', 'p2p'].includes(body.pull_barrier_scope)) ||
888
992
  (body.observation_id !== undefined && typeof body.observation_id !== 'string') ||
889
- (body.release_pull_barrier !== undefined && typeof body.release_pull_barrier !== 'boolean')) {
993
+ (body.release_pull_barrier !== undefined && typeof body.release_pull_barrier !== 'boolean') ||
994
+ ['p2p_subscription', 'p2p_delivery_paused', 'preserve_observations'].some((key) => body[key] !== undefined && typeof body[key] !== 'boolean')) {
890
995
  sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid observation parameters' });
891
996
  return;
892
997
  }
998
+ if (typeof body.p2p_subscription === 'boolean')
999
+ this.setP2pSubscription(body.p2p_subscription);
1000
+ const p2pPullSpanIds = body.preserve_observations === true && body.p2p_subscription === false
1001
+ ? this.rpcSpans.filter((span) => span.method === 'message.pull' || span.method === 'message.v2.pull').map((span) => textOf(span.span_id)) : undefined;
1002
+ if (typeof body.p2p_delivery_paused === 'boolean')
1003
+ this.setP2pDeliveryPaused(body.p2p_delivery_paused);
1004
+ if (body.preserve_observations === true) {
1005
+ if (body.release_pull_barrier === true)
1006
+ this.releasePullBarrier();
1007
+ sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId, ...(p2pPullSpanIds !== undefined ? { p2p_pull_span_ids: p2pPullSpanIds } : {}) });
1008
+ return;
1009
+ }
893
1010
  if (body.release_pull_barrier === true) {
894
1011
  this.releasePullBarrier();
895
1012
  sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
@@ -954,6 +1071,7 @@ export class CrossSdkTsAgent {
954
1071
  rpcMethodCounts[method] = Number(rpcMethodCounts[method] ?? 0) + 1;
955
1072
  sendJson(res, 200, {
956
1073
  ok: true, time_ms: Date.now(), items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
1074
+ observation_id: this.rawPushObservationId,
957
1075
  rpc_calls: rpcEntries,
958
1076
  group_changed_events: groupChangedEvents.slice(-limit),
959
1077
  group_invite_events: groupInviteEvents.slice(-limit),
@@ -964,6 +1082,7 @@ export class CrossSdkTsAgent {
964
1082
  batch_event_mode: this.batchEventMode ?? false,
965
1083
  batch_events: batchEvents,
966
1084
  single_event_counts: this.singleEventCounts ?? { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 },
1085
+ application_delivery: this.applicationDeliveryState(),
967
1086
  pull_barrier: { target: this.pullBarrier?.target ?? 0, waiting: this.pullBarrier?.waiting ?? 0, released: this.pullBarrier?.released ?? true, scope: this.pullBarrier?.scope ?? 'all' },
968
1087
  });
969
1088
  return;
@@ -1584,6 +1703,7 @@ export class CrossSdkTsAgent {
1584
1703
  try {
1585
1704
  if (typeof body.batch_events === 'boolean')
1586
1705
  this.batchEventMode = body.batch_events;
1706
+ this.retireP2pDelivery(this.client);
1587
1707
  await this.client.close();
1588
1708
  this.client = this.newClient();
1589
1709
  this.bindClientEvents();
@@ -1660,15 +1780,36 @@ export class CrossSdkTsAgent {
1660
1780
  const now = Math.floor(Date.now() / 1000);
1661
1781
  if (op === 'request')
1662
1782
  return buildRequestBinding(textOf(params.method), textOf(params.target), params.params, textOf(params.credential_cid), textOf(params.nonce), textOf(params.request_id), now, now + 300);
1663
- if (['register', 'sign', 'certificate', 'create', 'prepare'].includes(op)) {
1783
+ if (op === 'revocation_checkpoint') {
1784
+ const internal = this.client;
1785
+ if (params.sync === true)
1786
+ await internal._getCredentialRenewal()._syncRevocations();
1787
+ return internal._tokenStore._loadMetadataStrict(this.aid)?.credential_revocations_v1 ?? { version: 1, authorities: {} };
1788
+ }
1789
+ if (op === 'credential_process')
1790
+ return params.startup === true ? this.credentialStartup : this.credentialProcessCheckpoint();
1791
+ if (op === 'manage_credential') {
1792
+ const manager = this.client._getCredentialRenewal();
1793
+ await manager.serial(() => manager.rememberNow(params.credential, {}, this.aid, undefined, true));
1794
+ return await this.client.credential.current(textOf(params.credential.cid));
1795
+ }
1796
+ if (op === 'current_credential')
1797
+ return await this.client.credential.current(textOf(params.credential_cid));
1798
+ if (['register', 'sign', 'certificate', 'local_certificate', 'create', 'prepare', 'transfer_accept', 'renew'].includes(op)) {
1664
1799
  const store = this.aidStore();
1665
1800
  try {
1666
- if (op === 'prepare') {
1801
+ if (op === 'transfer_accept')
1802
+ return await this.client.org.lifecycle.transferAccept({ ...jsonObjectOf(params.params), aidStore: store });
1803
+ if (op === 'prepare' || op === 'renew') {
1667
1804
  const request = jsonObjectOf(params.params) ?? {};
1668
1805
  const orgId = textOf(params.org_id ?? request.org_id);
1669
1806
  const loaded = orgId ? store.load(orgId) : null;
1670
1807
  const orgSigner = loaded?.ok && loaded.data.aid.isPrivateKeyValid() ? loaded.data.aid : undefined;
1671
- return this.client.org.prepareRequest(textOf(params.method), request, textOf(params.target), this.client.currentAid, { ...jsonObjectOf(params.options), orgSigner });
1808
+ const credentialOptions = jsonObjectOf(params.credential_options);
1809
+ if (op === 'prepare')
1810
+ return this.client.org.prepareRequest(textOf(params.method), request, textOf(params.target), this.client.currentAid, { ...jsonObjectOf(params.options), orgSigner, ...(credentialOptions ? { credentialOptions } : {}) });
1811
+ const prepared = this.client.org.prepareRequest('org.member.renewCredential', request, textOf(params.target) || `org.${orgId.slice(orgId.indexOf('.') + 1)}`, this.client.currentAid, { orgSigner, credentialOptions: jsonObjectOf(params.credential_options) ?? undefined });
1812
+ return { result: await this.client.org.member.renewCredential(prepared), params: prepared };
1672
1813
  }
1673
1814
  if (op === 'register') {
1674
1815
  const registered = await store.register(textOf(params.org_id), { type: 'org', owner: this.aid, exttype: 'company' });
@@ -1677,12 +1818,14 @@ export class CrossSdkTsAgent {
1677
1818
  }
1678
1819
  const loaded = op === 'certificate'
1679
1820
  ? await store.resolve(textOf(params.as_aid), { skipAgentMd: true })
1680
- : (params.as_aid || params.org_id) ? store.load(textOf(params.as_aid ?? params.org_id)) : null;
1821
+ : (params.as_aid || op === 'local_certificate' || params.org_id) ? store.load(textOf(params.as_aid ?? params.org_id)) : null;
1681
1822
  if (loaded && !loaded.ok)
1682
1823
  throw new Error(String(loaded.error));
1683
1824
  const identity = loaded?.data?.aid ?? this.client.currentAid;
1684
1825
  const result = { cert_fingerprint: identity.certFingerprint, certificate: identity.certPem };
1685
- if (op === 'sign') {
1826
+ if (op === 'local_certificate')
1827
+ result.has_private_key = identity.isPrivateKeyValid();
1828
+ else if (op === 'sign') {
1686
1829
  const signed = await identity.sign(Buffer.from(canonicalJson(params.payload)));
1687
1830
  if (!signed.ok)
1688
1831
  throw new Error(String(signed.error));
@@ -1710,6 +1853,7 @@ export class CrossSdkTsAgent {
1710
1853
  const actual = jsonObjectOf(params.context) ?? params;
1711
1854
  const memberships = jsonObjectOf(params.memberships) ?? {};
1712
1855
  result.verification = { ...this.client.credential.verify(value, { resolveCertificate: resolver, resolveActorCertificate: resolver,
1856
+ resolveStatusProof: (cid) => params.status_proofs?.[cid],
1713
1857
  resolveStatus: (cid) => params.statuses?.[cid], now: verificationNow, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
1714
1858
  ns: textOf(actual.ns), action: textOf(actual.action), resource: textOf(actual.resource), rootIssuer: textOf(actual.root_issuer) || undefined,
1715
1859
  params: jsonObjectOf(actual.params) ?? {}, units: jsonObjectOf(actual.units) ?? {}, clientIp: textOf(actual.client_ip), method: textOf(actual.method), target: textOf(actual.target),
@@ -1751,6 +1895,18 @@ export class CrossSdkTsAgent {
1751
1895
  case 'sdk.org.protocol':
1752
1896
  result = await this.orgProtocol(params);
1753
1897
  break;
1898
+ case 'credential.register':
1899
+ result = await this.client.credential.register(params);
1900
+ break;
1901
+ case 'credential.status':
1902
+ result = await this.client.credential.status(params);
1903
+ break;
1904
+ case 'credential.revoke':
1905
+ result = await this.client.credential.revoke(params);
1906
+ break;
1907
+ case 'credential.revocationList':
1908
+ result = await this.client.credential.revocationList(params);
1909
+ break;
1754
1910
  case 'org.lifecycle.create':
1755
1911
  result = await this.client.org.lifecycle.create(params);
1756
1912
  break;
@@ -1843,6 +1999,10 @@ export class CrossSdkTsAgent {
1843
1999
  error_code: err instanceof Error ? err.name : 'Error',
1844
2000
  error_message: err instanceof Error ? err.message : String(err),
1845
2001
  };
2002
+ if (method.startsWith('org.') || method === 'sdk.org.protocol') {
2003
+ const remote = err;
2004
+ error.rpc_error_code = remote?.stringCode || remote?.data?.error_code || remote?.data?.code || (typeof remote?.code === 'string' ? remote.code : '');
2005
+ }
1846
2006
  this.recordTrace(traceId, { stage: 'group_call_error', method, error });
1847
2007
  sendJson(res, 500, error);
1848
2008
  }
@@ -2007,6 +2167,8 @@ export class CrossSdkTsAgent {
2007
2167
  }
2008
2168
  }
2009
2169
  async callGroupFsAction(action, params, asGroupAid = '') {
2170
+ if (action === 'cache_read')
2171
+ return await this.cacheRead(params, true, asGroupAid);
2010
2172
  const groupFs = this.client.group.fs;
2011
2173
  let signingStore = null;
2012
2174
  const withSigning = (source) => {
@@ -2146,7 +2308,124 @@ export class CrossSdkTsAgent {
2146
2308
  }
2147
2309
  return response;
2148
2310
  }
2311
+ async cacheRead(params, group = false, asGroupAid = '') {
2312
+ const operation = textOf(params.operation || 'read_bytes');
2313
+ if (!['read_bytes', 'stat', 'lstat', 'list', 'ls', 'find', 'du', 'df'].includes(operation)) {
2314
+ throw new Error(`unsupported cache read operation: ${operation}`);
2315
+ }
2316
+ const transport = this.client._transport;
2317
+ const lowlevel = group ? this.client.group.fs.lowlevel : this.client.storage.lowlevel;
2318
+ const counters = { rpc_count: 0, http_count: 0, body_transfers: 0, body_bytes: 0, methods: {}, wire_params: [] };
2319
+ const releaseCall = acquireCacheReadObserver(transport, 'call', (originalCall) => (method, request, ...tail) => {
2320
+ const context = cacheReadContext.getStore();
2321
+ const counted = context && (method.startsWith('storage.') || method.startsWith('group.fs.'));
2322
+ if (counted) {
2323
+ context.rpc_count += 1;
2324
+ context.methods[method] = (context.methods[method] || 0) + 1;
2325
+ context.wire_params.push({ method, params: JSON.parse(JSON.stringify(request ?? {})) });
2326
+ }
2327
+ const pending = originalCall.call(transport, method, request, ...tail);
2328
+ const observed = pending.then((result) => {
2329
+ const payload = jsonObjectOf(result);
2330
+ const content = payload?.content_base64 ?? payload?.content;
2331
+ if (counted && !payload?.not_modified && typeof content === 'string') {
2332
+ context.body_transfers += 1;
2333
+ context.body_bytes += Buffer.from(content, 'base64').byteLength;
2334
+ }
2335
+ return result;
2336
+ });
2337
+ if (pending.cancel)
2338
+ observed.cancel = () => pending.cancel();
2339
+ return observed;
2340
+ });
2341
+ const releaseHttp = acquireCacheReadObserver(lowlevel, 'httpGet', (originalHttp) => async (...args) => {
2342
+ const context = cacheReadContext.getStore();
2343
+ if (context)
2344
+ context.http_count += 1;
2345
+ const data = await originalHttp.call(lowlevel, ...args);
2346
+ if (context) {
2347
+ context.body_transfers += 1;
2348
+ context.body_bytes += data.byteLength;
2349
+ }
2350
+ return data;
2351
+ });
2352
+ const readOnce = async () => {
2353
+ const remotePath = textOf(params.path);
2354
+ const noCache = Boolean(params.no_cache ?? params.noCache);
2355
+ if (group) {
2356
+ const options = withoutKeys(params, ['operation', 'count', 'concurrent', 'no_cache']);
2357
+ options.noCache = noCache;
2358
+ if (operation !== 'read_bytes')
2359
+ return await this.callGroupFsAction(operation === 'list' ? 'ls' : operation, options, asGroupAid);
2360
+ delete options.path;
2361
+ const dst = this.writeTempText('', '.bin');
2362
+ try {
2363
+ const result = await this.callGroupFsAction('cp', { ...options, src: remotePath, dst, force: true }, asGroupAid);
2364
+ const response = jsonObjectOf(result);
2365
+ return { content_base64: response.content_base64, size_bytes: response.size_bytes };
2366
+ }
2367
+ finally {
2368
+ fs.rmSync(dst, { force: true });
2369
+ }
2370
+ }
2371
+ const storage = this.client.storage;
2372
+ const options = {
2373
+ owner: textOf(params.owner_aid ?? params.ownerAID).trim() || undefined,
2374
+ bucket: textOf(params.bucket).trim() || 'default', noCache,
2375
+ };
2376
+ if (operation !== 'df')
2377
+ options.token = textOf(params.token).trim() || undefined;
2378
+ if (operation === 'read_bytes') {
2379
+ const shareId = textOf(params.share_id ?? params.shareId).trim();
2380
+ if (shareId) {
2381
+ const result = await storage.lowlevel.getByShare({ shareId, noCache });
2382
+ const data = result.download_url ? await storage.lowlevel.httpGet(textOf(result.download_url)) : Buffer.from(textOf(result.content_base64 ?? result.content), 'base64');
2383
+ return { content_base64: Buffer.from(data).toString('base64'), size_bytes: data.byteLength };
2384
+ }
2385
+ for (const key of ['offset', 'limit'])
2386
+ if (params[key] != null)
2387
+ options[key] = Number(params[key]);
2388
+ const data = await storage.readBytes(remotePath, options);
2389
+ return { content_base64: Buffer.from(data).toString('base64'), size_bytes: data.byteLength };
2390
+ }
2391
+ const fields = { list: ['page', 'size', 'marker', 'long', 'recursive'], find: ['name', 'mtime', 'page'], du: [] };
2392
+ for (const key of fields[operation] ?? [])
2393
+ if (params[key] != null)
2394
+ options[key] = params[key];
2395
+ if (params.page_size != null || params.pageSize != null)
2396
+ options.pageSize = Number(params.page_size ?? params.pageSize);
2397
+ if (params.max_depth != null || params.maxDepth != null)
2398
+ options.maxDepth = Number(params.max_depth ?? params.maxDepth);
2399
+ if (params.node_type != null || params.nodeType != null)
2400
+ options.nodeType = params.node_type ?? params.nodeType;
2401
+ if (operation === 'find' && (params.size_expr != null || params.sizeExpr != null))
2402
+ options.size = params.size_expr ?? params.sizeExpr;
2403
+ const method = storage[operation === 'ls' ? 'list' : operation];
2404
+ return await method.call(storage, ...(operation === 'df' ? [options] : [remotePath, options]));
2405
+ };
2406
+ try {
2407
+ const count = Math.max(1, Number(params.count || 1));
2408
+ const values = await cacheReadContext.run(counters, async () => {
2409
+ if (params.concurrent)
2410
+ return await Promise.all(Array.from({ length: count }, () => readOnce()));
2411
+ const items = [];
2412
+ for (let index = 0; index < count; index += 1)
2413
+ items.push(await readOnce());
2414
+ return items;
2415
+ });
2416
+ if (values.some((value) => stableStringify(value) !== stableStringify(values[0])))
2417
+ throw new Error('cache_read returned inconsistent values');
2418
+ const result = operation === 'read_bytes' ? jsonObjectOf(values[0]) : { value: values[0] };
2419
+ return { ...result, ...counters };
2420
+ }
2421
+ finally {
2422
+ releaseCall();
2423
+ releaseHttp();
2424
+ }
2425
+ }
2149
2426
  async callStorageAction(action, params) {
2427
+ if (action === 'cache_read')
2428
+ return await this.cacheRead(params);
2150
2429
  const storage = this.client.storage;
2151
2430
  const path = textOf(params.path).trim();
2152
2431
  const owner = textOf(params.owner_aid ?? params.ownerAID).trim() || undefined;
@@ -2325,7 +2604,10 @@ export class CrossSdkTsAgent {
2325
2604
  case 'revoke_share_link':
2326
2605
  return await storage.lowlevel.revokeShareLink({ shareId: textOf(params.share_id ?? params.shareId).trim() });
2327
2606
  case 'get_by_share': {
2328
- const result = await storage.lowlevel.getByShare({ shareId: textOf(params.share_id ?? params.shareId).trim() });
2607
+ const result = await storage.lowlevel.getByShare({
2608
+ shareId: textOf(params.share_id ?? params.shareId).trim(),
2609
+ noCache: Boolean(params.no_cache ?? params.noCache),
2610
+ });
2329
2611
  const resultObj = jsonObjectOf(result);
2330
2612
  const content = textOf(resultObj?.content);
2331
2613
  if (content) {
@@ -2616,7 +2898,7 @@ export class CrossSdkTsAgent {
2616
2898
  aidStore() {
2617
2899
  return new AIDStore({
2618
2900
  aunPath: this.aunPath,
2619
- encryptionSeed: '',
2901
+ encryptionSeed: process.env.AUN_TEST_ENCRYPTION_SEED || process.env.AUN_ENCRYPTION_SEED || '',
2620
2902
  slotId: this.slotId,
2621
2903
  debug: this.debug,
2622
2904
  });