@agentunion/fastaun 0.5.31 → 0.5.33

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 (91) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/_packed_docs/CHANGELOG.md +69 -1
  3. package/_packed_docs/sdk/0.5.32-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +350 -0
  4. package/_packed_docs/sdk/0.5.32-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +319 -0
  5. package/_packed_docs/sdk/0.5.33-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +316 -0
  6. package/_packed_docs/sdk/0.5.33-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +304 -0
  7. package/_packed_docs/sdk/09-storage-rpc-manual.md +33 -16
  8. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +12 -5
  9. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +459 -0
  10. package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +554 -0
  11. package/_packed_docs/sdk/INDEX.md +27 -5
  12. package/_packed_docs/sdk//345/255/220AID/344/277/256/346/224/271agent.md/351/200/232/347/237/245Owner/345/234/272/346/231/257/346/214/207/345/215/227.md +234 -0
  13. package/dist/agent-md.d.ts +2 -1
  14. package/dist/agent-md.js +17 -0
  15. package/dist/agent-md.js.map +1 -1
  16. package/dist/client/control-messages.d.ts +22 -6
  17. package/dist/client/control-messages.js +210 -148
  18. package/dist/client/control-messages.js.map +1 -1
  19. package/dist/client/delivery.d.ts +3 -0
  20. package/dist/client/delivery.js +67 -3
  21. package/dist/client/delivery.js.map +1 -1
  22. package/dist/client/group-state.d.ts +3 -0
  23. package/dist/client/group-state.js +48 -14
  24. package/dist/client/group-state.js.map +1 -1
  25. package/dist/client/lifecycle.js +3 -2
  26. package/dist/client/lifecycle.js.map +1 -1
  27. package/dist/client/rpc-pipeline.d.ts +9 -3
  28. package/dist/client/rpc-pipeline.js +80 -27
  29. package/dist/client/rpc-pipeline.js.map +1 -1
  30. package/dist/client/v2-e2ee.d.ts +13 -1
  31. package/dist/client/v2-e2ee.js +32 -19
  32. package/dist/client/v2-e2ee.js.map +1 -1
  33. package/dist/client.d.ts +13 -5
  34. package/dist/client.js +121 -37
  35. package/dist/client.js.map +1 -1
  36. package/dist/group-fs.js +1 -1
  37. package/dist/group-fs.js.map +1 -1
  38. package/dist/index.d.ts +3 -1
  39. package/dist/index.js +1 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/ownership-workflow.d.ts +23 -3
  42. package/dist/ownership-workflow.js +39 -16
  43. package/dist/ownership-workflow.js.map +1 -1
  44. package/dist/pubsub/binding.d.ts +54 -0
  45. package/dist/pubsub/binding.js +343 -0
  46. package/dist/pubsub/binding.js.map +1 -0
  47. package/dist/pubsub/channel.d.ts +45 -0
  48. package/dist/pubsub/channel.js +137 -0
  49. package/dist/pubsub/channel.js.map +1 -0
  50. package/dist/pubsub/client.d.ts +93 -0
  51. package/dist/pubsub/client.js +318 -0
  52. package/dist/pubsub/client.js.map +1 -0
  53. package/dist/pubsub/credentials.d.ts +51 -0
  54. package/dist/pubsub/credentials.js +360 -0
  55. package/dist/pubsub/credentials.js.map +1 -0
  56. package/dist/pubsub/errors.d.ts +5 -0
  57. package/dist/pubsub/errors.js +6 -0
  58. package/dist/pubsub/errors.js.map +1 -0
  59. package/dist/pubsub/index.d.ts +48 -0
  60. package/dist/pubsub/index.js +152 -0
  61. package/dist/pubsub/index.js.map +1 -0
  62. package/dist/pubsub/scheduler.d.ts +58 -0
  63. package/dist/pubsub/scheduler.js +249 -0
  64. package/dist/pubsub/scheduler.js.map +1 -0
  65. package/dist/pubsub/service.d.ts +100 -0
  66. package/dist/pubsub/service.js +650 -0
  67. package/dist/pubsub/service.js.map +1 -0
  68. package/dist/pubsub/types.d.ts +119 -0
  69. package/dist/pubsub/types.js +2 -0
  70. package/dist/pubsub/types.js.map +1 -0
  71. package/dist/storage/lowlevel.d.ts +4 -2
  72. package/dist/storage/lowlevel.js +46 -7
  73. package/dist/storage/lowlevel.js.map +1 -1
  74. package/dist/storage/read-cache.d.ts +7 -3
  75. package/dist/storage/read-cache.js +149 -18
  76. package/dist/storage/read-cache.js.map +1 -1
  77. package/dist/storage/vfs.d.ts +5 -2
  78. package/dist/storage/vfs.js +13 -3
  79. package/dist/storage/vfs.js.map +1 -1
  80. package/dist/tools/cross-sdk-agent.d.ts +8 -2
  81. package/dist/tools/cross-sdk-agent.js +81 -16
  82. package/dist/tools/cross-sdk-agent.js.map +1 -1
  83. package/dist/tools/pubsub-actor.d.ts +27 -0
  84. package/dist/tools/pubsub-actor.js +260 -0
  85. package/dist/tools/pubsub-actor.js.map +1 -0
  86. package/dist/transport.d.ts +25 -1
  87. package/dist/transport.js +175 -10
  88. package/dist/transport.js.map +1 -1
  89. package/dist/version.d.ts +1 -1
  90. package/dist/version.js +1 -1
  91. package/package.json +1 -1
@@ -0,0 +1,459 @@
1
+ # AUN事件发布订阅应用集成指南
2
+
3
+ 面向应用开发者,按 Bus 宿主、Pub 发布方、Sub 订阅方说明调用顺序、配置与结果处理;每条主流程用序列图配套实际 SDK 示例。本文对照当前工作区四端 SDK 与[事件订阅发布方案](../../../docs/AUN文档/AUN事件订阅发布方案.md)整理;源码能力、包发布和服务部署状态应分别核对。SDK 内部契约见[开发移植指南](AUN事件发布订阅开发移植指南.md)。
4
+
5
+ ## 1. 适用场景与交付边界
6
+
7
+ EventService 是运行在一个 AID 的 SDK 内的轻量事件服务。事件由 `(bus_aid, event_name)` 唯一确定,例如服务 `tasks.example.com` 发布 `task.completed`,正文是 JSON 对象。
8
+
9
+ ### 1.1 整体系统架构
10
+
11
+ ```mermaid
12
+ %%{init: {"theme":"base","themeVariables":{"fontFamily":"Microsoft YaHei, sans-serif","fontSize":"14px","lineColor":"#64748b"},"flowchart":{"curve":"linear","nodeSpacing":30,"rankSpacing":45}}}%%
13
+ flowchart TB
14
+ subgraph APPS["应用侧 · 三种 AID 角色"]
15
+ direction LR
16
+ P["Pub · 发布应用<br/>pubClient.EventService<br/>鉴权 · 发布 · 观察受理结果"]:::pub
17
+ B["Bus · 服务宿主<br/>busClient.EventService<br/>目录 · 授权 · 订阅表 · 公平分发"]:::bus
18
+ S["Sub · 订阅应用<br/>subClient.EventService<br/>鉴权 · 订阅 · 应用回调"]:::sub
19
+ end
20
+ G["Gateway · AUN 接入<br/>认证连接及设备身份 · 转发 RPC 与消息通知"]:::gateway
21
+ subgraph INFRA["AUN 基础服务"]
22
+ direction LR
23
+ M["消息服务 Message<br/>Bus 单实例注册与租约<br/>设备定向 · TTL 内存收件箱 · Push / Pull / ACK"]:::infra
24
+ C["凭证服务 Credential<br/>登记 Bus 已签名的凭证<br/>签名状态证明 · 撤销处理"]:::infra
25
+ end
26
+ P <-->|"发布请求 / 受理回复"| G
27
+ B <-->|"实例注册 / 收取发布 / 发送事件"| G
28
+ S <-->|"订阅请求 / 定向事件"| G
29
+ G <-->|"消息与实例 RPC"| M
30
+ G <-->|"凭证 RPC"| C
31
+ classDef pub fill:#eff6ff,stroke:#93c5fd,color:#1e3a8a,rx:10,ry:10
32
+ classDef bus fill:#eef2ff,stroke:#818cf8,stroke-width:2px,color:#312e81,rx:12,ry:12
33
+ classDef sub fill:#ecfdf5,stroke:#6ee7b7,color:#065f46,rx:10,ry:10
34
+ classDef gateway fill:#fff7ed,stroke:#fdba74,color:#9a3412,rx:10,ry:10
35
+ classDef infra fill:#f8fafc,stroke:#cbd5e1,color:#334155,rx:10,ry:10
36
+ style APPS fill:#ffffff,stroke:#e2e8f0,rx:12,ry:12
37
+ style INFRA fill:#f8fafc,stroke:#e2e8f0,rx:12,ry:12
38
+ ```
39
+
40
+ 三方各自通过 AUNClient 连接 Gateway。一次事件沿“Pub → Gateway → 消息服务 → Gateway → Bus”,再沿“Bus → Gateway → 消息服务 → Gateway → Sub”投递;图中 Gateway 合并表示各方的接入连接。
41
+
42
+ Bus SDK 决定谁能发布、谁订阅了什么,并将事件逐个发往目标设备。消息服务维护 Bus 的唯一承接实例,提供两跳 TTL 内存收件箱;Gateway 提供可信连接、设备身份及转发。凭证模式下,Bus 使用自己的私钥签发、在 SDK 内验证,经过 Gateway 调用凭证服务登记、查询状态或撤销。
43
+
44
+ ### 1.2 从三个角色看一次事件
45
+
46
+ | 场景 | 使用方式与边界 |
47
+ | --- | --- |
48
+ | 多个 agent 观察任务完成、状态变化 | 发布方发给 Bus,Bus 向已订阅的设备实例逐个转发 |
49
+ | 应用直接控制全部参与者 | 配置事件目录和发布、订阅 AID 白名单 |
50
+ | 应用审批后授予临时权限 | Bus 宿主应用本地签发凭证,再将完整凭证交给调用方 |
51
+ | 同一 AID 多设备选择不同事件 | 每个设备分别鉴权和订阅,只向实际订阅的设备实例投递 |
52
+ | 短暂断线后继续观察 | SDK 恢复授权和订阅;尚在 TTL 内的内存消息可能补收 |
53
+ | 必须保存历史、补齐每次变化或确认业务提交 | 当前 EventService 不提供事件日志、历史查询、重放或业务 ACK;不能据此保证每次变化都已处理 |
54
+
55
+ 事件只走正 TTL 的 volatile 内存消息路径。超过 TTL、队列容量耗尽、消息服务重启或 Bus 重启,都可能丢失尚未交付的事件。它适合在线通知,不提供持久消息队列的交付承诺。
56
+
57
+ ```mermaid
58
+ sequenceDiagram
59
+ participant P as 发布方
60
+ participant S as Bus服务AID
61
+ participant C as 订阅设备
62
+ C->>S: authorize(含目录)→ subscribe
63
+ S-->>C: 订阅生效
64
+ P->>S: authorize(含目录)
65
+ P->>P: publish返回event_id:本地入队
66
+ P->>S: 后台发送publish
67
+ opt replyMode为accepted
68
+ S-->>P: publish.result:Bus受理
69
+ end
70
+ S-->>C: event:按剩余TTL定向投递
71
+ C->>C: SDK校验后交付应用
72
+ ```
73
+
74
+ 图中跨 AID 的交互均由 AUN E2EE P2P 消息承载;应用不需要创建 HTTP 接口或远程启停 RPC。
75
+
76
+ ## 2. 身份与公开入口
77
+
78
+ 准备已加载身份并已连接就绪的 `busClient`、`pubClient`、`subClient`。身份加载和连接见[快速开始](01-快速开始.md)与[连接与认证](04-连接与认证.md)。以下 TypeScript 片段按小节顺序衔接,客户端和业务处理函数由应用提供。
79
+
80
+ | SDK | 入口 | 方法风格 |
81
+ | --- | --- | --- |
82
+ | Python | `client.EventService` 属性 | `issue_grant`、`wait_published`、`on_event` |
83
+ | TypeScript / JavaScript | `client.EventService` 属性 | `issueGrant`、`waitPublished`、`onEvent` |
84
+ | Go | `client.EventService()` | `IssueGrant`、`WaitPublished`、`OnEvent`;网络及等待调用传 `context.Context` |
85
+
86
+ 所有业务方法都以 `bus_aid` 为目标参数。应用使用统一门面,内部 Binding、租约令牌、device 和 slot 由 SDK 管理。
87
+
88
+ - 同一个 client 对同一个 Bus 只能承担 `pub`、`sub`、`bus` 中一种角色;切换会报 `BUS_ROLE_CONFLICT`,`close` 不释放这个角色绑定。
89
+ - 同一个 client 可以在不同 Bus 上承担不同角色。签发、撤销和统计只由已启动本地 Bus 的宿主调用。
90
+ - 未绑定时,`start(bus_aid, ...)` 表示在当前身份启动本地 Bus,要求 `bus_aid == client.aid`。接入远端 Bus 直接调用 `authorize`,由它自动建立客户端。
91
+ - 若 Bus 自己也要发布,使用同 AID 的另一个 AUNClient 作为 `pub`,并为它授予发布权限。
92
+
93
+ ### 2.1 按自己的角色阅读
94
+
95
+ | 角色 | 应用需要做什么 | 代码与序列图 |
96
+ | --- | --- | --- |
97
+ | Bus 宿主 | 配置目录与权限 → start → 本地签发/撤销或更新名单 → stats → drain/close | §3.1、§4、§7 |
98
+ | Pub 发布方 | authorize 自动加载目录 → 分别 publish 多个事件 → 观察完成结果 | §3.3;凭证接入见 §4.3 |
99
+ | Sub 订阅方 | authorize 自动加载目录 → 注册回调 → subscribe → 按事件名处理 → 按需取消 | §3.2;凭证接入见 §4.2 |
100
+
101
+ 下面按角色配套序列图和代码。角色图中的“AUN 消息通道”合并表示架构图中的 Gateway 与消息服务;Pub/Sub 到 Bus 的请求及定向回复均经过它。SDK 内部队列和协议执行顺序见[开发移植指南](AUN事件发布订阅开发移植指南.md)。
102
+
103
+ ## 3. 场景一:白名单接入
104
+
105
+ ### 3.1 在服务身份启动 Bus
106
+
107
+ **Bus 视角:** 宿主准备目录及白名单或凭证模式,本地调用 start。成功后承接发布与订阅;停机前停止上游提交,再 drain、close。
108
+
109
+ ```mermaid
110
+ sequenceDiagram
111
+ participant A as Bus 宿主应用
112
+ participant B as 本地 Bus SDK
113
+ participant G as Gateway
114
+ participant M as 消息服务
115
+ A->>B: start(bus_aid, catalog, 授权配置)
116
+ B->>G: 注册 Bus 承接设备(SDK 自动)
117
+ G->>M: 注册当前 Bus AID 的设备实例
118
+ alt 注册成功
119
+ M-->>G: 实例租约:100 秒有效,30 秒后续期
120
+ G-->>B: 注册结果
121
+ B->>B: 启用承接、队列与分发
122
+ B-->>A: start 完成
123
+ else 已有实例
124
+ M-->>G: DEVICE_CONFLICT
125
+ G-->>B: 冲突错误
126
+ B-->>A: start 失败
127
+ end
128
+ opt 成功运行期间,每 30 秒
129
+ B->>G: 续期承接设备(SDK 自动)
130
+ G->>M: 原实例续期
131
+ M-->>G: 新实例期限
132
+ G-->>B: 续期成功
133
+ end
134
+ opt 宿主主动停机
135
+ A->>B: drain(bus_aid),随后 close(bus_aid)
136
+ B->>G: 注销承接设备(SDK 自动)
137
+ G->>M: 注销原实例
138
+ M-->>G: 注销结果
139
+ G-->>B: 结果
140
+ B-->>A: 清理完成
141
+ end
142
+ ```
143
+
144
+ 先约定事件目录,再配置谁可以发布、谁可以订阅。`content_schema` 是当前实现支持的结构约束,不代表完整 JSON Schema 引擎;支持范围见[移植指南 §5](AUN事件发布订阅开发移植指南.md#5-目录版本与内容校验)。
145
+
146
+ ```ts
147
+ const bus = busClient.EventService;
148
+ const busAid = String(busClient.aid);
149
+ const eventNames = ['task.completed', 'task.failed'];
150
+ const eventName = eventNames[0];
151
+ const catalog = {
152
+ 'task.completed': { description: '任务完成', schema_version: 1, default_ttl_seconds: 30, max_ttl_seconds: 300 },
153
+ 'task.failed': { description: '任务失败', schema_version: 1, default_ttl_seconds: 15, max_ttl_seconds: 120 },
154
+ };
155
+ ```
156
+
157
+ ```ts
158
+ const allowlist = {
159
+ 'task.completed': { publish_aids: [String(pubClient.aid)], subscribe_aids: [String(subClient.aid)] },
160
+ 'task.failed': { publish_aids: [String(pubClient.aid)], subscribe_aids: [String(subClient.aid)] },
161
+ };
162
+ await bus.start(busAid, { catalog, allowlist });
163
+ ```
164
+
165
+ Bus 宿主直接维护配置、签发和撤销凭证、查询统计,无需额外管理身份。
166
+
167
+ 一个 Bus 的目录可登记多个事件。示例让同一发布方、订阅方使用两个事件;需要不同权限时,分别配置各事件的 `publish_aids` 和 `subscribe_aids`。各事件也可独立设置正文约束、版本和 TTL。
168
+
169
+ 同一个 Bus AID 同时只允许一个设备实例运行。第二个实例启动返回 `DEVICE_CONFLICT`;需要独立服务时使用不同 Bus AID。实际环境须支持 `aun.pubsub/1` 控制消息及 `message.evtsvc.*` 实例注册能力。
170
+
171
+ ### 3.2 订阅方一次鉴权多个事件并登记订阅
172
+
173
+ **Sub 视角:** 先鉴权并缓存目录,注册回调后订阅。SDK 收到有效事件,才调用应用处理函数;以下代码处理两个事件。
174
+
175
+ ```mermaid
176
+ sequenceDiagram
177
+ participant A as 订阅应用
178
+ participant S as Sub SDK
179
+ participant N as AUN 消息通道
180
+ participant B as Bus SDK
181
+ A->>S: authorize(bus_aid, sub, 模式, 多个事件)
182
+ S->>N: authorize
183
+ N->>B: 申请订阅权限
184
+ B-->>N: 设备租约及获准目录
185
+ N-->>S: SDK 自动加载目录,必要时续页
186
+ S-->>A: 鉴权完成
187
+ A->>S: onEvent(bus_aid, handler)
188
+ A->>S: subscribe(bus_aid, events)
189
+ S->>N: subscribe:设备租约及事件列表
190
+ N->>B: 登记当前设备、slot 的订阅
191
+ B-->>N: subscribe.result
192
+ N-->>S: 订阅成功
193
+ S-->>A: subscribe 完成
194
+ B->>N: event:指定已订阅的设备与 slot
195
+ N-->>S: Push 或 TTL 内的 Pull
196
+ S->>S: 解密、校验来源/租约/版本/TTL,并去重
197
+ S-->>A: onEvent 回调,按 event_name 处理 data
198
+ opt 每 1 小时或连接恢复
199
+ S->>N: renew:原设备授权租约
200
+ N->>B: 复核权限并续期
201
+ B-->>N: 新期限,或 REAUTH_REQUIRED
202
+ N-->>S: 更新租约;必要时重新鉴权并恢复订阅
203
+ end
204
+ ```
205
+
206
+ ```ts
207
+ const sub = subClient.EventService;
208
+ await sub.authorize(busAid, 'sub', 'allowlist', eventNames);
209
+ const stopEvents = sub.onEvent(busAid, async event => {
210
+ if (event.event_name === 'task.completed') await handleTaskCompleted(event.data);
211
+ if (event.event_name === 'task.failed') await handleTaskFailed(event.data);
212
+ });
213
+ await sub.subscribe(busAid, eventNames);
214
+ ```
215
+
216
+ `authorize` 建立权限租约,同时取得并缓存本次获准事件的目录;白名单和凭证模式都无需再单独调用 `catalog`。大目录由 SDK 自动分页取齐后才返回。`subscribe` 成功回复后订阅生效,可以一次传全部获准事件,也可只传其中一部分;新增范围需重新鉴权。鉴权范围内任一事件未获准,整个鉴权失败。
217
+
218
+ 回调在订阅前注册,支持返回 Promise。也可以不注册回调,使用 `await sub.nextEvent(busAid, 5)` 主动等待,超时单位为秒。Python 对应 `next_event(bus_aid, timeout=5)`;Go 用 `NextEvent(ctx, bus_aid)` 的 context 控制超时。
219
+
220
+ 回调与主动等待选择一种消费方式;有回调时,新事件进入回调路径,不会同时成为 `nextEvent` 的副本。回调按顺序执行,异常不会终止后续事件,也不会要求 Bus 重发;慢处理会占用有界接收容量。
221
+
222
+ ### 3.3 发布并观察完成结果
223
+
224
+ **Pub 视角:** 一次鉴权多个事件后,分别发布 JSON 正文;publish 返回本地 event_id,后续通过完成结果观察实际发送或 Bus 受理。
225
+
226
+ ```mermaid
227
+ sequenceDiagram
228
+ participant A as 发布应用
229
+ participant P as Pub SDK
230
+ participant N as AUN 消息通道
231
+ participant B as Bus SDK
232
+ A->>P: authorize(bus_aid, pub, 模式, 多个事件)
233
+ P->>N: authorize:申请发布范围
234
+ N->>B: 路由到 Bus 承接实例
235
+ B->>B: 校验权限,建立设备授权租约
236
+ B-->>N: authorize.result:租约与目录首页
237
+ N-->>P: 定向返回原 Pub 设备
238
+ P->>P: SDK 自动取齐后续页,缓存获准目录
239
+ P-->>A: 目录缓存就绪,鉴权完成
240
+ A->>P: publish(bus_aid, event_name, JSON对象)
241
+ P-->>A: 返回 event_id:已进入本地有界队列
242
+ P->>N: 后台发送 publish,保留原 TTL
243
+ N-->>P: AUN 已接受发送
244
+ N->>B: 发布请求
245
+ B->>B: 校验、去重,接入有界 fanout 队列
246
+ opt replyMode 为 accepted
247
+ B-->>N: publish.result:Bus 已受理
248
+ N-->>P: 仅回原 Pub 设备
249
+ end
250
+ P-->>A: waitPublished / onPublish 返回对应阶段结果
251
+ Note over B: 后台向有效订阅设备公平分发
252
+ ```
253
+
254
+ ```ts
255
+ const pub = pubClient.EventService;
256
+ await pub.authorize(busAid, 'pub', 'allowlist', eventNames);
257
+ const options = { schemaVersion: 1, ttlSeconds: 30, replyMode: 'accepted' as const };
258
+ const doneId = await pub.publish(busAid, 'task.completed',
259
+ { task_id: 'T1', status: 'done' }, options);
260
+ const failedId = await pub.publish(busAid, 'task.failed',
261
+ { task_id: 'T2', reason: 'timeout' }, options);
262
+ await Promise.all([pub.waitPublished(busAid, doneId), pub.waitPublished(busAid, failedId)]);
263
+ ```
264
+
265
+ | 观察结果 | 可以确认什么 |
266
+ | --- | --- |
267
+ | `publish` 返回 `event_id` | 本地发布队列已收下任务,尚未保证发送成功 |
268
+ | 默认 `replyMode: 'none'` 的 `waitPublished` 成功 | AUN 接受了发往 Bus 的消息 |
269
+ | `replyMode: 'accepted'` 的 `waitPublished` 返回 `status: 'accepted'` | Bus 完成授权、去重及内存受理,后续分发仍在后台执行 |
270
+ | `onEvent` / `nextEvent` 收到事件 | 当前设备 SDK 已收到并校验;业务处理结果由应用负责 |
271
+
272
+ 一次 `authorize` 可授权多个事件;每次 `publish` 指定一个完整事件名和对应 JSON 正文。不同事件的发布共用该 Bus 的有界队列与公平调度,应用按每个 `event_id` 观察结果。订阅方通过 `event.event_name` 分发给各业务处理函数。
273
+
274
+ `waitPublished` 的错误必须由调用方处理;也可用 `onPublish(busAid, handler)` 观察后台发送完成,其参数含 `event_id`、`result` 或 `error`。结果缓存有界且只在内存中,不能作为长期审计记录,过期或淘汰后等待可能报 `UNKNOWN_EVENT`。
275
+
276
+ `data` 必须为 JSON 对象,允许嵌套 JSON 值;不能直接传数组、字符串、`null`、非有限数或不可序列化对象。TTL 默认为目录配置(缺省 30 秒),取整数 1—3600 秒且不得超过该事件 `max_ttl_seconds`。正文连同事件封装受 64 KiB 明文预算限制。
277
+
278
+ 本地排队、加密、两跳网络发送与 Bus 排队共用原始期限。超时表示结果可能未知;重试复用原 `eventId` 和原事件内容,由 SDK 保留原期限。同 ID 改正文、事件名或版本会冲突;内存去重不构成持久的“恰好一次”保证。
279
+
280
+ ## 4. 场景二:应用审批与凭证授权
281
+
282
+ 本节与白名单场景二选一,从 Bus 启动开始。准备已加载各自 AID 并连接就绪的 `busClient`、`pubClient`、`subClient`。Bus 使用自己的 AUN 身份接入消息、凭证服务;宿主调用本机 `start`,随后本地签发、撤销凭证和查询统计。
283
+
284
+ ### 4.1 启动凭证模式 Bus
285
+
286
+ ```ts
287
+ const bus = busClient.EventService;
288
+ const busAid = String(busClient.aid);
289
+ const eventNames = ['task.completed', 'task.failed'];
290
+ const catalog = {
291
+ 'task.completed': { description: '任务完成', schema_version: 1, default_ttl_seconds: 30, max_ttl_seconds: 300 },
292
+ 'task.failed': { description: '任务失败', schema_version: 1, default_ttl_seconds: 15, max_ttl_seconds: 120 },
293
+ };
294
+ ```
295
+
296
+ ```ts
297
+ await bus.start(busAid, { catalog });
298
+ const sub = subClient.EventService;
299
+ const pub = pubClient.EventService;
300
+ ```
301
+
302
+ 这里省略 `allowlist`,由凭证授予发布和订阅权限。SDK 自动创建凭证适配器、注册唯一承接设备。本地 Bus 的签发、撤销及统计方法直接操作当前服务;远程调用不能执行这些管理操作。
303
+
304
+ **Bus AID 签发,Bus SDK 验证。** 宿主应用审批后调用自己的 `bus.issueGrant`,SDK 用 Bus 私钥签名并向 AUN 凭证服务登记。发布者、订阅者向 Bus 提交完整凭证;Bus SDK 验证凭证和权限,并按需查询当前状态证明。
305
+
306
+ ```mermaid
307
+ sequenceDiagram
308
+ participant A as Bus 宿主应用
309
+ participant B as Bus SDK
310
+ participant G as Gateway
311
+ participant C as 凭证服务
312
+ participant P as Pub 或 Sub 应用
313
+ A->>A: 完成业务鉴权,批准事件和操作范围
314
+ A->>B: 本地 issueGrant:目标 AID、scope、期限
315
+ B->>B: 使用 Bus 私钥签署完整 grant
316
+ B->>G: credential.register
317
+ G->>C: 登记已签名凭证
318
+ C-->>G: 登记回执
319
+ G-->>B: 回执
320
+ B-->>A: grant、cid、receipt
321
+ A-->>P: 通过应用业务通道交付完整 grant
322
+ P->>P: 调用本地 Pub/Sub SDK,沿 AUN 消息通道 authorize
323
+ Note over P,B: authorize 携带完整 grant 和申请范围
324
+ B->>B: 验证签发链、主体、受众、scope 与请求绑定
325
+ opt 需要当前状态证明
326
+ B->>G: credential.status
327
+ G->>C: 查询凭证状态
328
+ C-->>G: 签名状态证明
329
+ G-->>B: 状态证明
330
+ end
331
+ B->>B: 完整校验通过后建立设备授权租约
332
+ ```
333
+
334
+ ### 4.2 应用审批后,为订阅方签发并鉴权
335
+
336
+ Bus 宿主应用完成自己的业务鉴权后,本地签发 `aun-cap/1` 凭证。完整 `grant` 交给指定订阅者;应用可通过自己的业务接口传递。
337
+
338
+ ```ts
339
+ const issued = await bus.issueGrant(busAid, {
340
+ subject_aid: String(subClient.aid),
341
+ scope: [
342
+ { event_name: 'task.completed', action: 'subscribe' },
343
+ { event_name: 'task.failed', action: 'subscribe' },
344
+ ],
345
+ grant_expires_at: Date.now() + 24 * 3600 * 1000,
346
+ });
347
+ ```
348
+
349
+ ```ts
350
+ await sub.authorize(busAid, 'sub', 'credential', eventNames, issued.grant as Record<string, unknown>);
351
+ const stopEvents = sub.onEvent(busAid, async event => {
352
+ if (event.event_name === 'task.completed') await handleTaskCompleted(event.data);
353
+ if (event.event_name === 'task.failed') await handleTaskFailed(event.data);
354
+ });
355
+ await sub.subscribe(busAid, eventNames);
356
+ ```
357
+
358
+ ### 4.3 为发布方签发并鉴权,分别发布事件
359
+
360
+ Bus 宿主应用批准发布权限后,为发布者签发两个事件的凭证:
361
+
362
+ ```ts
363
+ const pubGrant = await bus.issueGrant(busAid, {
364
+ subject_aid: String(pubClient.aid),
365
+ scope: [
366
+ { event_name: 'task.completed', action: 'publish' },
367
+ { event_name: 'task.failed', action: 'publish' },
368
+ ],
369
+ grant_expires_at: Date.now() + 24 * 3600 * 1000,
370
+ });
371
+ ```
372
+
373
+ ```ts
374
+ await pub.authorize(busAid, 'pub', 'credential', eventNames, pubGrant.grant as Record<string, unknown>);
375
+ const doneId = await pub.publish(busAid, 'task.completed', { task_id: 'T1', status: 'done' });
376
+ const failedId = await pub.publish(busAid, 'task.failed', { task_id: 'T2', reason: 'timeout' });
377
+ await Promise.all([pub.waitPublished(busAid, doneId), pub.waitPublished(busAid, failedId)]);
378
+ ```
379
+
380
+ Bus 从 AUN 验签结果取得发布者、订阅者的 AID 和设备身份,再完整校验 grant 的签发链、主体、受众、事件、操作、期限、约束、当前状态证明和请求绑定,通过后建立设备租约并附带目录。两方的 `authorize` 自动加载目录,订阅方另调用 `subscribe` 后生效。
381
+
382
+ `grant_expires_at` 使用 Unix 毫秒,凭证内部 `exp` 使用秒,转换由 SDK 完成。发布方、订阅方分别持有绑定自身 AID 和操作的凭证;仅有 CID 无法完成鉴权。
383
+
384
+ 正常签发返回 `grant`、`cid` 和 `receipt`。登记回复丢失且超过原绑定窗口时,SDK 可依据已保存的真实发送记录及最新签名状态证明确认原登记,返回同一 `grant/cid`、`receipt: null` 和 `registration_confirmation`;不能仅因 receipt 为空认定失败。跨进程恢复依赖 §7 的持久化接线。
385
+
386
+ 宿主撤销使用 `bus.revokeGrant(busAid, { cid: issued.cid })`。Bus 先暂停关联授权,再提交撤销;结果未知期间继续暂停。外部有效撤销同步到达时同样使关联关系失效。凭证已到期或撤销,需应用重新审批签发。
387
+
388
+ ## 5. 场景三:多设备、权限变更与事件版本
389
+
390
+ 同一 AID 的多个设备分别执行“鉴权(自动取得目录)→ 订阅”。例如手机只订阅 `task.completed`,桌面只订阅 `task.failed`。租约绑定可信 AID、device 和 slot;事件只发给已订阅的实例,其他设备不会收到副本,也不能通过 Pull 或 ACK 消费该消息。多设备会各收到一份符合其订阅的事件,但公平调度额度仍按 AID 聚合。
391
+
392
+ | 变更 | 应用动作与生效边界 |
393
+ | --- | --- |
394
+ | 取消某事件 | `await sub.unsubscribe(busAid, [eventName])`;SDK 移除本地意图,后续恢复不再补订阅它 |
395
+ | 白名单缩权 | Bus 宿主 `updateConfig(busAid, { allowlist })`;取消的权限及对应订阅立即收缩,凭证租约继续按凭证校验 |
396
+ | 白名单增加权限 | 调用方重新 `authorize` 申请完整所需范围,自动加载目录后订阅新增事件;续期不会扩展原 scope |
397
+ | 修改事件目录或版本 | Bus 宿主 `updateConfig(busAid, { catalog })`;客户端重新 `catalog`,发布使用当前 `schemaVersion` |
398
+ | 关闭回调 | 调用注册返回的 `stopEvents()`;它只取消本地 handler,不等于远端 `unsubscribe` |
399
+
400
+ 白名单可使用事件条目 `"*"`,也可在 AID 数组使用 `"*"`,分别表示不限制事件或 AID,精确条目与通配条目按并集授权。通配应仅限本机受控多 agent 之间使用,其他情况不建议如此。通配不会创建目录外事件;`authorize`、`subscribe` 仍传完整事件名数组,不接受订阅模式。
401
+
402
+ `authorize` 回复的 `catalog` 是本次授权目录首页,最多 2 KiB;普通单事件或少量事件通常一次请求完成,大结构由 SDK 自动续查并缓存,应用调用方式相同。返回值保留首页,不汇总全部条目。独立 `catalog` 用于查询或刷新,响应含 `items`、`next_page_token`、`catalog_revision`,门面另提供 `events` 别名。远程查询按有效租约过滤,本地 Bus 可查全部目录;分页默认 50、上限 200、完整回包最多 64 KiB,应用继续传 `page_token` 直至结束。目录变化导致 `CATALOG_CHANGED` 时重新鉴权或从查询首页刷新。客户端保留短期旧版本校验窗口,只处理仍在原 TTL 内的在途事件。
403
+
404
+ ## 6. 租约、断线与重启
405
+
406
+ | 生命周期 | 当前行为 |
407
+ | --- | --- |
408
+ | Bus 承接实例 | 有效 100 秒、每 30 秒续期;SDK 自动注册和注销 |
409
+ | 发布/订阅授权 | 默认有效 200 分钟、每 1 小时续期;凭证模式还受凭证链期限约束,以回复 `expires_at` 为准 |
410
+ | 凭证状态证明 | 按 `proofExpiresAt` 独立刷新;过期且无法刷新时暂停相关操作,不等每小时续期 |
411
+ | 当前进程短断线 | 连接 ready 后尝试续期;需要重新鉴权时使用已保存的授权材料,并恢复未取消的订阅 |
412
+ | Bus 重启 | 配置可恢复,原租约、订阅和事件队列不恢复;客户端重新鉴权和订阅 |
413
+ | 客户端进程重启或显式 close | 应用重新执行接入流程;SDK 不保存跨进程订阅意图 |
414
+ | Bus 失去实例租约 | 停止承接与分发,不自动抢占后来实例;宿主显式 `start`,客户端重新鉴权、订阅 |
415
+
416
+ 自动恢复只恢复关系,不补发已过期或丢失事件。无需调用内部 `recover`,公开门面没有该方法。
417
+
418
+ 停机先停止上游提交,再对相关 Bus 执行 `drain(busAid)`、`close(busAid)`;先排空发布方,再排空 Bus。`drain` 等待已接管任务及回调,不证明所有设备都收到或提交业务。`close` 清理队列、挂起等待和授权状态;再次接入需重新鉴权和订阅,目录随鉴权自动加载。关闭 AUNClient 也会关闭其 EventService。
419
+
420
+ ## 7. 持久化、统计与故障定位
421
+
422
+ | SDK | Bus 配置与凭证材料保存方式 |
423
+ | --- | --- |
424
+ | Python / Go | 凭证材料默认自动写入 `{aun_path}/AIDs/{self-aid}/aun.db`;显式 `state_path` / `StatePath` 时,指定目录保存 `event-service.json` 和 `credential-state.json` |
425
+ | TS | 凭证材料默认自动写入自身 AID 的 `aun.db`;服务配置通过 `start` 的 `state.load/save` 保存,`credentialOptions.state.load/save` 可覆盖凭证存储 |
426
+ | JS | 凭证材料默认自动写入浏览器 IndexedDB `aun-keystore` 的 `metadata`,按自身 AID 隔离;服务配置及自定义凭证存储回调同 TS |
427
+
428
+ 服务状态包括目录、白名单、签发及暂停记录;它不保存事件历史、租约、订阅或投递队列。重启加载的已保存配置优先于初始配置,变更应通过 `updateConfig` 提交。各语言内部状态格式不是跨语言导入协议,不能直接交换目录中的状态文件。
429
+
430
+ 默认凭证适配器统一使用元数据键 `pubsub_credentials_v1`,保存原凭证、登记/撤销请求、提交证据和结果,应用无需另写存储回调。凭证材料自动保存与 Bus 配置保存是两件事;要恢复完整 Bus 配置,仍需按上表配置服务状态存储。`self-aid` 指运行 Bus 的当前身份,不是发布者或订阅者。
431
+
432
+ Bus 宿主应用调用 `bus.stats(busAid, { event_name: eventName })`,结果位于 `counts`,`since` 为本次运行起点,`as_of` 为查询时点。计数不持久保存。
433
+
434
+ | 统计字段 | 含义 |
435
+ | --- | --- |
436
+ | `active_publishers` / `active_subscribers` | 有效发布方 / 订阅方的去重 AID 数 |
437
+ | `active_subscriptions` / `active_leases` | 设备实例与事件的订阅关系数 / 有效授权租约数 |
438
+ | `published` / `matched` | 受理事件数 / 受理快照中匹配的目标关系累计数 |
439
+ | `submitted` | AUN 接受的目标设备投递数,不是业务处理成功数 |
440
+ | `failed` / `unconfirmed` | 已确认发送失败 / 发送结果尚不确定 |
441
+ | `skipped_expired` / `skipped_overloaded` / `skipped_revoked` | 因到期、目标容量或授权变化跳过的投递 |
442
+ | `queued_events` / `queued_deliveries` / `queued_bytes` / `inflight` | 当前积压与发送负载;不是持久待办队列 |
443
+
444
+ | 表现或错误 | 核查顺序 |
445
+ | --- | --- |
446
+ | `SERVICE_NOT_STARTED` / `DEVICE_CONFLICT` | 确认 Bus 是否已成功 start、是否有其他承接实例 |
447
+ | `BUS_ROLE_CONFLICT` / `SERVICE_IDENTITY_MISMATCH` | 检查同 client 的角色及宿主 AID |
448
+ | `FORBIDDEN` / `REAUTH_REQUIRED` | 检查名单或凭证 scope、租约、证明和撤销状态 |
449
+ | `CATALOG_REQUIRED` / `SCHEMA_MISMATCH` / `UNSUPPORTED_SCHEMA_VERSION` | 获取当前事件目录,再使用匹配版本发布 |
450
+ | `OVERLOADED` / `MESSAGE_TOO_LARGE` | 检查发布及接收容量、正文尺寸;未入队的调用不能视为成功 |
451
+ | `TIMEOUT` | 结果可能未知;保留事件或管理操作的原有幂等材料 |
452
+ | 已受理但未收到 | 核对订阅生效时点、接收端目录、当前设备实例、TTL、授权、接收速度及 Bus 统计 |
453
+
454
+ ## 8. 代码与验收依据
455
+
456
+ - 门面与应用接线:[Python](../../python/src/aun_core/pubsub/__init__.py)、[TypeScript](../../ts/src/pubsub/index.ts)、[JavaScript](../../js/src/pubsub/index.ts)、[Go](../../go/pubsub_service.go)。
457
+ - 应用场景:[Python 应用测试](../../python/tests/unit/test_pubsub_application.py)、[场景测试](../../python/tests/unit/test_pubsub_scenarios.py)、[设备与恢复 E2E](../../python/tests/e2e_test_event_pubsub_recovery.py)。
458
+ - 本轮真实跨端证据:[实施计划 §9.9](../../../docs/AUN文档/AUN事件订阅发布TDD实施计划.md#99-凭证默认存储授权目录与移除管理身份)与[128项汇总](../../tests/cross-sdk/artifacts/pubsub-4sdk-20261105/remove-manager-acceptance/final-acceptance-summary.json):四种 Bus 各32/32,覆盖两种授权、同SDK和有向跨SDK;本地签发与统计、authorize自动目录后直接发布订阅均通过。
459
+ - 证据边界:JS 使用 Node 与 fake-indexeddb;真实浏览器、macOS、真实双域设备 E2E,以及实填 64 MiB/RSS 压力未验,详见[方案 §15](../../../docs/AUN文档/AUN事件订阅发布方案.md#15-轻量服务与单实例使用边界)。