@agentunion/fastaun 0.3.5 → 0.3.6
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 +18 -0
- package/_packed_docs/CHANGELOG.md +18 -0
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +19 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +89 -0
- package/dist/auth.d.ts +18 -1
- package/dist/auth.js +28 -9
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +10 -0
- package/dist/client.js +192 -5
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/namespaces/auth.js +2 -0
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/secret-store/file-store.d.ts +5 -0
- package/dist/secret-store/file-store.js +32 -13
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/transport.js +1 -1
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -411,6 +411,7 @@ export declare class AUNClient {
|
|
|
411
411
|
skipAutoAck?: boolean;
|
|
412
412
|
gateLocked?: boolean;
|
|
413
413
|
scheduleFollowup?: boolean;
|
|
414
|
+
force?: boolean;
|
|
414
415
|
}): Promise<Array<Record<string, unknown>>>;
|
|
415
416
|
/** V2 P2P ack,并触发旧 SPK 销毁自检。 */
|
|
416
417
|
ackV2(upToSeq?: number): Promise<unknown>;
|
|
@@ -437,6 +438,15 @@ export declare class AUNClient {
|
|
|
437
438
|
private _attachV2EnvelopeMetadata;
|
|
438
439
|
private _attachV2EnvelopeMetadataFromSource;
|
|
439
440
|
private _extractV2EnvelopeFromSource;
|
|
441
|
+
private _truthyBool;
|
|
442
|
+
private _encryptedPushEnvelope;
|
|
443
|
+
private _isEncryptedPushMessage;
|
|
444
|
+
private _isEncryptedEnvelopePayload;
|
|
445
|
+
private _isV2EncryptedEnvelopePayload;
|
|
446
|
+
private _safeUndecryptablePushEvent;
|
|
447
|
+
private _decryptEncryptedPushPayload;
|
|
448
|
+
private _publishEncryptedPushAsUndecryptable;
|
|
449
|
+
private _publishEncryptedPushMessage;
|
|
440
450
|
private _metadataWithoutAuth;
|
|
441
451
|
private _putMessageThoughtEncryptedV2;
|
|
442
452
|
private _putGroupThoughtEncryptedV2;
|
package/dist/client.js
CHANGED
|
@@ -1493,11 +1493,12 @@ export class AUNClient {
|
|
|
1493
1493
|
if (method === 'message.pull' || method === 'message.v2.pull') {
|
|
1494
1494
|
await this._ensureV2SessionReady('message.pull');
|
|
1495
1495
|
const skipAutoAck = p._skip_auto_ack === true || p.skip_auto_ack === true;
|
|
1496
|
+
const force = p.force === true;
|
|
1496
1497
|
const afterSeq = Number(p.after_seq ?? 0) || 0;
|
|
1497
1498
|
const limit = Number(p.limit ?? 50) || 50;
|
|
1498
1499
|
const messages = skipAutoAck
|
|
1499
|
-
? await runWithRpcPriority(() => this.pullV2(afterSeq, limit, { skipAutoAck: true, gateLocked: true }))
|
|
1500
|
-
: await runWithRpcPriority(() => this.pullV2(afterSeq, limit, { gateLocked: true }));
|
|
1500
|
+
? await runWithRpcPriority(() => this.pullV2(afterSeq, limit, { skipAutoAck: true, gateLocked: true, force }))
|
|
1501
|
+
: await runWithRpcPriority(() => this.pullV2(afterSeq, limit, { gateLocked: true, force }));
|
|
1501
1502
|
return { messages };
|
|
1502
1503
|
}
|
|
1503
1504
|
if (method === 'message.ack' || method === 'message.v2.ack') {
|
|
@@ -1762,6 +1763,7 @@ export class AUNClient {
|
|
|
1762
1763
|
this._clientLog.debug(`P2P push filtered by instance: message_id=${String(msg.message_id ?? '')}, seq=${String(msg.seq ?? '')}, target_device=${String(msg.device_id ?? '')}, target_slot=${String(msg.slot_id ?? '')}, local_device=${this._deviceId}, local_slot=${this._slotId}`);
|
|
1763
1764
|
return;
|
|
1764
1765
|
}
|
|
1766
|
+
const encryptedPush = this._isEncryptedPushMessage(msg);
|
|
1765
1767
|
// P2P 空洞检测
|
|
1766
1768
|
const seq = msg.seq;
|
|
1767
1769
|
if (seq !== undefined && seq !== null && this._aid) {
|
|
@@ -1770,7 +1772,9 @@ export class AUNClient {
|
|
|
1770
1772
|
if (seq > 0)
|
|
1771
1773
|
this._seqTracker.updateMaxSeen(ns, seq);
|
|
1772
1774
|
const contigBefore = this._seqTracker.getContiguousSeq(ns);
|
|
1773
|
-
const published =
|
|
1775
|
+
const published = encryptedPush
|
|
1776
|
+
? await this._publishEncryptedPushMessage('message.received', 'message.undecryptable', ns, seq, msg, false)
|
|
1777
|
+
: await this._publishOrderedMessage('message.received', ns, seq, msg);
|
|
1774
1778
|
const contigAfter = this._seqTracker.getContiguousSeq(ns);
|
|
1775
1779
|
const needPull = Number(seq) > contigAfter && !published;
|
|
1776
1780
|
if (needPull) {
|
|
@@ -1790,8 +1794,14 @@ export class AUNClient {
|
|
|
1790
1794
|
// 即时持久化 cursor,异常断连后不回退
|
|
1791
1795
|
if (contigAfter !== contigBefore)
|
|
1792
1796
|
this._saveSeqTrackerState();
|
|
1797
|
+
if (encryptedPush)
|
|
1798
|
+
return;
|
|
1793
1799
|
}
|
|
1794
1800
|
else {
|
|
1801
|
+
if (encryptedPush) {
|
|
1802
|
+
await this._publishEncryptedPushMessage('message.received', 'message.undecryptable', '', seq ?? 0, msg, false);
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1795
1805
|
// V2-only:普通 _raw.message.received 只承载明文;V2 密文由 peer.v2.message_received 通知触发 pull。
|
|
1796
1806
|
await this._publishAppEvent('message.received', msg, 'push');
|
|
1797
1807
|
}
|
|
@@ -1850,13 +1860,16 @@ export class AUNClient {
|
|
|
1850
1860
|
});
|
|
1851
1861
|
return;
|
|
1852
1862
|
}
|
|
1863
|
+
const encryptedPush = this._isEncryptedPushMessage(msg);
|
|
1853
1864
|
if (groupId && seq !== undefined && seq !== null) {
|
|
1854
1865
|
const ns = `group:${groupId}`;
|
|
1855
1866
|
// Push 只先更新 maxSeenSeq;contiguous_seq 是已交付游标,必须等应用层发布返回后再推进。
|
|
1856
1867
|
if (seq > 0)
|
|
1857
1868
|
this._seqTracker.updateMaxSeen(ns, seq);
|
|
1858
1869
|
const contigBefore = this._seqTracker.getContiguousSeq(ns);
|
|
1859
|
-
const published =
|
|
1870
|
+
const published = encryptedPush
|
|
1871
|
+
? await this._publishEncryptedPushMessage('group.message_created', 'group.message_undecryptable', ns, seq, msg, true)
|
|
1872
|
+
: await this._publishOrderedMessage('group.message_created', ns, seq, msg);
|
|
1860
1873
|
const contigAfter = this._seqTracker.getContiguousSeq(ns);
|
|
1861
1874
|
const needPull = Number(seq) > contigAfter && !published;
|
|
1862
1875
|
if (needPull) {
|
|
@@ -1874,8 +1887,14 @@ export class AUNClient {
|
|
|
1874
1887
|
}
|
|
1875
1888
|
if (contigAfter !== contigBefore)
|
|
1876
1889
|
this._saveSeqTrackerState();
|
|
1890
|
+
if (encryptedPush)
|
|
1891
|
+
return;
|
|
1877
1892
|
}
|
|
1878
1893
|
else {
|
|
1894
|
+
if (encryptedPush) {
|
|
1895
|
+
await this._publishEncryptedPushMessage('group.message_created', 'group.message_undecryptable', '', seq ?? 0, msg, true);
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1879
1898
|
// V2-only:普通 group.message_created 只承载明文;V2 密文由 group.v2.message_created 通知触发 pull。
|
|
1880
1899
|
await this._publishAppEvent('group.message_created', msg, 'group-push');
|
|
1881
1900
|
}
|
|
@@ -3749,6 +3768,27 @@ export class AUNClient {
|
|
|
3749
3768
|
identity = null;
|
|
3750
3769
|
}
|
|
3751
3770
|
}
|
|
3771
|
+
if (!identity?.private_key_pem) {
|
|
3772
|
+
// fallback:缓存的 identity 可能被 instanceState 污染,重新从 keystore 加载
|
|
3773
|
+
try {
|
|
3774
|
+
identity = this._keystore.loadIdentity(this._aid);
|
|
3775
|
+
if (identity?.private_key_pem) {
|
|
3776
|
+
this._identity = identity;
|
|
3777
|
+
this._clientLog.warn('V2 session init: identity cache was stale, reloaded from keystore');
|
|
3778
|
+
// 重新持久化 instance_state,清理脏数据
|
|
3779
|
+
const persistIdentity = this._auth._persistIdentity;
|
|
3780
|
+
if (typeof persistIdentity === 'function') {
|
|
3781
|
+
try {
|
|
3782
|
+
persistIdentity.call(this._auth, identity);
|
|
3783
|
+
}
|
|
3784
|
+
catch { /* best-effort */ }
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
catch {
|
|
3789
|
+
identity = null;
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3752
3792
|
if (!identity?.private_key_pem) {
|
|
3753
3793
|
this._clientLog.warn('V2 session init skipped: no AID private key');
|
|
3754
3794
|
return;
|
|
@@ -4212,7 +4252,7 @@ export class AUNClient {
|
|
|
4212
4252
|
}
|
|
4213
4253
|
const decrypted = [];
|
|
4214
4254
|
let totalRawCount = 0;
|
|
4215
|
-
let nextAfterSeq = afterSeq || (ns ? this._seqTracker.getContiguousSeq(ns) : 0);
|
|
4255
|
+
let nextAfterSeq = opts?.force ? afterSeq : (afterSeq || (ns ? this._seqTracker.getContiguousSeq(ns) : 0));
|
|
4216
4256
|
let pageCount = 0;
|
|
4217
4257
|
const maxPages = 100;
|
|
4218
4258
|
while (pageCount < maxPages) {
|
|
@@ -4221,6 +4261,7 @@ export class AUNClient {
|
|
|
4221
4261
|
const result = await this._callRawV2Rpc('message.v2.pull', {
|
|
4222
4262
|
after_seq: nextAfterSeq,
|
|
4223
4263
|
limit,
|
|
4264
|
+
...(opts?.force ? { force: true } : {}),
|
|
4224
4265
|
});
|
|
4225
4266
|
const messages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
4226
4267
|
totalRawCount += messages.length;
|
|
@@ -4887,6 +4928,12 @@ export class AUNClient {
|
|
|
4887
4928
|
encrypted: true,
|
|
4888
4929
|
e2ee,
|
|
4889
4930
|
};
|
|
4931
|
+
const explicitDirection = String(msg.direction ?? '').trim();
|
|
4932
|
+
result.direction = explicitDirection || (fromAid && fromAid === this._aid ? 'outbound_sync' : 'inbound');
|
|
4933
|
+
if (msg.device_id !== undefined)
|
|
4934
|
+
result.device_id = msg.device_id;
|
|
4935
|
+
if (msg.slot_id !== undefined)
|
|
4936
|
+
result.slot_id = msg.slot_id;
|
|
4890
4937
|
this._attachV2EnvelopeMetadata(result, e2ee);
|
|
4891
4938
|
this._logMessageDebug('decrypt-ok', 'v2.decrypt', groupIdForKeys ? 'group.message_created' : 'message.received', result);
|
|
4892
4939
|
return result;
|
|
@@ -4953,6 +5000,146 @@ export class AUNClient {
|
|
|
4953
5000
|
}
|
|
4954
5001
|
return null;
|
|
4955
5002
|
}
|
|
5003
|
+
_truthyBool(value) {
|
|
5004
|
+
if (value === true || value === 1)
|
|
5005
|
+
return true;
|
|
5006
|
+
if (typeof value === 'string') {
|
|
5007
|
+
const normalized = value.trim().toLowerCase();
|
|
5008
|
+
return normalized === 'true' || normalized === '1' || normalized === 'yes' || normalized === 'on';
|
|
5009
|
+
}
|
|
5010
|
+
return false;
|
|
5011
|
+
}
|
|
5012
|
+
_encryptedPushEnvelope(msg) {
|
|
5013
|
+
const payload = msg.payload;
|
|
5014
|
+
if (this._isEncryptedEnvelopePayload(payload))
|
|
5015
|
+
return payload;
|
|
5016
|
+
if (typeof msg.envelope_json === 'string' && msg.envelope_json.trim()) {
|
|
5017
|
+
try {
|
|
5018
|
+
const parsed = JSON.parse(msg.envelope_json);
|
|
5019
|
+
if (this._isEncryptedEnvelopePayload(parsed))
|
|
5020
|
+
return parsed;
|
|
5021
|
+
}
|
|
5022
|
+
catch {
|
|
5023
|
+
return null;
|
|
5024
|
+
}
|
|
5025
|
+
}
|
|
5026
|
+
return null;
|
|
5027
|
+
}
|
|
5028
|
+
_isEncryptedPushMessage(msg) {
|
|
5029
|
+
if (this._truthyBool(msg.encrypted))
|
|
5030
|
+
return true;
|
|
5031
|
+
return this._encryptedPushEnvelope(msg) !== null;
|
|
5032
|
+
}
|
|
5033
|
+
_isEncryptedEnvelopePayload(payload) {
|
|
5034
|
+
if (!isJsonObject(payload))
|
|
5035
|
+
return false;
|
|
5036
|
+
const envelope = payload;
|
|
5037
|
+
const payloadType = String(envelope.type ?? '').trim();
|
|
5038
|
+
if (payloadType.startsWith('e2ee.'))
|
|
5039
|
+
return true;
|
|
5040
|
+
if (!String(envelope.ciphertext ?? '').trim())
|
|
5041
|
+
return false;
|
|
5042
|
+
return envelope.nonce !== undefined
|
|
5043
|
+
|| envelope.tag !== undefined
|
|
5044
|
+
|| envelope.recipient !== undefined
|
|
5045
|
+
|| envelope.recipients !== undefined
|
|
5046
|
+
|| envelope.wrapped_key !== undefined
|
|
5047
|
+
|| envelope.recipients_digest !== undefined;
|
|
5048
|
+
}
|
|
5049
|
+
_isV2EncryptedEnvelopePayload(envelope) {
|
|
5050
|
+
if (!envelope)
|
|
5051
|
+
return false;
|
|
5052
|
+
const payloadType = String(envelope.type ?? '').trim();
|
|
5053
|
+
if (payloadType === 'e2ee.p2p_encrypted' || payloadType === 'e2ee.group_encrypted')
|
|
5054
|
+
return true;
|
|
5055
|
+
return String(envelope.version ?? '').trim().toLowerCase() === 'v2' && payloadType.startsWith('e2ee.');
|
|
5056
|
+
}
|
|
5057
|
+
_safeUndecryptablePushEvent(msg, group) {
|
|
5058
|
+
const event = {
|
|
5059
|
+
message_id: msg.message_id,
|
|
5060
|
+
from: msg.from,
|
|
5061
|
+
seq: msg.seq,
|
|
5062
|
+
timestamp: (msg.timestamp ?? msg.t_server),
|
|
5063
|
+
device_id: msg.device_id,
|
|
5064
|
+
slot_id: msg.slot_id,
|
|
5065
|
+
_decrypt_error: 'encrypted push payload is not decryptable on raw push path',
|
|
5066
|
+
_decrypt_stage: 'push_envelope',
|
|
5067
|
+
};
|
|
5068
|
+
if (group) {
|
|
5069
|
+
event.group_id = msg.group_id;
|
|
5070
|
+
}
|
|
5071
|
+
else {
|
|
5072
|
+
event.to = msg.to;
|
|
5073
|
+
}
|
|
5074
|
+
const envelope = this._encryptedPushEnvelope(msg);
|
|
5075
|
+
if (envelope) {
|
|
5076
|
+
event._envelope_type = String(envelope.type ?? '');
|
|
5077
|
+
event._suite = String(envelope.suite ?? '');
|
|
5078
|
+
if (this._isV2EncryptedEnvelopePayload(envelope)) {
|
|
5079
|
+
this._attachV2EnvelopeMetadata(event, this._v2E2eeMeta(envelope));
|
|
5080
|
+
}
|
|
5081
|
+
}
|
|
5082
|
+
return event;
|
|
5083
|
+
}
|
|
5084
|
+
async _decryptEncryptedPushPayload(msg, group) {
|
|
5085
|
+
const envelope = this._encryptedPushEnvelope(msg);
|
|
5086
|
+
if (!this._isV2EncryptedEnvelopePayload(envelope))
|
|
5087
|
+
return null;
|
|
5088
|
+
const aad = isJsonObject(envelope.aad) ? envelope.aad : {};
|
|
5089
|
+
const fromAid = String(msg.from_aid ?? msg.from ?? msg.sender_aid ?? aad.from ?? '').trim();
|
|
5090
|
+
const plaintext = await this._decryptV2EnvelopeForThought({ envelope, fromAid });
|
|
5091
|
+
if (!plaintext)
|
|
5092
|
+
return null;
|
|
5093
|
+
const e2ee = this._v2E2eeMeta(envelope);
|
|
5094
|
+
const result = {
|
|
5095
|
+
message_id: String(msg.message_id ?? ''),
|
|
5096
|
+
from: fromAid,
|
|
5097
|
+
seq: msg.seq,
|
|
5098
|
+
timestamp: (msg.t_server ?? msg.timestamp),
|
|
5099
|
+
payload: plaintext,
|
|
5100
|
+
encrypted: true,
|
|
5101
|
+
e2ee,
|
|
5102
|
+
};
|
|
5103
|
+
result.direction = fromAid && fromAid === this._aid ? 'outbound_sync' : 'inbound';
|
|
5104
|
+
if (msg.t_server !== undefined)
|
|
5105
|
+
result.t_server = msg.t_server;
|
|
5106
|
+
if (msg.device_id !== undefined)
|
|
5107
|
+
result.device_id = msg.device_id;
|
|
5108
|
+
if (msg.slot_id !== undefined)
|
|
5109
|
+
result.slot_id = msg.slot_id;
|
|
5110
|
+
if (group) {
|
|
5111
|
+
result.group_id = (msg.group_id ?? aad.group_id ?? envelope.group_id);
|
|
5112
|
+
}
|
|
5113
|
+
else {
|
|
5114
|
+
result.to = (msg.to ?? this._aid ?? '');
|
|
5115
|
+
}
|
|
5116
|
+
this._attachV2EnvelopeMetadata(result, e2ee);
|
|
5117
|
+
this._logMessageDebug('decrypt-ok', 'push.encrypted', group ? 'group.message_created' : 'message.received', result);
|
|
5118
|
+
return result;
|
|
5119
|
+
}
|
|
5120
|
+
async _publishEncryptedPushAsUndecryptable(event, ns, seq, msg, group) {
|
|
5121
|
+
const safeEvent = this._safeUndecryptablePushEvent(msg, group);
|
|
5122
|
+
this._logMessageDebug('decrypt-fail', 'push.encrypted', event, safeEvent);
|
|
5123
|
+
if (ns) {
|
|
5124
|
+
return await this._publishOrderedMessage(event, ns, seq, safeEvent);
|
|
5125
|
+
}
|
|
5126
|
+
const published = this._publishAppEvent(event, safeEvent, 'push');
|
|
5127
|
+
if (isPromiseLike(published))
|
|
5128
|
+
await published;
|
|
5129
|
+
return true;
|
|
5130
|
+
}
|
|
5131
|
+
async _publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group) {
|
|
5132
|
+
const decrypted = await this._decryptEncryptedPushPayload(msg, group);
|
|
5133
|
+
if (decrypted) {
|
|
5134
|
+
if (ns)
|
|
5135
|
+
return await this._publishOrderedMessage(normalEvent, ns, seq, decrypted);
|
|
5136
|
+
const published = this._publishAppEvent(normalEvent, decrypted, 'push');
|
|
5137
|
+
if (isPromiseLike(published))
|
|
5138
|
+
await published;
|
|
5139
|
+
return true;
|
|
5140
|
+
}
|
|
5141
|
+
return await this._publishEncryptedPushAsUndecryptable(undecryptableEvent, ns, seq, msg, group);
|
|
5142
|
+
}
|
|
4956
5143
|
_metadataWithoutAuth(value) {
|
|
4957
5144
|
const candidate = value;
|
|
4958
5145
|
if (!isJsonObject(candidate))
|