@agentunion/fastaun 0.2.13

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.
Files changed (69) hide show
  1. package/LICENSE +17 -0
  2. package/README.md +78 -0
  3. package/dist/auth.d.ts +287 -0
  4. package/dist/auth.js +1668 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/client.d.ts +359 -0
  7. package/dist/client.js +3918 -0
  8. package/dist/client.js.map +1 -0
  9. package/dist/config.d.ts +43 -0
  10. package/dist/config.js +119 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/crypto.d.ts +41 -0
  13. package/dist/crypto.js +85 -0
  14. package/dist/crypto.js.map +1 -0
  15. package/dist/discovery.d.ts +22 -0
  16. package/dist/discovery.js +110 -0
  17. package/dist/discovery.js.map +1 -0
  18. package/dist/e2ee-group.d.ts +192 -0
  19. package/dist/e2ee-group.js +1134 -0
  20. package/dist/e2ee-group.js.map +1 -0
  21. package/dist/e2ee.d.ts +120 -0
  22. package/dist/e2ee.js +890 -0
  23. package/dist/e2ee.js.map +1 -0
  24. package/dist/errors.d.ts +115 -0
  25. package/dist/errors.js +253 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/events.d.ts +39 -0
  28. package/dist/events.js +82 -0
  29. package/dist/events.js.map +1 -0
  30. package/dist/index.d.ts +23 -0
  31. package/dist/index.js +32 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/keystore/aid-db.d.ts +79 -0
  34. package/dist/keystore/aid-db.js +621 -0
  35. package/dist/keystore/aid-db.js.map +1 -0
  36. package/dist/keystore/file.d.ts +82 -0
  37. package/dist/keystore/file.js +395 -0
  38. package/dist/keystore/file.js.map +1 -0
  39. package/dist/keystore/index.d.ts +88 -0
  40. package/dist/keystore/index.js +7 -0
  41. package/dist/keystore/index.js.map +1 -0
  42. package/dist/keystore/sqlite-backup.d.ts +40 -0
  43. package/dist/keystore/sqlite-backup.js +379 -0
  44. package/dist/keystore/sqlite-backup.js.map +1 -0
  45. package/dist/logger.d.ts +6 -0
  46. package/dist/logger.js +53 -0
  47. package/dist/logger.js.map +1 -0
  48. package/dist/namespaces/auth.d.ts +49 -0
  49. package/dist/namespaces/auth.js +248 -0
  50. package/dist/namespaces/auth.js.map +1 -0
  51. package/dist/namespaces/custody.d.ts +47 -0
  52. package/dist/namespaces/custody.js +231 -0
  53. package/dist/namespaces/custody.js.map +1 -0
  54. package/dist/secret-store/file-store.d.ts +25 -0
  55. package/dist/secret-store/file-store.js +124 -0
  56. package/dist/secret-store/file-store.js.map +1 -0
  57. package/dist/secret-store/index.d.ts +28 -0
  58. package/dist/secret-store/index.js +19 -0
  59. package/dist/secret-store/index.js.map +1 -0
  60. package/dist/seq-tracker.d.ts +29 -0
  61. package/dist/seq-tracker.js +221 -0
  62. package/dist/seq-tracker.js.map +1 -0
  63. package/dist/transport.d.ts +60 -0
  64. package/dist/transport.js +355 -0
  65. package/dist/transport.js.map +1 -0
  66. package/dist/types.d.ts +170 -0
  67. package/dist/types.js +12 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +42 -0
package/dist/crypto.js ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * AUN SDK 密码学工具
3
+ *
4
+ * 使用 Node.js 内置 crypto 模块,与 Python SDK 的 CryptoProvider 对齐。
5
+ */
6
+ import { generateKeyPairSync, createSign, createHash, randomBytes, X509Certificate } from 'node:crypto';
7
+ /**
8
+ * 密码学操作提供者。
9
+ */
10
+ export class CryptoProvider {
11
+ curveName = 'P-256';
12
+ /**
13
+ * 生成 ECDSA P-256 密钥对。
14
+ * 返回 PEM 私钥 + DER 公钥(base64)+ 曲线名称。
15
+ */
16
+ generateIdentity() {
17
+ const { privateKey, publicKey } = generateKeyPairSync('ec', {
18
+ namedCurve: 'prime256v1',
19
+ });
20
+ const privateKeyPem = privateKey
21
+ .export({ type: 'pkcs8', format: 'pem' })
22
+ .toString();
23
+ const publicKeyDer = publicKey.export({ type: 'spki', format: 'der' });
24
+ return {
25
+ private_key_pem: privateKeyPem,
26
+ public_key_der_b64: publicKeyDer.toString('base64'),
27
+ curve: this.curveName,
28
+ };
29
+ }
30
+ /**
31
+ * 使用私钥签名登录 nonce。
32
+ * 签名数据格式:"nonce:timestamp"
33
+ *
34
+ * @returns [signature_b64, timestamp]
35
+ */
36
+ signLoginNonce(privateKeyPem, nonce, clientTime) {
37
+ const usedTime = clientTime ?? String(Math.floor(Date.now() / 1000));
38
+ const signData = `${nonce}:${usedTime}`;
39
+ // Python 的 cryptography 库默认输出 DER 编码签名,
40
+ // Node.js createSign 默认也输出 DER 编码,保持一致。
41
+ const signer = createSign('SHA256');
42
+ signer.update(signData);
43
+ signer.end();
44
+ const signature = signer.sign(privateKeyPem);
45
+ return [signature.toString('base64'), usedTime];
46
+ }
47
+ /**
48
+ * 生成客户端 nonce(12 字节随机数的 base64 编码)。
49
+ */
50
+ newClientNonce() {
51
+ return randomBytes(12).toString('base64');
52
+ }
53
+ }
54
+ /**
55
+ * 统一的证书 SHA-256 指纹计算(对齐 Python certificate_sha256_fingerprint)。
56
+ * 返回 "sha256:{hex}" 格式的指纹。
57
+ * e2ee.ts 与 keystore/file.ts 共用此函数,避免两套实现不一致。
58
+ */
59
+ export function certificateSha256Fingerprint(certPem) {
60
+ const raw = Buffer.isBuffer(certPem) ? certPem : Buffer.from(certPem, 'utf-8');
61
+ const text = raw.toString('utf-8');
62
+ let der;
63
+ if (text.includes('-----BEGIN CERTIFICATE-----')) {
64
+ // PEM → DER 解码
65
+ const body = text
66
+ .replace(/-----BEGIN CERTIFICATE-----/g, '')
67
+ .replace(/-----END CERTIFICATE-----/g, '')
68
+ .replace(/\s+/g, '');
69
+ if (!body)
70
+ return '';
71
+ der = Buffer.from(body, 'base64');
72
+ }
73
+ else {
74
+ // 尝试通过 X509Certificate 获取 DER
75
+ try {
76
+ der = new X509Certificate(raw).raw;
77
+ }
78
+ catch {
79
+ der = raw;
80
+ }
81
+ }
82
+ const hash = createHash('sha256').update(der).digest('hex');
83
+ return `sha256:${hash}`;
84
+ }
85
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../src/crypto.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export declare class GatewayDiscovery {
2
+ private _verifySsl;
3
+ private _lastHealthy;
4
+ constructor(opts?: {
5
+ verifySsl?: boolean;
6
+ });
7
+ /** 最近一次 health check 结果,null 表示尚未检查 */
8
+ get lastHealthy(): boolean | null;
9
+ /**
10
+ * 向 gatewayUrl 对应的 /health 端点发送 GET 请求,检查网关可用性。
11
+ * 结果缓存到 lastHealthy,同时返回检查结果。
12
+ */
13
+ checkHealth(gatewayUrl: string, timeout?: number): Promise<boolean>;
14
+ /**
15
+ * 从 well-known URL 发现 Gateway。
16
+ *
17
+ * @param wellKnownUrl - well-known 发现端点(如 https://alice.aid.com/.well-known/aun-gateway)
18
+ * @param timeout - 请求超时(毫秒,默认 5000)
19
+ * @returns Gateway WebSocket URL
20
+ */
21
+ discover(wellKnownUrl: string, timeout?: number): Promise<string>;
22
+ }
@@ -0,0 +1,110 @@
1
+ import * as http from 'node:http';
2
+ import * as https from 'node:https';
3
+ import { ConnectionError, ValidationError } from './errors.js';
4
+ import { isJsonObject } from './types.js';
5
+ function _httpGetJson(url, verifySsl, timeout) {
6
+ return new Promise((resolve, reject) => {
7
+ const parsed = new URL(url);
8
+ const mod = parsed.protocol === 'https:' ? https : http;
9
+ const options = { timeout };
10
+ if (!verifySsl) {
11
+ options.rejectUnauthorized = false;
12
+ }
13
+ const req = mod.get(url, options, (res) => {
14
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
15
+ reject(new Error(`HTTP ${res.statusCode}`));
16
+ res.resume();
17
+ return;
18
+ }
19
+ const chunks = [];
20
+ res.on('data', (chunk) => chunks.push(chunk));
21
+ res.on('end', () => {
22
+ try {
23
+ resolve(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
24
+ }
25
+ catch (error) {
26
+ reject(error);
27
+ }
28
+ });
29
+ res.on('error', reject);
30
+ });
31
+ req.on('error', reject);
32
+ req.on('timeout', () => {
33
+ req.destroy();
34
+ reject(new Error(`timeout fetching ${url}`));
35
+ });
36
+ });
37
+ }
38
+ function _httpGetOk(url, verifySsl, timeout) {
39
+ return new Promise((resolve) => {
40
+ const parsed = new URL(url);
41
+ const mod = parsed.protocol === 'https:' ? https : http;
42
+ const options = { method: 'GET', timeout };
43
+ if (!verifySsl)
44
+ options.rejectUnauthorized = false;
45
+ const req = mod.request(url, options, (res) => {
46
+ res.resume();
47
+ resolve(res.statusCode === 200);
48
+ });
49
+ req.on('error', () => resolve(false));
50
+ req.on('timeout', () => { req.destroy(); resolve(false); });
51
+ req.end();
52
+ });
53
+ }
54
+ export class GatewayDiscovery {
55
+ _verifySsl;
56
+ _lastHealthy = null;
57
+ constructor(opts) {
58
+ this._verifySsl = opts?.verifySsl ?? true;
59
+ }
60
+ /** 最近一次 health check 结果,null 表示尚未检查 */
61
+ get lastHealthy() { return this._lastHealthy; }
62
+ /**
63
+ * 向 gatewayUrl 对应的 /health 端点发送 GET 请求,检查网关可用性。
64
+ * 结果缓存到 lastHealthy,同时返回检查结果。
65
+ */
66
+ async checkHealth(gatewayUrl, timeout = 5_000) {
67
+ const parsed = new URL(gatewayUrl);
68
+ parsed.protocol = parsed.protocol === 'wss:' ? 'https:' : 'http:';
69
+ parsed.pathname = '/health';
70
+ parsed.search = '';
71
+ parsed.hash = '';
72
+ const healthUrl = parsed.toString();
73
+ this._lastHealthy = await _httpGetOk(healthUrl, this._verifySsl, timeout);
74
+ return this._lastHealthy;
75
+ }
76
+ /**
77
+ * 从 well-known URL 发现 Gateway。
78
+ *
79
+ * @param wellKnownUrl - well-known 发现端点(如 https://alice.aid.com/.well-known/aun-gateway)
80
+ * @param timeout - 请求超时(毫秒,默认 5000)
81
+ * @returns Gateway WebSocket URL
82
+ */
83
+ async discover(wellKnownUrl, timeout = 5_000) {
84
+ let payload;
85
+ try {
86
+ const rawPayload = await _httpGetJson(wellKnownUrl, this._verifySsl, timeout);
87
+ if (!isJsonObject(rawPayload)) {
88
+ throw new ValidationError('well-known returned invalid payload');
89
+ }
90
+ payload = rawPayload;
91
+ }
92
+ catch (err) {
93
+ throw new ConnectionError(`gateway discovery failed for ${wellKnownUrl}: ${err instanceof Error ? err.message : String(err)}`, { retryable: true });
94
+ }
95
+ const gateways = payload.gateways;
96
+ if (!Array.isArray(gateways) || gateways.length === 0) {
97
+ throw new ValidationError('well-known returned empty gateways');
98
+ }
99
+ // 按 priority 排序(数值越小优先级越高)
100
+ const sorted = [...gateways].sort((a, b) => (Number(a.priority ?? 999)) - (Number(b.priority ?? 999)));
101
+ const url = sorted[0]?.url;
102
+ if (!url || typeof url !== 'string') {
103
+ throw new ValidationError('well-known missing gateway url');
104
+ }
105
+ // 发现后自动检查网关可用性(不阻塞,失败不影响返回)
106
+ this.checkHealth(url, timeout).catch(() => { });
107
+ return url;
108
+ }
109
+ }
110
+ //# sourceMappingURL=discovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.js","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * GroupE2EEManager — 群组端到端加密管理器 + 纯函数
3
+ *
4
+ * 与 E2EEManager 平行:所有网络操作(P2P 发送、RPC 调用)由调用方负责。
5
+ * 内置防重放、epoch 降级防护、密钥请求频率限制。
6
+ */
7
+ import type { KeyStore } from './keystore/index.js';
8
+ import { type IdentityRecord, type JsonObject, type Message } from './types.js';
9
+ export interface LoadedGroupSecret {
10
+ epoch: number;
11
+ secret: Buffer;
12
+ commitment: string;
13
+ member_aids: string[];
14
+ epoch_chain?: string;
15
+ pending_rotation_id?: string;
16
+ epoch_chain_unverified?: boolean;
17
+ epoch_chain_unverified_reason?: string;
18
+ }
19
+ /**
20
+ * 计算 Epoch Transcript Chain。
21
+ * genesis(prev_chain=null)使用固定前缀;后续 epoch 使用上一个 chain 的字节。
22
+ */
23
+ export declare function computeEpochChain(prevChain: string | null, epoch: number, commitment: string, rotatorAid: string): string;
24
+ /**
25
+ * 验证 Epoch Transcript Chain(常量时间比较)。
26
+ */
27
+ export declare function verifyEpochChain(epochChain: string, prevChain: string | null, epoch: number, commitment: string, rotatorAid: string): boolean;
28
+ export declare function signGroupKeyResponse(payload: JsonObject, privateKeyPem: string): JsonObject;
29
+ export declare function verifyGroupKeyResponseSignature(payload: JsonObject, responderCertPem: string): boolean;
30
+ /**
31
+ * 加密群组消息,返回 e2ee.group_encrypted 信封。
32
+ * senderPrivateKeyPem: 可选,传入时为密文附加发送方 ECDSA 签名。
33
+ */
34
+ export declare function encryptGroupMessage(groupId: string, epoch: number, groupSecret: Buffer, payload: JsonObject, opts: {
35
+ fromAid: string;
36
+ messageId: string;
37
+ timestamp: number;
38
+ senderPrivateKeyPem?: string | null;
39
+ senderCertPem?: string | null;
40
+ }): JsonObject;
41
+ /**
42
+ * 解密群组消息。
43
+ * groupSecrets: {epoch: groupSecretBytes} 映射。
44
+ * requireSignature: 为 true 时(默认),若消息含签名但无证书可验证,或缺少签名,则拒绝。
45
+ */
46
+ export declare function decryptGroupMessage(message: Message, groupSecrets: Map<number, Buffer>, senderCertPem?: string | null, opts?: {
47
+ requireSignature?: boolean;
48
+ }): Message | null;
49
+ /**
50
+ * 计算 Membership Commitment。
51
+ * commitment = SHA-256(sort(aids).join("|") + "|" + epoch + "|" + groupId + "|" + SHA256(groupSecret).hex())
52
+ */
53
+ export declare function computeMembershipCommitment(memberAids: string[], epoch: number, groupId: string, groupSecret: Buffer): string;
54
+ /**
55
+ * 验证 Membership Commitment。
56
+ * 1. 重算 commitment 是否匹配
57
+ * 2. 检查 myAid 是否在 memberAids 中
58
+ */
59
+ export declare function verifyMembershipCommitment(commitment: string, memberAids: string[], epoch: number, groupId: string, myAid: string, groupSecret: Buffer): boolean;
60
+ /** 构建 Membership Manifest(未签名) */
61
+ export declare function buildMembershipManifest(groupId: string, epoch: number, prevEpoch: number | null, memberAids: string[], opts?: {
62
+ added?: string[];
63
+ removed?: string[];
64
+ initiatorAid?: string;
65
+ }): JsonObject;
66
+ /** 对 Membership Manifest 签名,返回带 signature 字段的新 manifest */
67
+ export declare function signMembershipManifest(manifest: JsonObject, privateKeyPem: string): JsonObject;
68
+ /** 验证 Membership Manifest 签名 */
69
+ export declare function verifyMembershipManifest(manifest: JsonObject, initiatorCertPem: string): boolean;
70
+ /** 存储 group_secret 到 keystore metadata */
71
+ export declare function storeGroupSecret(keystore: KeyStore, aid: string, groupId: string, epoch: number, groupSecret: Buffer, commitment: string, memberAids: string[], epochChain?: string, pendingRotationId?: string, epochChainUnverified?: boolean | null, epochChainUnverifiedReason?: string | null): boolean;
72
+ /** 保存指定 epoch key;低于 current 时写入 old epoch,不覆盖 current。 */
73
+ export declare function storeGroupSecretEpoch(keystore: KeyStore, aid: string, groupId: string, epoch: number, groupSecret: Buffer, commitment: string, memberAids: string[], epochChain?: string, pendingRotationId?: string, epochChainUnverified?: boolean | null, epochChainUnverifiedReason?: string | null): boolean;
74
+ /** 读取 group_secret */
75
+ export declare function loadGroupSecret(keystore: KeyStore, aid: string, groupId: string, epoch?: number | null): LoadedGroupSecret | null;
76
+ /** 加载某群组所有 epoch 的 group_secret */
77
+ export declare function loadAllGroupSecrets(keystore: KeyStore, aid: string, groupId: string): Map<number, Buffer>;
78
+ /** 清理过期的旧 epoch 记录。返回清理数量。 */
79
+ export declare function cleanupOldEpochs(keystore: KeyStore, aid: string, groupId: string, retentionSeconds?: number): number;
80
+ /** 仅回滚指定 rotation 写入的本地 pending epoch key。 */
81
+ export declare function discardPendingGroupSecret(keystore: KeyStore, aid: string, groupId: string, epoch: number, rotationId: string): boolean;
82
+ /** 删除群组的所有密钥数据(群组解散时使用) */
83
+ export declare function deleteGroupSecret(keystore: KeyStore, aid: string, groupId: string): void;
84
+ /** 群组消息防重放守卫 */
85
+ export declare class GroupReplayGuard {
86
+ private _seen;
87
+ private _maxSize;
88
+ constructor(maxSize?: number);
89
+ /** 检查并记录。返回 true 表示首次(通过),false 表示重放(拒绝)。 */
90
+ checkAndRecord(groupId: string, senderAid: string, messageId: string): boolean;
91
+ /** 仅检查是否已记录 */
92
+ isSeen(groupId: string, senderAid: string, messageId: string): boolean;
93
+ /** 仅记录,不检查 */
94
+ record(groupId: string, senderAid: string, messageId: string): void;
95
+ /** LRU 裁剪(供外部调用) */
96
+ trim(): void;
97
+ get size(): number;
98
+ }
99
+ /** 群组密钥请求/响应频率限制 */
100
+ export declare class GroupKeyRequestThrottle {
101
+ private _last;
102
+ private _cooldown;
103
+ constructor(cooldown?: number);
104
+ /** 检查是否允许操作 */
105
+ allow(key: string): boolean;
106
+ reset(key: string): void;
107
+ }
108
+ /** 生成 32 字节随机 group_secret */
109
+ export declare function generateGroupSecret(): Buffer;
110
+ /** 构建 group key 分发消息 payload */
111
+ export declare function buildKeyDistribution(groupId: string, epoch: number, groupSecret: Buffer, memberAids: string[], distributedBy: string, manifest?: JsonObject | null, epochChain?: string): JsonObject;
112
+ /** 处理收到的 group key 分发消息 */
113
+ export declare function handleKeyDistribution(message: Message | JsonObject, keystore: KeyStore, aid: string, initiatorCertPem?: string | null): boolean;
114
+ /** 构建密钥请求 payload */
115
+ export declare function buildKeyRequest(groupId: string, epoch: number, requesterAid: string, requestId?: string): JsonObject;
116
+ /** 处理收到的密钥请求 */
117
+ export declare function handleKeyRequest(request: Message | JsonObject, keystore: KeyStore, aid: string, currentMembers: string[], privateKeyPem?: string | null): JsonObject | null;
118
+ /** 处理收到的密钥响应 */
119
+ export declare function handleKeyResponse(response: Message | JsonObject, keystore: KeyStore, aid: string, opts?: {
120
+ expectedRequest?: JsonObject | null;
121
+ responderCertPem?: string | null;
122
+ currentMembers?: string[];
123
+ strict?: boolean;
124
+ }): boolean;
125
+ export declare class GroupE2EEManager {
126
+ private _identityFn;
127
+ private _keystore;
128
+ private _replayGuard;
129
+ private _requestThrottle;
130
+ private _responseThrottle;
131
+ private _senderCertResolver;
132
+ private _initiatorCertResolver;
133
+ private _pendingKeyRequests;
134
+ constructor(opts: {
135
+ identityFn: () => IdentityRecord;
136
+ keystore: KeyStore;
137
+ requestCooldown?: number;
138
+ responseCooldown?: number;
139
+ senderCertResolver?: (aid: string) => string | null;
140
+ initiatorCertResolver?: (aid: string) => string | null;
141
+ });
142
+ /** 用当前身份私钥签名 manifest */
143
+ private _signManifest;
144
+ /** 创建首个 epoch。返回 {epoch, commitment, distributions: [{to, payload}]} */
145
+ createEpoch(groupId: string, memberAids: string[]): JsonObject;
146
+ /** 轮换 epoch(踢人/退出后调用) */
147
+ rotateEpoch(groupId: string, memberAids: string[]): JsonObject;
148
+ /** 指定目标 epoch 号轮换(配合服务端 CAS 使用) */
149
+ rotateEpochTo(groupId: string, newEpoch: number, memberAids: string[], opts?: {
150
+ rotationId?: string;
151
+ }): JsonObject;
152
+ discardPendingSecret(groupId: string, epoch: number, rotationId: string): boolean;
153
+ /** 加密群消息(含发送方签名)。无密钥时抛 E2EEGroupSecretMissingError。 */
154
+ encrypt(groupId: string, payload: JsonObject): JsonObject;
155
+ /** 使用指定 epoch 加密群消息。 */
156
+ encryptWithEpoch(groupId: string, epoch: number, payload: JsonObject): JsonObject;
157
+ /** 解密单条群消息。内置防重放 + 发送方验签。 */
158
+ decrypt(message: Message, opts?: {
159
+ skipReplay?: boolean;
160
+ }): Message | null;
161
+ /** 批量解密 */
162
+ decryptBatch(messages: Message[], opts?: {
163
+ skipReplay?: boolean;
164
+ }): Message[];
165
+ /**
166
+ * 处理已解密的 P2P 密钥消息。
167
+ * 返回 "distribution"/"request"/"response" 表示已成功处理。
168
+ * 返回 "distribution_rejected"/"response_rejected" 表示被拒绝。
169
+ * 返回 null 表示不是密钥消息。
170
+ */
171
+ handleIncoming(payload: JsonObject): string | null;
172
+ /** 构建恢复请求。返回 {to, payload} 或 null(限流/无目标)。 */
173
+ buildRecoveryRequest(groupId: string, epoch: number, senderAid?: string): JsonObject | null;
174
+ rememberKeyRequest(payload: JsonObject, expectedResponderAid?: string | null): void;
175
+ /** 处理密钥请求。返回响应 payload(受频率限制 + 成员资格验证)。 */
176
+ handleKeyRequestMsg(requestPayload: JsonObject, members: string[]): JsonObject | null;
177
+ /** 检查是否有群组密钥 */
178
+ hasSecret(groupId: string): boolean;
179
+ /** 获取当前 epoch */
180
+ currentEpoch(groupId: string): number | null;
181
+ /** 获取群组成员 AID 列表 */
182
+ getMemberAids(groupId: string): string[];
183
+ /** 加载群组密钥数据 */
184
+ loadSecret(groupId: string, epoch?: number | null): LoadedGroupSecret | null;
185
+ /** 清理过期的旧 epoch */
186
+ cleanup(groupId: string, retentionSeconds?: number): void;
187
+ /** 清理过期缓存(replay guard 等),供外部定时调用 */
188
+ cleanExpiredCaches(): void;
189
+ /** 删除群组的所有本地状态(群组解散时使用) */
190
+ removeGroup(groupId: string): void;
191
+ private _currentAid;
192
+ }