@agentunion/fastaun-browser 0.5.18 → 0.5.20

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 (149) hide show
  1. package/CHANGELOG.md +172 -79
  2. package/_packed_docs/CHANGELOG.md +172 -79
  3. package/_packed_docs/INDEX.md +244 -116
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +98 -69
  5. package/_packed_docs/agent.md/SCHEMA.md +245 -205
  6. package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
  7. 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 +42 -15
  8. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -4
  9. package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +45 -134
  10. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
  11. package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +5 -20
  12. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +687 -668
  13. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +19 -28
  14. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
  15. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
  16. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
  17. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
  18. package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
  19. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
  20. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +68 -61
  21. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
  22. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +279 -285
  23. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
  24. package/_packed_docs/sdk/09-group-rpc-manual.md +271 -235
  25. package/_packed_docs/sdk/09-message-rpc-manual.md +47 -6
  26. package/_packed_docs/sdk/09-storage-rpc-manual.md +70 -68
  27. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +63 -34
  28. package/_packed_docs/sdk/INDEX.md +105 -34
  29. package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
  30. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
  31. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
  32. package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
  33. 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 +300 -0
  34. 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 +382 -0
  35. 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 +708 -0
  36. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
  37. package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
  38. package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
  39. 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 +173 -0
  40. 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 +211 -0
  41. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
  42. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
  43. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
  44. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
  45. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
  46. package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
  47. package/dist/agent-md-schema.d.ts +17 -3
  48. package/dist/agent-md-schema.d.ts.map +1 -1
  49. package/dist/agent-md-schema.js +94 -26
  50. package/dist/agent-md-schema.js.map +1 -1
  51. package/dist/agent-md.d.ts +54 -3
  52. package/dist/agent-md.d.ts.map +1 -1
  53. package/dist/agent-md.js +711 -65
  54. package/dist/agent-md.js.map +1 -1
  55. package/dist/aid-store.d.ts +58 -46
  56. package/dist/aid-store.d.ts.map +1 -1
  57. package/dist/aid-store.js +254 -248
  58. package/dist/aid-store.js.map +1 -1
  59. package/dist/aid.d.ts +4 -1
  60. package/dist/aid.d.ts.map +1 -1
  61. package/dist/aid.js +27 -33
  62. package/dist/aid.js.map +1 -1
  63. package/dist/auth.d.ts +4 -1
  64. package/dist/auth.d.ts.map +1 -1
  65. package/dist/auth.js +426 -39
  66. package/dist/auth.js.map +1 -1
  67. package/dist/bundle.js +38179 -32054
  68. package/dist/cert-utils.d.ts +50 -2
  69. package/dist/cert-utils.d.ts.map +1 -1
  70. package/dist/cert-utils.js +105 -47
  71. package/dist/cert-utils.js.map +1 -1
  72. package/dist/client/delivery.d.ts +22 -1
  73. package/dist/client/delivery.d.ts.map +1 -1
  74. package/dist/client/delivery.js +389 -78
  75. package/dist/client/delivery.js.map +1 -1
  76. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  77. package/dist/client/rpc-pipeline.js +103 -77
  78. package/dist/client/rpc-pipeline.js.map +1 -1
  79. package/dist/client/v2-e2ee.d.ts +1 -0
  80. package/dist/client/v2-e2ee.d.ts.map +1 -1
  81. package/dist/client/v2-e2ee.js +182 -89
  82. package/dist/client/v2-e2ee.js.map +1 -1
  83. package/dist/client.d.ts +100 -1
  84. package/dist/client.d.ts.map +1 -1
  85. package/dist/client.js +1220 -25
  86. package/dist/client.js.map +1 -1
  87. package/dist/crypto.d.ts.map +1 -1
  88. package/dist/crypto.js +8 -6
  89. package/dist/crypto.js.map +1 -1
  90. package/dist/errors.d.ts.map +1 -1
  91. package/dist/errors.js +3 -2
  92. package/dist/errors.js.map +1 -1
  93. package/dist/events.d.ts +9 -2
  94. package/dist/events.d.ts.map +1 -1
  95. package/dist/events.js +72 -13
  96. package/dist/events.js.map +1 -1
  97. package/dist/facades.d.ts +82 -2
  98. package/dist/facades.d.ts.map +1 -1
  99. package/dist/facades.js +100 -6
  100. package/dist/facades.js.map +1 -1
  101. package/dist/index.d.ts +7 -3
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +3 -2
  104. package/dist/index.js.map +1 -1
  105. package/dist/keystore/index.d.ts +17 -0
  106. package/dist/keystore/index.d.ts.map +1 -1
  107. package/dist/keystore/indexeddb-shared.d.ts +10 -1
  108. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  109. package/dist/keystore/indexeddb-shared.js +111 -0
  110. package/dist/keystore/indexeddb-shared.js.map +1 -1
  111. package/dist/keystore/indexeddb-token-store.d.ts +7 -1
  112. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  113. package/dist/keystore/indexeddb-token-store.js +30 -1
  114. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  115. package/dist/org-protocol.d.ts +98 -0
  116. package/dist/org-protocol.d.ts.map +1 -0
  117. package/dist/org-protocol.js +1802 -0
  118. package/dist/org-protocol.js.map +1 -0
  119. package/dist/ownership-proof.d.ts +93 -5
  120. package/dist/ownership-proof.d.ts.map +1 -1
  121. package/dist/ownership-proof.js +239 -53
  122. package/dist/ownership-proof.js.map +1 -1
  123. package/dist/ownership-v21.d.ts +194 -0
  124. package/dist/ownership-v21.d.ts.map +1 -0
  125. package/dist/ownership-v21.js +678 -0
  126. package/dist/ownership-v21.js.map +1 -0
  127. package/dist/ownership-workflow.d.ts +151 -0
  128. package/dist/ownership-workflow.d.ts.map +1 -0
  129. package/dist/ownership-workflow.js +1229 -0
  130. package/dist/ownership-workflow.js.map +1 -0
  131. package/dist/seq-tracker.d.ts +1 -0
  132. package/dist/seq-tracker.d.ts.map +1 -1
  133. package/dist/seq-tracker.js +10 -2
  134. package/dist/seq-tracker.js.map +1 -1
  135. package/dist/tools/cross-sdk-agent.d.ts +83 -0
  136. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  137. package/dist/tools/cross-sdk-agent.js +415 -7
  138. package/dist/tools/cross-sdk-agent.js.map +1 -1
  139. package/dist/transport.d.ts +7 -0
  140. package/dist/transport.d.ts.map +1 -1
  141. package/dist/transport.js +90 -2
  142. package/dist/transport.js.map +1 -1
  143. package/dist/v2/e2ee/decrypt.d.ts +1 -0
  144. package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
  145. package/dist/v2/e2ee/decrypt.js +9 -1
  146. package/dist/v2/e2ee/decrypt.js.map +1 -1
  147. package/dist/version.d.ts +1 -1
  148. package/dist/version.js +1 -1
  149. package/package.json +1 -1
@@ -4,9 +4,14 @@ import * as http from 'node:http';
4
4
  import * as https from 'node:https';
5
5
  import * as crypto from 'node:crypto';
6
6
  import * as fs from 'node:fs';
7
- import { URL } from 'node:url';
7
+ import * as path from 'node:path';
8
+ import { URL, fileURLToPath } from 'node:url';
8
9
  import { AID, AIDStore, AUNClient, __version__ } from '../index.js';
9
10
  import { publicKeyFingerprint } from '../cert-utils.js';
11
+ import { StateError } from '../errors.js';
12
+ import { OwnershipStorageAdapter } from '../ownership-v21.js';
13
+ import { ownershipErrorCode, ownershipTaskResult } from '../ownership-workflow.js';
14
+ import { canonicalJson, digest, buildRequestBinding, verifyMembershipBinding, ProtocolError } from '../org-protocol.js';
10
15
  function installNodeBrowserShims() {
11
16
  const root = globalThis;
12
17
  if (!root.WebSocket)
@@ -120,7 +125,7 @@ function envelopeMetadata(data) {
120
125
  out.protected_headers = jsonSafe(protectedHeaders);
121
126
  return out;
122
127
  }
123
- class CrossSdkJsAgent {
128
+ export class CrossSdkJsAgent {
124
129
  language = 'js';
125
130
  sdkVersion = __version__;
126
131
  aid = textOf(process.env.AUN_TEST_AID || `cross-js-${crypto.randomUUID().slice(0, 8)}.agentid.pub`).trim();
@@ -139,6 +144,8 @@ class CrossSdkJsAgent {
139
144
  rpcObservedTransports = new WeakSet();
140
145
  groupChangedEvents = [];
141
146
  groupInviteEvents = [];
147
+ readStateEvents = [];
148
+ ownershipEvents = [];
142
149
  traces = new Map();
143
150
  sendResults = new Map();
144
151
  inlineRealtimePayloadV1 = true;
@@ -178,6 +185,19 @@ class CrossSdkJsAgent {
178
185
  });
179
186
  this.client.on('group.changed', (data) => this.storeGroupChangedPublicEvent(data));
180
187
  this.client.on('group.invite_received', (data) => this.storeGroupInviteEvent(data));
188
+ for (const event of ['message.read_changed', 'group.read_changed', 'read_state_sync']) {
189
+ this.client.on(event, (payload) => { this.readStateEvents.push({ event, payload: jsonSafe(payload) }); });
190
+ }
191
+ for (const event of ['ownership.request', 'ownership.action_required', 'ownership.progress', 'ownership.completed', 'ownership.rejected', 'ownership.stopped', 'ownership.error']) {
192
+ this.client.on(event, (payload) => { this.ownershipEvents.push({ event, payload: jsonSafe(payload) }); });
193
+ }
194
+ for (const suffix of ['join_requested', 'join_approved', 'join_rejected', 'invite_created', 'invite_accepted', 'invite_rejected']) {
195
+ const event = 'org.member.' + suffix;
196
+ this.client.on(event, (value) => {
197
+ const payload = value;
198
+ this.recordTrace(textOf(payload.org_id), { stage: event, payload });
199
+ });
200
+ }
181
201
  }
182
202
  installTransportRpcObserver() {
183
203
  const transport = this.client._transport;
@@ -317,10 +337,38 @@ class CrossSdkJsAgent {
317
337
  '',
318
338
  ].join('\n');
319
339
  }
340
+ async agentMdContentForUpload(aid, name, type, description) {
341
+ let content = '';
342
+ try {
343
+ const manager = this.client._agentMdManager;
344
+ const managed = await Promise.resolve(manager?.readContent?.(aid));
345
+ content = typeof managed === 'string' ? managed : '';
346
+ }
347
+ catch {
348
+ content = '';
349
+ }
350
+ if (content && (content.includes('owner:') || content.includes('ownership_proof:') || content.includes('ownership_release:')))
351
+ return content;
352
+ try {
353
+ content = fs.readFileSync(path.join(this.aunPath, 'AIDs', aid, 'agent.md'), 'utf-8');
354
+ if (content.includes('owner:') || content.includes('ownership_proof:') || content.includes('ownership_release:'))
355
+ return content;
356
+ }
357
+ catch {
358
+ }
359
+ return this.agentMdContent(aid, name, type, description);
360
+ }
320
361
  async ensureConnected(backgroundSync = true) {
321
362
  const store = this.aidStore();
322
363
  try {
323
- const registered = await store.register(this.aid);
364
+ const fixedIdentityFile = textOf(process.env.AUN_TEST_FIXED_IDENTITY_FILE).trim();
365
+ if (fixedIdentityFile) {
366
+ const identity = JSON.parse(fs.readFileSync(fixedIdentityFile, 'utf-8'));
367
+ if (identity.aid !== this.aid)
368
+ throw new Error('固定测试身份与配置 AID 不一致');
369
+ await store._keystore.saveIdentity(this.aid, identity);
370
+ }
371
+ const registered = fixedIdentityFile ? await store.load(this.aid) : await store.register(this.aid, { type: 'service' });
324
372
  if (!registered.ok) {
325
373
  const loaded = await store.load(this.aid);
326
374
  if (!loaded.ok) {
@@ -331,9 +379,16 @@ class CrossSdkJsAgent {
331
379
  if (!loaded.ok) {
332
380
  throw new Error(`load identity failed: ${loaded.error.code}: ${loaded.error.message}`);
333
381
  }
334
- const uploaded = await store.uploadAgentMd(this.aid, this.agentMdContent(this.aid, this.aid, 'agent', 'Cross-SDK JavaScript test agent'));
335
- if (!uploaded.ok) {
336
- throw new Error(`upload agent.md failed: ${uploaded.error.code}: ${uploaded.error.message}`);
382
+ const checked = await store.checkAgentMd(this.aid);
383
+ const localEtag = checked.ok ? String(checked.data.local_etag ?? '').trim() : '';
384
+ const remoteEtag = checked.ok ? String(checked.data.remote_etag ?? '').trim() : '';
385
+ const inSync = Boolean(localEtag && remoteEtag && localEtag === remoteEtag);
386
+ const remoteFound = checked.ok && checked.data.remote_found === true;
387
+ if (!inSync && !remoteFound) {
388
+ const uploaded = await store.uploadAgentMd(this.aid, await this.agentMdContentForUpload(this.aid, this.aid, 'agent', 'Cross-SDK JavaScript test agent'));
389
+ if (!uploaded.ok) {
390
+ throw new Error(`upload agent.md failed: ${uploaded.error.code}: ${uploaded.error.message}`);
391
+ }
337
392
  }
338
393
  let identity = loaded.data.aid;
339
394
  if (this.debug && !identity.debug) {
@@ -439,6 +494,106 @@ class CrossSdkJsAgent {
439
494
  ...envelopeMetadata(data),
440
495
  };
441
496
  }
497
+ async ownershipControl(req, res, action) {
498
+ const body = await readJson(req);
499
+ const required = { bind: ['role', 'peer_aid'], accept: ['operation_id'], reject: ['operation_id'], release: ['sub_aid'], transfer: ['sub_aid', 'new_owner_aid'] };
500
+ const fields = required[action];
501
+ if (!fields || fields.some((key) => typeof body[key] !== 'string' || !textOf(body[key]).trim()) || Object.keys(body).some((key) => !fields.includes(key) && !(action === 'reject' && key === 'reason')) || action === 'reject' && body.reason !== undefined && typeof body.reason !== 'string' || action === 'bind' && !['owner', 'subject'].includes(textOf(body.role))) {
502
+ sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'Ownership 2.1 control parameters are invalid' });
503
+ return;
504
+ }
505
+ try {
506
+ let task;
507
+ switch (action) {
508
+ case 'bind':
509
+ task = body.role === 'owner' ? await this.client.bindSubaid(textOf(body.peer_aid)) : await this.client.bindOwneraid(textOf(body.peer_aid));
510
+ break;
511
+ case 'accept':
512
+ task = await this.client.acceptOwnershipRequest(textOf(body.operation_id));
513
+ break;
514
+ case 'reject':
515
+ task = await this.client.rejectOwnershipRequest(textOf(body.operation_id), body.reason);
516
+ break;
517
+ case 'release':
518
+ task = await this.client.releaseSubaid(textOf(body.sub_aid));
519
+ break;
520
+ default: task = await this.client.transferSubaid(textOf(body.sub_aid), textOf(body.new_owner_aid));
521
+ }
522
+ sendJson(res, 200, { ok: true, operation_id: task.operation_id, task: jsonSafe(task) });
523
+ }
524
+ catch (error) {
525
+ sendJson(res, 500, { ok: false, error_code: ownershipErrorCode(error), error_message: error instanceof Error ? error.message : String(error) });
526
+ }
527
+ }
528
+ async ownershipStatus(url, res) {
529
+ try {
530
+ const internal = this.client;
531
+ if (!internal._tokenStore?.listOwnershipTasks)
532
+ throw new StateError('ownership durable task storage is unavailable', { stringCode: 'persistence_unavailable' });
533
+ const operationId = textOf(url.searchParams.get('operation_id')).trim();
534
+ const requestedSub = textOf(url.searchParams.get('sub_aid')).trim();
535
+ const records = (await internal._tokenStore.listOwnershipTasks(this.aid))
536
+ .map((snapshot) => snapshot.record)
537
+ .filter((record) => (!operationId || record.operation_id === operationId) && (!requestedSub || record.sub_aid === requestedSub))
538
+ .sort((first, second) => second.created_at - first.created_at);
539
+ const publicTask = (record) => {
540
+ const localApproval = { pending: 'pending', accept: 'accepted', reject: 'rejected' }[record.local_decision];
541
+ if (!localApproval || typeof record.initiator_aid !== 'string' || !record.initiator_aid)
542
+ throw new StateError('ownership persisted approval is damaged', { stringCode: 'persistence_unavailable' });
543
+ return { ...ownershipTaskResult(record), initiator_aid: record.initiator_aid, local_approval: localApproval };
544
+ };
545
+ const tasks = records.map(publicTask), task = tasks[0] ?? null, record = records[0];
546
+ const subAid = record?.sub_aid || requestedSub || this.aid;
547
+ const consensus = await this.client.checkOwnershipConsensus(subAid);
548
+ const files = { agent_md: null, owner_md: null, storage_agent_md: null };
549
+ const fileErrors = {};
550
+ const evidence = (raw) => ({ raw_base64: Buffer.from(raw, 'utf-8').toString('base64'), sha256: crypto.createHash('sha256').update(raw, 'utf-8').digest('hex') });
551
+ const errorText = (error) => ownershipErrorCode(error) + ': ' + (error instanceof Error ? error.message : String(error));
552
+ let ns = null;
553
+ try {
554
+ if (!internal._agentMdManager)
555
+ throw new StateError('NS fresh reader is unavailable', { stringCode: 'unavailable' });
556
+ ns = await internal._agentMdManager.readOwnershipFresh(subAid);
557
+ if (ns.exists)
558
+ files.agent_md = evidence(ns.content);
559
+ else
560
+ fileErrors.agent_md = 'not_found';
561
+ }
562
+ catch (error) {
563
+ fileErrors.agent_md = errorText(error);
564
+ }
565
+ const storage = new OwnershipStorageAdapter({ currentAid: () => this.aid, call: (method, params) => this.client.call(method, params) });
566
+ const ownerAid = record ? record.operation === 'transfer' ? record.new_owner_aid : record.owner_aid : consensus.owner_aid;
567
+ if (ownerAid) {
568
+ try {
569
+ const owner = await storage.readOwnerMd(ownerAid, subAid);
570
+ if (owner.exists)
571
+ files.owner_md = evidence(owner.content);
572
+ else
573
+ fileErrors.owner_md = 'not_found';
574
+ }
575
+ catch (error) {
576
+ fileErrors.owner_md = errorText(error);
577
+ }
578
+ }
579
+ else
580
+ fileErrors.owner_md = 'owner_unavailable';
581
+ try {
582
+ const copy = await storage.readFile(subAid, 'agent.md');
583
+ if (copy.exists)
584
+ files.storage_agent_md = evidence(copy.content);
585
+ else
586
+ fileErrors.storage_agent_md = 'not_found';
587
+ }
588
+ catch (error) {
589
+ fileErrors.storage_agent_md = errorText(error);
590
+ }
591
+ sendJson(res, 200, { ok: true, aid: this.aid, task: jsonSafe(task), tasks: jsonSafe(tasks), events: this.ownershipEvents, consensus: jsonSafe(consensus), ns_protocol: ns?.protocol ?? null, files, file_errors: fileErrors });
592
+ }
593
+ catch (error) {
594
+ sendJson(res, 500, { ok: false, error_code: ownershipErrorCode(error), error_message: error instanceof Error ? error.message : String(error) });
595
+ }
596
+ }
442
597
  async handle(req, res) {
443
598
  const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
444
599
  try {
@@ -478,6 +633,7 @@ class CrossSdkJsAgent {
478
633
  this.rawPushRpcMethods = [];
479
634
  this.groupChangedEvents = [];
480
635
  this.groupInviteEvents = [];
636
+ this.readStateEvents = [];
481
637
  this.rawPushObservationId = textOf(body.observation_id ?? '');
482
638
  sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
483
639
  return;
@@ -516,6 +672,7 @@ class CrossSdkJsAgent {
516
672
  rpc_calls: rpcEntries,
517
673
  group_changed_events: groupChangedEvents.slice(-limit),
518
674
  group_invite_events: groupInviteEvents.slice(-limit),
675
+ read_state_events: this.readStateEvents.slice(-limit),
519
676
  });
520
677
  return;
521
678
  }
@@ -523,6 +680,22 @@ class CrossSdkJsAgent {
523
680
  sendJson(res, 200, await this.identity());
524
681
  return;
525
682
  }
683
+ if (req.method === 'POST' && url.pathname === '/signature/call') {
684
+ await this.signatureCall(req, res);
685
+ return;
686
+ }
687
+ if (req.method === 'POST' && ['/ownership/bind', '/ownership/accept', '/ownership/reject', '/ownership/release', '/ownership/transfer'].includes(url.pathname)) {
688
+ await this.ownershipControl(req, res, url.pathname.slice('/ownership/'.length));
689
+ return;
690
+ }
691
+ if (req.method === 'GET' && url.pathname === '/ownership/status') {
692
+ await this.ownershipStatus(url, res);
693
+ return;
694
+ }
695
+ if (req.method === 'GET' && url.pathname === '/ownership/events') {
696
+ sendJson(res, 200, { ok: true, events: this.ownershipEvents });
697
+ return;
698
+ }
526
699
  if (req.method === 'POST' && url.pathname === '/send') {
527
700
  await this.send(req, res);
528
701
  return;
@@ -535,6 +708,10 @@ class CrossSdkJsAgent {
535
708
  await this.pull(req, res);
536
709
  return;
537
710
  }
711
+ if (req.method === 'POST' && url.pathname === '/message/call') {
712
+ await this.messageCall(req, res);
713
+ return;
714
+ }
538
715
  if (req.method === 'GET' && url.pathname === '/sync/state') {
539
716
  this.syncState(url, res);
540
717
  return;
@@ -655,6 +832,77 @@ class CrossSdkJsAgent {
655
832
  });
656
833
  }
657
834
  }
835
+ async signatureCall(req, res) {
836
+ const body = await readJson(req);
837
+ const action = textOf(body.action), aid = textOf(body.aid || this.aid);
838
+ const store = this.aidStore();
839
+ let fileBytes;
840
+ let identity;
841
+ const output = (result) => {
842
+ const response = result.ok ? { ok: true, result: jsonSafe(result.data) } : { ok: false, error_code: result.error.code, error_message: result.error.message };
843
+ if (identity) {
844
+ response.certificate = identity.certPem;
845
+ response.cert_fingerprint = identity.certFingerprint;
846
+ }
847
+ if (fileBytes)
848
+ response.file_base64 = Buffer.from(fileBytes).toString('base64');
849
+ sendJson(res, 200, response);
850
+ };
851
+ try {
852
+ if (!['certificate', 'sign', 'verify', 'upload_agent_md', 'download_agent_md'].includes(action)) {
853
+ output({ ok: false, error: { code: 'unsupported_action', message: action } });
854
+ return;
855
+ }
856
+ if (action === 'upload_agent_md') {
857
+ output(await store.uploadAgentMd(aid, textOf(body.input)));
858
+ return;
859
+ }
860
+ if (action === 'download_agent_md') {
861
+ output(await store.downloadAgentMd(aid));
862
+ return;
863
+ }
864
+ let input = textOf(body.input);
865
+ if (body.input_type === 'file') {
866
+ fileBytes = new Uint8Array(typeof body.input_base64 === 'string' ? Buffer.from(body.input_base64, 'base64') : Buffer.from(textOf(body.input), 'utf8'));
867
+ input = body.in_place === true ? {
868
+ getFile: async () => new Blob([new Uint8Array(fileBytes)]),
869
+ createWritable: async () => ({ write: async (content) => { fileBytes = new TextEncoder().encode(content); }, close: async () => { } }),
870
+ } : new Blob([new Uint8Array(fileBytes)]);
871
+ }
872
+ if (action !== 'verify') {
873
+ const loaded = await store.load(aid);
874
+ if (!loaded.ok) {
875
+ output(loaded);
876
+ return;
877
+ }
878
+ identity = loaded.data.aid;
879
+ if (action === 'certificate') {
880
+ output({ ok: true, data: { aid, certificate: identity.certPem, cert_fingerprint: identity.certFingerprint } });
881
+ return;
882
+ }
883
+ const options = { identity, inputType: body.input_type, format: body.format, append: body.append, tag: body.tag, inPlace: body.in_place };
884
+ output(await store.sign(aid, input, options));
885
+ return;
886
+ }
887
+ const certificates = jsonObjectOf(body.certificates) ?? {};
888
+ const resolveCertificate = (signer) => {
889
+ const certificate = textOf(certificates[signer]);
890
+ if (!certificate)
891
+ throw new Error(`verification certificate missing for AID: ${signer}`);
892
+ return certificate;
893
+ };
894
+ if (body.format !== 'document' || certificates[aid])
895
+ identity = await AID.create({ aid, aunPath: this.aunPath, certPem: resolveCertificate(aid), privateKeyPem: null, certValid: true, privateKeyValid: false });
896
+ const options = { identity, inputType: body.input_type, format: body.format, certificateResolver: resolveCertificate };
897
+ output(await store.verify(aid, input, textOf(body.signature), options));
898
+ }
899
+ catch (error) {
900
+ output({ ok: false, error: { code: action === 'verify' ? 'VERIFICATION_OPERATION_ERROR' : 'SIGNATURE_OPERATION_ERROR', message: error instanceof Error ? error.message : String(error) } });
901
+ }
902
+ finally {
903
+ store.close();
904
+ }
905
+ }
658
906
  async collabCall(req, res) {
659
907
  const body = await readJson(req);
660
908
  const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
@@ -678,6 +926,84 @@ class CrossSdkJsAgent {
678
926
  sendJson(res, 500, error);
679
927
  }
680
928
  }
929
+ async orgProtocol(params) {
930
+ const op = textOf(params.op);
931
+ const now = Math.floor(Date.now() / 1000);
932
+ if (op === 'request')
933
+ return buildRequestBinding(textOf(params.method), textOf(params.target), params.params, textOf(params.credential_cid), textOf(params.nonce), textOf(params.request_id), now, now + 300);
934
+ if (['register', 'sign', 'certificate', 'create', 'prepare'].includes(op)) {
935
+ const store = this.aidStore();
936
+ try {
937
+ if (op === 'prepare') {
938
+ const request = jsonObjectOf(params.params) ?? {};
939
+ const orgId = textOf(params.org_id ?? request.org_id);
940
+ const loaded = orgId ? await store.load(orgId) : null;
941
+ const orgSigner = loaded?.ok && loaded.data.aid.isPrivateKeyValid() ? loaded.data.aid : undefined;
942
+ return await this.client.org.prepareRequest(textOf(params.method), request, textOf(params.target), this.client.currentAid, { ...jsonObjectOf(params.options), orgSigner });
943
+ }
944
+ if (op === 'register') {
945
+ const registered = await store.register(textOf(params.org_id), { type: 'org', owner: this.aid, exttype: 'company' });
946
+ if (!registered.ok)
947
+ throw new Error(String(registered.error));
948
+ }
949
+ const loaded = op === 'certificate'
950
+ ? await store.resolve(textOf(params.as_aid), { skipAgentMd: true })
951
+ : (params.as_aid || params.org_id) ? await store.load(textOf(params.as_aid ?? params.org_id)) : null;
952
+ if (loaded && !loaded.ok)
953
+ throw new Error(String(loaded.error));
954
+ const identity = loaded?.data?.aid ?? this.client.currentAid;
955
+ const result = { cert_fingerprint: identity.certFingerprint, certificate: identity.certPem };
956
+ if (op === 'sign') {
957
+ const signed = await identity.sign(canonicalJson(params.payload));
958
+ if (!signed.ok)
959
+ throw new Error(String(signed.error));
960
+ result.signature = { cert_fingerprint: identity.certFingerprint, timestamp: now, signature: signed.data.signature };
961
+ }
962
+ else if (op === 'create') {
963
+ result.credential = params.membership_binding
964
+ ? await this.client.org.member.createCredential(params.membership_binding, identity, params.options)
965
+ : await this.client.credential.create(params.options, identity);
966
+ }
967
+ return result;
968
+ }
969
+ finally {
970
+ store.close();
971
+ }
972
+ }
973
+ const value = params.document ?? params.payload;
974
+ const result = { canonical: new TextDecoder().decode(canonicalJson(value)), digest: digest(value), error_code: '' };
975
+ if (op === 'verify' || op === 'verify_full') {
976
+ const verificationNow = typeof params.now === 'number' ? params.now : now;
977
+ const certificates = params.certificates;
978
+ const resolver = (fingerprint) => certificates[fingerprint];
979
+ try {
980
+ if (op === 'verify_full') {
981
+ const actual = jsonObjectOf(params.context) ?? params;
982
+ const memberships = jsonObjectOf(params.memberships) ?? {};
983
+ result.verification = { ...await this.client.credential.verify(value, { resolveCertificate: resolver, resolveActorCertificate: resolver,
984
+ resolveStatus: (cid) => params.statuses?.[cid], now: verificationNow, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
985
+ ns: textOf(actual.ns), action: textOf(actual.action), resource: textOf(actual.resource), rootIssuer: textOf(actual.root_issuer) || undefined,
986
+ params: jsonObjectOf(actual.params) ?? {}, units: jsonObjectOf(actual.units) ?? {}, clientIp: textOf(actual.client_ip), method: textOf(actual.method), target: textOf(actual.target),
987
+ requestId: textOf(actual.request_id), nonce: textOf(actual.nonce), requestBinding: actual.request_binding, proofChain: actual.proof_chain, membershipBinding: actual.membership_binding,
988
+ orgSigner: textOf(actual.org_signer) || undefined, actor: textOf(actual.actor) || undefined, for: textOf(actual.for) || undefined, representationProof: actual.representation_proof,
989
+ resolveMembershipStatus: (binding, credential) => { const current = jsonObjectOf(memberships[String(binding.membership_id)]); return current?.status === 'active' && current.credential_cid === credential.cid; },
990
+ resolveOwnership: (subject, owner) => (jsonObjectOf(params.ownerships) ?? {})[subject] === owner, checkRateLimit: () => params.rate_limit_allowed === true }) };
991
+ }
992
+ else if (params.kind === 'credential')
993
+ result.verification = { ...await this.client.credential.verifySignature(value, resolver, verificationNow, textOf(params.audience)) };
994
+ else if (params.kind === 'binding')
995
+ await verifyMembershipBinding(value, resolver, verificationNow, textOf(params.expected_org_signer));
996
+ else
997
+ result.verification = { ...await this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid), verificationNow) };
998
+ }
999
+ catch (error) {
1000
+ if (!(error instanceof ProtocolError))
1001
+ throw error;
1002
+ result.error_code = error.code;
1003
+ }
1004
+ }
1005
+ return result;
1006
+ }
681
1007
  async groupCall(req, res) {
682
1008
  const body = await readJson(req);
683
1009
  const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
@@ -693,6 +1019,48 @@ class CrossSdkJsAgent {
693
1019
  try {
694
1020
  let result;
695
1021
  switch (method) {
1022
+ case 'sdk.org.protocol':
1023
+ result = await this.orgProtocol(params);
1024
+ break;
1025
+ case 'org.lifecycle.create':
1026
+ result = await this.client.org.lifecycle.create(params);
1027
+ break;
1028
+ case 'org.lifecycle.dissolve':
1029
+ result = await this.client.org.lifecycle.dissolve(params);
1030
+ break;
1031
+ case 'org.lifecycle.transfer':
1032
+ result = await this.client.org.lifecycle.transfer(params);
1033
+ break;
1034
+ case 'org.lifecycle.transferAccept':
1035
+ result = await this.client.org.lifecycle.transferAccept(params);
1036
+ break;
1037
+ case 'org.member.join':
1038
+ result = await this.client.org.member.join(params);
1039
+ break;
1040
+ case 'org.member.approveJoin':
1041
+ result = await this.client.org.member.approveJoin(params);
1042
+ break;
1043
+ case 'org.member.rejectJoin':
1044
+ result = await this.client.org.member.rejectJoin(params);
1045
+ break;
1046
+ case 'org.member.invite':
1047
+ result = await this.client.org.member.invite(params);
1048
+ break;
1049
+ case 'org.member.inviteAccept':
1050
+ result = await this.client.org.member.inviteAccept(params);
1051
+ break;
1052
+ case 'org.member.inviteReject':
1053
+ result = await this.client.org.member.inviteReject(params);
1054
+ break;
1055
+ case 'org.member.leave':
1056
+ result = await this.client.org.member.leave(params);
1057
+ break;
1058
+ case 'org.member.offboard':
1059
+ result = await this.client.org.member.offboard(params);
1060
+ break;
1061
+ case 'org.credential.getStatus':
1062
+ result = await this.client.org.credential.getStatus(params);
1063
+ break;
696
1064
  case 'sdk.update_group_index':
697
1065
  result = await this.client.group.updateGroupIndex(params);
698
1066
  break;
@@ -726,6 +1094,12 @@ class CrossSdkJsAgent {
726
1094
  case 'group.get_member_statuses':
727
1095
  result = await this.client.group.getMemberStatuses(params);
728
1096
  break;
1097
+ case 'group.read':
1098
+ result = await this.client.group.read(params);
1099
+ break;
1100
+ case 'group.read_state':
1101
+ result = await this.client.group.readState(params);
1102
+ break;
729
1103
  default: result = await this.client.call(method, params);
730
1104
  }
731
1105
  const response = { ok: true, trace_id: traceId, method, result: jsonSafe(result) };
@@ -1395,6 +1769,39 @@ class CrossSdkJsAgent {
1395
1769
  });
1396
1770
  }
1397
1771
  }
1772
+ async messageCall(req, res) {
1773
+ const body = await readJson(req);
1774
+ const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
1775
+ const method = textOf(body.method).trim();
1776
+ const params = jsonObjectOf(body.params) ?? {};
1777
+ if (!method) {
1778
+ sendJson(res, 400, { ok: false, trace_id: traceId, error_code: 'bad_request', error_message: 'method is required' });
1779
+ return;
1780
+ }
1781
+ try {
1782
+ let result;
1783
+ if (method === 'message.read')
1784
+ result = await this.client.message.read(params);
1785
+ else if (method === 'message.read_state')
1786
+ result = await this.client.message.readState(params);
1787
+ else
1788
+ result = await this.client.call(method, params);
1789
+ const response = { ok: true, trace_id: traceId, method, result: jsonSafe(result) };
1790
+ this.recordTrace(traceId, { stage: 'message_call', method, result: response });
1791
+ sendJson(res, 200, response);
1792
+ }
1793
+ catch (err) {
1794
+ const error = {
1795
+ ok: false,
1796
+ trace_id: traceId,
1797
+ method,
1798
+ error_code: err instanceof Error ? err.name : 'Error',
1799
+ error_message: err instanceof Error ? err.message : String(err),
1800
+ };
1801
+ this.recordTrace(traceId, { stage: 'message_call_error', method, error });
1802
+ sendJson(res, 500, error);
1803
+ }
1804
+ }
1398
1805
  syncNamespace(scope, groupId = '') {
1399
1806
  if (scope === 'p2p')
1400
1807
  return `p2p:${this.aid}`;
@@ -1831,5 +2238,6 @@ async function main() {
1831
2238
  process.on('SIGINT', () => { void shutdown(); });
1832
2239
  process.on('SIGTERM', () => { void shutdown(); });
1833
2240
  }
1834
- void main();
2241
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url))
2242
+ void main();
1835
2243
  //# sourceMappingURL=cross-sdk-agent.js.map