@agentunion/fastaun-browser 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 +16 -4
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +65 -30
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +35 -3
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +384 -150
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +59 -0
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +202 -16
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +4 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +24 -42
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +89 -18
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +6410 -1848
- package/dist/cert-utils.d.ts +50 -1
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +123 -56
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +108 -16
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +1411 -184
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +203 -117
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +22 -3
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +1312 -684
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +23 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +419 -101
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +8 -6
- package/dist/crypto.js.map +1 -1
- package/dist/events.d.ts +24 -3
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +171 -29
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +42 -1
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +368 -12
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +14 -1
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +4 -0
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.js +30 -13
- package/dist/keystore/indexeddb-identity-store.js.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +19 -2
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +193 -6
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +15 -2
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +308 -27
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +45 -0
- package/dist/org-protocol.d.ts.map +1 -1
- package/dist/org-protocol.js +827 -21
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +43 -9
- package/dist/ownership-v21.d.ts.map +1 -1
- package/dist/ownership-v21.js +183 -81
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +36 -1
- package/dist/ownership-workflow.d.ts.map +1 -1
- package/dist/ownership-workflow.js +610 -89
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/seq-tracker.d.ts +2 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +3 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +61 -3
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
- package/dist/tools/cross-sdk-agent.js +524 -45
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +4 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +64 -30
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ import * as path from 'node:path';
|
|
|
8
8
|
import { URL, fileURLToPath } from 'node:url';
|
|
9
9
|
import { AID, AIDStore, AUNClient, __version__ } from '../index.js';
|
|
10
10
|
import { publicKeyFingerprint } from '../cert-utils.js';
|
|
11
|
-
import { StateError } from '../errors.js';
|
|
11
|
+
import { StateError, TimeoutError } from '../errors.js';
|
|
12
12
|
import { OwnershipStorageAdapter } from '../ownership-v21.js';
|
|
13
13
|
import { ownershipErrorCode, ownershipTaskResult } from '../ownership-workflow.js';
|
|
14
14
|
import { canonicalJson, digest, buildRequestBinding, verifyMembershipBinding, ProtocolError } from '../org-protocol.js';
|
|
@@ -141,14 +141,28 @@ export class CrossSdkJsAgent {
|
|
|
141
141
|
rawPushObservations = [];
|
|
142
142
|
rawPushObservationId = '';
|
|
143
143
|
rawPushRpcMethods = [];
|
|
144
|
+
eventLedger = [];
|
|
145
|
+
rpcSpans = [];
|
|
146
|
+
observationGeneration = 0;
|
|
147
|
+
pullBarrier = null;
|
|
148
|
+
rpcFailures = [];
|
|
149
|
+
messageOperations = new Map();
|
|
144
150
|
rpcObservedTransports = new WeakSet();
|
|
151
|
+
rpcObservedPipelines = new WeakSet();
|
|
152
|
+
rpcObservedPulls = new WeakMap();
|
|
145
153
|
groupChangedEvents = [];
|
|
146
154
|
groupInviteEvents = [];
|
|
147
155
|
readStateEvents = [];
|
|
156
|
+
peersSnapshots = [];
|
|
148
157
|
ownershipEvents = [];
|
|
149
158
|
traces = new Map();
|
|
150
159
|
sendResults = new Map();
|
|
151
160
|
inlineRealtimePayloadV1 = true;
|
|
161
|
+
batchEventMode = false;
|
|
162
|
+
batchEvents = [];
|
|
163
|
+
singleEventCounts = { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
|
|
164
|
+
clientEventsBound = new WeakSet();
|
|
165
|
+
batchEventHandlers = new WeakMap();
|
|
152
166
|
constructor() {
|
|
153
167
|
this.client = this.newClient();
|
|
154
168
|
}
|
|
@@ -166,28 +180,41 @@ export class CrossSdkJsAgent {
|
|
|
166
180
|
}
|
|
167
181
|
bindClientEvents() {
|
|
168
182
|
this.installTransportRpcObserver();
|
|
183
|
+
if (this.clientEventsBound.has(this.client)) {
|
|
184
|
+
this.syncBatchEventHandlers();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.clientEventsBound.add(this.client);
|
|
169
188
|
this.client.on('_raw.message.received', (data) => this.storeRawPushObservation('_raw.message.received', 'p2p', 'legacy', data));
|
|
170
189
|
this.client.on('_raw.peer.v2.message_received', (data) => this.storeRawPushObservation('_raw.peer.v2.message_received', 'p2p', 'v2', data));
|
|
171
190
|
this.client.on('_raw.group.message_created', (data) => this.storeRawPushObservation('_raw.group.message_created', 'group', 'legacy', data));
|
|
172
191
|
this.client.on('_raw.group.v2.message_created', (data) => this.storeRawPushObservation('_raw.group.v2.message_created', 'group', 'v2', data));
|
|
173
192
|
this.client.on('_raw.group.changed', (data) => this.storeRawGroupChangedObservation(data));
|
|
174
193
|
this.client.on('message.received', (msg) => {
|
|
175
|
-
|
|
194
|
+
this.singleEventCounts['message.received']++;
|
|
195
|
+
this.storeApplicationEvent('message.received', msg);
|
|
176
196
|
});
|
|
177
197
|
this.client.on('message.undecryptable', (msg) => {
|
|
178
198
|
void this.storeInboxItem(this.normalizeMessage(msg, false, 'undecryptable'));
|
|
179
199
|
});
|
|
180
200
|
this.client.on('group.message_created', (msg) => {
|
|
181
|
-
|
|
201
|
+
this.singleEventCounts['group.message_created']++;
|
|
202
|
+
this.storeApplicationEvent('group.message_created', msg);
|
|
182
203
|
});
|
|
183
204
|
this.client.on('group.message_undecryptable', (msg) => {
|
|
184
205
|
void this.storeGroupInboxItem(this.normalizeGroupMessage(msg, false, 'undecryptable'));
|
|
185
206
|
});
|
|
186
|
-
this.client.on('group.changed', (data) =>
|
|
207
|
+
this.client.on('group.changed', (data) => {
|
|
208
|
+
this.singleEventCounts['group.changed']++;
|
|
209
|
+
this.storeApplicationEvent('group.changed', data);
|
|
210
|
+
});
|
|
187
211
|
this.client.on('group.invite_received', (data) => this.storeGroupInviteEvent(data));
|
|
188
212
|
for (const event of ['message.read_changed', 'group.read_changed', 'read_state_sync']) {
|
|
189
213
|
this.client.on(event, (payload) => { this.readStateEvents.push({ event, payload: jsonSafe(payload) }); });
|
|
190
214
|
}
|
|
215
|
+
this.client.on('message.peers_snapshot', (payload) => {
|
|
216
|
+
this.peersSnapshots.push({ event: 'message.peers_snapshot', payload: jsonSafe(payload) });
|
|
217
|
+
});
|
|
191
218
|
for (const event of ['ownership.request', 'ownership.action_required', 'ownership.progress', 'ownership.completed', 'ownership.rejected', 'ownership.stopped', 'ownership.error']) {
|
|
192
219
|
this.client.on(event, (payload) => { this.ownershipEvents.push({ event, payload: jsonSafe(payload) }); });
|
|
193
220
|
}
|
|
@@ -198,16 +225,89 @@ export class CrossSdkJsAgent {
|
|
|
198
225
|
this.recordTrace(textOf(payload.org_id), { stage: event, payload });
|
|
199
226
|
});
|
|
200
227
|
}
|
|
228
|
+
this.syncBatchEventHandlers();
|
|
229
|
+
}
|
|
230
|
+
syncBatchEventHandlers() {
|
|
231
|
+
const bound = this.batchEventHandlers.get(this.client);
|
|
232
|
+
if (!this.batchEventMode) {
|
|
233
|
+
if (bound)
|
|
234
|
+
for (const [event, handler] of bound)
|
|
235
|
+
this.client.off(event, handler);
|
|
236
|
+
this.batchEventHandlers.delete(this.client);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (bound)
|
|
240
|
+
return;
|
|
241
|
+
const handlers = new Map();
|
|
242
|
+
for (const event of ['message.batch', 'group.message_batch', 'group.event_batch']) {
|
|
243
|
+
const handler = (value) => {
|
|
244
|
+
const data = jsonObjectOf(value) ?? {};
|
|
245
|
+
const items = JSON.parse(stableStringify(jsonSafe(Array.isArray(data.items) ? data.items : [])));
|
|
246
|
+
this.batchEvents.push({ observation_id: this.rawPushObservationId, time_ms: Date.now(), event, namespace: textOf(data.namespace), source: textOf(data.source), items });
|
|
247
|
+
for (const item of items)
|
|
248
|
+
this.storeApplicationEvent(textOf(item.event), item.payload);
|
|
249
|
+
};
|
|
250
|
+
handlers.set(event, handler);
|
|
251
|
+
this.client.on(event, handler);
|
|
252
|
+
}
|
|
253
|
+
this.batchEventHandlers.set(this.client, handlers);
|
|
254
|
+
}
|
|
255
|
+
storeApplicationEvent(event, value) {
|
|
256
|
+
if (event === 'message.received') {
|
|
257
|
+
this.storePublicMessageEvent(event, 'p2p', value);
|
|
258
|
+
void this.storeInboxItem(this.normalizeMessage(value, true));
|
|
259
|
+
}
|
|
260
|
+
else if (event === 'group.message_created') {
|
|
261
|
+
this.storePublicMessageEvent(event, 'group', value);
|
|
262
|
+
void this.storeGroupInboxItem(this.normalizeGroupMessage(value, true));
|
|
263
|
+
}
|
|
264
|
+
else if (event === 'group.changed')
|
|
265
|
+
this.storeGroupChangedPublicEvent(value);
|
|
201
266
|
}
|
|
202
267
|
installTransportRpcObserver() {
|
|
268
|
+
const client = this.client;
|
|
269
|
+
const pipeline = client._rpcPipeline;
|
|
270
|
+
this.rpcObservedPipelines ??= new WeakSet();
|
|
271
|
+
this.rpcObservedPulls ??= new WeakMap();
|
|
272
|
+
if (pipeline?.bindActivePullCancellation && !this.rpcObservedPipelines.has(pipeline)) {
|
|
273
|
+
const originalBind = pipeline.bindActivePullCancellation;
|
|
274
|
+
this.rpcObservedPipelines.add(pipeline);
|
|
275
|
+
pipeline.bindActivePullCancellation = (method, params, request, gateKey = '') => {
|
|
276
|
+
const result = originalBind.call(pipeline, method, params, request, gateKey);
|
|
277
|
+
const observed = this.rpcObservedPulls.get(request);
|
|
278
|
+
if (!this.pullBarrier?.preserve || !observed || !['message.pull', 'message.v2.pull'].includes(method)
|
|
279
|
+
|| !textOf(params.sender_aid) || typeof client._delivery?.peerNamespace !== 'function')
|
|
280
|
+
return result;
|
|
281
|
+
const key = gateKey || pipeline.pullGateKeyForCall(method, params);
|
|
282
|
+
const namespace = client._delivery?.peerNamespace(textOf(params.sender_aid));
|
|
283
|
+
const owners = [...pipeline.pullGateStates.values()].flatMap((state) => [...state.activeByNamespace.values()])
|
|
284
|
+
.filter((job) => job.key === key && job.namespace === namespace && job.running && !job.timedOut && !job.invalidated && job.cancel);
|
|
285
|
+
if (owners.length === 1)
|
|
286
|
+
observed.owner = { job: owners[0], cancel: owners[0].cancel, key, namespace: namespace };
|
|
287
|
+
return result;
|
|
288
|
+
};
|
|
289
|
+
}
|
|
203
290
|
const transport = this.client._transport;
|
|
204
291
|
transport?.setTraceMode('diag');
|
|
205
292
|
if (!transport?.call || this.rpcObservedTransports.has(transport))
|
|
206
293
|
return;
|
|
207
294
|
const originalCall = transport.call;
|
|
295
|
+
const originalClose = transport.close;
|
|
208
296
|
this.rpcObservedTransports.add(transport);
|
|
209
|
-
|
|
297
|
+
if (originalClose)
|
|
298
|
+
transport.close = async () => {
|
|
299
|
+
if (!this.pullBarrier?.preserve)
|
|
300
|
+
this.releasePullBarrier();
|
|
301
|
+
return await originalClose.call(transport);
|
|
302
|
+
};
|
|
303
|
+
transport.call = (method, params, timeout, trace, background) => {
|
|
210
304
|
this.storeRawPushRpcMethod({ type: 'rpc', method, request_window: this.rawPushRequestWindow(params) });
|
|
305
|
+
const generation = this.observationGeneration;
|
|
306
|
+
const span = this.startRpcSpan(method, params);
|
|
307
|
+
const barrier = this.pullBarrier;
|
|
308
|
+
const controller = new AbortController();
|
|
309
|
+
let originalPromise;
|
|
310
|
+
const observed = {};
|
|
211
311
|
let requestParams = params;
|
|
212
312
|
if (method === 'auth.connect' && !this.inlineRealtimePayloadV1 && jsonObjectOf(params)) {
|
|
213
313
|
const request = { ...jsonObjectOf(params) };
|
|
@@ -218,9 +318,196 @@ export class CrossSdkJsAgent {
|
|
|
218
318
|
}
|
|
219
319
|
requestParams = request;
|
|
220
320
|
}
|
|
221
|
-
|
|
321
|
+
const observedPromise = (async () => {
|
|
322
|
+
try {
|
|
323
|
+
originalPromise = originalCall.call(transport, method, requestParams, timeout, trace, background);
|
|
324
|
+
const result = await originalPromise;
|
|
325
|
+
if (span && generation === this.observationGeneration)
|
|
326
|
+
this.finishRpcResponse(span, result);
|
|
327
|
+
const isPull = ['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull', 'group.pull_events'].includes(method);
|
|
328
|
+
const isP2PPull = ['message.pull', 'message.v2.pull'].includes(method);
|
|
329
|
+
if (barrier && generation === this.observationGeneration && (isPull || barrier.method === method) && (barrier.scope !== 'p2p' || isP2PPull || barrier.method === method)
|
|
330
|
+
&& (!barrier.sender || barrier.sender === span?.sender_aid) && (!barrier.method || barrier.method === method)) {
|
|
331
|
+
const duration = (typeof timeout === 'number' && timeout > 0 ? timeout : transport._timeout ?? 35) * 1000;
|
|
332
|
+
await this.waitPullBarrier(barrier, controller.signal, Math.max(1, duration - (Date.now() - Number(span?.start_ms ?? Date.now()))), method, span, observed.owner);
|
|
333
|
+
}
|
|
334
|
+
if (span && generation === this.observationGeneration) {
|
|
335
|
+
this.injectRPCFailure(method, span);
|
|
336
|
+
span.success = true;
|
|
337
|
+
}
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
catch (error) {
|
|
341
|
+
if (span && generation === this.observationGeneration) {
|
|
342
|
+
span.error = true;
|
|
343
|
+
span.success = false;
|
|
344
|
+
}
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
finally {
|
|
348
|
+
if (span && generation === this.observationGeneration)
|
|
349
|
+
span.end_ms = Date.now();
|
|
350
|
+
}
|
|
351
|
+
})();
|
|
352
|
+
observedPromise.cancel = () => { controller.abort(); originalPromise?.cancel?.(); };
|
|
353
|
+
this.rpcObservedPulls.set(observedPromise, observed);
|
|
354
|
+
return observedPromise;
|
|
222
355
|
};
|
|
223
356
|
}
|
|
357
|
+
storePublicMessageEvent(event, scope, value) {
|
|
358
|
+
const data = jsonObjectOf(value) ?? {};
|
|
359
|
+
const internal = this.client;
|
|
360
|
+
const payload = JSON.parse(stableStringify(jsonSafe(data.payload ?? null)));
|
|
361
|
+
this.eventLedger.push({
|
|
362
|
+
observation_id: this.rawPushObservationId, scope, event, event_index: this.eventLedger.length + 1,
|
|
363
|
+
time_ms: Date.now(), receiver_aid: this.aid, device_id: textOf(internal._deviceId), slot_id: textOf(internal._slotId || this.slotId),
|
|
364
|
+
sender_aid: textOf(data.from ?? data.from_aid ?? data.sender_aid), group_id: textOf(data.group_id),
|
|
365
|
+
message_id: textOf(data.message_id ?? data.id), seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
|
|
366
|
+
storage: textOf(data.storage), volatile_cursor: textOf(data.volatile_cursor),
|
|
367
|
+
payload, payload_sha256: sha256Json(payload), need_render: data.need_render !== false,
|
|
368
|
+
...(Object.prototype.hasOwnProperty.call(data, 'mention_mode') ? { mention_mode: data.mention_mode } : {}),
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
startRpcSpan(method, value) {
|
|
372
|
+
if (!['message.pull', 'message.v2.pull', 'group.pull', 'group.v2.pull', 'group.events', 'group.pull_events', 'message.peers', 'message.ack', 'message.v2.ack', 'group.ack', 'group.v2.ack', 'group.ack_messages', 'group.ack_events'].includes(method))
|
|
373
|
+
return null;
|
|
374
|
+
const params = jsonObjectOf(value) ?? {};
|
|
375
|
+
const senderAid = textOf(params.sender_aid ?? params.peer_aid).trim();
|
|
376
|
+
const groupId = textOf(params.group_id ?? params.group_aid).trim();
|
|
377
|
+
const groupEvent = ['group.events', 'group.pull_events', 'group.ack_events'].includes(method);
|
|
378
|
+
const group = method.startsWith('group.');
|
|
379
|
+
const span = {
|
|
380
|
+
observation_id: this.rawPushObservationId, scope: group ? 'group' : 'p2p', span_id: this.rpcSpans.length + 1,
|
|
381
|
+
boundary: 'transport_call', method, start_ms: Date.now(), response_ms: null, end_ms: null, success: null,
|
|
382
|
+
cursor_scope: textOf(params.cursor_scope) || (group ? groupEvent ? 'group_event' : 'group' : senderAid || method === 'message.peers' ? 'peer' : 'global'),
|
|
383
|
+
sender_aid: senderAid, group_id: groupId,
|
|
384
|
+
namespace: group ? groupId ? `${groupEvent ? 'group_event' : 'group'}:${groupId}` : '' : method === 'message.peers' ? '' : this.syncNamespace('p2p', '', senderAid),
|
|
385
|
+
request_window: this.rpcSpanRequestWindow(params), response_message_count: 0, response_volatile_count: 0, response_event_count: 0, response_peer_count: 0, response_item_keys: [], response_event_keys: [],
|
|
386
|
+
has_more: null, has_more_persistent: null, has_more_volatile: null, persistent_has_more: null, volatile_has_more: null,
|
|
387
|
+
effective_ack_seq: null, peer_ack_seq: null, server_ack_seq: null, error: false,
|
|
388
|
+
};
|
|
389
|
+
this.rpcSpans.push(span);
|
|
390
|
+
return span;
|
|
391
|
+
}
|
|
392
|
+
rpcSpanRequestWindow(params) {
|
|
393
|
+
const out = this.rawPushRequestWindow(params);
|
|
394
|
+
for (const key of ['before_seq', 'until_seq', 'after_message_seq', 'after_event_seq', 'snapshot_head_seq', 'seq', 'up_to_seq', 'msg_seq', 'event_seq']) {
|
|
395
|
+
const value = params[key];
|
|
396
|
+
if (typeof value === 'number' && Number.isSafeInteger(value))
|
|
397
|
+
out[key] = value;
|
|
398
|
+
}
|
|
399
|
+
for (const key of ['after_volatile_cursor', 'volatile_cursor', 'mode', 'after_sender_aid', 'peer_mode', 'directory_mode'])
|
|
400
|
+
if (typeof params[key] === 'string')
|
|
401
|
+
out[key] = params[key];
|
|
402
|
+
for (const key of ['force', 'pending_only'])
|
|
403
|
+
if (typeof params[key] === 'boolean')
|
|
404
|
+
out[key] = params[key];
|
|
405
|
+
for (const key of ['volatile_cursors', 'volatile_ids'])
|
|
406
|
+
if (Array.isArray(params[key]))
|
|
407
|
+
out[key] = params[key].filter((item) => typeof item === 'string');
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
finishRpcResponse(span, value) {
|
|
411
|
+
const result = jsonObjectOf(value) ?? {};
|
|
412
|
+
span.response_ms = Date.now();
|
|
413
|
+
const messages = Array.isArray(result.messages) ? result.messages : [];
|
|
414
|
+
span.response_message_count = messages.length;
|
|
415
|
+
span.response_volatile_count = messages.filter((item) => jsonObjectOf(item)?.storage === 'volatile').length;
|
|
416
|
+
span.response_event_count = Array.isArray(result.events) ? result.events.length : 0;
|
|
417
|
+
span.response_peer_count = Array.isArray(result.peers) ? result.peers.length : 0;
|
|
418
|
+
const sequenceOf = (...values) => values.find((value) => typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value)) ?? 0;
|
|
419
|
+
const itemKeys = new Map();
|
|
420
|
+
for (const key of ['messages', 'volatile_messages', 'volatile'])
|
|
421
|
+
for (const value of Array.isArray(result[key]) ? result[key] : []) {
|
|
422
|
+
const item = jsonObjectOf(value) ?? {};
|
|
423
|
+
const cursor = textOf(item.volatile_cursor);
|
|
424
|
+
const identity = { message_id: textOf(item.message_id ?? item.id), seq: sequenceOf(item.seq, item.message_seq, item.msg_seq), storage: textOf(item.storage) || (cursor ? 'volatile' : 'persistent'), volatile_cursor: cursor };
|
|
425
|
+
itemKeys.set(stableStringify(identity), identity);
|
|
426
|
+
}
|
|
427
|
+
span.response_item_keys = [...itemKeys.values()];
|
|
428
|
+
span.response_event_keys = (Array.isArray(result.events) ? result.events : []).map((value) => {
|
|
429
|
+
const item = jsonObjectOf(value) ?? {};
|
|
430
|
+
return { event_id: textOf(item.event_id), event_seq: sequenceOf(item.event_seq, item.seq) };
|
|
431
|
+
});
|
|
432
|
+
span.has_more = typeof result.has_more === 'boolean' ? result.has_more : null;
|
|
433
|
+
span.has_more_persistent = typeof result.has_more_persistent === 'boolean' ? result.has_more_persistent : typeof result.persistent_has_more === 'boolean' ? result.persistent_has_more : null;
|
|
434
|
+
span.has_more_volatile = typeof result.has_more_volatile === 'boolean' ? result.has_more_volatile : typeof result.volatile_has_more === 'boolean' ? result.volatile_has_more : null;
|
|
435
|
+
span.persistent_has_more = typeof result.persistent_has_more === 'boolean' ? result.persistent_has_more : null;
|
|
436
|
+
span.volatile_has_more = typeof result.volatile_has_more === 'boolean' ? result.volatile_has_more : null;
|
|
437
|
+
const safeSeq = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
438
|
+
span.peer_ack_seq = safeSeq(result.peer_ack_seq);
|
|
439
|
+
span.server_ack_seq = safeSeq(result.server_ack_seq);
|
|
440
|
+
span.effective_ack_seq = safeSeq(result.peer_ack_seq ?? result.effective_ack_seq ?? result.ack_seq ?? result.server_ack_seq ?? jsonObjectOf(result.cursor)?.current_seq ?? result.cursor);
|
|
441
|
+
const volatileAck = jsonObjectOf(result.volatile_ack);
|
|
442
|
+
if (volatileAck) {
|
|
443
|
+
const ack = {};
|
|
444
|
+
for (const key of ['accepted', 'expired'])
|
|
445
|
+
if (typeof volatileAck[key] === 'number' && Number.isSafeInteger(volatileAck[key]))
|
|
446
|
+
ack[key] = volatileAck[key];
|
|
447
|
+
if (typeof volatileAck.storage === 'string')
|
|
448
|
+
ack.storage = volatileAck.storage;
|
|
449
|
+
span.volatile_ack = ack;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
releasePullBarrier() {
|
|
453
|
+
if (!this.pullBarrier || this.pullBarrier.released)
|
|
454
|
+
return;
|
|
455
|
+
this.pullBarrier.released = true;
|
|
456
|
+
this.pullBarrier.release();
|
|
457
|
+
}
|
|
458
|
+
injectRPCFailure(method, span) {
|
|
459
|
+
const failure = this.rpcFailures?.find((item) => !item.hit && item.method === method && item.sender_aid === span.sender_aid);
|
|
460
|
+
if (!failure)
|
|
461
|
+
return;
|
|
462
|
+
failure.hit = true;
|
|
463
|
+
span.injected_failure = failure.kind ?? 'error';
|
|
464
|
+
if (failure.kind === 'timeout')
|
|
465
|
+
throw new TimeoutError('cross-sdk 响应后一次超时');
|
|
466
|
+
throw new Error('cross-sdk 响应后一次失败');
|
|
467
|
+
}
|
|
468
|
+
resetObservationLedgers(count = 0, scope = 'all', controls = {}) {
|
|
469
|
+
this.releasePullBarrier();
|
|
470
|
+
this.observationGeneration++;
|
|
471
|
+
this.eventLedger = [];
|
|
472
|
+
this.rpcSpans = [];
|
|
473
|
+
this.batchEvents = [];
|
|
474
|
+
this.singleEventCounts = { 'message.received': 0, 'group.message_created': 0, 'group.changed': 0 };
|
|
475
|
+
this.rpcFailures = Array.isArray(controls.rpc_failures) ? controls.rpc_failures.map((item) => ({ ...jsonObjectOf(item) })) : [];
|
|
476
|
+
let release;
|
|
477
|
+
const promise = new Promise((resolve) => { release = resolve; });
|
|
478
|
+
this.pullBarrier = { target: count, admitted: 0, waiting: 0, released: count <= 0, scope: scope === 'p2p' ? 'p2p' : 'all', promise, release,
|
|
479
|
+
sender: textOf(controls.pull_barrier_sender_aid), method: textOf(controls.pull_barrier_method), preserve: controls.preserve_pull_barrier === true };
|
|
480
|
+
}
|
|
481
|
+
async waitPullBarrier(barrier, signal, timeout, method, span, owner) {
|
|
482
|
+
if (barrier.released || barrier.admitted >= barrier.target)
|
|
483
|
+
return;
|
|
484
|
+
if (signal.aborted && !barrier.preserve)
|
|
485
|
+
throw new Error('transport call cancelled');
|
|
486
|
+
barrier.admitted++;
|
|
487
|
+
barrier.waiting++;
|
|
488
|
+
if (barrier.preserve) {
|
|
489
|
+
barrier.heldMethod = method;
|
|
490
|
+
barrier.owner = owner;
|
|
491
|
+
barrier.span = span ?? undefined;
|
|
492
|
+
}
|
|
493
|
+
let timer;
|
|
494
|
+
let onAbort;
|
|
495
|
+
try {
|
|
496
|
+
await Promise.race([barrier.promise, new Promise((_resolve, reject) => {
|
|
497
|
+
onAbort = () => reject(new Error('transport call cancelled'));
|
|
498
|
+
if (!barrier.preserve)
|
|
499
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
500
|
+
timer = setTimeout(() => reject(new Error('pull barrier timed out')), timeout);
|
|
501
|
+
})]);
|
|
502
|
+
}
|
|
503
|
+
finally {
|
|
504
|
+
barrier.waiting--;
|
|
505
|
+
if (timer)
|
|
506
|
+
clearTimeout(timer);
|
|
507
|
+
if (onAbort)
|
|
508
|
+
signal.removeEventListener('abort', onAbort);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
224
511
|
storeRawPushObservation(rawEvent, scope, defaultKind, value) {
|
|
225
512
|
const data = jsonObjectOf(value) ?? {};
|
|
226
513
|
const hasInline = Object.prototype.hasOwnProperty.call(data, 'inline_message');
|
|
@@ -308,6 +595,7 @@ export class CrossSdkJsAgent {
|
|
|
308
595
|
};
|
|
309
596
|
}
|
|
310
597
|
async close() {
|
|
598
|
+
this.resetObservationLedgers();
|
|
311
599
|
try {
|
|
312
600
|
await this.client.close();
|
|
313
601
|
}
|
|
@@ -465,6 +753,7 @@ export class CrossSdkJsAgent {
|
|
|
465
753
|
decrypted,
|
|
466
754
|
encrypted: Boolean(data.e2ee ?? data.encrypted),
|
|
467
755
|
seq: Number(data.seq ?? data.message_seq ?? 0) || 0,
|
|
756
|
+
need_render: data.need_render !== false,
|
|
468
757
|
ack_seq: Number(data.ack_seq ?? 0) || 0,
|
|
469
758
|
storage: textOf(data.storage),
|
|
470
759
|
volatile_cursor: textOf(data.volatile_cursor),
|
|
@@ -489,6 +778,7 @@ export class CrossSdkJsAgent {
|
|
|
489
778
|
decrypted,
|
|
490
779
|
encrypted: Boolean(data.e2ee ?? data.encrypted),
|
|
491
780
|
seq: Number(data.seq ?? data.message_seq ?? data.msg_seq ?? 0) || 0,
|
|
781
|
+
need_render: data.need_render !== false,
|
|
492
782
|
error_code: errorCode,
|
|
493
783
|
raw_sha256: sha256Json(data),
|
|
494
784
|
...envelopeMetadata(data),
|
|
@@ -547,7 +837,7 @@ export class CrossSdkJsAgent {
|
|
|
547
837
|
const consensus = await this.client.checkOwnershipConsensus(subAid);
|
|
548
838
|
const files = { agent_md: null, owner_md: null, storage_agent_md: null };
|
|
549
839
|
const fileErrors = {};
|
|
550
|
-
const evidence = (raw
|
|
840
|
+
const evidence = (raw) => ({ raw_base64: Buffer.from(raw, 'utf-8').toString('base64'), sha256: crypto.createHash('sha256').update(raw, 'utf-8').digest('hex') });
|
|
551
841
|
const errorText = (error) => ownershipErrorCode(error) + ': ' + (error instanceof Error ? error.message : String(error));
|
|
552
842
|
let ns = null;
|
|
553
843
|
try {
|
|
@@ -568,7 +858,7 @@ export class CrossSdkJsAgent {
|
|
|
568
858
|
try {
|
|
569
859
|
const owner = await storage.readOwnerMd(ownerAid, subAid);
|
|
570
860
|
if (owner.exists)
|
|
571
|
-
files.owner_md = evidence(owner.content
|
|
861
|
+
files.owner_md = evidence(owner.content);
|
|
572
862
|
else
|
|
573
863
|
fileErrors.owner_md = 'not_found';
|
|
574
864
|
}
|
|
@@ -581,7 +871,7 @@ export class CrossSdkJsAgent {
|
|
|
581
871
|
try {
|
|
582
872
|
const copy = await storage.readFile(subAid, 'agent.md');
|
|
583
873
|
if (copy.exists)
|
|
584
|
-
files.storage_agent_md = evidence(copy.content
|
|
874
|
+
files.storage_agent_md = evidence(copy.content);
|
|
585
875
|
else
|
|
586
876
|
fileErrors.storage_agent_md = 'not_found';
|
|
587
877
|
}
|
|
@@ -611,6 +901,7 @@ export class CrossSdkJsAgent {
|
|
|
611
901
|
}
|
|
612
902
|
if (req.method === 'POST' && url.pathname === '/reset') {
|
|
613
903
|
const body = await readJson(req);
|
|
904
|
+
this.resetObservationLedgers();
|
|
614
905
|
const traceId = textOf(body.trace_id ?? '');
|
|
615
906
|
if (traceId) {
|
|
616
907
|
this.inbox = this.inbox.filter((item) => item.trace_id !== traceId);
|
|
@@ -621,6 +912,7 @@ export class CrossSdkJsAgent {
|
|
|
621
912
|
else {
|
|
622
913
|
this.inbox = [];
|
|
623
914
|
this.groupInbox = [];
|
|
915
|
+
this.peersSnapshots = [];
|
|
624
916
|
this.traces.clear();
|
|
625
917
|
this.sendResults.clear();
|
|
626
918
|
}
|
|
@@ -629,11 +921,25 @@ export class CrossSdkJsAgent {
|
|
|
629
921
|
}
|
|
630
922
|
if (req.method === 'POST' && url.pathname === '/test/raw-push/reset') {
|
|
631
923
|
const body = await readJson(req);
|
|
924
|
+
if ((body.pull_barrier_count !== undefined && (typeof body.pull_barrier_count !== 'number' || !Number.isSafeInteger(body.pull_barrier_count) || body.pull_barrier_count < 0)) ||
|
|
925
|
+
(body.pull_barrier_scope !== undefined && !['all', 'p2p'].includes(body.pull_barrier_scope)) ||
|
|
926
|
+
(body.observation_id !== undefined && typeof body.observation_id !== 'string') ||
|
|
927
|
+
(body.release_pull_barrier !== undefined && typeof body.release_pull_barrier !== 'boolean')) {
|
|
928
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid observation parameters' });
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (body.release_pull_barrier === true) {
|
|
932
|
+
this.releasePullBarrier();
|
|
933
|
+
sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
|
|
934
|
+
return;
|
|
935
|
+
}
|
|
936
|
+
this.resetObservationLedgers(body.pull_barrier_count, textOf(body.pull_barrier_scope), body);
|
|
632
937
|
this.rawPushObservations = [];
|
|
633
938
|
this.rawPushRpcMethods = [];
|
|
634
939
|
this.groupChangedEvents = [];
|
|
635
940
|
this.groupInviteEvents = [];
|
|
636
941
|
this.readStateEvents = [];
|
|
942
|
+
this.peersSnapshots = [];
|
|
637
943
|
this.rawPushObservationId = textOf(body.observation_id ?? '');
|
|
638
944
|
sendJson(res, 200, { ok: true, observation_id: this.rawPushObservationId });
|
|
639
945
|
return;
|
|
@@ -652,6 +958,11 @@ export class CrossSdkJsAgent {
|
|
|
652
958
|
let rpcEntries = [...this.rawPushRpcMethods];
|
|
653
959
|
let groupChangedEvents = [...this.groupChangedEvents];
|
|
654
960
|
let groupInviteEvents = [...this.groupInviteEvents];
|
|
961
|
+
let eventLedger = [...this.eventLedger];
|
|
962
|
+
let rpcSpans = [...this.rpcSpans];
|
|
963
|
+
let batchEvents = [...this.batchEvents];
|
|
964
|
+
if (observationId)
|
|
965
|
+
batchEvents = batchEvents.filter((item) => textOf(item.observation_id) === observationId);
|
|
655
966
|
if (observationId)
|
|
656
967
|
rpcEntries = rpcEntries.filter((item) => textOf(item.observation_id) === observationId);
|
|
657
968
|
if (observationId)
|
|
@@ -662,17 +973,26 @@ export class CrossSdkJsAgent {
|
|
|
662
973
|
groupChangedEvents = groupChangedEvents.filter((item) => textOf(item.group_id) === filters.group_id);
|
|
663
974
|
if (filters.group_id)
|
|
664
975
|
groupInviteEvents = groupInviteEvents.filter((item) => textOf(item.group_id) === filters.group_id);
|
|
976
|
+
for (const key of ['observation_id', 'scope', 'group_id'])
|
|
977
|
+
if (filters[key]) {
|
|
978
|
+
eventLedger = eventLedger.filter((item) => textOf(item[key]) === filters[key]);
|
|
979
|
+
rpcSpans = rpcSpans.filter((item) => textOf(item[key]) === filters[key]);
|
|
980
|
+
}
|
|
665
981
|
rpcEntries = rpcEntries.slice(-limit);
|
|
666
982
|
const rpcMethods = rpcEntries.map((item) => textOf(item.method));
|
|
667
983
|
const rpcMethodCounts = {};
|
|
668
984
|
for (const method of rpcMethods)
|
|
669
985
|
rpcMethodCounts[method] = Number(rpcMethodCounts[method] ?? 0) + 1;
|
|
670
986
|
sendJson(res, 200, {
|
|
671
|
-
ok: true, items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
|
|
987
|
+
ok: true, time_ms: Date.now(), items, count: items.length, rpc_methods: rpcMethods, rpc_method_counts: rpcMethodCounts,
|
|
672
988
|
rpc_calls: rpcEntries,
|
|
673
989
|
group_changed_events: groupChangedEvents.slice(-limit),
|
|
674
990
|
group_invite_events: groupInviteEvents.slice(-limit),
|
|
675
991
|
read_state_events: this.readStateEvents.slice(-limit),
|
|
992
|
+
peers_snapshots: this.peersSnapshots.slice(-limit),
|
|
993
|
+
event_ledger: eventLedger, rpc_spans: rpcSpans,
|
|
994
|
+
batch_event_mode: this.batchEventMode, batch_events: batchEvents, single_event_counts: this.singleEventCounts,
|
|
995
|
+
pull_barrier: { target: this.pullBarrier?.target ?? 0, waiting: this.pullBarrier?.waiting ?? 0, released: this.pullBarrier?.released ?? true, scope: this.pullBarrier?.scope ?? 'all' },
|
|
676
996
|
});
|
|
677
997
|
return;
|
|
678
998
|
}
|
|
@@ -680,6 +1000,10 @@ export class CrossSdkJsAgent {
|
|
|
680
1000
|
sendJson(res, 200, await this.identity());
|
|
681
1001
|
return;
|
|
682
1002
|
}
|
|
1003
|
+
if (req.method === 'POST' && url.pathname === '/signature/call') {
|
|
1004
|
+
await this.signatureCall(req, res);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
683
1007
|
if (req.method === 'POST' && ['/ownership/bind', '/ownership/accept', '/ownership/reject', '/ownership/release', '/ownership/transfer'].includes(url.pathname)) {
|
|
684
1008
|
await this.ownershipControl(req, res, url.pathname.slice('/ownership/'.length));
|
|
685
1009
|
return;
|
|
@@ -828,6 +1152,77 @@ export class CrossSdkJsAgent {
|
|
|
828
1152
|
});
|
|
829
1153
|
}
|
|
830
1154
|
}
|
|
1155
|
+
async signatureCall(req, res) {
|
|
1156
|
+
const body = await readJson(req);
|
|
1157
|
+
const action = textOf(body.action), aid = textOf(body.aid || this.aid);
|
|
1158
|
+
const store = this.aidStore();
|
|
1159
|
+
let fileBytes;
|
|
1160
|
+
let identity;
|
|
1161
|
+
const output = (result) => {
|
|
1162
|
+
const response = result.ok ? { ok: true, result: jsonSafe(result.data) } : { ok: false, error_code: result.error.code, error_message: result.error.message };
|
|
1163
|
+
if (identity) {
|
|
1164
|
+
response.certificate = identity.certPem;
|
|
1165
|
+
response.cert_fingerprint = identity.certFingerprint;
|
|
1166
|
+
}
|
|
1167
|
+
if (fileBytes)
|
|
1168
|
+
response.file_base64 = Buffer.from(fileBytes).toString('base64');
|
|
1169
|
+
sendJson(res, 200, response);
|
|
1170
|
+
};
|
|
1171
|
+
try {
|
|
1172
|
+
if (!['certificate', 'sign', 'verify', 'upload_agent_md', 'download_agent_md'].includes(action)) {
|
|
1173
|
+
output({ ok: false, error: { code: 'unsupported_action', message: action } });
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
if (action === 'upload_agent_md') {
|
|
1177
|
+
output(await store.uploadAgentMd(aid, textOf(body.input)));
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
if (action === 'download_agent_md') {
|
|
1181
|
+
output(await store.downloadAgentMd(aid));
|
|
1182
|
+
return;
|
|
1183
|
+
}
|
|
1184
|
+
let input = textOf(body.input);
|
|
1185
|
+
if (body.input_type === 'file') {
|
|
1186
|
+
fileBytes = new Uint8Array(typeof body.input_base64 === 'string' ? Buffer.from(body.input_base64, 'base64') : Buffer.from(textOf(body.input), 'utf8'));
|
|
1187
|
+
input = body.in_place === true ? {
|
|
1188
|
+
getFile: async () => new Blob([new Uint8Array(fileBytes)]),
|
|
1189
|
+
createWritable: async () => ({ write: async (content) => { fileBytes = new TextEncoder().encode(content); }, close: async () => { } }),
|
|
1190
|
+
} : new Blob([new Uint8Array(fileBytes)]);
|
|
1191
|
+
}
|
|
1192
|
+
if (action !== 'verify') {
|
|
1193
|
+
const loaded = await store.load(aid);
|
|
1194
|
+
if (!loaded.ok) {
|
|
1195
|
+
output(loaded);
|
|
1196
|
+
return;
|
|
1197
|
+
}
|
|
1198
|
+
identity = loaded.data.aid;
|
|
1199
|
+
if (action === 'certificate') {
|
|
1200
|
+
output({ ok: true, data: { aid, certificate: identity.certPem, cert_fingerprint: identity.certFingerprint } });
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
const options = { identity, inputType: body.input_type, format: body.format, append: body.append, tag: body.tag, inPlace: body.in_place };
|
|
1204
|
+
output(await store.sign(aid, input, options));
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
const certificates = jsonObjectOf(body.certificates) ?? {};
|
|
1208
|
+
const resolveCertificate = (signer) => {
|
|
1209
|
+
const certificate = textOf(certificates[signer]);
|
|
1210
|
+
if (!certificate)
|
|
1211
|
+
throw new Error(`verification certificate missing for AID: ${signer}`);
|
|
1212
|
+
return certificate;
|
|
1213
|
+
};
|
|
1214
|
+
if (body.format !== 'document' || certificates[aid])
|
|
1215
|
+
identity = await AID.create({ aid, aunPath: this.aunPath, certPem: resolveCertificate(aid), privateKeyPem: null, certValid: true, privateKeyValid: false });
|
|
1216
|
+
const options = { identity, inputType: body.input_type, format: body.format, certificateResolver: resolveCertificate };
|
|
1217
|
+
output(await store.verify(aid, input, textOf(body.signature), options));
|
|
1218
|
+
}
|
|
1219
|
+
catch (error) {
|
|
1220
|
+
output({ ok: false, error: { code: action === 'verify' ? 'VERIFICATION_OPERATION_ERROR' : 'SIGNATURE_OPERATION_ERROR', message: error instanceof Error ? error.message : String(error) } });
|
|
1221
|
+
}
|
|
1222
|
+
finally {
|
|
1223
|
+
store.close();
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
831
1226
|
async collabCall(req, res) {
|
|
832
1227
|
const body = await readJson(req);
|
|
833
1228
|
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
@@ -856,9 +1251,16 @@ export class CrossSdkJsAgent {
|
|
|
856
1251
|
const now = Math.floor(Date.now() / 1000);
|
|
857
1252
|
if (op === 'request')
|
|
858
1253
|
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)) {
|
|
1254
|
+
if (['register', 'sign', 'certificate', 'create', 'prepare'].includes(op)) {
|
|
860
1255
|
const store = this.aidStore();
|
|
861
1256
|
try {
|
|
1257
|
+
if (op === 'prepare') {
|
|
1258
|
+
const request = jsonObjectOf(params.params) ?? {};
|
|
1259
|
+
const orgId = textOf(params.org_id ?? request.org_id);
|
|
1260
|
+
const loaded = orgId ? await store.load(orgId) : null;
|
|
1261
|
+
const orgSigner = loaded?.ok && loaded.data.aid.isPrivateKeyValid() ? loaded.data.aid : undefined;
|
|
1262
|
+
return await this.client.org.prepareRequest(textOf(params.method), request, textOf(params.target), this.client.currentAid, { ...jsonObjectOf(params.options), orgSigner });
|
|
1263
|
+
}
|
|
862
1264
|
if (op === 'register') {
|
|
863
1265
|
const registered = await store.register(textOf(params.org_id), { type: 'org', owner: this.aid, exttype: 'company' });
|
|
864
1266
|
if (!registered.ok)
|
|
@@ -891,6 +1293,7 @@ export class CrossSdkJsAgent {
|
|
|
891
1293
|
const value = params.document ?? params.payload;
|
|
892
1294
|
const result = { canonical: new TextDecoder().decode(canonicalJson(value)), digest: digest(value), error_code: '' };
|
|
893
1295
|
if (op === 'verify' || op === 'verify_full') {
|
|
1296
|
+
const verificationNow = typeof params.now === 'number' ? params.now : now;
|
|
894
1297
|
const certificates = params.certificates;
|
|
895
1298
|
const resolver = (fingerprint) => certificates[fingerprint];
|
|
896
1299
|
try {
|
|
@@ -898,7 +1301,7 @@ export class CrossSdkJsAgent {
|
|
|
898
1301
|
const actual = jsonObjectOf(params.context) ?? params;
|
|
899
1302
|
const memberships = jsonObjectOf(params.memberships) ?? {};
|
|
900
1303
|
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,
|
|
1304
|
+
resolveStatus: (cid) => params.statuses?.[cid], now: verificationNow, audience: textOf(actual.audience), subject: textOf(actual.subject) || undefined,
|
|
902
1305
|
ns: textOf(actual.ns), action: textOf(actual.action), resource: textOf(actual.resource), rootIssuer: textOf(actual.root_issuer) || undefined,
|
|
903
1306
|
params: jsonObjectOf(actual.params) ?? {}, units: jsonObjectOf(actual.units) ?? {}, clientIp: textOf(actual.client_ip), method: textOf(actual.method), target: textOf(actual.target),
|
|
904
1307
|
requestId: textOf(actual.request_id), nonce: textOf(actual.nonce), requestBinding: actual.request_binding, proofChain: actual.proof_chain, membershipBinding: actual.membership_binding,
|
|
@@ -907,11 +1310,11 @@ export class CrossSdkJsAgent {
|
|
|
907
1310
|
resolveOwnership: (subject, owner) => (jsonObjectOf(params.ownerships) ?? {})[subject] === owner, checkRateLimit: () => params.rate_limit_allowed === true }) };
|
|
908
1311
|
}
|
|
909
1312
|
else if (params.kind === 'credential')
|
|
910
|
-
result.verification = { ...await this.client.credential.verifySignature(value, resolver,
|
|
1313
|
+
result.verification = { ...await this.client.credential.verifySignature(value, resolver, verificationNow, textOf(params.audience)) };
|
|
911
1314
|
else if (params.kind === 'binding')
|
|
912
|
-
await verifyMembershipBinding(value, resolver,
|
|
1315
|
+
await verifyMembershipBinding(value, resolver, verificationNow, textOf(params.expected_org_signer));
|
|
913
1316
|
else
|
|
914
|
-
result.verification = { ...await this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid),
|
|
1317
|
+
result.verification = { ...await this.client.credential.verifyRegistrationReceipt(value, params.credential, params.request, resolver, textOf(params.service_aid), verificationNow) };
|
|
915
1318
|
}
|
|
916
1319
|
catch (error) {
|
|
917
1320
|
if (!(error instanceof ProtocolError))
|
|
@@ -1610,6 +2013,8 @@ export class CrossSdkJsAgent {
|
|
|
1610
2013
|
};
|
|
1611
2014
|
if (body.ttl !== undefined)
|
|
1612
2015
|
params.ttl = Number(body.ttl);
|
|
2016
|
+
if (body.need_render !== undefined)
|
|
2017
|
+
params.need_render = body.need_render;
|
|
1613
2018
|
const result = await this.client.call('message.send', params);
|
|
1614
2019
|
const resultObj = jsonObjectOf(result) ?? {};
|
|
1615
2020
|
const response = {
|
|
@@ -1644,6 +2049,10 @@ export class CrossSdkJsAgent {
|
|
|
1644
2049
|
const params = {};
|
|
1645
2050
|
if (seq > 0)
|
|
1646
2051
|
params.seq = seq;
|
|
2052
|
+
if (textOf(body.sender_aid).trim())
|
|
2053
|
+
params.sender_aid = textOf(body.sender_aid).trim();
|
|
2054
|
+
if (body.cursor_scope !== undefined)
|
|
2055
|
+
params.cursor_scope = body.cursor_scope;
|
|
1647
2056
|
if (Array.isArray(body.volatile_cursors))
|
|
1648
2057
|
params.volatile_cursors = body.volatile_cursors;
|
|
1649
2058
|
else if (textOf(body.volatile_cursor))
|
|
@@ -1665,9 +2074,26 @@ export class CrossSdkJsAgent {
|
|
|
1665
2074
|
}
|
|
1666
2075
|
async pull(req, res) {
|
|
1667
2076
|
const body = await readJson(req);
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
2077
|
+
if ((body.limit !== undefined && (typeof body.limit !== 'number' || !Number.isSafeInteger(body.limit) || body.limit < 1 || body.limit > 200)) ||
|
|
2078
|
+
(body.max_pages !== undefined && (typeof body.max_pages !== 'number' || !Number.isSafeInteger(body.max_pages) || body.max_pages < 1)) ||
|
|
2079
|
+
(body.after_seq !== undefined && (typeof body.after_seq !== 'number' || !Number.isSafeInteger(body.after_seq) || body.after_seq < 0)) ||
|
|
2080
|
+
['sender_aid', 'cursor_scope', 'after_volatile_cursor'].some((key) => body[key] !== undefined && typeof body[key] !== 'string') ||
|
|
2081
|
+
(body.force !== undefined && typeof body.force !== 'boolean') ||
|
|
2082
|
+
(body.window_mode !== undefined && !['forward', 'tail'].includes(body.window_mode))) {
|
|
2083
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid pull parameters' });
|
|
2084
|
+
return;
|
|
2085
|
+
}
|
|
2086
|
+
const senderAid = textOf(body.sender_aid).trim();
|
|
2087
|
+
const limit = body.limit ?? 50;
|
|
2088
|
+
const params = { limit };
|
|
2089
|
+
if (senderAid)
|
|
2090
|
+
params.sender_aid = senderAid;
|
|
2091
|
+
if (body.after_seq !== undefined)
|
|
2092
|
+
params.after_seq = body.after_seq;
|
|
2093
|
+
for (const key of ['cursor_scope', 'force', 'window_mode', 'max_pages']) {
|
|
2094
|
+
if (body[key] !== undefined)
|
|
2095
|
+
params[key] = body[key];
|
|
2096
|
+
}
|
|
1671
2097
|
if (textOf(body.after_volatile_cursor))
|
|
1672
2098
|
params.after_volatile_cursor = textOf(body.after_volatile_cursor);
|
|
1673
2099
|
try {
|
|
@@ -1688,6 +2114,20 @@ export class CrossSdkJsAgent {
|
|
|
1688
2114
|
}
|
|
1689
2115
|
async messageCall(req, res) {
|
|
1690
2116
|
const body = await readJson(req);
|
|
2117
|
+
if (['start', 'status', 'detach'].includes(textOf(body.action))) {
|
|
2118
|
+
this.messageOperations ??= new Map();
|
|
2119
|
+
const id = textOf(body.operation_id);
|
|
2120
|
+
if (body.action === 'start') {
|
|
2121
|
+
const operation = { ok: true, operation_id: id, status: 'running', started_ms: Date.now() };
|
|
2122
|
+
this.messageOperations.set(id, operation);
|
|
2123
|
+
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) }));
|
|
2124
|
+
}
|
|
2125
|
+
const operation = this.messageOperations.get(id);
|
|
2126
|
+
if (body.action === 'detach')
|
|
2127
|
+
Object.assign(operation, { detached: true, cancellation: 'application_wait_only', detached_ms: Date.now() });
|
|
2128
|
+
sendJson(res, 200, operation);
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
1691
2131
|
const traceId = textOf(body.trace_id || crypto.randomUUID().replace(/-/g, ''));
|
|
1692
2132
|
const method = textOf(body.method).trim();
|
|
1693
2133
|
const params = jsonObjectOf(body.params) ?? {};
|
|
@@ -1719,15 +2159,15 @@ export class CrossSdkJsAgent {
|
|
|
1719
2159
|
sendJson(res, 500, error);
|
|
1720
2160
|
}
|
|
1721
2161
|
}
|
|
1722
|
-
syncNamespace(scope, groupId = '') {
|
|
2162
|
+
syncNamespace(scope, groupId = '', senderAid = '') {
|
|
1723
2163
|
if (scope === 'p2p')
|
|
1724
|
-
return `p2p:${this.aid}`;
|
|
2164
|
+
return senderAid ? `p2p.peer.v1/${Buffer.from(senderAid, 'utf8').toString('base64url')}` : `p2p:${this.aid}`;
|
|
1725
2165
|
if (scope === 'group' && groupId)
|
|
1726
2166
|
return `group:${groupId}`;
|
|
1727
2167
|
throw new Error('scope must be p2p, or group with group_id');
|
|
1728
2168
|
}
|
|
1729
|
-
syncStateValue(scope, groupId = '') {
|
|
1730
|
-
const namespace = this.syncNamespace(scope, groupId);
|
|
2169
|
+
syncStateValue(scope, groupId = '', senderAid = '') {
|
|
2170
|
+
const namespace = this.syncNamespace(scope, groupId, senderAid);
|
|
1731
2171
|
const tracker = this.client._seqTracker;
|
|
1732
2172
|
if (!tracker)
|
|
1733
2173
|
throw new Error('SeqTracker is unavailable');
|
|
@@ -1736,9 +2176,10 @@ export class CrossSdkJsAgent {
|
|
|
1736
2176
|
syncState(url, res) {
|
|
1737
2177
|
const scope = textOf(url.searchParams.get('scope') || 'p2p').trim().toLowerCase();
|
|
1738
2178
|
const groupId = textOf(url.searchParams.get('group_id') || '').trim();
|
|
2179
|
+
const senderAid = textOf(url.searchParams.get('sender_aid') || '').trim();
|
|
1739
2180
|
try {
|
|
1740
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
1741
|
-
sendJson(res, 200, { ok: true, scope, ...snapshot });
|
|
2181
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
2182
|
+
sendJson(res, 200, { ok: true, scope, ...snapshot, peers_snapshots: this.peersSnapshots });
|
|
1742
2183
|
}
|
|
1743
2184
|
catch (err) {
|
|
1744
2185
|
sendJson(res, 400, {
|
|
@@ -1758,27 +2199,24 @@ export class CrossSdkJsAgent {
|
|
|
1758
2199
|
const body = await readJson(req);
|
|
1759
2200
|
const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
|
|
1760
2201
|
const groupId = textOf(body.group_id || '').trim();
|
|
2202
|
+
const senderAid = textOf(body.sender_aid || '').trim();
|
|
1761
2203
|
const afterSeq = Number(body.after_seq ?? 0) || 0;
|
|
1762
2204
|
const limit = Number(body.limit ?? 50) || 50;
|
|
1763
2205
|
try {
|
|
1764
|
-
const params = {
|
|
1765
|
-
if (
|
|
1766
|
-
params.
|
|
2206
|
+
const params = { limit, ...(tail ? { window_mode: 'tail' } : {}) };
|
|
2207
|
+
if (scope === 'group' || body.after_seq !== undefined)
|
|
2208
|
+
params.after_seq = afterSeq;
|
|
2209
|
+
if (scope === 'p2p' && senderAid)
|
|
2210
|
+
params.sender_aid = senderAid;
|
|
2211
|
+
for (const key of ['cursor_scope', 'force', 'max_pages', 'after_volatile_cursor']) {
|
|
2212
|
+
if (body[key] !== undefined)
|
|
2213
|
+
params[key] = body[key];
|
|
2214
|
+
}
|
|
1767
2215
|
const method = scope === 'group' ? 'group.pull' : 'message.pull';
|
|
1768
2216
|
if (scope === 'group')
|
|
1769
2217
|
params.group_id = groupId;
|
|
1770
|
-
this.syncNamespace(scope, groupId);
|
|
1771
|
-
|
|
1772
|
-
if (tail) {
|
|
1773
|
-
result = await this.client.call(method, params);
|
|
1774
|
-
}
|
|
1775
|
-
else {
|
|
1776
|
-
const transport = this.client._transport;
|
|
1777
|
-
if (!transport?.call)
|
|
1778
|
-
throw new Error('raw transport is unavailable');
|
|
1779
|
-
const wireMethod = scope === 'group' ? 'group.v2.pull' : 'message.v2.pull';
|
|
1780
|
-
result = await transport.call(wireMethod, params);
|
|
1781
|
-
}
|
|
2218
|
+
this.syncNamespace(scope, groupId, senderAid);
|
|
2219
|
+
const result = await this.client.call(method, params);
|
|
1782
2220
|
const resultObj = jsonObjectOf(result) ?? {};
|
|
1783
2221
|
const messages = Array.isArray(resultObj.messages) ? resultObj.messages : [];
|
|
1784
2222
|
if (tail) {
|
|
@@ -1789,7 +2227,7 @@ export class CrossSdkJsAgent {
|
|
|
1789
2227
|
await this.storeInboxItem(this.normalizeMessage(msg, true));
|
|
1790
2228
|
}
|
|
1791
2229
|
}
|
|
1792
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
2230
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
1793
2231
|
sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
|
|
1794
2232
|
}
|
|
1795
2233
|
catch (err) {
|
|
@@ -1805,6 +2243,7 @@ export class CrossSdkJsAgent {
|
|
|
1805
2243
|
const body = await readJson(req);
|
|
1806
2244
|
const scope = textOf(body.scope || 'p2p').trim().toLowerCase();
|
|
1807
2245
|
const groupId = textOf(body.group_id || '').trim();
|
|
2246
|
+
const senderAid = textOf(body.sender_aid || '').trim();
|
|
1808
2247
|
const beforeSeq = Number(body.before_seq ?? 0) || 0;
|
|
1809
2248
|
const limit = Number(body.limit ?? 50) || 50;
|
|
1810
2249
|
try {
|
|
@@ -1812,9 +2251,13 @@ export class CrossSdkJsAgent {
|
|
|
1812
2251
|
const method = scope === 'group' ? 'group.history' : 'message.history';
|
|
1813
2252
|
if (scope === 'group')
|
|
1814
2253
|
params.group_id = groupId;
|
|
1815
|
-
|
|
2254
|
+
if (scope === 'p2p' && senderAid)
|
|
2255
|
+
params.sender_aid = senderAid;
|
|
2256
|
+
if (body.cursor_scope !== undefined)
|
|
2257
|
+
params.cursor_scope = body.cursor_scope;
|
|
2258
|
+
this.syncNamespace(scope, groupId, senderAid);
|
|
1816
2259
|
const result = await this.client.call(method, params);
|
|
1817
|
-
const snapshot = this.syncStateValue(scope, groupId);
|
|
2260
|
+
const snapshot = this.syncStateValue(scope, groupId, senderAid);
|
|
1818
2261
|
sendJson(res, 200, { ok: true, scope, ...snapshot, result: jsonSafe(result) });
|
|
1819
2262
|
}
|
|
1820
2263
|
catch (err) {
|
|
@@ -1829,7 +2272,27 @@ export class CrossSdkJsAgent {
|
|
|
1829
2272
|
async sessionDisconnect(res) {
|
|
1830
2273
|
try {
|
|
1831
2274
|
this.ready = false;
|
|
1832
|
-
|
|
2275
|
+
if (!this.pullBarrier?.preserve)
|
|
2276
|
+
this.releasePullBarrier();
|
|
2277
|
+
const barrier = this.pullBarrier;
|
|
2278
|
+
let restore;
|
|
2279
|
+
if (barrier?.preserve && barrier.waiting === 1 && !barrier.released && ['message.pull', 'message.v2.pull'].includes(barrier.heldMethod ?? '')) {
|
|
2280
|
+
const binding = barrier.owner, pipeline = this.client._rpcPipeline;
|
|
2281
|
+
const owners = [...(pipeline?.pullGateStates.values() ?? [])].flatMap((state) => [...state.activeByNamespace.values()])
|
|
2282
|
+
.filter((job) => job === binding?.job && job.key === binding.key && job.namespace === binding.namespace && job.running && !job.timedOut && !job.invalidated && job.cancel === binding.cancel);
|
|
2283
|
+
if (owners.length !== 1)
|
|
2284
|
+
throw new Error('保留响应无法定位唯一受跟踪的 pull owner');
|
|
2285
|
+
const owner = owners[0], settled = owner.settled;
|
|
2286
|
+
owner.settled = Promise.resolve();
|
|
2287
|
+
restore = () => { owner.settled = settled; };
|
|
2288
|
+
barrier.span.barrier_owner_isolated = true;
|
|
2289
|
+
}
|
|
2290
|
+
try {
|
|
2291
|
+
await this.client.disconnect();
|
|
2292
|
+
}
|
|
2293
|
+
finally {
|
|
2294
|
+
restore?.();
|
|
2295
|
+
}
|
|
1833
2296
|
sendJson(res, 200, { ok: true, state: textOf(this.client.state) });
|
|
1834
2297
|
}
|
|
1835
2298
|
catch (err) {
|
|
@@ -1842,14 +2305,21 @@ export class CrossSdkJsAgent {
|
|
|
1842
2305
|
}
|
|
1843
2306
|
async sessionConnect(req, res) {
|
|
1844
2307
|
const body = await readJson(req);
|
|
2308
|
+
if (['background_sync', 'inline_realtime_payload_v1', 'batch_events'].some((key) => body[key] !== undefined && typeof body[key] !== 'boolean')) {
|
|
2309
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid session parameters' });
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
1845
2312
|
const backgroundSync = body.background_sync === true;
|
|
1846
2313
|
if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
|
|
1847
2314
|
this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
|
|
1848
2315
|
}
|
|
2316
|
+
if (typeof body.batch_events === 'boolean')
|
|
2317
|
+
this.batchEventMode = body.batch_events;
|
|
2318
|
+
this.syncBatchEventHandlers();
|
|
1849
2319
|
try {
|
|
1850
2320
|
await this.client.connect({ auto_reconnect: false, background_sync: backgroundSync });
|
|
1851
2321
|
this.ready = true;
|
|
1852
|
-
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
|
|
2322
|
+
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1, batch_events: this.batchEventMode });
|
|
1853
2323
|
}
|
|
1854
2324
|
catch (err) {
|
|
1855
2325
|
sendJson(res, 500, {
|
|
@@ -1861,19 +2331,27 @@ export class CrossSdkJsAgent {
|
|
|
1861
2331
|
}
|
|
1862
2332
|
async sessionRestart(req, res) {
|
|
1863
2333
|
const body = await readJson(req);
|
|
2334
|
+
if (['background_sync', 'inline_realtime_payload_v1', 'batch_events'].some((key) => body[key] !== undefined && typeof body[key] !== 'boolean')) {
|
|
2335
|
+
sendJson(res, 400, { ok: false, error_code: 'bad_request', error_message: 'invalid session parameters' });
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
1864
2338
|
const backgroundSync = body.background_sync === true;
|
|
1865
2339
|
if (Object.prototype.hasOwnProperty.call(body, 'inline_realtime_payload_v1')) {
|
|
1866
2340
|
this.inlineRealtimePayloadV1 = body.inline_realtime_payload_v1 !== false;
|
|
1867
2341
|
}
|
|
2342
|
+
if (typeof body.batch_events === 'boolean')
|
|
2343
|
+
this.batchEventMode = body.batch_events;
|
|
2344
|
+
this.syncBatchEventHandlers();
|
|
1868
2345
|
this.ready = false;
|
|
1869
2346
|
try {
|
|
2347
|
+
this.releasePullBarrier();
|
|
1870
2348
|
await this.client.close();
|
|
1871
2349
|
this.client = this.newClient();
|
|
1872
2350
|
this.bindClientEvents();
|
|
1873
2351
|
await this.ensureConnected(backgroundSync);
|
|
1874
2352
|
this.startupError = '';
|
|
1875
2353
|
this.ready = true;
|
|
1876
|
-
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1 });
|
|
2354
|
+
sendJson(res, 200, { ok: true, background_sync: backgroundSync, inline_realtime_payload_v1: this.inlineRealtimePayloadV1, batch_events: this.batchEventMode });
|
|
1877
2355
|
}
|
|
1878
2356
|
catch (err) {
|
|
1879
2357
|
this.startupError = `${err instanceof Error ? err.name : 'Error'}: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -1987,6 +2465,7 @@ export class CrossSdkJsAgent {
|
|
|
1987
2465
|
payload: { type: 'text', text, trace_id: traceId, case_id: textOf(body.case_id || traceId) },
|
|
1988
2466
|
encrypt: e2ee,
|
|
1989
2467
|
message_id: messageId,
|
|
2468
|
+
...(body.need_render !== undefined ? { need_render: body.need_render } : {}),
|
|
1990
2469
|
});
|
|
1991
2470
|
const resultObj = jsonObjectOf(result) ?? {};
|
|
1992
2471
|
const response = {
|