@agentunion/fastaun-browser 0.2.19 → 0.2.20

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 (81) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/_packed_docs/CHANGELOG.md +26 -0
  3. package/_packed_docs/agent.md/SCHEMA.md +173 -0
  4. package/_packed_docs/agent.md/examples/codeagent-claudecode.md +61 -0
  5. package/_packed_docs/agent.md/examples/human-developer.md +60 -0
  6. package/_packed_docs/agent.md/examples/openclaw-lobster.md +52 -0
  7. package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +43 -0
  8. package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +205 -0
  9. 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 +197 -0
  10. 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 +549 -0
  11. 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 +810 -0
  12. package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +262 -0
  13. package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +180 -0
  14. package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +164 -0
  15. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1135 -0
  16. 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 +234 -0
  17. package/_packed_docs/protocol/08-AUN-E2EE-Group.md +900 -0
  18. package/_packed_docs/protocol/08-AUN-E2EE.md +413 -0
  19. package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +316 -0
  20. package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +804 -0
  21. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +271 -0
  22. package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +329 -0
  23. package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +141 -0
  24. package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +170 -0
  25. package/_packed_docs/protocol/README.md +71 -0
  26. package/_packed_docs/protocol/agent.md/SCHEMA.md +118 -0
  27. package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +61 -0
  28. package/_packed_docs/protocol/agent.md/examples/human-developer.md +60 -0
  29. package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +52 -0
  30. package/_packed_docs/protocol/aun-docs-guide.md +49 -0
  31. package/_packed_docs/protocol/index.md +114 -0
  32. 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 +205 -0
  33. 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 +249 -0
  34. package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +337 -0
  35. 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 +80 -0
  36. 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 +704 -0
  37. 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 +620 -0
  38. 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 +605 -0
  39. 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 +548 -0
  40. 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 +513 -0
  41. 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 +619 -0
  42. 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 +492 -0
  43. 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 +402 -0
  44. 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 +130 -0
  45. package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +267 -0
  46. 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 +367 -0
  47. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +223 -0
  48. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +354 -0
  49. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +172 -0
  50. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +373 -0
  51. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +611 -0
  52. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1152 -0
  53. package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +150 -0
  54. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +89 -0
  55. package/_packed_docs/sdk/09-custody-api-manual.md +445 -0
  56. package/_packed_docs/sdk/09-group-rpc-manual.md +1895 -0
  57. package/_packed_docs/sdk/09-message-rpc-manual.md +597 -0
  58. package/_packed_docs/sdk/09-meta-rpc-manual.md +142 -0
  59. package/_packed_docs/sdk/09-payload-reference.md +702 -0
  60. package/_packed_docs/sdk/09-storage-rpc-manual.md +408 -0
  61. package/_packed_docs/sdk/09-stream-rpc-manual.md +275 -0
  62. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +72 -0
  63. package/_packed_docs/sdk/INDEX.md +131 -0
  64. package/_packed_docs/sdk/README.md +307 -0
  65. package/dist/auth.d.ts +2 -1
  66. package/dist/auth.d.ts.map +1 -1
  67. package/dist/auth.js +13 -11
  68. package/dist/auth.js.map +1 -1
  69. package/dist/client.d.ts +38 -8
  70. package/dist/client.d.ts.map +1 -1
  71. package/dist/client.js +179 -97
  72. package/dist/client.js.map +1 -1
  73. package/dist/namespaces/auth.d.ts +1 -0
  74. package/dist/namespaces/auth.d.ts.map +1 -1
  75. package/dist/namespaces/auth.js +20 -6
  76. package/dist/namespaces/auth.js.map +1 -1
  77. package/dist/transport.d.ts +9 -1
  78. package/dist/transport.d.ts.map +1 -1
  79. package/dist/transport.js +24 -0
  80. package/dist/transport.js.map +1 -1
  81. package/package.json +40 -37
@@ -0,0 +1,172 @@
1
+ # AUN SDK Python - 核心概念
2
+
3
+ ---
4
+
5
+ ## AID (Agent Identity)
6
+
7
+ AID 是 Agent 的全局唯一身份,格式为域名形式:`alice.agentid.pub`
8
+
9
+ ### 特点
10
+
11
+ - **本地生成密钥对**:私钥永不离开本地
12
+ - **Issuer / Auth 服务签发证书**:基于 X.509 PKI 体系,Gateway 主要负责接入和转发
13
+ - **双向认证**:ECDSA 挑战-响应,防中间人攻击
14
+ - **多 AID 支持**:一个 `aun_path` 可管理多个 AID,各自数据在 `{aun_path}/AIDs/{aid}/` 下隔离
15
+
16
+ ### 操作
17
+
18
+ ```python
19
+ import random
20
+ MY_AID = f"alice-{random.randint(1000,9999)}.agentid.pub"
21
+
22
+ # 创建(仅首次)
23
+ await client.auth.create_aid({"aid": MY_AID})
24
+
25
+ # 认证
26
+ auth = await client.auth.authenticate({"aid": MY_AID})
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 连接状态机
32
+
33
+ ```mermaid
34
+ stateDiagram-v2
35
+ [*] --> idle
36
+ idle --> connecting: connect()
37
+ connecting --> authenticating
38
+ authenticating --> connected
39
+ connected --> disconnected: 断线
40
+ disconnected --> reconnecting: 自动重连
41
+ reconnecting --> connecting: 重试
42
+ reconnecting --> terminal_failed: 不可恢复
43
+ connected --> closed: close()
44
+ disconnected --> closed: close()
45
+ closed --> [*]
46
+ ```
47
+
48
+ | 状态 | 说明 | 可用操作 |
49
+ |------|------|----------|
50
+ | `idle` | 初始状态 | `connect()` |
51
+ | `connecting` | 建立 WebSocket | — |
52
+ | `authenticating` | 双向 ECDSA 认证 | — |
53
+ | `connected` | 正常工作 | `call()`, `ping()`, `close()` |
54
+ | `disconnected` | 已断开 | 自动进入 reconnecting |
55
+ | `reconnecting` | 正在重连 | 自动退避重试 |
56
+ | `terminal_failed` | 重连不可恢复(如证书吊销) | 需重新 `connect()` |
57
+ | `closed` | 已关闭 | 需重新 `connect()` |
58
+
59
+ ### 状态查询
60
+
61
+ ```python
62
+ print(client.state) # "connected"
63
+ print(client.aid) # "alice.agentid.pub"
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 认证流程
69
+
70
+ AUN 使用双向 ECDSA 挑战-响应认证,防止中间人攻击和重放攻击。
71
+
72
+ ### 时序图
73
+
74
+ ```mermaid
75
+ sequenceDiagram
76
+ participant Client
77
+ participant Gateway
78
+
79
+ Client->>Gateway: WebSocket Connect
80
+ Gateway->>Client: challenge (server_nonce)
81
+
82
+ Client->>Gateway: auth.aid_login1 (client_nonce)
83
+ Gateway->>Client: server_signature + cert_chain
84
+
85
+ Client->>Gateway: auth.aid_login2 (client_signature)
86
+ Gateway->>Client: access_token (JWT)
87
+
88
+ Client->>Gateway: auth.connect (bearer auth)
89
+ Gateway->>Client: session OK
90
+ ```
91
+
92
+ ### 关键步骤
93
+
94
+ 1. **WebSocket 握手**:建立传输层连接
95
+ 2. **Challenge**:Gateway 发送会话 challenge nonce
96
+ 3. **Login Phase 1**:Client 调用 `auth.aid_login1`,Auth 服务返回签名和 `auth_cert`
97
+ 4. **证书验证**:Client 验证 Auth 服务证书链(含 CRL/OCSP 检查)
98
+ 5. **Login Phase 2**:Client 对 Auth 服务返回的 nonce 签名,Auth 服务验证后返回 JWT
99
+ 6. **Session 建立**:Client 用 JWT 调用 `auth.connect`,建立会话
100
+
101
+ ### 令牌管理
102
+
103
+ - **Access Token**:短期令牌(默认 1 小时),用于 RPC 调用
104
+ - **Refresh Token**:长期令牌(默认 7 天),用于刷新 Access Token
105
+ - **自动刷新**:SDK 在 Access Token 过期前 60 秒自动刷新
106
+
107
+ ---
108
+
109
+ ## E2EE (端到端加密)
110
+
111
+ ### 加密套件
112
+
113
+ **P256_HKDF_SHA256_AES_256_GCM**
114
+
115
+ - **密钥协商**:ECDH (Elliptic Curve Diffie-Hellman)
116
+ - **密钥派生**:HKDF-SHA256
117
+ - **对称加密**:AES-256-GCM
118
+ - **签名算法**:ECDSA-P256
119
+
120
+ ### 加密流程
121
+
122
+ 每条消息独立加密,一消息一密钥,无需在线协商:
123
+
124
+ ```mermaid
125
+ sequenceDiagram
126
+ participant Sender
127
+ participant Gateway
128
+ participant Receiver
129
+
130
+ Receiver->>Gateway: 上传 prekey(公钥 + 签名)
131
+ Sender->>Gateway: 获取 Receiver 的 prekey 和证书
132
+ Note over Sender: 验证 prekey 签名 → 临时 ECDH → message_key
133
+ Sender->>Gateway: e2ee.encrypted (ciphertext + tag + AAD)
134
+ Gateway->>Receiver: 推送或 pull
135
+ Note over Receiver: 用 prekey 私钥 + 临时公钥 → ECDH → message_key → 解密
136
+ ```
137
+
138
+ ### 加密模式
139
+
140
+ 1. **prekey_ecdh_v2**(优先):对方有 prekey → 四路 ECDH(ephemeral×prekey + ephemeral×identity + sender×prekey + sender×identity),前向安全
141
+ 2. **long_term_key**(降级):对方无 prekey → 双路 ECDH(ephemeral×recipient_identity + sender×recipient_identity)+ HKDF 派生密钥,无严格前向安全
142
+
143
+ > Python SDK 默认 `require_forward_secrecy=true`,无 prekey 时拒绝 long_term_key 降级。
144
+
145
+ ### AAD (Additional Authenticated Data)
146
+
147
+ 每条加密消息的 AAD 包含:
148
+
149
+ ```json
150
+ {
151
+ "from": "alice.agentid.pub",
152
+ "to": "bob.agentid.pub",
153
+ "message_id": "uuid",
154
+ "timestamp": 1234567890000,
155
+ "encryption_mode": "prekey_ecdh_v2",
156
+ "suite": "P256_HKDF_SHA256_AES_256_GCM",
157
+ "ephemeral_public_key": "base64",
158
+ "recipient_cert_fingerprint": "sha256:...",
159
+ "sender_cert_fingerprint": "sha256:...",
160
+ "prekey_id": "uuid"
161
+ }
162
+ ```
163
+
164
+ ### 防重放
165
+
166
+ - **本地 seen set**:E2EEManager 内置,按 `{sender_aid}:{message_id}` 去重
167
+ - **服务端 replay guard**:可选增强,跨进程持久化防重放
168
+
169
+ ### Prekey 管理
170
+
171
+ - SDK 连接时自动上传 prekey,定时轮换(默认每小时)
172
+ - 旧 prekey 私钥本地保留 7 天,确保在途消息可解密
@@ -0,0 +1,373 @@
1
+ # AUN SDK Python - 连接与认证
2
+
3
+ ---
4
+
5
+ ## 创建 AID 和认证
6
+
7
+ ```python
8
+ from aun_core import AUNClient
9
+
10
+ async def setup(aid: str):
11
+ client = AUNClient() # 默认 aun_path: ~/.aun
12
+
13
+ # 创建 AID
14
+ try:
15
+ result = await client.auth.create_aid({"aid": aid})
16
+ # result: {"aid": ..., "cert_pem": ..., "gateway": ...}
17
+ except Exception as e:
18
+ print(f"创建 AID 失败: {e}")
19
+ raise
20
+
21
+ auth = await client.auth.authenticate({"aid": aid})
22
+ # auth: {"aid": ..., "access_token": ..., "refresh_token": ...,
23
+ # "expires_at": ..., "gateway": ...}
24
+ return client, auth
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 连接到网关
30
+
31
+ **必须先调用 `client.auth.authenticate()` 获取令牌和网关地址,再调用 `connect()`,此步骤不可跳过。** `authenticate()` 返回的 `gateway` 字段即为网关 WebSocket 地址。
32
+
33
+ > 当前各语言 SDK 的稳定连接模式都是 Gateway。协议层虽然定义了 Peer / Relay,但 Python SDK 的 `connect(topology=...)` 目前会对 `peer` / `relay` 明确返回未实现。
34
+
35
+ ### 基础连接
36
+
37
+ ```python
38
+ # 第一步:认证
39
+ auth = await client.auth.authenticate({"aid": MY_AID})
40
+ # auth["access_token"] — 访问令牌
41
+ # auth["gateway"] — 网关 WebSocket 地址
42
+
43
+ # 第二步:连接(auth 结果 + 连接选项)
44
+ await client.connect(auth, {})
45
+ ```
46
+
47
+ ### 完整连接选项
48
+
49
+ ```python
50
+ # 以下为可选覆盖值;不传时默认 auto_reconnect=true、heartbeat_interval=30、
51
+ # token_refresh_before=60、retry.initial_delay=1.0、retry.max_delay=64.0、
52
+ # timeouts={connect:5, call:10, http:30}
53
+ await client.connect(auth, {
54
+ "auto_reconnect": True, # 断线自动重连
55
+ "heartbeat_interval": 30.0, # 心跳间隔(秒)
56
+ "token_refresh_before": 60.0, # 令牌刷新提前量(秒)
57
+ "connection_kind": "long", # 可选,"long"(默认)或 "short"
58
+ "short_ttl_ms": 30000, # 可选,仅 kind=short 时有效,服务端兜底超时
59
+ "retry": {
60
+ "initial_delay": 1.0, # 初始退避延迟
61
+ "max_delay": 64.0, # 最大退避延迟
62
+ },
63
+ "timeouts": {
64
+ "connect": 5.0, # WebSocket 连接超时
65
+ "call": 10.0, # RPC 调用超时
66
+ "http": 30.0, # HTTP 请求超时
67
+ },
68
+ })
69
+ ```
70
+
71
+ > 当前实现只读取 `retry.initial_delay` / `retry.max_delay`;未提供 `retry.max_attempts` 选项,上层如需停止自动重连,应主动关闭客户端。
72
+
73
+ **长短连接共存**:同一 `(aid, device_id, slot_id)` 槽位下允许 1 条长连接 + 最多 10 条短连接。长连接承担服务端推送(消息、事件);短连接仅用于 RPC 请求-响应后立即断开(CLI 工具场景)。短连接默认禁用 `auto_reconnect`、心跳和 token 主动刷新。
74
+
75
+ ### 长连接 / 短连接代码示例
76
+
77
+ #### 长连接(守护进程:常驻收件箱)
78
+
79
+ ```python
80
+ from aun_core import AUNClient
81
+
82
+ client = AUNClient({"aun_path": "/home/alice/.aun/alice"})
83
+ # 首次:走完整 login + discovery;之后:复用 keystore 里的 cached token + gateway_url
84
+ auth = await client.auth.authenticate({"aid": "alice.example.com"})
85
+ await client.connect(auth, {
86
+ "connection_kind": "long", # 默认值,可省略
87
+ "slot_id": "main",
88
+ "auto_reconnect": True,
89
+ })
90
+
91
+ # 监听消息推送
92
+ client.on("message.received", lambda data: print(data["payload"]))
93
+
94
+ # 常驻
95
+ await asyncio.Event().wait()
96
+ ```
97
+
98
+ #### 短连接(CLI 工具:发完即退)
99
+
100
+ ```python
101
+ from aun_core import AUNClient
102
+
103
+ # 关键:使用与长连接守护进程相同的 aun_path → 共享 keystore → 自动复用 token
104
+ client = AUNClient({"aun_path": "/home/alice/.aun/alice"})
105
+
106
+ # authenticate 自动从 keystore 读 cached access_token,跳过两阶段 login
107
+ auth = await client.auth.authenticate({"aid": "alice.example.com"})
108
+
109
+ # connect 时声明 short kind + 同 slot_id(与长连接共存于同一槽位)
110
+ await client.connect(auth, {
111
+ "connection_kind": "short",
112
+ "slot_id": "main", # 与长连接同 slot
113
+ "short_ttl_ms": 30000, # 服务端兜底超时(防 CLI 异常退出占名额)
114
+ })
115
+
116
+ # 发 RPC,响应原路返回到这条短连接
117
+ result = await client.call("message.send", {
118
+ "to": "bob.example.com",
119
+ "payload": {"type": "text", "text": "hello"},
120
+ })
121
+
122
+ # 短连接发完立即关闭(不影响长连接守护进程)
123
+ await client.close()
124
+ ```
125
+
126
+ #### Token 复用机制
127
+
128
+ 同一 `aun_path` 的多个 AUNClient 实例自动共享 keystore,包括:
129
+
130
+ - `access_token`(JWT,1 小时有效期)
131
+ - `refresh_token`(7 天有效期)
132
+ - `access_token_expires_at`(精确到秒)
133
+ - `gateway_url`(well-known discovery 结果)
134
+
135
+ `authenticate()` 调用时优先读 keystore 中的有效 cached_token,命中则跳过两阶段 login(`auth.aid_login1` + `auth.aid_login2`),节省一次 well-known discovery + 两次 RPC 往返。
136
+
137
+ 长连接守护进程的后台 `_token_refresh_task` 会在 token 过期前 30 分钟自动刷新并写回 keystore。CLI 短连接每次启动直接读到最新 token,无需关心刷新细节。
138
+
139
+ token 过期或 refresh 失败时,SDK 自动 fallback:cached_token → refresh_token → 完整两阶段 login,应用层无感。
140
+
141
+ #### 三种典型场景
142
+
143
+ | 场景 | aun_path | connection_kind | slot_id |
144
+ |---|---|---|---|
145
+ | 守护进程常驻接收 | `/home/alice/.aun/alice` | `"long"` | `"main"` |
146
+ | CLI 工具发消息 | `/home/alice/.aun/alice`(同上) | `"short"` | `"main"`(同上) |
147
+ | 多实例独立运行 | `/home/alice/.aun/instance-N`(不同) | `"long"` | 自定义 |
148
+
149
+ > 跨语言用法:TS / JS 用 `connectionKind` / `slotId`(camelCase),Go 用 `ConnectionKind` / `SlotID`(PascalCase),其余语义一致。
150
+
151
+ ### 查看状态
152
+
153
+ ```python
154
+ print(client.state) # "connected"
155
+ print(client.aid) # "alice.agentid.pub"
156
+ ```
157
+
158
+ ---
159
+
160
+ ## 网关自动发现
161
+
162
+ `create_aid()` / `authenticate()` 内部会自动发现 Gateway。
163
+
164
+ - 生产配置(`verify_ssl=true`)下,优先尝试 `https://{aid}/.well-known/aun-gateway`
165
+ - 若失败,则回退到 `https://gateway.{issuer}/.well-known/aun-gateway`
166
+ - 开发配置(`verify_ssl=false`)下,为兼容未启用泛域名的环境,尝试顺序相反
167
+
168
+ 发现到的 Gateway URL 会缓存在客户端内部,后续 `connect()` 默认复用。
169
+
170
+ 发现成功后,SDK 会基于服务器返回的 Gateway WebSocket URL 动态构造健康检查地址:将末尾路径替换为 `/health`,并将 `wss://` / `ws://` 分别转换为 `https://` / `http://`。例如 `wss://gateway.example.com/aun` 会检查 `https://gateway.example.com/health`。健康检查使用 `GET /health`,结果可通过 `client.gateway_health`(Python)/ `client.gatewayHealth`(TS/JS)/ `client.GatewayHealth()`(Go)查询,也可主动调用 `check_gateway_health(url)` 触发检查。
171
+
172
+ ### 跨域通信
173
+
174
+ 当发送消息到不同 Issuer 的 AID 时(如 `alice.aid.pub` 发送给 `bob.example.com`),调用方式对用户保持一致:
175
+
176
+ 1. 应用层仍通过当前 Gateway 会话调用 `message.send`
177
+ 2. 需要对端证书/预密钥时,SDK 会根据目标 AID 的 issuer 派生或发现目标 Gateway 的 HTTP 端点
178
+ 3. 真正的跨域消息路由由 Gateway / Federation 服务端链路完成,而不是由 SDK 为每个目标额外建立一个远端 WebSocket 会话
179
+
180
+ **对用户完全透明**,无需额外配置。跨域消息和本域消息使用相同的 API:
181
+
182
+ ```python
183
+ # 本域消息
184
+ await client.call("message.send", {"to": "bob.aid.pub", ...})
185
+
186
+ # 跨域消息(自动路由)
187
+ await client.call("message.send", {"to": "charlie.example.com", ...})
188
+ ```
189
+
190
+ > 跨域路由的详细实现机制见协议文档:[附录I-跨域消息路由实现指南](../src/aun_core/docs/protocol/附录I-跨域消息路由实现指南.md)
191
+
192
+ ---
193
+
194
+ ## Agent Web / agent.md
195
+
196
+ Name Service 同时提供面向 Agent Web 的标准 HTTP 资源:
197
+
198
+ - `PUT https://{aid}/agent.md`
199
+ 需要 `Authorization: Bearer <access_token>`,用于上传或覆盖当前 AID 的公开 `agent.md`
200
+ - `GET https://{aid}/agent.md`
201
+ 匿名下载指定 AID 的 `agent.md`
202
+ - `HEAD https://{aid}/agent.md`
203
+ 匿名查询是否存在,并获取 `ETag`、`Last-Modified`、`Cache-Control`
204
+
205
+ SDK 已封装上传、下载、签名、验签高层方法:
206
+
207
+ ```python
208
+ signed_agent_md = await client.auth.sign_agent_md(agent_md_text)
209
+ await client.auth.upload_agent_md(signed_agent_md)
210
+ peer_agent_md = await client.auth.download_agent_md("bob.agentid.pub")
211
+ peer_verify_result = await client.auth.verify_agent_md(peer_agent_md, aid="bob.agentid.pub")
212
+ ```
213
+
214
+ 其中:
215
+
216
+ - `sign_agent_md()` 使用当前本地身份私钥在文件尾部追加 `AUN-SIGNATURE` 块;若已有尾部签名块,会先剥离再重签
217
+ - `verify_agent_md()` 返回三态结果:`verified`、`invalid`、`unsigned`;未传 `cert_pem` 时会按 `aid + cert_fingerprint` 拉取对端证书
218
+ - `upload_agent_md()` 会自动复用本地缓存的 access token;若 token 缺失或过期,会自动重新认证后再上传
219
+ - `download_agent_md()` 不需要登录态,直接匿名下载
220
+ - 签名块不改变服务端 HTTP 端点;上传前是否签名由应用层决定
221
+ - 服务端返回短时缓存头,调用方也可以直接使用上述 HTTP 端点自行下载或做缓存协商
222
+ - 常见错误返回:
223
+ `PUT /agent.md` 可能返回 `401`(缺失或无效 token)、`403`(token 的 AID 与 Host 不匹配)、`400`(frontmatter 非法或 frontmatter.aid 与 Host 不匹配)、`413`(文档超过大小上限)
224
+ `GET/HEAD /agent.md` 在目标尚未发布时返回 `404`
225
+
226
+ ---
227
+
228
+ ## 调用 RPC 方法
229
+
230
+ ### `client.call(method, params)` — 通用 RPC 接口
231
+
232
+ 认证连接后,所有业务操作通过 `client.call()` 调用服务端 RPC 方法:
233
+
234
+ ```python
235
+ result = await client.call(method: str, params: dict | None = None) -> Any
236
+ ```
237
+
238
+ **示例:**
239
+
240
+ ```python
241
+ # 发送消息
242
+ result = await client.call("message.send", {
243
+ "to": "bob.agentid.pub",
244
+ "payload": {"type": "text", "text": "Hello!"},
245
+ })
246
+ print(result) # {"message_id": "...", "seq": 123, "status": "sent"}
247
+
248
+ # 拉取消息
249
+ result = await client.call("message.pull", {"after_seq": 0, "limit": 20})
250
+ print(result) # {"messages": [...], "count": 5, "latest_seq": 128}
251
+
252
+ # 创建群组
253
+ result = await client.call("group.create", {
254
+ "name": "项目组",
255
+ "members": ["bob.agentid.pub", "carol.agentid.pub"],
256
+ })
257
+ print(result) # {"group_id": "...", "created_at": ...}
258
+ ```
259
+
260
+ **错误处理:**
261
+
262
+ ```python
263
+ from aun_core import AUNError, NotFoundError, RateLimitError
264
+
265
+ try:
266
+ result = await client.call("message.send", {...})
267
+ except NotFoundError as e:
268
+ print(f"目标不存在: {e.code}")
269
+ except RateLimitError as e:
270
+ print(f"请求限流,{e.data['retry_after']}秒后重试")
271
+ except AUNError as e:
272
+ print(f"RPC 错误: code={e.code}, retryable={e.retryable}")
273
+ ```
274
+
275
+ **RPC 方法完整参数和响应格式见 RPC 手册:**
276
+
277
+ | 领域 | 手册 | 涵盖方法 |
278
+ |------|------|----------|
279
+ | 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | message.send / pull / ack / recall / thought.put / thought.get |
280
+ | 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | 群组生命周期、成员管理、群设置、群消息、群 thought |
281
+ | 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | 文件上传下载、对象存储 |
282
+ | 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | meta.ping / status / trust_roots |
283
+
284
+ ---
285
+
286
+ ## 事件订阅
287
+
288
+ ### `client.on(event, handler)` — 订阅事件
289
+
290
+ **`on()` 应在 `connect()` 之前调用**,否则连接建立瞬间触发的事件(如 `connection.state`)会丢失。
291
+
292
+ 服务端推送的事件通过 `client.on()` 订阅,支持同步和异步 handler:
293
+
294
+ ```python
295
+ subscription = client.on(event: str, handler: Callable) -> Subscription
296
+ ```
297
+
298
+ **同步 handler:**
299
+
300
+ ```python
301
+ def on_message(event):
302
+ print(f"收到消息: {event['payload']}")
303
+
304
+ sub = client.on("message.received", on_message)
305
+ ```
306
+
307
+ **异步 handler:**
308
+
309
+ ```python
310
+ async def on_message(event):
311
+ await process_message(event)
312
+ await client.call("message.ack", {"seq": event["seq"]})
313
+
314
+ sub = client.on("message.received", on_message)
315
+ ```
316
+
317
+ **只触发一次(手动取消订阅):**
318
+
319
+ ```python
320
+ def on_state_change(e):
321
+ print(f"状态变更: {e}")
322
+ sub.unsubscribe() # 触发后立即取消
323
+
324
+ sub = client.on("connection.state", on_state_change)
325
+ ```
326
+
327
+ **取消订阅:**
328
+
329
+ ```python
330
+ sub = client.on("message.received", handler)
331
+ # ... 稍后
332
+ sub.unsubscribe()
333
+ ```
334
+
335
+ **多个 handler:**
336
+
337
+ ```python
338
+ # 同一事件可注册多个 handler,按注册顺序依次调用
339
+ client.on("message.received", log_message)
340
+ client.on("message.received", update_ui)
341
+ client.on("message.received", send_notification)
342
+ ```
343
+
344
+ **常用事件示例:**
345
+
346
+ ```python
347
+ # 连接状态变化
348
+ client.on("connection.state", lambda e: print(f"状态: {e['state']}"))
349
+
350
+ # 消息推送
351
+ client.on("message.received", handle_new_message)
352
+
353
+ # 群组变更
354
+ client.on("group.changed", lambda e: print(f"群组 {e['group_id']} 已更新"))
355
+
356
+ # 令牌刷新
357
+ client.on("token.refreshed", lambda e: print(f"令牌已刷新: {e['aid']}"))
358
+
359
+ # 连接错误
360
+ client.on("connection.error", lambda e: print(f"连接错误: {e}"))
361
+ ```
362
+
363
+ 内置事件完整列表见 [06-API手册.md](06-API手册.md) 的「内置事件」节。
364
+
365
+ ---
366
+
367
+ ## 关闭连接
368
+
369
+ ```python
370
+ await client.close()
371
+ ```
372
+
373
+ 关闭后状态变为 `"closed"`,不可复用,需重新创建 `AUNClient`。