@agentunion/fastaun 0.5.19 → 0.5.21
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 +176 -78
- package/_packed_docs/CHANGELOG.md +176 -78
- package/_packed_docs/INDEX.md +288 -269
- package/_packed_docs/KITE_DOCS_GUIDE.md +101 -95
- package/_packed_docs/agent.md/SCHEMA.md +7 -7
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
- package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
- package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
- package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
- 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
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +372 -331
- package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +676 -676
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +8 -12
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +445 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +666 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
- package/_packed_docs/protocol/agent.md/SCHEMA.md +1 -1
- package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
- package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +70 -63
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +43 -40
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +179 -19
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +50 -49
- package/_packed_docs/sdk/09-group-rpc-manual.md +261 -254
- package/_packed_docs/sdk/09-message-rpc-manual.md +253 -160
- package/_packed_docs/sdk/09-storage-rpc-manual.md +18 -26
- package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +71 -0
- package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +88 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +105 -92
- package/_packed_docs/sdk/INDEX.md +197 -167
- package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +930 -0
- package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
- 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 +363 -0
- 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 +553 -0
- 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
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +480 -0
- package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
- package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/dist/agent-md-schema.d.ts +13 -1
- package/dist/agent-md-schema.js +63 -33
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +30 -3
- package/dist/agent-md.js +293 -152
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +42 -0
- package/dist/aid-store.js +224 -44
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.js +18 -13
- package/dist/aid.js.map +1 -1
- package/dist/auth.js +78 -2
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +44 -1
- package/dist/cert-utils.js +157 -56
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +68 -1
- package/dist/client/delivery.js +1196 -135
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +5 -1
- package/dist/client/rpc-pipeline.js +240 -111
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +12 -2
- package/dist/client/v2-e2ee.js +842 -379
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +26 -2
- package/dist/client.js +420 -75
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +23 -4
- package/dist/events.js +171 -15
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +43 -1
- package/dist/facades.js +368 -12
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +7 -2
- package/dist/keystore/aid-db.js +194 -43
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +15 -1
- package/dist/keystore/local-token-store.d.ts +10 -2
- package/dist/keystore/local-token-store.js +81 -16
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +45 -0
- package/dist/org-protocol.js +829 -22
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +40 -9
- package/dist/ownership-v21.js +174 -79
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +22 -0
- package/dist/ownership-workflow.js +861 -66
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/secret-store/file-store.js +84 -0
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +2 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +67 -3
- package/dist/tools/cross-sdk-agent.js +575 -42
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +4 -0
- package/dist/transport.js +108 -44
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2/e2ee/encrypt-group.js +1 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/e2ee/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -4,14 +4,17 @@ import * as https from 'node:https';
|
|
|
4
4
|
import * as crypto from 'node:crypto';
|
|
5
5
|
import * as fs from 'node:fs';
|
|
6
6
|
import * as path from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
7
8
|
import { URL } from 'node:url';
|
|
8
9
|
import { fileURLToPath } from 'node:url';
|
|
9
10
|
import { AUNClient } from '../client.js';
|
|
11
|
+
import { AID } from '../aid.js';
|
|
10
12
|
import { AIDStore } from '../aid-store.js';
|
|
11
13
|
import { publicKeyFingerprint } from '../cert-utils.js';
|
|
12
14
|
import { VERSION } from '../version.js';
|
|
13
15
|
import { canonicalJson, digest, buildRequestBinding, verifyMembershipBinding, ProtocolError } from '../org-protocol.js';
|
|
14
16
|
import { ownershipTaskResult } from '../ownership-workflow.js';
|
|
17
|
+
import { TimeoutError } from '../errors.js';
|
|
15
18
|
function envBool(name, fallback = false) {
|
|
16
19
|
const raw = process.env[name];
|
|
17
20
|
if (raw == null)
|
|
@@ -126,10 +129,25 @@ export class CrossSdkTsAgent {
|
|
|
126
129
|
rawPushObservations = [];
|
|
127
130
|
rawPushObservationId = '';
|
|
128
131
|
rawPushRpcMethods = [];
|
|
132
|
+
eventLedger = [];
|
|
133
|
+
batchEventMode = false;
|
|
134
|
+
batchEvents = [];
|
|
135
|
+
singleEventCounts = { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
|
|
136
|
+
eventBoundClients = new WeakSet();
|
|
137
|
+
batchEventClient;
|
|
138
|
+
batchEventHandlers = [];
|
|
139
|
+
rpcSpans = [];
|
|
140
|
+
observationGeneration = 0;
|
|
141
|
+
pullBarrier = null;
|
|
142
|
+
rpcFailures = [];
|
|
143
|
+
messageOperations = new Map();
|
|
129
144
|
rpcObservedTransports = new WeakSet();
|
|
145
|
+
rpcObservedPipelines = new WeakSet();
|
|
146
|
+
rpcObservedPulls = new WeakMap();
|
|
130
147
|
groupChangedEvents = [];
|
|
131
148
|
groupInviteEvents = [];
|
|
132
149
|
readStateEvents = [];
|
|
150
|
+
peersSnapshots = [];
|
|
133
151
|
ownershipEvents = [];
|
|
134
152
|
traces = new Map();
|
|
135
153
|
sendResults = new Map();
|
|
@@ -152,6 +170,12 @@ export class CrossSdkTsAgent {
|
|
|
152
170
|
return client;
|
|
153
171
|
}
|
|
154
172
|
bindClientEvents() {
|
|
173
|
+
this.eventBoundClients ??= new WeakSet();
|
|
174
|
+
if (this.eventBoundClients.has(this.client)) {
|
|
175
|
+
this.setBatchEventMode(this.batchEventMode ?? false);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
this.eventBoundClients.add(this.client);
|
|
155
179
|
this.installTransportRpcObserver();
|
|
156
180
|
this.client.on('_raw.message.received', (data) => this.storeRawPushObservation('_raw.message.received', 'p2p', 'legacy', data));
|
|
157
181
|
this.client.on('_raw.peer.v2.message_received', (data) => this.storeRawPushObservation('_raw.peer.v2.message_received', 'p2p', 'v2', data));
|
|
@@ -159,22 +183,30 @@ export class CrossSdkTsAgent {
|
|
|
159
183
|
this.client.on('_raw.group.v2.message_created', (data) => this.storeRawPushObservation('_raw.group.v2.message_created', 'group', 'v2', data));
|
|
160
184
|
this.client.on('_raw.group.changed', (data) => this.storeRawGroupChangedObservation(data));
|
|
161
185
|
this.client.on('message.received', (msg) => {
|
|
162
|
-
|
|
186
|
+
this.countSingleEvent('message.received');
|
|
187
|
+
void this.storeApplicationEvent('message.received', msg);
|
|
163
188
|
});
|
|
164
189
|
this.client.on('message.undecryptable', (msg) => {
|
|
165
190
|
void this.storeInboxItem(this.normalizeMessage(msg, false, 'undecryptable'));
|
|
166
191
|
});
|
|
167
192
|
this.client.on('group.message_created', (msg) => {
|
|
168
|
-
|
|
193
|
+
this.countSingleEvent('group.message_created');
|
|
194
|
+
void this.storeApplicationEvent('group.message_created', msg);
|
|
169
195
|
});
|
|
170
196
|
this.client.on('group.message_undecryptable', (msg) => {
|
|
171
197
|
void this.storeGroupInboxItem(this.normalizeGroupMessage(msg, false, 'undecryptable'));
|
|
172
198
|
});
|
|
173
|
-
this.client.on('group.changed', (data) =>
|
|
199
|
+
this.client.on('group.changed', (data) => {
|
|
200
|
+
this.countSingleEvent('group.changed');
|
|
201
|
+
void this.storeApplicationEvent('group.changed', data);
|
|
202
|
+
});
|
|
174
203
|
this.client.on('group.invite_received', (data) => this.storeGroupInviteEvent(data));
|
|
175
204
|
for (const event of ['message.read_changed', 'group.read_changed', 'read_state_sync']) {
|
|
176
205
|
this.client.on(event, (payload) => { this.readStateEvents.push({ event, payload: jsonSafe(payload) }); });
|
|
177
206
|
}
|
|
207
|
+
this.client.on('message.peers_snapshot', (payload) => {
|
|
208
|
+
this.peersSnapshots.push({ event: 'message.peers_snapshot', payload: jsonSafe(payload) });
|
|
209
|
+
});
|
|
178
210
|
for (const event of ['ownership.request', 'ownership.pending', 'ownership.progress', 'ownership.completed', 'ownership.rejected', 'ownership.stopped', 'ownership.error', 'ownership.action_required']) {
|
|
179
211
|
this.client.on(event, (payload) => {
|
|
180
212
|
this.ownershipEvents.push({ event, payload: jsonSafe(payload) });
|
|
@@ -187,15 +219,91 @@ export class CrossSdkTsAgent {
|
|
|
187
219
|
this.recordTrace(textOf(payload.org_id), { stage: event, payload });
|
|
188
220
|
});
|
|
189
221
|
}
|
|
222
|
+
this.setBatchEventMode(this.batchEventMode ?? false);
|
|
223
|
+
}
|
|
224
|
+
countSingleEvent(event) {
|
|
225
|
+
this.singleEventCounts ??= { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
|
|
226
|
+
this.singleEventCounts[event] = Number(this.singleEventCounts[event] ?? 0) + 1;
|
|
227
|
+
}
|
|
228
|
+
async storeApplicationEvent(event, value) {
|
|
229
|
+
if (event === 'message.received') {
|
|
230
|
+
this.storeEventLedger(event, value);
|
|
231
|
+
await this.storeInboxItem(this.normalizeMessage(value, true));
|
|
232
|
+
}
|
|
233
|
+
else if (event === 'group.message_created') {
|
|
234
|
+
this.storeEventLedger(event, value);
|
|
235
|
+
await this.storeGroupInboxItem(this.normalizeGroupMessage(value, true));
|
|
236
|
+
}
|
|
237
|
+
else if (event === 'group.changed')
|
|
238
|
+
this.storeGroupChangedPublicEvent(value);
|
|
239
|
+
}
|
|
240
|
+
setBatchEventMode(enabled) {
|
|
241
|
+
this.batchEventMode = enabled;
|
|
242
|
+
if (enabled && this.batchEventClient === this.client && this.batchEventHandlers?.length)
|
|
243
|
+
return;
|
|
244
|
+
for (const { event, handler } of this.batchEventHandlers ?? [])
|
|
245
|
+
this.batchEventClient?.off(event, handler);
|
|
246
|
+
this.batchEventClient = this.client;
|
|
247
|
+
this.batchEventHandlers = [];
|
|
248
|
+
if (!enabled)
|
|
249
|
+
return;
|
|
250
|
+
for (const event of ['message.batch', 'group.message_batch', 'group.event_batch']) {
|
|
251
|
+
const handler = async (value) => {
|
|
252
|
+
const data = jsonObjectOf(value) ?? {};
|
|
253
|
+
const items = JSON.parse(JSON.stringify(jsonSafe(Array.isArray(data.items) ? data.items : [])));
|
|
254
|
+
this.batchEvents ??= [];
|
|
255
|
+
this.batchEvents.push({ observation_id: this.rawPushObservationId, time_ms: Date.now(), event,
|
|
256
|
+
namespace: textOf(data.namespace), source: textOf(data.source), items });
|
|
257
|
+
for (const item of items)
|
|
258
|
+
await this.storeApplicationEvent(textOf(item.event), item.payload);
|
|
259
|
+
};
|
|
260
|
+
this.batchEventHandlers.push({ event, handler });
|
|
261
|
+
this.client.on(event, handler);
|
|
262
|
+
}
|
|
190
263
|
}
|
|
191
264
|
installTransportRpcObserver() {
|
|
265
|
+
const client = this.client;
|
|
266
|
+
const pipeline = client._rpcPipeline;
|
|
267
|
+
this.rpcObservedPipelines ??= new WeakSet();
|
|
268
|
+
this.rpcObservedPulls ??= new WeakMap();
|
|
269
|
+
if (pipeline?.bindActivePullCancellation && !this.rpcObservedPipelines.has(pipeline)) {
|
|
270
|
+
const originalBind = pipeline.bindActivePullCancellation;
|
|
271
|
+
this.rpcObservedPipelines.add(pipeline);
|
|
272
|
+
pipeline.bindActivePullCancellation = (method, params, request, gateKey = '') => {
|
|
273
|
+
const result = originalBind.call(pipeline, method, params, request, gateKey);
|
|
274
|
+
const observed = this.rpcObservedPulls.get(request);
|
|
275
|
+
if (!this.pullBarrier?.preserve || !observed || !['message.pull', 'message.v2.pull'].includes(method)
|
|
276
|
+
|| !textOf(params.sender_aid) || typeof client._delivery?.peerNamespace !== 'function')
|
|
277
|
+
return result;
|
|
278
|
+
const key = gateKey || pipeline.pullGateKeyForCall(method, params);
|
|
279
|
+
const namespace = client._delivery?.peerNamespace(textOf(params.sender_aid));
|
|
280
|
+
const owners = [...pipeline.pullGateStates.values()].flatMap((state) => [...state.activeByNamespace.values()])
|
|
281
|
+
.filter((job) => job.key === key && job.namespace === namespace && job.running && !job.timedOut && !job.invalidated && job.cancel);
|
|
282
|
+
if (owners.length === 1)
|
|
283
|
+
observed.owner = { job: owners[0], cancel: owners[0].cancel, key, namespace: namespace };
|
|
284
|
+
return result;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
192
287
|
const transport = this.client._transport;
|
|
193
288
|
transport?.setTraceMode('diag');
|
|
194
289
|
if (!transport?.call || this.rpcObservedTransports.has(transport))
|
|
195
290
|
return;
|
|
196
291
|
const originalCall = transport.call;
|
|
292
|
+
const originalClose = transport.close;
|
|
197
293
|
this.rpcObservedTransports.add(transport);
|
|
198
|
-
|
|
294
|
+
if (originalClose)
|
|
295
|
+
transport.close = async () => {
|
|
296
|
+
if (!this.pullBarrier?.preserve)
|
|
297
|
+
this.releasePullBarrier();
|
|
298
|
+
try {
|
|
299
|
+
await originalClose.call(transport);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
if (!this.pullBarrier?.preserve)
|
|
303
|
+
this.releasePullBarrier();
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
transport.call = (method, params, timeout, trace, background) => {
|
|
199
307
|
this.storeRawPushRpcMethod({ type: 'rpc', method, request_window: this.rawPushRequestWindow(params) });
|
|
200
308
|
let requestParams = params;
|
|
201
309
|
if (method === 'auth.connect' && !this.inlineRealtimePayloadV1 && jsonObjectOf(params)) {
|
|
@@ -207,8 +315,219 @@ export class CrossSdkTsAgent {
|
|
|
207
315
|
}
|
|
208
316
|
requestParams = request;
|
|
209
317
|
}
|
|
210
|
-
|
|
318
|
+
const generation = this.observationGeneration;
|
|
319
|
+
const span = this.startRpcSpan(method, requestParams);
|
|
320
|
+
const controller = new AbortController();
|
|
321
|
+
let request;
|
|
322
|
+
const observed = {};
|
|
323
|
+
const pending = (async () => {
|
|
324
|
+
try {
|
|
325
|
+
request = originalCall.call(transport, method, requestParams, timeout, trace, background);
|
|
326
|
+
const result = await request;
|
|
327
|
+
if (span) {
|
|
328
|
+
this.finishRpcResponse(span, result);
|
|
329
|
+
if (generation === this.observationGeneration && (this.isObservedPull(method) || this.pullBarrier?.method === method)) {
|
|
330
|
+
await this.waitForPullBarrier(method, controller.signal, timeout ?? transport._timeout ?? 35_000, span, observed.owner);
|
|
331
|
+
}
|
|
332
|
+
if (generation === this.observationGeneration)
|
|
333
|
+
this.injectRPCFailure(method, span);
|
|
334
|
+
span.success = true;
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (span) {
|
|
340
|
+
span.success = false;
|
|
341
|
+
span.error = error instanceof Error ? error.name : String(error);
|
|
342
|
+
}
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
if (span)
|
|
347
|
+
span.end_ms = Date.now();
|
|
348
|
+
}
|
|
349
|
+
})();
|
|
350
|
+
pending.cancel = () => { controller.abort(); request?.cancel?.(); };
|
|
351
|
+
this.rpcObservedPulls.set(pending, observed);
|
|
352
|
+
return pending;
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
storeEventLedger(event, value) {
|
|
356
|
+
const data = jsonObjectOf(value) ?? {};
|
|
357
|
+
const internal = this.client;
|
|
358
|
+
const payload = JSON.parse(stableStringify(jsonSafe(data.payload ?? null)));
|
|
359
|
+
this.eventLedger.push({
|
|
360
|
+
observation_id: this.rawPushObservationId, event, event_index: this.eventLedger.length + 1, time_ms: Date.now(),
|
|
361
|
+
scope: event === 'message.received' ? 'p2p' : 'group', receiver_aid: this.aid,
|
|
362
|
+
device_id: textOf(internal._deviceId), slot_id: textOf(internal._slotId ?? this.slotId),
|
|
363
|
+
sender_aid: textOf(data.sender_aid ?? data.from_aid ?? data.from), group_id: textOf(data.group_id),
|
|
364
|
+
message_id: textOf(data.message_id ?? data.id), seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
|
|
365
|
+
storage: textOf(data.storage), volatile_cursor: textOf(data.volatile_cursor),
|
|
366
|
+
payload, payload_sha256: sha256Json(payload), need_render: data.need_render !== false,
|
|
367
|
+
...(Object.prototype.hasOwnProperty.call(data, 'mention_mode') ? { mention_mode: data.mention_mode } : {}),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
isObservedPull(method) {
|
|
371
|
+
return ['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull', 'group.pull_messages', 'group.pull_events', 'group.events'].includes(method);
|
|
372
|
+
}
|
|
373
|
+
startRpcSpan(method, value) {
|
|
374
|
+
if (!this.isObservedPull(method) && !['message.peers', 'message.ack', 'message.v2.ack', 'group.ack', 'group.v2.ack', 'group.ack_messages', 'group.ack_events'].includes(method))
|
|
375
|
+
return null;
|
|
376
|
+
const params = jsonObjectOf(value) ?? {};
|
|
377
|
+
const senderAid = textOf(params.sender_aid), groupId = textOf(params.group_id ?? params.group_aid);
|
|
378
|
+
const namespace = method === 'message.peers' ? '' : method.startsWith('group.')
|
|
379
|
+
? groupId ? `${method === 'group.pull_events' || method === 'group.events' || method === 'group.ack_events' ? 'group_event' : 'group'}:${groupId}` : ''
|
|
380
|
+
: senderAid ? `p2p.peer.v1/${Buffer.from(senderAid, 'utf8').toString('base64url')}` : `p2p:${this.aid}`;
|
|
381
|
+
const span = {
|
|
382
|
+
observation_id: this.rawPushObservationId, span_id: crypto.randomUUID(), boundary: 'transport_call', method,
|
|
383
|
+
start_ms: Date.now(), response_ms: null, end_ms: null, success: null,
|
|
384
|
+
cursor_scope: textOf(params.cursor_scope) || (method === 'message.peers' || senderAid ? 'peer' : method.startsWith('group.') ? namespace.startsWith('group_event:') ? 'group_event' : 'group' : 'global'), sender_aid: senderAid,
|
|
385
|
+
group_id: groupId, namespace, request_window: this.observedRequestWindow(params),
|
|
386
|
+
response_message_count: 0, response_volatile_count: 0, response_event_count: 0,
|
|
387
|
+
response_item_keys: [], response_event_keys: [],
|
|
388
|
+
has_more: null, persistent_has_more: null, volatile_has_more: null, has_more_persistent: null, has_more_volatile: null, effective_ack_seq: null,
|
|
389
|
+
};
|
|
390
|
+
this.rpcSpans.push(span);
|
|
391
|
+
return span;
|
|
392
|
+
}
|
|
393
|
+
observedRequestWindow(params) {
|
|
394
|
+
const window = {};
|
|
395
|
+
for (const key of ['after_seq', 'before_seq', 'until_seq', 'after_message_seq', 'after_event_seq', 'up_to_seq', 'seq', 'ack_seq', 'msg_seq', 'event_seq', 'limit', 'max_pages']) {
|
|
396
|
+
if (typeof params[key] === 'number' && Number.isSafeInteger(params[key]))
|
|
397
|
+
window[key] = params[key];
|
|
398
|
+
}
|
|
399
|
+
for (const key of ['window_mode', 'cursor_scope', 'sender_aid', 'group_id', 'group_aid', 'device_id', 'slot_id', 'after_volatile_cursor', 'mode', 'after_sender_aid']) {
|
|
400
|
+
if (typeof params[key] === 'string')
|
|
401
|
+
window[key] = params[key];
|
|
402
|
+
}
|
|
403
|
+
for (const key of ['force', 'pending_only'])
|
|
404
|
+
if (typeof params[key] === 'boolean')
|
|
405
|
+
window[key] = params[key];
|
|
406
|
+
for (const key of ['volatile_cursors', 'volatile_ids']) {
|
|
407
|
+
if (Array.isArray(params[key]))
|
|
408
|
+
window[key] = params[key].filter((item) => typeof item === 'string');
|
|
409
|
+
}
|
|
410
|
+
return window;
|
|
411
|
+
}
|
|
412
|
+
finishRpcResponse(span, value) {
|
|
413
|
+
const response = jsonObjectOf(value) ?? {};
|
|
414
|
+
const messages = Array.isArray(response.messages) ? response.messages : [];
|
|
415
|
+
span.response_ms = Date.now();
|
|
416
|
+
span.response_message_count = messages.length;
|
|
417
|
+
span.response_volatile_count = messages.filter((item) => jsonObjectOf(item)?.storage === 'volatile').length;
|
|
418
|
+
span.response_event_count = Array.isArray(response.events) ? response.events.length : 0;
|
|
419
|
+
const sequenceOf = (raw, ...keys) => {
|
|
420
|
+
for (const key of keys) {
|
|
421
|
+
const value = raw[key];
|
|
422
|
+
if (typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value))
|
|
423
|
+
return value;
|
|
424
|
+
}
|
|
425
|
+
return 0;
|
|
211
426
|
};
|
|
427
|
+
const itemKeys = new Map();
|
|
428
|
+
for (const key of ['messages', 'volatile_messages', 'volatile']) {
|
|
429
|
+
for (const item of Array.isArray(response[key]) ? response[key] : []) {
|
|
430
|
+
const raw = jsonObjectOf(item) ?? {}, cursor = textOf(raw.volatile_cursor);
|
|
431
|
+
const identity = { message_id: textOf(raw.message_id ?? raw.id),
|
|
432
|
+
seq: sequenceOf(raw, 'seq', 'message_seq', 'msg_seq'),
|
|
433
|
+
storage: typeof raw.storage === 'string' ? raw.storage : cursor ? 'volatile' : 'persistent', volatile_cursor: cursor };
|
|
434
|
+
itemKeys.set(JSON.stringify(identity), identity);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
span.response_item_keys = [...itemKeys.values()];
|
|
438
|
+
span.response_event_keys = (Array.isArray(response.events) ? response.events : []).map((item) => {
|
|
439
|
+
const raw = jsonObjectOf(item) ?? {};
|
|
440
|
+
return { event_id: textOf(raw.event_id), event_seq: sequenceOf(raw, 'event_seq', 'seq') };
|
|
441
|
+
});
|
|
442
|
+
span.has_more = typeof response.has_more === 'boolean' ? response.has_more : null;
|
|
443
|
+
span.has_more_persistent = typeof response.persistent_has_more === 'boolean' ? response.persistent_has_more : null;
|
|
444
|
+
span.has_more_volatile = typeof response.volatile_has_more === 'boolean' ? response.volatile_has_more : null;
|
|
445
|
+
span.persistent_has_more = span.has_more_persistent;
|
|
446
|
+
span.volatile_has_more = span.has_more_volatile;
|
|
447
|
+
for (const key of ['peer_ack_seq', 'effective_ack_seq', 'server_ack_seq', 'ack_seq', 'up_to_seq', 'acked_seq']) {
|
|
448
|
+
if (typeof response[key] === 'number' && Number.isSafeInteger(response[key]))
|
|
449
|
+
span[key] = response[key];
|
|
450
|
+
}
|
|
451
|
+
const effective = response.peer_ack_seq ?? response.effective_ack_seq ?? response.ack_seq ?? response.up_to_seq ?? response.acked_seq;
|
|
452
|
+
if (typeof effective === 'number' && Number.isSafeInteger(effective))
|
|
453
|
+
span.effective_ack_seq = effective;
|
|
454
|
+
const volatileAck = jsonObjectOf(response.volatile_ack);
|
|
455
|
+
if (volatileAck) {
|
|
456
|
+
const ack = {};
|
|
457
|
+
for (const key of ['accepted', 'expired'])
|
|
458
|
+
if (typeof volatileAck[key] === 'number' && Number.isSafeInteger(volatileAck[key]))
|
|
459
|
+
ack[key] = volatileAck[key];
|
|
460
|
+
if (typeof volatileAck.storage === 'string')
|
|
461
|
+
ack.storage = volatileAck.storage;
|
|
462
|
+
span.volatile_ack = ack;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
releasePullBarrier() {
|
|
466
|
+
const barrier = this.pullBarrier;
|
|
467
|
+
if (!barrier || barrier.released)
|
|
468
|
+
return;
|
|
469
|
+
barrier.released = true;
|
|
470
|
+
barrier.release();
|
|
471
|
+
}
|
|
472
|
+
injectRPCFailure(method, span) {
|
|
473
|
+
const failure = this.rpcFailures?.find((item) => !item.hit && item.method === method && item.sender_aid === span.sender_aid);
|
|
474
|
+
if (!failure)
|
|
475
|
+
return;
|
|
476
|
+
failure.hit = true;
|
|
477
|
+
span.injected_failure = failure.kind ?? 'error';
|
|
478
|
+
if (failure.kind === 'timeout')
|
|
479
|
+
throw new TimeoutError('cross-sdk 响应后一次超时');
|
|
480
|
+
throw new Error('cross-sdk 响应后一次失败');
|
|
481
|
+
}
|
|
482
|
+
resetObservationLedgers(pullBarrierCount = 0, scope = 'all', controls = {}) {
|
|
483
|
+
this.releasePullBarrier();
|
|
484
|
+
this.observationGeneration = (this.observationGeneration ?? 0) + 1;
|
|
485
|
+
this.eventLedger = [];
|
|
486
|
+
this.batchEvents = [];
|
|
487
|
+
this.singleEventCounts = { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
|
|
488
|
+
this.rpcSpans = [];
|
|
489
|
+
this.rpcFailures = Array.isArray(controls.rpc_failures) ? controls.rpc_failures.map((item) => ({ ...jsonObjectOf(item) })) : [];
|
|
490
|
+
this.pullBarrier = null;
|
|
491
|
+
if (Number.isSafeInteger(pullBarrierCount) && pullBarrierCount > 0) {
|
|
492
|
+
let release;
|
|
493
|
+
const promise = new Promise((resolve) => { release = resolve; });
|
|
494
|
+
this.pullBarrier = { target: pullBarrierCount, scope: scope === 'p2p' ? 'p2p' : 'all', admitted: 0, waiting: 0, released: false, promise, release,
|
|
495
|
+
sender: textOf(controls.pull_barrier_sender_aid), method: textOf(controls.pull_barrier_method), preserve: controls.preserve_pull_barrier === true };
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async waitForPullBarrier(method, signal, timeout, span, owner) {
|
|
499
|
+
const barrier = this.pullBarrier;
|
|
500
|
+
if (!barrier || barrier.released || barrier.admitted >= barrier.target)
|
|
501
|
+
return;
|
|
502
|
+
if (barrier.scope === 'p2p' && method !== 'message.pull' && method !== 'message.v2.pull' && barrier.method !== method)
|
|
503
|
+
return;
|
|
504
|
+
if ((barrier.sender && barrier.sender !== span.sender_aid) || (barrier.method && barrier.method !== method))
|
|
505
|
+
return;
|
|
506
|
+
barrier.admitted += 1;
|
|
507
|
+
barrier.waiting += 1;
|
|
508
|
+
if (barrier.preserve) {
|
|
509
|
+
barrier.heldMethod = method;
|
|
510
|
+
barrier.owner = owner;
|
|
511
|
+
barrier.span = span;
|
|
512
|
+
}
|
|
513
|
+
let timer;
|
|
514
|
+
let abort;
|
|
515
|
+
try {
|
|
516
|
+
await Promise.race([barrier.promise, new Promise((_resolve, reject) => {
|
|
517
|
+
abort = () => reject(new Error('pull observation barrier cancelled'));
|
|
518
|
+
if (!barrier.preserve)
|
|
519
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
520
|
+
if (signal.aborted && !barrier.preserve)
|
|
521
|
+
abort();
|
|
522
|
+
timer = setTimeout(() => reject(new Error('pull observation barrier timeout')), Math.max(1, timeout));
|
|
523
|
+
})]);
|
|
524
|
+
}
|
|
525
|
+
finally {
|
|
526
|
+
if (timer)
|
|
527
|
+
clearTimeout(timer);
|
|
528
|
+
signal.removeEventListener('abort', abort);
|
|
529
|
+
barrier.waiting -= 1;
|
|
530
|
+
}
|
|
212
531
|
}
|
|
213
532
|
storeRawPushObservation(rawEvent, scope, defaultKind, value) {
|
|
214
533
|
const data = jsonObjectOf(value) ?? {};
|
|
@@ -297,6 +616,7 @@ export class CrossSdkTsAgent {
|
|
|
297
616
|
};
|
|
298
617
|
}
|
|
299
618
|
async close() {
|
|
619
|
+
this.resetObservationLedgers();
|
|
300
620
|
try {
|
|
301
621
|
await this.client.close();
|
|
302
622
|
}
|
|
@@ -441,7 +761,7 @@ export class CrossSdkTsAgent {
|
|
|
441
761
|
if (!current.exists)
|
|
442
762
|
fileErrors[name] = 'not_found';
|
|
443
763
|
else
|
|
444
|
-
files[name] = { raw_base64: Buffer.from(current.content, 'utf8').toString('base64'), sha256: crypto.createHash('sha256').update(current.content, 'utf8').digest('hex')
|
|
764
|
+
files[name] = { raw_base64: Buffer.from(current.content, 'utf8').toString('base64'), sha256: crypto.createHash('sha256').update(current.content, 'utf8').digest('hex') };
|
|
445
765
|
}
|
|
446
766
|
catch (err) {
|
|
447
767
|
fileErrors[name] = err instanceof Error ? err.message : String(err);
|
|
@@ -487,6 +807,7 @@ export class CrossSdkTsAgent {
|
|
|
487
807
|
decrypted,
|
|
488
808
|
encrypted: Boolean(data.e2ee ?? data.encrypted),
|
|
489
809
|
seq: Number(data.seq ?? data.message_seq ?? 0) || 0,
|
|
810
|
+
need_render: data.need_render !== false,
|
|
490
811
|
ack_seq: Number(data.ack_seq ?? 0) || 0,
|
|
491
812
|
storage: textOf(data.storage),
|
|
492
813
|
volatile_cursor: textOf(data.volatile_cursor),
|
|
@@ -512,6 +833,7 @@ export class CrossSdkTsAgent {
|
|
|
512
833
|
decrypted,
|
|
513
834
|
encrypted: Boolean(data.e2ee ?? data.encrypted),
|
|
514
835
|
seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
|
|
836
|
+
need_render: data.need_render !== false,
|
|
515
837
|
error_code: errorCode,
|
|
516
838
|
raw_sha256: sha256Json(data),
|
|
517
839
|
...metadata,
|
|
@@ -541,6 +863,7 @@ export class CrossSdkTsAgent {
|
|
|
541
863
|
}
|
|
542
864
|
if (req.method === 'POST' && url.pathname === '/reset') {
|
|
543
865
|
const body = await readJson(req);
|
|
866
|
+
this.resetObservationLedgers();
|
|
544
867
|
const traceId = textOf(body.trace_id ?? '');
|
|
545
868
|
if (traceId) {
|
|
546
869
|
this.inbox = this.inbox.filter((item) => item.trace_id !== traceId);
|
|
@@ -551,6 +874,7 @@ export class CrossSdkTsAgent {
|
|
|
551
874
|
else {
|
|
552
875
|
this.inbox = [];
|
|
553
876
|
this.groupInbox = [];
|
|
877
|
+
this.peersSnapshots = [];
|
|
554
878
|
this.traces.clear();
|
|
555
879
|
this.sendResults.clear();
|
|
556
880
|
}
|
|
@@ -559,11 +883,25 @@ export class CrossSdkTsAgent {
|
|
|
559
883
|
}
|
|
560
884
|
if (req.method === 'POST' && url.pathname === '/test/raw-push/reset') {
|
|
561
885
|
const body = await readJson(req);
|
|
886
|
+
if ((body.pull_barrier_count !== undefined && (typeof body.pull_barrier_count !== 'number' || !Number.isSafeInteger(body.pull_barrier_count) || body.pull_barrier_count < 0)) ||
|
|
887
|
+
(body.pull_barrier_scope !== undefined && !['all', 'p2p'].includes(body.pull_barrier_scope)) ||
|
|
888
|
+
(body.observation_id !== undefined && typeof body.observation_id !== 'string') ||
|
|
889
|
+
(body.release_pull_barrier !== undefined && typeof body.release_pull_barrier !== 'boolean')) {
|
|
890
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid observation parameters' });
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (body.release_pull_barrier === true) {
|
|
894
|
+
this.releasePullBarrier();
|
|
895
|
+
sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
this.resetObservationLedgers(body.pull_barrier_count, textOf(body.pull_barrier_scope), body);
|
|
562
899
|
this.rawPushObservations = [];
|
|
563
900
|
this.rawPushRpcMethods = [];
|
|
564
901
|
this.groupChangedEvents = [];
|
|
565
902
|
this.groupInviteEvents = [];
|
|
566
903
|
this.readStateEvents = [];
|
|
904
|
+
this.peersSnapshots = [];
|
|
567
905
|
this.rawPushObservationId = textOf(body.observation_id ?? '');
|
|
568
906
|
sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
|
|
569
907
|
return;
|
|
@@ -582,6 +920,23 @@ export class CrossSdkTsAgent {
|
|
|
582
920
|
let rpcEntries = [...this.rawPushRpcMethods];
|
|
583
921
|
let groupChangedEvents = [...this.groupChangedEvents];
|
|
584
922
|
let groupInviteEvents = [...this.groupInviteEvents];
|
|
923
|
+
let eventLedger = [...this.eventLedger ?? []];
|
|
924
|
+
let rpcSpans = [...this.rpcSpans ?? []];
|
|
925
|
+
let batchEvents = [...this.batchEvents ?? []];
|
|
926
|
+
if (observationId)
|
|
927
|
+
batchEvents = batchEvents.filter((item) => textOf(item.observation_id) === observationId);
|
|
928
|
+
if (observationId)
|
|
929
|
+
eventLedger = eventLedger.filter((item) => textOf(item.observation_id) === observationId);
|
|
930
|
+
if (observationId)
|
|
931
|
+
rpcSpans = rpcSpans.filter((item) => textOf(item.observation_id) === observationId);
|
|
932
|
+
if (filters.scope)
|
|
933
|
+
eventLedger = eventLedger.filter((item) => textOf(item.scope) === filters.scope);
|
|
934
|
+
if (filters.scope)
|
|
935
|
+
rpcSpans = rpcSpans.filter((item) => textOf(item.method).startsWith(filters.scope === 'p2p' ? 'message.' : 'group.'));
|
|
936
|
+
if (filters.group_id)
|
|
937
|
+
eventLedger = eventLedger.filter((item) => textOf(item.group_id) === filters.group_id);
|
|
938
|
+
if (filters.group_id)
|
|
939
|
+
rpcSpans = rpcSpans.filter((item) => textOf(item.group_id) === filters.group_id);
|
|
585
940
|
if (observationId)
|
|
586
941
|
rpcEntries = rpcEntries.filter((item) => textOf(item.observation_id) === observationId);
|
|
587
942
|
if (observationId)
|
|
@@ -598,11 +953,18 @@ export class CrossSdkTsAgent {
|
|
|
598
953
|
for (const method of rpcMethods)
|
|
599
954
|
rpcMethodCounts[method] = Number(rpcMethodCounts[method] ?? 0) + 1;
|
|
600
955
|
sendJson(res, 200, {
|
|
601
|
-
ok: true, items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
|
|
956
|
+
ok: true, time_ms: Date.now(), items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
|
|
602
957
|
rpc_calls: rpcEntries,
|
|
603
958
|
group_changed_events: groupChangedEvents.slice(-limit),
|
|
604
959
|
group_invite_events: groupInviteEvents.slice(-limit),
|
|
605
960
|
read_state_events: this.readStateEvents.slice(-limit),
|
|
961
|
+
peers_snapshots: this.peersSnapshots.slice(-limit),
|
|
962
|
+
event_ledger: eventLedger,
|
|
963
|
+
rpc_spans: rpcSpans,
|
|
964
|
+
batch_event_mode: this.batchEventMode ?? false,
|
|
965
|
+
batch_events: batchEvents,
|
|
966
|
+
single_event_counts: this.singleEventCounts ?? { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 },
|
|
967
|
+
pull_barrier: { target: this.pullBarrier?.target ?? 0, waiting: this.pullBarrier?.waiting ?? 0, released: this.pullBarrier?.released ?? true, scope: this.pullBarrier?.scope ?? 'all' },
|
|
606
968
|
});
|
|
607
969
|
return;
|
|
608
970
|
}
|
|
@@ -610,6 +972,10 @@ export class CrossSdkTsAgent {
|
|
|
610
972
|
sendJson(res, 200, this.identity());
|
|
611
973
|
return;
|
|
612
974
|
}
|
|
975
|
+
if (req.method === 'POST' && url.pathname === '/signature/call') {
|
|
976
|
+
await this.signatureCall(req, res);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
613
979
|
if (req.method === 'POST' && url.pathname === '/ownership/bind') {
|
|
614
980
|
await this.ownershipBind(req, res);
|
|
615
981
|
return;
|
|
@@ -776,6 +1142,95 @@ export class CrossSdkTsAgent {
|
|
|
776
1142
|
});
|
|
777
1143
|
}
|
|
778
1144
|
}
|
|
1145
|
+
async signatureCall(req, res) {
|
|
1146
|
+
const body = await readJson(req);
|
|
1147
|
+
const action = textOf(body.action);
|
|
1148
|
+
const target = textOf(body.aid ?? this.aid);
|
|
1149
|
+
const store = this.aidStore();
|
|
1150
|
+
let directory = '', file = '';
|
|
1151
|
+
try {
|
|
1152
|
+
if (action === 'upload_agent_md' || action === 'download_agent_md') {
|
|
1153
|
+
const operation = action === 'upload_agent_md'
|
|
1154
|
+
? await store.uploadAgentMd(target, textOf(body.input))
|
|
1155
|
+
: await store.downloadAgentMd(target);
|
|
1156
|
+
sendJson(res, 200, operation.ok
|
|
1157
|
+
? { ok: true, result: jsonSafe(operation.data) }
|
|
1158
|
+
: { ok: false, error_code: operation.error.code, error_message: operation.error.message });
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (action === 'certificate') {
|
|
1162
|
+
const loaded = store.load(target);
|
|
1163
|
+
if (!loaded.ok) {
|
|
1164
|
+
sendJson(res, 200, { ok: false, error_code: loaded.error.code, error_message: loaded.error.message });
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
const identity = loaded.data.aid;
|
|
1168
|
+
const result = { aid: target, certificate: identity.certPem, cert_fingerprint: identity.certFingerprint };
|
|
1169
|
+
sendJson(res, 200, { ok: true, result, certificate: identity.certPem, cert_fingerprint: identity.certFingerprint });
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
if (action !== 'sign' && action !== 'verify') {
|
|
1173
|
+
sendJson(res, 200, { ok: false, error_code: 'bad_request', error_message: `unsupported signature action: ${action}` });
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const options = {
|
|
1177
|
+
inputType: (body.input_type ?? 'text'),
|
|
1178
|
+
format: (body.format ?? 'raw'),
|
|
1179
|
+
append: body.append === true,
|
|
1180
|
+
tag: body.tag == null ? undefined : textOf(body.tag),
|
|
1181
|
+
inPlace: body.in_place === true,
|
|
1182
|
+
};
|
|
1183
|
+
let input = textOf(body.input);
|
|
1184
|
+
if (options.inputType === 'file') {
|
|
1185
|
+
directory = fs.mkdtempSync(path.join(tmpdir(), 'aun-cross-signature-'));
|
|
1186
|
+
file = path.join(directory, 'input');
|
|
1187
|
+
fs.writeFileSync(file, body.input_base64 == null ? Buffer.from(input, 'utf8') : Buffer.from(textOf(body.input_base64), 'base64'));
|
|
1188
|
+
input = file;
|
|
1189
|
+
}
|
|
1190
|
+
if (action === 'verify') {
|
|
1191
|
+
const certificates = jsonObjectOf(body.certificates) ?? {};
|
|
1192
|
+
const certificate = textOf(certificates[target]);
|
|
1193
|
+
if (certificate)
|
|
1194
|
+
options.identity = AID._create({ aid: target, aunPath: this.aunPath, certPem: certificate, privateKeyPem: null, certValid: true, privateKeyValid: false });
|
|
1195
|
+
if (options.format === 'document')
|
|
1196
|
+
options.certificateResolver = (signer) => {
|
|
1197
|
+
const certificate = textOf(certificates[signer]);
|
|
1198
|
+
if (!certificate)
|
|
1199
|
+
throw new Error(`certificate unavailable: ${signer}`);
|
|
1200
|
+
return certificate;
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
const operation = action === 'sign'
|
|
1204
|
+
? await store.sign(target, input, options)
|
|
1205
|
+
: await store.verify(target, input, body.signature == null ? undefined : textOf(body.signature), options);
|
|
1206
|
+
if (!operation.ok) {
|
|
1207
|
+
const response = { ok: false, error_code: operation.error.code, error_message: operation.error.message };
|
|
1208
|
+
if (file)
|
|
1209
|
+
response.file_base64 = fs.readFileSync(file).toString('base64');
|
|
1210
|
+
sendJson(res, 200, response);
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
const loaded = options.identity ? { ok: true, data: { aid: options.identity } } : store.load(target);
|
|
1214
|
+
const identity = loaded.ok ? loaded.data.aid : undefined;
|
|
1215
|
+
const response = { ok: true, result: jsonSafe(operation.data), certificate: identity?.certPem ?? '', cert_fingerprint: identity?.certFingerprint ?? '' };
|
|
1216
|
+
if (file)
|
|
1217
|
+
response.file_base64 = fs.readFileSync(file).toString('base64');
|
|
1218
|
+
sendJson(res, 200, response);
|
|
1219
|
+
}
|
|
1220
|
+
catch (error) {
|
|
1221
|
+
const response = { ok: false, error_code: error instanceof Error ? error.name : 'Error', error_message: error instanceof Error ? error.message : String(error) };
|
|
1222
|
+
if (file && fs.existsSync(file))
|
|
1223
|
+
response.file_base64 = fs.readFileSync(file).toString('base64');
|
|
1224
|
+
sendJson(res, 200, response);
|
|
1225
|
+
}
|
|
1226
|
+
finally {
|
|
1227
|
+
store.close();
|
|
1228
|
+
if (file)
|
|
1229
|
+
fs.unlinkSync(file);
|
|
1230
|
+
if (directory)
|
|
1231
|
+
fs.rmdirSync(directory);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
779
1234
|
async send(req, res) {
|
|
780
1235
|
const body = await readJson(req);
|
|
781
1236
|
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
@@ -801,6 +1256,8 @@ export class CrossSdkTsAgent {
|
|
|
801
1256
|
};
|
|
802
1257
|
if (body.ttl !== undefined)
|
|
803
1258
|
params.ttl = Number(body.ttl);
|
|
1259
|
+
if (body.need_render !== undefined)
|
|
1260
|
+
params.need_render = body.need_render;
|
|
804
1261
|
try {
|
|
805
1262
|
const result = await this.client.call('message.send', params);
|
|
806
1263
|
const resultObj = result && typeof result === 'object' ? result : {};
|
|
@@ -836,6 +1293,10 @@ export class CrossSdkTsAgent {
|
|
|
836
1293
|
const params = {};
|
|
837
1294
|
if (seq > 0)
|
|
838
1295
|
params.seq = seq;
|
|
1296
|
+
if (textOf(body.sender_aid).trim())
|
|
1297
|
+
params.sender_aid = textOf(body.sender_aid).trim();
|
|
1298
|
+
if (body.cursor_scope !== undefined)
|
|
1299
|
+
params.cursor_scope = body.cursor_scope;
|
|
839
1300
|
if (Array.isArray(body.volatile_cursors))
|
|
840
1301
|
params.volatile_cursors = body.volatile_cursors;
|
|
841
1302
|
else if (textOf(body.volatile_cursor))
|
|
@@ -857,9 +1318,26 @@ export class CrossSdkTsAgent {
|
|
|
857
1318
|
}
|
|
858
1319
|
async pull(req, res) {
|
|
859
1320
|
const body = await readJson(req);
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
1321
|
+
if ((body.limit !== undefined && (typeof body.limit !== 'number' || !Number.isSafeInteger(body.limit) || body.limit < 1 || body.limit > 200)) ||
|
|
1322
|
+
(body.max_pages !== undefined && (typeof body.max_pages !== 'number' || !Number.isSafeInteger(body.max_pages) || body.max_pages < 1)) ||
|
|
1323
|
+
(body.after_seq !== undefined && (typeof body.after_seq !== 'number' || !Number.isSafeInteger(body.after_seq) || body.after_seq < 0)) ||
|
|
1324
|
+
['sender_aid', 'cursor_scope', 'after_volatile_cursor'].some((key) => body[key] !== undefined && typeof body[key] !== 'string') ||
|
|
1325
|
+
(body.force !== undefined && typeof body.force !== 'boolean') ||
|
|
1326
|
+
(body.window_mode !== undefined && !['forward', 'tail'].includes(body.window_mode))) {
|
|
1327
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid pull parameters' });
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
const senderAid = textOf(body.sender_aid).trim();
|
|
1331
|
+
const limit = body.limit ?? 50;
|
|
1332
|
+
const params = { limit };
|
|
1333
|
+
if (senderAid)
|
|
1334
|
+
params.sender_aid = senderAid;
|
|
1335
|
+
if (body.after_seq !== undefined)
|
|
1336
|
+
params.after_seq = body.after_seq;
|
|
1337
|
+
for (const key of ['cursor_scope', 'force', 'window_mode', 'max_pages']) {
|
|
1338
|
+
if (body[key] !== undefined)
|
|
1339
|
+
params[key] = body[key];
|
|
1340
|
+
}
|
|
863
1341
|
if (textOf(body.after_volatile_cursor))
|
|
864
1342
|
params.after_volatile_cursor = textOf(body.after_volatile_cursor);
|
|
865
1343
|
try {
|
|
@@ -881,6 +1359,20 @@ export class CrossSdkTsAgent {
|
|
|
881
1359
|
}
|
|
882
1360
|
async messageCall(req, res) {
|
|
883
1361
|
const body = await readJson(req);
|
|
1362
|
+
if (['start', 'status', 'detach'].includes(textOf(body.action))) {
|
|
1363
|
+
this.messageOperations ??= new Map();
|
|
1364
|
+
const id = textOf(body.operation_id);
|
|
1365
|
+
if (body.action === 'start') {
|
|
1366
|
+
const operation = { ok: true, operation_id: id, status: 'running', started_ms: Date.now() };
|
|
1367
|
+
this.messageOperations.set(id, operation);
|
|
1368
|
+
Promise.resolve().then(() => { operation.entered_ms = Date.now(); return this.client.call(textOf(body.method), (jsonObjectOf(body.params) ?? {})); }).then((result) => Object.assign(operation, { status: 'completed', end_ms: Date.now(), result: jsonSafe(result) }), (error) => Object.assign(operation, { status: 'failed', end_ms: Date.now(), error_code: error instanceof Error ? error.name : 'Error', error_message: error instanceof Error ? error.message : String(error) }));
|
|
1369
|
+
}
|
|
1370
|
+
const operation = this.messageOperations.get(id);
|
|
1371
|
+
if (body.action === 'detach')
|
|
1372
|
+
Object.assign(operation, { detached: true, cancellation: 'application_wait_only', detached_ms: Date.now() });
|
|
1373
|
+
sendJson(res, 200, operation);
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
884
1376
|
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
885
1377
|
const method = textOf(body.method).trim();
|
|
886
1378
|
const params = jsonObjectOf(body.params) ?? {};
|
|
@@ -912,15 +1404,15 @@ export class CrossSdkTsAgent {
|
|
|
912
1404
|
sendJson(res, 500, error);
|
|
913
1405
|
}
|
|
914
1406
|
}
|
|
915
|
-
syncNamespace(scope, groupId = '') {
|
|
1407
|
+
syncNamespace(scope, groupId = '', senderAid = '') {
|
|
916
1408
|
if (scope === 'p2p')
|
|
917
|
-
return `p2p:${this.aid}`;
|
|
1409
|
+
return senderAid ? `p2p.peer.v1/${Buffer.from(senderAid, 'utf8').toString('base64url')}` : `p2p:${this.aid}`;
|
|
918
1410
|
if (scope === 'group' && groupId)
|
|
919
1411
|
return `group:${groupId}`;
|
|
920
1412
|
throw new Error('scope must be p2p, or group with group_id');
|
|
921
1413
|
}
|
|
922
|
-
syncStateValue(scope, groupId = '') {
|
|
923
|
-
const namespace = this.syncNamespace(scope, groupId);
|
|
1414
|
+
syncStateValue(scope, groupId = '', senderAid = '') {
|
|
1415
|
+
const namespace = this.syncNamespace(scope, groupId, senderAid);
|
|
924
1416
|
const tracker = this.client._seqTracker;
|
|
925
1417
|
if (!tracker)
|
|
926
1418
|
throw new Error('SeqTracker is unavailable');
|
|
@@ -929,9 +1421,10 @@ export class CrossSdkTsAgent {
|
|
|
929
1421
|
syncState(url, res) {
|
|
930
1422
|
const scope = textOf(url.searchParams.get('scope') || 'p2p').trim().toLowerCase();
|
|
931
1423
|
const groupId = textOf(url.searchParams.get('group_id') || '').trim();
|
|
1424
|
+
const senderAid = textOf(url.searchParams.get('sender_aid') || '').trim();
|
|
932
1425
|
try {
|
|
933
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
934
|
-
sendJson(res, 200, { ok: true, scope, ...snapshot });
|
|
1426
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
1427
|
+
sendJson(res, 200, { ok: true, scope, ...snapshot, peers_snapshots: this.peersSnapshots });
|
|
935
1428
|
}
|
|
936
1429
|
catch (err) {
|
|
937
1430
|
sendJson(res, 400, {
|
|
@@ -951,27 +1444,24 @@ export class CrossSdkTsAgent {
|
|
|
951
1444
|
const body = await readJson(req);
|
|
952
1445
|
const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
|
|
953
1446
|
const groupId = textOf(body.group_id || '').trim();
|
|
1447
|
+
const senderAid = textOf(body.sender_aid || '').trim();
|
|
954
1448
|
const afterSeq = Number(body.after_seq ?? 0) || 0;
|
|
955
1449
|
const limit = Number(body.limit ?? 50) || 50;
|
|
956
1450
|
try {
|
|
957
|
-
const params = {
|
|
958
|
-
if (
|
|
959
|
-
params.
|
|
1451
|
+
const params = { limit, window_mode: tail ? 'tail' : 'forward' };
|
|
1452
|
+
if (scope === 'group' || body.after_seq !== undefined)
|
|
1453
|
+
params.after_seq = afterSeq;
|
|
1454
|
+
if (scope === 'p2p' && senderAid)
|
|
1455
|
+
params.sender_aid = senderAid;
|
|
1456
|
+
for (const key of ['cursor_scope', 'force', 'max_pages', 'after_volatile_cursor']) {
|
|
1457
|
+
if (body[key] !== undefined)
|
|
1458
|
+
params[key] = body[key];
|
|
1459
|
+
}
|
|
960
1460
|
const method = scope === 'group' ? 'group.pull' : 'message.pull';
|
|
961
1461
|
if (scope === 'group')
|
|
962
1462
|
params.group_id = groupId;
|
|
963
|
-
this.syncNamespace(scope, groupId);
|
|
964
|
-
|
|
965
|
-
if (tail) {
|
|
966
|
-
result = await this.client.call(method, params);
|
|
967
|
-
}
|
|
968
|
-
else {
|
|
969
|
-
const transport = this.client._transport;
|
|
970
|
-
if (!transport?.call)
|
|
971
|
-
throw new Error('raw transport is unavailable');
|
|
972
|
-
const wireMethod = scope === 'group' ? 'group.v2.pull' : 'message.v2.pull';
|
|
973
|
-
result = await transport.call(wireMethod, params);
|
|
974
|
-
}
|
|
1463
|
+
this.syncNamespace(scope, groupId, senderAid);
|
|
1464
|
+
const result = await this.client.call(method, params);
|
|
975
1465
|
const resultObj = jsonObjectOf(result) ?? {};
|
|
976
1466
|
const messages = Array.isArray(resultObj.messages) ? resultObj.messages : [];
|
|
977
1467
|
if (tail) {
|
|
@@ -982,7 +1472,7 @@ export class CrossSdkTsAgent {
|
|
|
982
1472
|
await this.storeInboxItem(this.normalizeMessage(msg, true));
|
|
983
1473
|
}
|
|
984
1474
|
}
|
|
985
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
1475
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
986
1476
|
sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
|
|
987
1477
|
}
|
|
988
1478
|
catch (err) {
|
|
@@ -998,6 +1488,7 @@ export class CrossSdkTsAgent {
|
|
|
998
1488
|
const body = await readJson(req);
|
|
999
1489
|
const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
|
|
1000
1490
|
const groupId = textOf(body.group_id || '').trim();
|
|
1491
|
+
const senderAid = textOf(body.sender_aid || '').trim();
|
|
1001
1492
|
const beforeSeq = Number(body.before_seq ?? 0) || 0;
|
|
1002
1493
|
const limit = Number(body.limit ?? 50) || 50;
|
|
1003
1494
|
try {
|
|
@@ -1005,9 +1496,13 @@ export class CrossSdkTsAgent {
|
|
|
1005
1496
|
const method = scope === 'group' ? 'group.history' : 'message.history';
|
|
1006
1497
|
if (scope === 'group')
|
|
1007
1498
|
params.group_id = groupId;
|
|
1008
|
-
|
|
1499
|
+
if (scope === 'p2p' && senderAid)
|
|
1500
|
+
params.sender_aid = senderAid;
|
|
1501
|
+
if (body.cursor_scope !== undefined)
|
|
1502
|
+
params.cursor_scope = body.cursor_scope;
|
|
1503
|
+
this.syncNamespace(scope, groupId, senderAid);
|
|
1009
1504
|
const result = await this.client.call(method, params);
|
|
1010
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
1505
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
1011
1506
|
sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
|
|
1012
1507
|
}
|
|
1013
1508
|
catch (err) {
|
|
@@ -1022,7 +1517,25 @@ export class CrossSdkTsAgent {
|
|
|
1022
1517
|
async sessionDisconnect(res) {
|
|
1023
1518
|
try {
|
|
1024
1519
|
this.ready = false;
|
|
1025
|
-
|
|
1520
|
+
const barrier = this.pullBarrier;
|
|
1521
|
+
let restore;
|
|
1522
|
+
if (barrier?.preserve && barrier.waiting === 1 && !barrier.released && ['message.pull', 'message.v2.pull'].includes(barrier.heldMethod ?? '')) {
|
|
1523
|
+
const binding = barrier.owner, pipeline = this.client._rpcPipeline;
|
|
1524
|
+
const owners = [...(pipeline?.pullGateStates.values() ?? [])].flatMap((state) => [...state.activeByNamespace.values()])
|
|
1525
|
+
.filter((job) => job === binding?.job && job.key === binding.key && job.namespace === binding.namespace && job.running && !job.timedOut && !job.invalidated && job.cancel === binding.cancel);
|
|
1526
|
+
if (owners.length !== 1)
|
|
1527
|
+
throw new Error('保留响应无法定位唯一受跟踪的 pull owner');
|
|
1528
|
+
const owner = owners[0], settled = owner.settled;
|
|
1529
|
+
owner.settled = Promise.resolve();
|
|
1530
|
+
restore = () => { owner.settled = settled; };
|
|
1531
|
+
barrier.span.barrier_owner_isolated = true;
|
|
1532
|
+
}
|
|
1533
|
+
try {
|
|
1534
|
+
await this.client.disconnect();
|
|
1535
|
+
}
|
|
1536
|
+
finally {
|
|
1537
|
+
restore?.();
|
|
1538
|
+
}
|
|
1026
1539
|
sendJson(res, 200, { ok: true, state: textOf(this.client.state) });
|
|
1027
1540
|
}
|
|
1028
1541
|
catch (err) {
|
|
@@ -1035,14 +1548,19 @@ export class CrossSdkTsAgent {
|
|
|
1035
1548
|
}
|
|
1036
1549
|
async sessionConnect(req, res) {
|
|
1037
1550
|
const body = await readJson(req);
|
|
1551
|
+
if (['background_sync', 'inline_realtime_payload_v1', 'batch_events'].some((key) => body[key] !== undefined && typeof body[key] !== 'boolean')) {
|
|
1552
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid session parameters' });
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1038
1555
|
const backgroundSync = body.background_sync === true;
|
|
1039
1556
|
if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
|
|
1040
1557
|
this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
|
|
1041
1558
|
}
|
|
1042
1559
|
try {
|
|
1560
|
+
this.setBatchEventMode(typeof body.batch_events === 'boolean' ? body.batch_events : this.batchEventMode ?? false);
|
|
1043
1561
|
await this.client.connect({ auto_reconnect: false, background_sync: backgroundSync });
|
|
1044
1562
|
this.ready = true;
|
|
1045
|
-
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
|
|
1563
|
+
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1, batch_events: this.batchEventMode });
|
|
1046
1564
|
}
|
|
1047
1565
|
catch (err) {
|
|
1048
1566
|
sendJson(res, 500, {
|
|
@@ -1054,19 +1572,25 @@ export class CrossSdkTsAgent {
|
|
|
1054
1572
|
}
|
|
1055
1573
|
async sessionRestart(req, res) {
|
|
1056
1574
|
const body = await readJson(req);
|
|
1575
|
+
if (['background_sync', 'inline_realtime_payload_v1', 'batch_events'].some((key) => body[key] !== undefined && typeof body[key] !== 'boolean')) {
|
|
1576
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid session parameters' });
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1057
1579
|
const backgroundSync = body.background_sync === true;
|
|
1058
1580
|
if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
|
|
1059
1581
|
this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
|
|
1060
1582
|
}
|
|
1061
1583
|
this.ready = false;
|
|
1062
1584
|
try {
|
|
1585
|
+
if (typeof body.batch_events === 'boolean')
|
|
1586
|
+
this.batchEventMode = body.batch_events;
|
|
1063
1587
|
await this.client.close();
|
|
1064
1588
|
this.client = this.newClient();
|
|
1065
1589
|
this.bindClientEvents();
|
|
1066
1590
|
await this.ensureConnected(backgroundSync);
|
|
1067
1591
|
this.startupError = '';
|
|
1068
1592
|
this.ready = true;
|
|
1069
|
-
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
|
|
1593
|
+
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1, batch_events: this.batchEventMode });
|
|
1070
1594
|
}
|
|
1071
1595
|
catch (err) {
|
|
1072
1596
|
this.startupError = `${err instanceof Error ? err.name : 'Error'}: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -1136,9 +1660,16 @@ export class CrossSdkTsAgent {
|
|
|
1136
1660
|
const now = Math.floor(Date.now() / 1000);
|
|
1137
1661
|
if (op === 'request')
|
|
1138
1662
|
return buildRequestBinding(textOf(params.method), textOf(params.target), params.params, textOf(params.credential_cid), textOf(params.nonce), textOf(params.request_id), now, now + 300);
|
|
1139
|
-
if (['register', 'sign', 'certificate', 'create'].includes(op)) {
|
|
1663
|
+
if (['register', 'sign', 'certificate', 'create', 'prepare'].includes(op)) {
|
|
1140
1664
|
const store = this.aidStore();
|
|
1141
1665
|
try {
|
|
1666
|
+
if (op === 'prepare') {
|
|
1667
|
+
const request = jsonObjectOf(params.params) ?? {};
|
|
1668
|
+
const orgId = textOf(params.org_id ?? request.org_id);
|
|
1669
|
+
const loaded = orgId ? store.load(orgId) : null;
|
|
1670
|
+
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 });
|
|
1672
|
+
}
|
|
1142
1673
|
if (op === 'register') {
|
|
1143
1674
|
const registered = await store.register(textOf(params.org_id), { type: 'org', owner: this.aid, exttype: 'company' });
|
|
1144
1675
|
if (!registered.ok)
|
|
@@ -1171,6 +1702,7 @@ export class CrossSdkTsAgent {
|
|
|
1171
1702
|
const value = params.document ?? params.payload;
|
|
1172
1703
|
const result = { canonical: new TextDecoder().decode(canonicalJson(value)), digest: digest(value), error_code: '' };
|
|
1173
1704
|
if (op === 'verify' || op === 'verify_full') {
|
|
1705
|
+
const verificationNow = typeof params.now === 'number' ? params.now : now;
|
|
1174
1706
|
const certificates = params.certificates;
|
|
1175
1707
|
const resolver = (fingerprint) => certificates[fingerprint];
|
|
1176
1708
|
try {
|
|
@@ -1178,7 +1710,7 @@ export class CrossSdkTsAgent {
|
|
|
1178
1710
|
const actual = jsonObjectOf(params.context) ?? params;
|
|
1179
1711
|
const memberships = jsonObjectOf(params.memberships) ?? {};
|
|
1180
1712
|
result.verification = { ...this.client.credential.verify(value, { resolveCertificate: resolver, resolveActorCertificate: resolver,
|
|
1181
|
-
resolveStatus: (cid) => params.statuses?.[cid], now, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
|
|
1713
|
+
resolveStatus: (cid) => params.statuses?.[cid], now: verificationNow, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
|
|
1182
1714
|
ns: textOf(actual.ns), action: textOf(actual.action), resource: textOf(actual.resource), rootIssuer: textOf(actual.root_issuer) || undefined,
|
|
1183
1715
|
params: jsonObjectOf(actual.params) ?? {}, units: jsonObjectOf(actual.units) ?? {}, clientIp: textOf(actual.client_ip), method: textOf(actual.method), target: textOf(actual.target),
|
|
1184
1716
|
requestId: textOf(actual.request_id), nonce: textOf(actual.nonce), requestBinding: actual.request_binding, proofChain: actual.proof_chain, membershipBinding: actual.membership_binding,
|
|
@@ -1187,11 +1719,11 @@ export class CrossSdkTsAgent {
|
|
|
1187
1719
|
resolveOwnership: (subject, owner) => (jsonObjectOf(params.ownerships) ?? {})[subject] === owner, checkRateLimit: () => params.rate_limit_allowed === true }) };
|
|
1188
1720
|
}
|
|
1189
1721
|
else if (params.kind === 'credential')
|
|
1190
|
-
result.verification = { ...this.client.credential.verifySignature(value, resolver,
|
|
1722
|
+
result.verification = { ...this.client.credential.verifySignature(value, resolver, verificationNow, textOf(params.audience)) };
|
|
1191
1723
|
else if (params.kind === 'binding')
|
|
1192
|
-
await verifyMembershipBinding(value, resolver,
|
|
1724
|
+
await verifyMembershipBinding(value, resolver, verificationNow, textOf(params.expected_org_signer));
|
|
1193
1725
|
else
|
|
1194
|
-
result.verification = { ...this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid),
|
|
1726
|
+
result.verification = { ...this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid), verificationNow) };
|
|
1195
1727
|
}
|
|
1196
1728
|
catch (error) {
|
|
1197
1729
|
if (!(error instanceof ProtocolError))
|
|
@@ -1950,6 +2482,7 @@ export class CrossSdkTsAgent {
|
|
|
1950
2482
|
payload,
|
|
1951
2483
|
encrypt: e2ee,
|
|
1952
2484
|
message_id: messageId,
|
|
2485
|
+
...(body.need_render !== undefined ? { need_render: body.need_render } : {}),
|
|
1953
2486
|
});
|
|
1954
2487
|
const resultObj = result && typeof result === 'object' && !Array.isArray(result) ? result : {};
|
|
1955
2488
|
const response = {
|