@agentunion/fastaun-browser 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/_packed_docs/CHANGELOG.md +26 -0
- package/_packed_docs/INDEX.md +81 -0
- package/_packed_docs/KITE_DOCS_GUIDE.md +55 -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 +328 -0
- package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +686 -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 +665 -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 +257 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +5 -5
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +1 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -2
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +454 -429
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +1410 -1398
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +19 -1
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +20 -5
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +8 -8
- package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +171 -0
- package/_packed_docs/sdk/INDEX.md +22 -22
- package/_packed_docs/sdk/README.md +3 -3
- package/dist/auth.d.ts +10 -11
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +127 -91
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +625 -274
- package/dist/client.d.ts +19 -10
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +238 -111
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +7 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +5 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb.d.ts +12 -0
- package/dist/keystore/indexeddb.d.ts.map +1 -1
- package/dist/keystore/indexeddb.js +64 -6
- package/dist/keystore/indexeddb.js.map +1 -1
- package/dist/namespaces/auth.d.ts +3 -3
- package/dist/namespaces/auth.d.ts.map +1 -1
- package/dist/namespaces/auth.js +39 -20
- package/dist/namespaces/auth.js.map +1 -1
- package/dist/secret-store/indexeddb-store.js +1 -1
- package/dist/secret-store/indexeddb-store.js.map +1 -1
- package/dist/transport.d.ts +9 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +158 -64
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.js +1 -1
- package/dist/v2/e2ee/decrypt.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.d.ts.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +3 -2
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/session/session.d.ts +1 -0
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +7 -1
- package/dist/v2/session/session.js.map +1 -1
- package/package.json +43 -43
- package/dist/e2ee-group.d.ts +0 -276
- package/dist/e2ee-group.d.ts.map +0 -1
- package/dist/e2ee-group.js +0 -1653
- package/dist/e2ee-group.js.map +0 -1
|
@@ -1,429 +1,454 @@
|
|
|
1
|
-
# AUN SDK Python - 连接与认证
|
|
2
|
-
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
#
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
await client.connect(auth, {
|
|
111
|
-
"connection_kind": "
|
|
112
|
-
"slot_id": "main",
|
|
113
|
-
"
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
#
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
```python
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
client.on("message.received",
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
#
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
#
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
|
|
1
|
+
# AUN SDK Python - 连接与认证
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## v0.3.4 行为变更
|
|
6
|
+
|
|
7
|
+
### 注册与认证分离
|
|
8
|
+
|
|
9
|
+
v0.3.4 起,`authenticate()` **不再隐式注册**。如果本地无完整身份(缺 keypair 或 cert),`authenticate()` 直接抛 `StateError`。
|
|
10
|
+
|
|
11
|
+
正确流程:
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
await client.auth.register_aid({"aid": aid}) # 1. 注册(生成密钥对 + 获取证书)
|
|
15
|
+
auth = await client.auth.authenticate({"aid": aid}) # 2. 认证(获取 token)
|
|
16
|
+
await client.connect(auth, {}) # 3. 连接
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**半成品恢复**:如果本地有 keypair 但无 cert(上次注册中断),`register_aid()` 会自动向服务端查询并恢复证书,无需手动清理。
|
|
20
|
+
|
|
21
|
+
### 迁移说明
|
|
22
|
+
|
|
23
|
+
| 旧 API(v0.3.3-) | 新 API(v0.3.4+) |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `create_aid()` | `register_aid()`(已移除 `create_aid`) |
|
|
26
|
+
| `authenticate()` 自动注册 | 必须先显式调用 `register_aid()` |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 注册 AID 和认证
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from aun_core import AUNClient
|
|
34
|
+
|
|
35
|
+
async def setup(aid: str):
|
|
36
|
+
client = AUNClient() # 默认 aun_path: ~/.aun
|
|
37
|
+
|
|
38
|
+
# 注册 AID
|
|
39
|
+
try:
|
|
40
|
+
result = await client.auth.register_aid({"aid": aid})
|
|
41
|
+
# result: {"aid": ..., "cert_pem": ..., "gateway": ...}
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"注册 AID 失败: {e}")
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
auth = await client.auth.authenticate({"aid": aid})
|
|
47
|
+
# auth: {"aid": ..., "access_token": ..., "refresh_token": ...,
|
|
48
|
+
# "expires_at": ..., "gateway": ...}
|
|
49
|
+
return client, auth
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 连接到网关
|
|
55
|
+
|
|
56
|
+
**必须先调用 `client.auth.authenticate()` 获取令牌和网关地址,再调用 `connect()`,此步骤不可跳过。** `authenticate()` 返回的 `gateway` 字段即为网关 WebSocket 地址。
|
|
57
|
+
|
|
58
|
+
> 当前各语言 SDK 的稳定连接模式都是 Gateway。协议层虽然定义了 Peer / Relay,但 Python SDK 的 `connect(topology=...)` 目前会对 `peer` / `relay` 明确返回未实现。
|
|
59
|
+
|
|
60
|
+
### 基础连接
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
# 第一步:认证
|
|
64
|
+
auth = await client.auth.authenticate({"aid": MY_AID})
|
|
65
|
+
# auth["access_token"] — 访问令牌
|
|
66
|
+
# auth["gateway"] — 网关 WebSocket 地址
|
|
67
|
+
|
|
68
|
+
# 第二步:连接(auth 结果 + 连接选项)
|
|
69
|
+
await client.connect(auth, {})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 完整连接选项
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
# 以下为可选覆盖值;不传时默认 auto_reconnect=true、heartbeat_interval=30、
|
|
76
|
+
# token_refresh_before=60、retry.initial_delay=1.0、retry.max_delay=64.0、
|
|
77
|
+
# timeouts={connect:5, call:10, http:30}
|
|
78
|
+
await client.connect(auth, {
|
|
79
|
+
"auto_reconnect": True, # 断线自动重连
|
|
80
|
+
"heartbeat_interval": 30.0, # 心跳间隔(秒)
|
|
81
|
+
"token_refresh_before": 60.0, # 令牌刷新提前量(秒)
|
|
82
|
+
"connection_kind": "long", # 可选,"long"(默认)或 "short"
|
|
83
|
+
"short_ttl_ms": 30000, # 可选,仅 kind=short 时有效,服务端兜底超时
|
|
84
|
+
"retry": {
|
|
85
|
+
"initial_delay": 1.0, # 初始退避延迟
|
|
86
|
+
"max_delay": 64.0, # 最大退避延迟
|
|
87
|
+
},
|
|
88
|
+
"timeouts": {
|
|
89
|
+
"connect": 5.0, # WebSocket 连接超时
|
|
90
|
+
"call": 10.0, # RPC 调用超时
|
|
91
|
+
"http": 30.0, # HTTP 请求超时
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
> 当前实现只读取 `retry.initial_delay` / `retry.max_delay`;未提供 `retry.max_attempts` 选项,上层如需停止自动重连,应主动关闭客户端。
|
|
97
|
+
|
|
98
|
+
**长短连接共存**:同一 `(aid, device_id, slot_id)` 槽位下允许 1 条长连接 + 最多 10 条短连接。长连接承担服务端推送(消息、事件);短连接仅用于 RPC 请求-响应后立即断开(CLI 工具场景)。短连接默认禁用 `auto_reconnect`、心跳和 token 主动刷新。
|
|
99
|
+
|
|
100
|
+
### 长连接 / 短连接代码示例
|
|
101
|
+
|
|
102
|
+
#### 长连接(守护进程:常驻收件箱)
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from aun_core import AUNClient
|
|
106
|
+
|
|
107
|
+
client = AUNClient({"aun_path": "/home/alice/.aun/alice"})
|
|
108
|
+
# 首次:走完整 login + discovery;之后:复用 keystore 里的 cached token + gateway_url
|
|
109
|
+
auth = await client.auth.authenticate({"aid": "alice.example.com"})
|
|
110
|
+
await client.connect(auth, {
|
|
111
|
+
"connection_kind": "long", # 默认值,可省略
|
|
112
|
+
"slot_id": "main",
|
|
113
|
+
"auto_reconnect": True,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
# 监听消息推送
|
|
117
|
+
client.on("message.received", lambda data: print(data["payload"]))
|
|
118
|
+
|
|
119
|
+
# 常驻
|
|
120
|
+
await asyncio.Event().wait()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
#### 短连接(CLI 工具:发完即退)
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from aun_core import AUNClient
|
|
127
|
+
|
|
128
|
+
# 关键:使用与长连接守护进程相同的 aun_path → 共享 keystore → 自动复用 token
|
|
129
|
+
client = AUNClient({"aun_path": "/home/alice/.aun/alice"})
|
|
130
|
+
|
|
131
|
+
# authenticate 自动从 keystore 读 cached access_token,跳过两阶段 login
|
|
132
|
+
auth = await client.auth.authenticate({"aid": "alice.example.com"})
|
|
133
|
+
|
|
134
|
+
# connect 时声明 short kind + 同 slot_id(与长连接共存于同一槽位)
|
|
135
|
+
await client.connect(auth, {
|
|
136
|
+
"connection_kind": "short",
|
|
137
|
+
"slot_id": "main", # 与长连接同 slot
|
|
138
|
+
"short_ttl_ms": 30000, # 服务端兜底超时(防 CLI 异常退出占名额)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
# 发 RPC,响应原路返回到这条短连接
|
|
142
|
+
result = await client.call("message.send", {
|
|
143
|
+
"to": "bob.example.com",
|
|
144
|
+
"payload": {"type": "text", "text": "hello"},
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
# 短连接发完立即关闭(不影响长连接守护进程)
|
|
148
|
+
await client.close()
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### Token 复用机制
|
|
152
|
+
|
|
153
|
+
同一 `aun_path` 的多个 AUNClient 实例自动共享 keystore,包括:
|
|
154
|
+
|
|
155
|
+
- `access_token`(JWT,1 小时有效期)
|
|
156
|
+
- `refresh_token`(7 天有效期)
|
|
157
|
+
- `access_token_expires_at`(精确到秒)
|
|
158
|
+
- `gateway_url`(well-known discovery 结果)
|
|
159
|
+
|
|
160
|
+
`authenticate()` 调用时优先读 keystore 中的有效 cached_token,命中则跳过两阶段 login(`auth.aid_login1` + `auth.aid_login2`),节省一次 well-known discovery + 两次 RPC 往返。
|
|
161
|
+
|
|
162
|
+
长连接守护进程的后台 `_token_refresh_task` 会在 token 过期前 30 分钟自动刷新并写回 keystore。CLI 短连接每次启动直接读到最新 token,无需关心刷新细节。
|
|
163
|
+
|
|
164
|
+
token 过期或 refresh 失败时,SDK 自动 fallback:cached_token → refresh_token → 完整两阶段 login,应用层无感。
|
|
165
|
+
|
|
166
|
+
#### 三种典型场景
|
|
167
|
+
|
|
168
|
+
| 场景 | aun_path | connection_kind | slot_id |
|
|
169
|
+
|---|---|---|---|
|
|
170
|
+
| 守护进程常驻接收 | `/home/alice/.aun/alice` | `"long"` | `"main"` |
|
|
171
|
+
| CLI 工具发消息 | `/home/alice/.aun/alice`(同上) | `"short"` | `"main"`(同上) |
|
|
172
|
+
| 多实例独立运行 | `/home/alice/.aun/instance-N`(不同) | `"long"` | 自定义 |
|
|
173
|
+
|
|
174
|
+
> 跨语言用法:TS / JS 用 `connectionKind` / `slotId`(camelCase),Go 用 `ConnectionKind` / `SlotID`(PascalCase),其余语义一致。
|
|
175
|
+
|
|
176
|
+
### 查看状态
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
print(client.state) # "connected"
|
|
180
|
+
print(client.aid) # "alice.agentid.pub"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 网关自动发现
|
|
186
|
+
|
|
187
|
+
`register_aid()` / `authenticate()` 内部会自动发现 Gateway。
|
|
188
|
+
|
|
189
|
+
- 生产配置(`verify_ssl=true`)下,优先尝试 `https://{aid}/.well-known/aun-gateway`
|
|
190
|
+
- 若失败,则回退到 `https://gateway.{issuer}/.well-known/aun-gateway`
|
|
191
|
+
- 开发配置(`verify_ssl=false`)下,为兼容未启用泛域名的环境,尝试顺序相反
|
|
192
|
+
|
|
193
|
+
发现到的 Gateway URL 会缓存在客户端内部,后续 `connect()` 默认复用。
|
|
194
|
+
|
|
195
|
+
发现成功后,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)` 触发检查。
|
|
196
|
+
|
|
197
|
+
### 跨域通信
|
|
198
|
+
|
|
199
|
+
当发送消息到不同 Issuer 的 AID 时(如 `alice.aid.pub` 发送给 `bob.example.com`),调用方式对用户保持一致:
|
|
200
|
+
|
|
201
|
+
1. 应用层仍通过当前 Gateway 会话调用 `message.send`
|
|
202
|
+
2. 需要对端证书/预密钥时,SDK 会根据目标 AID 的 issuer 派生或发现目标 Gateway 的 HTTP 端点
|
|
203
|
+
3. 真正的跨域消息路由由 Gateway / Federation 服务端链路完成,而不是由 SDK 为每个目标额外建立一个远端 WebSocket 会话
|
|
204
|
+
|
|
205
|
+
**对用户完全透明**,无需额外配置。跨域消息和本域消息使用相同的 API:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# 本域消息
|
|
209
|
+
await client.call("message.send", {"to": "bob.aid.pub", ...})
|
|
210
|
+
|
|
211
|
+
# 跨域消息(自动路由)
|
|
212
|
+
await client.call("message.send", {"to": "charlie.example.com", ...})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
> 跨域路由的详细实现机制见协议文档:[附录I-跨域消息路由实现指南](../src/aun_core/docs/protocol/附录I-跨域消息路由实现指南.md)
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Agent Web / agent.md
|
|
220
|
+
|
|
221
|
+
Name Service 同时提供面向 Agent Web 的标准 HTTP 资源:
|
|
222
|
+
|
|
223
|
+
- `PUT https://{aid}/agent.md`
|
|
224
|
+
需要 `Authorization: Bearer <access_token>`,用于上传或覆盖当前 AID 的公开 `agent.md`
|
|
225
|
+
- `GET https://{aid}/agent.md`
|
|
226
|
+
匿名下载指定 AID 的 `agent.md`
|
|
227
|
+
- `HEAD https://{aid}/agent.md`
|
|
228
|
+
匿名查询是否存在,并获取 `ETag`、`Last-Modified`、`Cache-Control`
|
|
229
|
+
|
|
230
|
+
### 推荐主 API(自 v0.x 起)
|
|
231
|
+
|
|
232
|
+
SDK 在 `AUNClient` 上提供三个一站式主方法,分别封装"发布"、"下载"、"一致性检查"三条主线。文件统一存放在 `{aun_path}/AgentMDs/{aid}/agent.md`,由 SDK 管理;元数据持久化到 `agent_md_cache` 表 / `agentmd.json`。
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
# 发布自己的 agent.md(读 SDK 管理的本地文件 → 签名 → 上传 → 刷新内部 etag)
|
|
236
|
+
await client.publish_agent_md()
|
|
237
|
+
|
|
238
|
+
# 检查本地与云端一致性(不主动下载;max_unsynced_days=0 时强制 HEAD)
|
|
239
|
+
state = await client.check_agent_md("bob.agentid.pub", max_unsynced_days=3)
|
|
240
|
+
|
|
241
|
+
# 下载并自动验签(自动写到 SDK 管理的目录)
|
|
242
|
+
if state["remote_found"] and not state["in_sync"]:
|
|
243
|
+
info = await client.fetch_agent_md("bob.agentid.pub")
|
|
244
|
+
print(info["signature"]["status"], info["in_sync"], info["saved_to"])
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
详细签名见 `06-API手册.md` 中的 `publish_agent_md` / `fetch_agent_md` / `check_agent_md` 章节。
|
|
248
|
+
|
|
249
|
+
| SDK | publish | fetch | check |
|
|
250
|
+
|------|---------|-------|-------|
|
|
251
|
+
| Python | `client.publish_agent_md()` | `client.fetch_agent_md(aid?)` | `client.check_agent_md(aid?, max_unsynced_days=0)` |
|
|
252
|
+
| TypeScript(Node) | `client.publishAgentMd()` | `client.fetchAgentMd(aid?)` | `client.checkAgentMd(aid?, maxUnsyncedDays=0)` |
|
|
253
|
+
| Go | `client.PublishAgentMD(ctx)` | `client.FetchAgentMD(ctx, aid)` | `client.CheckAgentMD(ctx, aid, maxUnsyncedDays...)` |
|
|
254
|
+
| C++ | `client.PublishAgentMd(out)` | `client.FetchAgentMd(aid, out)` | `client.CheckAgentMd(aid, max_days, out)` |
|
|
255
|
+
| JavaScript(浏览器) | `client.publishAgentMd(content?)` | `client.fetchAgentMd(aid?)` | `client.checkAgentMd(aid?, maxUnsyncedDays=0)` |
|
|
256
|
+
|
|
257
|
+
> **`check_agent_md` 不主动下载**:仅当远程存在且本地从未保存过该 aid 时,SDK 才异步触发后台 fetch;其他场景由应用层根据返回值决定是否调 `fetch_agent_md`。
|
|
258
|
+
|
|
259
|
+
### Deprecated(保留代码、未来版本将移除)
|
|
260
|
+
|
|
261
|
+
| 旧方法 | 推荐替代 |
|
|
262
|
+
|--------|----------|
|
|
263
|
+
| `client.auth.sign_agent_md` | `client.publish_agent_md` 内部已包含 |
|
|
264
|
+
| `client.auth.verify_agent_md` | `client.fetch_agent_md` 内部已包含 |
|
|
265
|
+
| `client.auth.upload_agent_md` | `client.publish_agent_md` |
|
|
266
|
+
| `client.auth.download_agent_md` | `client.fetch_agent_md` |
|
|
267
|
+
| `client.auth.head_agent_md` | `client.check_agent_md`(带缓存窗口) |
|
|
268
|
+
|
|
269
|
+
底层方法仅推荐用于离线签名 / 纯文本验签等特殊场景。
|
|
270
|
+
|
|
271
|
+
> v0.x 起删除了 `set_local_agent_md_path` / `get_local_agent_md_etag` / `get_remote_agent_md_etag` 三个 client 端 API;本地 etag 现在由 `publish_agent_md` / `fetch_agent_md(自身 aid)` 自动计算并缓存。事件 payload 仍会注入 `_agent_md.{local_etag, remote_etag}` 供应用层比对。
|
|
272
|
+
|
|
273
|
+
### Gateway 多 AID etag 推送(v0.x+)
|
|
274
|
+
|
|
275
|
+
Gateway 在 RPC response 和事件通知的 `_meta` 中**最多同时注入两个 AID** 的 agent.md 元数据,每个条目都带 `aid` 字段:
|
|
276
|
+
|
|
277
|
+
- **requester**:调用者 / 事件订阅方(自身)
|
|
278
|
+
- **peer**:RPC 对端 / 事件源(仅当 `peer_aid != requester_aid` 时注入;否则该条目省略)
|
|
279
|
+
|
|
280
|
+
```json
|
|
281
|
+
{
|
|
282
|
+
"_meta": {
|
|
283
|
+
"agent_md_etag": "\"requester-etag\"",
|
|
284
|
+
"agent_md_etags": {
|
|
285
|
+
"requester": {"aid": "alice.agentid.pub", "etag": "\"...\"", "last_modified": "..."},
|
|
286
|
+
"peer": {"aid": "bob.agentid.pub", "etag": "\"...\"", "last_modified": "..."}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
| 场景 | requester | peer |
|
|
293
|
+
|------|-----------|------|
|
|
294
|
+
| RPC response | 调用者 | RPC 涉及的 `to_aid`(不等于调用者时) |
|
|
295
|
+
| 事件通知 | 订阅方 | 事件源(存在且不等于订阅方时) |
|
|
296
|
+
|
|
297
|
+
`receiver` / `to` / `target` / `sender` / `from` 等键也会同时注入以兼容旧 SDK,但它们指向与 `requester` 或 `peer` 完全相同的对象(不是独立 AID)。**新 SDK 只需读 `requester` 和 `peer` 即可**。
|
|
298
|
+
|
|
299
|
+
SDK 收到后通过 `_observe_agent_md_meta` 写入 `agent_md_cache` 持久化记录(按 AID 自动去重);若发现"远程有 etag 但本地从未保存该 aid",会异步触发后台 fetch(不阻塞 RPC 返回)。
|
|
300
|
+
|
|
301
|
+
### 错误返回
|
|
302
|
+
|
|
303
|
+
- `PUT /agent.md` 可能返回 `401`(缺失或无效 token)、`403`(token 的 AID 与 Host 不匹配)、`400`(frontmatter 非法或 frontmatter.aid 与 Host 不匹配)、`413`(文档超过大小上限)
|
|
304
|
+
- `GET/HEAD /agent.md` 在目标尚未发布时返回 `404`
|
|
305
|
+
- 主 API 在上述场景抛对应异常(NotFoundError / AUNError 等)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 调用 RPC 方法
|
|
310
|
+
|
|
311
|
+
### `client.call(method, params)` — 通用 RPC 接口
|
|
312
|
+
|
|
313
|
+
认证连接后,所有业务操作通过 `client.call()` 调用服务端 RPC 方法:
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
result = await client.call(method: str, params: dict | None = None) -> Any
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**示例:**
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
# 发送消息
|
|
323
|
+
result = await client.call("message.send", {
|
|
324
|
+
"to": "bob.agentid.pub",
|
|
325
|
+
"payload": {"type": "text", "text": "Hello!"},
|
|
326
|
+
})
|
|
327
|
+
print(result) # {"message_id": "...", "seq": 123, "status": "sent"}
|
|
328
|
+
|
|
329
|
+
# 拉取消息
|
|
330
|
+
result = await client.call("message.pull", {"after_seq": 0, "limit": 20})
|
|
331
|
+
print(result) # {"messages": [...], "count": 5, "latest_seq": 128}
|
|
332
|
+
|
|
333
|
+
# 创建群组
|
|
334
|
+
result = await client.call("group.create", {
|
|
335
|
+
"name": "项目组",
|
|
336
|
+
"members": ["bob.agentid.pub", "carol.agentid.pub"],
|
|
337
|
+
})
|
|
338
|
+
print(result) # {"group_id": "...", "created_at": ...}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**错误处理:**
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
from aun_core import AUNError, NotFoundError, RateLimitError
|
|
345
|
+
|
|
346
|
+
try:
|
|
347
|
+
result = await client.call("message.send", {...})
|
|
348
|
+
except NotFoundError as e:
|
|
349
|
+
print(f"目标不存在: {e.code}")
|
|
350
|
+
except RateLimitError as e:
|
|
351
|
+
print(f"请求限流,{e.data['retry_after']}秒后重试")
|
|
352
|
+
except AUNError as e:
|
|
353
|
+
print(f"RPC 错误: code={e.code}, retryable={e.retryable}")
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**RPC 方法完整参数和响应格式见 RPC 手册:**
|
|
357
|
+
|
|
358
|
+
| 领域 | 手册 | 涵盖方法 |
|
|
359
|
+
|------|------|----------|
|
|
360
|
+
| 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | message.send / pull / ack / recall / thought.put / thought.get |
|
|
361
|
+
| 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | 群组生命周期、成员管理、群设置、群消息、群 thought |
|
|
362
|
+
| 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | 文件上传下载、对象存储 |
|
|
363
|
+
| 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | meta.ping / status / trust_roots |
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## 事件订阅
|
|
368
|
+
|
|
369
|
+
### `client.on(event, handler)` — 订阅事件
|
|
370
|
+
|
|
371
|
+
**`on()` 应在 `connect()` 之前调用**,否则连接建立瞬间触发的事件(如 `connection.state`)会丢失。
|
|
372
|
+
|
|
373
|
+
服务端推送的事件通过 `client.on()` 订阅,支持同步和异步 handler:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
subscription = client.on(event: str, handler: Callable) -> Subscription
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**同步 handler:**
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
def on_message(event):
|
|
383
|
+
print(f"收到消息: {event['payload']}")
|
|
384
|
+
|
|
385
|
+
sub = client.on("message.received", on_message)
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**异步 handler:**
|
|
389
|
+
|
|
390
|
+
```python
|
|
391
|
+
async def on_message(event):
|
|
392
|
+
await process_message(event)
|
|
393
|
+
await client.call("message.ack", {"seq": event["seq"]})
|
|
394
|
+
|
|
395
|
+
sub = client.on("message.received", on_message)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**只触发一次(手动取消订阅):**
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
def on_state_change(e):
|
|
402
|
+
print(f"状态变更: {e}")
|
|
403
|
+
sub.unsubscribe() # 触发后立即取消
|
|
404
|
+
|
|
405
|
+
sub = client.on("connection.state", on_state_change)
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**取消订阅:**
|
|
409
|
+
|
|
410
|
+
```python
|
|
411
|
+
sub = client.on("message.received", handler)
|
|
412
|
+
# ... 稍后
|
|
413
|
+
sub.unsubscribe()
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
**多个 handler:**
|
|
417
|
+
|
|
418
|
+
```python
|
|
419
|
+
# 同一事件可注册多个 handler,按注册顺序依次调用
|
|
420
|
+
client.on("message.received", log_message)
|
|
421
|
+
client.on("message.received", update_ui)
|
|
422
|
+
client.on("message.received", send_notification)
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
**常用事件示例:**
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
# 连接状态变化
|
|
429
|
+
client.on("connection.state", lambda e: print(f"状态: {e['state']}"))
|
|
430
|
+
|
|
431
|
+
# 消息推送
|
|
432
|
+
client.on("message.received", handle_new_message)
|
|
433
|
+
|
|
434
|
+
# 群组变更
|
|
435
|
+
client.on("group.changed", lambda e: print(f"群组 {e['group_id']} 已更新"))
|
|
436
|
+
|
|
437
|
+
# 令牌刷新
|
|
438
|
+
client.on("token.refreshed", lambda e: print(f"令牌已刷新: {e['aid']}"))
|
|
439
|
+
|
|
440
|
+
# 连接错误
|
|
441
|
+
client.on("connection.error", lambda e: print(f"连接错误: {e}"))
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
内置事件完整列表见 [06-API手册.md](06-API手册.md) 的「内置事件」节。
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 关闭连接
|
|
449
|
+
|
|
450
|
+
```python
|
|
451
|
+
await client.close()
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
关闭后状态变为 `"closed"`,不可复用,需重新创建 `AUNClient`。
|