@agentunion/fastaun-browser 0.4.13 → 0.5.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.
- package/CHANGELOG.md +536 -386
- package/_packed_docs/CHANGELOG-validators.md +144 -0
- package/_packed_docs/CHANGELOG.md +536 -386
- package/_packed_docs/INDEX.md +159 -84
- package/_packed_docs/KITE_DOCS_GUIDE.md +48 -31
- package/_packed_docs/agent.md/SCHEMA.md +2 -0
- package/_packed_docs/agent.md/examples/codeagent-claudecode.md +2 -0
- package/_packed_docs/agent.md/examples/human-developer.md +2 -0
- package/_packed_docs/agent.md/examples/openclaw-lobster.md +2 -0
- package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +2 -0
- 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 +2 -0
- package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +266 -686
- package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +331 -0
- package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +2 -0
- package/_packed_docs/design/AUNClient/346/213/206/345/210/206/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +2 -0
- 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 +2 -0
- 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 +2 -0
- package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -0
- package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +2 -0
- 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 -0
- package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +2 -0
- package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +2 -0
- package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +2 -0
- package/_packed_docs/protocol/08-AUN-E2EE-Group.md +296 -900
- package/_packed_docs/protocol/08-AUN-E2EE.md +2 -0
- package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +2 -0
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +66 -173
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +55 -0
- package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +2 -0
- 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 +2 -0
- 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 +2 -0
- package/_packed_docs/protocol/16-/347/263/273/347/273/237/347/233/256/345/275/225/344/277/235/346/212/244/346/226/271/346/241/210.md +177 -0
- package/_packed_docs/protocol/README.md +4 -1
- package/_packed_docs/protocol/agent.md/SCHEMA.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/human-developer.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +2 -0
- package/_packed_docs/protocol/aun-docs-guide.md +2 -0
- package/_packed_docs/protocol/index.md +10 -3
- package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +2 -0
- 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 +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- 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 +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- 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 +2 -0
- 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 +2 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +2 -0
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +2 -0
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -252
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +219 -177
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +4 -1
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +2 -0
- package/_packed_docs/sdk/09-collab-rpc-manual.md +581 -0
- package/_packed_docs/sdk/09-custody-api-manual.md +2 -0
- package/_packed_docs/sdk/09-group-rpc-manual.md +301 -634
- package/_packed_docs/sdk/09-message-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-payload-reference.md +2 -0
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +984 -274
- package/_packed_docs/sdk/09-stream-rpc-manual.md +2 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +36 -23
- 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 +2 -0
- package/_packed_docs/sdk/INDEX.md +47 -30
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +2 -0
- package/_packed_docs/sdk/README.md +17 -15
- package/dist/aid-store.d.ts +25 -0
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +89 -8
- package/dist/aid-store.js.map +1 -1
- package/dist/bundle.js +19615 -16870
- package/dist/client/delivery.d.ts +15 -2
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +303 -54
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +36 -14
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +2 -2
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +2 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +235 -82
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +14 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +300 -121
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +36 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +460 -57
- package/dist/client.js.map +1 -1
- package/dist/collab/client.d.ts +47 -0
- package/dist/collab/client.d.ts.map +1 -0
- package/dist/collab/client.js +136 -0
- package/dist/collab/client.js.map +1 -0
- package/dist/collab/errors.d.ts +16 -0
- package/dist/collab/errors.d.ts.map +1 -0
- package/dist/collab/errors.js +60 -0
- package/dist/collab/errors.js.map +1 -0
- package/dist/collab/index.d.ts +4 -0
- package/dist/collab/index.d.ts.map +1 -0
- package/dist/collab/index.js +4 -0
- package/dist/collab/index.js.map +1 -0
- package/dist/collab/types.d.ts +111 -0
- package/dist/collab/types.d.ts.map +1 -0
- package/dist/collab/types.js +2 -0
- package/dist/collab/types.js.map +1 -0
- package/dist/crypto.d.ts +6 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +6 -0
- package/dist/crypto.js.map +1 -1
- package/dist/errors.d.ts +0 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +9 -37
- package/dist/errors.js.map +1 -1
- package/dist/facades.d.ts +94 -0
- package/dist/facades.d.ts.map +1 -0
- package/dist/facades.js +389 -0
- package/dist/facades.js.map +1 -0
- package/dist/group-fs.d.ts +120 -0
- package/dist/group-fs.d.ts.map +1 -0
- package/dist/group-fs.js +539 -0
- package/dist/group-fs.js.map +1 -0
- package/dist/group-id.d.ts +9 -12
- package/dist/group-id.d.ts.map +1 -1
- package/dist/group-id.js +41 -63
- package/dist/group-id.js.map +1 -1
- package/dist/group-resources.d.ts +98 -0
- package/dist/group-resources.d.ts.map +1 -0
- package/dist/group-resources.js +635 -0
- package/dist/group-resources.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +2 -54
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +3 -0
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.js +65 -0
- package/dist/keystore/indexeddb-identity-store.js.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +3 -17
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +4 -47
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +1 -64
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +45 -774
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -1
- package/dist/register-flow.d.ts +1 -0
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +3 -0
- package/dist/register-flow.js.map +1 -1
- package/dist/service-proxy.d.ts +1 -0
- package/dist/service-proxy.d.ts.map +1 -1
- package/dist/service-proxy.js +15 -3
- package/dist/service-proxy.js.map +1 -1
- package/dist/storage/errors.d.ts +28 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +65 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/lowlevel.d.ts +322 -0
- package/dist/storage/lowlevel.d.ts.map +1 -0
- package/dist/storage/lowlevel.js +471 -0
- package/dist/storage/lowlevel.js.map +1 -0
- package/dist/storage/types.d.ts +59 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +113 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/vfs.d.ts +166 -0
- package/dist/storage/vfs.d.ts.map +1 -0
- package/dist/storage/vfs.js +457 -0
- package/dist/storage/vfs.js.map +1 -0
- package/dist/tools/cross-sdk-agent.d.ts +3 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -0
- package/dist/tools/cross-sdk-agent.js +1376 -0
- package/dist/tools/cross-sdk-agent.js.map +1 -0
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +125 -12
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +39 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/v2/session/session.d.ts +2 -0
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +58 -22
- package/dist/v2/session/session.js.map +1 -1
- package/dist/v2/state/commitment.d.ts +1 -1
- package/dist/v2/state/commitment.d.ts.map +1 -1
- package/dist/v2/state/commitment.js +5 -3
- package/dist/v2/state/commitment.js.map +1 -1
- package/dist/validators.d.ts +35 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +127 -0
- package/dist/validators.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +44 -44
package/dist/client.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
import { createConfig, getDeviceId, normalizeSlotId } from './config.js';
|
|
8
8
|
import { EventDispatcher } from './events.js';
|
|
9
9
|
import { normalizeGroupId } from './group-id.js';
|
|
10
|
+
import { validateAIDFormat, validateGroupIDFormat } from './validators.js';
|
|
10
11
|
import { GatewayDiscovery } from './discovery.js';
|
|
12
|
+
import { AIDStore } from './aid-store.js';
|
|
11
13
|
import { RPCTransport } from './transport.js';
|
|
12
14
|
import { AuthFlow } from './auth.js';
|
|
13
15
|
import { SeqTracker } from './seq-tracker.js';
|
|
@@ -17,6 +19,9 @@ import { IdentityRuntimeManager } from './client/identity.js';
|
|
|
17
19
|
import { LifecycleController } from './client/lifecycle.js';
|
|
18
20
|
import { PeerDirectory } from './client/peers.js';
|
|
19
21
|
import { RpcPipeline } from './client/rpc-pipeline.js';
|
|
22
|
+
import { GroupFacade, MessageFacade, StreamFacade } from './facades.js';
|
|
23
|
+
import { StorageVFS } from './storage/vfs.js';
|
|
24
|
+
import { CollabClient } from './collab/client.js';
|
|
20
25
|
import { V2E2EECoordinator } from './client/v2-e2ee.js';
|
|
21
26
|
import { GroupStateCoordinator } from './client/group-state.js';
|
|
22
27
|
import { CryptoProvider, base64ToUint8, pemToArrayBuffer, ecdsaVerifyDer, importCertPublicKeyEcdsa, } from './crypto.js';
|
|
@@ -26,7 +31,7 @@ import { ecdsaVerifyRaw } from './v2/crypto/ecdsa.js';
|
|
|
26
31
|
import { AUNLogger } from './logger.js';
|
|
27
32
|
import { AgentMdManager } from './agent-md.js';
|
|
28
33
|
import { certMatchesFingerprint, normalizeFingerprintHex } from './cert-utils.js';
|
|
29
|
-
import { AUNError, AuthError, ConnectionError, E2EEError, NotFoundError, PermissionError, StateError, ValidationError, } from './errors.js';
|
|
34
|
+
import { AUNError, AuthError, ConnectionError, E2EEError, NotFoundError, PermissionError, StateError, TimeoutError, ValidationError, } from './errors.js';
|
|
30
35
|
import { isJsonObject, ConnectionState, STATE_TO_PUBLIC, } from './types.js';
|
|
31
36
|
import { AID } from './aid.js';
|
|
32
37
|
function getV2DeviceId(dev) {
|
|
@@ -101,8 +106,8 @@ const NON_IDEMPOTENT_TIMEOUT = 35;
|
|
|
101
106
|
const NON_IDEMPOTENT_METHODS = new Set([
|
|
102
107
|
'message.send', 'group.send', 'group.create', 'group.invite',
|
|
103
108
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
104
|
-
'group.
|
|
105
|
-
'group.
|
|
109
|
+
'group.set_settings',
|
|
110
|
+
'group.update_announcement', 'group.update_rules',
|
|
106
111
|
'storage.put_object', 'storage.delete_object',
|
|
107
112
|
'storage.create_share_link', 'storage.revoke_share_link',
|
|
108
113
|
'storage.get_by_share',
|
|
@@ -110,18 +115,26 @@ const NON_IDEMPOTENT_METHODS = new Set([
|
|
|
110
115
|
'storage.create_folder', 'storage.rename_folder', 'storage.move_folder',
|
|
111
116
|
'storage.delete_folder', 'storage.move_object', 'storage.copy_object',
|
|
112
117
|
'storage.batch_delete', 'storage.set_object_meta', 'storage.append_object',
|
|
118
|
+
'storage.set_acl', 'storage.remove_acl', 'storage.set_visibility',
|
|
119
|
+
'storage.issue_token', 'storage.revoke_token',
|
|
120
|
+
'storage.create_symlink', 'storage.atomic_repoint',
|
|
121
|
+
'storage.rename_symlink', 'storage.delete_symlink',
|
|
122
|
+
'storage.fs.mkdir', 'storage.fs.remove', 'storage.fs.rename', 'storage.fs.copy',
|
|
123
|
+
'storage.fs.mount', 'storage.fs.approve', 'storage.fs.reject', 'storage.fs.unmount',
|
|
124
|
+
'storage.fs.invalidate_membership',
|
|
125
|
+
'storage.volume.create', 'storage.volume.renew', 'storage.volume.expire_due',
|
|
113
126
|
'auth.create_aid', 'auth.renew_cert', 'auth.rekey',
|
|
114
127
|
'message.thought.put', 'group.thought.put',
|
|
115
|
-
'group.add_member',
|
|
116
|
-
'group.
|
|
117
|
-
'group.
|
|
118
|
-
'group.
|
|
119
|
-
'group.
|
|
120
|
-
'group.
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
'
|
|
124
|
-
'
|
|
128
|
+
'group.add_member', 'group.bind_group_aid', 'group.complete_transfer',
|
|
129
|
+
'group.fs.mkdir', 'group.fs.rm', 'group.fs.cp', 'group.fs.mv',
|
|
130
|
+
'group.fs.set_acl', 'group.fs.remove_acl',
|
|
131
|
+
'group.fs.mount', 'group.fs.umount',
|
|
132
|
+
'group.fs.check_upload', 'group.fs.create_upload_session',
|
|
133
|
+
'group.fs.complete_upload', 'group.fs.create_download_ticket',
|
|
134
|
+
'collab.create', 'collab.commit', 'collab.clone',
|
|
135
|
+
'collab.prune', 'collab.unregister',
|
|
136
|
+
'collab.tag.create', 'collab.tag.restore',
|
|
137
|
+
'collab.tag.rm', 'collab.tag.prune',
|
|
125
138
|
]);
|
|
126
139
|
/** 需要客户端签名的关键方法。运行时逻辑已迁入 RpcPipeline,此处保留给源码审计测试。 */
|
|
127
140
|
const SIGNED_METHODS = new Set([
|
|
@@ -135,23 +148,23 @@ const SIGNED_METHODS = new Set([
|
|
|
135
148
|
'group.v2.propose_state', 'group.v2.confirm_state',
|
|
136
149
|
'group.v2.get_proposal',
|
|
137
150
|
'group.kick', 'group.add_member',
|
|
138
|
-
'group.leave', 'group.remove_member',
|
|
139
|
-
'group.update',
|
|
140
|
-
'group.
|
|
141
|
-
'group.transfer_owner', 'group.
|
|
151
|
+
'group.leave', 'group.remove_member',
|
|
152
|
+
'group.update',
|
|
153
|
+
'group.set_role',
|
|
154
|
+
'group.transfer_owner', 'group.bind_group_aid', 'group.complete_transfer',
|
|
155
|
+
'group.review_join_request',
|
|
142
156
|
'group.batch_review_join_request',
|
|
143
157
|
'group.request_join', 'group.use_invite_code',
|
|
144
158
|
'group.thought.put',
|
|
145
159
|
'message.thought.put',
|
|
146
160
|
'group.set_settings',
|
|
147
|
-
'group.
|
|
148
|
-
'group.
|
|
149
|
-
'group.
|
|
150
|
-
'group.
|
|
151
|
-
'group.
|
|
152
|
-
'group.
|
|
153
|
-
'group.
|
|
154
|
-
'group.resources.get_access', 'group.resources.resolve_access_ticket',
|
|
161
|
+
'group.update_announcement',
|
|
162
|
+
'group.update_rules',
|
|
163
|
+
'group.fs.mkdir', 'group.fs.rm', 'group.fs.cp', 'group.fs.mv',
|
|
164
|
+
'group.fs.set_acl', 'group.fs.remove_acl',
|
|
165
|
+
'group.fs.mount', 'group.fs.umount',
|
|
166
|
+
'group.fs.check_upload', 'group.fs.create_upload_session',
|
|
167
|
+
'group.fs.complete_upload', 'group.fs.create_download_ticket',
|
|
155
168
|
'storage.put_object', 'storage.delete_object',
|
|
156
169
|
'storage.create_share_link', 'storage.revoke_share_link',
|
|
157
170
|
'storage.get_by_share',
|
|
@@ -159,6 +172,19 @@ const SIGNED_METHODS = new Set([
|
|
|
159
172
|
'storage.create_folder', 'storage.rename_folder', 'storage.move_folder',
|
|
160
173
|
'storage.delete_folder', 'storage.move_object', 'storage.copy_object',
|
|
161
174
|
'storage.batch_delete', 'storage.set_object_meta', 'storage.append_object',
|
|
175
|
+
'storage.set_acl', 'storage.remove_acl', 'storage.set_visibility',
|
|
176
|
+
'storage.check_access',
|
|
177
|
+
'storage.issue_token', 'storage.revoke_token',
|
|
178
|
+
'storage.create_symlink', 'storage.atomic_repoint',
|
|
179
|
+
'storage.rename_symlink', 'storage.delete_symlink',
|
|
180
|
+
'storage.fs.mkdir', 'storage.fs.remove', 'storage.fs.rename', 'storage.fs.copy',
|
|
181
|
+
'storage.fs.mount', 'storage.fs.approve', 'storage.fs.reject', 'storage.fs.unmount',
|
|
182
|
+
'storage.fs.invalidate_membership',
|
|
183
|
+
'storage.volume.create', 'storage.volume.renew', 'storage.volume.expire_due',
|
|
184
|
+
'collab.create', 'collab.commit', 'collab.clone',
|
|
185
|
+
'collab.prune', 'collab.unregister',
|
|
186
|
+
'collab.tag.create', 'collab.tag.restore',
|
|
187
|
+
'collab.tag.rm', 'collab.tag.prune',
|
|
162
188
|
'group.commit_state',
|
|
163
189
|
'group.ban', 'group.unban',
|
|
164
190
|
'group.dissolve', 'group.suspend', 'group.resume',
|
|
@@ -560,6 +586,7 @@ export class AUNClient {
|
|
|
560
586
|
_onlineUnreadHintIntervalMs = 50;
|
|
561
587
|
/** gap fill 来源标记:true 表示当前正在补洞(pull 触发),false 表示非补洞 */
|
|
562
588
|
_gapFillActive = false;
|
|
589
|
+
_backgroundRpcDepth = 0;
|
|
563
590
|
// Pull Gate:序列化同一 key 的并发 pull 操作,防止重复拉取
|
|
564
591
|
_pullGates = new Map();
|
|
565
592
|
// 重连相关
|
|
@@ -595,6 +622,11 @@ export class AUNClient {
|
|
|
595
622
|
_delivery;
|
|
596
623
|
_v2E2EE;
|
|
597
624
|
_groupState;
|
|
625
|
+
_storage;
|
|
626
|
+
_collab;
|
|
627
|
+
_messageFacade;
|
|
628
|
+
_groupFacade;
|
|
629
|
+
_streamFacade;
|
|
598
630
|
constructor(aid) {
|
|
599
631
|
if (aid !== null && aid !== undefined && !isAIDObject(aid)) {
|
|
600
632
|
throw new ValidationError('AUNClient only accepts an AID object or no argument');
|
|
@@ -709,7 +741,7 @@ export class AUNClient {
|
|
|
709
741
|
this._dispatcher.subscribe('_raw.group.message_recalled', (data) => {
|
|
710
742
|
this._safeAsync(this._onRawGroupMessageRecalled(data));
|
|
711
743
|
});
|
|
712
|
-
// 群组变更事件:验签 + 透传 + gap 检测
|
|
744
|
+
// 群组变更事件:验签 + 透传 + gap 检测 + V2 state/SPK 维护
|
|
713
745
|
this._dispatcher.subscribe('_raw.group.changed', (data) => {
|
|
714
746
|
this._onRawGroupChanged(data);
|
|
715
747
|
});
|
|
@@ -720,9 +752,6 @@ export class AUNClient {
|
|
|
720
752
|
this._dispatcher.subscribe('_raw.group.v2.message_created', (data) => {
|
|
721
753
|
this._safeAsync(this._onRawGroupV2MessageCreated(data));
|
|
722
754
|
});
|
|
723
|
-
this._dispatcher.subscribe('_raw.group.v2.epoch_rotated', (data) => {
|
|
724
|
-
this._safeAsync(this._onV2EpochRotated(data));
|
|
725
|
-
});
|
|
726
755
|
this._dispatcher.subscribe('_raw.group.v2.state_proposed', (data) => {
|
|
727
756
|
this._safeAsync(this._onV2StateProposed(data));
|
|
728
757
|
});
|
|
@@ -751,6 +780,36 @@ export class AUNClient {
|
|
|
751
780
|
get aid() {
|
|
752
781
|
return this._aid;
|
|
753
782
|
}
|
|
783
|
+
/** AUN Storage VFS 入口 */
|
|
784
|
+
get storage() {
|
|
785
|
+
if (!this._storage)
|
|
786
|
+
this._storage = new StorageVFS(this);
|
|
787
|
+
return this._storage;
|
|
788
|
+
}
|
|
789
|
+
/** AUN collab 协作层入口 */
|
|
790
|
+
get collab() {
|
|
791
|
+
if (!this._collab)
|
|
792
|
+
this._collab = new CollabClient(this);
|
|
793
|
+
return this._collab;
|
|
794
|
+
}
|
|
795
|
+
/** Message RPC facade 入口 */
|
|
796
|
+
get message() {
|
|
797
|
+
if (!this._messageFacade)
|
|
798
|
+
this._messageFacade = new MessageFacade(this);
|
|
799
|
+
return this._messageFacade;
|
|
800
|
+
}
|
|
801
|
+
/** Group RPC facade 入口 */
|
|
802
|
+
get group() {
|
|
803
|
+
if (!this._groupFacade)
|
|
804
|
+
this._groupFacade = new GroupFacade(this);
|
|
805
|
+
return this._groupFacade;
|
|
806
|
+
}
|
|
807
|
+
/** Stream RPC facade 入口 */
|
|
808
|
+
get stream() {
|
|
809
|
+
if (!this._streamFacade)
|
|
810
|
+
this._streamFacade = new StreamFacade(this);
|
|
811
|
+
return this._streamFacade;
|
|
812
|
+
}
|
|
754
813
|
async _observeAgentMdFromEnvelope(envelope) {
|
|
755
814
|
await this._agentMdManager.observeEnvelope(envelope);
|
|
756
815
|
}
|
|
@@ -990,7 +1049,326 @@ export class AUNClient {
|
|
|
990
1049
|
* 自动解密 message.pull/group.pull、Group E2EE 生命周期编排。
|
|
991
1050
|
*/
|
|
992
1051
|
async call(method, params) {
|
|
993
|
-
|
|
1052
|
+
try {
|
|
1053
|
+
return await this._rpcPipeline.call(method, params);
|
|
1054
|
+
}
|
|
1055
|
+
catch (err) {
|
|
1056
|
+
await this._maybeHandleHalfOpenRpcFailure(err);
|
|
1057
|
+
throw err;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
async _maybeHandleHalfOpenRpcFailure(err) {
|
|
1061
|
+
if (this._closing || this.state !== ConnectionState.READY)
|
|
1062
|
+
return;
|
|
1063
|
+
if (!this._sessionOptions?.auto_reconnect || this._reconnectActive)
|
|
1064
|
+
return;
|
|
1065
|
+
if (!(err instanceof ConnectionError) && !(err instanceof TimeoutError))
|
|
1066
|
+
return;
|
|
1067
|
+
await this._handleTransportDisconnect(err);
|
|
1068
|
+
}
|
|
1069
|
+
async createGroup(params = {}) {
|
|
1070
|
+
if (!isJsonObject(params)) {
|
|
1071
|
+
throw new ValidationError('createGroup params must be an object');
|
|
1072
|
+
}
|
|
1073
|
+
const payload = { ...params };
|
|
1074
|
+
const groupName = String(payload.group_name ?? payload.groupName ?? '').trim();
|
|
1075
|
+
if (!groupName) {
|
|
1076
|
+
return await this.call('group.create', payload);
|
|
1077
|
+
}
|
|
1078
|
+
const keyPair = await new CryptoProvider().generateIdentity();
|
|
1079
|
+
payload.public_key = keyPair.public_key_der_b64;
|
|
1080
|
+
payload.curve = keyPair.curve;
|
|
1081
|
+
const result = await this.call('group.create', payload);
|
|
1082
|
+
const resultMap = isJsonObject(result) ? result : {};
|
|
1083
|
+
const group = isJsonObject(resultMap.group) ? resultMap.group : {};
|
|
1084
|
+
const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
|
|
1085
|
+
const groupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
|
|
1086
|
+
const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
|
|
1087
|
+
if (!groupAid || !certPem) {
|
|
1088
|
+
throw new ValidationError('group.create named group response missing group.group_aid or aid_cert.cert');
|
|
1089
|
+
}
|
|
1090
|
+
const store = new AIDStore({
|
|
1091
|
+
aunPath: this.configModel.aunPath,
|
|
1092
|
+
encryptionSeed: this.configModel.seedPassword ?? '',
|
|
1093
|
+
slotId: this._slotId,
|
|
1094
|
+
rootCaPem: this.configModel.rootCaPem,
|
|
1095
|
+
verifySsl: this.configModel.verifySsl,
|
|
1096
|
+
});
|
|
1097
|
+
try {
|
|
1098
|
+
const imported = await store.importGroupIdentity(groupAid, {
|
|
1099
|
+
private_key_pem: keyPair.private_key_pem,
|
|
1100
|
+
public_key_der_b64: keyPair.public_key_der_b64,
|
|
1101
|
+
curve: keyPair.curve,
|
|
1102
|
+
cert_pem: certPem,
|
|
1103
|
+
});
|
|
1104
|
+
if (!imported.ok) {
|
|
1105
|
+
throw new ValidationError(imported.error.message);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
finally {
|
|
1109
|
+
store.close();
|
|
1110
|
+
}
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
1113
|
+
async bindGroupAid(params = {}, options = {}) {
|
|
1114
|
+
if (!isJsonObject(params)) {
|
|
1115
|
+
throw new ValidationError('bindGroupAid params must be an object');
|
|
1116
|
+
}
|
|
1117
|
+
const store = options.aidStore;
|
|
1118
|
+
if (!store) {
|
|
1119
|
+
throw new ValidationError('bindGroupAid requires aidStore');
|
|
1120
|
+
}
|
|
1121
|
+
const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
1122
|
+
const keystore = store._keystore;
|
|
1123
|
+
let keyPair = null;
|
|
1124
|
+
// 优先复用 pending 槽位中已暂存的待绑定密钥(崩溃/重试幂等)
|
|
1125
|
+
if (groupId && keystore && typeof keystore.loadPendingGroupBind === 'function') {
|
|
1126
|
+
keyPair = await keystore.loadPendingGroupBind(groupId);
|
|
1127
|
+
}
|
|
1128
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
1129
|
+
// 未命中 pending,生成新密钥
|
|
1130
|
+
const generated = await new CryptoProvider().generateIdentity();
|
|
1131
|
+
keyPair = {
|
|
1132
|
+
private_key_pem: generated.private_key_pem,
|
|
1133
|
+
public_key_der_b64: generated.public_key_der_b64,
|
|
1134
|
+
curve: generated.curve,
|
|
1135
|
+
};
|
|
1136
|
+
// 发 RPC 前先落盘暂存,确保崩溃后重试能复用同一密钥
|
|
1137
|
+
if (groupId && keystore && typeof keystore.savePendingGroupBind === 'function') {
|
|
1138
|
+
await keystore.savePendingGroupBind(groupId, keyPair);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
if (!keyPair) {
|
|
1142
|
+
throw new ValidationError('bindGroupAid: failed to generate or load key pair');
|
|
1143
|
+
}
|
|
1144
|
+
const payload = { ...params };
|
|
1145
|
+
if (!String(payload.group_id ?? '').trim() && groupId) {
|
|
1146
|
+
payload.group_id = groupId;
|
|
1147
|
+
}
|
|
1148
|
+
payload.public_key = keyPair.public_key_der_b64;
|
|
1149
|
+
payload.curve = keyPair.curve;
|
|
1150
|
+
const result = await this.call('group.bind_group_aid', payload);
|
|
1151
|
+
const resultMap = isJsonObject(result) ? result : {};
|
|
1152
|
+
const group = isJsonObject(resultMap.group) ? resultMap.group : {};
|
|
1153
|
+
const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
|
|
1154
|
+
const groupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
|
|
1155
|
+
const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
|
|
1156
|
+
if (!groupAid || !certPem) {
|
|
1157
|
+
throw new ValidationError('group.bind_group_aid response missing group.group_aid or aid_cert.cert');
|
|
1158
|
+
}
|
|
1159
|
+
const imported = await store.importGroupIdentity(groupAid, {
|
|
1160
|
+
private_key_pem: keyPair.private_key_pem,
|
|
1161
|
+
public_key_der_b64: keyPair.public_key_der_b64,
|
|
1162
|
+
curve: keyPair.curve,
|
|
1163
|
+
cert_pem: certPem,
|
|
1164
|
+
});
|
|
1165
|
+
if (!imported.ok) {
|
|
1166
|
+
throw new ValidationError(imported.error.message);
|
|
1167
|
+
}
|
|
1168
|
+
// 落盘成功,清除 pending 槽位
|
|
1169
|
+
if (groupId && keystore && typeof keystore.clearPendingGroupBind === 'function') {
|
|
1170
|
+
await keystore.clearPendingGroupBind(groupId);
|
|
1171
|
+
}
|
|
1172
|
+
return result;
|
|
1173
|
+
}
|
|
1174
|
+
async renewGroupAid(params = {}, options = {}) {
|
|
1175
|
+
if (!isJsonObject(params)) {
|
|
1176
|
+
throw new ValidationError('renewGroupAid params must be an object');
|
|
1177
|
+
}
|
|
1178
|
+
const store = options.aidStore;
|
|
1179
|
+
if (!store) {
|
|
1180
|
+
throw new ValidationError('renewGroupAid requires aidStore');
|
|
1181
|
+
}
|
|
1182
|
+
const groupId = String(params.group_id ?? params.group_aid ?? '').trim();
|
|
1183
|
+
if (!groupId) {
|
|
1184
|
+
throw new ValidationError('renewGroupAid requires group_id or group_aid');
|
|
1185
|
+
}
|
|
1186
|
+
let groupAid = String(params.group_aid ?? '').trim();
|
|
1187
|
+
if (!groupAid) {
|
|
1188
|
+
const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
|
|
1189
|
+
groupAid = String(info?.group_aid ?? '').trim();
|
|
1190
|
+
}
|
|
1191
|
+
if (!groupAid) {
|
|
1192
|
+
throw new ValidationError('renewGroupAid: unable to determine group_aid');
|
|
1193
|
+
}
|
|
1194
|
+
const loaded = await store.load(groupAid);
|
|
1195
|
+
if (!loaded.ok || !loaded.data) {
|
|
1196
|
+
throw new ValidationError(`renewGroupAid: group_aid identity not found: ${groupAid}`);
|
|
1197
|
+
}
|
|
1198
|
+
const oldAidObj = loaded.data.aid;
|
|
1199
|
+
if (!oldAidObj.isPrivateKeyValid() || !oldAidObj.privateKeyPem) {
|
|
1200
|
+
throw new ValidationError(`renewGroupAid: group_aid has no private key: ${groupAid}`);
|
|
1201
|
+
}
|
|
1202
|
+
const oldPublicKey = oldAidObj.publicKey;
|
|
1203
|
+
if (!oldPublicKey) {
|
|
1204
|
+
throw new ValidationError(`renewGroupAid: cannot determine old public key for ${groupAid}`);
|
|
1205
|
+
}
|
|
1206
|
+
const newKeyPair = await new CryptoProvider().generateIdentity();
|
|
1207
|
+
const newPublicKey = newKeyPair.public_key_der_b64;
|
|
1208
|
+
const newPrivateKey = newKeyPair.private_key_pem;
|
|
1209
|
+
const curve = newKeyPair.curve || 'P-256';
|
|
1210
|
+
if (!newPublicKey || !newPrivateKey) {
|
|
1211
|
+
throw new ValidationError('renewGroupAid: generated incomplete group identity');
|
|
1212
|
+
}
|
|
1213
|
+
const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
|
|
1214
|
+
const issuedMs = Date.now();
|
|
1215
|
+
const oldHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(oldPublicKey))));
|
|
1216
|
+
const newHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(newPublicKey))));
|
|
1217
|
+
const canonical = [
|
|
1218
|
+
'aun-group-aid-renew-v1',
|
|
1219
|
+
groupId.toLowerCase(),
|
|
1220
|
+
groupAid.toLowerCase(),
|
|
1221
|
+
oldHash,
|
|
1222
|
+
newHash,
|
|
1223
|
+
nonce,
|
|
1224
|
+
String(issuedMs),
|
|
1225
|
+
].join('|');
|
|
1226
|
+
const signed = await oldAidObj.sign(canonical);
|
|
1227
|
+
if (!signed.ok) {
|
|
1228
|
+
throw new ValidationError(`renewGroupAid: sign failed: ${signed.error.message}`);
|
|
1229
|
+
}
|
|
1230
|
+
const payload = { ...params };
|
|
1231
|
+
if (!String(payload.group_id ?? '').trim()) {
|
|
1232
|
+
payload.group_id = groupId;
|
|
1233
|
+
}
|
|
1234
|
+
payload.group_aid = groupAid;
|
|
1235
|
+
payload.old_public_key = oldPublicKey;
|
|
1236
|
+
payload.new_public_key = newPublicKey;
|
|
1237
|
+
payload.curve = curve;
|
|
1238
|
+
payload.renew_proof = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
|
|
1239
|
+
const result = await this.call('group.renew_group_aid', payload);
|
|
1240
|
+
const resultMap = isJsonObject(result) ? result : {};
|
|
1241
|
+
const group = isJsonObject(resultMap.group) ? resultMap.group : {};
|
|
1242
|
+
const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
|
|
1243
|
+
const returnedGroupAid = String(group.group_aid ?? resultMap.group_aid ?? groupAid).trim();
|
|
1244
|
+
const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
|
|
1245
|
+
if (!returnedGroupAid || !certPem) {
|
|
1246
|
+
throw new ValidationError('renewGroupAid response missing group.group_aid or aid_cert.cert');
|
|
1247
|
+
}
|
|
1248
|
+
const imported = await store.importGroupIdentity(returnedGroupAid, {
|
|
1249
|
+
private_key_pem: newPrivateKey,
|
|
1250
|
+
public_key_der_b64: newPublicKey,
|
|
1251
|
+
curve,
|
|
1252
|
+
cert_pem: certPem,
|
|
1253
|
+
});
|
|
1254
|
+
if (!imported.ok) {
|
|
1255
|
+
throw new ValidationError(`renewGroupAid failed to persist group identity: ${imported.error.message}`);
|
|
1256
|
+
}
|
|
1257
|
+
return result;
|
|
1258
|
+
}
|
|
1259
|
+
async startGroupTransfer(params = {}, options = {}) {
|
|
1260
|
+
if (!isJsonObject(params)) {
|
|
1261
|
+
throw new ValidationError('startGroupTransfer params must be an object');
|
|
1262
|
+
}
|
|
1263
|
+
const store = options.aidStore;
|
|
1264
|
+
if (!store) {
|
|
1265
|
+
throw new ValidationError('startGroupTransfer requires aidStore');
|
|
1266
|
+
}
|
|
1267
|
+
const groupId = String(params.group_id ?? '').trim();
|
|
1268
|
+
const newOwner = String(params.new_owner ?? '').trim();
|
|
1269
|
+
if (!groupId || !newOwner) {
|
|
1270
|
+
throw new ValidationError('startGroupTransfer requires group_id and new_owner');
|
|
1271
|
+
}
|
|
1272
|
+
let groupAid = String(params.group_aid ?? '').trim();
|
|
1273
|
+
if (!groupAid) {
|
|
1274
|
+
const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
|
|
1275
|
+
groupAid = String(info?.group_aid ?? '').trim();
|
|
1276
|
+
}
|
|
1277
|
+
if (!groupAid) {
|
|
1278
|
+
throw new ValidationError('startGroupTransfer: unable to determine group_aid');
|
|
1279
|
+
}
|
|
1280
|
+
const loaded = await store.load(groupAid);
|
|
1281
|
+
if (!loaded.ok || !loaded.data) {
|
|
1282
|
+
throw new ValidationError(`startGroupTransfer: group_aid identity not found: ${groupAid}`);
|
|
1283
|
+
}
|
|
1284
|
+
const aidObj = loaded.data.aid;
|
|
1285
|
+
if (!aidObj.isPrivateKeyValid() || !aidObj.privateKeyPem) {
|
|
1286
|
+
throw new ValidationError(`startGroupTransfer: group_aid private key not found: ${groupAid}`);
|
|
1287
|
+
}
|
|
1288
|
+
const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
|
|
1289
|
+
const issuedMs = Date.now();
|
|
1290
|
+
const canonical = [
|
|
1291
|
+
'aun-group-owner-transfer-v1',
|
|
1292
|
+
groupId.toLowerCase(),
|
|
1293
|
+
groupAid.toLowerCase(),
|
|
1294
|
+
newOwner.toLowerCase(),
|
|
1295
|
+
nonce,
|
|
1296
|
+
String(issuedMs),
|
|
1297
|
+
].join('|');
|
|
1298
|
+
const signed = await aidObj.sign(canonical);
|
|
1299
|
+
if (!signed.ok) {
|
|
1300
|
+
throw new ValidationError(`startGroupTransfer: sign failed: ${signed.error.message}`);
|
|
1301
|
+
}
|
|
1302
|
+
const payload = { ...params, group_aid: groupAid };
|
|
1303
|
+
payload.transfer_auth = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
|
|
1304
|
+
return await this.call('group.transfer_owner', payload);
|
|
1305
|
+
}
|
|
1306
|
+
async completeGroupTransfer(params = {}, options = {}) {
|
|
1307
|
+
if (!isJsonObject(params)) {
|
|
1308
|
+
throw new ValidationError('completeGroupTransfer params must be an object');
|
|
1309
|
+
}
|
|
1310
|
+
const store = options.aidStore;
|
|
1311
|
+
if (!store) {
|
|
1312
|
+
throw new ValidationError('completeGroupTransfer requires aidStore');
|
|
1313
|
+
}
|
|
1314
|
+
const keyPair = await new CryptoProvider().generateIdentity();
|
|
1315
|
+
const payload = { ...params };
|
|
1316
|
+
const groupId = String(params.group_id ?? '').trim();
|
|
1317
|
+
if (!groupId) {
|
|
1318
|
+
throw new ValidationError('completeGroupTransfer requires group_id');
|
|
1319
|
+
}
|
|
1320
|
+
let groupAid = String(params.group_aid ?? '').trim();
|
|
1321
|
+
if (!groupAid) {
|
|
1322
|
+
const info = await this.call('group.get_info', { group_id: groupId, required: ['member'] });
|
|
1323
|
+
groupAid = String(info?.group_aid ?? '').trim();
|
|
1324
|
+
}
|
|
1325
|
+
if (!groupAid) {
|
|
1326
|
+
throw new ValidationError('completeGroupTransfer: unable to determine group_aid');
|
|
1327
|
+
}
|
|
1328
|
+
const current = this.currentAid;
|
|
1329
|
+
const newOwner = String(current?.aid ?? '').trim();
|
|
1330
|
+
if (!current || !newOwner || !current.isPrivateKeyValid()) {
|
|
1331
|
+
throw new ValidationError('completeGroupTransfer requires current new-owner AID with private key');
|
|
1332
|
+
}
|
|
1333
|
+
const nonce = globalThis.crypto.randomUUID().replace(/-/g, '');
|
|
1334
|
+
const issuedMs = Date.now();
|
|
1335
|
+
const publicKeyHash = bytesToHex(new Uint8Array(await globalThis.crypto.subtle.digest('SHA-256', new TextEncoder().encode(keyPair.public_key_der_b64))));
|
|
1336
|
+
const canonical = [
|
|
1337
|
+
'aun-group-owner-transfer-accept-v1',
|
|
1338
|
+
groupId.toLowerCase(),
|
|
1339
|
+
groupAid.toLowerCase(),
|
|
1340
|
+
newOwner.toLowerCase(),
|
|
1341
|
+
publicKeyHash,
|
|
1342
|
+
nonce,
|
|
1343
|
+
String(issuedMs),
|
|
1344
|
+
].join('|');
|
|
1345
|
+
const signed = await current.sign(canonical);
|
|
1346
|
+
if (!signed.ok) {
|
|
1347
|
+
throw new ValidationError(`completeGroupTransfer: accept sign failed: ${signed.error.message}`);
|
|
1348
|
+
}
|
|
1349
|
+
payload.group_aid = groupAid;
|
|
1350
|
+
payload.public_key = keyPair.public_key_der_b64;
|
|
1351
|
+
payload.curve = keyPair.curve;
|
|
1352
|
+
payload.transfer_accept = { nonce, issued_ms: issuedMs, signature: signed.data.signature };
|
|
1353
|
+
const result = await this.call('group.complete_transfer', payload);
|
|
1354
|
+
const resultMap = isJsonObject(result) ? result : {};
|
|
1355
|
+
const group = isJsonObject(resultMap.group) ? resultMap.group : {};
|
|
1356
|
+
const aidCert = isJsonObject(resultMap.aid_cert) ? resultMap.aid_cert : {};
|
|
1357
|
+
const returnedGroupAid = String(group.group_aid ?? resultMap.group_aid ?? '').trim();
|
|
1358
|
+
const certPem = String(aidCert.cert ?? aidCert.cert_pem ?? resultMap.cert ?? resultMap.cert_pem ?? '').trim();
|
|
1359
|
+
if (!returnedGroupAid || !certPem) {
|
|
1360
|
+
throw new ValidationError('group.complete_transfer response missing group.group_aid or aid_cert.cert');
|
|
1361
|
+
}
|
|
1362
|
+
const imported = await store.importGroupIdentity(returnedGroupAid, {
|
|
1363
|
+
private_key_pem: keyPair.private_key_pem,
|
|
1364
|
+
public_key_der_b64: keyPair.public_key_der_b64,
|
|
1365
|
+
curve: keyPair.curve,
|
|
1366
|
+
cert_pem: certPem,
|
|
1367
|
+
});
|
|
1368
|
+
if (!imported.ok) {
|
|
1369
|
+
throw new ValidationError(imported.error.message);
|
|
1370
|
+
}
|
|
1371
|
+
return result;
|
|
994
1372
|
}
|
|
995
1373
|
static _notifyParamsSizeOk(params) {
|
|
996
1374
|
return new TextEncoder().encode(JSON.stringify(params)).length <= MAX_NOTIFY_PAYLOAD_SIZE;
|
|
@@ -1026,7 +1404,7 @@ export class AUNClient {
|
|
|
1026
1404
|
throw new ValidationError('notify payload is too large');
|
|
1027
1405
|
}
|
|
1028
1406
|
const targetAid = String(options.to ?? '').trim();
|
|
1029
|
-
const targetGroupId = String(options.group_id ?? options.groupId ?? '').trim();
|
|
1407
|
+
const targetGroupId = String(options.group_aid ?? options.groupAid ?? options.group_id ?? options.groupId ?? '').trim();
|
|
1030
1408
|
const targetDeviceId = String(options.device_id ?? options.deviceId ?? '').trim();
|
|
1031
1409
|
const targetSlotId = String(options.slot_id ?? options.slotId ?? '').trim();
|
|
1032
1410
|
const ttl = AUNClient._normalizeNotifyTtl(options.ttl_ms ?? options.ttlMs);
|
|
@@ -1038,6 +1416,8 @@ export class AUNClient {
|
|
|
1038
1416
|
}
|
|
1039
1417
|
if (targetAid) {
|
|
1040
1418
|
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
1419
|
+
// 校验目标 AID 格式(拒绝 __system__ 等非法格式)
|
|
1420
|
+
validateAIDFormat(targetAid, 'notify.to');
|
|
1041
1421
|
const target = { type: 'aid', aid: targetAid };
|
|
1042
1422
|
if (targetDeviceId)
|
|
1043
1423
|
target.device_id = targetDeviceId;
|
|
@@ -1054,11 +1434,14 @@ export class AUNClient {
|
|
|
1054
1434
|
}
|
|
1055
1435
|
if (targetGroupId) {
|
|
1056
1436
|
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
1437
|
+
// 校验目标 Group ID 格式
|
|
1438
|
+
validateGroupIDFormat(targetGroupId, 'notify.group_id');
|
|
1057
1439
|
const normalizedGroupId = normalizeGroupId(targetGroupId);
|
|
1058
1440
|
if (!normalizedGroupId) {
|
|
1059
1441
|
throw new ValidationError('group_id is required for group notify');
|
|
1060
1442
|
}
|
|
1061
1443
|
const routeParams = {
|
|
1444
|
+
group_aid: normalizedGroupId,
|
|
1062
1445
|
group_id: normalizedGroupId,
|
|
1063
1446
|
deliver: { method: eventMethod, params: payload },
|
|
1064
1447
|
};
|
|
@@ -1078,12 +1461,17 @@ export class AUNClient {
|
|
|
1078
1461
|
}
|
|
1079
1462
|
async _callRawV2Rpc(method, params) {
|
|
1080
1463
|
const p = { ...(params ?? {}) };
|
|
1464
|
+
const rpcBackground = Boolean(p._rpc_background) || this._backgroundRpcDepth > 0;
|
|
1465
|
+
delete p._rpc_background;
|
|
1081
1466
|
delete p._pull_gate_locked;
|
|
1082
1467
|
delete p._skip_auto_ack;
|
|
1083
1468
|
delete p.skip_auto_ack;
|
|
1084
1469
|
delete p._group_cursor_params;
|
|
1085
|
-
if (method.startsWith('group.') && p.
|
|
1086
|
-
p.
|
|
1470
|
+
if (method.startsWith('group.') && p.group_aid !== undefined && p.group_aid !== null) {
|
|
1471
|
+
p.group_aid = normalizeGroupId(String(p.group_aid)) || String(p.group_aid);
|
|
1472
|
+
}
|
|
1473
|
+
else if (method.startsWith('group.') && p.group_id !== undefined && p.group_id !== null) {
|
|
1474
|
+
p.group_aid = normalizeGroupId(String(p.group_id)) || String(p.group_id);
|
|
1087
1475
|
}
|
|
1088
1476
|
if (method.startsWith('group.') && p.device_id === undefined) {
|
|
1089
1477
|
p.device_id = this._deviceId;
|
|
@@ -1091,7 +1479,7 @@ export class AUNClient {
|
|
|
1091
1479
|
if (method.startsWith('group.') && p.slot_id === undefined) {
|
|
1092
1480
|
p.slot_id = this._slotId;
|
|
1093
1481
|
}
|
|
1094
|
-
return await this._rpcPipeline.rawCall(method, p);
|
|
1482
|
+
return await this._rpcPipeline.rawCall(method, p, { background: rpcBackground });
|
|
1095
1483
|
}
|
|
1096
1484
|
// ── 事件 ──────────────────────────────────────────
|
|
1097
1485
|
/**
|
|
@@ -1195,19 +1583,19 @@ export class AUNClient {
|
|
|
1195
1583
|
}
|
|
1196
1584
|
_extractGroupIdFromResult(result) {
|
|
1197
1585
|
const group = isJsonObject(result.group) ? result.group : null;
|
|
1198
|
-
const gid = group ? String(group.group_id ?? '') : '';
|
|
1586
|
+
const gid = group ? String(group.group_aid ?? group.group_id ?? '') : '';
|
|
1199
1587
|
if (gid)
|
|
1200
1588
|
return gid;
|
|
1201
|
-
const directGid = String(result.group_id ?? '');
|
|
1589
|
+
const directGid = String(result.group_aid ?? result.group_id ?? '');
|
|
1202
1590
|
if (directGid)
|
|
1203
1591
|
return directGid;
|
|
1204
1592
|
const member = isJsonObject(result.member) ? result.member : null;
|
|
1205
|
-
return member ? String(member.group_id ?? '') : '';
|
|
1593
|
+
return member ? String(member.group_aid ?? member.group_id ?? '') : '';
|
|
1206
1594
|
}
|
|
1207
1595
|
async _onRawGroupChanged(data) {
|
|
1208
1596
|
const tStart = Date.now();
|
|
1209
1597
|
const action = String(data?.action ?? '');
|
|
1210
|
-
const groupIdInit = String(data?.group_id ?? '');
|
|
1598
|
+
const groupIdInit = String(data?.group_aid ?? data?.group_id ?? '');
|
|
1211
1599
|
this._clientLog.debug(`_onRawGroupChanged enter: group_id=${groupIdInit} action=${action}`);
|
|
1212
1600
|
try {
|
|
1213
1601
|
if (isJsonObject(data)) {
|
|
@@ -1223,7 +1611,7 @@ export class AUNClient {
|
|
|
1223
1611
|
d._verified = this._isEventSignatureVerified(verified);
|
|
1224
1612
|
}
|
|
1225
1613
|
}
|
|
1226
|
-
const groupId = (d.group_id ?? '');
|
|
1614
|
+
const groupId = (d.group_aid ?? d.group_id ?? '');
|
|
1227
1615
|
await this._delivery.handleGroupChangedEventSeq(d, groupId);
|
|
1228
1616
|
}
|
|
1229
1617
|
else {
|
|
@@ -1256,9 +1644,13 @@ export class AUNClient {
|
|
|
1256
1644
|
this._v2E2EE.deleteBootstrapCacheEntry(`group:${groupId}`);
|
|
1257
1645
|
this._v2GroupSecurityLevels.delete(groupId);
|
|
1258
1646
|
this._v2StateChains.delete(groupId);
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
this.
|
|
1647
|
+
const groupNs = `group:${groupId}`;
|
|
1648
|
+
const groupEventNs = `group_event:${groupId}`;
|
|
1649
|
+
this._seqTracker.removeNamespace(groupNs);
|
|
1650
|
+
this._seqTracker.removeNamespace(groupEventNs);
|
|
1651
|
+
this._persistRepairedSeq(groupNs);
|
|
1652
|
+
this._persistRepairedSeq(groupEventNs);
|
|
1653
|
+
void this._saveSeqTrackerState();
|
|
1262
1654
|
// 2. 清理补洞去重缓存中的相关条目
|
|
1263
1655
|
for (const key of this._gapFillDone.keys()) {
|
|
1264
1656
|
if (key.includes(groupId)) {
|
|
@@ -1266,10 +1658,10 @@ export class AUNClient {
|
|
|
1266
1658
|
}
|
|
1267
1659
|
}
|
|
1268
1660
|
// 3. 清理推送 seq 去重缓存
|
|
1269
|
-
this._pushedSeqs.delete(
|
|
1270
|
-
this._pushedSeqs.delete(
|
|
1271
|
-
this._pendingOrderedMsgs.delete(
|
|
1272
|
-
this._pendingOrderedMsgs.delete(
|
|
1661
|
+
this._pushedSeqs.delete(groupNs);
|
|
1662
|
+
this._pushedSeqs.delete(groupEventNs);
|
|
1663
|
+
this._pendingOrderedMsgs.delete(groupNs);
|
|
1664
|
+
this._pendingOrderedMsgs.delete(groupEventNs);
|
|
1273
1665
|
this._clientLog.info(`cleanup dissolved group ${groupId} local state`);
|
|
1274
1666
|
}
|
|
1275
1667
|
async _verifyEventSignature(_event, cs) {
|
|
@@ -1766,7 +2158,7 @@ export class AUNClient {
|
|
|
1766
2158
|
let consecutiveFailures = 0;
|
|
1767
2159
|
const maxFailures = 2;
|
|
1768
2160
|
this._heartbeatTimer = setInterval(async () => {
|
|
1769
|
-
if (this.
|
|
2161
|
+
if (this.state !== ConnectionState.READY || this._closing)
|
|
1770
2162
|
return;
|
|
1771
2163
|
try {
|
|
1772
2164
|
const pong = await this._transport.call('meta.ping', {});
|
|
@@ -1801,7 +2193,7 @@ export class AUNClient {
|
|
|
1801
2193
|
clearInterval(this._heartbeatTimer);
|
|
1802
2194
|
this._heartbeatTimer = null;
|
|
1803
2195
|
}
|
|
1804
|
-
if (newInterval > 0 && this.
|
|
2196
|
+
if (newInterval > 0 && this.state === ConnectionState.READY && !this._closing) {
|
|
1805
2197
|
this._startHeartbeat();
|
|
1806
2198
|
}
|
|
1807
2199
|
}
|
|
@@ -1820,7 +2212,7 @@ export class AUNClient {
|
|
|
1820
2212
|
if (this._closing)
|
|
1821
2213
|
return;
|
|
1822
2214
|
this._tokenRefreshTimer = null;
|
|
1823
|
-
if (this.
|
|
2215
|
+
if (this.state !== ConnectionState.READY || !this._gatewayUrl) {
|
|
1824
2216
|
scheduleRefresh();
|
|
1825
2217
|
return;
|
|
1826
2218
|
}
|
|
@@ -1838,14 +2230,14 @@ export class AUNClient {
|
|
|
1838
2230
|
scheduleRefresh();
|
|
1839
2231
|
return;
|
|
1840
2232
|
}
|
|
1841
|
-
if (this.
|
|
2233
|
+
if (this.state !== ConnectionState.READY || !this._gatewayUrl || this._closing) {
|
|
1842
2234
|
scheduleRefresh();
|
|
1843
2235
|
return;
|
|
1844
2236
|
}
|
|
1845
2237
|
try {
|
|
1846
2238
|
identity = await this._auth.refreshCachedTokens(this._gatewayUrl, identity);
|
|
1847
2239
|
// 刷新期间可能已断线,复检状态,避免写回 stale identity
|
|
1848
|
-
if (this.
|
|
2240
|
+
if (this.state !== ConnectionState.READY) {
|
|
1849
2241
|
scheduleRefresh();
|
|
1850
2242
|
return;
|
|
1851
2243
|
}
|
|
@@ -1930,6 +2322,9 @@ export class AUNClient {
|
|
|
1930
2322
|
this._state = 'standby';
|
|
1931
2323
|
// 先停止后台任务,避免心跳/token刷新在重连期间继续触发
|
|
1932
2324
|
this._stopBackgroundTasks();
|
|
2325
|
+
void this._transport.close().catch((exc) => {
|
|
2326
|
+
this._clientLog.debug(`transport cleanup skipped: ${formatCaughtError(exc)}`);
|
|
2327
|
+
});
|
|
1933
2328
|
await this._dispatcher.publish('state_change', {
|
|
1934
2329
|
state: this._publicState(this._state),
|
|
1935
2330
|
error,
|
|
@@ -2108,6 +2503,7 @@ export class AUNClient {
|
|
|
2108
2503
|
return JSON.stringify([this._aid, this._deviceId, this._slotId]);
|
|
2109
2504
|
}
|
|
2110
2505
|
_resetSeqTrackingState() {
|
|
2506
|
+
void this._saveSeqTrackerState();
|
|
2111
2507
|
this._resetV2IdentityRuntime();
|
|
2112
2508
|
this._seqTracker = new SeqTracker();
|
|
2113
2509
|
this._seqTrackerContext = null;
|
|
@@ -2128,6 +2524,7 @@ export class AUNClient {
|
|
|
2128
2524
|
const nextContext = this._currentSeqTrackerContext();
|
|
2129
2525
|
if (nextContext === this._seqTrackerContext)
|
|
2130
2526
|
return;
|
|
2527
|
+
void this._saveSeqTrackerState();
|
|
2131
2528
|
this._seqTracker = new SeqTracker();
|
|
2132
2529
|
this._gapFillDone.clear();
|
|
2133
2530
|
this._pushedSeqs.clear();
|
|
@@ -2147,6 +2544,9 @@ export class AUNClient {
|
|
|
2147
2544
|
_saveSeqTrackerState() {
|
|
2148
2545
|
return this._delivery.saveSeqTrackerState();
|
|
2149
2546
|
}
|
|
2547
|
+
_persistSeq(ns, forceSeq) {
|
|
2548
|
+
return this._delivery.persistSeq(ns, forceSeq);
|
|
2549
|
+
}
|
|
2150
2550
|
_persistRepairedSeq(ns) {
|
|
2151
2551
|
return this._delivery.persistRepairedSeq(ns);
|
|
2152
2552
|
}
|
|
@@ -2663,12 +3063,6 @@ export class AUNClient {
|
|
|
2663
3063
|
async _onV2PushNotification(data) {
|
|
2664
3064
|
return this._delivery.onV2PushNotification(data);
|
|
2665
3065
|
}
|
|
2666
|
-
/**
|
|
2667
|
-
* 处理 V2 epoch 轮换事件:清除 bootstrap 缓存 + 触发 SPK rotation。
|
|
2668
|
-
*/
|
|
2669
|
-
async _onV2EpochRotated(data) {
|
|
2670
|
-
return this._v2E2EE.handleV2EpochRotated(data);
|
|
2671
|
-
}
|
|
2672
3066
|
/** 安全执行异步操作(不阻塞调用方,错误打 warning 便于排障) */
|
|
2673
3067
|
_safeAsync(promise) {
|
|
2674
3068
|
promise.catch((exc) => {
|
|
@@ -2688,6 +3082,15 @@ export class AUNClient {
|
|
|
2688
3082
|
&& (!slotId || slotId === (this._slotId ?? ''));
|
|
2689
3083
|
}
|
|
2690
3084
|
// ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
|
|
3085
|
+
async _withBackgroundRpc(operation) {
|
|
3086
|
+
this._backgroundRpcDepth += 1;
|
|
3087
|
+
try {
|
|
3088
|
+
return await operation();
|
|
3089
|
+
}
|
|
3090
|
+
finally {
|
|
3091
|
+
this._backgroundRpcDepth = Math.max(0, this._backgroundRpcDepth - 1);
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
2691
3094
|
async _runPullSerialized(key, operation) {
|
|
2692
3095
|
return await this._rpcPipeline.runPullSerialized(key, operation);
|
|
2693
3096
|
}
|