@agentunion/fastaun-browser 0.4.3 → 0.4.4
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 +190 -178
- package/_packed_docs/AUN_SDK_0.4.0_/350/256/276/350/256/241/345/257/271/346/257/224/345/210/206/346/236/220.md +194 -194
- package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/345/256/236/346/226/275/350/256/241/345/210/222.md +596 -596
- package/_packed_docs/AUN_SDK_/351/207/215/346/236/204/350/256/276/350/256/241/346/226/271/346/241/210_v3.md +1698 -1697
- package/_packed_docs/CHANGELOG.md +190 -178
- package/_packed_docs/INDEX.md +17 -17
- package/_packed_docs/KITE_DOCS_GUIDE.md +11 -11
- package/_packed_docs/agent.md/SCHEMA.md +49 -49
- package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +22 -22
- package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +327 -327
- package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +686 -686
- package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +542 -542
- package/_packed_docs/design/E2EE_V2/347/256/200/345/214/226/344/270/2721DH/345/212/240Per-AID_Wrap/346/226/271/346/241/210.md +124 -124
- package/_packed_docs/design//350/267/250/350/257/255/350/250/200/345/256/271/345/231/250E2E/346/265/213/350/257/225/346/226/271/346/241/210.md +665 -665
- 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 +2 -2
- package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +170 -170
- package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +419 -419
- package/_packed_docs/protocol/README.md +1 -1
- package/_packed_docs/protocol/aun-docs-guide.md +1 -1
- package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +15 -15
- package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +4 -4
- package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +98 -98
- package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +46 -46
- package/_packed_docs/protocol//351/231/204/345/275/225N-/345/210/206/345/270/203/345/274/217Trace/345/215/217/350/256/256.md +257 -257
- package/_packed_docs/python-sdk-v2-only-changelog.md +189 -189
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1 -0
- package/_packed_docs/sdk/09-payload-reference.md +13 -13
- package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +171 -171
- package/dist/aid.d.ts +2 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +7 -6
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +4 -0
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +237 -149
- package/dist/client.d.ts +7 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +238 -153
- package/dist/client.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -52,6 +52,14 @@ function getV2DeviceId(dev) {
|
|
|
52
52
|
}
|
|
53
53
|
return { present: false, value: '' };
|
|
54
54
|
}
|
|
55
|
+
function isAIDObject(value) {
|
|
56
|
+
const candidate = value;
|
|
57
|
+
return Boolean(candidate
|
|
58
|
+
&& typeof candidate === 'object'
|
|
59
|
+
&& typeof candidate.aid === 'string'
|
|
60
|
+
&& typeof candidate.aunPath === 'string'
|
|
61
|
+
&& typeof candidate.isPrivateKeyValid === 'function');
|
|
62
|
+
}
|
|
55
63
|
function sortObjectKeys(obj) {
|
|
56
64
|
if (obj === null || obj === undefined || typeof obj !== 'object')
|
|
57
65
|
return obj;
|
|
@@ -169,6 +177,20 @@ const DEFAULT_SESSION_OPTIONS = {
|
|
|
169
177
|
http: 30.0,
|
|
170
178
|
},
|
|
171
179
|
};
|
|
180
|
+
const PUBLIC_CONNECTION_OPTION_KEYS = new Set([
|
|
181
|
+
'auto_reconnect',
|
|
182
|
+
'connect_timeout',
|
|
183
|
+
'retry_initial_delay',
|
|
184
|
+
'retry_max_delay',
|
|
185
|
+
'retry_max_attempts',
|
|
186
|
+
'heartbeat_interval',
|
|
187
|
+
'call_timeout',
|
|
188
|
+
'connection_kind',
|
|
189
|
+
'short_ttl_ms',
|
|
190
|
+
'delivery_mode',
|
|
191
|
+
'extra_info',
|
|
192
|
+
'background_sync',
|
|
193
|
+
]);
|
|
172
194
|
const PROTECTED_HEADERS_METHODS = new Set([
|
|
173
195
|
'message.send',
|
|
174
196
|
'group.send',
|
|
@@ -656,6 +678,7 @@ export class AUNClient {
|
|
|
656
678
|
// V2 E2EE 状态
|
|
657
679
|
_v2Session;
|
|
658
680
|
_v2KeyStore;
|
|
681
|
+
_v2SessionInitInFlight = null;
|
|
659
682
|
_v2BootstrapCache = new Map();
|
|
660
683
|
_v2SenderIKPending = new Map();
|
|
661
684
|
_v2SenderIKFetching = new Set();
|
|
@@ -732,10 +755,10 @@ export class AUNClient {
|
|
|
732
755
|
_logDiscovery;
|
|
733
756
|
_logEvents;
|
|
734
757
|
constructor(aid) {
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
throw new ValidationError('AUNClient aid must be an AID object, not a string');
|
|
758
|
+
if (aid !== null && aid !== undefined && !isAIDObject(aid)) {
|
|
759
|
+
throw new ValidationError('AUNClient only accepts an AID object or no argument');
|
|
738
760
|
}
|
|
761
|
+
const inputAid = aid ?? null;
|
|
739
762
|
const rawConfig = {};
|
|
740
763
|
if (inputAid)
|
|
741
764
|
rawConfig.aun_path = inputAid.aunPath;
|
|
@@ -761,7 +784,7 @@ export class AUNClient {
|
|
|
761
784
|
this._clientLog.info(`AUNClient initialized: debug=${_debug} aunPath=${this.configModel.aunPath} aid=${initAid ?? '-'}`);
|
|
762
785
|
this._dispatcher = new EventDispatcher();
|
|
763
786
|
this._discovery = new GatewayDiscovery();
|
|
764
|
-
this._keystore = new IndexedDBKeyStore({
|
|
787
|
+
this._keystore = new IndexedDBKeyStore({});
|
|
765
788
|
this._slotId = inputAid?.slotId || 'default';
|
|
766
789
|
this._connectDeliveryMode = normalizeDeliveryModeConfig({ mode: 'fanout' });
|
|
767
790
|
this._defaultConnectDeliveryMode = { ...this._connectDeliveryMode };
|
|
@@ -790,7 +813,7 @@ export class AUNClient {
|
|
|
790
813
|
this._currentAid = inputAid;
|
|
791
814
|
this._identity = {
|
|
792
815
|
aid: inputAid.aid,
|
|
793
|
-
private_key_pem: inputAid.
|
|
816
|
+
private_key_pem: inputAid.privateKeyPem,
|
|
794
817
|
public_key_der_b64: inputAid.publicKey,
|
|
795
818
|
cert: inputAid.certPem,
|
|
796
819
|
};
|
|
@@ -1564,6 +1587,63 @@ export class AUNClient {
|
|
|
1564
1587
|
get lastError() { return this._lastError; }
|
|
1565
1588
|
/** 最近一次错误码(对齐 Python last_error_code) */
|
|
1566
1589
|
get lastErrorCode() { return this._lastErrorCode; }
|
|
1590
|
+
_applyAidRuntimeContext(aid) {
|
|
1591
|
+
const nextConfig = createConfig({
|
|
1592
|
+
aunPath: aid.aunPath,
|
|
1593
|
+
rootCaPem: aid.rootCaPath,
|
|
1594
|
+
verifySsl: aid.verifySsl,
|
|
1595
|
+
});
|
|
1596
|
+
Object.assign(this.configModel, nextConfig);
|
|
1597
|
+
this.config.aun_path = nextConfig.aunPath;
|
|
1598
|
+
this.config.root_ca_path = nextConfig.rootCaPem;
|
|
1599
|
+
this.config.seed_password = nextConfig.seedPassword;
|
|
1600
|
+
this._agentMdPath = this._agentMdDefaultRoot();
|
|
1601
|
+
this._agentMdCache.clear();
|
|
1602
|
+
this._agentMdFetchInflight.clear();
|
|
1603
|
+
this._peerCache.clear();
|
|
1604
|
+
this._certCache.clear();
|
|
1605
|
+
this._gatewayUrl = null;
|
|
1606
|
+
this._deviceId = aid.deviceId || getDeviceId();
|
|
1607
|
+
this._slotId = aid.slotId || 'default';
|
|
1608
|
+
this._logger = new AUNLogger({ debug: aid.debug, aunPath: nextConfig.aunPath });
|
|
1609
|
+
this._logger.bindDeviceId(this._deviceId);
|
|
1610
|
+
this._clientLog = this._logger.for('aun_core.client');
|
|
1611
|
+
this._logAuth = this._logger.for('aun_core.auth');
|
|
1612
|
+
this._logTransport = this._logger.for('aun_core.transport');
|
|
1613
|
+
this._logKeystore = this._logger.for('aun_core.keystore');
|
|
1614
|
+
this._logDiscovery = this._logger.for('aun_core.discovery');
|
|
1615
|
+
this._logEvents = this._logger.for('aun_core.events');
|
|
1616
|
+
this._discovery = new GatewayDiscovery();
|
|
1617
|
+
this._keystore = new IndexedDBKeyStore({});
|
|
1618
|
+
this._auth = new AuthFlow({
|
|
1619
|
+
keystore: this._keystore,
|
|
1620
|
+
crypto: new CryptoProvider(),
|
|
1621
|
+
aid: aid.aid,
|
|
1622
|
+
deviceId: this._deviceId,
|
|
1623
|
+
slotId: this._slotId,
|
|
1624
|
+
rootCaPem: nextConfig.rootCaPem,
|
|
1625
|
+
verifySsl: nextConfig.verifySsl,
|
|
1626
|
+
});
|
|
1627
|
+
this._transport = new RPCTransport({
|
|
1628
|
+
eventDispatcher: this._dispatcher,
|
|
1629
|
+
timeout: DEFAULT_SESSION_OPTIONS.timeouts.call,
|
|
1630
|
+
onDisconnect: (error, closeCode) => this._handleTransportDisconnect(error, closeCode),
|
|
1631
|
+
});
|
|
1632
|
+
this._transport.setMetaObserver((meta) => {
|
|
1633
|
+
void this._observeRpcMeta(meta).catch((exc) => {
|
|
1634
|
+
this._clientLog.debug(`agent.md meta observer skipped: ${String(exc)}`);
|
|
1635
|
+
});
|
|
1636
|
+
});
|
|
1637
|
+
this._auth.setLogger(this._logAuth);
|
|
1638
|
+
this._transport.setLogger(this._logTransport);
|
|
1639
|
+
this._dispatcher.setLogger(this._logEvents);
|
|
1640
|
+
if (typeof this._discovery.setLogger === 'function') {
|
|
1641
|
+
this._discovery.setLogger(this._logDiscovery);
|
|
1642
|
+
}
|
|
1643
|
+
if (typeof this._keystore.setLogger === 'function') {
|
|
1644
|
+
this._keystore.setLogger(this._logKeystore);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1567
1647
|
loadIdentity(aid) {
|
|
1568
1648
|
if (!aid?.isPrivateKeyValid())
|
|
1569
1649
|
throw new StateError('loadIdentity requires an AID with a valid private key');
|
|
@@ -1571,16 +1651,15 @@ export class AUNClient {
|
|
|
1571
1651
|
if (publicState !== ConnectionState.NO_IDENTITY && publicState !== ConnectionState.CLOSED) {
|
|
1572
1652
|
throw new StateError(`loadIdentity not allowed in state ${publicState}`);
|
|
1573
1653
|
}
|
|
1654
|
+
this._applyAidRuntimeContext(aid);
|
|
1574
1655
|
this._currentAid = aid;
|
|
1575
1656
|
this._aid = aid.aid;
|
|
1576
1657
|
this._identity = {
|
|
1577
1658
|
aid: aid.aid,
|
|
1578
|
-
private_key_pem: aid.
|
|
1659
|
+
private_key_pem: aid.privateKeyPem,
|
|
1579
1660
|
public_key_der_b64: aid.publicKey,
|
|
1580
1661
|
cert: aid.certPem,
|
|
1581
1662
|
};
|
|
1582
|
-
this._auth._aid = aid.aid;
|
|
1583
|
-
this._slotId = aid.slotId || 'default';
|
|
1584
1663
|
this._state = 'disconnected';
|
|
1585
1664
|
this._closing = false;
|
|
1586
1665
|
}
|
|
@@ -1632,9 +1711,6 @@ export class AUNClient {
|
|
|
1632
1711
|
get gatewayUrl() {
|
|
1633
1712
|
return this._gatewayUrl;
|
|
1634
1713
|
}
|
|
1635
|
-
set gatewayUrl(url) {
|
|
1636
|
-
this._gatewayUrl = url;
|
|
1637
|
-
}
|
|
1638
1714
|
get discovery() {
|
|
1639
1715
|
return this._discovery;
|
|
1640
1716
|
}
|
|
@@ -1676,10 +1752,11 @@ export class AUNClient {
|
|
|
1676
1752
|
/** 连接到 Gateway;身份来自构造函数或 loadIdentity(aid),认证由 SDK 内部自动完成。 */
|
|
1677
1753
|
async connect(opts) {
|
|
1678
1754
|
const tStart = Date.now();
|
|
1679
|
-
if (opts !== undefined && typeof opts === 'object') {
|
|
1755
|
+
if (opts !== undefined && opts !== null && typeof opts === 'object') {
|
|
1680
1756
|
const raw = opts;
|
|
1681
|
-
|
|
1682
|
-
|
|
1757
|
+
const invalid = Object.keys(raw).filter((key) => !PUBLIC_CONNECTION_OPTION_KEYS.has(key)).sort();
|
|
1758
|
+
if (invalid.length > 0) {
|
|
1759
|
+
throw new ValidationError(`connect options contain unsupported field(s): ${invalid.join(', ')}`);
|
|
1683
1760
|
}
|
|
1684
1761
|
}
|
|
1685
1762
|
const target = this._currentAid?.aid ?? this._aid ?? '';
|
|
@@ -1855,6 +1932,14 @@ export class AUNClient {
|
|
|
1855
1932
|
}
|
|
1856
1933
|
this._validateOutboundCall(method, p);
|
|
1857
1934
|
this._injectMessageCursorContext(method, p);
|
|
1935
|
+
if (method.startsWith('group.')
|
|
1936
|
+
&& !('_group_cursor_params' in p)
|
|
1937
|
+
&& !Boolean(p._pull_gate_locked)) {
|
|
1938
|
+
const explicitCursorParams = this._groupCursorParams(p);
|
|
1939
|
+
if (Object.keys(explicitCursorParams).length > 0) {
|
|
1940
|
+
p._group_cursor_params = explicitCursorParams;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1858
1943
|
// group.* 方法的 group_id 归一化为 canonical 格式(兼容老/污染数据)
|
|
1859
1944
|
if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
|
|
1860
1945
|
const rawGroupId = String(p.group_id);
|
|
@@ -1876,9 +1961,7 @@ export class AUNClient {
|
|
|
1876
1961
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1877
1962
|
delete p.encrypt;
|
|
1878
1963
|
if (encrypt) {
|
|
1879
|
-
|
|
1880
|
-
throw new StateError('V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
|
|
1881
|
-
}
|
|
1964
|
+
await this._ensureV2SessionReady('message.send', 'V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
|
|
1882
1965
|
this._clientLog.debug('call route: message.send → V2 encrypted send');
|
|
1883
1966
|
return await this._sendV2(String(p.to ?? ''), p.payload ?? {}, {
|
|
1884
1967
|
messageId: String(p.message_id ?? '') || undefined,
|
|
@@ -1895,9 +1978,7 @@ export class AUNClient {
|
|
|
1895
1978
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1896
1979
|
delete p.encrypt;
|
|
1897
1980
|
if (encrypt) {
|
|
1898
|
-
|
|
1899
|
-
throw new StateError('V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)');
|
|
1900
|
-
}
|
|
1981
|
+
await this._ensureV2SessionReady('group.send', 'V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)');
|
|
1901
1982
|
this._clientLog.debug('call route: group.send → V2 encrypted send');
|
|
1902
1983
|
return await this._sendGroupV2(String(p.group_id ?? ''), p.payload ?? {}, {
|
|
1903
1984
|
messageId: String(p.message_id ?? '') || undefined,
|
|
@@ -1912,9 +1993,7 @@ export class AUNClient {
|
|
|
1912
1993
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1913
1994
|
delete p.encrypt;
|
|
1914
1995
|
if (encrypt) {
|
|
1915
|
-
|
|
1916
|
-
throw new StateError('V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)');
|
|
1917
|
-
}
|
|
1996
|
+
await this._ensureV2SessionReady('group.thought.put', 'V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)');
|
|
1918
1997
|
this._clientLog.debug('call route: group.thought.put → V2 encrypted put');
|
|
1919
1998
|
return this._putGroupThoughtEncryptedV2(p);
|
|
1920
1999
|
}
|
|
@@ -1923,9 +2002,7 @@ export class AUNClient {
|
|
|
1923
2002
|
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1924
2003
|
delete p.encrypt;
|
|
1925
2004
|
if (encrypt) {
|
|
1926
|
-
|
|
1927
|
-
throw new StateError('V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)');
|
|
1928
|
-
}
|
|
2005
|
+
await this._ensureV2SessionReady('message.thought.put', 'V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)');
|
|
1929
2006
|
this._clientLog.debug('call route: message.thought.put → V2 encrypted put');
|
|
1930
2007
|
return this._putMessageThoughtEncryptedV2(p);
|
|
1931
2008
|
}
|
|
@@ -1944,26 +2021,45 @@ export class AUNClient {
|
|
|
1944
2021
|
* 拆分出来以便 pull gate 包裹整个操作。
|
|
1945
2022
|
*/
|
|
1946
2023
|
async _callImplInner(method, p) {
|
|
1947
|
-
// message.pull:V2
|
|
1948
|
-
if (method === 'message.pull'
|
|
2024
|
+
// message.pull:V2-only,按需初始化后走 V2 pull
|
|
2025
|
+
if (method === 'message.pull') {
|
|
2026
|
+
await this._ensureV2SessionReady('message.pull');
|
|
1949
2027
|
this._clientLog.debug('call route: message.pull → V2 pull');
|
|
1950
2028
|
const messages = await this._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, { force: p.force === true });
|
|
1951
2029
|
return { messages };
|
|
1952
2030
|
}
|
|
1953
|
-
// message.ack:V2
|
|
1954
|
-
if (method === 'message.ack'
|
|
2031
|
+
// message.ack:V2-only,按需初始化后走 V2 ack
|
|
2032
|
+
if (method === 'message.ack') {
|
|
2033
|
+
await this._ensureV2SessionReady('message.ack');
|
|
1955
2034
|
this._clientLog.debug('call route: message.ack → V2 ack');
|
|
1956
2035
|
return await this._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || undefined);
|
|
1957
2036
|
}
|
|
1958
|
-
// group.pull:V2
|
|
1959
|
-
if (method === 'group.pull' &&
|
|
2037
|
+
// group.pull:V2-only,按需初始化后走 V2 pull
|
|
2038
|
+
if (method === 'group.pull' && p.group_id) {
|
|
2039
|
+
await this._ensureV2SessionReady('group.pull');
|
|
1960
2040
|
this._clientLog.debug('call route: group.pull → V2 pull');
|
|
1961
|
-
const
|
|
2041
|
+
const hasExplicitAfterSeq = 'after_seq' in p || 'after_message_seq' in p;
|
|
2042
|
+
const cursorParams = this._explicitGroupCursorParams(p);
|
|
2043
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
|
|
2044
|
+
const pullOpts = {};
|
|
2045
|
+
if (hasExplicitAfterSeq)
|
|
2046
|
+
pullOpts.explicitAfterSeq = true;
|
|
2047
|
+
if (Object.keys(cursorParams).length > 0)
|
|
2048
|
+
pullOpts.cursorParams = cursorParams;
|
|
2049
|
+
if (!ownsCursor)
|
|
2050
|
+
pullOpts.ownsCursor = false;
|
|
2051
|
+
const messages = await this._pullGroupV2(String(p.group_id), Number(p.after_seq ?? p.after_message_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, Object.keys(pullOpts).length > 0 ? pullOpts : undefined);
|
|
1962
2052
|
return { messages };
|
|
1963
2053
|
}
|
|
1964
|
-
// group.ack_messages:V2
|
|
1965
|
-
if (method === 'group.ack_messages' &&
|
|
2054
|
+
// group.ack_messages:V2-only,按需初始化后走 V2 ack
|
|
2055
|
+
if (method === 'group.ack_messages' && p.group_id) {
|
|
2056
|
+
await this._ensureV2SessionReady('group.ack_messages');
|
|
1966
2057
|
this._clientLog.debug('call route: group.ack_messages → V2 ack');
|
|
2058
|
+
const cursorParams = this._explicitGroupCursorParams(p);
|
|
2059
|
+
const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
|
|
2060
|
+
if (!ownsCursor) {
|
|
2061
|
+
return await this._rawGroupAckMessages(p);
|
|
2062
|
+
}
|
|
1967
2063
|
return await this._ackGroupV2(String(p.group_id), Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || undefined);
|
|
1968
2064
|
}
|
|
1969
2065
|
// 关键操作自动附加客户端签名
|
|
@@ -2070,6 +2166,7 @@ export class AUNClient {
|
|
|
2070
2166
|
delete p._pull_gate_locked;
|
|
2071
2167
|
delete p._skip_auto_ack;
|
|
2072
2168
|
delete p.skip_auto_ack;
|
|
2169
|
+
delete p._group_cursor_params;
|
|
2073
2170
|
if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
|
|
2074
2171
|
p.group_id = normalizeGroupId(String(p.group_id)) || String(p.group_id);
|
|
2075
2172
|
}
|
|
@@ -2433,6 +2530,9 @@ export class AUNClient {
|
|
|
2433
2530
|
// 状态保护:非 connected 或正在关闭时跳过(与 Python 对齐)
|
|
2434
2531
|
if (this._state !== 'connected' || this._closing)
|
|
2435
2532
|
return;
|
|
2533
|
+
groupId = normalizeGroupId(groupId) || String(groupId ?? '').trim();
|
|
2534
|
+
if (!groupId)
|
|
2535
|
+
return;
|
|
2436
2536
|
const ns = `group:${groupId}`;
|
|
2437
2537
|
const afterSeq = this._seqTracker.getContiguousSeq(ns);
|
|
2438
2538
|
// per-namespace 去重:同一 group namespace 只允许 1 个 in-flight pull
|
|
@@ -2441,45 +2541,11 @@ export class AUNClient {
|
|
|
2441
2541
|
return;
|
|
2442
2542
|
this._gapFillDone.add(dedupKey);
|
|
2443
2543
|
this._gapFillActive = true;
|
|
2544
|
+
let filled = 0;
|
|
2444
2545
|
try {
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
device_id: this._deviceId,
|
|
2449
|
-
limit: 50,
|
|
2450
|
-
});
|
|
2451
|
-
if (isJsonObject(result)) {
|
|
2452
|
-
const messages = result.messages;
|
|
2453
|
-
if (Array.isArray(messages)) {
|
|
2454
|
-
// ⚠️ 不再重复调用 onPullResult:call('group.pull') 拦截器已在内部调用过一次
|
|
2455
|
-
const pushed = this._pushedSeqs.get(ns);
|
|
2456
|
-
for (const msg of messages) {
|
|
2457
|
-
if (isJsonObject(msg)) {
|
|
2458
|
-
const s = msg.seq;
|
|
2459
|
-
if (pushed && s !== undefined && s !== null && pushed.has(s))
|
|
2460
|
-
continue;
|
|
2461
|
-
if (s !== undefined && s !== null) {
|
|
2462
|
-
await this._publishPulledMessage('group.message_created', ns, s, msg);
|
|
2463
|
-
}
|
|
2464
|
-
else {
|
|
2465
|
-
await this._publishAppEvent('group.message_created', msg);
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
this._prunePushedSeqs(ns);
|
|
2470
|
-
// publish 完成后 auto-ack
|
|
2471
|
-
const contig = this._seqTracker.getContiguousSeq(ns);
|
|
2472
|
-
if (contig > 0) {
|
|
2473
|
-
const gid = groupId;
|
|
2474
|
-
this._transport.call('group.ack_messages', {
|
|
2475
|
-
group_id: gid,
|
|
2476
|
-
msg_seq: contig,
|
|
2477
|
-
device_id: this._deviceId,
|
|
2478
|
-
slot_id: this._slotId,
|
|
2479
|
-
}).catch((e) => { this._clientLog.warn(`group gap-fill auto-ack failed: group=${gid}`, e); });
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2546
|
+
const messages = await this._pullGroupV2(groupId, afterSeq, 50);
|
|
2547
|
+
filled = messages.length;
|
|
2548
|
+
this._prunePushedSeqs(ns);
|
|
2483
2549
|
}
|
|
2484
2550
|
catch (exc) {
|
|
2485
2551
|
this._clientLog.warn(`group message gap-fill failed:${String(exc)}`);
|
|
@@ -2488,6 +2554,9 @@ export class AUNClient {
|
|
|
2488
2554
|
// S1: 成功 / 失败路径都必须清理飞行标记
|
|
2489
2555
|
this._gapFillDone.delete(dedupKey);
|
|
2490
2556
|
this._gapFillActive = false;
|
|
2557
|
+
if (filled > 0 && this._seqTracker.getContiguousSeq(ns) > afterSeq) {
|
|
2558
|
+
this._safeAsync(this._fillGroupGap(groupId));
|
|
2559
|
+
}
|
|
2491
2560
|
}
|
|
2492
2561
|
}
|
|
2493
2562
|
/** 后台补齐群事件空洞 */
|
|
@@ -2604,42 +2673,11 @@ export class AUNClient {
|
|
|
2604
2673
|
return;
|
|
2605
2674
|
this._gapFillDone.add(dedupKey);
|
|
2606
2675
|
this._gapFillActive = true;
|
|
2676
|
+
let filled = 0;
|
|
2607
2677
|
try {
|
|
2608
|
-
const
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
});
|
|
2612
|
-
if (isJsonObject(result)) {
|
|
2613
|
-
const messages = result.messages;
|
|
2614
|
-
if (Array.isArray(messages)) {
|
|
2615
|
-
// ⚠️ 不再重复调用 onPullResult:call('message.pull') 拦截器已在内部调用过一次
|
|
2616
|
-
// 与 _fillGroupGap 路径对齐,避免双重 tracker 推进。
|
|
2617
|
-
const pushed = this._pushedSeqs.get(ns);
|
|
2618
|
-
for (const msg of messages) {
|
|
2619
|
-
if (isJsonObject(msg)) {
|
|
2620
|
-
const s = msg.seq;
|
|
2621
|
-
if (pushed && s !== undefined && s !== null && pushed.has(s))
|
|
2622
|
-
continue;
|
|
2623
|
-
if (s !== undefined && s !== null) {
|
|
2624
|
-
await this._publishPulledMessage('message.received', ns, s, msg);
|
|
2625
|
-
}
|
|
2626
|
-
else {
|
|
2627
|
-
await this._publishAppEvent('message.received', msg);
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
|
-
}
|
|
2631
|
-
this._prunePushedSeqs(ns);
|
|
2632
|
-
// publish 完成后 auto-ack
|
|
2633
|
-
const contig = this._seqTracker.getContiguousSeq(ns);
|
|
2634
|
-
if (contig > 0) {
|
|
2635
|
-
this._transport.call('message.ack', {
|
|
2636
|
-
seq: contig,
|
|
2637
|
-
device_id: this._deviceId,
|
|
2638
|
-
slot_id: this._slotId,
|
|
2639
|
-
}).catch((e) => { this._clientLog.warn(`P2P gap-fill auto-ack failed:${String(e)}`); });
|
|
2640
|
-
}
|
|
2641
|
-
}
|
|
2642
|
-
}
|
|
2678
|
+
const messages = await this._pullV2(afterSeq, 50);
|
|
2679
|
+
filled = messages.length;
|
|
2680
|
+
this._prunePushedSeqs(ns);
|
|
2643
2681
|
}
|
|
2644
2682
|
catch (exc) {
|
|
2645
2683
|
this._clientLog.warn(`P2P message gap-fill failed:${String(exc)}`);
|
|
@@ -2648,6 +2686,9 @@ export class AUNClient {
|
|
|
2648
2686
|
// S1: 成功 / 失败路径都必须清理飞行标记
|
|
2649
2687
|
this._gapFillDone.delete(dedupKey);
|
|
2650
2688
|
this._gapFillActive = false;
|
|
2689
|
+
if (filled > 0 && this._seqTracker.getContiguousSeq(ns) > afterSeq) {
|
|
2690
|
+
this._safeAsync(this._fillP2pGap());
|
|
2691
|
+
}
|
|
2651
2692
|
}
|
|
2652
2693
|
}
|
|
2653
2694
|
/** 只按硬上限裁剪 published guard,不能按 contiguousSeq 清理。 */
|
|
@@ -3582,11 +3623,11 @@ export class AUNClient {
|
|
|
3582
3623
|
* 使用 SubtleCrypto 异步签名。
|
|
3583
3624
|
*/
|
|
3584
3625
|
async _signClientOperation(method, params) {
|
|
3585
|
-
const
|
|
3586
|
-
if (!
|
|
3626
|
+
const currentAid = this._currentAid;
|
|
3627
|
+
if (!currentAid?.privateKeyPem)
|
|
3587
3628
|
return;
|
|
3588
3629
|
try {
|
|
3589
|
-
const aid =
|
|
3630
|
+
const aid = currentAid.aid;
|
|
3590
3631
|
const ts = String(Math.floor(Date.now() / 1000));
|
|
3591
3632
|
// 计算 params hash:覆盖所有非 _ 前缀且非 client_signature 的业务字段
|
|
3592
3633
|
const paramsForHash = {};
|
|
@@ -3603,14 +3644,14 @@ export class AUNClient {
|
|
|
3603
3644
|
.join('');
|
|
3604
3645
|
const signData = new TextEncoder().encode(`${method}|${aid}|${ts}|${paramsHash}`);
|
|
3605
3646
|
// 导入私钥并签名
|
|
3606
|
-
const pkcs8 = pemToArrayBuffer(
|
|
3647
|
+
const pkcs8 = pemToArrayBuffer(currentAid.privateKeyPem);
|
|
3607
3648
|
const cryptoKey = await crypto.subtle.importKey('pkcs8', pkcs8, { name: 'ECDSA', namedCurve: 'P-256' }, false, ['sign']);
|
|
3608
3649
|
const sigP1363 = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, cryptoKey, signData);
|
|
3609
3650
|
// P1363 → DER 格式(与 Python 兼容)
|
|
3610
3651
|
const sigDer = p1363ToDer(new Uint8Array(sigP1363));
|
|
3611
3652
|
// 证书指纹
|
|
3612
3653
|
let certFingerprint = '';
|
|
3613
|
-
const certPem =
|
|
3654
|
+
const certPem = currentAid.certPem;
|
|
3614
3655
|
if (certPem) {
|
|
3615
3656
|
const certDer = pemToArrayBuffer(certPem);
|
|
3616
3657
|
const fpBuf = await crypto.subtle.digest('SHA-256', certDer);
|
|
@@ -3706,15 +3747,25 @@ export class AUNClient {
|
|
|
3706
3747
|
await this._restoreSeqTrackerState();
|
|
3707
3748
|
}
|
|
3708
3749
|
this._startBackgroundTasks();
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3750
|
+
const connectionKind = String(params.connection_kind ?? 'long');
|
|
3751
|
+
const isShortConnection = connectionKind === 'short';
|
|
3752
|
+
if (!isShortConnection) {
|
|
3753
|
+
// V2 E2EE: 长连接上线时初始化 session 并注册设备 SPK(与 Python `_init_v2_session` 对齐)
|
|
3754
|
+
try {
|
|
3755
|
+
await this._initV2Session();
|
|
3756
|
+
}
|
|
3757
|
+
catch (exc) {
|
|
3758
|
+
this._clientLog.warn(`V2 session init failed (non-fatal): ${String(exc)}`);
|
|
3759
|
+
}
|
|
3712
3760
|
}
|
|
3713
|
-
|
|
3714
|
-
this._clientLog.
|
|
3761
|
+
else {
|
|
3762
|
+
this._clientLog.debug('V2 session init deferred for short connection');
|
|
3715
3763
|
}
|
|
3716
3764
|
// connect/reconnect 成功后自动触发一次 P2P message.pull,补齐离线期间积压
|
|
3717
|
-
|
|
3765
|
+
const hasExplicitBackgroundSync = Object.prototype.hasOwnProperty.call(params, 'background_sync');
|
|
3766
|
+
const backgroundSyncEnabled = this._sessionOptions?.background_sync !== false
|
|
3767
|
+
&& (!isShortConnection || hasExplicitBackgroundSync);
|
|
3768
|
+
if (backgroundSyncEnabled) {
|
|
3718
3769
|
this._safeAsync(this._fillP2pGap());
|
|
3719
3770
|
}
|
|
3720
3771
|
this._clientLog.debug(`_connectOnce exit: elapsed=${Date.now() - tStart}ms aid=${this._aid ?? '-'}`);
|
|
@@ -4660,6 +4711,20 @@ export class AUNClient {
|
|
|
4660
4711
|
this._clientLog.warn(`${label} push repaired contiguous_seq: ns=${ns} payload=${hasPayload} push_seq=${pushSeq} contiguous=${contig}->${repaired}`);
|
|
4661
4712
|
return repaired;
|
|
4662
4713
|
}
|
|
4714
|
+
async _ensureV2SessionReady(method, errorMessage) {
|
|
4715
|
+
if (!this._v2Session) {
|
|
4716
|
+
if (!this._v2SessionInitInFlight) {
|
|
4717
|
+
this._v2SessionInitInFlight = this._initV2Session()
|
|
4718
|
+
.finally(() => {
|
|
4719
|
+
this._v2SessionInitInFlight = null;
|
|
4720
|
+
});
|
|
4721
|
+
}
|
|
4722
|
+
await this._v2SessionInitInFlight;
|
|
4723
|
+
}
|
|
4724
|
+
if (!this._v2Session) {
|
|
4725
|
+
throw new StateError(errorMessage ?? `V2 session not initialized; encrypted ${method} requires E2EE V2`);
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4663
4728
|
// ── V2 E2EE API(async,与 Python `client.py` `_init_v2_session` / `send_v2` / `pull_v2` / `ack_v2` 对齐) ──
|
|
4664
4729
|
/**
|
|
4665
4730
|
* 初始化 V2 session:从 AID PEM 私钥提取 raw scalar + DER 公钥,
|
|
@@ -4670,35 +4735,16 @@ export class AUNClient {
|
|
|
4670
4735
|
async _initV2Session() {
|
|
4671
4736
|
if (!this._aid)
|
|
4672
4737
|
return;
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
try {
|
|
4677
|
-
const reloaded = await this._keystore.loadIdentity(this._aid);
|
|
4678
|
-
if (reloaded?.private_key_pem) {
|
|
4679
|
-
this._identity = reloaded;
|
|
4680
|
-
identity = reloaded;
|
|
4681
|
-
this._clientLog.warn('V2 session init: identity cache was stale, reloaded from keystore');
|
|
4682
|
-
// 自愈:重新持久化,清理 instance_state 中的脏数据
|
|
4683
|
-
try {
|
|
4684
|
-
const persistIdentity = this._auth._persistIdentity;
|
|
4685
|
-
if (typeof persistIdentity === 'function') {
|
|
4686
|
-
await persistIdentity.call(this._auth, reloaded);
|
|
4687
|
-
}
|
|
4688
|
-
}
|
|
4689
|
-
catch { /* best-effort */ }
|
|
4690
|
-
}
|
|
4691
|
-
}
|
|
4692
|
-
catch { /* ignore */ }
|
|
4693
|
-
}
|
|
4694
|
-
if (!identity?.private_key_pem) {
|
|
4738
|
+
// 私钥由 AIDStore 管理,直接从 _currentAid 读取明文私钥
|
|
4739
|
+
const currentAid = this._currentAid;
|
|
4740
|
+
if (!currentAid?.privateKeyPem) {
|
|
4695
4741
|
this._clientLog.warn('V2 session init skipped: no AID private key');
|
|
4696
4742
|
return;
|
|
4697
4743
|
}
|
|
4698
4744
|
if (this._v2Session)
|
|
4699
4745
|
return;
|
|
4700
4746
|
// 1. PEM → DER PKCS8(去掉 header/footer 后 base64 解码)
|
|
4701
|
-
const pem =
|
|
4747
|
+
const pem = currentAid.privateKeyPem.trim();
|
|
4702
4748
|
const pemBody = pem
|
|
4703
4749
|
.replace(/-----BEGIN [^-]+-----/g, '')
|
|
4704
4750
|
.replace(/-----END [^-]+-----/g, '')
|
|
@@ -5096,6 +5142,7 @@ export class AUNClient {
|
|
|
5096
5142
|
decrypted.push(plaintext);
|
|
5097
5143
|
}
|
|
5098
5144
|
}
|
|
5145
|
+
const hasServerAckSeq = Object.prototype.hasOwnProperty.call(result, 'server_ack_seq');
|
|
5099
5146
|
const serverAckSeq = Number(result.server_ack_seq ?? 0);
|
|
5100
5147
|
if (ns && Number.isFinite(serverAckSeq) && serverAckSeq > 0) {
|
|
5101
5148
|
const contig = this._seqTracker.getContiguousSeq(ns);
|
|
@@ -5111,7 +5158,10 @@ export class AUNClient {
|
|
|
5111
5158
|
await this._drainOrderedMessages(ns);
|
|
5112
5159
|
this._saveSeqTrackerState();
|
|
5113
5160
|
}
|
|
5114
|
-
|
|
5161
|
+
const ackNeeded = messages.length > 0
|
|
5162
|
+
&& ackSeq > 0
|
|
5163
|
+
&& (contigAdvanced || (hasServerAckSeq && ackSeq > serverAckSeq));
|
|
5164
|
+
if (ackNeeded) {
|
|
5115
5165
|
this._safeAsync(this._ackV2(ackSeq).then(() => undefined));
|
|
5116
5166
|
}
|
|
5117
5167
|
}
|
|
@@ -5143,7 +5193,7 @@ export class AUNClient {
|
|
|
5143
5193
|
seq = maxSeen;
|
|
5144
5194
|
}
|
|
5145
5195
|
}
|
|
5146
|
-
const raw = await this.
|
|
5196
|
+
const raw = await this._callRawV2Rpc('message.v2.ack', { up_to_seq: seq });
|
|
5147
5197
|
const result = isJsonObject(raw)
|
|
5148
5198
|
? { ...raw }
|
|
5149
5199
|
: { result: raw };
|
|
@@ -5435,7 +5485,7 @@ export class AUNClient {
|
|
|
5435
5485
|
* @param afterSeq 从此 seq 之后开始拉取(0/省略 = 从当前 contiguous 开始)
|
|
5436
5486
|
* @param limit 最多拉取条数
|
|
5437
5487
|
*/
|
|
5438
|
-
async _pullGroupV2(groupId, afterSeq = 0, limit = 50) {
|
|
5488
|
+
async _pullGroupV2(groupId, afterSeq = 0, limit = 50, opts) {
|
|
5439
5489
|
if (!this._v2Session) {
|
|
5440
5490
|
throw new StateError('V2 session not initialized (not connected?)');
|
|
5441
5491
|
}
|
|
@@ -5444,15 +5494,18 @@ export class AUNClient {
|
|
|
5444
5494
|
throw new ValidationError('group.pull requires group_id');
|
|
5445
5495
|
const ns = `group:${gid}`;
|
|
5446
5496
|
const decrypted = [];
|
|
5447
|
-
|
|
5497
|
+
const cursorParams = opts?.cursorParams ?? {};
|
|
5498
|
+
const ownsCursor = opts?.ownsCursor !== false;
|
|
5499
|
+
let nextAfterSeq = opts?.explicitAfterSeq ? afterSeq : (afterSeq || this._seqTracker.getContiguousSeq(ns));
|
|
5448
5500
|
let pageCount = 0;
|
|
5449
5501
|
const maxPages = 100;
|
|
5450
5502
|
while (pageCount < maxPages) {
|
|
5451
5503
|
pageCount += 1;
|
|
5452
|
-
const result = await this.
|
|
5504
|
+
const result = await this._callRawV2Rpc('group.v2.pull', {
|
|
5453
5505
|
group_id: gid,
|
|
5454
5506
|
after_seq: nextAfterSeq,
|
|
5455
5507
|
limit,
|
|
5508
|
+
...cursorParams,
|
|
5456
5509
|
});
|
|
5457
5510
|
const messages = (Array.isArray(result?.messages) ? result.messages : []);
|
|
5458
5511
|
const seqs = messages
|
|
@@ -5519,6 +5572,7 @@ export class AUNClient {
|
|
|
5519
5572
|
decrypted.push(plaintext);
|
|
5520
5573
|
}
|
|
5521
5574
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
5575
|
+
const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, 'current_seq');
|
|
5522
5576
|
const serverAckSeq = Number(cursor?.current_seq ?? 0);
|
|
5523
5577
|
if (Number.isFinite(serverAckSeq) && serverAckSeq > 0) {
|
|
5524
5578
|
const contig = this._seqTracker.getContiguousSeq(ns);
|
|
@@ -5533,10 +5587,16 @@ export class AUNClient {
|
|
|
5533
5587
|
await this._drainOrderedMessages(ns);
|
|
5534
5588
|
this._saveSeqTrackerState();
|
|
5535
5589
|
}
|
|
5536
|
-
|
|
5590
|
+
const ackNeeded = messages.length > 0
|
|
5591
|
+
&& ackSeq > 0
|
|
5592
|
+
&& ownsCursor
|
|
5593
|
+
&& (contigAdvanced || (hasServerCursor && ackSeq > serverAckSeq));
|
|
5594
|
+
if (ackNeeded) {
|
|
5537
5595
|
this._safeAsync(this._ackGroupV2(gid, ackSeq).then(() => undefined));
|
|
5538
5596
|
}
|
|
5539
5597
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
5598
|
+
if (!ownsCursor)
|
|
5599
|
+
break;
|
|
5540
5600
|
if (messages.length === 0 || nextAfter <= nextAfterSeq || result.has_more === false)
|
|
5541
5601
|
break;
|
|
5542
5602
|
nextAfterSeq = nextAfter;
|
|
@@ -5546,6 +5606,31 @@ export class AUNClient {
|
|
|
5546
5606
|
}
|
|
5547
5607
|
return decrypted;
|
|
5548
5608
|
}
|
|
5609
|
+
_groupCursorParams(params) {
|
|
5610
|
+
const cursorParams = {};
|
|
5611
|
+
for (const key of ['device_id', 'slot_id', 'device_name', 'device_type']) {
|
|
5612
|
+
const value = params[key];
|
|
5613
|
+
if (value !== undefined && value !== null)
|
|
5614
|
+
cursorParams[key] = value;
|
|
5615
|
+
}
|
|
5616
|
+
return cursorParams;
|
|
5617
|
+
}
|
|
5618
|
+
_explicitGroupCursorParams(params) {
|
|
5619
|
+
const value = params._group_cursor_params;
|
|
5620
|
+
if (!isJsonObject(value))
|
|
5621
|
+
return {};
|
|
5622
|
+
return { ...value };
|
|
5623
|
+
}
|
|
5624
|
+
_groupCursorTargetsCurrentInstance(params) {
|
|
5625
|
+
const deviceId = String(params.device_id ?? '').trim();
|
|
5626
|
+
const slotId = String(params.slot_id ?? '').trim();
|
|
5627
|
+
return (!deviceId || deviceId === (this._deviceId ?? ''))
|
|
5628
|
+
&& (!slotId || slotId === (this._slotId ?? ''));
|
|
5629
|
+
}
|
|
5630
|
+
async _rawGroupAckMessages(params) {
|
|
5631
|
+
const p = { ...params };
|
|
5632
|
+
return await this._callRawV2Rpc('group.ack_messages', p);
|
|
5633
|
+
}
|
|
5549
5634
|
/**
|
|
5550
5635
|
* 确认 V2 群消息已消费。
|
|
5551
5636
|
*
|
|
@@ -5566,7 +5651,7 @@ export class AUNClient {
|
|
|
5566
5651
|
this._clientLog.warn(`ackGroupV2 clamp: group=${gid} up_to_seq=${seq} > max_seen=${maxSeen}, clamp`);
|
|
5567
5652
|
seq = maxSeen;
|
|
5568
5653
|
}
|
|
5569
|
-
return this.
|
|
5654
|
+
return this._callRawV2Rpc('group.v2.ack', { group_id: gid, up_to_seq: seq });
|
|
5570
5655
|
}
|
|
5571
5656
|
// ── V2 thought(per-device wrap,服务端透传,不持久化)──────────
|
|
5572
5657
|
/**
|
|
@@ -6387,8 +6472,8 @@ export class AUNClient {
|
|
|
6387
6472
|
return;
|
|
6388
6473
|
}
|
|
6389
6474
|
let signature = '';
|
|
6390
|
-
const
|
|
6391
|
-
if (
|
|
6475
|
+
const currentAid = this._currentAid;
|
|
6476
|
+
if (currentAid?.privateKeyPem) {
|
|
6392
6477
|
try {
|
|
6393
6478
|
const signPayloadObj = {
|
|
6394
6479
|
group_id: groupId,
|
|
@@ -6398,7 +6483,7 @@ export class AUNClient {
|
|
|
6398
6483
|
};
|
|
6399
6484
|
const signPayload = stableStringify(signPayloadObj);
|
|
6400
6485
|
const signPayloadBytes = new TextEncoder().encode(signPayload);
|
|
6401
|
-
const privKey = await importPrivateKeyEcdsa(
|
|
6486
|
+
const privKey = await importPrivateKeyEcdsa(currentAid.privateKeyPem);
|
|
6402
6487
|
const sigBytes = await ecdsaSignDer(privKey, signPayloadBytes);
|
|
6403
6488
|
signature = uint8ToBase64(sigBytes);
|
|
6404
6489
|
}
|
|
@@ -6594,7 +6679,7 @@ export class AUNClient {
|
|
|
6594
6679
|
if (newContig > 0 && newContig !== contigBefore) {
|
|
6595
6680
|
const maxSeen = this._seqTracker.getMaxSeenSeq(ns);
|
|
6596
6681
|
const ackSeq = maxSeen > 0 ? Math.min(newContig, maxSeen) : newContig;
|
|
6597
|
-
this.
|
|
6682
|
+
this._callRawV2Rpc('message.v2.ack', { up_to_seq: ackSeq })
|
|
6598
6683
|
.catch(e => this._clientLog.debug(`V2 P2P push-ack failed: ${e}`));
|
|
6599
6684
|
}
|
|
6600
6685
|
this._clientLog.debug(`_onV2PushNotification: push 带 payload 解密成功, contiguous_seq=${contigBefore}->${newContig} push_seq=${pushSeq}`);
|