@agentunion/fastaun-browser 0.5.18 → 0.5.19
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/CHANGELOG.md +44 -2
- package/_packed_docs/CHANGELOG.md +44 -2
- package/_packed_docs/INDEX.md +136 -14
- package/_packed_docs/KITE_DOCS_GUIDE.md +42 -16
- package/_packed_docs/agent.md/SCHEMA.md +245 -205
- 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
- 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
- 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
- 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
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +28 -9
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +22 -27
- package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +5 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +250 -283
- package/_packed_docs/sdk/09-group-rpc-manual.md +46 -14
- package/_packed_docs/sdk/09-message-rpc-manual.md +40 -2
- package/_packed_docs/sdk/09-storage-rpc-manual.md +74 -64
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +25 -5
- package/_packed_docs/sdk/INDEX.md +76 -17
- package/dist/agent-md-schema.d.ts +3 -0
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +45 -11
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +45 -2
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +519 -30
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +12 -46
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +113 -246
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +20 -7
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +4 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +343 -23
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +37330 -32005
- package/dist/cert-utils.d.ts +1 -1
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +19 -7
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +15 -1
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +147 -51
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +19 -5
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +104 -30
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +100 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1204 -23
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +41 -1
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +82 -2
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +98 -4
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +17 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +10 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +111 -0
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +7 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +30 -1
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.d.ts.map +1 -0
- package/dist/org-protocol.js +1742 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +93 -5
- package/dist/ownership-proof.d.ts.map +1 -1
- package/dist/ownership-proof.js +239 -53
- package/dist/ownership-proof.js.map +1 -1
- package/dist/ownership-v21.d.ts +177 -0
- package/dist/ownership-v21.d.ts.map +1 -0
- package/dist/ownership-v21.js +609 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +146 -0
- package/dist/ownership-workflow.d.ts.map +1 -0
- package/dist/ownership-workflow.js +1066 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +10 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +82 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
- package/dist/tools/cross-sdk-agent.js +332 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +6 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +88 -2
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
- package/dist/v2/e2ee/decrypt.js +9 -1
- package/dist/v2/e2ee/decrypt.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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
|
|
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
|
|
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
|
|
335
|
-
|
|
336
|
-
|
|
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, revision) => ({ raw_base64: Buffer.from(raw, 'utf-8').toString('base64'), sha256: crypto.createHash('sha256').update(raw, 'utf-8').digest('hex'), ...(revision ? { revision } : {}) });
|
|
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, owner.revision);
|
|
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, copy.revision);
|
|
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,18 @@ class CrossSdkJsAgent {
|
|
|
523
680
|
sendJson(res, 200, await this.identity());
|
|
524
681
|
return;
|
|
525
682
|
}
|
|
683
|
+
if (req.method === 'POST' && ['/ownership/bind', '/ownership/accept', '/ownership/reject', '/ownership/release', '/ownership/transfer'].includes(url.pathname)) {
|
|
684
|
+
await this.ownershipControl(req, res, url.pathname.slice('/ownership/'.length));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (req.method === 'GET' && url.pathname === '/ownership/status') {
|
|
688
|
+
await this.ownershipStatus(url, res);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (req.method === 'GET' && url.pathname === '/ownership/events') {
|
|
692
|
+
sendJson(res, 200, { ok: true, events: this.ownershipEvents });
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
526
695
|
if (req.method === 'POST' && url.pathname === '/send') {
|
|
527
696
|
await this.send(req, res);
|
|
528
697
|
return;
|
|
@@ -535,6 +704,10 @@ class CrossSdkJsAgent {
|
|
|
535
704
|
await this.pull(req, res);
|
|
536
705
|
return;
|
|
537
706
|
}
|
|
707
|
+
if (req.method === 'POST' && url.pathname === '/message/call') {
|
|
708
|
+
await this.messageCall(req, res);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
538
711
|
if (req.method === 'GET' && url.pathname === '/sync/state') {
|
|
539
712
|
this.syncState(url, res);
|
|
540
713
|
return;
|
|
@@ -678,6 +851,76 @@ class CrossSdkJsAgent {
|
|
|
678
851
|
sendJson(res, 500, error);
|
|
679
852
|
}
|
|
680
853
|
}
|
|
854
|
+
async orgProtocol(params) {
|
|
855
|
+
const op = textOf(params.op);
|
|
856
|
+
const now = Math.floor(Date.now() / 1000);
|
|
857
|
+
if (op === 'request')
|
|
858
|
+
return buildRequestBinding(textOf(params.method), textOf(params.target), params.params, textOf(params.credential_cid), textOf(params.nonce), textOf(params.request_id), now, now + 300);
|
|
859
|
+
if (['register', 'sign', 'certificate', 'create'].includes(op)) {
|
|
860
|
+
const store = this.aidStore();
|
|
861
|
+
try {
|
|
862
|
+
if (op === 'register') {
|
|
863
|
+
const registered = await store.register(textOf(params.org_id), { type: 'org', owner: this.aid, exttype: 'company' });
|
|
864
|
+
if (!registered.ok)
|
|
865
|
+
throw new Error(String(registered.error));
|
|
866
|
+
}
|
|
867
|
+
const loaded = op === 'certificate'
|
|
868
|
+
? await store.resolve(textOf(params.as_aid), { skipAgentMd: true })
|
|
869
|
+
: (params.as_aid || params.org_id) ? await store.load(textOf(params.as_aid ?? params.org_id)) : null;
|
|
870
|
+
if (loaded && !loaded.ok)
|
|
871
|
+
throw new Error(String(loaded.error));
|
|
872
|
+
const identity = loaded?.data?.aid ?? this.client.currentAid;
|
|
873
|
+
const result = { cert_fingerprint: identity.certFingerprint, certificate: identity.certPem };
|
|
874
|
+
if (op === 'sign') {
|
|
875
|
+
const signed = await identity.sign(canonicalJson(params.payload));
|
|
876
|
+
if (!signed.ok)
|
|
877
|
+
throw new Error(String(signed.error));
|
|
878
|
+
result.signature = { cert_fingerprint: identity.certFingerprint, timestamp: now, signature: signed.data.signature };
|
|
879
|
+
}
|
|
880
|
+
else if (op === 'create') {
|
|
881
|
+
result.credential = params.membership_binding
|
|
882
|
+
? await this.client.org.member.createCredential(params.membership_binding, identity, params.options)
|
|
883
|
+
: await this.client.credential.create(params.options, identity);
|
|
884
|
+
}
|
|
885
|
+
return result;
|
|
886
|
+
}
|
|
887
|
+
finally {
|
|
888
|
+
store.close();
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
const value = params.document ?? params.payload;
|
|
892
|
+
const result = { canonical: new TextDecoder().decode(canonicalJson(value)), digest: digest(value), error_code: '' };
|
|
893
|
+
if (op === 'verify' || op === 'verify_full') {
|
|
894
|
+
const certificates = params.certificates;
|
|
895
|
+
const resolver = (fingerprint) => certificates[fingerprint];
|
|
896
|
+
try {
|
|
897
|
+
if (op === 'verify_full') {
|
|
898
|
+
const actual = jsonObjectOf(params.context) ?? params;
|
|
899
|
+
const memberships = jsonObjectOf(params.memberships) ?? {};
|
|
900
|
+
result.verification = { ...await this.client.credential.verify(value, { resolveCertificate: resolver, resolveActorCertificate: resolver,
|
|
901
|
+
resolveStatus: (cid) => params.statuses?.[cid], now, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
|
|
902
|
+
ns: textOf(actual.ns), action: textOf(actual.action), resource: textOf(actual.resource), rootIssuer: textOf(actual.root_issuer) || undefined,
|
|
903
|
+
params: jsonObjectOf(actual.params) ?? {}, units: jsonObjectOf(actual.units) ?? {}, clientIp: textOf(actual.client_ip), method: textOf(actual.method), target: textOf(actual.target),
|
|
904
|
+
requestId: textOf(actual.request_id), nonce: textOf(actual.nonce), requestBinding: actual.request_binding, proofChain: actual.proof_chain, membershipBinding: actual.membership_binding,
|
|
905
|
+
orgSigner: textOf(actual.org_signer) || undefined, actor: textOf(actual.actor) || undefined, for: textOf(actual.for) || undefined, representationProof: actual.representation_proof,
|
|
906
|
+
resolveMembershipStatus: (binding, credential) => { const current = jsonObjectOf(memberships[String(binding.membership_id)]); return current?.status === 'active' && current.credential_cid === credential.cid; },
|
|
907
|
+
resolveOwnership: (subject, owner) => (jsonObjectOf(params.ownerships) ?? {})[subject] === owner, checkRateLimit: () => params.rate_limit_allowed === true }) };
|
|
908
|
+
}
|
|
909
|
+
else if (params.kind === 'credential')
|
|
910
|
+
result.verification = { ...await this.client.credential.verifySignature(value, resolver, now, textOf(params.audience)) };
|
|
911
|
+
else if (params.kind === 'binding')
|
|
912
|
+
await verifyMembershipBinding(value, resolver, now, textOf(params.expected_org_signer));
|
|
913
|
+
else
|
|
914
|
+
result.verification = { ...await this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid), now) };
|
|
915
|
+
}
|
|
916
|
+
catch (error) {
|
|
917
|
+
if (!(error instanceof ProtocolError))
|
|
918
|
+
throw error;
|
|
919
|
+
result.error_code = error.code;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return result;
|
|
923
|
+
}
|
|
681
924
|
async groupCall(req, res) {
|
|
682
925
|
const body = await readJson(req);
|
|
683
926
|
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
@@ -693,6 +936,48 @@ class CrossSdkJsAgent {
|
|
|
693
936
|
try {
|
|
694
937
|
let result;
|
|
695
938
|
switch (method) {
|
|
939
|
+
case 'sdk.org.protocol':
|
|
940
|
+
result = await this.orgProtocol(params);
|
|
941
|
+
break;
|
|
942
|
+
case 'org.lifecycle.create':
|
|
943
|
+
result = await this.client.org.lifecycle.create(params);
|
|
944
|
+
break;
|
|
945
|
+
case 'org.lifecycle.dissolve':
|
|
946
|
+
result = await this.client.org.lifecycle.dissolve(params);
|
|
947
|
+
break;
|
|
948
|
+
case 'org.lifecycle.transfer':
|
|
949
|
+
result = await this.client.org.lifecycle.transfer(params);
|
|
950
|
+
break;
|
|
951
|
+
case 'org.lifecycle.transferAccept':
|
|
952
|
+
result = await this.client.org.lifecycle.transferAccept(params);
|
|
953
|
+
break;
|
|
954
|
+
case 'org.member.join':
|
|
955
|
+
result = await this.client.org.member.join(params);
|
|
956
|
+
break;
|
|
957
|
+
case 'org.member.approveJoin':
|
|
958
|
+
result = await this.client.org.member.approveJoin(params);
|
|
959
|
+
break;
|
|
960
|
+
case 'org.member.rejectJoin':
|
|
961
|
+
result = await this.client.org.member.rejectJoin(params);
|
|
962
|
+
break;
|
|
963
|
+
case 'org.member.invite':
|
|
964
|
+
result = await this.client.org.member.invite(params);
|
|
965
|
+
break;
|
|
966
|
+
case 'org.member.inviteAccept':
|
|
967
|
+
result = await this.client.org.member.inviteAccept(params);
|
|
968
|
+
break;
|
|
969
|
+
case 'org.member.inviteReject':
|
|
970
|
+
result = await this.client.org.member.inviteReject(params);
|
|
971
|
+
break;
|
|
972
|
+
case 'org.member.leave':
|
|
973
|
+
result = await this.client.org.member.leave(params);
|
|
974
|
+
break;
|
|
975
|
+
case 'org.member.offboard':
|
|
976
|
+
result = await this.client.org.member.offboard(params);
|
|
977
|
+
break;
|
|
978
|
+
case 'org.credential.getStatus':
|
|
979
|
+
result = await this.client.org.credential.getStatus(params);
|
|
980
|
+
break;
|
|
696
981
|
case 'sdk.update_group_index':
|
|
697
982
|
result = await this.client.group.updateGroupIndex(params);
|
|
698
983
|
break;
|
|
@@ -726,6 +1011,12 @@ class CrossSdkJsAgent {
|
|
|
726
1011
|
case 'group.get_member_statuses':
|
|
727
1012
|
result = await this.client.group.getMemberStatuses(params);
|
|
728
1013
|
break;
|
|
1014
|
+
case 'group.read':
|
|
1015
|
+
result = await this.client.group.read(params);
|
|
1016
|
+
break;
|
|
1017
|
+
case 'group.read_state':
|
|
1018
|
+
result = await this.client.group.readState(params);
|
|
1019
|
+
break;
|
|
729
1020
|
default: result = await this.client.call(method, params);
|
|
730
1021
|
}
|
|
731
1022
|
const response = { ok: true, trace_id: traceId, method, result: jsonSafe(result) };
|
|
@@ -1395,6 +1686,39 @@ class CrossSdkJsAgent {
|
|
|
1395
1686
|
});
|
|
1396
1687
|
}
|
|
1397
1688
|
}
|
|
1689
|
+
async messageCall(req, res) {
|
|
1690
|
+
const body = await readJson(req);
|
|
1691
|
+
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
1692
|
+
const method = textOf(body.method).trim();
|
|
1693
|
+
const params = jsonObjectOf(body.params) ?? {};
|
|
1694
|
+
if (!method) {
|
|
1695
|
+
sendJson(res, 400, { ok: false, trace_id: traceId, error_code: 'bad_request', error_message: 'method is required' });
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
try {
|
|
1699
|
+
let result;
|
|
1700
|
+
if (method === 'message.read')
|
|
1701
|
+
result = await this.client.message.read(params);
|
|
1702
|
+
else if (method === 'message.read_state')
|
|
1703
|
+
result = await this.client.message.readState(params);
|
|
1704
|
+
else
|
|
1705
|
+
result = await this.client.call(method, params);
|
|
1706
|
+
const response = { ok: true, trace_id: traceId, method, result: jsonSafe(result) };
|
|
1707
|
+
this.recordTrace(traceId, { stage: 'message_call', method, result: response });
|
|
1708
|
+
sendJson(res, 200, response);
|
|
1709
|
+
}
|
|
1710
|
+
catch (err) {
|
|
1711
|
+
const error = {
|
|
1712
|
+
ok: false,
|
|
1713
|
+
trace_id: traceId,
|
|
1714
|
+
method,
|
|
1715
|
+
error_code: err instanceof Error ? err.name : 'Error',
|
|
1716
|
+
error_message: err instanceof Error ? err.message : String(err),
|
|
1717
|
+
};
|
|
1718
|
+
this.recordTrace(traceId, { stage: 'message_call_error', method, error });
|
|
1719
|
+
sendJson(res, 500, error);
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1398
1722
|
syncNamespace(scope, groupId = '') {
|
|
1399
1723
|
if (scope === 'p2p')
|
|
1400
1724
|
return `p2p:${this.aid}`;
|
|
@@ -1831,5 +2155,6 @@ async function main() {
|
|
|
1831
2155
|
process.on('SIGINT', () => { void shutdown(); });
|
|
1832
2156
|
process.on('SIGTERM', () => { void shutdown(); });
|
|
1833
2157
|
}
|
|
1834
|
-
|
|
2158
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url))
|
|
2159
|
+
void main();
|
|
1835
2160
|
//# sourceMappingURL=cross-sdk-agent.js.map
|