@agentunion/fastaun 0.2.20 → 0.3.1

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 (105) hide show
  1. package/CHANGELOG.md +63 -23
  2. package/_packed_docs/CHANGELOG.md +63 -23
  3. package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +542 -0
  4. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1 -24
  5. 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 -0
  6. package/_packed_docs/protocol/index.md +13 -3
  7. package/_packed_docs/python-sdk-v2-only-changelog.md +189 -0
  8. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +39 -16
  9. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +131 -39
  10. package/_packed_docs/sdk/09-message-rpc-manual.md +30 -67
  11. package/dist/auth.js +26 -7
  12. package/dist/auth.js.map +1 -1
  13. package/dist/client.d.ts +117 -166
  14. package/dist/client.js +2130 -3419
  15. package/dist/client.js.map +1 -1
  16. package/dist/config.d.ts +0 -4
  17. package/dist/config.js +0 -4
  18. package/dist/config.js.map +1 -1
  19. package/dist/e2ee.d.ts +5 -139
  20. package/dist/e2ee.js +4 -1151
  21. package/dist/e2ee.js.map +1 -1
  22. package/dist/errors.d.ts +0 -8
  23. package/dist/errors.js +0 -14
  24. package/dist/errors.js.map +1 -1
  25. package/dist/index.d.ts +9 -5
  26. package/dist/index.js +6 -3
  27. package/dist/index.js.map +1 -1
  28. package/dist/keystore/aid-db.d.ts +12 -61
  29. package/dist/keystore/aid-db.js +41 -539
  30. package/dist/keystore/aid-db.js.map +1 -1
  31. package/dist/keystore/file.d.ts +5 -41
  32. package/dist/keystore/file.js +8 -64
  33. package/dist/keystore/file.js.map +1 -1
  34. package/dist/keystore/index.d.ts +1 -49
  35. package/dist/namespaces/auth.d.ts +8 -0
  36. package/dist/namespaces/auth.js +169 -2
  37. package/dist/namespaces/auth.js.map +1 -1
  38. package/dist/protected-headers.d.ts +13 -0
  39. package/dist/protected-headers.js +47 -0
  40. package/dist/protected-headers.js.map +1 -0
  41. package/dist/seq-tracker.d.ts +7 -2
  42. package/dist/seq-tracker.js +33 -13
  43. package/dist/seq-tracker.js.map +1 -1
  44. package/dist/transport.d.ts +11 -1
  45. package/dist/transport.js +255 -6
  46. package/dist/transport.js.map +1 -1
  47. package/dist/types.d.ts +0 -56
  48. package/dist/v2/crypto/aead.d.ts +20 -0
  49. package/dist/v2/crypto/aead.js +59 -0
  50. package/dist/v2/crypto/aead.js.map +1 -0
  51. package/dist/v2/crypto/canonical.d.ts +20 -0
  52. package/dist/v2/crypto/canonical.js +119 -0
  53. package/dist/v2/crypto/canonical.js.map +1 -0
  54. package/dist/v2/crypto/dh-path.d.ts +39 -0
  55. package/dist/v2/crypto/dh-path.js +55 -0
  56. package/dist/v2/crypto/dh-path.js.map +1 -0
  57. package/dist/v2/crypto/ecdh.d.ts +29 -0
  58. package/dist/v2/crypto/ecdh.js +122 -0
  59. package/dist/v2/crypto/ecdh.js.map +1 -0
  60. package/dist/v2/crypto/ecdsa.d.ts +29 -0
  61. package/dist/v2/crypto/ecdsa.js +120 -0
  62. package/dist/v2/crypto/ecdsa.js.map +1 -0
  63. package/dist/v2/crypto/hkdf.d.ts +19 -0
  64. package/dist/v2/crypto/hkdf.js +47 -0
  65. package/dist/v2/crypto/hkdf.js.map +1 -0
  66. package/dist/v2/crypto/index.d.ts +8 -0
  67. package/dist/v2/crypto/index.js +8 -0
  68. package/dist/v2/crypto/index.js.map +1 -0
  69. package/dist/v2/crypto/recipients.d.ts +32 -0
  70. package/dist/v2/crypto/recipients.js +183 -0
  71. package/dist/v2/crypto/recipients.js.map +1 -0
  72. package/dist/v2/e2ee/decrypt.d.ts +29 -0
  73. package/dist/v2/e2ee/decrypt.js +159 -0
  74. package/dist/v2/e2ee/decrypt.js.map +1 -0
  75. package/dist/v2/e2ee/encrypt-group.d.ts +17 -0
  76. package/dist/v2/e2ee/encrypt-group.js +143 -0
  77. package/dist/v2/e2ee/encrypt-group.js.map +1 -0
  78. package/dist/v2/e2ee/encrypt-p2p.d.ts +31 -0
  79. package/dist/v2/e2ee/encrypt-p2p.js +190 -0
  80. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -0
  81. package/dist/v2/e2ee/index.d.ts +9 -0
  82. package/dist/v2/e2ee/index.js +9 -0
  83. package/dist/v2/e2ee/index.js.map +1 -0
  84. package/dist/v2/e2ee/metadata-auth.d.ts +15 -0
  85. package/dist/v2/e2ee/metadata-auth.js +50 -0
  86. package/dist/v2/e2ee/metadata-auth.js.map +1 -0
  87. package/dist/v2/e2ee/types.d.ts +57 -0
  88. package/dist/v2/e2ee/types.js +7 -0
  89. package/dist/v2/e2ee/types.js.map +1 -0
  90. package/dist/v2/session/index.d.ts +4 -0
  91. package/dist/v2/session/index.js +3 -0
  92. package/dist/v2/session/index.js.map +1 -0
  93. package/dist/v2/session/keystore.d.ts +50 -0
  94. package/dist/v2/session/keystore.js +138 -0
  95. package/dist/v2/session/keystore.js.map +1 -0
  96. package/dist/v2/session/session.d.ts +124 -0
  97. package/dist/v2/session/session.js +318 -0
  98. package/dist/v2/session/session.js.map +1 -0
  99. package/dist/v2/state/commitment.d.ts +58 -0
  100. package/dist/v2/state/commitment.js +85 -0
  101. package/dist/v2/state/commitment.js.map +1 -0
  102. package/dist/v2/state/index.d.ts +2 -0
  103. package/dist/v2/state/index.js +2 -0
  104. package/dist/v2/state/index.js.map +1 -0
  105. package/package.json +4 -3
@@ -0,0 +1,190 @@
1
+ /**
2
+ * AUN E2EE V2: P2P 加密引擎
3
+ *
4
+ * 构造完整的 `e2ee.p2p_encrypted` envelope。纯计算,无 IO。
5
+ *
6
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p.encrypt_p2p_message` 对齐。
7
+ *
8
+ * 步骤:
9
+ * 1. 生成 master_key + msg_nonce
10
+ * 2. 构造 message_id / timestamp
11
+ * 3. 推导 peer_aid 与 wrap_protocol_str
12
+ * 4. 构造 AAD 并加密 payload
13
+ * 5. 生成 sender_session keypair
14
+ * 6. 计算 wrap_salt
15
+ * 7. 为每个 target wrap master_key
16
+ * 8. 排序 recipients + 计算 digest
17
+ * 9. 计算 sender_signature
18
+ * 10. 计算 sender_cert_fingerprint
19
+ * 11. 组装 envelope
20
+ */
21
+ import { createHash, randomUUID, randomBytes } from 'node:crypto';
22
+ import { canonicalJson } from '../crypto/canonical.js';
23
+ import { ecdsaSignRaw } from '../crypto/ecdsa.js';
24
+ import { aesGcmEncrypt } from '../crypto/aead.js';
25
+ import { generateP256Keypair } from '../crypto/ecdh.js';
26
+ import { compute3DHWrap, compute1DHWrap } from '../crypto/dh-path.js';
27
+ import { sortRecipients, computeRecipientsDigest } from '../crypto/recipients.js';
28
+ import { ProtectedHeaders } from '../../protected-headers.js';
29
+ import { SUITE_NAME, } from './types.js';
30
+ import { withMetadataAuth, PROTECTED_HEADERS_DOMAIN, PROTECTED_CONTEXT_DOMAIN } from './metadata-auth.js';
31
+ const TEXT = new TextEncoder();
32
+ /**
33
+ * 构造完整的 V2 P2P 加密 envelope。
34
+ */
35
+ export function encryptP2PMessage(sender, targetSet, payload, opts = {}) {
36
+ // 1. master_key + msg_nonce
37
+ const masterKey = new Uint8Array(randomBytes(32));
38
+ const msgNonce = new Uint8Array(randomBytes(12));
39
+ // 2. message_id + timestamp
40
+ const messageId = opts.messageId ?? `m-${randomUUID().replace(/-/g, '')}`;
41
+ const timestamp = opts.timestamp ?? Date.now();
42
+ // 3. peer_aid(首个非 audit 的 target.aid;与 Python 一致)
43
+ let peerAid = '';
44
+ for (const t of targetSet.targets) {
45
+ if (t.role !== 'audit') {
46
+ peerAid = t.aid;
47
+ break;
48
+ }
49
+ }
50
+ // 4. wrap_protocol_str
51
+ const allTargets = [
52
+ ...targetSet.targets,
53
+ ...(targetSet.auditRecipients ?? []),
54
+ ];
55
+ const protocolSet = new Set();
56
+ for (const t of allTargets) {
57
+ const has3DH = !!t.spkPkDer &&
58
+ (t.keySource === 'peer_device_prekey' || t.keySource === 'group_device_prekey');
59
+ protocolSet.add(has3DH ? '3DH' : '1DH');
60
+ }
61
+ const wrapProtocolStr = protocolSet.size === 0 ? '1DH' : [...protocolSet].sort().join('+');
62
+ // 5. AAD(顺序在 canonical_json 时由键名排序统一,这里只是构造对象)
63
+ const aad = {
64
+ from: sender.aid,
65
+ from_device: sender.deviceId,
66
+ to: peerAid,
67
+ message_id: messageId,
68
+ timestamp,
69
+ suite: SUITE_NAME,
70
+ wrap_protocol: wrapProtocolStr,
71
+ };
72
+ // 6. encrypt body
73
+ const plaintextBytes = canonicalJson(payload);
74
+ const aadBytes = canonicalJson(aad);
75
+ const { ciphertext, tag } = aesGcmEncrypt(masterKey, msgNonce, plaintextBytes, aadBytes);
76
+ // 7. sender_session keypair
77
+ const [senderSessionPriv, senderSessionPubDer] = generateP256Keypair();
78
+ // 8. wrap_salt = SHA256(canonical_aad || sender_session_pk_der || suite)[:16]
79
+ const wrapSalt = computeWrapSalt(aadBytes, senderSessionPubDer);
80
+ // 9. wrap recipients
81
+ const recipientsRows = [];
82
+ for (const target of allTargets) {
83
+ recipientsRows.push(wrapForRecipient(target, masterKey, senderSessionPriv, sender.ikPriv, wrapSalt));
84
+ }
85
+ // 10. sort + digest
86
+ const sortedRows = sortRecipients(recipientsRows);
87
+ const digestHex = computeRecipientsDigest(sortedRows);
88
+ // 11. sender_signature
89
+ const digestBytes = Buffer.from(digestHex, 'hex');
90
+ const signInput = Buffer.concat([
91
+ Buffer.from(ciphertext),
92
+ Buffer.from(tag),
93
+ Buffer.from(aadBytes),
94
+ digestBytes,
95
+ ]);
96
+ const senderSig = ecdsaSignRaw(sender.ikPriv, new Uint8Array(signInput));
97
+ // 12. cert_fingerprint
98
+ const certFpHash = createHash('sha256').update(Buffer.from(sender.ikPubDer)).digest('hex');
99
+ const certFp = `sha256:${certFpHash.substring(0, 16)}`;
100
+ const envelope = {
101
+ type: 'e2ee.p2p_encrypted',
102
+ version: 'v2',
103
+ suite: SUITE_NAME,
104
+ msg_type: 'original',
105
+ t_send: timestamp,
106
+ t_supplement: null,
107
+ t_server: null,
108
+ nonce: Buffer.from(msgNonce).toString('base64'),
109
+ ciphertext: Buffer.from(ciphertext).toString('base64'),
110
+ tag: Buffer.from(tag).toString('base64'),
111
+ sender_signature: Buffer.from(senderSig).toString('base64'),
112
+ sender_cert_fingerprint: certFp,
113
+ sender_session_pk: Buffer.from(senderSessionPubDer).toString('base64'),
114
+ recipients_digest: digestHex,
115
+ recipients: sortedRows,
116
+ aad,
117
+ };
118
+ // protected_headers / context:HMAC 签名,不进 AAD。
119
+ // payload_type 自动注入 + value 转 string(与 Python _normalize_headers 对齐)
120
+ const normalizedHeaders = normalizeProtectedHeaders(opts.protectedHeaders, payload);
121
+ if (Object.keys(normalizedHeaders).length > 0) {
122
+ envelope.protected_headers = withMetadataAuth(normalizedHeaders, masterKey, PROTECTED_HEADERS_DOMAIN);
123
+ }
124
+ if (isPlainObject(opts.context) && Object.keys(opts.context).length > 0) {
125
+ // context 过滤 _auth 字段(withMetadataAuth 内部已处理)
126
+ envelope.context = withMetadataAuth(opts.context, masterKey, PROTECTED_CONTEXT_DOMAIN);
127
+ }
128
+ return envelope;
129
+ }
130
+ /**
131
+ * 规范化 protected_headers:value 转 string + 自动注入 payload_type。
132
+ * 与 Python `_normalize_headers` 对齐。
133
+ */
134
+ export function normalizeProtectedHeaders(headers, payload) {
135
+ const normalized = {};
136
+ if (headers instanceof ProtectedHeaders) {
137
+ Object.assign(normalized, headers.toObject());
138
+ }
139
+ else if (isPlainObject(headers)) {
140
+ Object.assign(normalized, ProtectedHeaders.from(headers).toObject());
141
+ }
142
+ // payload_type 自动注入(与 Python 对齐:即使未显式传 protected_headers 也会注入)
143
+ const payloadType = typeof payload?.type === 'string' ? payload.type : '';
144
+ if (payloadType && !('payload_type' in normalized)) {
145
+ normalized['payload_type'] = payloadType;
146
+ }
147
+ return normalized;
148
+ }
149
+ function isPlainObject(value) {
150
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
151
+ return false;
152
+ }
153
+ const proto = Object.getPrototypeOf(value);
154
+ return proto === Object.prototype || proto === null;
155
+ }
156
+ function computeWrapSalt(aadBytes, senderSessionPubDer) {
157
+ const suiteBytes = TEXT.encode(SUITE_NAME);
158
+ const h = createHash('sha256');
159
+ h.update(Buffer.from(aadBytes));
160
+ h.update(Buffer.from(senderSessionPubDer));
161
+ h.update(Buffer.from(suiteBytes));
162
+ return new Uint8Array(h.digest()).subarray(0, 16);
163
+ }
164
+ function wrapForRecipient(target, masterKey, senderSessionPriv, senderMasterPriv, wrapSalt) {
165
+ const fpHash = createHash('sha256').update(Buffer.from(target.ikPkDer)).digest('hex');
166
+ const fp = `sha256:${fpHash.substring(0, 16)}`;
167
+ const wrapNonce = new Uint8Array(randomBytes(12));
168
+ let wrapKey;
169
+ if (target.spkPkDer &&
170
+ (target.keySource === 'peer_device_prekey' || target.keySource === 'group_device_prekey')) {
171
+ wrapKey = compute3DHWrap(senderSessionPriv, senderMasterPriv, target.ikPkDer, target.spkPkDer, wrapSalt);
172
+ }
173
+ else {
174
+ wrapKey = compute1DHWrap(senderSessionPriv, target.ikPkDer, wrapSalt);
175
+ }
176
+ // AES-GCM wrap master_key(无 AAD)
177
+ const { ciphertext: wrappedCt, tag: wrappedTag } = aesGcmEncrypt(wrapKey, wrapNonce, masterKey, new Uint8Array(0));
178
+ const wrappedKey = Buffer.concat([Buffer.from(wrappedCt), Buffer.from(wrappedTag)]);
179
+ return [
180
+ target.aid,
181
+ target.deviceId,
182
+ target.role,
183
+ target.keySource,
184
+ fp,
185
+ target.spkId ?? '',
186
+ Buffer.from(wrapNonce).toString('base64'),
187
+ wrappedKey.toString('base64'),
188
+ ];
189
+ }
190
+ //# sourceMappingURL=encrypt-p2p.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"encrypt-p2p.js","sourceRoot":"","sources":["../../../src/v2/e2ee/encrypt-p2p.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎导出。
3
+ */
4
+ export { encryptP2PMessage } from './encrypt-p2p.js';
5
+ export { encryptGroupMessage } from './encrypt-group.js';
6
+ export { decryptMessage } from './decrypt.js';
7
+ export { withMetadataAuth, PROTECTED_HEADERS_DOMAIN, PROTECTED_CONTEXT_DOMAIN, METADATA_KEY_DOMAIN } from './metadata-auth.js';
8
+ export type { Sender, Target, TargetSet, EncryptOptions, StateCommitmentAAD } from './types.js';
9
+ export { SUITE_NAME } from './types.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎导出。
3
+ */
4
+ export { encryptP2PMessage } from './encrypt-p2p.js';
5
+ export { encryptGroupMessage } from './encrypt-group.js';
6
+ export { decryptMessage } from './decrypt.js';
7
+ export { withMetadataAuth, PROTECTED_HEADERS_DOMAIN, PROTECTED_CONTEXT_DOMAIN, METADATA_KEY_DOMAIN } from './metadata-auth.js';
8
+ export { SUITE_NAME } from './types.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/e2ee/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAE/H,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * AUN E2EE V2: protected_headers / context HMAC 签名
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p._with_metadata_auth` 对齐。
5
+ * 用 master_key 派生 HMAC key,对 metadata body 做签名,生成 `_auth` 字段。
6
+ */
7
+ export declare const METADATA_KEY_DOMAIN: Buffer<ArrayBuffer>;
8
+ export declare const PROTECTED_HEADERS_DOMAIN: Buffer<ArrayBuffer>;
9
+ export declare const PROTECTED_CONTEXT_DOMAIN: Buffer<ArrayBuffer>;
10
+ /**
11
+ * 为 metadata 对象添加 HMAC 签名(_auth 字段)。
12
+ *
13
+ * 如果 body(去除 _auth 后)为空,返回空对象。
14
+ */
15
+ export declare function withMetadataAuth(metadata: Record<string, unknown>, key: Uint8Array, domain: Buffer): Record<string, unknown>;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * AUN E2EE V2: protected_headers / context HMAC 签名
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p._with_metadata_auth` 对齐。
5
+ * 用 master_key 派生 HMAC key,对 metadata body 做签名,生成 `_auth` 字段。
6
+ */
7
+ import { createHmac } from 'node:crypto';
8
+ import { canonicalJson } from '../crypto/canonical.js';
9
+ export const METADATA_KEY_DOMAIN = Buffer.from('aun-envelope-metadata-key-v1', 'utf-8');
10
+ export const PROTECTED_HEADERS_DOMAIN = Buffer.from('aun-protected-headers-v1', 'utf-8');
11
+ export const PROTECTED_CONTEXT_DOMAIN = Buffer.from('aun-protected-context-v1', 'utf-8');
12
+ /**
13
+ * 计算 metadata HMAC 签名 tag。
14
+ *
15
+ * metadata_key = HMAC-SHA256(key, METADATA_KEY_DOMAIN)
16
+ * sign_input = domain + "\0" + canonical_json(body)
17
+ * tag = HMAC-SHA256(metadata_key, sign_input)
18
+ */
19
+ function metadataAuthTag(key, domain, body) {
20
+ const metadataKey = createHmac('sha256', key).update(METADATA_KEY_DOMAIN).digest();
21
+ const bodyBytes = canonicalJson(body);
22
+ return createHmac('sha256', metadataKey)
23
+ .update(domain)
24
+ .update(Buffer.from([0]))
25
+ .update(Buffer.from(bodyBytes))
26
+ .digest();
27
+ }
28
+ /**
29
+ * 为 metadata 对象添加 HMAC 签名(_auth 字段)。
30
+ *
31
+ * 如果 body(去除 _auth 后)为空,返回空对象。
32
+ */
33
+ export function withMetadataAuth(metadata, key, domain) {
34
+ const body = {};
35
+ for (const [k, v] of Object.entries(metadata)) {
36
+ if (k !== '_auth')
37
+ body[k] = v;
38
+ }
39
+ if (Object.keys(body).length === 0)
40
+ return {};
41
+ const tag = metadataAuthTag(key, domain, body);
42
+ return {
43
+ ...body,
44
+ _auth: {
45
+ alg: 'HMAC-SHA256',
46
+ tag: tag.toString('base64'),
47
+ },
48
+ };
49
+ }
50
+ //# sourceMappingURL=metadata-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-auth.js","sourceRoot":"","sources":["../../../src/v2/e2ee/metadata-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEvD,MAAM,CAAC,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;AACxF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;AACzF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,OAAO,CAAC,CAAC;AAEzF;;;;;;GAMG;AACH,SAAS,eAAe,CAAC,GAAe,EAAE,MAAc,EAAE,IAA6B;IACrF,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,MAAM,EAAE,CAAC;IACnF,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,UAAU,CAAC,QAAQ,EAAE,WAAW,CAAC;SACrC,MAAM,CAAC,MAAM,CAAC;SACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACxB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SAC9B,MAAM,EAAE,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAiC,EACjC,GAAe,EACf,MAAc;IAEd,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9C,IAAI,CAAC,KAAK,OAAO;YAAE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,OAAO;QACL,GAAG,IAAI;QACP,KAAK,EAAE;YACL,GAAG,EAAE,aAAa;YAClB,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC;SAC5B;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎类型定义
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p` / `encrypt_group` / `decrypt` 对齐。
5
+ */
6
+ import type { ProtectedHeadersInput } from '../../protected-headers.js';
7
+ export declare const SUITE_NAME: "P256_HKDF_SHA256_AES_256_GCM";
8
+ /** 发送方身份。 */
9
+ export interface Sender {
10
+ /** 发送方 AID。 */
11
+ aid: string;
12
+ /** 发送方 device_id。 */
13
+ deviceId: string;
14
+ /** 32 字节 P-256 私钥标量(AID 主私钥)。 */
15
+ ikPriv: Uint8Array;
16
+ /** SPKI DER 编码的公钥(用于签名指纹计算)。 */
17
+ ikPubDer: Uint8Array;
18
+ }
19
+ /** 接收方目标设备。 */
20
+ export interface Target {
21
+ aid: string;
22
+ deviceId: string;
23
+ /** "peer" | "member" | "self_sync" | "audit" 等。 */
24
+ role: string;
25
+ /** "peer_device_prekey" | "group_device_prekey" | "aid_master"。 */
26
+ keySource: string;
27
+ /** 接收方 IK 公钥(DER SPKI)。 */
28
+ ikPkDer: Uint8Array;
29
+ /** 接收方 SPK 公钥(DER SPKI);undefined 表示走 1DH 路径。 */
30
+ spkPkDer?: Uint8Array;
31
+ /** SPK 标识;3DH 时为非空字符串,1DH 时为空串/未定义。 */
32
+ spkId?: string;
33
+ }
34
+ /** 接收方集合(P2P)。 */
35
+ export interface TargetSet {
36
+ /** 普通接收设备。 */
37
+ targets: Target[];
38
+ /** 监管方设备(可选)。 */
39
+ auditRecipients?: Target[];
40
+ }
41
+ /** 加密可选参数。 */
42
+ export interface EncryptOptions {
43
+ /** 消息 ID;不传则自动生成 `m-{uuid4 hex}`。 */
44
+ messageId?: string;
45
+ /** 时间戳(毫秒);不传则用 Date.now()。 */
46
+ timestamp?: number;
47
+ /** 端到端保护的信封元数据(HMAC 签名,不进 AAD)。 */
48
+ protectedHeaders?: ProtectedHeadersInput;
49
+ /** 端到端保护的上下文元数据(HMAC 签名,不进 AAD)。 */
50
+ context?: Record<string, unknown>;
51
+ }
52
+ /** Group AAD 中的 state_commitment 子结构。 */
53
+ export interface StateCommitmentAAD {
54
+ state_version: number;
55
+ state_hash: string;
56
+ state_chain: string;
57
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * AUN E2EE V2: 加解密引擎类型定义
3
+ *
4
+ * 与 Python `aun_core.v2.e2ee.encrypt_p2p` / `encrypt_group` / `decrypt` 对齐。
5
+ */
6
+ export const SUITE_NAME = 'P256_HKDF_SHA256_AES_256_GCM';
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/v2/e2ee/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,CAAC,MAAM,UAAU,GAAG,8BAAuC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { V2KeyStore, V2_DEVICE_KEYS_DDL } from './keystore.js';
2
+ export type { SqliteLike } from './keystore.js';
3
+ export { V2Session, PEER_KEY_CACHE_TTL_MS, DESTROY_DELAY_MS, RECENT_GENERATIONS, } from './session.js';
4
+ export type { CallFn, SenderIdentity } from './session.js';
@@ -0,0 +1,3 @@
1
+ export { V2KeyStore, V2_DEVICE_KEYS_DDL } from './keystore.js';
2
+ export { V2Session, PEER_KEY_CACHE_TTL_MS, DESTROY_DELAY_MS, RECENT_GENERATIONS, } from './session.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/v2/session/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAE/D,OAAO,EACL,SAAS,EACT,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * V2 E2EE 设备密钥存储。
3
+ *
4
+ * 每个 (aid, device_id) 持有一对 IK 和若干 SPK(按 spk_id 索引)。
5
+ * 表结构与 Python / Go SDK 完全一致(v2_device_keys)。
6
+ *
7
+ * 实现说明:
8
+ * - TS SDK 统一使用 Node 22+ 内置的 `node:sqlite`(与 `keystore/aid-db.ts` 保持一致)
9
+ * - 接受任意 sqlite-like 句柄(exec / prepare),便于复用 AIDDatabase 的连接
10
+ * - BLOB 字段返回 Uint8Array(node:sqlite 默认行为)
11
+ */
12
+ interface SqliteStatement {
13
+ run(...params: unknown[]): unknown;
14
+ get(...params: unknown[]): unknown;
15
+ all(...params: unknown[]): unknown;
16
+ }
17
+ export interface SqliteLike {
18
+ exec(sql: string): unknown;
19
+ prepare(sql: string): SqliteStatement;
20
+ }
21
+ export declare const V2_DEVICE_KEYS_DDL = "\nCREATE TABLE IF NOT EXISTS v2_device_keys (\n device_id TEXT NOT NULL,\n key_type TEXT NOT NULL,\n key_id TEXT NOT NULL DEFAULT '',\n private_key BLOB NOT NULL,\n public_key BLOB NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (device_id, key_type, key_id)\n)";
22
+ export declare class V2KeyStore {
23
+ private db;
24
+ constructor(db: SqliteLike);
25
+ saveIK(deviceId: string, priv: Uint8Array, pubDer: Uint8Array): void;
26
+ loadIK(deviceId: string): {
27
+ priv: Uint8Array;
28
+ pubDer: Uint8Array;
29
+ } | null;
30
+ saveSPK(deviceId: string, spkId: string, priv: Uint8Array, pubDer: Uint8Array): void;
31
+ loadSPK(deviceId: string, spkId: string): Uint8Array | null;
32
+ loadCurrentSPK(deviceId: string): {
33
+ spkId: string;
34
+ priv: Uint8Array;
35
+ pubDer: Uint8Array;
36
+ } | null;
37
+ deleteSPK(deviceId: string, spkId: string): void;
38
+ listRecentSPKIds(deviceId: string, n: number): string[];
39
+ listExpiredSPKIds(deviceId: string, maxAgeMs: number): string[];
40
+ /** 复合 key_id: `${groupId}\0${spkId}` */
41
+ private _groupSPKKeyId;
42
+ saveGroupSPK(deviceId: string, groupId: string, spkId: string, priv: Uint8Array, pubDer: Uint8Array): void;
43
+ loadGroupSPK(deviceId: string, groupId: string, spkId: string): Uint8Array | null;
44
+ loadCurrentGroupSPK(deviceId: string, groupId: string): {
45
+ spkId: string;
46
+ priv: Uint8Array;
47
+ pubDer: Uint8Array;
48
+ } | null;
49
+ }
50
+ export {};
@@ -0,0 +1,138 @@
1
+ /**
2
+ * V2 E2EE 设备密钥存储。
3
+ *
4
+ * 每个 (aid, device_id) 持有一对 IK 和若干 SPK(按 spk_id 索引)。
5
+ * 表结构与 Python / Go SDK 完全一致(v2_device_keys)。
6
+ *
7
+ * 实现说明:
8
+ * - TS SDK 统一使用 Node 22+ 内置的 `node:sqlite`(与 `keystore/aid-db.ts` 保持一致)
9
+ * - 接受任意 sqlite-like 句柄(exec / prepare),便于复用 AIDDatabase 的连接
10
+ * - BLOB 字段返回 Uint8Array(node:sqlite 默认行为)
11
+ */
12
+ export const V2_DEVICE_KEYS_DDL = `
13
+ CREATE TABLE IF NOT EXISTS v2_device_keys (
14
+ device_id TEXT NOT NULL,
15
+ key_type TEXT NOT NULL,
16
+ key_id TEXT NOT NULL DEFAULT '',
17
+ private_key BLOB NOT NULL,
18
+ public_key BLOB NOT NULL,
19
+ created_at INTEGER NOT NULL,
20
+ PRIMARY KEY (device_id, key_type, key_id)
21
+ )`;
22
+ function asBuffer(value) {
23
+ if (value instanceof Uint8Array)
24
+ return value;
25
+ if (Buffer.isBuffer(value))
26
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
27
+ if (Array.isArray(value))
28
+ return new Uint8Array(value);
29
+ throw new Error(`unexpected blob type: ${typeof value}`);
30
+ }
31
+ /** node:sqlite 不接受裸 Uint8Array,需要 Buffer 包装才会绑定为 BLOB。 */
32
+ function toSqliteBlob(value) {
33
+ return Buffer.isBuffer(value) ? value : Buffer.from(value.buffer, value.byteOffset, value.byteLength);
34
+ }
35
+ export class V2KeyStore {
36
+ db;
37
+ constructor(db) {
38
+ this.db = db;
39
+ this.db.exec(V2_DEVICE_KEYS_DDL);
40
+ }
41
+ saveIK(deviceId, priv, pubDer) {
42
+ this.db
43
+ .prepare(`INSERT OR REPLACE INTO v2_device_keys (device_id, key_type, key_id, private_key, public_key, created_at)
44
+ VALUES (?, 'ik', '', ?, ?, ?)`)
45
+ .run(deviceId, toSqliteBlob(priv), toSqliteBlob(pubDer), Date.now());
46
+ }
47
+ loadIK(deviceId) {
48
+ const row = this.db
49
+ .prepare(`SELECT private_key, public_key FROM v2_device_keys WHERE device_id=? AND key_type='ik' AND key_id=''`)
50
+ .get(deviceId);
51
+ if (!row)
52
+ return null;
53
+ return { priv: asBuffer(row.private_key), pubDer: asBuffer(row.public_key) };
54
+ }
55
+ saveSPK(deviceId, spkId, priv, pubDer) {
56
+ this.db
57
+ .prepare(`INSERT OR REPLACE INTO v2_device_keys (device_id, key_type, key_id, private_key, public_key, created_at)
58
+ VALUES (?, 'spk', ?, ?, ?, ?)`)
59
+ .run(deviceId, spkId, toSqliteBlob(priv), toSqliteBlob(pubDer), Date.now());
60
+ }
61
+ loadSPK(deviceId, spkId) {
62
+ const row = this.db
63
+ .prepare(`SELECT private_key FROM v2_device_keys WHERE device_id=? AND key_type='spk' AND key_id=?`)
64
+ .get(deviceId, spkId);
65
+ return row ? asBuffer(row.private_key) : null;
66
+ }
67
+ loadCurrentSPK(deviceId) {
68
+ const row = this.db
69
+ .prepare(`SELECT key_id, private_key, public_key FROM v2_device_keys
70
+ WHERE device_id=? AND key_type='spk' ORDER BY created_at DESC, key_id DESC LIMIT 1`)
71
+ .get(deviceId);
72
+ if (!row)
73
+ return null;
74
+ return {
75
+ spkId: row.key_id,
76
+ priv: asBuffer(row.private_key),
77
+ pubDer: asBuffer(row.public_key),
78
+ };
79
+ }
80
+ deleteSPK(deviceId, spkId) {
81
+ this.db
82
+ .prepare(`DELETE FROM v2_device_keys WHERE device_id=? AND key_type='spk' AND key_id=?`)
83
+ .run(deviceId, spkId);
84
+ }
85
+ listRecentSPKIds(deviceId, n) {
86
+ if (n <= 0)
87
+ return [];
88
+ const rows = this.db
89
+ .prepare(`SELECT key_id FROM v2_device_keys
90
+ WHERE device_id=? AND key_type='spk' ORDER BY created_at DESC, key_id DESC LIMIT ?`)
91
+ .all(deviceId, n);
92
+ return rows.map((r) => r.key_id);
93
+ }
94
+ listExpiredSPKIds(deviceId, maxAgeMs) {
95
+ const cutoff = Date.now() / 1000 - maxAgeMs / 1000;
96
+ const rows = this.db
97
+ .prepare(`SELECT key_id FROM v2_device_keys
98
+ WHERE device_id=? AND key_type='spk' AND created_at < ?`)
99
+ .all(deviceId, cutoff);
100
+ return rows.map((r) => r.key_id);
101
+ }
102
+ // ── Group SPK ──────────────────────────────────────────────────
103
+ /** 复合 key_id: `${groupId}\0${spkId}` */
104
+ _groupSPKKeyId(groupId, spkId) {
105
+ return `${groupId}\0${spkId}`;
106
+ }
107
+ saveGroupSPK(deviceId, groupId, spkId, priv, pubDer) {
108
+ this.db
109
+ .prepare(`INSERT OR REPLACE INTO v2_device_keys (device_id, key_type, key_id, private_key, public_key, created_at)
110
+ VALUES (?, 'group_spk', ?, ?, ?, ?)`)
111
+ .run(deviceId, this._groupSPKKeyId(groupId, spkId), toSqliteBlob(priv), toSqliteBlob(pubDer), Date.now());
112
+ }
113
+ loadGroupSPK(deviceId, groupId, spkId) {
114
+ const row = this.db
115
+ .prepare(`SELECT private_key FROM v2_device_keys WHERE device_id=? AND key_type='group_spk' AND key_id=?`)
116
+ .get(deviceId, this._groupSPKKeyId(groupId, spkId));
117
+ return row ? asBuffer(row.private_key) : null;
118
+ }
119
+ loadCurrentGroupSPK(deviceId, groupId) {
120
+ const prefix = `${groupId}\0%`;
121
+ const row = this.db
122
+ .prepare(`SELECT key_id, private_key, public_key FROM v2_device_keys
123
+ WHERE device_id=? AND key_type='group_spk' AND key_id LIKE ?
124
+ ORDER BY created_at DESC LIMIT 1`)
125
+ .get(deviceId, prefix);
126
+ if (!row)
127
+ return null;
128
+ // key_id = "{groupId}\0{spkId}",提取 spkId
129
+ const parts = row.key_id.split('\0');
130
+ const spkId = parts.length > 1 ? parts[1] : row.key_id;
131
+ return {
132
+ spkId,
133
+ priv: asBuffer(row.private_key),
134
+ pubDer: asBuffer(row.public_key),
135
+ };
136
+ }
137
+ }
138
+ //# sourceMappingURL=keystore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keystore.js","sourceRoot":"","sources":["../../../src/v2/session/keystore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;;;;EAShC,CAAC;AAEH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,UAAU;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACpG,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,UAAU,CAAC,KAAiB,CAAC,CAAC;IACnE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,SAAS,YAAY,CAAC,KAAiB;IACrC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;AACxG,CAAC;AAED,MAAM,OAAO,UAAU;IACb,EAAE,CAAa;IAEvB,YAAY,EAAc;QACxB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,QAAgB,EAAE,IAAgB,EAAE,MAAkB;QAC3D,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;uCAC+B,CAChC;aACA,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,MAAM,CAAC,QAAgB;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN,sGAAsG,CACvG;aACA,GAAG,CAAC,QAAQ,CAA8D,CAAC;QAC9E,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;IAC/E,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,KAAa,EAAE,IAAgB,EAAE,MAAkB;QAC3E,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;uCAC+B,CAChC;aACA,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,OAAO,CAAC,QAAgB,EAAE,KAAa;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN,0FAA0F,CAC3F;aACA,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAyC,CAAC;QAChE,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAED,cAAc,CAAC,QAAgB;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN;4FACoF,CACrF;aACA,GAAG,CAAC,QAAQ,CAA8E,CAAC;QAC9F,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO;YACL,KAAK,EAAE,GAAG,CAAC,MAAM;YACjB,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;YAC/B,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,KAAa;QACvC,IAAI,CAAC,EAAE;aACJ,OAAO,CAAC,8EAA8E,CAAC;aACvF,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,gBAAgB,CAAC,QAAgB,EAAE,CAAS;QAC1C,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN;4FACoF,CACrF;aACA,GAAG,CAAC,QAAQ,EAAE,CAAC,CAA8B,CAAC;QACjD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,iBAAiB,CAAC,QAAgB,EAAE,QAAgB;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,CAAC;QACnD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE;aACjB,OAAO,CACN;iEACyD,CAC1D;aACA,GAAG,CAAC,QAAQ,EAAE,MAAM,CAA8B,CAAC;QACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,kEAAkE;IAElE,wCAAwC;IAChC,cAAc,CAAC,OAAe,EAAE,KAAa;QACnD,OAAO,GAAG,OAAO,KAAK,KAAK,EAAE,CAAC;IAChC,CAAC;IAED,YAAY,CAAC,QAAgB,EAAE,OAAe,EAAE,KAAa,EAAE,IAAgB,EAAE,MAAkB;QACjG,IAAI,CAAC,EAAE;aACJ,OAAO,CACN;6CACqC,CACtC;aACA,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9G,CAAC;IAED,YAAY,CAAC,QAAgB,EAAE,OAAe,EAAE,KAAa;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN,gGAAgG,CACjG;aACA,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,CAAyC,CAAC;QAC9F,OAAO,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAED,mBAAmB,CAAC,QAAgB,EAAE,OAAe;QACnD,MAAM,MAAM,GAAG,GAAG,OAAO,KAAK,CAAC;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE;aAChB,OAAO,CACN;;0CAEkC,CACnC;aACA,GAAG,CAAC,QAAQ,EAAE,MAAM,CAA8E,CAAC;QACtG,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,yCAAyC;QACzC,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC;QACvD,OAAO;YACL,KAAK;YACL,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC;YAC/B,MAAM,EAAE,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC;SACjC,CAAC;IACJ,CAAC;CACF"}