@agentunion/fastaun-browser 0.4.9 → 0.4.10
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 +16 -0
- package/_packed_docs/CHANGELOG.md +16 -0
- package/_packed_docs/INDEX.md +31 -14
- package/_packed_docs/KITE_DOCS_GUIDE.md +20 -14
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +244 -16
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +113 -27
- package/_packed_docs/sdk/09-group-rpc-manual.md +97 -0
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +231 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +117 -4
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +15 -11
- package/_packed_docs/sdk/INDEX.md +14 -8
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +214 -0
- package/_packed_docs/sdk/README.md +8 -6
- package/dist/bundle.js +1447 -6
- package/dist/client/delivery.d.ts +4 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +173 -0
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.js +1 -1
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +7 -0
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +21 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +95 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/service-proxy.d.ts +219 -0
- package/dist/service-proxy.d.ts.map +1 -0
- package/dist/service-proxy.js +1321 -0
- package/dist/service-proxy.js.map +1 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +34 -0
- package/dist/transport.js.map +1 -1
- 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 +1 -1
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
|
|
7
7
|
- [AIDStore](#aidstore)
|
|
8
8
|
- [AID](#aid)
|
|
9
|
-
- [AUNClient](#aunclient)
|
|
10
|
-
- [事件](#事件)
|
|
11
|
-
- [
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
9
|
+
- [AUNClient](#aunclient)
|
|
10
|
+
- [事件](#事件)
|
|
11
|
+
- [ServiceProxyClient](#serviceproxyclient)
|
|
12
|
+
- [E2EE 高级 API](#e2ee-高级-api)
|
|
13
|
+
- [RPC 方法参考](#rpc-方法参考)
|
|
14
|
+
- [Stream 使用指南](#stream-使用指南)
|
|
14
15
|
|
|
15
16
|
> **多语言命名约定**:Python 使用 `snake_case`(如 `aun_path`、`download_agent_md`),TS/JS 使用 `camelCase`(如 `aunPath`、`downloadAgentMd`),Go 使用 `PascalCase` 公开方法(如 `Load`、`Register`)。本手册表格中各列对应各语言的实际命名。
|
|
16
17
|
|
|
@@ -238,16 +239,51 @@ result = await client.call("message.send", {
|
|
|
238
239
|
})
|
|
239
240
|
```
|
|
240
241
|
|
|
241
|
-
常用 meta RPC 直接透传:
|
|
242
|
-
|
|
243
|
-
```python
|
|
244
|
-
await client.call("meta.ping", {})
|
|
245
|
-
await client.call("meta.status", {})
|
|
246
|
-
await client.call("meta.trust_roots", {})
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
###
|
|
250
|
-
|
|
242
|
+
常用 meta RPC 直接透传:
|
|
243
|
+
|
|
244
|
+
```python
|
|
245
|
+
await client.call("meta.ping", {})
|
|
246
|
+
await client.call("meta.status", {})
|
|
247
|
+
await client.call("meta.trust_roots", {})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Notify
|
|
251
|
+
|
|
252
|
+
`notify()` 发送轻量在线通知,底层是 JSON-RPC Notification,无 `id`,不进入离线存储、seq、pull 或 ack。
|
|
253
|
+
|
|
254
|
+
| Python | TS/JS | Go | 说明 |
|
|
255
|
+
|--------|-------|----|------|
|
|
256
|
+
| `notify(method, params=None, *, to=None, group_id=None, device_id=None, slot_id=None, ttl_ms=None)` | `notify(method, params?, options?)` | `Notify(ctx, method, params, NotifyOptions{...})` | 发送在线轻量通知 |
|
|
257
|
+
|
|
258
|
+
常见用法:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
await client.notify("notification/client.activity", {"state": "idle"})
|
|
262
|
+
await client.notify("event/app.typing", {"thread_id": "t1"}, to="bob.agentid.pub", ttl_ms=5000)
|
|
263
|
+
await client.notify("event/app.presence", {"state": "active"}, group_id="group.agentid.pub/123")
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
路由选项:
|
|
267
|
+
|
|
268
|
+
| 选项 | 说明 |
|
|
269
|
+
|------|------|
|
|
270
|
+
| `to` / `To` | 目标 AID;可同域或跨域 |
|
|
271
|
+
| `group_id` / `groupId` / `GroupID` | 目标群;与 `to` 互斥 |
|
|
272
|
+
| `device_id` / `deviceId` / `DeviceID` | 限定目标 AID 的在线设备;必须配合 `to` |
|
|
273
|
+
| `slot_id` / `slotId` / `SlotID` | 限定目标设备的在线 slot;必须配合 `device_id` |
|
|
274
|
+
| `ttl_ms` / `ttlMs` / `TTLMS` | `0..60000`,只控制在线投递过期,不表示离线缓存 |
|
|
275
|
+
|
|
276
|
+
约束:
|
|
277
|
+
|
|
278
|
+
- 未指定 `to` / `group_id` 时,`method` 必须以 `notification/` 开头,表示直发 Gateway 的协议级通知。
|
|
279
|
+
- 指定 `to` 或 `group_id` 时,`method` 必须是 `event/app.*`,接收端通过 `client.on("app.xxx", handler)` 订阅。
|
|
280
|
+
- 跨域 AID notify 已支持 federation 在线转发,但仍是 best-effort;目标离线或 federation 不可用时丢弃。
|
|
281
|
+
- 可靠、敏感或需要审计的业务事件应继续使用 `message.send` / `group.send`。
|
|
282
|
+
|
|
283
|
+
详细语义见 [Notify通知方案.md](Notify通知方案.md)。
|
|
284
|
+
|
|
285
|
+
### protected_headers
|
|
286
|
+
|
|
251
287
|
```python
|
|
252
288
|
client = AUNClient(aid)
|
|
253
289
|
client.set_protected_headers({"sdk": "python", "trace": "abc"})
|
|
@@ -275,13 +311,62 @@ headers = client.get_protected_headers()
|
|
|
275
311
|
- 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
|
|
276
312
|
- SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
|
|
277
313
|
- `Accept: text/markdown` 与 agent.md 的 YAML frontmatter + Markdown 格式兼容;agent.md 仍是 Markdown 媒体类型上的结构化约定。
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
##
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## ServiceProxyClient
|
|
318
|
+
|
|
319
|
+
Service Proxy 用于 provider 通过 AUN 身份暴露本地 HTTP / WebSocket 服务。当前公开封装在 Python SDK 的 `ServiceProxyClient` 中;其它语言可以按 [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) 直接实现同等控制面和隧道消息。
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
from aun_core.service_proxy import ServiceProxyClient
|
|
323
|
+
|
|
324
|
+
proxy_client = ServiceProxyClient(
|
|
325
|
+
provider_aid="alice.agentid.pub",
|
|
326
|
+
aun_client=client,
|
|
327
|
+
)
|
|
328
|
+
proxy_client.register_service(
|
|
329
|
+
"fileshare",
|
|
330
|
+
"http://127.0.0.1:8080",
|
|
331
|
+
visibility="public",
|
|
332
|
+
)
|
|
333
|
+
await proxy_client.serve_forever()
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
proxy-server 连接地址不能由应用外部传入或配置。`ServiceProxyClient` 会先读取 provider AID 本地 SQLite metadata 中 1 小时 TTL 的 `service_proxy_discovery` 缓存;缓存缺失或过期时,按协议查询 `https://{provider_aid}/.well-known/aun-proxy`,失败后回退 `https://proxy.{issuer}/.well-known/aun-proxy`,并使用返回的 `ws_url` 建立隧道。
|
|
337
|
+
|
|
338
|
+
关键 API:
|
|
339
|
+
|
|
340
|
+
| Python | 说明 |
|
|
341
|
+
|--------|------|
|
|
342
|
+
| `register_service(service_name, endpoint, service_type="http", visibility="private", metadata=None)` | 注册本地 embedded endpoint |
|
|
343
|
+
| `unregister_service(service_name)` | 注销本地服务 |
|
|
344
|
+
| `list_service_summaries()` | 获取可上报到 Gateway 和 proxy-server 的服务摘要 |
|
|
345
|
+
| `register_services_with_gateway()` | 显式调用 Gateway `proxy.register_services` |
|
|
346
|
+
| `unregister_services_from_gateway(service_names=None)` | 显式调用 Gateway `proxy.unregister_services` |
|
|
347
|
+
| `list_gateway_services()` | 显式调用 Gateway `proxy.list_services` |
|
|
348
|
+
| `register_services_with_proxy_server(ws)` | 通过已认证 proxy-server 隧道发送 `register_services` |
|
|
349
|
+
| `discover_proxy_server(force_refresh=False)` | 通过缓存 / well-known 发现 proxy-server |
|
|
350
|
+
| `connect_once()` | 建立一次 proxy-server 隧道并完成认证、数据面注册和可选心跳 |
|
|
351
|
+
| `serve_once()` | 处理有限数量的 proxy-server 转发请求 |
|
|
352
|
+
| `serve_forever(connection_mode="persistent")` | 持续提供 Service Proxy 服务;支持 persistent / on_demand |
|
|
353
|
+
|
|
354
|
+
自动注册顺序:
|
|
355
|
+
|
|
356
|
+
- `connect_once()`、`serve_once()`、`serve_forever()` 在存在 `aun_client.call()` 时,会先向 Gateway 调用 `proxy.register_services`。
|
|
357
|
+
- 建立 proxy-server 隧道前,SDK 必须通过缓存 / `/.well-known/aun-proxy` 发现得到 `ws_url`;不得由应用传入或配置 proxy-server 地址。
|
|
358
|
+
- proxy-server 隧道使用 `Authorization: Bearer <access_token>` 鉴权;SDK 优先复用 cached token,缺失或过期时通过 `aun_client.authenticate()` 向 Gateway 完成登录刷新。
|
|
359
|
+
- 每次 proxy-server 隧道认证成功后,都会立即向 proxy-server 发送 `register_services` 隧道消息。
|
|
360
|
+
- 服务列表与连接绑定;断开 Gateway 长连接或 proxy-server 隧道后,相应注册立即失效。
|
|
361
|
+
|
|
362
|
+
详细控制面 RPC、隧道消息和路由语义见 [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md)。
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 事件
|
|
367
|
+
|
|
368
|
+
```python
|
|
369
|
+
sub = client.on("message.received", handler)
|
|
285
370
|
client.off("message.received", handler)
|
|
286
371
|
sub.unsubscribe()
|
|
287
372
|
```
|
|
@@ -325,11 +410,12 @@ sub.unsubscribe()
|
|
|
325
410
|
|------|------|----------|
|
|
326
411
|
| 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | `message.send` / `message.pull` / `message.ack` / `message.thought.*` |
|
|
327
412
|
| 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.invite` / `group.send` / `group.v2.*` |
|
|
328
|
-
| 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.upload` / `storage.download` / `storage.share` |
|
|
329
|
-
| 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | `meta.ping` / `meta.status` / `meta.trust_roots` |
|
|
330
|
-
| Stream | [09-stream-rpc-manual.md](09-stream-rpc-manual.md) | `stream.create` / `stream.close` / `stream.list_active` |
|
|
331
|
-
|
|
332
|
-
|
|
413
|
+
| 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.upload` / `storage.download` / `storage.share` |
|
|
414
|
+
| 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | `meta.ping` / `meta.status` / `meta.trust_roots` |
|
|
415
|
+
| Stream | [09-stream-rpc-manual.md](09-stream-rpc-manual.md) | `stream.create` / `stream.close` / `stream.list_active` |
|
|
416
|
+
| Service Proxy | [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) | `proxy.register_services` / `proxy.unregister_services` / `proxy.list_services` |
|
|
417
|
+
|
|
418
|
+
---
|
|
333
419
|
|
|
334
420
|
## Stream 使用指南
|
|
335
421
|
|
|
@@ -67,6 +67,7 @@
|
|
|
67
67
|
| 方法 | 说明 |
|
|
68
68
|
|------|------|
|
|
69
69
|
| [group.send](#groupsend) | 发送群消息 |
|
|
70
|
+
| [group.recall](#grouprecall) | 撤回群消息 |
|
|
70
71
|
| [group.thought.put](#groupthoughtput) | 写入某个群上下文的思考内容 |
|
|
71
72
|
| [group.thought.get](#groupthoughtget) | 获取某个群上下文的思考内容 |
|
|
72
73
|
| [group.pull](#grouppull) | 增量拉取消息 |
|
|
@@ -1173,6 +1174,59 @@ result = await client.call("group.thought.get", {
|
|
|
1173
1174
|
{"cursor": 42}
|
|
1174
1175
|
```
|
|
1175
1176
|
|
|
1177
|
+
### group.recall
|
|
1178
|
+
|
|
1179
|
+
撤回群消息。仅**原发送者**可撤回自己的消息,受时间窗口限制(默认 **120 秒**,由群服务配置 `recall_window_seconds` 控制,`0` 表示不限制)。管理员撤回(`group_admin_recall_enabled`)默认关闭,本期不实现。
|
|
1180
|
+
|
|
1181
|
+
**双 tombstone 机制**:群消息使用 per-group 全局 `message_seq`(V1/V2 共享同一空间),直接删除原消息会让"还没拉到原消息"的客户端遇到永久 seq 空洞。因此撤回写入两条 tombstone:
|
|
1182
|
+
|
|
1183
|
+
- **原 seq 占位 tombstone**:占住被撤消息原来的 seq。V1 把原 `group_messages` 行 `message_type` 改为 `group.message_recalled` 并清空正文;V2 删除 `v2_group_messages` 密文体与所有 `v2_group_wraps`,再在 `group_messages` 插入同 `original_seq` 的明文 tombstone 顶替。服务对象是**还没读到原消息**的客户端——拉到该 seq 看到 tombstone,而非空洞。
|
|
1184
|
+
- **新 seq 通知 tombstone**:分配一个新的 `message_seq`,通知**已经读过原消息**(游标已越过 `original_seq`)的客户端"这条消息被撤回了"。
|
|
1185
|
+
|
|
1186
|
+
同时写一条 `group_events`(`event_type = group.message_recalled`)用于事件流审计,并在事务提交后推送 `event/group.message_recalled`(见下)。撤回真相记录在 `group_message_recalls` 表,`(group_id, original_message_id)` 与 `(group_id, original_seq)` 双唯一键防止重复撤回。
|
|
1187
|
+
|
|
1188
|
+
**参数**:
|
|
1189
|
+
|
|
1190
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
1191
|
+
|------|------|------|------|
|
|
1192
|
+
| `group_id` | string | 是 | 群组 ID |
|
|
1193
|
+
| `message_ids` | string[] | 是 | 待撤回消息 ID 列表,最多 100 个(`recall_max_batch`)|
|
|
1194
|
+
| `reason` | string | 否 | 可选撤回理由,建议短文本(最长 255 字符)|
|
|
1195
|
+
|
|
1196
|
+
**响应**:
|
|
1197
|
+
|
|
1198
|
+
```json
|
|
1199
|
+
{
|
|
1200
|
+
"success": true,
|
|
1201
|
+
"accepted": ["gm-aaa", "gm-bbb"],
|
|
1202
|
+
"recalled": ["gm-aaa"],
|
|
1203
|
+
"errors": [
|
|
1204
|
+
{"message_id": "gm-bbb", "error": "not_sender"}
|
|
1205
|
+
]
|
|
1206
|
+
}
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
| 字段 | 类型 | 说明 |
|
|
1210
|
+
|------|------|------|
|
|
1211
|
+
| `success` | boolean | 整体是否受理 |
|
|
1212
|
+
| `accepted` | string[] | 通过时间窗口前置过滤、进入撤回事务的消息 ID |
|
|
1213
|
+
| `recalled` | string[] | DB 真实撤回成功的消息 ID(避免并发撤回误通知)|
|
|
1214
|
+
| `errors` | array | 逐条错误:`{message_id, error}` |
|
|
1215
|
+
|
|
1216
|
+
**逐条错误码**:
|
|
1217
|
+
|
|
1218
|
+
| error | 说明 |
|
|
1219
|
+
|-------|------|
|
|
1220
|
+
| `not_found` | 消息不存在 |
|
|
1221
|
+
| `not_sender` | 操作者不是原发送者 |
|
|
1222
|
+
| `already_recalled` | 消息已被撤回(命中唯一键 / 占位 tombstone 已存在)|
|
|
1223
|
+
| `expired` | 超过撤回时间窗口 |
|
|
1224
|
+
| `group_inactive` | 群组非 active 状态(suspended / dissolved)|
|
|
1225
|
+
|
|
1226
|
+
**SDK 行为**:SDK 把 pull / push 收到的撤回 tombstone(占位与通知)归一化为 `group.message_recalled` 应用事件,**不**作为普通 `group.message_created` 交付;tombstone 仍占 seq,正常推进 SeqTracker 与 ack。SDK 按 `(group_id, message_ids)` 去重,因此即使同时收到在线 push 事件、占位 tombstone、通知 tombstone,应用层也**只回调一次**。去重键**不含 `recalled_at`**:占位 tombstone、通知 tombstone 与在线 push 三条通道对同一次撤回可能携带不同来源的时间戳(push 在事务提交后重取),若纳入 `recalled_at` 会使去重失效、导致重复回调;一条消息只能被撤回一次(服务端 `group_message_recalls` 唯一键保证),`(group_id, message_ids)` 已能唯一标识一次撤回。
|
|
1227
|
+
|
|
1228
|
+
> 所有语言 SDK 统一通过 `client.call("group.recall", {...})` 调用,不提供独立的便捷方法名。
|
|
1229
|
+
|
|
1176
1230
|
---
|
|
1177
1231
|
|
|
1178
1232
|
## 公告与规则
|
|
@@ -1874,6 +1928,49 @@ SDK 收到后自动解密 `payload`,解密后的明文消息直接交付用户
|
|
|
1874
1928
|
|
|
1875
1929
|
SDK 收到后自动调用 `group.pull` 拉取最新消息并逐条解密后交付用户回调。
|
|
1876
1930
|
|
|
1931
|
+
### event/group.message_recalled
|
|
1932
|
+
|
|
1933
|
+
群消息撤回后推送给所有在线成员(与 pull 双 tombstone 兜底互补)。在线 push 是实时通道,双 tombstone 是离线 / 未读 / push 丢失时的可靠性兜底;两者最终一致,SDK 去重保证应用层只感知一次。
|
|
1934
|
+
|
|
1935
|
+
**Payload**:
|
|
1936
|
+
|
|
1937
|
+
```json
|
|
1938
|
+
{
|
|
1939
|
+
"module_id": "group",
|
|
1940
|
+
"group_id": "g-abc123.agentid.pub",
|
|
1941
|
+
"seq": 43,
|
|
1942
|
+
"message_id": "grm-uuid",
|
|
1943
|
+
"message_ids": ["gm-aaa"],
|
|
1944
|
+
"target_message_seqs": [42],
|
|
1945
|
+
"sender_aid": "alice.agentid.pub",
|
|
1946
|
+
"recalled_by": "alice.agentid.pub",
|
|
1947
|
+
"recalled_at": 1234567890000,
|
|
1948
|
+
"reason": "",
|
|
1949
|
+
"member_aids": ["bob.agentid.pub"]
|
|
1950
|
+
}
|
|
1951
|
+
```
|
|
1952
|
+
|
|
1953
|
+
| 字段 | 类型 | 说明 |
|
|
1954
|
+
|------|------|------|
|
|
1955
|
+
| `module_id` | string | 固定 `"group"` |
|
|
1956
|
+
| `group_id` | string | 群组 ID |
|
|
1957
|
+
| `seq` | integer | 撤回通知 tombstone 的**新群消息 seq** |
|
|
1958
|
+
| `message_id` | string | 撤回通知 tombstone 自己的 message_id |
|
|
1959
|
+
| `message_ids` | string[] | 被撤回的**原消息 ID 列表** |
|
|
1960
|
+
| `target_message_seqs` | integer[] | 被撤回的原消息 seq 列表 |
|
|
1961
|
+
| `sender_aid` | string | 原消息发送方 |
|
|
1962
|
+
| `recalled_by` | string | 撤回操作者 |
|
|
1963
|
+
| `recalled_at` | integer | 撤回时间戳(毫秒)|
|
|
1964
|
+
| `reason` | string | 可选撤回理由 |
|
|
1965
|
+
|
|
1966
|
+
跨域成员通过 federation forward 转发(`group.message_recalled` 在跨域转发白名单内),路径与 `group.message_created` 一致。
|
|
1967
|
+
|
|
1968
|
+
**订阅**:
|
|
1969
|
+
|
|
1970
|
+
```python
|
|
1971
|
+
client.on("group.message_recalled", lambda ev: print("recalled:", ev["message_ids"]))
|
|
1972
|
+
```
|
|
1973
|
+
|
|
1877
1974
|
---
|
|
1878
1975
|
|
|
1879
1976
|
## 错误码
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Service Proxy — RPC Manual
|
|
2
|
+
|
|
3
|
+
## 方法索引
|
|
4
|
+
|
|
5
|
+
### Gateway 控制面方法
|
|
6
|
+
|
|
7
|
+
| 方法 | 说明 |
|
|
8
|
+
|------|------|
|
|
9
|
+
| [proxy.register_services](#proxyregister_services) | 注册当前 Gateway 长连接可提供的 Service Proxy 服务列表 |
|
|
10
|
+
| [proxy.unregister_services](#proxyunregister_services) | 注销当前 Gateway 长连接上的部分或全部服务列表 |
|
|
11
|
+
| [proxy.list_services](#proxylist_services) | 查询当前 Gateway 长连接已注册的服务列表 |
|
|
12
|
+
|
|
13
|
+
### proxy-server 数据面隧道消息
|
|
14
|
+
|
|
15
|
+
| 消息 | 方向 | 说明 |
|
|
16
|
+
|------|------|------|
|
|
17
|
+
| [register_services](#register_services-隧道消息) | proxy-client → proxy-server | proxy-server 隧道认证后注册本连接的数据面服务列表 |
|
|
18
|
+
| `register_services_ack` | proxy-server → proxy-client | 数据面服务注册成功确认 |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 控制面与数据面
|
|
23
|
+
|
|
24
|
+
Service Proxy 有两层注册,二者都必须存在:
|
|
25
|
+
|
|
26
|
+
- **Gateway 控制面**:provider 的 AUN 长连接调用 `proxy.register_services`,Gateway 记录该 provider 在线且声明了哪些服务。proxy-server 用它判断 provider 是否在线、目标服务是否声明、是否应该 wakeup。
|
|
27
|
+
- **proxy-server 数据面**:proxy-client 连接 proxy-server 的 `/ws/client` 并完成认证后,必须再发送 `register_services` 隧道消息。proxy-server 只会向本地已注册目标服务的数据面连接转发请求。
|
|
28
|
+
|
|
29
|
+
服务列表与连接绑定。Gateway 长连接断开后,Gateway 上的服务列表立即失效;proxy-server 隧道断开后,proxy-server 上的服务列表立即失效。
|
|
30
|
+
|
|
31
|
+
同一个 provider AID 可以有多个实例连接,但所有实例注册的服务摘要必须一致。若同一 provider AID 的第二条连接注册了不同服务列表,服务端应拒绝该次注册并返回 `proxy_services_inconsistent`。
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 服务摘要
|
|
36
|
+
|
|
37
|
+
服务摘要只描述可公开发现的服务能力,不包含本地 endpoint。
|
|
38
|
+
|
|
39
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
40
|
+
|------|------|------|------|
|
|
41
|
+
| `service_name` | string | 是 | 服务名,建议使用 `[a-z0-9_-]+` |
|
|
42
|
+
| `service_type` | string | 否 | 服务类型,默认 `http`;常见值为 `http` / `websocket` / `sse` / `mcp` |
|
|
43
|
+
| `visibility` | string | 否 | 可见性,默认 `private` |
|
|
44
|
+
| `metadata` | object | 否 | 非敏感描述信息。服务端会移除 token、secret、endpoint、url、cookie、key、cert 等敏感字段 |
|
|
45
|
+
|
|
46
|
+
Python `ServiceProxyClient.register_service()` 会保存本地 endpoint,但 `list_service_summaries()`、Gateway 注册和 proxy-server 注册只发送摘要。
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## proxy.register_services
|
|
51
|
+
|
|
52
|
+
注册当前 Gateway 长连接提供的 Service Proxy 服务列表。服务端必须以连接认证得到的 AID 作为 provider AID,不能信任客户端传入的 `provider_aid` 覆盖认证身份。
|
|
53
|
+
|
|
54
|
+
### 参数
|
|
55
|
+
|
|
56
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
57
|
+
|------|------|------|------|
|
|
58
|
+
| `provider_aid` | string | 否 | 兼容字段或诊断字段;服务端以认证身份为准 |
|
|
59
|
+
| `services` | array | 是 | 服务摘要列表 |
|
|
60
|
+
|
|
61
|
+
### 响应
|
|
62
|
+
|
|
63
|
+
| 字段 | 类型 | 说明 |
|
|
64
|
+
|------|------|------|
|
|
65
|
+
| `ok` | boolean | 固定为 `true` |
|
|
66
|
+
| `provider_aid` | string | 认证后的 provider AID |
|
|
67
|
+
| `connection_id` | string | Gateway 连接 ID |
|
|
68
|
+
| `count` | integer | 已注册服务数量 |
|
|
69
|
+
| `services` | array | 服务端清洗后的服务摘要列表 |
|
|
70
|
+
|
|
71
|
+
### 示例
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
result = await client.call("proxy.register_services", {
|
|
75
|
+
"services": [
|
|
76
|
+
{
|
|
77
|
+
"service_name": "fileshare",
|
|
78
|
+
"service_type": "http",
|
|
79
|
+
"visibility": "public",
|
|
80
|
+
"metadata": {"label": "Files"},
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 错误
|
|
87
|
+
|
|
88
|
+
| JSON-RPC code | message | 原因 |
|
|
89
|
+
|---------------|---------|------|
|
|
90
|
+
| -32602 | `proxy service registration requires a long connection` | 短连接不能注册 Service Proxy 服务 |
|
|
91
|
+
| -32020 | `proxy_services_inconsistent` | 同一 provider AID 的已有连接注册了不一致的服务列表 |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## proxy.unregister_services
|
|
96
|
+
|
|
97
|
+
注销当前 Gateway 长连接上的部分或全部服务。
|
|
98
|
+
|
|
99
|
+
### 参数
|
|
100
|
+
|
|
101
|
+
| 参数 | 类型 | 必填 | 说明 |
|
|
102
|
+
|------|------|------|------|
|
|
103
|
+
| `service_names` | array | 否 | 要注销的服务名列表;省略时注销当前连接上的全部服务 |
|
|
104
|
+
| `service_name` | string | 否 | 单服务兼容字段 |
|
|
105
|
+
|
|
106
|
+
### 响应
|
|
107
|
+
|
|
108
|
+
| 字段 | 类型 | 说明 |
|
|
109
|
+
|------|------|------|
|
|
110
|
+
| `ok` | boolean | 固定为 `true` |
|
|
111
|
+
| `provider_aid` | string | 认证后的 provider AID |
|
|
112
|
+
| `connection_id` | string | Gateway 连接 ID |
|
|
113
|
+
| `removed` | array | 实际移除的服务名 |
|
|
114
|
+
| `count` | integer | 剩余服务数量;全部注销时可省略 |
|
|
115
|
+
|
|
116
|
+
### 示例
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
await client.call("proxy.unregister_services", {
|
|
120
|
+
"service_names": ["fileshare"],
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## proxy.list_services
|
|
127
|
+
|
|
128
|
+
查询当前 Gateway 长连接已注册的 Service Proxy 服务列表。
|
|
129
|
+
|
|
130
|
+
### 参数
|
|
131
|
+
|
|
132
|
+
无有效参数。`provider_aid` 若存在,也只能作为兼容字段;服务端以当前连接身份为准。
|
|
133
|
+
|
|
134
|
+
### 响应
|
|
135
|
+
|
|
136
|
+
| 字段 | 类型 | 说明 |
|
|
137
|
+
|------|------|------|
|
|
138
|
+
| `provider_aid` | string | 当前连接认证后的 AID |
|
|
139
|
+
| `connection_id` | string | Gateway 连接 ID |
|
|
140
|
+
| `services` | array | 当前连接已注册的服务摘要列表 |
|
|
141
|
+
|
|
142
|
+
### 示例
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
current = await client.call("proxy.list_services", {})
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## register_services 隧道消息
|
|
151
|
+
|
|
152
|
+
proxy-client 连接 proxy-server `/ws/client` 并收到 `service_proxy_auth_response.ok=true` 后,必须立即发送 `register_services` 隧道消息。proxy-server 只根据这个数据面注册表选择实际转发连接。
|
|
153
|
+
|
|
154
|
+
### 请求
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"type": "register_services",
|
|
159
|
+
"request_id": "register-services",
|
|
160
|
+
"services": [
|
|
161
|
+
{
|
|
162
|
+
"service_name": "fileshare",
|
|
163
|
+
"service_type": "http",
|
|
164
|
+
"visibility": "public",
|
|
165
|
+
"metadata": {"label": "Files"}
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 成功响应
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{"type": "register_services_ack", "request_id": "register-services", "ok": true, "count": 1}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 失败响应
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"type": "service_proxy_error",
|
|
182
|
+
"request_id": "register-services",
|
|
183
|
+
"error": {
|
|
184
|
+
"code": "proxy_services_inconsistent",
|
|
185
|
+
"message": "provider service list is inconsistent with existing connections"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Python ServiceProxyClient
|
|
193
|
+
|
|
194
|
+
Python SDK 的 `ServiceProxyClient` 已封装双注册流程:
|
|
195
|
+
|
|
196
|
+
1. `connect_once()` / `serve_once()` / `serve_forever()` 建立 proxy-server 隧道前,会在存在 `aun_client.call()` 时自动调用 `proxy.register_services`。
|
|
197
|
+
2. proxy-server 连接地址必须通过 `/.well-known/aun-proxy` 发现:先读 provider AID SQLite metadata 中 1 小时 TTL 的 `service_proxy_discovery` 缓存;缓存缺失或过期时查询 `https://{provider_aid}/.well-known/aun-proxy`,失败后回退 `https://proxy.{issuer}/.well-known/aun-proxy`。应用不得传入、配置或硬拼 proxy-server URL。
|
|
198
|
+
3. proxy-server 隧道使用 AUN auth token 鉴权:优先复用 cached access token;缓存缺失或过期时,必须通过 `aun_client.authenticate()` 经 Gateway 两步登录刷新 token。
|
|
199
|
+
4. proxy-server 隧道认证成功后,会自动发送 `register_services` 隧道消息。
|
|
200
|
+
5. persistent 和 on-demand 重连时,每条新连接都会重新执行以上流程。
|
|
201
|
+
|
|
202
|
+
常用入口:
|
|
203
|
+
|
|
204
|
+
| 方法 | 说明 |
|
|
205
|
+
|------|------|
|
|
206
|
+
| `register_service(name, endpoint, service_type="http", visibility="private", metadata=None)` | 注册本地 embedded endpoint |
|
|
207
|
+
| `unregister_service(name)` | 移除本地 endpoint |
|
|
208
|
+
| `list_service_summaries()` | 返回可上报的服务摘要 |
|
|
209
|
+
| `register_services_with_gateway()` | 显式向 Gateway 注册控制面服务列表 |
|
|
210
|
+
| `unregister_services_from_gateway(names=None)` | 显式从 Gateway 注销控制面服务 |
|
|
211
|
+
| `list_gateway_services()` | 查询 Gateway 当前连接服务列表 |
|
|
212
|
+
| `register_services_with_proxy_server(ws)` | 通过已认证 proxy-server 隧道注册数据面服务列表 |
|
|
213
|
+
| `discover_proxy_server(force_refresh=False)` | 通过缓存 / well-known 发现 proxy-server |
|
|
214
|
+
| `serve_once()` / `serve_forever()` | 连接 proxy-server 并处理转发请求 |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 路由与 wakeup 语义
|
|
219
|
+
|
|
220
|
+
proxy-server 收到 `https://proxy.{issuer}/{user_name}/{svc_name}` 或对应 WebSocket 请求时,应按以下顺序处理:
|
|
221
|
+
|
|
222
|
+
1. 本 proxy-server 已有 `(provider_aid, service_name)` 数据面连接:直接转发,并按 visitor/provider/service 维持稳定粘性。
|
|
223
|
+
2. provider 已连接本 proxy-server 且已注册服务列表,但没有目标服务:返回 `service_not_registered`,不 wakeup。
|
|
224
|
+
3. provider 未连接本 proxy-server:查询 Gateway 控制面。
|
|
225
|
+
4. Gateway 检查 provider AID 证书不存在:返回 `provider_aid_not_found`;证书查询失败:返回 `provider_aid_check_failed`。
|
|
226
|
+
5. Gateway 显示 provider 没有在线长连接:返回 `provider_offline`。
|
|
227
|
+
6. Gateway 显示 provider 在线但未声明目标服务:返回 `service_not_registered`,不 wakeup。
|
|
228
|
+
7. Gateway 显示 provider 在线且声明目标服务:仅向注册了该服务的 provider 连接发送 wakeup。
|
|
229
|
+
8. wakeup 已投递但本 proxy-server 等不到目标数据面隧道注册时,返回 `provider_wakeup_timeout`。
|
|
230
|
+
|
|
231
|
+
因此,只向 Gateway 注册不足以承载请求;只有 proxy-server 数据面也注册了目标服务,访问才会真正转发。
|