@agentunion/fastaun-browser 0.5.17 → 0.5.18
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 +41 -0
- package/_packed_docs/CHANGELOG.md +41 -0
- package/_packed_docs/INDEX.md +28 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +11 -5
- package/_packed_docs/agent.md/SCHEMA.md +37 -18
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +23 -17
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -2
- package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +95 -6
- package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +19 -4
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +25 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +42 -4
- package/_packed_docs/sdk/09-storage-rpc-manual.md +25 -4
- package/_packed_docs/sdk/INDEX.md +3 -2
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +2 -0
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +1 -1
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +6 -3
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +45 -0
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +231 -3
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +17 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +49 -2
- package/dist/aid.js.map +1 -1
- package/dist/bundle.js +1315 -628
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +5 -3
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +1 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +51 -7
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +9 -1
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +771 -471
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +13 -2
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +2 -0
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +6 -0
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/ownership-proof.d.ts +30 -0
- package/dist/ownership-proof.d.ts.map +1 -0
- package/dist/ownership-proof.js +97 -0
- package/dist/ownership-proof.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
package/dist/bundle.js
CHANGED
|
@@ -460,7 +460,7 @@ var init_indexeddb_store = __esm({
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
// src/version.ts
|
|
463
|
-
var VERSION = "0.5.
|
|
463
|
+
var VERSION = "0.5.18";
|
|
464
464
|
|
|
465
465
|
// src/types.ts
|
|
466
466
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -6723,6 +6723,16 @@ var MessageDeliveryEngine = class {
|
|
|
6723
6723
|
if (typeof senderDevice !== "string" || !senderDevice.trim() || !String(client._deviceId ?? "").trim()) return false;
|
|
6724
6724
|
return senderDevice.trim() === String(client._deviceId).trim();
|
|
6725
6725
|
}
|
|
6726
|
+
isSelfSentP2PMessage(message) {
|
|
6727
|
+
if (!isJsonObject(message)) return false;
|
|
6728
|
+
const client = this.runtime.client;
|
|
6729
|
+
if (String(message.direction ?? "").trim() !== "outbound_sync") return false;
|
|
6730
|
+
const selfAid = String(client._aid ?? "").trim().toLowerCase();
|
|
6731
|
+
const senderAid = String(message.sender_aid ?? message.from_aid ?? message.from ?? "").trim().toLowerCase();
|
|
6732
|
+
if (!selfAid || senderAid !== selfAid) return false;
|
|
6733
|
+
const senderDevice = Object.prototype.hasOwnProperty.call(message, "sender_device_id") ? message.sender_device_id : message.from_device_id;
|
|
6734
|
+
return typeof senderDevice === "string" && Boolean(senderDevice.trim()) && Boolean(String(client._deviceId ?? "").trim()) && senderDevice.trim() === String(client._deviceId).trim();
|
|
6735
|
+
}
|
|
6726
6736
|
strictTargetString(source, key) {
|
|
6727
6737
|
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
6728
6738
|
return { present: false, valid: false, value: "" };
|
|
@@ -6828,14 +6838,18 @@ var MessageDeliveryEngine = class {
|
|
|
6828
6838
|
await this.processV2P2PNotification(msg, "v1", inlineGeneration);
|
|
6829
6839
|
return;
|
|
6830
6840
|
}
|
|
6841
|
+
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
6831
6842
|
if (client._aid && legacyPushSeq > 0) {
|
|
6832
6843
|
const ns2 = `p2p:${client._aid}`;
|
|
6833
6844
|
client._seqTracker.updateMaxSeen(ns2, legacyPushSeq);
|
|
6834
|
-
if (
|
|
6845
|
+
if (encryptedPush) {
|
|
6846
|
+
if (client._sessionOptions?.background_sync !== false) client._safeAsync(this.fillP2pGap());
|
|
6847
|
+
return;
|
|
6848
|
+
}
|
|
6849
|
+
await this.publishPulledMessage("message.received", ns2, legacyPushSeq, msg, true, "push");
|
|
6835
6850
|
return;
|
|
6836
6851
|
}
|
|
6837
6852
|
const seq2 = msg.seq;
|
|
6838
|
-
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
6839
6853
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
6840
6854
|
const seqNum = positiveSafeSequenceHint(seq2);
|
|
6841
6855
|
if (ns && seqNum > 0) {
|
|
@@ -6848,7 +6862,7 @@ var MessageDeliveryEngine = class {
|
|
|
6848
6862
|
}
|
|
6849
6863
|
if (ns && seq2 !== void 0 && seq2 !== null) {
|
|
6850
6864
|
await this.publishOrderedMessage("message.received", ns, seq2, msg);
|
|
6851
|
-
} else {
|
|
6865
|
+
} else if (!this.isSelfSentP2PMessage(msg)) {
|
|
6852
6866
|
await client._publishAppEvent("message.received", msg, "push");
|
|
6853
6867
|
}
|
|
6854
6868
|
} catch (exc) {
|
|
@@ -6907,9 +6921,18 @@ var MessageDeliveryEngine = class {
|
|
|
6907
6921
|
if (groupId && legacyPushSeq > 0) {
|
|
6908
6922
|
const ns2 = `group:${groupId}`;
|
|
6909
6923
|
client._seqTracker.updateMaxSeen(ns2, legacyPushSeq);
|
|
6910
|
-
if (client._sessionOptions?.background_sync
|
|
6924
|
+
if (client._sessionOptions?.background_sync === false) return;
|
|
6925
|
+
const encryptedPush2 = client._isEncryptedPushMessage(msg);
|
|
6926
|
+
if (encryptedPush2) {
|
|
6927
|
+
client._safeAsync(this.fillGroupGap(groupId));
|
|
6928
|
+
return;
|
|
6929
|
+
}
|
|
6930
|
+
if (!this.isSelfSentGroupMessage(msg)) {
|
|
6931
|
+
await this.publishPulledMessage("group.message_created", ns2, legacyPushSeq, msg, true, "push");
|
|
6932
|
+
}
|
|
6911
6933
|
return;
|
|
6912
6934
|
}
|
|
6935
|
+
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
6913
6936
|
if (payload === void 0 || payload === null || typeof payload === "object" && Object.keys(payload).length === 0) {
|
|
6914
6937
|
if (client._v2Session) await this.autoPullGroupMessages(msg);
|
|
6915
6938
|
else if (groupId && seq2 !== void 0 && seq2 !== null) {
|
|
@@ -6923,7 +6946,6 @@ var MessageDeliveryEngine = class {
|
|
|
6923
6946
|
} else await client._publishAppEvent("group.message_created", msg, "push");
|
|
6924
6947
|
return;
|
|
6925
6948
|
}
|
|
6926
|
-
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
6927
6949
|
const ns = groupId ? `group:${groupId}` : "";
|
|
6928
6950
|
const seqNum = positiveSafeSequenceHint(seq2);
|
|
6929
6951
|
if (ns && seqNum > 0) {
|
|
@@ -7581,7 +7603,8 @@ var MessageDeliveryEngine = class {
|
|
|
7581
7603
|
const tracker = client._seqTracker;
|
|
7582
7604
|
const snapshot = typeof tracker.snapshotNamespace === "function" ? tracker.snapshotNamespace(ns) : null;
|
|
7583
7605
|
try {
|
|
7584
|
-
const
|
|
7606
|
+
const selfSync = event === "message.received" && this.isSelfSentP2PMessage(payload) || event === "group.message_created" && this.isSelfSentGroupMessage(payload);
|
|
7607
|
+
const published = selfSync ? (this.markPublishedSeq(ns, seq2), true) : await this.publishPulledMessage(event, ns, seq2, payload, true, "inline_push");
|
|
7585
7608
|
if (!this.isInlineGenerationCurrent(generation)) return false;
|
|
7586
7609
|
if (!published) return false;
|
|
7587
7610
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq2));
|
|
@@ -8448,6 +8471,11 @@ var MessageDeliveryEngine = class {
|
|
|
8448
8471
|
if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8449
8472
|
return false;
|
|
8450
8473
|
}
|
|
8474
|
+
if (event === "message.received" && this.isSelfSentP2PMessage(payload)) {
|
|
8475
|
+
this.markPublishedSeq(ns, seqNum);
|
|
8476
|
+
client._clientLog.debug(`publish ordered skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
8477
|
+
return false;
|
|
8478
|
+
}
|
|
8451
8479
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
8452
8480
|
if (seqNum > contig) {
|
|
8453
8481
|
this.enqueueOrderedMessage(ns, event, seqNum, payload, source);
|
|
@@ -8494,6 +8522,11 @@ var MessageDeliveryEngine = class {
|
|
|
8494
8522
|
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8495
8523
|
return false;
|
|
8496
8524
|
}
|
|
8525
|
+
if (event === "message.received" && this.isSelfSentP2PMessage(payload)) {
|
|
8526
|
+
this.markPublishedSeq(ns, seqNum);
|
|
8527
|
+
client._clientLog.debug(`publish pulled skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
8528
|
+
return source === "inline_push";
|
|
8529
|
+
}
|
|
8497
8530
|
await this.drainOrderedMessages(ns, seqNum, false, persist, batch, source);
|
|
8498
8531
|
if (pullScoped) this.ensurePullOperationCurrent(ns);
|
|
8499
8532
|
queue?.delete(seqNum);
|
|
@@ -9190,8 +9223,11 @@ async function certMatchesFingerprint(certPem, fingerprint) {
|
|
|
9190
9223
|
const expected = normalizeFingerprintHex(fingerprint);
|
|
9191
9224
|
if (!expected) return false;
|
|
9192
9225
|
const derHex = (await certificateSha256Fingerprint(certPem)).slice("sha256:".length);
|
|
9193
|
-
|
|
9194
|
-
|
|
9226
|
+
const spkiHex = (await publicKeyFingerprint(certPem)).slice("sha256:".length);
|
|
9227
|
+
if (expected.length === 16) {
|
|
9228
|
+
return derHex.slice(0, 16) === expected || spkiHex.slice(0, 16) === expected;
|
|
9229
|
+
}
|
|
9230
|
+
return derHex === expected || spkiHex === expected;
|
|
9195
9231
|
}
|
|
9196
9232
|
async function publicKeyMatchesFingerprint(certPem, fingerprint) {
|
|
9197
9233
|
const expected = normalizeFingerprintHex(fingerprint);
|
|
@@ -15648,6 +15684,241 @@ function parseDocument(source, options = {}) {
|
|
|
15648
15684
|
return doc;
|
|
15649
15685
|
}
|
|
15650
15686
|
|
|
15687
|
+
// src/v2/crypto/canonical.ts
|
|
15688
|
+
var encoder = new TextEncoder();
|
|
15689
|
+
var MAX_SAFE_JSON_INTEGER = 9007199254740991;
|
|
15690
|
+
function canonicalJson(obj) {
|
|
15691
|
+
return encoder.encode(serialize(obj));
|
|
15692
|
+
}
|
|
15693
|
+
function serialize(obj) {
|
|
15694
|
+
if (obj === null) return "null";
|
|
15695
|
+
if (obj === true) return "true";
|
|
15696
|
+
if (obj === false) return "false";
|
|
15697
|
+
if (typeof obj === "number") {
|
|
15698
|
+
return serializeNumber(obj);
|
|
15699
|
+
}
|
|
15700
|
+
if (typeof obj === "string") {
|
|
15701
|
+
return serializeString(obj);
|
|
15702
|
+
}
|
|
15703
|
+
if (Array.isArray(obj)) {
|
|
15704
|
+
return serializeArray(obj);
|
|
15705
|
+
}
|
|
15706
|
+
if (typeof obj === "object") {
|
|
15707
|
+
return serializeObject(obj);
|
|
15708
|
+
}
|
|
15709
|
+
throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
|
|
15710
|
+
}
|
|
15711
|
+
function serializeNumber(n) {
|
|
15712
|
+
if (!isFinite(n)) {
|
|
15713
|
+
throw new RangeError("canonicalJson: Infinity and NaN not allowed");
|
|
15714
|
+
}
|
|
15715
|
+
if (Object.is(n, -0)) return "0";
|
|
15716
|
+
if (Number.isInteger(n)) {
|
|
15717
|
+
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
15718
|
+
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
15719
|
+
}
|
|
15720
|
+
return String(n);
|
|
15721
|
+
}
|
|
15722
|
+
return expandExponent(String(n));
|
|
15723
|
+
}
|
|
15724
|
+
function expandExponent(s) {
|
|
15725
|
+
if (!/[eE]/.test(s)) return s;
|
|
15726
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
15727
|
+
if (!match) {
|
|
15728
|
+
throw new TypeError(`canonicalJson: invalid number ${s}`);
|
|
15729
|
+
}
|
|
15730
|
+
const sign = match[1] ?? "";
|
|
15731
|
+
const intPart = match[2] ?? "";
|
|
15732
|
+
const fracPart = match[3] ?? "";
|
|
15733
|
+
const exp = Number(match[4]);
|
|
15734
|
+
const digits = intPart + fracPart;
|
|
15735
|
+
const point = intPart.length + exp;
|
|
15736
|
+
if (point <= 0) {
|
|
15737
|
+
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
15738
|
+
}
|
|
15739
|
+
if (point >= digits.length) {
|
|
15740
|
+
return `${sign}${digits}${"0".repeat(point - digits.length)}`;
|
|
15741
|
+
}
|
|
15742
|
+
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
15743
|
+
}
|
|
15744
|
+
function serializeString(s) {
|
|
15745
|
+
let result = '"';
|
|
15746
|
+
for (let i = 0; i < s.length; i++) {
|
|
15747
|
+
const ch = s[i];
|
|
15748
|
+
const code = s.charCodeAt(i);
|
|
15749
|
+
if (ch === '"') {
|
|
15750
|
+
result += '\\"';
|
|
15751
|
+
} else if (ch === "\\") {
|
|
15752
|
+
result += "\\\\";
|
|
15753
|
+
} else if (ch === "\b") {
|
|
15754
|
+
result += "\\b";
|
|
15755
|
+
} else if (ch === "\f") {
|
|
15756
|
+
result += "\\f";
|
|
15757
|
+
} else if (ch === "\n") {
|
|
15758
|
+
result += "\\n";
|
|
15759
|
+
} else if (ch === "\r") {
|
|
15760
|
+
result += "\\r";
|
|
15761
|
+
} else if (ch === " ") {
|
|
15762
|
+
result += "\\t";
|
|
15763
|
+
} else if (code < 32) {
|
|
15764
|
+
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
15765
|
+
} else {
|
|
15766
|
+
result += ch;
|
|
15767
|
+
}
|
|
15768
|
+
}
|
|
15769
|
+
result += '"';
|
|
15770
|
+
return result;
|
|
15771
|
+
}
|
|
15772
|
+
function serializeArray(arr) {
|
|
15773
|
+
const items = arr.map((item) => serialize(item));
|
|
15774
|
+
return "[" + items.join(",") + "]";
|
|
15775
|
+
}
|
|
15776
|
+
function serializeObject(obj) {
|
|
15777
|
+
const sortedKeys = Object.keys(obj).sort(compareCodePoints);
|
|
15778
|
+
const pairs2 = sortedKeys.map(
|
|
15779
|
+
(key) => serializeString(key) + ":" + serialize(obj[key])
|
|
15780
|
+
);
|
|
15781
|
+
return "{" + pairs2.join(",") + "}";
|
|
15782
|
+
}
|
|
15783
|
+
function compareCodePoints(a, b) {
|
|
15784
|
+
const ac = Array.from(a);
|
|
15785
|
+
const bc = Array.from(b);
|
|
15786
|
+
const n = Math.min(ac.length, bc.length);
|
|
15787
|
+
for (let i = 0; i < n; i++) {
|
|
15788
|
+
const av = ac[i].codePointAt(0) ?? 0;
|
|
15789
|
+
const bv = bc[i].codePointAt(0) ?? 0;
|
|
15790
|
+
if (av !== bv) return av - bv;
|
|
15791
|
+
}
|
|
15792
|
+
return ac.length - bc.length;
|
|
15793
|
+
}
|
|
15794
|
+
|
|
15795
|
+
// src/ownership-proof.ts
|
|
15796
|
+
var OWNERSHIP_RELATION_DOMAIN = "AUN-AID-OWNER-RELATION-V1;";
|
|
15797
|
+
var PROOF_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
15798
|
+
var FINGERPRINT_RE = /^sha256:[0-9a-f]{64}$/i;
|
|
15799
|
+
function sha256Hex(input) {
|
|
15800
|
+
const K = [1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298];
|
|
15801
|
+
const bytes = new Uint8Array(input.length + 1 + ((55 - input.length) % 64 + 64) % 64 + 8);
|
|
15802
|
+
bytes.set(input);
|
|
15803
|
+
bytes[input.length] = 128;
|
|
15804
|
+
const bits = input.length * 8;
|
|
15805
|
+
new DataView(bytes.buffer).setUint32(bytes.length - 4, bits >>> 0, false);
|
|
15806
|
+
new DataView(bytes.buffer).setUint32(bytes.length - 8, Math.floor(bits / 4294967296), false);
|
|
15807
|
+
let h = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225];
|
|
15808
|
+
const rotr2 = (x, n) => x >>> n | x << 32 - n;
|
|
15809
|
+
for (let o = 0; o < bytes.length; o += 64) {
|
|
15810
|
+
const w = new Uint32Array(64);
|
|
15811
|
+
for (let i = 0; i < 16; i++) w[i] = new DataView(bytes.buffer).getUint32(o + i * 4, false);
|
|
15812
|
+
for (let i = 16; i < 64; i++) {
|
|
15813
|
+
const x = w[i - 15], y = w[i - 2];
|
|
15814
|
+
w[i] = w[i - 16] + (rotr2(x, 7) ^ rotr2(x, 18) ^ x >>> 3) + w[i - 7] + (rotr2(y, 17) ^ rotr2(y, 19) ^ y >>> 10) >>> 0;
|
|
15815
|
+
}
|
|
15816
|
+
let [a, b, c, d, e, f, g, hh] = h;
|
|
15817
|
+
for (let i = 0; i < 64; i++) {
|
|
15818
|
+
const s1 = rotr2(e, 6) ^ rotr2(e, 11) ^ rotr2(e, 25), ch = e & f ^ ~e & g, t1 = hh + s1 + ch + K[i] + w[i] >>> 0;
|
|
15819
|
+
const s0 = rotr2(a, 2) ^ rotr2(a, 13) ^ rotr2(a, 22), maj = a & b ^ a & c ^ b & c, t2 = s0 + maj >>> 0;
|
|
15820
|
+
[hh, g, f, e, d, c, b, a] = [g, f, e, d + t1 >>> 0, c, b, a, t1 + t2 >>> 0];
|
|
15821
|
+
}
|
|
15822
|
+
h = h.map((v, i) => v + [a, b, c, d, e, f, g, hh][i] >>> 0);
|
|
15823
|
+
}
|
|
15824
|
+
return h.map((v) => v.toString(16).padStart(8, "0")).join("");
|
|
15825
|
+
}
|
|
15826
|
+
var OwnershipStatement = class _OwnershipStatement {
|
|
15827
|
+
constructor(proofId, subjectAid, ownerAid) {
|
|
15828
|
+
__publicField(this, "proofId");
|
|
15829
|
+
__publicField(this, "subjectAid");
|
|
15830
|
+
__publicField(this, "ownerAid");
|
|
15831
|
+
__publicField(this, "canonicalFact");
|
|
15832
|
+
__publicField(this, "canonicalStatement");
|
|
15833
|
+
__publicField(this, "factHash");
|
|
15834
|
+
__publicField(this, "statementHash");
|
|
15835
|
+
this.proofId = proofId;
|
|
15836
|
+
this.subjectAid = subjectAid;
|
|
15837
|
+
this.ownerAid = ownerAid;
|
|
15838
|
+
this.canonicalFact = canonicalJson({ subject_aid: subjectAid, owner_aid: ownerAid });
|
|
15839
|
+
this.canonicalStatement = canonicalJson({ proof_id: proofId, subject_aid: subjectAid, owner_aid: ownerAid });
|
|
15840
|
+
const signed = new Uint8Array(new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN).length + this.canonicalStatement.length);
|
|
15841
|
+
signed.set(new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN));
|
|
15842
|
+
signed.set(this.canonicalStatement, new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN).length);
|
|
15843
|
+
this.factHash = sha256Hex(this.canonicalFact);
|
|
15844
|
+
this.statementHash = sha256Hex(signed);
|
|
15845
|
+
}
|
|
15846
|
+
static create(proofId, subjectAid, ownerAid) {
|
|
15847
|
+
const id = String(proofId ?? "").trim();
|
|
15848
|
+
if (!PROOF_ID_RE.test(id)) throw new ValidationError("invalid proof_id");
|
|
15849
|
+
let s, o;
|
|
15850
|
+
try {
|
|
15851
|
+
s = validateAIDFormat(subjectAid, "subject_aid");
|
|
15852
|
+
o = validateAIDFormat(ownerAid, "owner_aid");
|
|
15853
|
+
} catch (e) {
|
|
15854
|
+
throw new ValidationError(String(e instanceof Error ? e.message : e));
|
|
15855
|
+
}
|
|
15856
|
+
return new _OwnershipStatement(id, s, o);
|
|
15857
|
+
}
|
|
15858
|
+
get signingBytes() {
|
|
15859
|
+
const p = new TextEncoder().encode(OWNERSHIP_RELATION_DOMAIN), r = new Uint8Array(p.length + this.canonicalStatement.length);
|
|
15860
|
+
r.set(p);
|
|
15861
|
+
r.set(this.canonicalStatement, p.length);
|
|
15862
|
+
return r;
|
|
15863
|
+
}
|
|
15864
|
+
};
|
|
15865
|
+
function validateOwnershipFrontmatter(frontmatter2, expectedAid) {
|
|
15866
|
+
const op = Object.prototype.hasOwnProperty.call(frontmatter2, "owner"), pp = Object.prototype.hasOwnProperty.call(frontmatter2, "ownership_proof");
|
|
15867
|
+
if (op !== pp) throw new ValidationError("owner and ownership_proof must appear together");
|
|
15868
|
+
const type = String(frontmatter2.type ?? "").trim().toLowerCase();
|
|
15869
|
+
if (type === "human" && (op || pp)) throw new ValidationError("human AID cannot specify owner");
|
|
15870
|
+
if (!op) return;
|
|
15871
|
+
const aid = validateAIDFormat(frontmatter2.aid, "aid"), owner = validateAIDFormat(frontmatter2.owner, "owner");
|
|
15872
|
+
if (String(frontmatter2.aid).trim() !== aid || String(frontmatter2.owner).trim() !== owner) throw new ValidationError("owner and subject AID must be lowercase");
|
|
15873
|
+
if (expectedAid && aid !== expectedAid.trim().toLowerCase()) throw new ValidationError("ownership subject AID does not match expected AID");
|
|
15874
|
+
if (!frontmatter2.ownership_proof || typeof frontmatter2.ownership_proof !== "object" || Array.isArray(frontmatter2.ownership_proof)) throw new ValidationError("ownership_proof must be an object");
|
|
15875
|
+
const raw = frontmatter2.ownership_proof, allowed = /* @__PURE__ */ new Set(["proof_id", "subject_aid", "owner_aid", "fact_hash", "statement_hash", "subject_signature", "owner_signature"]);
|
|
15876
|
+
if (Object.keys(raw).some((k) => !allowed.has(k))) throw new ValidationError("ownership_proof contains unknown fields");
|
|
15877
|
+
const statement = OwnershipStatement.create(String(raw.proof_id ?? ""), String(raw.subject_aid ?? ""), String(raw.owner_aid ?? ""));
|
|
15878
|
+
if (statement.subjectAid !== aid || statement.ownerAid !== owner) throw new ValidationError("ownership_proof does not match agent.md owner");
|
|
15879
|
+
if (raw.fact_hash !== void 0 && String(raw.fact_hash).toLowerCase() !== statement.factHash) throw new ValidationError("fact_hash mismatch");
|
|
15880
|
+
if (raw.statement_hash !== void 0 && String(raw.statement_hash).toLowerCase() !== statement.statementHash) throw new ValidationError("statement_hash mismatch");
|
|
15881
|
+
if (!validSignature(raw.subject_signature)) throw new ValidationError("subject_signature is invalid");
|
|
15882
|
+
if (aid === owner) {
|
|
15883
|
+
if (raw.owner_signature !== void 0) throw new ValidationError("self-owner proof must not contain owner_signature");
|
|
15884
|
+
} else if (!raw.owner_signature || !validSignature(raw.owner_signature)) throw new ValidationError("owner_signature is required");
|
|
15885
|
+
}
|
|
15886
|
+
function validateOwnershipProof(proof, expectedSubject, expectedOwner) {
|
|
15887
|
+
let s, o;
|
|
15888
|
+
try {
|
|
15889
|
+
s = validateAIDFormat(expectedSubject, "subject_aid");
|
|
15890
|
+
o = validateAIDFormat(expectedOwner, "owner_aid");
|
|
15891
|
+
} catch (e) {
|
|
15892
|
+
throw new ValidationError(String(e instanceof Error ? e.message : e));
|
|
15893
|
+
}
|
|
15894
|
+
if (proof.statement.subjectAid !== s || proof.statement.ownerAid !== o) throw new ValidationError("ownership proof statement does not match owner or subject");
|
|
15895
|
+
if (proof.fact_hash && proof.fact_hash.toLowerCase() !== proof.statement.factHash) throw new ValidationError("fact_hash mismatch");
|
|
15896
|
+
if (proof.statement_hash && proof.statement_hash.toLowerCase() !== proof.statement.statementHash) throw new ValidationError("statement_hash mismatch");
|
|
15897
|
+
if (!validSignature(proof.subject_signature)) throw new ValidationError("subject_signature is invalid");
|
|
15898
|
+
if (s === o) {
|
|
15899
|
+
if (proof.owner_signature) throw new ValidationError("self-owner proof must not contain owner_signature");
|
|
15900
|
+
} else if (!proof.owner_signature || !validSignature(proof.owner_signature)) throw new ValidationError("owner_signature is required");
|
|
15901
|
+
}
|
|
15902
|
+
function validSignature(s) {
|
|
15903
|
+
return !!s && FINGERPRINT_RE.test(String(s?.cert_fingerprint ?? "").trim()) && isStrictBase64(String(s?.signature ?? ""));
|
|
15904
|
+
}
|
|
15905
|
+
async function signOwnershipStatement(aid, statement) {
|
|
15906
|
+
const sig = await aid.sign(statement.signingBytes);
|
|
15907
|
+
if (!sig.ok || !sig.data) throw new ValidationError(sig.error?.message ?? "ownership signature failed");
|
|
15908
|
+
if (!FINGERPRINT_RE.test(aid.certFingerprint)) throw new ValidationError("certificate fingerprint is unavailable");
|
|
15909
|
+
return { cert_fingerprint: aid.certFingerprint, signature: sig.data.signature };
|
|
15910
|
+
}
|
|
15911
|
+
async function verifyOwnershipSignature(certPem, statement, signature) {
|
|
15912
|
+
if (!validSignature(signature)) return false;
|
|
15913
|
+
if ((await certificateSha256Fingerprint(certPem)).toLowerCase() !== signature.cert_fingerprint.toLowerCase()) return false;
|
|
15914
|
+
return verifySignatureWithCert(certPem, signature.signature, new TextEncoder().encode(new TextDecoder().decode(statement.signingBytes)));
|
|
15915
|
+
}
|
|
15916
|
+
async function verifyOwnershipProof(proof, subjectCertPem, ownerCertPem) {
|
|
15917
|
+
validateOwnershipProof(proof, proof.statement.subjectAid, proof.statement.ownerAid);
|
|
15918
|
+
if (!await verifyOwnershipSignature(subjectCertPem, proof.statement, proof.subject_signature)) return false;
|
|
15919
|
+
return proof.statement.subjectAid === proof.statement.ownerAid || !!ownerCertPem && !!proof.owner_signature && await verifyOwnershipSignature(ownerCertPem, proof.statement, proof.owner_signature);
|
|
15920
|
+
}
|
|
15921
|
+
|
|
15651
15922
|
// src/agent-md-schema.ts
|
|
15652
15923
|
var AID_RE = /^[A-Za-z0-9_][A-Za-z0-9_-]*(?:\.[A-Za-z0-9_][A-Za-z0-9_-]*)+$/;
|
|
15653
15924
|
var SEMVER_RE = /^\d+\.\d+\.\d+$/;
|
|
@@ -15693,6 +15964,7 @@ function validateAgentMdDocument(content, opts = {}) {
|
|
|
15693
15964
|
if (Array.from(meta.avatar).length > 256) throw new ValidationError("agent.md avatar exceeds 256 characters");
|
|
15694
15965
|
}
|
|
15695
15966
|
if (meta.tags !== void 0 && (!Array.isArray(meta.tags) || meta.tags.some((item) => typeof item !== "string"))) throw new ValidationError("agent.md tags must be an array of strings");
|
|
15967
|
+
validateOwnershipFrontmatter(meta, opts.expectedAid);
|
|
15696
15968
|
const fields = parsed.fields;
|
|
15697
15969
|
if (fields) {
|
|
15698
15970
|
if (Object.keys(fields).some((key) => !["cert_fingerprint", "public_key_fingerprint", "timestamp", "signature"].includes(key))) throw new ValidationError("agent.md signature contains unknown fields");
|
|
@@ -15789,6 +16061,8 @@ var AID = class _AID {
|
|
|
15789
16061
|
return this._privateKeyValid;
|
|
15790
16062
|
}
|
|
15791
16063
|
async sign(payload) {
|
|
16064
|
+
const now = Date.now();
|
|
16065
|
+
if (!this._certValid || now < this.certNotBefore.getTime() || now > this.certNotAfter.getTime()) return resultErr(CERT_NOT_VALID, "certificate is not valid");
|
|
15792
16066
|
if (!this._privateKeyValid || !this.privateKeyPem) return resultErr(PRIVATE_KEY_NOT_VALID, "private key is not valid");
|
|
15793
16067
|
try {
|
|
15794
16068
|
const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
|
|
@@ -15797,7 +16071,8 @@ var AID = class _AID {
|
|
|
15797
16071
|
return resultErr(SIGNATURE_OPERATION_ERROR, String(exc), exc);
|
|
15798
16072
|
}
|
|
15799
16073
|
}
|
|
15800
|
-
async verify(payload, signature) {
|
|
16074
|
+
async verify(payload, signature, context) {
|
|
16075
|
+
if (context) return this.verifyWithContext(payload, signature, context);
|
|
15801
16076
|
if (!this._certValid) return resultErr(CERT_NOT_VALID, "certificate is not valid");
|
|
15802
16077
|
try {
|
|
15803
16078
|
const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
|
|
@@ -15807,6 +16082,8 @@ var AID = class _AID {
|
|
|
15807
16082
|
}
|
|
15808
16083
|
}
|
|
15809
16084
|
async signAgentMd(content) {
|
|
16085
|
+
const now = Date.now();
|
|
16086
|
+
if (!this._certValid || now < this.certNotBefore.getTime() || now > this.certNotAfter.getTime()) return resultErr(CERT_NOT_VALID, "certificate is not valid");
|
|
15810
16087
|
if (!this._privateKeyValid || !this.privateKeyPem) return resultErr(PRIVATE_KEY_NOT_VALID, "private key is not valid");
|
|
15811
16088
|
try {
|
|
15812
16089
|
const parsed = parseAgentMdTailSignature(String(content ?? ""));
|
|
@@ -15828,6 +16105,40 @@ var AID = class _AID {
|
|
|
15828
16105
|
return resultErr(SIGNATURE_OPERATION_ERROR, String(exc), exc);
|
|
15829
16106
|
}
|
|
15830
16107
|
}
|
|
16108
|
+
async verifyWithContext(payload, signature, context) {
|
|
16109
|
+
try {
|
|
16110
|
+
const expectedAid = String(context.expectedAid ?? context.expected_aid ?? context.aid ?? "").trim();
|
|
16111
|
+
if (!expectedAid) return resultErr(VERIFICATION_OPERATION_ERROR, "expected AID is required");
|
|
16112
|
+
if (expectedAid.toLowerCase() !== this.aid.trim().toLowerCase() || expectedAid.toLowerCase() !== this.certSubject.trim().toLowerCase()) {
|
|
16113
|
+
return resultErr(VERIFICATION_OPERATION_ERROR, "certificate subject does not match AID");
|
|
16114
|
+
}
|
|
16115
|
+
const expectedFingerprint = String(context.certFingerprint ?? context.cert_fingerprint ?? "").trim().toLowerCase().replace(/^sha256:/, "").replace(/:/g, "");
|
|
16116
|
+
const actualFingerprint = this.certFingerprint.trim().toLowerCase().replace(/^sha256:/, "").replace(/:/g, "");
|
|
16117
|
+
if (!/^[0-9a-f]{64}$/.test(expectedFingerprint) || expectedFingerprint !== actualFingerprint) {
|
|
16118
|
+
return resultErr(VERIFICATION_OPERATION_ERROR, "certificate fingerprint mismatch");
|
|
16119
|
+
}
|
|
16120
|
+
const timestampValue = context.signatureTimestamp ?? context.signature_timestamp ?? context.timestamp;
|
|
16121
|
+
if (typeof timestampValue !== "number" || !Number.isFinite(timestampValue) || !Number.isInteger(timestampValue)) {
|
|
16122
|
+
return resultErr(VERIFICATION_OPERATION_ERROR, "signature timestamp is invalid");
|
|
16123
|
+
}
|
|
16124
|
+
const notBefore = this.certNotBefore.getTime() / 1e3;
|
|
16125
|
+
const notAfter = this.certNotAfter.getTime() / 1e3;
|
|
16126
|
+
if (timestampValue < notBefore || timestampValue > notAfter) {
|
|
16127
|
+
return resultErr(VERIFICATION_OPERATION_ERROR, "signature timestamp is outside certificate validity");
|
|
16128
|
+
}
|
|
16129
|
+
const nowValue = context.now instanceof Date ? context.now.getTime() / 1e3 : context.now;
|
|
16130
|
+
const now = nowValue === void 0 ? Date.now() / 1e3 : Number(nowValue);
|
|
16131
|
+
if (!Number.isFinite(now)) return resultErr(VERIFICATION_OPERATION_ERROR, "verification time is invalid");
|
|
16132
|
+
if ((context.requireCurrentValidity ?? context.require_current_validity ?? false) && (now < notBefore || now > notAfter)) {
|
|
16133
|
+
return resultErr(CERT_NOT_VALID, "certificate is expired or not yet valid");
|
|
16134
|
+
}
|
|
16135
|
+
if (!isStrictBase64(signature)) return resultErr(VERIFICATION_OPERATION_ERROR, "invalid base64 signature");
|
|
16136
|
+
const data = typeof payload === "string" ? new TextEncoder().encode(payload) : payload;
|
|
16137
|
+
return resultOk({ valid: await verifySignatureWithCert(this.certPem, signature, data) });
|
|
16138
|
+
} catch (exc) {
|
|
16139
|
+
return resultErr(VERIFICATION_OPERATION_ERROR, String(exc), exc);
|
|
16140
|
+
}
|
|
16141
|
+
}
|
|
15831
16142
|
async verifyAgentMd(content) {
|
|
15832
16143
|
try {
|
|
15833
16144
|
const rawContent = String(content ?? "");
|
|
@@ -16577,6 +16888,13 @@ var RpcPipeline = class {
|
|
|
16577
16888
|
if (!currentAid?.privateKeyPem) return;
|
|
16578
16889
|
try {
|
|
16579
16890
|
const aid = currentAid.aid;
|
|
16891
|
+
const certPem = currentAid.certPem ?? "";
|
|
16892
|
+
if (!certPem) throw new Error("\u7B7E\u540D\u8BC1\u4E66\u4E0D\u53EF\u7528");
|
|
16893
|
+
const certMeta = parseCertMetadata(certPem);
|
|
16894
|
+
const now = Date.now();
|
|
16895
|
+
if (now < certMeta.notBefore.getTime() || now > certMeta.notAfter.getTime()) {
|
|
16896
|
+
throw new Error("\u7B7E\u540D\u8BC1\u4E66\u5DF2\u8FC7\u671F\u6216\u5C1A\u672A\u751F\u6548");
|
|
16897
|
+
}
|
|
16580
16898
|
const ts = String(Math.floor(Date.now() / 1e3));
|
|
16581
16899
|
const paramsForHash = {};
|
|
16582
16900
|
for (const [k, v] of Object.entries(params2)) {
|
|
@@ -16599,7 +16917,6 @@ var RpcPipeline = class {
|
|
|
16599
16917
|
);
|
|
16600
16918
|
const sigDer = p1363ToDer(new Uint8Array(sigP1363));
|
|
16601
16919
|
let certFingerprint = "";
|
|
16602
|
-
const certPem = currentAid.certPem;
|
|
16603
16920
|
if (certPem) {
|
|
16604
16921
|
certFingerprint = await signingCertFingerprint(certPem);
|
|
16605
16922
|
}
|
|
@@ -17951,7 +18268,7 @@ function unmountFromAny(input, fallback) {
|
|
|
17951
18268
|
}
|
|
17952
18269
|
|
|
17953
18270
|
// src/storage/vfs.ts
|
|
17954
|
-
async function
|
|
18271
|
+
async function sha256Hex2(data) {
|
|
17955
18272
|
const copy = new Uint8Array(data.byteLength);
|
|
17956
18273
|
copy.set(data);
|
|
17957
18274
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -18072,7 +18389,7 @@ var StorageVFS = class {
|
|
|
18072
18389
|
const owner = this.owner(options.owner);
|
|
18073
18390
|
const bucket = options.bucket ?? "default";
|
|
18074
18391
|
const objectKey = pathToKey(path);
|
|
18075
|
-
const sha2566 = await
|
|
18392
|
+
const sha2566 = await sha256Hex2(data);
|
|
18076
18393
|
const overwrite = options.overwrite ?? false;
|
|
18077
18394
|
try {
|
|
18078
18395
|
const check = await this.lowlevel.checkUpload({ owner, bucket, objectKey, size: data.length, sha256: sha2566 });
|
|
@@ -18178,7 +18495,7 @@ var StorageVFS = class {
|
|
|
18178
18495
|
}
|
|
18179
18496
|
async downloadFile(path, options = {}) {
|
|
18180
18497
|
const { data, sha256: expectedSha } = await this.readBytesWithMetadata(path, options);
|
|
18181
|
-
const actualSha = await
|
|
18498
|
+
const actualSha = await sha256Hex2(data);
|
|
18182
18499
|
const expected = expectedSha.toLowerCase();
|
|
18183
18500
|
const verified = Boolean(expected) && actualSha.toLowerCase() === expected;
|
|
18184
18501
|
if (!verified && expected && (options.verifyHash ?? true)) {
|
|
@@ -18531,7 +18848,7 @@ function bearerHeaders(client) {
|
|
|
18531
18848
|
function toExactArrayBuffer(bytes) {
|
|
18532
18849
|
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
18533
18850
|
}
|
|
18534
|
-
async function
|
|
18851
|
+
async function sha256Hex3(data) {
|
|
18535
18852
|
const subtle = globalThis.crypto?.subtle;
|
|
18536
18853
|
if (!subtle?.digest) throw new StorageError("SHA-256 unavailable", "EUNSUPPORTED");
|
|
18537
18854
|
const digest = await subtle.digest("SHA-256", toExactArrayBuffer(data));
|
|
@@ -18786,7 +19103,7 @@ var GroupFSVFS = class {
|
|
|
18786
19103
|
}
|
|
18787
19104
|
async uploadToGroup(src, dst, options) {
|
|
18788
19105
|
const { data, contentType } = await this.sourceBytes(src, options);
|
|
18789
|
-
const digest = await
|
|
19106
|
+
const digest = await sha256Hex3(data);
|
|
18790
19107
|
const groupAid = options.group_aid ?? options.groupAid ?? options.dst_group_aid ?? options.dstGroupAid;
|
|
18791
19108
|
const groupId = options.group_id ?? options.groupId ?? options.dst_group_id ?? options.dstGroupId;
|
|
18792
19109
|
const baseParams = stripNil({
|
|
@@ -18910,7 +19227,7 @@ var GroupFSVFS = class {
|
|
|
18910
19227
|
const data = await this.lowlevel.httpGet(downloadUrl, bearerHeaders(this.client));
|
|
18911
19228
|
const expectedSha = text2(rawTicket.sha256);
|
|
18912
19229
|
const verifyHash = options.verify_hash ?? options.verifyHash ?? true;
|
|
18913
|
-
const verified = !verifyHash || !expectedSha || await
|
|
19230
|
+
const verified = !verifyHash || !expectedSha || await sha256Hex3(data) === expectedSha.toLowerCase();
|
|
18914
19231
|
if (verifyHash && !verified) {
|
|
18915
19232
|
throw new StorageConflictError("download hash verification failed", "ECONFLICT", src, ticket);
|
|
18916
19233
|
}
|
|
@@ -18941,114 +19258,6 @@ var GroupFSVFS = class {
|
|
|
18941
19258
|
}
|
|
18942
19259
|
};
|
|
18943
19260
|
|
|
18944
|
-
// src/v2/crypto/canonical.ts
|
|
18945
|
-
var encoder = new TextEncoder();
|
|
18946
|
-
var MAX_SAFE_JSON_INTEGER = 9007199254740991;
|
|
18947
|
-
function canonicalJson(obj) {
|
|
18948
|
-
return encoder.encode(serialize(obj));
|
|
18949
|
-
}
|
|
18950
|
-
function serialize(obj) {
|
|
18951
|
-
if (obj === null) return "null";
|
|
18952
|
-
if (obj === true) return "true";
|
|
18953
|
-
if (obj === false) return "false";
|
|
18954
|
-
if (typeof obj === "number") {
|
|
18955
|
-
return serializeNumber(obj);
|
|
18956
|
-
}
|
|
18957
|
-
if (typeof obj === "string") {
|
|
18958
|
-
return serializeString(obj);
|
|
18959
|
-
}
|
|
18960
|
-
if (Array.isArray(obj)) {
|
|
18961
|
-
return serializeArray(obj);
|
|
18962
|
-
}
|
|
18963
|
-
if (typeof obj === "object") {
|
|
18964
|
-
return serializeObject(obj);
|
|
18965
|
-
}
|
|
18966
|
-
throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
|
|
18967
|
-
}
|
|
18968
|
-
function serializeNumber(n) {
|
|
18969
|
-
if (!isFinite(n)) {
|
|
18970
|
-
throw new RangeError("canonicalJson: Infinity and NaN not allowed");
|
|
18971
|
-
}
|
|
18972
|
-
if (Object.is(n, -0)) return "0";
|
|
18973
|
-
if (Number.isInteger(n)) {
|
|
18974
|
-
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
18975
|
-
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
18976
|
-
}
|
|
18977
|
-
return String(n);
|
|
18978
|
-
}
|
|
18979
|
-
return expandExponent(String(n));
|
|
18980
|
-
}
|
|
18981
|
-
function expandExponent(s) {
|
|
18982
|
-
if (!/[eE]/.test(s)) return s;
|
|
18983
|
-
const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
18984
|
-
if (!match) {
|
|
18985
|
-
throw new TypeError(`canonicalJson: invalid number ${s}`);
|
|
18986
|
-
}
|
|
18987
|
-
const sign = match[1] ?? "";
|
|
18988
|
-
const intPart = match[2] ?? "";
|
|
18989
|
-
const fracPart = match[3] ?? "";
|
|
18990
|
-
const exp = Number(match[4]);
|
|
18991
|
-
const digits = intPart + fracPart;
|
|
18992
|
-
const point = intPart.length + exp;
|
|
18993
|
-
if (point <= 0) {
|
|
18994
|
-
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
18995
|
-
}
|
|
18996
|
-
if (point >= digits.length) {
|
|
18997
|
-
return `${sign}${digits}${"0".repeat(point - digits.length)}`;
|
|
18998
|
-
}
|
|
18999
|
-
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
19000
|
-
}
|
|
19001
|
-
function serializeString(s) {
|
|
19002
|
-
let result = '"';
|
|
19003
|
-
for (let i = 0; i < s.length; i++) {
|
|
19004
|
-
const ch = s[i];
|
|
19005
|
-
const code = s.charCodeAt(i);
|
|
19006
|
-
if (ch === '"') {
|
|
19007
|
-
result += '\\"';
|
|
19008
|
-
} else if (ch === "\\") {
|
|
19009
|
-
result += "\\\\";
|
|
19010
|
-
} else if (ch === "\b") {
|
|
19011
|
-
result += "\\b";
|
|
19012
|
-
} else if (ch === "\f") {
|
|
19013
|
-
result += "\\f";
|
|
19014
|
-
} else if (ch === "\n") {
|
|
19015
|
-
result += "\\n";
|
|
19016
|
-
} else if (ch === "\r") {
|
|
19017
|
-
result += "\\r";
|
|
19018
|
-
} else if (ch === " ") {
|
|
19019
|
-
result += "\\t";
|
|
19020
|
-
} else if (code < 32) {
|
|
19021
|
-
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
19022
|
-
} else {
|
|
19023
|
-
result += ch;
|
|
19024
|
-
}
|
|
19025
|
-
}
|
|
19026
|
-
result += '"';
|
|
19027
|
-
return result;
|
|
19028
|
-
}
|
|
19029
|
-
function serializeArray(arr) {
|
|
19030
|
-
const items = arr.map((item) => serialize(item));
|
|
19031
|
-
return "[" + items.join(",") + "]";
|
|
19032
|
-
}
|
|
19033
|
-
function serializeObject(obj) {
|
|
19034
|
-
const sortedKeys = Object.keys(obj).sort(compareCodePoints);
|
|
19035
|
-
const pairs2 = sortedKeys.map(
|
|
19036
|
-
(key) => serializeString(key) + ":" + serialize(obj[key])
|
|
19037
|
-
);
|
|
19038
|
-
return "{" + pairs2.join(",") + "}";
|
|
19039
|
-
}
|
|
19040
|
-
function compareCodePoints(a, b) {
|
|
19041
|
-
const ac = Array.from(a);
|
|
19042
|
-
const bc = Array.from(b);
|
|
19043
|
-
const n = Math.min(ac.length, bc.length);
|
|
19044
|
-
for (let i = 0; i < n; i++) {
|
|
19045
|
-
const av = ac[i].codePointAt(0) ?? 0;
|
|
19046
|
-
const bv = bc[i].codePointAt(0) ?? 0;
|
|
19047
|
-
if (av !== bv) return av - bv;
|
|
19048
|
-
}
|
|
19049
|
-
return ac.length - bc.length;
|
|
19050
|
-
}
|
|
19051
|
-
|
|
19052
19261
|
// src/group-index.ts
|
|
19053
19262
|
var GROUP_INDEX_SCHEMA = "aun.group.index.v1";
|
|
19054
19263
|
var GROUP_INDEX_KEY = "group.index";
|
|
@@ -19156,13 +19365,13 @@ var GroupIndexMetaCache = class {
|
|
|
19156
19365
|
}
|
|
19157
19366
|
};
|
|
19158
19367
|
async function computeGroupIndexBodyHash(entries) {
|
|
19159
|
-
return `sha256:${await
|
|
19368
|
+
return `sha256:${await sha256Hex4(entriesBytes(entries))}`;
|
|
19160
19369
|
}
|
|
19161
19370
|
async function groupIndexEtag(entries) {
|
|
19162
|
-
return `"sha256:${await
|
|
19371
|
+
return `"sha256:${await sha256Hex4(entriesBytes(entries))}"`;
|
|
19163
19372
|
}
|
|
19164
19373
|
async function groupIndexSettingEtag(value) {
|
|
19165
|
-
return `"sha256:${await
|
|
19374
|
+
return `"sha256:${await sha256Hex4(encode(canonicalStringify(value)))}"`;
|
|
19166
19375
|
}
|
|
19167
19376
|
function groupIndexSigningPayload(meta, entries) {
|
|
19168
19377
|
const metaWithoutSignature = { ...meta };
|
|
@@ -19266,7 +19475,7 @@ function canonicalStringify(value) {
|
|
|
19266
19475
|
function encode(text3) {
|
|
19267
19476
|
return new TextEncoder().encode(text3);
|
|
19268
19477
|
}
|
|
19269
|
-
async function
|
|
19478
|
+
async function sha256Hex4(data) {
|
|
19270
19479
|
const digest = await globalThis.crypto.subtle.digest("SHA-256", exactArrayBuffer(data));
|
|
19271
19480
|
return bytesToHex(new Uint8Array(digest));
|
|
19272
19481
|
}
|
|
@@ -19366,6 +19575,9 @@ var MessageFacade = class extends RpcFacade {
|
|
|
19366
19575
|
send(params2) {
|
|
19367
19576
|
return this.call("message.send", params2);
|
|
19368
19577
|
}
|
|
19578
|
+
getAuditStatus(params2) {
|
|
19579
|
+
return this.call("message.get_audit_status", params2);
|
|
19580
|
+
}
|
|
19369
19581
|
pull(params2) {
|
|
19370
19582
|
return this.call("message.pull", params2);
|
|
19371
19583
|
}
|
|
@@ -19811,6 +20023,9 @@ var GroupFacade = class extends RpcFacade {
|
|
|
19811
20023
|
validateGroupIDFormat(params2?.group_id, "group_id");
|
|
19812
20024
|
return this.call("group.send", params2);
|
|
19813
20025
|
}
|
|
20026
|
+
getAuditStatus(params2) {
|
|
20027
|
+
return this.call("group.get_audit_status", params2);
|
|
20028
|
+
}
|
|
19814
20029
|
recall(params2) {
|
|
19815
20030
|
return this.call("group.recall", params2);
|
|
19816
20031
|
}
|
|
@@ -23900,7 +24115,7 @@ var PEER_KEY_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
|
23900
24115
|
var DESTROY_DELAY_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
23901
24116
|
var RECENT_GENERATIONS = 7;
|
|
23902
24117
|
var HARD_LIMIT_MS = 180 * 24 * 60 * 60 * 1e3;
|
|
23903
|
-
async function
|
|
24118
|
+
async function sha256Hex5(data) {
|
|
23904
24119
|
const buf = await crypto.subtle.digest("SHA-256", data.slice().buffer);
|
|
23905
24120
|
const arr = new Uint8Array(buf);
|
|
23906
24121
|
let hex = "";
|
|
@@ -23991,7 +24206,7 @@ var V2Session = class {
|
|
|
23991
24206
|
}
|
|
23992
24207
|
async _generateNewSPK() {
|
|
23993
24208
|
const [priv, pubDer] = await generateP256Keypair();
|
|
23994
|
-
const hex = await
|
|
24209
|
+
const hex = await sha256Hex5(pubDer);
|
|
23995
24210
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
23996
24211
|
await this._store.saveSPK(this._storeDeviceId, spkId, priv, pubDer);
|
|
23997
24212
|
this._spkId = spkId;
|
|
@@ -23999,7 +24214,7 @@ var V2Session = class {
|
|
|
23999
24214
|
this._spkPubDer = pubDer;
|
|
24000
24215
|
}
|
|
24001
24216
|
async _ikSPKId() {
|
|
24002
|
-
const hex = await
|
|
24217
|
+
const hex = await sha256Hex5(this._ikPubDer);
|
|
24003
24218
|
return `sha256:${hex.substring(0, 16)}`;
|
|
24004
24219
|
}
|
|
24005
24220
|
_groupKey(groupId) {
|
|
@@ -24229,7 +24444,7 @@ var V2Session = class {
|
|
|
24229
24444
|
}
|
|
24230
24445
|
}
|
|
24231
24446
|
const [priv, pubDer] = await generateP256Keypair();
|
|
24232
|
-
const hex = await
|
|
24447
|
+
const hex = await sha256Hex5(pubDer);
|
|
24233
24448
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
24234
24449
|
await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
|
|
24235
24450
|
return { spkId, priv, pubDer };
|
|
@@ -24254,7 +24469,7 @@ var V2Session = class {
|
|
|
24254
24469
|
await this.ensureKeys();
|
|
24255
24470
|
const gk = this._groupKey(groupId);
|
|
24256
24471
|
const [priv, pubDer] = await generateP256Keypair();
|
|
24257
|
-
const hex = await
|
|
24472
|
+
const hex = await sha256Hex5(pubDer);
|
|
24258
24473
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
24259
24474
|
await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
|
|
24260
24475
|
await this._publishGroupSPK(gk, spkId, pubDer, callFn);
|
|
@@ -24329,6 +24544,46 @@ var V2_GROUP_STALE_BOOTSTRAP_CODE = -33054;
|
|
|
24329
24544
|
var V2_PULL_DEFAULT_PAGE_LIMIT = 50;
|
|
24330
24545
|
var V2_PULL_MAX_PAGE_LIMIT = 50;
|
|
24331
24546
|
var V2_SENDER_IK_SYNC_FETCH_TIMEOUT_MS = 3e3;
|
|
24547
|
+
var V2_PULL_PIPELINE_QUEUE_CAPACITY = 100;
|
|
24548
|
+
var V2PullPageQueue = class {
|
|
24549
|
+
constructor(capacity) {
|
|
24550
|
+
this.capacity = capacity;
|
|
24551
|
+
__publicField(this, "items", []);
|
|
24552
|
+
__publicField(this, "takers", []);
|
|
24553
|
+
__publicField(this, "putters", []);
|
|
24554
|
+
__publicField(this, "queuedWeight", 0);
|
|
24555
|
+
__publicField(this, "closed", false);
|
|
24556
|
+
}
|
|
24557
|
+
async put(item, weight = 1) {
|
|
24558
|
+
const itemWeight = Math.max(1, Math.trunc(weight));
|
|
24559
|
+
while (!this.closed && this.items.length > 0 && this.queuedWeight + itemWeight > this.capacity) {
|
|
24560
|
+
await new Promise((resolve) => this.putters.push(resolve));
|
|
24561
|
+
}
|
|
24562
|
+
if (this.closed) return false;
|
|
24563
|
+
const taker = this.takers.shift();
|
|
24564
|
+
if (taker) taker(item);
|
|
24565
|
+
else {
|
|
24566
|
+
this.items.push(item);
|
|
24567
|
+
this.queuedWeight += itemWeight;
|
|
24568
|
+
}
|
|
24569
|
+
return true;
|
|
24570
|
+
}
|
|
24571
|
+
async take() {
|
|
24572
|
+
const item = this.items.shift();
|
|
24573
|
+
if (item !== void 0) {
|
|
24574
|
+
this.queuedWeight = Math.max(0, this.queuedWeight - Math.max(1, Math.trunc(Number(item.weight ?? 1))));
|
|
24575
|
+
this.putters.shift()?.();
|
|
24576
|
+
return item;
|
|
24577
|
+
}
|
|
24578
|
+
if (this.closed) return void 0;
|
|
24579
|
+
return await new Promise((resolve) => this.takers.push(resolve));
|
|
24580
|
+
}
|
|
24581
|
+
close() {
|
|
24582
|
+
this.closed = true;
|
|
24583
|
+
for (const resolve of this.takers.splice(0)) resolve(void 0);
|
|
24584
|
+
for (const resolve of this.putters.splice(0)) resolve();
|
|
24585
|
+
}
|
|
24586
|
+
};
|
|
24332
24587
|
var V2_SESSION_INIT_RETRY_LIMIT = 12;
|
|
24333
24588
|
var V2_SESSION_INIT_RETRY_BASE_MS = 1e3;
|
|
24334
24589
|
var V2_SESSION_INIT_RETRY_MAX_MS = 5e3;
|
|
@@ -25301,6 +25556,9 @@ var V2E2EECoordinator = class {
|
|
|
25301
25556
|
payload,
|
|
25302
25557
|
encrypted: false
|
|
25303
25558
|
};
|
|
25559
|
+
if (msg.sender_device_id !== void 0) plaintext.sender_device_id = msg.sender_device_id;
|
|
25560
|
+
const direction = String(msg.direction ?? "").trim();
|
|
25561
|
+
if (direction) plaintext.direction = direction;
|
|
25304
25562
|
attachGatewayProximity(plaintext, msg);
|
|
25305
25563
|
} else if (isJsonObject(payload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
25306
25564
|
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
@@ -25314,6 +25572,9 @@ var V2E2EECoordinator = class {
|
|
|
25314
25572
|
client._clientLog.warn(`P2P Tail \u5355\u6761\u6D88\u606F\u89E3\u5BC6\u5931\u8D25\uFF0C\u8FDE\u7EED\u6027\u8BC1\u660E\u4ECD\u4FDD\u7559: seq=${seq2}`);
|
|
25315
25573
|
return null;
|
|
25316
25574
|
}
|
|
25575
|
+
if (publish && client._delivery.isSelfSentP2PMessage?.(plaintext)) {
|
|
25576
|
+
return plaintext;
|
|
25577
|
+
}
|
|
25317
25578
|
if (publish) {
|
|
25318
25579
|
const event = client._delivery.p2pAppEventForMessage(plaintext);
|
|
25319
25580
|
if (orderedPublish) {
|
|
@@ -25380,6 +25641,7 @@ var V2E2EECoordinator = class {
|
|
|
25380
25641
|
payload,
|
|
25381
25642
|
encrypted: false
|
|
25382
25643
|
};
|
|
25644
|
+
if (msg.sender_device_id !== void 0) plaintext.sender_device_id = msg.sender_device_id;
|
|
25383
25645
|
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
25384
25646
|
attachGatewayProximity(plaintext, msg);
|
|
25385
25647
|
}
|
|
@@ -25543,257 +25805,367 @@ var V2E2EECoordinator = class {
|
|
|
25543
25805
|
let lastAutoAckSeq = 0;
|
|
25544
25806
|
let pendingAckSeq = deferredForwardAck;
|
|
25545
25807
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
25546
|
-
|
|
25547
|
-
|
|
25548
|
-
|
|
25549
|
-
|
|
25550
|
-
|
|
25551
|
-
|
|
25552
|
-
|
|
25553
|
-
|
|
25554
|
-
|
|
25555
|
-
|
|
25556
|
-
|
|
25557
|
-
|
|
25558
|
-
|
|
25559
|
-
|
|
25560
|
-
|
|
25561
|
-
|
|
25562
|
-
|
|
25563
|
-
|
|
25564
|
-
|
|
25565
|
-
|
|
25566
|
-
|
|
25567
|
-
|
|
25568
|
-
|
|
25569
|
-
|
|
25570
|
-
|
|
25571
|
-
|
|
25572
|
-
|
|
25808
|
+
let pipelineRunning = false;
|
|
25809
|
+
let pipelineError = null;
|
|
25810
|
+
const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
25811
|
+
let pipelineProducer = null;
|
|
25812
|
+
let pipelineStopped = false;
|
|
25813
|
+
const startPipeline = (startAfterSeq, startVolatileCursor) => {
|
|
25814
|
+
if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT) return;
|
|
25815
|
+
pipelineRunning = true;
|
|
25816
|
+
pipelineProducer = (async () => {
|
|
25817
|
+
let producerAfterSeq = startAfterSeq;
|
|
25818
|
+
let producerVolatileCursor = startVolatileCursor;
|
|
25819
|
+
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
25820
|
+
if (pipelineStopped) break;
|
|
25821
|
+
const requestedVolatileCursor = producerVolatileCursor;
|
|
25822
|
+
const result = await client._callRawV2Rpc("message.v2.pull", {
|
|
25823
|
+
after_seq: producerAfterSeq,
|
|
25824
|
+
limit,
|
|
25825
|
+
...producerVolatileCursor ? { after_volatile_cursor: producerVolatileCursor } : {},
|
|
25826
|
+
...opts?.force ? { force: true } : {}
|
|
25827
|
+
}, pullGateKey);
|
|
25828
|
+
if (pipelineStopped) break;
|
|
25829
|
+
const rawPageMessages = Array.isArray(result?.messages) ? result.messages : [];
|
|
25830
|
+
const enqueued = await pipelineQueue.put({
|
|
25831
|
+
result,
|
|
25832
|
+
afterSeq: producerAfterSeq,
|
|
25833
|
+
ackUpToSeq: 0,
|
|
25834
|
+
inlineAckGeneration: null,
|
|
25835
|
+
requestedVolatileCursor,
|
|
25836
|
+
weight: rawPageMessages.length
|
|
25837
|
+
}, rawPageMessages.length);
|
|
25838
|
+
if (!enqueued || pipelineStopped) break;
|
|
25839
|
+
const volatileMessages = client._delivery.volatileMessages(rawPageMessages);
|
|
25840
|
+
if (volatileMessages.length > 0) break;
|
|
25841
|
+
const messages = deduplicateForwardPageMessages(rawPageMessages, producerAfterSeq);
|
|
25842
|
+
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
|
|
25843
|
+
const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
|
|
25844
|
+
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
25845
|
+
const shouldContinue = shouldContinueForwardPage({
|
|
25846
|
+
rawCount: messages.length,
|
|
25847
|
+
nextAfterSeq: producerAfterSeq,
|
|
25848
|
+
nextAfter,
|
|
25849
|
+
limit,
|
|
25850
|
+
hasMoreField: Object.prototype.hasOwnProperty.call(result, "has_more"),
|
|
25851
|
+
hasMore: result.has_more === true,
|
|
25852
|
+
serverHead: safeNonNegativeSeq(cursor?.latest_seq) ?? 0,
|
|
25853
|
+
observedUpper: Math.max(nextAfter, Number(client._pendingP2pPullUpper?.get?.(ns) ?? 0)),
|
|
25854
|
+
pendingAckSeq: 0
|
|
25855
|
+
});
|
|
25856
|
+
if (!shouldContinue) break;
|
|
25857
|
+
producerAfterSeq = nextAfter;
|
|
25858
|
+
producerVolatileCursor = "";
|
|
25859
|
+
}
|
|
25860
|
+
})().catch((error) => {
|
|
25861
|
+
pipelineError = error;
|
|
25862
|
+
}).finally(() => {
|
|
25863
|
+
pipelineQueue.close();
|
|
25864
|
+
});
|
|
25865
|
+
};
|
|
25866
|
+
const stopPipeline = async () => {
|
|
25867
|
+
pipelineStopped = true;
|
|
25868
|
+
pipelineQueue.close();
|
|
25869
|
+
if (pipelineProducer) {
|
|
25870
|
+
const producer = pipelineProducer;
|
|
25871
|
+
pipelineProducer = null;
|
|
25872
|
+
await producer;
|
|
25873
|
+
}
|
|
25874
|
+
pipelineRunning = false;
|
|
25875
|
+
};
|
|
25876
|
+
try {
|
|
25877
|
+
while (pageCount < maxPages) {
|
|
25878
|
+
let pipelinePage;
|
|
25879
|
+
if (pipelineRunning) {
|
|
25880
|
+
pipelinePage = await pipelineQueue.take();
|
|
25881
|
+
if (!pipelinePage) {
|
|
25882
|
+
pipelineRunning = false;
|
|
25883
|
+
if (pipelineError) throw pipelineError;
|
|
25884
|
+
break;
|
|
25573
25885
|
}
|
|
25574
|
-
|
|
25575
|
-
|
|
25576
|
-
|
|
25886
|
+
}
|
|
25887
|
+
pageCount += 1;
|
|
25888
|
+
const volatileCursorBeforePage = pipelinePage?.requestedVolatileCursor ?? volatileCursor;
|
|
25889
|
+
const ackUpToSeq = pipelinePage?.ackUpToSeq ?? pendingAckSeq;
|
|
25890
|
+
const pendingInlineGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0 ? client._delivery?.pendingP2PInlineAckGeneration?.(ns) ?? null : null);
|
|
25891
|
+
let result;
|
|
25892
|
+
if (pipelinePage) {
|
|
25893
|
+
nextAfterSeq = pipelinePage.afterSeq;
|
|
25894
|
+
result = pipelinePage.result;
|
|
25577
25895
|
} else {
|
|
25578
|
-
|
|
25579
|
-
|
|
25580
|
-
|
|
25581
|
-
|
|
25582
|
-
|
|
25583
|
-
|
|
25584
|
-
|
|
25585
|
-
|
|
25586
|
-
|
|
25587
|
-
|
|
25588
|
-
|
|
25589
|
-
|
|
25590
|
-
|
|
25591
|
-
|
|
25592
|
-
|
|
25593
|
-
|
|
25594
|
-
|
|
25595
|
-
|
|
25596
|
-
|
|
25597
|
-
|
|
25598
|
-
|
|
25599
|
-
|
|
25600
|
-
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
25601
|
-
const pageContiguousSeq = contiguousPullSeq(
|
|
25602
|
-
pageContigBefore,
|
|
25603
|
-
seqs,
|
|
25604
|
-
result.contiguous_seq
|
|
25605
|
-
);
|
|
25606
|
-
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
25607
|
-
let blockedSeq = 0;
|
|
25608
|
-
const pageDecryptedBefore = decrypted.length;
|
|
25609
|
-
for (const msg of messages) {
|
|
25610
|
-
const seq2 = Number(msg.seq ?? 0);
|
|
25611
|
-
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
25612
|
-
const version = String(msg.version ?? "v2");
|
|
25613
|
-
if (version === "v1") {
|
|
25614
|
-
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
25615
|
-
const legacyPayload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
|
|
25616
|
-
const payloadType = isJsonObject(legacyPayload) ? String(legacyPayload.type ?? "").trim() : "";
|
|
25617
|
-
if (legacyPayload !== void 0 && legacyPayload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
|
|
25618
|
-
const v1Msg = {
|
|
25619
|
-
message_id: String(msg.message_id ?? ""),
|
|
25620
|
-
from: String(msg.from_aid ?? ""),
|
|
25621
|
-
to: String(legacy.to ?? client._aid ?? ""),
|
|
25622
|
-
seq: msg.seq,
|
|
25623
|
-
type: String(msg.type ?? ""),
|
|
25624
|
-
timestamp: msg.t_server,
|
|
25625
|
-
payload: legacyPayload,
|
|
25626
|
-
encrypted: false
|
|
25627
|
-
};
|
|
25628
|
-
attachGatewayProximity(v1Msg, msg);
|
|
25629
|
-
const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
|
|
25630
|
-
if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
|
|
25631
|
-
else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
|
|
25632
|
-
decrypted.push(v1Msg);
|
|
25633
|
-
} else if (isJsonObject(legacyPayload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
25634
|
-
const deferStatus2 = {};
|
|
25635
|
-
const plaintext2 = await client._decryptV2Message(
|
|
25636
|
-
{ ...msg, envelope_json: JSON.stringify(legacyPayload) },
|
|
25637
|
-
true,
|
|
25638
|
-
true,
|
|
25639
|
-
true,
|
|
25640
|
-
true,
|
|
25641
|
-
deferStatus2,
|
|
25642
|
-
true,
|
|
25643
|
-
"pull"
|
|
25644
|
-
);
|
|
25645
|
-
if (deferStatus2.deferred) {
|
|
25646
|
-
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25647
|
-
if (deferStatus2.fromAid) {
|
|
25648
|
-
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
25649
|
-
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
25650
|
-
}
|
|
25651
|
-
}
|
|
25652
|
-
if (plaintext2) {
|
|
25653
|
-
if (ns) await client._publishPulledMessage("message.received", ns, seq2, plaintext2, false);
|
|
25654
|
-
else await client._publishAppEvent("message.received", plaintext2, "pull");
|
|
25655
|
-
decrypted.push(plaintext2);
|
|
25896
|
+
try {
|
|
25897
|
+
result = await client._callRawV2Rpc("message.v2.pull", {
|
|
25898
|
+
after_seq: nextAfterSeq,
|
|
25899
|
+
limit,
|
|
25900
|
+
...volatileCursor ? { after_volatile_cursor: volatileCursor } : {},
|
|
25901
|
+
...opts?.force ? { force: true } : {},
|
|
25902
|
+
...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
|
|
25903
|
+
}, pullGateKey);
|
|
25904
|
+
} catch (exc) {
|
|
25905
|
+
const text3 = exc instanceof Error ? exc.message : String(exc);
|
|
25906
|
+
if (!volatileCursor || !text3.toLowerCase().includes("volatile cursor epoch mismatch")) throw exc;
|
|
25907
|
+
client._delivery.resetVolatilePullCursor();
|
|
25908
|
+
volatileCursor = "";
|
|
25909
|
+
pageCount -= 1;
|
|
25910
|
+
continue;
|
|
25911
|
+
}
|
|
25912
|
+
}
|
|
25913
|
+
if (ackUpToSeq > 0) {
|
|
25914
|
+
const actualAckSeq = client._delivery?.resolveP2PPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
25915
|
+
if (actualAckSeq >= ackUpToSeq) {
|
|
25916
|
+
if (pendingInlineGeneration !== null) {
|
|
25917
|
+
client._delivery?.completePendingP2PInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
|
|
25656
25918
|
}
|
|
25919
|
+
pendingAckSeq = 0;
|
|
25920
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
25921
|
+
this.clearForwardAck(ns, ackUpToSeq);
|
|
25657
25922
|
} else {
|
|
25658
|
-
client._clientLog.
|
|
25923
|
+
client._clientLog.warn(`message.v2.pull piggyback ACK below target: requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
25924
|
+
await this.confirmForwardP2PAck(ackUpToSeq, pendingInlineGeneration ?? void 0);
|
|
25925
|
+
pendingAckSeq = 0;
|
|
25926
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
25659
25927
|
}
|
|
25660
|
-
continue;
|
|
25661
|
-
}
|
|
25662
|
-
if (version !== "v2") {
|
|
25663
|
-
client._clientLog.debug(`message.v2.pull skipping non-V2 row seq=${seq2} version=${String(msg.version ?? "")}`);
|
|
25664
|
-
continue;
|
|
25665
25928
|
}
|
|
25666
|
-
const
|
|
25667
|
-
|
|
25668
|
-
|
|
25669
|
-
|
|
25670
|
-
|
|
25671
|
-
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
|
|
25672
|
-
if (deferStatus.deferred) {
|
|
25673
|
-
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25674
|
-
}
|
|
25675
|
-
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
25676
|
-
const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
|
|
25677
|
-
deferredKeyFetches.set(key, {
|
|
25678
|
-
fromAid: deferStatus.fromAid,
|
|
25679
|
-
senderDeviceId: deferStatus.senderDeviceId ?? "",
|
|
25680
|
-
groupId: deferStatus.groupId ?? ""
|
|
25681
|
-
});
|
|
25929
|
+
const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
|
|
25930
|
+
const volatileMessages = client._delivery.volatileMessages(pageMessages);
|
|
25931
|
+
if (volatileMessages.length > 0) {
|
|
25932
|
+
decrypted.push(...await this.publishV2VolatileMessages(volatileMessages, "pull", true));
|
|
25933
|
+
volatileCursor = client._delivery.volatilePullCursor();
|
|
25682
25934
|
}
|
|
25683
|
-
|
|
25684
|
-
|
|
25685
|
-
|
|
25686
|
-
|
|
25687
|
-
|
|
25688
|
-
|
|
25689
|
-
|
|
25690
|
-
|
|
25691
|
-
|
|
25692
|
-
|
|
25693
|
-
|
|
25694
|
-
|
|
25695
|
-
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
25696
|
-
if (ns) {
|
|
25697
|
-
const commitTarget = Math.max(
|
|
25935
|
+
const volatileAdvanced = Boolean(volatileCursor) && volatileCursor !== volatileCursorBeforePage;
|
|
25936
|
+
const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
|
|
25937
|
+
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
25938
|
+
const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
|
|
25939
|
+
const hasMore = result.has_more === true;
|
|
25940
|
+
const serverHead = safeNonNegativeSeq(cursor?.latest_seq) ?? 0;
|
|
25941
|
+
client._clientLog.debug(`message.v2.pull page response: page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? "")}, server_head=${serverHead}, server_ack_seq=${String(result.server_ack_seq ?? "")}`);
|
|
25942
|
+
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
|
|
25943
|
+
const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
25944
|
+
const pageTrackerSnapshot = ns && typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
|
|
25945
|
+
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
25946
|
+
const pageContiguousSeq = contiguousPullSeq(
|
|
25698
25947
|
pageContigBefore,
|
|
25699
|
-
|
|
25700
|
-
|
|
25701
|
-
serverAckSeq,
|
|
25702
|
-
blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
|
|
25948
|
+
seqs,
|
|
25949
|
+
result.contiguous_seq
|
|
25703
25950
|
);
|
|
25704
|
-
|
|
25705
|
-
let
|
|
25706
|
-
|
|
25707
|
-
|
|
25708
|
-
|
|
25709
|
-
|
|
25710
|
-
|
|
25951
|
+
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
25952
|
+
let blockedSeq = 0;
|
|
25953
|
+
const pageDecryptedBefore = decrypted.length;
|
|
25954
|
+
for (const msg of messages) {
|
|
25955
|
+
const seq2 = Number(msg.seq ?? 0);
|
|
25956
|
+
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
25957
|
+
const version = String(msg.version ?? "v2");
|
|
25958
|
+
if (version === "v1") {
|
|
25959
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
25960
|
+
const legacyPayload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
|
|
25961
|
+
const payloadType = isJsonObject(legacyPayload) ? String(legacyPayload.type ?? "").trim() : "";
|
|
25962
|
+
if (legacyPayload !== void 0 && legacyPayload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
|
|
25963
|
+
const v1Msg = {
|
|
25964
|
+
message_id: String(msg.message_id ?? ""),
|
|
25965
|
+
from: String(msg.from_aid ?? ""),
|
|
25966
|
+
to: String(legacy.to ?? client._aid ?? ""),
|
|
25967
|
+
seq: msg.seq,
|
|
25968
|
+
type: String(msg.type ?? ""),
|
|
25969
|
+
timestamp: msg.t_server,
|
|
25970
|
+
payload: legacyPayload,
|
|
25971
|
+
encrypted: false
|
|
25972
|
+
};
|
|
25973
|
+
const direction = String(msg.direction ?? "").trim();
|
|
25974
|
+
if (direction) v1Msg.direction = direction;
|
|
25975
|
+
else if (String(msg.from_aid ?? "").trim() && String(msg.from_aid ?? "").trim() !== String(client._aid ?? "").trim()) {
|
|
25976
|
+
v1Msg.direction = "inbound";
|
|
25977
|
+
}
|
|
25978
|
+
if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
|
|
25979
|
+
attachGatewayProximity(v1Msg, msg);
|
|
25980
|
+
const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
|
|
25981
|
+
if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
|
|
25982
|
+
else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
|
|
25983
|
+
decrypted.push(v1Msg);
|
|
25984
|
+
} else if (isJsonObject(legacyPayload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
25985
|
+
const deferStatus2 = {};
|
|
25986
|
+
const plaintext2 = await client._decryptV2Message(
|
|
25987
|
+
{ ...msg, envelope_json: JSON.stringify(legacyPayload) },
|
|
25988
|
+
true,
|
|
25989
|
+
true,
|
|
25990
|
+
true,
|
|
25991
|
+
true,
|
|
25992
|
+
deferStatus2,
|
|
25993
|
+
true,
|
|
25994
|
+
"pull"
|
|
25995
|
+
);
|
|
25996
|
+
if (deferStatus2.deferred) {
|
|
25997
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25998
|
+
if (deferStatus2.fromAid) {
|
|
25999
|
+
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
26000
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
26001
|
+
}
|
|
26002
|
+
}
|
|
26003
|
+
if (plaintext2) {
|
|
26004
|
+
if (ns) await client._publishPulledMessage("message.received", ns, seq2, plaintext2, false);
|
|
26005
|
+
else await client._publishAppEvent("message.received", plaintext2, "pull");
|
|
26006
|
+
decrypted.push(plaintext2);
|
|
26007
|
+
}
|
|
26008
|
+
} else {
|
|
26009
|
+
client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq2} payload_type=${payloadType || "<none>"} (V1 E2EE removed)`);
|
|
26010
|
+
}
|
|
26011
|
+
continue;
|
|
26012
|
+
}
|
|
26013
|
+
if (version !== "v2") {
|
|
26014
|
+
client._clientLog.debug(`message.v2.pull skipping non-V2 row seq=${seq2} version=${String(msg.version ?? "")}`);
|
|
26015
|
+
continue;
|
|
26016
|
+
}
|
|
26017
|
+
const msgSpkId = String(msg.spk_id ?? "");
|
|
26018
|
+
if (msgSpkId && client._v2Session && !client._v2Session.isCurrentSPK(msgSpkId)) {
|
|
26019
|
+
client._v2Session.trackOldSPKMaxSeq(msgSpkId, seq2);
|
|
26020
|
+
}
|
|
26021
|
+
const deferStatus = {};
|
|
26022
|
+
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
|
|
26023
|
+
if (deferStatus.deferred) {
|
|
26024
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
26025
|
+
}
|
|
26026
|
+
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
26027
|
+
const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
|
|
26028
|
+
deferredKeyFetches.set(key, {
|
|
26029
|
+
fromAid: deferStatus.fromAid,
|
|
26030
|
+
senderDeviceId: deferStatus.senderDeviceId ?? "",
|
|
26031
|
+
groupId: deferStatus.groupId ?? ""
|
|
26032
|
+
});
|
|
26033
|
+
}
|
|
26034
|
+
if (plaintext === null) continue;
|
|
26035
|
+
if (ns) {
|
|
26036
|
+
await client._publishPulledMessage("message.received", ns, seq2, plaintext, false);
|
|
26037
|
+
decrypted.push(plaintext);
|
|
26038
|
+
} else {
|
|
26039
|
+
await client._publishAppEvent("message.received", plaintext, "pull");
|
|
26040
|
+
decrypted.push(plaintext);
|
|
26041
|
+
}
|
|
26042
|
+
}
|
|
26043
|
+
const hasServerAckSeq = Object.prototype.hasOwnProperty.call(result, "server_ack_seq");
|
|
26044
|
+
const parsedServerAckSeq = hasServerAckSeq ? safeNonNegativeSeq(result.server_ack_seq) : null;
|
|
26045
|
+
const serverAckSeq = parsedServerAckSeq ?? 0;
|
|
26046
|
+
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
26047
|
+
if (ns) {
|
|
26048
|
+
const commitTarget = Math.max(
|
|
26049
|
+
pageContigBefore,
|
|
26050
|
+
Number.isFinite(retentionFloor) ? retentionFloor : 0,
|
|
26051
|
+
pageCount === 1 ? deferredServerCursor : 0,
|
|
26052
|
+
serverAckSeq,
|
|
26053
|
+
blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
|
|
26054
|
+
);
|
|
26055
|
+
let ackSeq = pageContigBefore;
|
|
26056
|
+
let contigAdvanced = false;
|
|
26057
|
+
try {
|
|
26058
|
+
if (commitTarget > pageContigBefore) {
|
|
26059
|
+
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
26060
|
+
if (retentionFloor > pageContigBefore) {
|
|
26061
|
+
client._clientLog.info(`message.v2.pull retention-floor advance: ns=${ns} contiguous=${pageContigBefore} -> retention_floor_seq=${retentionFloor}`);
|
|
26062
|
+
}
|
|
26063
|
+
if (deferredServerCursor > pageContigBefore && pageCount === 1) {
|
|
26064
|
+
client._clientLog.info(`message.v2.pull restore local A from deferred server ACK cursor: ns=${ns}, contiguous=${pageContigBefore} -> server_ack_seq=${deferredServerCursor}`);
|
|
26065
|
+
}
|
|
26066
|
+
if (seqs.length > 0) {
|
|
26067
|
+
client._clientLog.debug(`message.v2.pull force contiguous: ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
|
|
26068
|
+
}
|
|
25711
26069
|
}
|
|
25712
|
-
|
|
25713
|
-
|
|
26070
|
+
ackSeq = client._seqTracker.getContiguousSeq(ns);
|
|
26071
|
+
contigAdvanced = ackSeq !== pageContigBefore;
|
|
26072
|
+
if (contigAdvanced) {
|
|
26073
|
+
await client._commitSeqTrackerState(ns);
|
|
26074
|
+
if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
|
|
25714
26075
|
}
|
|
25715
|
-
|
|
25716
|
-
|
|
26076
|
+
} catch (exc) {
|
|
26077
|
+
if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
|
|
26078
|
+
client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
|
|
25717
26079
|
}
|
|
26080
|
+
client._clientLog.error?.(`message.v2.pull Forward commit rolled back: ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
26081
|
+
throw exc;
|
|
25718
26082
|
}
|
|
25719
|
-
|
|
25720
|
-
|
|
25721
|
-
|
|
25722
|
-
await client._commitSeqTrackerState(ns);
|
|
25723
|
-
if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
|
|
26083
|
+
if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
|
|
26084
|
+
for (const fetch2 of deferredKeyFetches.values()) {
|
|
26085
|
+
this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
|
|
25724
26086
|
}
|
|
25725
|
-
|
|
25726
|
-
|
|
25727
|
-
client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
|
|
26087
|
+
if (parsedServerAckSeq !== null && serverAckSeq > ackSeq) {
|
|
26088
|
+
this.recordForwardCursor(ns, serverAckSeq, ackSeq);
|
|
25728
26089
|
}
|
|
25729
|
-
|
|
25730
|
-
|
|
25731
|
-
|
|
25732
|
-
|
|
25733
|
-
|
|
25734
|
-
|
|
25735
|
-
|
|
25736
|
-
|
|
25737
|
-
|
|
25738
|
-
|
|
25739
|
-
|
|
25740
|
-
|
|
25741
|
-
|
|
25742
|
-
this.recordForwardAck(ns, ackSeq);
|
|
25743
|
-
const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
25744
|
-
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq;
|
|
25745
|
-
if (canContinuePage) {
|
|
25746
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
25747
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
25748
|
-
} else {
|
|
25749
|
-
await this.confirmForwardP2PAck(ackSeq);
|
|
25750
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26090
|
+
const knownServerAckSeq = hasServerAckSeq ? serverAckSeq : pageCount === 1 ? deferredServerCursor : 0;
|
|
26091
|
+
const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && (hasServerAckSeq && ackSeq > serverAckSeq || contigAdvanced && ackSeq > knownServerAckSeq);
|
|
26092
|
+
if (ackNeeded) {
|
|
26093
|
+
this.recordForwardAck(ns, ackSeq);
|
|
26094
|
+
const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26095
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq;
|
|
26096
|
+
if (canContinuePage && !pipelineRunning) {
|
|
26097
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
26098
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26099
|
+
} else {
|
|
26100
|
+
await this.confirmForwardP2PAck(ackSeq);
|
|
26101
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26102
|
+
}
|
|
25751
26103
|
}
|
|
25752
26104
|
}
|
|
26105
|
+
if (ns) client._delivery.onPullPage?.(
|
|
26106
|
+
ns,
|
|
26107
|
+
decrypted.length - pageDecryptedBefore,
|
|
26108
|
+
hasMore,
|
|
26109
|
+
pageMaxSeq,
|
|
26110
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
26111
|
+
messages.length,
|
|
26112
|
+
client._seqTracker.getContiguousSeq(ns),
|
|
26113
|
+
pageMaxSeq
|
|
26114
|
+
);
|
|
26115
|
+
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26116
|
+
const rawCount = messages.length;
|
|
26117
|
+
if (blockedSeq > 0) break;
|
|
26118
|
+
let shouldContinue = shouldContinueForwardPage({
|
|
26119
|
+
rawCount,
|
|
26120
|
+
nextAfterSeq,
|
|
26121
|
+
nextAfter,
|
|
26122
|
+
limit,
|
|
26123
|
+
hasMoreField,
|
|
26124
|
+
hasMore,
|
|
26125
|
+
serverHead,
|
|
26126
|
+
pendingAckSeq,
|
|
26127
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
26128
|
+
});
|
|
26129
|
+
const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, "volatile_has_more");
|
|
26130
|
+
const volatileHasMore = result.volatile_has_more === true;
|
|
26131
|
+
if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || !volatileHasMoreField && limit > 0 && pageMessages.length >= limit)) {
|
|
26132
|
+
shouldContinue = true;
|
|
26133
|
+
}
|
|
26134
|
+
const shouldContinueForPipeline = shouldContinueForwardPage({
|
|
26135
|
+
rawCount,
|
|
26136
|
+
nextAfterSeq,
|
|
26137
|
+
nextAfter,
|
|
26138
|
+
limit,
|
|
26139
|
+
hasMoreField,
|
|
26140
|
+
hasMore,
|
|
26141
|
+
serverHead,
|
|
26142
|
+
pendingAckSeq: 0,
|
|
26143
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
26144
|
+
});
|
|
26145
|
+
if (!shouldContinue) break;
|
|
26146
|
+
if (shouldContinueForPipeline && !volatileAdvanced && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
26147
|
+
if (pendingAckSeq > 0) {
|
|
26148
|
+
await this.confirmForwardP2PAck(pendingAckSeq);
|
|
26149
|
+
pendingAckSeq = 0;
|
|
26150
|
+
}
|
|
26151
|
+
startPipeline(nextAfter, volatileCursor);
|
|
26152
|
+
}
|
|
26153
|
+
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
26154
|
+
const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
|
|
26155
|
+
after_seq: nextAfter,
|
|
26156
|
+
after_volatile_cursor: volatileCursor,
|
|
26157
|
+
force: opts?.force === true,
|
|
26158
|
+
limit
|
|
26159
|
+
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
26160
|
+
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
26161
|
+
pullGateKey = nextKey;
|
|
26162
|
+
}
|
|
26163
|
+
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
26164
|
+
if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
|
|
26165
|
+
nextAfterSeq = nextAfter;
|
|
25753
26166
|
}
|
|
25754
|
-
|
|
25755
|
-
|
|
25756
|
-
decrypted.length - pageDecryptedBefore,
|
|
25757
|
-
hasMore,
|
|
25758
|
-
pageMaxSeq,
|
|
25759
|
-
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
25760
|
-
messages.length,
|
|
25761
|
-
client._seqTracker.getContiguousSeq(ns),
|
|
25762
|
-
pageMaxSeq
|
|
25763
|
-
);
|
|
25764
|
-
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
25765
|
-
const rawCount = messages.length;
|
|
25766
|
-
if (blockedSeq > 0) break;
|
|
25767
|
-
let shouldContinue = shouldContinueForwardPage({
|
|
25768
|
-
rawCount,
|
|
25769
|
-
nextAfterSeq,
|
|
25770
|
-
nextAfter,
|
|
25771
|
-
limit,
|
|
25772
|
-
hasMoreField,
|
|
25773
|
-
hasMore,
|
|
25774
|
-
serverHead,
|
|
25775
|
-
pendingAckSeq,
|
|
25776
|
-
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
25777
|
-
});
|
|
25778
|
-
const volatileHasMoreField = Object.prototype.hasOwnProperty.call(result, "volatile_has_more");
|
|
25779
|
-
const volatileHasMore = result.volatile_has_more === true;
|
|
25780
|
-
if (!shouldContinue && volatileAdvanced && (volatileHasMore || hasMore || !volatileHasMoreField && limit > 0 && pageMessages.length >= limit)) {
|
|
25781
|
-
shouldContinue = true;
|
|
25782
|
-
}
|
|
25783
|
-
if (!shouldContinue) break;
|
|
25784
|
-
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
25785
|
-
const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
|
|
25786
|
-
after_seq: nextAfter,
|
|
25787
|
-
after_volatile_cursor: volatileCursor,
|
|
25788
|
-
force: opts?.force === true,
|
|
25789
|
-
limit
|
|
25790
|
-
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
25791
|
-
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
25792
|
-
pullGateKey = nextKey;
|
|
25793
|
-
}
|
|
25794
|
-
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
25795
|
-
if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
|
|
25796
|
-
nextAfterSeq = nextAfter;
|
|
26167
|
+
} finally {
|
|
26168
|
+
await stopPipeline();
|
|
25797
26169
|
}
|
|
25798
26170
|
if (pageCount >= maxPages) {
|
|
25799
26171
|
client._clientLog.warn(`message.v2.pull reached max_pages=${maxPages} after_seq=${nextAfterSeq}`);
|
|
@@ -26156,79 +26528,182 @@ var V2E2EECoordinator = class {
|
|
|
26156
26528
|
let lastAutoAckSeq = 0;
|
|
26157
26529
|
let pendingAckSeq = deferredForwardAck;
|
|
26158
26530
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
26159
|
-
|
|
26160
|
-
|
|
26161
|
-
|
|
26162
|
-
|
|
26163
|
-
|
|
26164
|
-
|
|
26165
|
-
|
|
26166
|
-
|
|
26167
|
-
|
|
26168
|
-
|
|
26169
|
-
|
|
26170
|
-
|
|
26171
|
-
|
|
26172
|
-
|
|
26173
|
-
|
|
26174
|
-
|
|
26175
|
-
|
|
26531
|
+
let pipelineRunning = false;
|
|
26532
|
+
let pipelineError = null;
|
|
26533
|
+
const pipelineQueue = new V2PullPageQueue(V2_PULL_PIPELINE_QUEUE_CAPACITY);
|
|
26534
|
+
let pipelineProducer = null;
|
|
26535
|
+
let pipelineStopped = false;
|
|
26536
|
+
const startPipeline = (startAfterSeq) => {
|
|
26537
|
+
if (pipelineRunning || pageCount >= maxPages || limit < V2_PULL_MAX_PAGE_LIMIT || !ownsCursor) return;
|
|
26538
|
+
pipelineRunning = true;
|
|
26539
|
+
pipelineProducer = (async () => {
|
|
26540
|
+
let producerAfterSeq = startAfterSeq;
|
|
26541
|
+
for (let produced = pageCount; produced < maxPages; produced += 1) {
|
|
26542
|
+
if (pipelineStopped) break;
|
|
26543
|
+
const result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
|
|
26544
|
+
group_id: wireGroupId,
|
|
26545
|
+
after_seq: producerAfterSeq,
|
|
26546
|
+
limit,
|
|
26547
|
+
...cursorParams,
|
|
26548
|
+
...opts?.force ? { force: true } : {}
|
|
26549
|
+
}, groupAid), pullGateKey);
|
|
26550
|
+
if (pipelineStopped) break;
|
|
26551
|
+
const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
|
|
26552
|
+
const enqueued = await pipelineQueue.put({
|
|
26553
|
+
result,
|
|
26554
|
+
afterSeq: producerAfterSeq,
|
|
26555
|
+
ackUpToSeq: 0,
|
|
26556
|
+
inlineAckGeneration: null,
|
|
26557
|
+
requestedVolatileCursor: "",
|
|
26558
|
+
weight: pageMessages.length
|
|
26559
|
+
}, pageMessages.length);
|
|
26560
|
+
if (!enqueued || pipelineStopped) break;
|
|
26561
|
+
const messages = deduplicateForwardPageMessages(pageMessages, producerAfterSeq);
|
|
26562
|
+
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
|
|
26563
|
+
const nextAfter = Math.max(contiguousPullSeq(producerAfterSeq, seqs, result.contiguous_seq), producerAfterSeq);
|
|
26564
|
+
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
26565
|
+
const shouldContinue = shouldContinueForwardPage({
|
|
26566
|
+
rawCount: messages.length,
|
|
26567
|
+
nextAfterSeq: producerAfterSeq,
|
|
26568
|
+
nextAfter,
|
|
26569
|
+
limit,
|
|
26570
|
+
hasMoreField: Object.prototype.hasOwnProperty.call(result, "has_more"),
|
|
26571
|
+
hasMore: result.has_more === true,
|
|
26572
|
+
serverHead: Math.max(safeNonNegativeSeq(result.latest_message_seq) ?? 0, safeNonNegativeSeq(cursor?.latest_seq) ?? 0),
|
|
26573
|
+
observedUpper: Math.max(nextAfter, typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : 0),
|
|
26574
|
+
pendingAckSeq: 0
|
|
26575
|
+
});
|
|
26576
|
+
if (!shouldContinue) break;
|
|
26577
|
+
producerAfterSeq = nextAfter;
|
|
26578
|
+
}
|
|
26579
|
+
})().catch((error) => {
|
|
26580
|
+
pipelineError = error;
|
|
26581
|
+
}).finally(() => {
|
|
26582
|
+
pipelineQueue.close();
|
|
26583
|
+
});
|
|
26584
|
+
};
|
|
26585
|
+
const stopPipeline = async () => {
|
|
26586
|
+
pipelineStopped = true;
|
|
26587
|
+
pipelineQueue.close();
|
|
26588
|
+
if (pipelineProducer) {
|
|
26589
|
+
const producer = pipelineProducer;
|
|
26590
|
+
pipelineProducer = null;
|
|
26591
|
+
await producer;
|
|
26592
|
+
}
|
|
26593
|
+
pipelineRunning = false;
|
|
26594
|
+
};
|
|
26595
|
+
try {
|
|
26596
|
+
while (pageCount < maxPages) {
|
|
26597
|
+
let pipelinePage;
|
|
26598
|
+
if (pipelineRunning) {
|
|
26599
|
+
pipelinePage = await pipelineQueue.take();
|
|
26600
|
+
if (!pipelinePage) {
|
|
26601
|
+
pipelineRunning = false;
|
|
26602
|
+
if (pipelineError) throw pipelineError;
|
|
26603
|
+
break;
|
|
26176
26604
|
}
|
|
26177
|
-
|
|
26178
|
-
|
|
26179
|
-
|
|
26605
|
+
}
|
|
26606
|
+
pageCount += 1;
|
|
26607
|
+
const ackUpToSeq = ownsCursor ? pipelinePage?.ackUpToSeq ?? pendingAckSeq : 0;
|
|
26608
|
+
const pendingInlineGeneration = pipelinePage?.inlineAckGeneration ?? (ackUpToSeq > 0 ? client._delivery?.pendingGroupInlineAckGeneration?.(ns) ?? null : null);
|
|
26609
|
+
let result;
|
|
26610
|
+
if (pipelinePage) {
|
|
26611
|
+
nextAfterSeq = pipelinePage.afterSeq;
|
|
26612
|
+
result = pipelinePage.result;
|
|
26180
26613
|
} else {
|
|
26181
|
-
client.
|
|
26182
|
-
|
|
26183
|
-
|
|
26184
|
-
|
|
26185
|
-
|
|
26186
|
-
|
|
26187
|
-
|
|
26188
|
-
|
|
26189
|
-
|
|
26190
|
-
|
|
26191
|
-
|
|
26192
|
-
|
|
26193
|
-
|
|
26194
|
-
|
|
26195
|
-
|
|
26196
|
-
|
|
26197
|
-
|
|
26198
|
-
|
|
26199
|
-
|
|
26200
|
-
|
|
26201
|
-
|
|
26202
|
-
|
|
26203
|
-
|
|
26204
|
-
pageContigBefore,
|
|
26205
|
-
seqs,
|
|
26206
|
-
result.contiguous_seq
|
|
26207
|
-
);
|
|
26208
|
-
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
26209
|
-
let blockedSeq = 0;
|
|
26210
|
-
const pageDecryptedBefore = decrypted.length;
|
|
26211
|
-
for (const msg of messages) {
|
|
26212
|
-
const seq2 = Number(msg.seq ?? 0);
|
|
26213
|
-
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
26214
|
-
const version = String(msg.version ?? "v2");
|
|
26215
|
-
if (version === "v1") {
|
|
26216
|
-
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
26217
|
-
const payload = msg.payload !== void 0 ? msg.payload : legacy.payload;
|
|
26218
|
-
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
26219
|
-
if (typeof client._delivery?.recallEventFromGroupMessage === "function" && client._delivery.recallEventFromGroupMessage(msg)) {
|
|
26220
|
-
await client._delivery.publishGroupRecallTombstone(gid, seq2, {
|
|
26221
|
-
...msg,
|
|
26222
|
-
group_id: gid,
|
|
26223
|
-
group_aid: eventGroupAid
|
|
26224
|
-
}, "pull");
|
|
26225
|
-
client._markPublishedSeq(ns, seq2);
|
|
26226
|
-
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq2}`);
|
|
26227
|
-
continue;
|
|
26614
|
+
result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
|
|
26615
|
+
group_id: wireGroupId,
|
|
26616
|
+
after_seq: nextAfterSeq,
|
|
26617
|
+
limit,
|
|
26618
|
+
...cursorParams,
|
|
26619
|
+
...opts?.force ? { force: true } : {},
|
|
26620
|
+
...ackUpToSeq > 0 ? { ack_up_to_seq: ackUpToSeq } : {}
|
|
26621
|
+
}, groupAid), pullGateKey);
|
|
26622
|
+
}
|
|
26623
|
+
if (ackUpToSeq > 0) {
|
|
26624
|
+
const actualAckSeq = client._delivery?.resolveGroupPullAckSeq?.(result, ackUpToSeq) ?? 0;
|
|
26625
|
+
if (actualAckSeq >= ackUpToSeq) {
|
|
26626
|
+
if (pendingInlineGeneration !== null) {
|
|
26627
|
+
client._delivery?.completePendingGroupInlineAckUpTo?.(ns, actualAckSeq, pendingInlineGeneration);
|
|
26628
|
+
}
|
|
26629
|
+
pendingAckSeq = 0;
|
|
26630
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
26631
|
+
this.clearForwardAck(ns, ackUpToSeq);
|
|
26632
|
+
} else {
|
|
26633
|
+
client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
26634
|
+
await this.confirmForwardGroupAck(gid, ackUpToSeq, groupAid);
|
|
26635
|
+
pendingAckSeq = 0;
|
|
26636
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
26228
26637
|
}
|
|
26229
|
-
|
|
26230
|
-
|
|
26231
|
-
|
|
26638
|
+
}
|
|
26639
|
+
const resultGroupAid = String(result.group_aid ?? result.groupAid ?? "").trim();
|
|
26640
|
+
if (resultGroupAid) groupAid = resultGroupAid;
|
|
26641
|
+
const eventGroupAid = resultGroupAid || groupAid || gid;
|
|
26642
|
+
const pageMessages = Array.isArray(result?.messages) ? result.messages : [];
|
|
26643
|
+
const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
|
|
26644
|
+
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
26645
|
+
const hasMoreField = Object.prototype.hasOwnProperty.call(result, "has_more");
|
|
26646
|
+
const hasMore = result.has_more === true;
|
|
26647
|
+
const serverHead = Math.max(
|
|
26648
|
+
safeNonNegativeSeq(result.latest_message_seq) ?? 0,
|
|
26649
|
+
safeNonNegativeSeq(cursor?.latest_seq) ?? 0
|
|
26650
|
+
);
|
|
26651
|
+
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq2) => Number.isFinite(seq2) && seq2 > 0);
|
|
26652
|
+
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
26653
|
+
const pageTrackerSnapshot = typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
|
|
26654
|
+
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
26655
|
+
const pageContiguousSeq = contiguousPullSeq(
|
|
26656
|
+
pageContigBefore,
|
|
26657
|
+
seqs,
|
|
26658
|
+
result.contiguous_seq
|
|
26659
|
+
);
|
|
26660
|
+
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
26661
|
+
let blockedSeq = 0;
|
|
26662
|
+
const pageDecryptedBefore = decrypted.length;
|
|
26663
|
+
for (const msg of messages) {
|
|
26664
|
+
const seq2 = Number(msg.seq ?? 0);
|
|
26665
|
+
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
26666
|
+
const version = String(msg.version ?? "v2");
|
|
26667
|
+
if (version === "v1") {
|
|
26668
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
26669
|
+
const payload = msg.payload !== void 0 ? msg.payload : legacy.payload;
|
|
26670
|
+
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
26671
|
+
if (typeof client._delivery?.recallEventFromGroupMessage === "function" && client._delivery.recallEventFromGroupMessage(msg)) {
|
|
26672
|
+
await client._delivery.publishGroupRecallTombstone(gid, seq2, {
|
|
26673
|
+
...msg,
|
|
26674
|
+
group_id: gid,
|
|
26675
|
+
group_aid: eventGroupAid
|
|
26676
|
+
}, "pull");
|
|
26677
|
+
client._markPublishedSeq(ns, seq2);
|
|
26678
|
+
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq2}`);
|
|
26679
|
+
continue;
|
|
26680
|
+
}
|
|
26681
|
+
if (payloadObj) {
|
|
26682
|
+
const payloadType = String(payloadObj.type ?? "").trim();
|
|
26683
|
+
if (payloadType !== "e2ee.encrypted" && payloadType !== "e2ee.group_encrypted" && payloadType !== "e2ee.p2p_encrypted") {
|
|
26684
|
+
let v1Msg = {
|
|
26685
|
+
message_id: String(msg.message_id ?? ""),
|
|
26686
|
+
from: String(msg.from_aid ?? ""),
|
|
26687
|
+
group_id: gid,
|
|
26688
|
+
group_aid: eventGroupAid,
|
|
26689
|
+
seq: msg.seq,
|
|
26690
|
+
type: String(msg.type ?? ""),
|
|
26691
|
+
timestamp: msg.t_server,
|
|
26692
|
+
payload,
|
|
26693
|
+
encrypted: false
|
|
26694
|
+
};
|
|
26695
|
+
if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
|
|
26696
|
+
v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
|
|
26697
|
+
attachGatewayProximity(v1Msg, msg);
|
|
26698
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
26699
|
+
await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
|
|
26700
|
+
} else {
|
|
26701
|
+
client._markPublishedSeq(ns, seq2);
|
|
26702
|
+
}
|
|
26703
|
+
decrypted.push(v1Msg);
|
|
26704
|
+
continue;
|
|
26705
|
+
}
|
|
26706
|
+
} else if (payload !== void 0 && payload !== null) {
|
|
26232
26707
|
let v1Msg = {
|
|
26233
26708
|
message_id: String(msg.message_id ?? ""),
|
|
26234
26709
|
from: String(msg.from_aid ?? ""),
|
|
@@ -26240,206 +26715,206 @@ var V2E2EECoordinator = class {
|
|
|
26240
26715
|
payload,
|
|
26241
26716
|
encrypted: false
|
|
26242
26717
|
};
|
|
26718
|
+
if (msg.sender_device_id !== void 0) v1Msg.sender_device_id = msg.sender_device_id;
|
|
26243
26719
|
v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
|
|
26244
26720
|
attachGatewayProximity(v1Msg, msg);
|
|
26245
|
-
|
|
26246
|
-
await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
|
|
26247
|
-
} else {
|
|
26248
|
-
client._markPublishedSeq(ns, seq2);
|
|
26249
|
-
}
|
|
26721
|
+
await client._publishPulledMessage("group.message_created", ns, seq2, v1Msg, false);
|
|
26250
26722
|
decrypted.push(v1Msg);
|
|
26251
26723
|
continue;
|
|
26252
26724
|
}
|
|
26253
|
-
|
|
26254
|
-
|
|
26255
|
-
|
|
26256
|
-
|
|
26257
|
-
|
|
26258
|
-
|
|
26259
|
-
|
|
26260
|
-
|
|
26261
|
-
|
|
26262
|
-
|
|
26263
|
-
|
|
26264
|
-
|
|
26265
|
-
|
|
26266
|
-
|
|
26267
|
-
|
|
26268
|
-
|
|
26269
|
-
|
|
26270
|
-
|
|
26271
|
-
if (payloadObj && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(String(payloadObj.type ?? "").trim())) {
|
|
26272
|
-
const deferStatus2 = {};
|
|
26273
|
-
const plaintext2 = await client._decryptV2Message(
|
|
26274
|
-
{ ...msg, envelope_json: JSON.stringify(payloadObj) },
|
|
26275
|
-
true,
|
|
26276
|
-
true,
|
|
26277
|
-
true,
|
|
26278
|
-
true,
|
|
26279
|
-
deferStatus2,
|
|
26280
|
-
true,
|
|
26281
|
-
"pull"
|
|
26282
|
-
);
|
|
26283
|
-
if (deferStatus2.deferred) {
|
|
26284
|
-
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
26285
|
-
if (deferStatus2.fromAid) {
|
|
26286
|
-
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
26287
|
-
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
26725
|
+
if (payloadObj && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(String(payloadObj.type ?? "").trim())) {
|
|
26726
|
+
const deferStatus2 = {};
|
|
26727
|
+
const plaintext2 = await client._decryptV2Message(
|
|
26728
|
+
{ ...msg, envelope_json: JSON.stringify(payloadObj) },
|
|
26729
|
+
true,
|
|
26730
|
+
true,
|
|
26731
|
+
true,
|
|
26732
|
+
true,
|
|
26733
|
+
deferStatus2,
|
|
26734
|
+
true,
|
|
26735
|
+
"pull"
|
|
26736
|
+
);
|
|
26737
|
+
if (deferStatus2.deferred) {
|
|
26738
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
26739
|
+
if (deferStatus2.fromAid) {
|
|
26740
|
+
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
26741
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
26742
|
+
}
|
|
26288
26743
|
}
|
|
26289
|
-
|
|
26290
|
-
|
|
26291
|
-
|
|
26292
|
-
|
|
26293
|
-
|
|
26294
|
-
|
|
26295
|
-
|
|
26296
|
-
|
|
26744
|
+
if (plaintext2) {
|
|
26745
|
+
plaintext2.group_id = gid;
|
|
26746
|
+
plaintext2.group_aid = eventGroupAid;
|
|
26747
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
26748
|
+
await client._publishPulledMessage("group.message_created", ns, seq2, plaintext2, false);
|
|
26749
|
+
} else {
|
|
26750
|
+
client._markPublishedSeq(ns, seq2);
|
|
26751
|
+
}
|
|
26752
|
+
decrypted.push(plaintext2);
|
|
26297
26753
|
}
|
|
26298
|
-
decrypted.push(plaintext2);
|
|
26299
26754
|
}
|
|
26755
|
+
client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq2} payload_type=${payloadObj ? String(payloadObj.type ?? "") : "<none>"} (V1 E2EE removed)`);
|
|
26756
|
+
continue;
|
|
26300
26757
|
}
|
|
26301
|
-
|
|
26302
|
-
|
|
26303
|
-
|
|
26304
|
-
|
|
26305
|
-
|
|
26306
|
-
|
|
26307
|
-
|
|
26308
|
-
|
|
26309
|
-
|
|
26310
|
-
|
|
26311
|
-
|
|
26312
|
-
|
|
26313
|
-
|
|
26314
|
-
|
|
26315
|
-
|
|
26316
|
-
|
|
26317
|
-
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26321
|
-
|
|
26322
|
-
|
|
26323
|
-
|
|
26324
|
-
|
|
26325
|
-
|
|
26326
|
-
|
|
26327
|
-
|
|
26328
|
-
client._markPublishedSeq(ns, seq2);
|
|
26758
|
+
if (version !== "v2") {
|
|
26759
|
+
client._clientLog.debug(`group.v2.pull skipping non-V2 row group=${gid} seq=${seq2} version=${String(msg.version ?? "")}`);
|
|
26760
|
+
continue;
|
|
26761
|
+
}
|
|
26762
|
+
const deferStatus = {};
|
|
26763
|
+
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
|
|
26764
|
+
if (deferStatus.deferred) {
|
|
26765
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
26766
|
+
}
|
|
26767
|
+
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
26768
|
+
const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
|
|
26769
|
+
deferredKeyFetches.set(key, {
|
|
26770
|
+
fromAid: deferStatus.fromAid,
|
|
26771
|
+
senderDeviceId: deferStatus.senderDeviceId ?? "",
|
|
26772
|
+
groupId: deferStatus.groupId ?? ""
|
|
26773
|
+
});
|
|
26774
|
+
}
|
|
26775
|
+
if (plaintext === null) continue;
|
|
26776
|
+
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
26777
|
+
plaintext.group_id = gid;
|
|
26778
|
+
plaintext.group_aid = eventGroupAid;
|
|
26779
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
26780
|
+
await client._publishPulledMessage("group.message_created", ns, seq2, plaintext, false);
|
|
26781
|
+
} else {
|
|
26782
|
+
client._markPublishedSeq(ns, seq2);
|
|
26783
|
+
}
|
|
26784
|
+
decrypted.push(plaintext);
|
|
26329
26785
|
}
|
|
26330
|
-
|
|
26331
|
-
|
|
26332
|
-
|
|
26333
|
-
|
|
26334
|
-
|
|
26335
|
-
|
|
26336
|
-
|
|
26337
|
-
|
|
26338
|
-
|
|
26339
|
-
|
|
26340
|
-
|
|
26341
|
-
|
|
26342
|
-
|
|
26343
|
-
|
|
26344
|
-
|
|
26345
|
-
|
|
26346
|
-
|
|
26347
|
-
|
|
26348
|
-
|
|
26349
|
-
|
|
26350
|
-
|
|
26351
|
-
|
|
26352
|
-
|
|
26353
|
-
|
|
26354
|
-
|
|
26355
|
-
|
|
26356
|
-
|
|
26357
|
-
|
|
26358
|
-
|
|
26359
|
-
|
|
26786
|
+
const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, "current_seq");
|
|
26787
|
+
const parsedCursorCurrentSeq = hasServerCursor ? safeNonNegativeSeq(cursor?.current_seq) : null;
|
|
26788
|
+
const cursorCurrentSeq = parsedCursorCurrentSeq ?? 0;
|
|
26789
|
+
const retentionFloor = Math.max(
|
|
26790
|
+
Number(result.retention_floor_message_seq ?? result.retention_floor_seq ?? 0),
|
|
26791
|
+
Number(cursor?.retention_floor_seq ?? 0)
|
|
26792
|
+
);
|
|
26793
|
+
const visibilityFloor = Math.max(
|
|
26794
|
+
0,
|
|
26795
|
+
Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1),
|
|
26796
|
+
Number(cursor?.join_seq ?? 0)
|
|
26797
|
+
);
|
|
26798
|
+
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
26799
|
+
const commitTarget = Math.max(
|
|
26800
|
+
pageContigBefore,
|
|
26801
|
+
Number.isFinite(effectiveFloor) ? effectiveFloor : 0,
|
|
26802
|
+
ownsCursor && pageCount === 1 ? deferredServerCursor : 0,
|
|
26803
|
+
cursorCurrentSeq,
|
|
26804
|
+
blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq
|
|
26805
|
+
);
|
|
26806
|
+
let ackSeq = pageContigBefore;
|
|
26807
|
+
let contigAdvanced = false;
|
|
26808
|
+
if (ownsCursor) {
|
|
26809
|
+
try {
|
|
26810
|
+
if (commitTarget > pageContigBefore) {
|
|
26811
|
+
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
26812
|
+
if (effectiveFloor > pageContigBefore) {
|
|
26813
|
+
client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
|
|
26814
|
+
}
|
|
26815
|
+
if (deferredServerCursor > pageContigBefore && pageCount === 1) {
|
|
26816
|
+
client._clientLog.info(`group.v2.pull restore local A from deferred server ACK cursor: group=${gid}, ns=${ns}, contiguous=${pageContigBefore} -> current_seq=${deferredServerCursor}`);
|
|
26817
|
+
}
|
|
26818
|
+
if (seqs.length > 0) {
|
|
26819
|
+
client._clientLog.debug(`group.v2.pull force contiguous: group=${gid}, ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
|
|
26820
|
+
}
|
|
26360
26821
|
}
|
|
26361
|
-
|
|
26362
|
-
|
|
26822
|
+
ackSeq = client._seqTracker.getContiguousSeq(ns);
|
|
26823
|
+
contigAdvanced = ackSeq !== pageContigBefore;
|
|
26824
|
+
if (contigAdvanced) {
|
|
26825
|
+
await client._commitSeqTrackerState(ns);
|
|
26826
|
+
if (pageCount === 1 && deferredServerCursor > 0) this.clearForwardCursor(ns, ackSeq);
|
|
26363
26827
|
}
|
|
26364
|
-
|
|
26365
|
-
|
|
26828
|
+
} catch (exc) {
|
|
26829
|
+
if (typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
|
|
26830
|
+
client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
|
|
26366
26831
|
}
|
|
26832
|
+
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
26833
|
+
throw exc;
|
|
26367
26834
|
}
|
|
26368
|
-
|
|
26369
|
-
|
|
26370
|
-
|
|
26371
|
-
|
|
26372
|
-
|
|
26373
|
-
|
|
26374
|
-
|
|
26375
|
-
|
|
26376
|
-
|
|
26835
|
+
if (contigAdvanced) await client._drainOrderedMessages(ns, void 0, false, false);
|
|
26836
|
+
}
|
|
26837
|
+
for (const fetch2 of deferredKeyFetches.values()) {
|
|
26838
|
+
this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
|
|
26839
|
+
}
|
|
26840
|
+
client._delivery.onPullPage?.(
|
|
26841
|
+
ns,
|
|
26842
|
+
decrypted.length - pageDecryptedBefore,
|
|
26843
|
+
hasMore,
|
|
26844
|
+
pageMaxSeq,
|
|
26845
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
26846
|
+
messages.length,
|
|
26847
|
+
client._seqTracker.getContiguousSeq(ns),
|
|
26848
|
+
pageMaxSeq
|
|
26849
|
+
);
|
|
26850
|
+
if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
|
|
26851
|
+
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
26852
|
+
}
|
|
26853
|
+
const knownServerCursorSeq = hasServerCursor ? cursorCurrentSeq : ownsCursor && pageCount === 1 ? deferredServerCursor : 0;
|
|
26854
|
+
const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
|
|
26855
|
+
if (ackNeeded) {
|
|
26856
|
+
this.recordForwardAck(ns, ackSeq);
|
|
26857
|
+
const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26858
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
|
|
26859
|
+
if (canContinuePage && !pipelineRunning) {
|
|
26860
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
26861
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26862
|
+
} else {
|
|
26863
|
+
await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
|
|
26864
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26377
26865
|
}
|
|
26378
|
-
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
26379
|
-
throw exc;
|
|
26380
26866
|
}
|
|
26381
|
-
|
|
26382
|
-
|
|
26383
|
-
|
|
26384
|
-
|
|
26385
|
-
|
|
26386
|
-
|
|
26387
|
-
|
|
26388
|
-
|
|
26389
|
-
|
|
26390
|
-
|
|
26391
|
-
|
|
26392
|
-
|
|
26393
|
-
|
|
26394
|
-
|
|
26395
|
-
|
|
26396
|
-
|
|
26397
|
-
|
|
26398
|
-
|
|
26399
|
-
|
|
26400
|
-
const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
|
|
26401
|
-
if (ackNeeded) {
|
|
26402
|
-
this.recordForwardAck(ns, ackSeq);
|
|
26403
|
-
const nextAfter2 = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26404
|
-
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
|
|
26405
|
-
if (canContinuePage) {
|
|
26406
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
26407
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26408
|
-
} else {
|
|
26409
|
-
await this.confirmForwardGroupAck(gid, ackSeq, groupAid);
|
|
26410
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
26411
|
-
}
|
|
26412
|
-
}
|
|
26413
|
-
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26414
|
-
if (!ownsCursor) break;
|
|
26415
|
-
const rawCount = messages.length;
|
|
26416
|
-
if (blockedSeq > 0) break;
|
|
26417
|
-
const shouldContinue = shouldContinueForwardPage({
|
|
26418
|
-
rawCount,
|
|
26419
|
-
nextAfterSeq,
|
|
26420
|
-
nextAfter,
|
|
26421
|
-
limit,
|
|
26422
|
-
hasMoreField,
|
|
26423
|
-
hasMore,
|
|
26424
|
-
serverHead,
|
|
26425
|
-
pendingAckSeq,
|
|
26426
|
-
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
26427
|
-
});
|
|
26428
|
-
if (!shouldContinue) break;
|
|
26429
|
-
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
26430
|
-
const nextKey = pullGateKeyForClient(client, "group.v2.pull", {
|
|
26431
|
-
group_id: gid,
|
|
26432
|
-
after_seq: nextAfter,
|
|
26433
|
-
force: opts?.force === true,
|
|
26867
|
+
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
26868
|
+
if (!ownsCursor) break;
|
|
26869
|
+
const rawCount = messages.length;
|
|
26870
|
+
if (blockedSeq > 0) break;
|
|
26871
|
+
const shouldContinue = shouldContinueForwardPage({
|
|
26872
|
+
rawCount,
|
|
26873
|
+
nextAfterSeq,
|
|
26874
|
+
nextAfter,
|
|
26875
|
+
limit,
|
|
26876
|
+
hasMoreField,
|
|
26877
|
+
hasMore,
|
|
26878
|
+
serverHead,
|
|
26879
|
+
pendingAckSeq,
|
|
26880
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
26881
|
+
});
|
|
26882
|
+
const shouldContinueForPipeline = shouldContinueForwardPage({
|
|
26883
|
+
rawCount,
|
|
26884
|
+
nextAfterSeq,
|
|
26885
|
+
nextAfter,
|
|
26434
26886
|
limit,
|
|
26435
|
-
|
|
26436
|
-
|
|
26437
|
-
|
|
26438
|
-
|
|
26887
|
+
hasMoreField,
|
|
26888
|
+
hasMore,
|
|
26889
|
+
serverHead,
|
|
26890
|
+
pendingAckSeq: 0,
|
|
26891
|
+
observedUpper: typeof client._seqTracker.getMaxSeenSeq === "function" ? client._seqTracker.getMaxSeenSeq(ns) : nextAfter
|
|
26892
|
+
});
|
|
26893
|
+
if (!shouldContinue) break;
|
|
26894
|
+
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
26895
|
+
if (pendingAckSeq > 0) {
|
|
26896
|
+
await this.confirmForwardGroupAck(gid, pendingAckSeq, groupAid);
|
|
26897
|
+
pendingAckSeq = 0;
|
|
26898
|
+
}
|
|
26899
|
+
startPipeline(nextAfter);
|
|
26900
|
+
}
|
|
26901
|
+
if (!pipelineRunning && pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
26902
|
+
const nextKey = pullGateKeyForClient(client, "group.v2.pull", {
|
|
26903
|
+
group_id: gid,
|
|
26904
|
+
after_seq: nextAfter,
|
|
26905
|
+
force: opts?.force === true,
|
|
26906
|
+
limit,
|
|
26907
|
+
_group_cursor_params: cursorParams
|
|
26908
|
+
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
26909
|
+
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
26910
|
+
pullGateKey = nextKey;
|
|
26911
|
+
}
|
|
26912
|
+
const tailDelayMs = v2PullTailDelayMs(rawCount, limit);
|
|
26913
|
+
if (tailDelayMs > 0) await v2Sleep(tailDelayMs);
|
|
26914
|
+
nextAfterSeq = nextAfter;
|
|
26439
26915
|
}
|
|
26440
|
-
|
|
26441
|
-
|
|
26442
|
-
nextAfterSeq = nextAfter;
|
|
26916
|
+
} finally {
|
|
26917
|
+
await stopPipeline();
|
|
26443
26918
|
}
|
|
26444
26919
|
if (pageCount >= maxPages) {
|
|
26445
26920
|
client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
|
|
@@ -26922,7 +27397,9 @@ var V2E2EECoordinator = class {
|
|
|
26922
27397
|
};
|
|
26923
27398
|
const senderDeviceId = String(aad.from_device ?? "").trim();
|
|
26924
27399
|
if (senderDeviceId) result.sender_device_id = senderDeviceId;
|
|
26925
|
-
|
|
27400
|
+
const direction = String(msg.direction ?? "").trim();
|
|
27401
|
+
if (direction) result.direction = direction;
|
|
27402
|
+
else if (fromAid && fromAid !== client._aid) result.direction = "inbound";
|
|
26926
27403
|
if (msg.t_server !== void 0) result.t_server = msg.t_server;
|
|
26927
27404
|
if (msg.device_id !== void 0) result.device_id = msg.device_id;
|
|
26928
27405
|
attachGatewayProximity(result, msg);
|
|
@@ -29292,7 +29769,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29292
29769
|
this._snapshotLoads.clear();
|
|
29293
29770
|
return this._agentMdPath;
|
|
29294
29771
|
}
|
|
29295
|
-
async upload(content) {
|
|
29772
|
+
async upload(content, ownerOperationId) {
|
|
29296
29773
|
const target = this._ownerAid();
|
|
29297
29774
|
const current = this._currentAid();
|
|
29298
29775
|
if (!target || !current) {
|
|
@@ -29322,7 +29799,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29322
29799
|
}
|
|
29323
29800
|
const signed = signedResult.data.signed;
|
|
29324
29801
|
await verifyAgentMdUpload(signed, current.certPem, target);
|
|
29325
|
-
const result = await this._uploadHttp(target, signed);
|
|
29802
|
+
const result = await this._uploadHttp(target, signed, ownerOperationId);
|
|
29326
29803
|
const localEtag = await _AgentMdManager.contentEtag(signed);
|
|
29327
29804
|
const remoteEtag = String(result.etag ?? "").trim();
|
|
29328
29805
|
await this.saveRecord(target, {
|
|
@@ -29713,7 +30190,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29713
30190
|
_url(aid, gatewayUrl) {
|
|
29714
30191
|
return `${agentMdHttpScheme(gatewayUrl)}://${this._safeAid(aid)}/agent.md`;
|
|
29715
30192
|
}
|
|
29716
|
-
async _uploadHttp(aid, content) {
|
|
30193
|
+
async _uploadHttp(aid, content, ownerOperationId) {
|
|
29717
30194
|
const gatewayUrl = await this._resolveGateway(aid);
|
|
29718
30195
|
const token = await this._accessToken(aid, gatewayUrl);
|
|
29719
30196
|
const viaGatewayHost = this._uploadViaGatewayHost && nodeBuiltin("node:http") !== null;
|
|
@@ -29722,6 +30199,8 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
29722
30199
|
"Content-Type": "text/markdown; charset=utf-8",
|
|
29723
30200
|
...viaGatewayHost ? { Host: this._safeAid(aid) } : {}
|
|
29724
30201
|
};
|
|
30202
|
+
const operationId = String(ownerOperationId ?? "").trim();
|
|
30203
|
+
if (operationId) headers["X-AUN-Owner-Operation-ID"] = operationId;
|
|
29725
30204
|
let url = this._url(aid, gatewayUrl);
|
|
29726
30205
|
if (viaGatewayHost) {
|
|
29727
30206
|
try {
|
|
@@ -30607,6 +31086,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30607
31086
|
/** transport 的 meta observer:吸收 gateway 注入的 _meta 字段。失败不影响业务。 */
|
|
30608
31087
|
async _observeRpcMeta(meta) {
|
|
30609
31088
|
const groupIndexes = isJsonObject(meta.group_indexes) ? meta.group_indexes : {};
|
|
31089
|
+
this._groupIndexMetaCache.observeRpcMeta(meta, { localAid: this._aid ?? "" });
|
|
30610
31090
|
for (const groupAid of Object.keys(groupIndexes)) {
|
|
30611
31091
|
await this._loadGroupIndexCache(groupAid);
|
|
30612
31092
|
}
|
|
@@ -32149,6 +32629,11 @@ ${invitee}` : "";
|
|
|
32149
32629
|
if (!paramsHash || !timestamp2 || !sigB64) {
|
|
32150
32630
|
return false;
|
|
32151
32631
|
}
|
|
32632
|
+
const signedAt = Number(timestamp2) * 1e3;
|
|
32633
|
+
const certMeta = parseCertMetadata(cached.certPem);
|
|
32634
|
+
if (!/^\d+$/.test(timestamp2) || !Number.isSafeInteger(Number(timestamp2)) || signedAt < certMeta.notBefore.getTime() || signedAt > certMeta.notAfter.getTime()) {
|
|
32635
|
+
return false;
|
|
32636
|
+
}
|
|
32152
32637
|
const pubKey = await importCertPublicKeyEcdsa(cached.certPem);
|
|
32153
32638
|
const signData = new TextEncoder().encode(`${method}|${sigAid}|${timestamp2}|${paramsHash}`);
|
|
32154
32639
|
const sigBytes = base64ToUint8(sigB64);
|
|
@@ -33472,7 +33957,8 @@ ${invitee}` : "";
|
|
|
33472
33957
|
};
|
|
33473
33958
|
if (senderDeviceId) result.sender_device_id = senderDeviceId;
|
|
33474
33959
|
const explicitDirection = String(msg.direction ?? "").trim();
|
|
33475
|
-
result.direction = explicitDirection
|
|
33960
|
+
if (explicitDirection) result.direction = explicitDirection;
|
|
33961
|
+
else if (fromAid && fromAid !== this._aid) result.direction = "inbound";
|
|
33476
33962
|
if (msg.device_id !== void 0) result.device_id = msg.device_id;
|
|
33477
33963
|
attachGatewayProximity2(result, msg);
|
|
33478
33964
|
attachV2EnvelopeMetadata2(result, e2ee);
|
|
@@ -34790,6 +35276,19 @@ function agentMdFailure(exc) {
|
|
|
34790
35276
|
}
|
|
34791
35277
|
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
34792
35278
|
}
|
|
35279
|
+
function ownerAgentMd(content, ownerAid, proof) {
|
|
35280
|
+
const marker = content.indexOf("<!-- AUN-SIGNATURE");
|
|
35281
|
+
const payload = marker >= 0 ? content.slice(0, marker) : content;
|
|
35282
|
+
const lines = payload.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
35283
|
+
const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
35284
|
+
if (end < 0) throw new ValidationError("agent.md frontmatter is required for ownership operation");
|
|
35285
|
+
const filtered = lines.filter((line, index) => !(index > 0 && index < end && (/^\s*owner\s*:/.test(line) || /^\s*ownership_proof\s*:/.test(line))));
|
|
35286
|
+
const closed = filtered.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
35287
|
+
filtered.splice(closed, 0, `owner: ${JSON.stringify(ownerAid)}`, `ownership_proof: ${JSON.stringify(proof)}`);
|
|
35288
|
+
const result = filtered.join("\n");
|
|
35289
|
+
return result.endsWith("\n") ? result : `${result}
|
|
35290
|
+
`;
|
|
35291
|
+
}
|
|
34793
35292
|
var AIDStore = class {
|
|
34794
35293
|
constructor(opts) {
|
|
34795
35294
|
__publicField(this, "aunPath");
|
|
@@ -34975,7 +35474,7 @@ var AIDStore = class {
|
|
|
34975
35474
|
listAsync() {
|
|
34976
35475
|
return this.list();
|
|
34977
35476
|
}
|
|
34978
|
-
async
|
|
35477
|
+
async registerIdentity(aid, uploadDefault = true) {
|
|
34979
35478
|
const target = String(aid ?? "").trim();
|
|
34980
35479
|
try {
|
|
34981
35480
|
if (!target) throw new ValidationError("AIDStore.register requires 'aid'");
|
|
@@ -34993,7 +35492,7 @@ var AIDStore = class {
|
|
|
34993
35492
|
});
|
|
34994
35493
|
}
|
|
34995
35494
|
await this._persistGatewayUrl(target, gatewayUrl);
|
|
34996
|
-
await this.uploadAgentMd(target, buildDefaultAgentMd(target));
|
|
35495
|
+
if (uploadDefault) await this.uploadAgentMd(target, buildDefaultAgentMd(target));
|
|
34997
35496
|
return resultOk({ registered: true });
|
|
34998
35497
|
} catch (exc) {
|
|
34999
35498
|
if (exc instanceof IdentityConflictError) {
|
|
@@ -35009,6 +35508,187 @@ var AIDStore = class {
|
|
|
35009
35508
|
return resultErr(SERVER_ERROR, message, exc);
|
|
35010
35509
|
}
|
|
35011
35510
|
}
|
|
35511
|
+
async register(aidOrParams) {
|
|
35512
|
+
if (typeof aidOrParams === "string") return await this.registerIdentity(aidOrParams);
|
|
35513
|
+
const params2 = aidOrParams;
|
|
35514
|
+
const target = String(params2?.aid ?? "").trim();
|
|
35515
|
+
const aidType = String(params2?.type ?? "").trim().toLowerCase();
|
|
35516
|
+
const owner = String(params2?.owner ?? params2?.owner_aid ?? "").trim();
|
|
35517
|
+
if (!target || !aidType) return resultErr(INVALID_AID_FORMAT, "register requires aid and type");
|
|
35518
|
+
try {
|
|
35519
|
+
const identity = await this.registerIdentity(target, false);
|
|
35520
|
+
if (!identity.ok) return identity;
|
|
35521
|
+
const gatewayUrl = await this._resolveGateway(target);
|
|
35522
|
+
const prepareParams = { aid: target, type: aidType };
|
|
35523
|
+
if (owner) prepareParams.owner = owner;
|
|
35524
|
+
const idempotencyKey = params2.idempotencyKey ?? params2.idempotency_key;
|
|
35525
|
+
if (idempotencyKey) prepareParams.idempotency_key = idempotencyKey;
|
|
35526
|
+
const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_prepare", prepareParams);
|
|
35527
|
+
const operationId = String(prepared.operation_id ?? "").trim();
|
|
35528
|
+
if (!operationId) throw new ValidationError("aid_register_prepare response missing operation_id");
|
|
35529
|
+
let subjectSignature = params2.subjectSignature ?? params2.subject_signature;
|
|
35530
|
+
const ownerSignature = params2.ownerSignature ?? params2.owner_signature;
|
|
35531
|
+
if (!subjectSignature && owner) {
|
|
35532
|
+
const loaded = await this.load(target);
|
|
35533
|
+
if (loaded.ok) {
|
|
35534
|
+
const statement = OwnershipStatement.create(String(prepared.proof_id ?? ""), target, owner);
|
|
35535
|
+
const signed = await signOwnershipStatement({ certFingerprint: loaded.data.aid.certFingerprint, sign: (payload) => loaded.data.aid.sign(payload) }, statement);
|
|
35536
|
+
subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
|
|
35537
|
+
}
|
|
35538
|
+
}
|
|
35539
|
+
const signatures = {};
|
|
35540
|
+
if (subjectSignature) signatures.subject_signature = subjectSignature;
|
|
35541
|
+
if (ownerSignature) signatures.owner_signature = ownerSignature;
|
|
35542
|
+
let response = prepared;
|
|
35543
|
+
if (Object.keys(signatures).length > 0) response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_submit", { operation_id: operationId, ...signatures });
|
|
35544
|
+
const state = String(response.state ?? response.operation_state ?? "").toLowerCase();
|
|
35545
|
+
const complete = !owner || owner === target || !!ownerSignature && !!subjectSignature;
|
|
35546
|
+
if (complete && state === "agent_md_pending") {
|
|
35547
|
+
let content = params2.agentMd ?? params2.agent_md ?? buildDefaultAgentMd(target, { type: aidType });
|
|
35548
|
+
if (owner) {
|
|
35549
|
+
const proof = { proof_id: prepared.proof_id, subject_aid: target, owner_aid: owner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: subjectSignature };
|
|
35550
|
+
if (owner !== target) proof.owner_signature = ownerSignature;
|
|
35551
|
+
content = ownerAgentMd(content, owner, proof);
|
|
35552
|
+
}
|
|
35553
|
+
const uploaded = await this.uploadAgentMd(target, content, { ownerOperationId: operationId });
|
|
35554
|
+
if (!uploaded.ok) return uploaded;
|
|
35555
|
+
response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_register_activate", { operation_id: operationId });
|
|
35556
|
+
}
|
|
35557
|
+
return resultOk(response);
|
|
35558
|
+
} catch (exc) {
|
|
35559
|
+
if (exc instanceof ValidationError) return resultErr(INVALID_AID_FORMAT, exc.message, exc);
|
|
35560
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35561
|
+
}
|
|
35562
|
+
}
|
|
35563
|
+
async create(params2) {
|
|
35564
|
+
return await this.register(params2);
|
|
35565
|
+
}
|
|
35566
|
+
async bindAid(params2) {
|
|
35567
|
+
const target = String(params2?.aid ?? "").trim();
|
|
35568
|
+
const aidType = String(params2?.type ?? "").trim().toLowerCase();
|
|
35569
|
+
const owner = String(params2?.owner ?? params2?.owner_aid ?? "").trim();
|
|
35570
|
+
if (!target || !aidType) return resultErr(INVALID_AID_FORMAT, "bindAid requires aid and type");
|
|
35571
|
+
try {
|
|
35572
|
+
this._registerFlow.validateAidName(target);
|
|
35573
|
+
const gatewayUrl = await this._resolveGateway(target);
|
|
35574
|
+
const prepareParams = { aid: target, type: aidType };
|
|
35575
|
+
if (owner) prepareParams.owner = owner;
|
|
35576
|
+
const idempotencyKey = params2.idempotencyKey ?? params2.idempotency_key;
|
|
35577
|
+
if (idempotencyKey) prepareParams.idempotency_key = idempotencyKey;
|
|
35578
|
+
const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_prepare", prepareParams);
|
|
35579
|
+
const operationId = String(prepared.operation_id ?? "").trim();
|
|
35580
|
+
if (!operationId) throw new ValidationError("aid_bind_prepare response missing operation_id");
|
|
35581
|
+
const signatures = {};
|
|
35582
|
+
let subjectSignature = params2.subjectSignature ?? params2.subject_signature;
|
|
35583
|
+
if (!subjectSignature && owner) {
|
|
35584
|
+
const loaded = await this.load(target);
|
|
35585
|
+
if (loaded.ok) {
|
|
35586
|
+
const statement = OwnershipStatement.create(String(prepared.proof_id ?? ""), target, owner);
|
|
35587
|
+
const signed = await signOwnershipStatement(loaded.data.aid, statement);
|
|
35588
|
+
subjectSignature = { cert_fingerprint: signed.cert_fingerprint, signature: signed.signature };
|
|
35589
|
+
}
|
|
35590
|
+
}
|
|
35591
|
+
if (subjectSignature) signatures.subject_signature = subjectSignature;
|
|
35592
|
+
const ownerSignature = params2.ownerSignature ?? params2.owner_signature;
|
|
35593
|
+
if (ownerSignature) signatures.owner_signature = ownerSignature;
|
|
35594
|
+
let response = prepared;
|
|
35595
|
+
if (Object.keys(signatures).length > 0) {
|
|
35596
|
+
response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_submit", { operation_id: operationId, ...signatures });
|
|
35597
|
+
}
|
|
35598
|
+
const agentMd = params2.agentMd ?? params2.agent_md;
|
|
35599
|
+
if (agentMd !== void 0) {
|
|
35600
|
+
const uploaded = await this.uploadAgentMd(target, agentMd, {
|
|
35601
|
+
ownerOperationId: operationId
|
|
35602
|
+
});
|
|
35603
|
+
if (!uploaded.ok) return uploaded;
|
|
35604
|
+
}
|
|
35605
|
+
const state = String(response.state ?? response.operation_state ?? "").toLowerCase();
|
|
35606
|
+
if (state === "agent_md_pending" || !Object.keys(signatures).length && !owner && String(prepared.state ?? "") === "agent_md_pending") {
|
|
35607
|
+
response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_activate", { operation_id: operationId });
|
|
35608
|
+
}
|
|
35609
|
+
return resultOk(response);
|
|
35610
|
+
} catch (exc) {
|
|
35611
|
+
if (exc instanceof ValidationError) return resultErr(INVALID_AID_FORMAT, exc.message, exc);
|
|
35612
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35613
|
+
}
|
|
35614
|
+
}
|
|
35615
|
+
async getAidBindingStatus(aid) {
|
|
35616
|
+
const target = String(aid ?? "").trim();
|
|
35617
|
+
if (!target) return resultErr(INVALID_AID_FORMAT, "getAidBindingStatus requires aid");
|
|
35618
|
+
try {
|
|
35619
|
+
this._registerFlow.validateAidName(target);
|
|
35620
|
+
const gatewayUrl = await this._resolveGateway(target);
|
|
35621
|
+
return resultOk(await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_bind_status", { aid: target }));
|
|
35622
|
+
} catch (exc) {
|
|
35623
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35624
|
+
}
|
|
35625
|
+
}
|
|
35626
|
+
async resumeAidBinding(operationId) {
|
|
35627
|
+
const target = String(operationId ?? "").trim();
|
|
35628
|
+
if (!target) return resultErr(INVALID_AID_FORMAT, "resumeAidBinding requires operationId");
|
|
35629
|
+
try {
|
|
35630
|
+
const gatewayUrl = await this._resolveGateway(target);
|
|
35631
|
+
let response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_operation_get", { operation_id: target });
|
|
35632
|
+
if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending") {
|
|
35633
|
+
const method = String(response.kind ?? "").toLowerCase() === "owner_change" ? "auth.owner_change_activate" : "auth.aid_bind_activate";
|
|
35634
|
+
response = await this._registerFlow.shortRpc(gatewayUrl, method, { operation_id: target });
|
|
35635
|
+
}
|
|
35636
|
+
return resultOk(response);
|
|
35637
|
+
} catch (exc) {
|
|
35638
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35639
|
+
}
|
|
35640
|
+
}
|
|
35641
|
+
async bind_aid(params2) {
|
|
35642
|
+
return this.bindAid(params2);
|
|
35643
|
+
}
|
|
35644
|
+
async get_aid_binding_status(aid) {
|
|
35645
|
+
return this.getAidBindingStatus(aid);
|
|
35646
|
+
}
|
|
35647
|
+
async resume_aid_binding(operationId) {
|
|
35648
|
+
return this.resumeAidBinding(operationId);
|
|
35649
|
+
}
|
|
35650
|
+
async changeOwner(params2) {
|
|
35651
|
+
const subject = String(params2?.subject_aid ?? params2?.aid ?? "").trim();
|
|
35652
|
+
const newOwner = String(params2?.new_owner_aid ?? params2?.new_owner ?? "").trim();
|
|
35653
|
+
if (!subject || !newOwner) return resultErr(INVALID_AID_FORMAT, "changeOwner requires subject_aid and new_owner_aid");
|
|
35654
|
+
try {
|
|
35655
|
+
this._registerFlow.validateAidName(subject);
|
|
35656
|
+
const gatewayUrl = await this._resolveGateway(subject);
|
|
35657
|
+
const prepare = { subject_aid: subject, new_owner_aid: newOwner };
|
|
35658
|
+
if (params2.current_owner_aid ?? params2.owner_aid) prepare.current_owner_aid = params2.current_owner_aid ?? params2.owner_aid;
|
|
35659
|
+
if (params2.expected_relation_version !== void 0) prepare.expected_relation_version = params2.expected_relation_version;
|
|
35660
|
+
if (params2.idempotencyKey ?? params2.idempotency_key) prepare.idempotency_key = params2.idempotencyKey ?? params2.idempotency_key;
|
|
35661
|
+
const prepared = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_prepare", prepare);
|
|
35662
|
+
const operationId = String(prepared.operation_id ?? "").trim();
|
|
35663
|
+
if (!operationId) throw new ValidationError("owner_change_prepare response missing operation_id");
|
|
35664
|
+
const signatures = {};
|
|
35665
|
+
for (const key of ["old_owner_signature", "subject_signature", "new_owner_signature"]) if (params2[key]) signatures[key] = params2[key];
|
|
35666
|
+
let response = prepared;
|
|
35667
|
+
if (Object.keys(signatures).length > 0) response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_submit", { operation_id: operationId, ...signatures });
|
|
35668
|
+
if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending" && (params2.agentMd ?? params2.agent_md) !== void 0) {
|
|
35669
|
+
const proof = { proof_id: prepared.proof_id, subject_aid: subject, owner_aid: newOwner, fact_hash: prepared.fact_hash, statement_hash: prepared.statement_hash, subject_signature: params2.subject_signature, owner_signature: params2.new_owner_signature };
|
|
35670
|
+
const content = ownerAgentMd(String(params2.agentMd ?? params2.agent_md), newOwner, proof);
|
|
35671
|
+
const uploaded = await this.uploadAgentMd(subject, content, { ownerOperationId: operationId });
|
|
35672
|
+
if (!uploaded.ok) return uploaded;
|
|
35673
|
+
response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_activate", { operation_id: operationId });
|
|
35674
|
+
}
|
|
35675
|
+
return resultOk(response);
|
|
35676
|
+
} catch (exc) {
|
|
35677
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35678
|
+
}
|
|
35679
|
+
}
|
|
35680
|
+
async resumeOwnerChange(operationId, aid) {
|
|
35681
|
+
const id = String(operationId ?? "").trim();
|
|
35682
|
+
if (!id) return resultErr(INVALID_AID_FORMAT, "resumeOwnerChange requires operationId");
|
|
35683
|
+
try {
|
|
35684
|
+
const gatewayUrl = await this._resolveGateway(String(aid ?? id));
|
|
35685
|
+
let response = await this._registerFlow.shortRpc(gatewayUrl, "auth.aid_operation_get", { operation_id: id });
|
|
35686
|
+
if (String(response.state ?? response.operation_state ?? "").toLowerCase() === "agent_md_pending") response = await this._registerFlow.shortRpc(gatewayUrl, "auth.owner_change_activate", { operation_id: id });
|
|
35687
|
+
return resultOk(response);
|
|
35688
|
+
} catch (exc) {
|
|
35689
|
+
return resultErr(NETWORK_ERROR, exc instanceof Error ? exc.message : String(exc), exc);
|
|
35690
|
+
}
|
|
35691
|
+
}
|
|
35012
35692
|
async exists(aid) {
|
|
35013
35693
|
const target = String(aid ?? "").trim();
|
|
35014
35694
|
try {
|
|
@@ -35135,7 +35815,8 @@ var AIDStore = class {
|
|
|
35135
35815
|
accessTokenResolver: async () => this._uploadAgentMdToken(uploader, await this._resolveGateway(uploaderTarget)),
|
|
35136
35816
|
aidValidator: (value) => this._registerFlow.validateAidName(value)
|
|
35137
35817
|
});
|
|
35138
|
-
|
|
35818
|
+
const ownerOperationId = options.ownerOperationId ?? options.owner_operation_id;
|
|
35819
|
+
return resultOk(await manager.upload(content, ownerOperationId));
|
|
35139
35820
|
} catch (exc) {
|
|
35140
35821
|
return agentMdFailure(exc);
|
|
35141
35822
|
}
|
|
@@ -36694,6 +37375,7 @@ export {
|
|
|
36694
37375
|
MessageFacade,
|
|
36695
37376
|
MessageThoughtFacade,
|
|
36696
37377
|
NotFoundError,
|
|
37378
|
+
OwnershipStatement,
|
|
36697
37379
|
PermissionError,
|
|
36698
37380
|
ProtectedHeaders,
|
|
36699
37381
|
ROOT_CA_PEM,
|
|
@@ -36744,13 +37426,18 @@ export {
|
|
|
36744
37426
|
prepareGroupSettingsWithIndex,
|
|
36745
37427
|
resultErr,
|
|
36746
37428
|
resultOk,
|
|
37429
|
+
signOwnershipStatement,
|
|
36747
37430
|
splitGroupId,
|
|
36748
37431
|
validateAIDFormat,
|
|
36749
37432
|
validateAgentMdDocument,
|
|
36750
37433
|
validateGroupAIDFormat,
|
|
36751
37434
|
validateGroupIDFormat,
|
|
37435
|
+
validateOwnershipFrontmatter,
|
|
37436
|
+
validateOwnershipProof,
|
|
36752
37437
|
verifyAgentMdUpload,
|
|
36753
|
-
verifyGroupIndex
|
|
37438
|
+
verifyGroupIndex,
|
|
37439
|
+
verifyOwnershipProof,
|
|
37440
|
+
verifyOwnershipSignature
|
|
36754
37441
|
};
|
|
36755
37442
|
/*! Bundled license information:
|
|
36756
37443
|
|