@agentunion/fastaun-browser 0.5.17 → 0.5.19
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 +83 -0
- package/_packed_docs/CHANGELOG.md +83 -0
- package/_packed_docs/INDEX.md +163 -16
- package/_packed_docs/KITE_DOCS_GUIDE.md +52 -20
- package/_packed_docs/agent.md/SCHEMA.md +266 -207
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +64 -31
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +8 -6
- 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 +39 -39
- 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 +1 -1
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +28 -9
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +20 -2
- package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +5 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +246 -241
- package/_packed_docs/sdk/09-group-rpc-manual.md +46 -14
- package/_packed_docs/sdk/09-message-rpc-manual.md +40 -2
- package/_packed_docs/sdk/09-storage-rpc-manual.md +69 -38
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +25 -5
- package/_packed_docs/sdk/INDEX.md +78 -18
- package/dist/agent-md-schema.d.ts +3 -0
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +46 -10
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +44 -1
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +518 -26
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +12 -1
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +113 -18
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +17 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +69 -9
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +4 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +343 -23
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +37513 -31501
- package/dist/cert-utils.d.ts +1 -1
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +24 -10
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +16 -1
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +198 -58
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +28 -6
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +858 -484
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +100 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1216 -24
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +41 -1
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +84 -2
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +104 -4
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +17 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +10 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +111 -0
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +7 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +30 -1
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.d.ts.map +1 -0
- package/dist/org-protocol.js +1742 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +118 -0
- package/dist/ownership-proof.d.ts.map +1 -0
- package/dist/ownership-proof.js +283 -0
- package/dist/ownership-proof.js.map +1 -0
- package/dist/ownership-v21.d.ts +177 -0
- package/dist/ownership-v21.d.ts.map +1 -0
- package/dist/ownership-v21.js +609 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +146 -0
- package/dist/ownership-workflow.d.ts.map +1 -0
- package/dist/ownership-workflow.js +1066 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +10 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +82 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
- package/dist/tools/cross-sdk-agent.js +332 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +6 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +88 -2
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
- package/dist/v2/e2ee/decrypt.js +9 -1
- package/dist/v2/e2ee/decrypt.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
|
@@ -67,7 +67,7 @@ store := aun.NewAIDStore(aunPath, encryptionSeed, aun.AIDStoreOptions{
|
|
|
67
67
|
| `list()` | `list()` | `List()` | 列出本地 AID |
|
|
68
68
|
| `exists(aid)` | `exists(aid)` | `Exists(ctx, aid)` | 远端存在性检查 |
|
|
69
69
|
| `resolve(aid, opts=None)` | `resolve(aid, opts?)` | `Resolve(ctx, aid, opts...)` | 拉证书并缓存;默认下载 agent.md,可用 `skip_agent_md` / `skipAgentMd` 跳过 |
|
|
70
|
-
| `upload_agent_md(aid, content=None)` | `uploadAgentMd(aid, content?)` | `UploadAgentMD(ctx, aid, content...)` |
|
|
70
|
+
| `upload_agent_md(aid, content=None)` | `uploadAgentMd(aid, content?)` | `UploadAgentMD(ctx, aid, content...)` | 普通主体以本身份签名/token 发布;Group/Org 仅当前 owner 本人尾签并携带 V2 owner 授权上传,见协议 §2.5 |
|
|
71
71
|
| `download_agent_md(aid, timeout_s=None)` | `downloadAgentMd(aid, timeoutMs=30000)` | `DownloadAgentMD(ctx, aid)` | 下载 agent.md,返回 `DownloadAgentMdResult` / `AgentMDInfo` |
|
|
72
72
|
| `check_agent_md(aid, ttl_days=1)` | `checkAgentMd(aid, ttlDays=1)` | `CheckAgentMD(ctx, aid, maxUnsyncedDays...)` | 通过 HEAD 和本地记录检查一致性 |
|
|
73
73
|
| `diagnose(aid)` | `diagnose(aid)` | `Diagnose(ctx, aid)` | 本地 + 远端诊断 |
|
|
@@ -152,8 +152,8 @@ AID 由 `AIDStore.load()` 返回,应用层不直接构造。
|
|
|
152
152
|
| `is_private_key_valid()` | `isPrivateKeyValid()` | `IsPrivateKeyValid()` | 私钥是否可用 |
|
|
153
153
|
| `sign(data)` | `sign(data)` | `Sign(data)` | 签名 bytes |
|
|
154
154
|
| `verify(data, signature)` | `verify(data, signature)` | `Verify(data, signature)` | 验签 |
|
|
155
|
-
| `sign_agent_md(content)` | `signAgentMd(content)` | `SignAgentMd(content)` | agent.md 签名 |
|
|
156
|
-
| `verify_agent_md(content)` | `verifyAgentMd(content)` | `VerifyAgentMd(content)` | agent.md 验签 |
|
|
155
|
+
| `sign_agent_md(content)` | `signAgentMd(content)` | `SignAgentMd(content)` | agent.md 签名 |
|
|
156
|
+
| `verify_agent_md(content)` | `verifyAgentMd(content)` | `VerifyAgentMd(content)` | agent.md 验签 |
|
|
157
157
|
|
|
158
158
|
---
|
|
159
159
|
|
|
@@ -193,20 +193,20 @@ client := aun.NewAUNClient(aid)
|
|
|
193
193
|
|
|
194
194
|
| Python | TS/JS | Go | 说明 |
|
|
195
195
|
|--------|-------|----|------|
|
|
196
|
-
| `load_identity(aid)` | `loadIdentity(aid)` | `LoadIdentity(aid)` | 在 `no_identity` / `closed` 状态加载身份 |
|
|
197
|
-
| `state` | `state` | `ConnectionState()` | 九态公开状态 |
|
|
198
|
-
| `gateway_url` | `gatewayUrl` | `GetGatewayURL()` | 当前连接的 Gateway URL(只读,自动发现,连接前为空) |
|
|
199
|
-
| `get_access_token()` | `getAccessToken()` | `GetAccessToken()` | 同步读取后台刷新任务维护的当前 access token;不发起认证、刷新或网络请求;无 token 时分别返回 `None` / `null` / 空字符串 |
|
|
200
|
-
| `current_aid` | `currentAid` | `CurrentAID()` | 当前 AID 对象 |
|
|
196
|
+
| `load_identity(aid)` | `loadIdentity(aid)` | `LoadIdentity(aid)` | 在 `no_identity` / `closed` 状态加载身份 |
|
|
197
|
+
| `state` | `state` | `ConnectionState()` | 九态公开状态 |
|
|
198
|
+
| `gateway_url` | `gatewayUrl` | `GetGatewayURL()` | 当前连接的 Gateway URL(只读,自动发现,连接前为空) |
|
|
199
|
+
| `get_access_token()` | `getAccessToken()` | `GetAccessToken()` | 同步读取后台刷新任务维护的当前 access token;不发起认证、刷新或网络请求;无 token 时分别返回 `None` / `null` / 空字符串 |
|
|
200
|
+
| `current_aid` | `currentAid` | `CurrentAID()` | 当前 AID 对象 |
|
|
201
201
|
| `aid` | `aid` | `AID()` | 当前 AID 字符串 |
|
|
202
202
|
| `has_identity` | `hasIdentity` | `HasIdentity()` | 是否已加载身份 |
|
|
203
203
|
| `can_sign` | `canSign` | `CanSign()` | 是否可签名 |
|
|
204
204
|
| `can_connect` | `canConnect` | `CanConnect()` | 是否可连接 |
|
|
205
205
|
| `can_send` / `is_ready` | `canSend` / `isReady` | `CanSend()` / `IsReady()` | 是否可发送 |
|
|
206
|
-
| `is_online` | `isOnline` | `IsOnline()` | 是否处于在线或重连相关状态 |
|
|
207
|
-
| `is_closed` | `isClosed` | `IsClosed()` | 是否已关闭 |
|
|
208
|
-
|
|
209
|
-
`get_access_token()` / `getAccessToken()` / `GetAccessToken()` 面向需要自行发起 HTTP 数据面请求的应用。返回值是调用时刻的缓存快照,不是可长期持有的凭据:应在每次需要 AID Bearer 的请求前重新读取,不能持续复用 `authenticate()` 首次返回的字符串。getter 本身不会主动刷新,也不独立检查 token 是否临近过期;其有效性依赖客户端后台刷新任务正常运行。返回空值时应先完成身份加载和认证/连接,且不得发送空的 `Authorization` 头。Storage VFS 和 Group FS 等 SDK 门面会自行编排其数据面请求,普通应用优先使用门面。Bearer、资源域 ticket、跨 issuer 下载和 401/403 重试边界见 [Storage RPC 手册:数据面 HTTP 下载流程](09-storage-rpc-manual.md#数据面-http-下载流程)。
|
|
206
|
+
| `is_online` | `isOnline` | `IsOnline()` | 是否处于在线或重连相关状态 |
|
|
207
|
+
| `is_closed` | `isClosed` | `IsClosed()` | 是否已关闭 |
|
|
208
|
+
|
|
209
|
+
`get_access_token()` / `getAccessToken()` / `GetAccessToken()` 面向需要自行发起 HTTP 数据面请求的应用。返回值是调用时刻的缓存快照,不是可长期持有的凭据:应在每次需要 AID Bearer 的请求前重新读取,不能持续复用 `authenticate()` 首次返回的字符串。getter 本身不会主动刷新,也不独立检查 token 是否临近过期;其有效性依赖客户端后台刷新任务正常运行。返回空值时应先完成身份加载和认证/连接,且不得发送空的 `Authorization` 头。Storage VFS 和 Group FS 等 SDK 门面会自行编排其数据面请求,普通应用优先使用门面。Bearer、资源域 ticket、跨 issuer 下载和 401/403 重试边界见 [Storage RPC 手册:数据面 HTTP 下载流程](09-storage-rpc-manual.md#数据面-http-下载流程)。
|
|
210
210
|
|
|
211
211
|
状态值:
|
|
212
212
|
|
|
@@ -220,35 +220,35 @@ retry_backoff / reconnecting / connection_failed / closed
|
|
|
220
220
|
Python:
|
|
221
221
|
|
|
222
222
|
```python
|
|
223
|
-
auth = await client.authenticate()
|
|
224
|
-
await client.connect({"auto_reconnect": True})
|
|
223
|
+
auth = await client.authenticate()
|
|
224
|
+
await client.connect({"auto_reconnect": True})
|
|
225
225
|
await client.disconnect()
|
|
226
226
|
await client.close()
|
|
227
227
|
```
|
|
228
228
|
|
|
229
229
|
说明:
|
|
230
230
|
|
|
231
|
-
- `authenticate()` 只取 token,不建立业务会话。
|
|
232
|
-
- `connect()` 可从 `standby` 自动认证并进入 `ready`。
|
|
233
|
-
- `disconnect()` 断开当前传输连接,对象仍可重新连接。
|
|
234
|
-
- `close()` 关闭连接和后台任务;之后只能重新加载身份再复用。
|
|
231
|
+
- `authenticate()` 只取 token,不建立业务会话。
|
|
232
|
+
- `connect()` 可从 `standby` 自动认证并进入 `ready`。
|
|
233
|
+
- `disconnect()` 断开当前传输连接,对象仍可重新连接。
|
|
234
|
+
- `close()` 关闭连接和后台任务;之后只能重新加载身份再复用。
|
|
235
235
|
|
|
236
236
|
四个 SDK 的公开构造入口均已对齐为“无参或 AID 对象”。调试、TLS、根证书、device_id、slot_id 等配置由 `AIDStore` 传递到 AID,再由 `AUNClient` 继承;连接级选项只传给 `connect()`。
|
|
237
237
|
|
|
238
238
|
### RPC
|
|
239
239
|
|
|
240
240
|
```python
|
|
241
|
-
result = await client.call("message.send", {
|
|
242
|
-
"to": "bob.agentid.pub",
|
|
243
|
-
"device_id": "bob-phone",
|
|
244
|
-
"ttl": 300,
|
|
245
|
-
"payload": {"type": "text", "text": "hello"},
|
|
246
|
-
})
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
`message.send` 可选传入目标 `device_id`,非空时只投递该设备;`to_device_id` 仅为兼容别名,规范字段优先。目标无效时发送失败且不会退化为全设备投递。这里的 `device_id` 是接收目标,`message.pull` / `message.ack` 的同名字段则表示当前消费设备。
|
|
250
|
-
|
|
251
|
-
`ttl` 单位为秒:缺省或 `<=0` 保持持久化;正数钳制到 1–3600 秒,只存于消息服务内存且不分配持久 `seq`。四个 SDK 自动维护 volatile 游标、去重及 ACK;返回和事件中的 `storage`、`volatile_cursor`、`created_at`、`expires_at` 可用于识别此类消息。
|
|
241
|
+
result = await client.call("message.send", {
|
|
242
|
+
"to": "bob.agentid.pub",
|
|
243
|
+
"device_id": "bob-phone",
|
|
244
|
+
"ttl": 300,
|
|
245
|
+
"payload": {"type": "text", "text": "hello"},
|
|
246
|
+
})
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
`message.send` 可选传入目标 `device_id`,非空时只投递该设备;`to_device_id` 仅为兼容别名,规范字段优先。目标无效时发送失败且不会退化为全设备投递。这里的 `device_id` 是接收目标,`message.pull` / `message.ack` 的同名字段则表示当前消费设备。
|
|
250
|
+
|
|
251
|
+
`ttl` 单位为秒:缺省或 `<=0` 保持持久化;正数钳制到 1–3600 秒,只存于消息服务内存且不分配持久 `seq`。四个 SDK 自动维护 volatile 游标、去重及 ACK;返回和事件中的 `storage`、`volatile_cursor`、`created_at`、`expires_at` 可用于识别此类消息。
|
|
252
252
|
|
|
253
253
|
常用 meta RPC 直接透传:
|
|
254
254
|
|
|
@@ -313,14 +313,14 @@ headers = client.get_protected_headers()
|
|
|
313
313
|
| Python | TS/JS | Go | 说明 |
|
|
314
314
|
|--------|-------|----|------|
|
|
315
315
|
| `AIDStore.upload_agent_md(aid, content=None)` | `store.uploadAgentMd(aid, content?)` | `store.UploadAgentMD(ctx, aid, content...)` | 发布指定本地 AID 的 agent.md |
|
|
316
|
-
| `AIDStore.download_agent_md(aid)` | `store.downloadAgentMd(aid)` | `store.DownloadAgentMD(ctx, aid)` | 下载并验签 |
|
|
317
|
-
| `AIDStore.check_agent_md(aid)` | `store.checkAgentMd(aid)` | `store.CheckAgentMD(ctx, aid, maxUnsyncedDays...)` | 检查一致性 |
|
|
318
|
-
|
|
319
|
-
说明:
|
|
320
|
-
|
|
321
|
-
- agent.md 上传、下载和检查入口都在 `AIDStore`;`AUNClient` 不再暴露上传入口。
|
|
322
|
-
- 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
|
|
323
|
-
- SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
|
|
316
|
+
| `AIDStore.download_agent_md(aid)` | `store.downloadAgentMd(aid)` | `store.DownloadAgentMD(ctx, aid)` | 下载并验签 |
|
|
317
|
+
| `AIDStore.check_agent_md(aid)` | `store.checkAgentMd(aid)` | `store.CheckAgentMD(ctx, aid, maxUnsyncedDays...)` | 检查一致性 |
|
|
318
|
+
|
|
319
|
+
说明:
|
|
320
|
+
|
|
321
|
+
- agent.md 上传、下载和检查入口都在 `AIDStore`;`AUNClient` 不再暴露上传入口。
|
|
322
|
+
- 上传要求目标 AID 已在本地加载且私钥有效;SDK 会对正文签名,并通过 `AuthFlow` 获取或复用该 AID 的 access_token。
|
|
323
|
+
- SDK 发起 GET 时只发送 `Accept: text/markdown`,不主动发送 `If-None-Match` / `If-Modified-Since`。如果服务端异常返回 304,本地有内容则复用;无内容时再发一次无条件 GET。
|
|
324
324
|
- SDK 会自动从 Gateway `_meta.agent_md_etags` 和信封 `agent_md` 观察远端版本;`requester`、`peer`、`group` 是标准角色键,`receiver`、`target`、`to`、`sender`、`from` 是兼容别名。
|
|
325
325
|
- `Accept: text/markdown` 与 agent.md 的 YAML frontmatter + Markdown 格式兼容;agent.md 仍是 Markdown 媒体类型上的结构化约定。
|
|
326
326
|
|
|
@@ -334,39 +334,41 @@ headers = client.get_protected_headers()
|
|
|
334
334
|
|
|
335
335
|
| 能力 | Python | TS/JS | Go | 说明 |
|
|
336
336
|
|------|--------|-------|----|------|
|
|
337
|
-
| Storage VFS | `client.storage` | `client.storage` | `client.Storage()` | 类 POSIX 文件操作;上传自动选择 inline / session / 秒传并统一 Content-Type,下载自动选择 inline / ticket;支持 `touch`、`find`、`du`、`df`、ACL/token/软链/挂载门面 |
|
|
337
|
+
| Storage VFS | `client.storage` | `client.storage` | `client.Storage()` | 类 POSIX 文件操作;上传自动选择 inline / session / 秒传并统一 Content-Type,下载自动选择 inline / ticket;支持 `touch`、`find`、`du`、`df`、ACL/token/软链/挂载门面 |
|
|
338
338
|
| Collab | `client.collab` | `client.collab` | `client.Collab()` | 版本化文档、标签、`gc` / `reflog` / `revert` |
|
|
339
339
|
| Group FS | `client.group.fs` | `client.group.fs` | `client.Group().FS()` | POSIX 风格群文件系统;`ls/find/stat/lstat/mkdir/rm/cp/mv/df/mount/umount`,以及 `set_acl/remove_acl/get_acl/list_acl` 角色 ACL 门面,上传下载数据面由 SDK 编排 |
|
|
340
340
|
|
|
341
|
-
四个 SDK 的 Storage VFS 与 Group FS 上传门面在未显式指定 `content_type` / `contentType` 时,先按实际字节识别 PNG、JPEG、GIF、BMP、WebP、PDF、gzip、ZIP、MP4、JSON、HTML 和 UTF-8 文本,再回退文件扩展名及 `application/octet-stream`。session 上传会把同一 MIME 值用于 create session、HTTP PUT 的 `Content-Type` 和 complete;session 响应中的其它 header 会保留,Content-Type 会替换为该 MIME。OSS 预签名 URL 会校验签名头,应用绕过 VFS 时缺少或改写该请求头可能直接收到 OSS 403。
|
|
342
|
-
|
|
343
|
-
群文件系统路径统一使用 `group_aid:/...`,成员数据区使用 `group_aid:/memberdata/{member_ref}/...`。SDK 不拼接真实 storage 路径,`memberdata` 到成员 `group_data/{group_aid}` 的映射只在服务端完成。群自有区写入允许当前 `group_aid` 证书签名;成员角色中 `owner/admin` 默认可写群自有区,`member` 默认不可写。`owner/admin` 可通过 `group.fs.set_acl` 对特定业务目录授予 `role:member` 的 `rw` 权限,用于后续群协作场景;`rw` 只允许创建/写入,不授予删除、移动、重命名权限。`rwd` 是 storage 内部权限位,SDK/RPC 对外按 POSIX 视图显示为 `rwx`,不给删除/移动/重命名权限时只使用 `rw`。`.group/` 是系统控制目录,默认给 `owner/admin` 写权限,用于群公告、群规则、入群要求附件,不能向 `role:member` 授权。老群如果缺少 `.group/` 默认 ACL,group 服务会在该群首次被 RPC 访问时 best-effort 触发 namespace/ACL lazy repair;ACL 同步全部成功后才记录本进程已检查,失败会在后续访问继续重试。JS 浏览器版上传中 `string` 默认表示文本内容,Node 本地路径需显式 `sourceType: "path"`、`localPath: true` 或 `local:` 前缀;Python/TS/Go 默认把 `string` 当本地路径。
|
|
341
|
+
四个 SDK 的 Storage VFS 与 Group FS 上传门面在未显式指定 `content_type` / `contentType` 时,先按实际字节识别 PNG、JPEG、GIF、BMP、WebP、PDF、gzip、ZIP、MP4、JSON、HTML 和 UTF-8 文本,再回退文件扩展名及 `application/octet-stream`。session 上传会把同一 MIME 值用于 create session、HTTP PUT 的 `Content-Type` 和 complete;session 响应中的其它 header 会保留,Content-Type 会替换为该 MIME。OSS 预签名 URL 会校验签名头,应用绕过 VFS 时缺少或改写该请求头可能直接收到 OSS 403。
|
|
342
|
+
|
|
343
|
+
群文件系统路径统一使用 `group_aid:/...`,成员数据区使用 `group_aid:/memberdata/{member_ref}/...`。SDK 不拼接真实 storage 路径,`memberdata` 到成员 `group_data/{group_aid}` 的映射只在服务端完成。群自有区写入允许当前 `group_aid` 证书签名;成员角色中 `owner/admin` 默认可写群自有区,`member` 默认不可写。`owner/admin` 可通过 `group.fs.set_acl` 对特定业务目录授予 `role:member` 的 `rw` 权限,用于后续群协作场景;`rw` 只允许创建/写入,不授予删除、移动、重命名权限。`rwd` 是 storage 内部权限位,SDK/RPC 对外按 POSIX 视图显示为 `rwx`,不给删除/移动/重命名权限时只使用 `rw`。`.group/` 是系统控制目录,默认给 `owner/admin` 写权限,用于群公告、群规则、入群要求附件,不能向 `role:member` 授权。老群如果缺少 `.group/` 默认 ACL,group 服务会在该群首次被 RPC 访问时 best-effort 触发 namespace/ACL lazy repair;ACL 同步全部成功后才记录本进程已检查,失败会在后续访问继续重试。JS 浏览器版上传中 `string` 默认表示文本内容,Node 本地路径需显式 `sourceType: "path"`、`localPath: true` 或 `local:` 前缀;Python/TS/Go 默认把 `string` 当本地路径。
|
|
344
344
|
|
|
345
345
|
### 消息与群组门面
|
|
346
346
|
|
|
347
347
|
| 能力 | Python | TS/JS | Go | 说明 |
|
|
348
348
|
|------|--------|-------|----|------|
|
|
349
|
-
| Message | `client.message` | `client.message` | `client.Message()` | 消息便利方法:`send()`、`pull()`、`history()`、`ack()`、`recall()`、`queryOnline()` |
|
|
349
|
+
| Message | `client.message` | `client.message` | `client.Message()` | 消息便利方法:`send()`、`pull()`、`history()`、`ack()`、`read()`、`read_state/readState/ReadState`、`recall()`、`queryOnline()` |
|
|
350
350
|
| Message Thought | `client.message.thought` | `client.message.thought` | `client.Message().Thought()` | P2P 思考内容:`put()`、`get()`(不持久化、不分配 seq、强制 E2EE) |
|
|
351
|
-
| Group | `client.group` | `client.group` | `client.Group()` | 群组便利方法:`create()`、`send()`、`pull()`、`history()`、`ack()`、群管理方法、群查询方法、群设置便利方法 |
|
|
352
|
-
| Group Thought | `client.group.thought` | `client.group.thought` | `client.Group().Thought()` | 群思考内容:`put()`、`get()`(不持久化、不分配 seq、强制 E2EE) |
|
|
353
|
-
| Offline Push | `client.push` | — | — | Python SDK:登记/注销当前安装的 `push_notify_aid`,Push Server 处理批次后 ACK |
|
|
354
|
-
| Stream | `client.stream` | `client.stream` | — | 流式数据:`createStream()`、`sendChunk()`、`endStream()`、`subscribeStream()` |
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
|
365
|
-
|
|
366
|
-
|
|
|
367
|
-
|
|
|
368
|
-
|
|
369
|
-
|
|
351
|
+
| Group | `client.group` | `client.group` | `client.Group()` | 群组便利方法:`create()`、`send()`、`pull()`、`history()`、`ack()`、`read()`、`read_state/readState/ReadState`、群管理方法、群查询方法、群设置便利方法 |
|
|
352
|
+
| Group Thought | `client.group.thought` | `client.group.thought` | `client.Group().Thought()` | 群思考内容:`put()`、`get()`(不持久化、不分配 seq、强制 E2EE) |
|
|
353
|
+
| Offline Push | `client.push` | — | — | Python SDK:登记/注销当前安装的 `push_notify_aid`,Push Server 处理批次后 ACK |
|
|
354
|
+
| Stream | `client.stream` | `client.stream` | — | 流式数据:`createStream()`、`sendChunk()`、`endStream()`、`subscribeStream()` |
|
|
355
|
+
|
|
356
|
+
业务已读与投递 ACK 独立。Python 使用 `read/read_state`,TS/JS 使用 `read/readState`,Go 使用 `Read/ReadState`;四个已读门面保留显式 `None/null/nil` 字段交服务端校验。只有省略 `states` 时兼容顶层单条,显式空/null 批次拒绝;写入回执不是共享汇总。完整字段与事件契约见 [Message RPC](09-message-rpc-manual.md#messageread) 和 [Group RPC](09-group-rpc-manual.md#groupread)。
|
|
357
|
+
|
|
358
|
+
`message.history` 和 `group.history` 使用 `before_seq` 排他上界向左翻页,下一页直接使用响应的 `next_before_seq`。History 只读,不推进实时 A/T/H、不 ACK、不触发实时消息事件。实时 Push 的最新页同步完全由 SDK 内部自动执行,再按 A/T/H 决定是否补一页 Forward;没有需要应用调用的 Tail 方法或参数。
|
|
359
|
+
|
|
360
|
+
消息回调只提供页级语义:单个 Tail/Forward 响应页内按 seq 升序、同 seq 最多一次;跨 Push/Tail/Forward 页可能重复或乱序。A/T/H 不能用于判断某条消息是否已经回调。全局去重与排序不是 SDK 业务逻辑保证;应用仓库应以 `(namespace, message_id)` 做唯一键,并按 `seq` 排序。SDK 的进程内折叠仅是 best-effort 优化。
|
|
361
|
+
|
|
362
|
+
**群成员管理与状态方法**:
|
|
363
|
+
|
|
364
|
+
| 语义 | Python | TS/JS | Go |
|
|
365
|
+
|------|--------|-------|----|
|
|
366
|
+
| 屏蔽/取消屏蔽 | `block()` / `unblock()` | `block()` / `unblock()` | `Block()` / `Unblock()` |
|
|
367
|
+
| 获取屏蔽列表 | `get_blocklist()` | `getBlocklist()` | `GetBlocklist()` |
|
|
368
|
+
| 上报自己的群内状态 | `report_status()` | `reportStatus()` | `ReportStatus()` |
|
|
369
|
+
| 获取所有成员状态 | `get_member_statuses()` | `getMemberStatuses()` | `GetMemberStatuses()` |
|
|
370
|
+
|
|
371
|
+
屏蔽与禁言相互独立且可以同时设置。屏蔽成员仍可向群发送消息,但不接收群消息;禁言成员不能向群发送消息,但仍可接收。群主转让是两阶段流程:旧群主先生成授权并调用 `group.transfer_owner`,新群主 SDK 收到 `owner_transfer_rekey_pending` 后生成新群密钥并调用 `group.complete_transfer`。四个 SDK 会在新群主在线或重连恢复时自动尝试第二阶段,也保留 `completeTransfer`(Python 为 `complete_transfer`,Go 为 `CompleteTransfer`)供显式恢复;发起调用返回 `pending_rekey`,不表示转让已经完成。
|
|
370
372
|
|
|
371
373
|
**群查询方法**:`GroupFacade` 提供三个查询方法,适用不同场景:
|
|
372
374
|
|
|
@@ -374,20 +376,20 @@ headers = client.get_protected_headers()
|
|
|
374
376
|
- `getInfo()` — 查询群组信息(扁平化格式,提升常用字段到顶层),**推荐外部使用**
|
|
375
377
|
- `info()` — 查询群组详细信息(带权限控制,非成员只能看公开群,成员能看 seq/epoch 等运行时状态)
|
|
376
378
|
|
|
377
|
-
**群设置便利方法**:`GroupFacade` 提供向后兼容的便利方法:
|
|
378
|
-
|
|
379
|
-
- `getAnnouncement()` / `updateAnnouncement()` — 群公告
|
|
380
|
-
- `getRules()` / `updateRules()` — 群规则
|
|
381
|
-
- `getJoinRequirements()` / `updateJoinRequirements()` — 入群要求
|
|
382
|
-
- `getSettingWithIndex()` / `updateSettingWithIndex()` — 通用文档型 indexed setting(Python 为 `get_setting_with_index()` / `update_setting_with_index()`,Go 为 `GetSettingWithIndex()` / `UpdateSettingWithIndex()`)
|
|
383
|
-
|
|
384
|
-
读取方法优先返回 SDK 本地缓存;本地没有对应值时才读取相应 settings 做初始化。便利读取从服务端拿到 canonical `group_aid` 后,会同时以 canonical `group_aid` 和本次入参 `group_id` 写入 settings cache,避免 legacy/base `group_id` 下一次读取直接 cache miss。即使 `checkGroupIndex` 观察到远端 etag 与本地 etag 不一致,`getAnnouncement()` / `getRules()` / `getJoinRequirements()` / `getSettingWithIndex()` 也不会自动拉取远端版本覆盖本地缓存。`updateAnnouncement()` / `updateRules()` / `updateJoinRequirements()` / `updateSettingWithIndex()` 属于 indexed 写入,内部会调用 `updateGroupIndex` 生成签名 `group.index` 并带 `expected_index_etag` CAS 提交。
|
|
385
|
-
|
|
386
|
-
`getSettingWithIndex()` / `updateSettingWithIndex()` 比三对预定义方法多一个 `keyName`(Python 可传 `key_name`,Go 同时接受 `keyName` / `key_name`)。SDK 会生成 `{keyName}.content` 和 `{keyName}.attachments` 两个 settings key;`keyName` 只能是受控文档名(`^[A-Za-z][A-Za-z0-9_-]{0,63}$`,且不能使用 `join` 等保留前缀)。`getRules()` / `updateRules()` 与 `getAnnouncement()` / `updateAnnouncement()` 是该通用方法在 `rules`、`announcement` 上的薄封装;`getJoinRequirements()` / `updateJoinRequirements()` 保持结构化 schema,不改成 `join.content`。
|
|
387
|
-
|
|
388
|
-
这些 `update*` 便利方法只更新群设置元数据:`updateRules()` / `updateAnnouncement()` / `updateSettingWithIndex()` 写入 `*.content` 与 `*.attachments`,`updateJoinRequirements()` 写入 `join.mode` / `join.question` / `join.auto_approve_patterns` / `join.max_pending` / `join.attachments`。`attachments` 只保存稳定引用,附件实体应先上传到 group.fs 群自有区,推荐路径为 `group_aid:/.group/attachments/{rules|announcement|join|<keyName>}/...`。
|
|
389
|
-
|
|
390
|
-
**Group Index 高级同步方法**:`group.index` 是 SDK 内部签名 manifest,用于记录群公告、群规则、入群要求及附件稳定引用的版本。SDK 观察 `_meta.group_indexes` 后只记录远端 etag;etag 不一致只表示本地与观察到的远端版本不同,可能是远端更新,也可能是本地有未提交修改。应用层需要显式选择 pull 远端或 push 本地。
|
|
379
|
+
**群设置便利方法**:`GroupFacade` 提供向后兼容的便利方法:
|
|
380
|
+
|
|
381
|
+
- `getAnnouncement()` / `updateAnnouncement()` — 群公告
|
|
382
|
+
- `getRules()` / `updateRules()` — 群规则
|
|
383
|
+
- `getJoinRequirements()` / `updateJoinRequirements()` — 入群要求
|
|
384
|
+
- `getSettingWithIndex()` / `updateSettingWithIndex()` — 通用文档型 indexed setting(Python 为 `get_setting_with_index()` / `update_setting_with_index()`,Go 为 `GetSettingWithIndex()` / `UpdateSettingWithIndex()`)
|
|
385
|
+
|
|
386
|
+
读取方法优先返回 SDK 本地缓存;本地没有对应值时才读取相应 settings 做初始化。便利读取从服务端拿到 canonical `group_aid` 后,会同时以 canonical `group_aid` 和本次入参 `group_id` 写入 settings cache,避免 legacy/base `group_id` 下一次读取直接 cache miss。即使 `checkGroupIndex` 观察到远端 etag 与本地 etag 不一致,`getAnnouncement()` / `getRules()` / `getJoinRequirements()` / `getSettingWithIndex()` 也不会自动拉取远端版本覆盖本地缓存。`updateAnnouncement()` / `updateRules()` / `updateJoinRequirements()` / `updateSettingWithIndex()` 属于 indexed 写入,内部会调用 `updateGroupIndex` 生成签名 `group.index` 并带 `expected_index_etag` CAS 提交。
|
|
387
|
+
|
|
388
|
+
`getSettingWithIndex()` / `updateSettingWithIndex()` 比三对预定义方法多一个 `keyName`(Python 可传 `key_name`,Go 同时接受 `keyName` / `key_name`)。SDK 会生成 `{keyName}.content` 和 `{keyName}.attachments` 两个 settings key;`keyName` 只能是受控文档名(`^[A-Za-z][A-Za-z0-9_-]{0,63}$`,且不能使用 `join` 等保留前缀)。`getRules()` / `updateRules()` 与 `getAnnouncement()` / `updateAnnouncement()` 是该通用方法在 `rules`、`announcement` 上的薄封装;`getJoinRequirements()` / `updateJoinRequirements()` 保持结构化 schema,不改成 `join.content`。
|
|
389
|
+
|
|
390
|
+
这些 `update*` 便利方法只更新群设置元数据:`updateRules()` / `updateAnnouncement()` / `updateSettingWithIndex()` 写入 `*.content` 与 `*.attachments`,`updateJoinRequirements()` 写入 `join.mode` / `join.question` / `join.auto_approve_patterns` / `join.max_pending` / `join.attachments`。`attachments` 只保存稳定引用,附件实体应先上传到 group.fs 群自有区,推荐路径为 `group_aid:/.group/attachments/{rules|announcement|join|<keyName>}/...`。
|
|
391
|
+
|
|
392
|
+
**Group Index 高级同步方法**:`group.index` 是 SDK 内部签名 manifest,用于记录群公告、群规则、入群要求及附件稳定引用的版本。SDK 观察 `_meta.group_indexes` 后只记录远端 etag;etag 不一致只表示本地与观察到的远端版本不同,可能是远端更新,也可能是本地有未提交修改。应用层需要显式选择 pull 远端或 push 本地。
|
|
391
393
|
|
|
392
394
|
| 语义 | Python | TS/JS | Go | 说明 |
|
|
393
395
|
|------|--------|-------|----|------|
|
|
@@ -395,7 +397,7 @@ headers = client.get_protected_headers()
|
|
|
395
397
|
| 显式 pull 远端 index | `client.group.get_group_index({...})` | `client.group.getGroupIndex({...})` | `client.Group().GetGroupIndex(ctx, params)` | 调用 `group.get_settings(keys=["group.index"])` 摘取 manifest,并按 entry etag 只拉取变化的 db settings 写入本地缓存 |
|
|
396
398
|
| 显式 push 本地 indexed settings + index | `client.group.update_group_index({...})` | `client.group.updateGroupIndex({...})` | `client.Group().UpdateGroupIndex(ctx, params)` | 先读取当前 index 得到 `expected_index_etag`,在远端基线上合并本地变更,生成签名 `group.index` 后 CAS push |
|
|
397
399
|
|
|
398
|
-
`getGroupIndex` pull 验签成功后会持久化本地视图。Python / TypeScript(Node) / Go 使用 `{aun_path}/AIDs/{local_aid}/groups/{group_aid}/index.jsonl` 保存签名 `group.index.body` 原文,并用同目录 `group-index-cache.json` 保存 `local_etag`、`remote_meta`、`settings`、`entry_etags` 等 cache envelope。浏览器 JavaScript 使用 IndexedDB `group_index_cache` store 的等价记录,按 `local_aid + group_aid` 隔离。普通便利读取可额外写入本次请求 `group_id` 的 settings cache alias;签名正文和 `getGroupIndex` 视图仍以 canonical `group_aid` 为准。不要使用 `{aun_path}/AIDs/{group_aid}/`,也不要使用旧单文件 `group-index.json`。
|
|
400
|
+
`getGroupIndex` pull 验签成功后会持久化本地视图。Python / TypeScript(Node) / Go 使用 `{aun_path}/AIDs/{local_aid}/groups/{group_aid}/index.jsonl` 保存签名 `group.index.body` 原文,并用同目录 `group-index-cache.json` 保存 `local_etag`、`remote_meta`、`settings`、`entry_etags` 等 cache envelope。浏览器 JavaScript 使用 IndexedDB `group_index_cache` store 的等价记录,按 `local_aid + group_aid` 隔离。普通便利读取可额外写入本次请求 `group_id` 的 settings cache alias;签名正文和 `getGroupIndex` 视图仍以 canonical `group_aid` 为准。不要使用 `{aun_path}/AIDs/{group_aid}/`,也不要使用旧单文件 `group-index.json`。
|
|
399
401
|
|
|
400
402
|
#### checkGroupIndex — 检查 index 同步状态
|
|
401
403
|
|
|
@@ -557,14 +559,14 @@ fmt.Printf("拉取成功,etag: %s\n", result["meta"].(map[string]any)["etag"])
|
|
|
557
559
|
| `last_modified` | int | ❌ | 时间戳毫秒(默认 `Date.now()` / `time.time()*1000`) |
|
|
558
560
|
| `max_attempts` | int | ❌ | CAS 冲突最大重试次数(默认 2) |
|
|
559
561
|
|
|
560
|
-
**支持的 indexed settings keys**:
|
|
561
|
-
|
|
562
|
-
- `rules.content` / `rules.attachments` — 群规则及附件
|
|
563
|
-
- `announcement.content` / `announcement.attachments` — 群公告及附件
|
|
564
|
-
- `join.mode` / `join.question` / `join.auto_approve_patterns` / `join.max_pending` / `join.attachments` — 入群要求及附件
|
|
565
|
-
- `{keyName}.content` / `{keyName}.attachments` — 通用文档型 indexed setting;`keyName` 需满足 `^[A-Za-z][A-Za-z0-9_-]{0,63}$`,且不能使用 `join` 等保留前缀
|
|
566
|
-
|
|
567
|
-
**返回值**:
|
|
562
|
+
**支持的 indexed settings keys**:
|
|
563
|
+
|
|
564
|
+
- `rules.content` / `rules.attachments` — 群规则及附件
|
|
565
|
+
- `announcement.content` / `announcement.attachments` — 群公告及附件
|
|
566
|
+
- `join.mode` / `join.question` / `join.auto_approve_patterns` / `join.max_pending` / `join.attachments` — 入群要求及附件
|
|
567
|
+
- `{keyName}.content` / `{keyName}.attachments` — 通用文档型 indexed setting;`keyName` 需满足 `^[A-Za-z][A-Za-z0-9_-]{0,63}$`,且不能使用 `join` 等保留前缀
|
|
568
|
+
|
|
569
|
+
**返回值**:
|
|
568
570
|
|
|
569
571
|
```python
|
|
570
572
|
{
|
|
@@ -633,16 +635,16 @@ console.log(`推送成功,新 etag: ${result._meta.group_indexes['g-team.agent
|
|
|
633
635
|
|
|
634
636
|
```go
|
|
635
637
|
// Go - 更新入群要求
|
|
636
|
-
result, err := client.Group().UpdateGroupIndex(ctx, map[string]any{
|
|
637
|
-
"group_id": "g-team.agentid.pub",
|
|
638
|
-
"settings": map[string]any{
|
|
639
|
-
"join.mode": "approval",
|
|
640
|
-
"join.question": "你是如何知道本群的?",
|
|
641
|
-
"join.attachments": []any{
|
|
642
|
-
map[string]any{"type": "group.fs", "path": "/.group/attachments/join/guide.pdf"},
|
|
643
|
-
},
|
|
644
|
-
},
|
|
645
|
-
})
|
|
638
|
+
result, err := client.Group().UpdateGroupIndex(ctx, map[string]any{
|
|
639
|
+
"group_id": "g-team.agentid.pub",
|
|
640
|
+
"settings": map[string]any{
|
|
641
|
+
"join.mode": "approval",
|
|
642
|
+
"join.question": "你是如何知道本群的?",
|
|
643
|
+
"join.attachments": []any{
|
|
644
|
+
map[string]any{"type": "group.fs", "path": "/.group/attachments/join/guide.pdf"},
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
})
|
|
646
648
|
if err != nil {
|
|
647
649
|
log.Fatal(err)
|
|
648
650
|
}
|
|
@@ -654,41 +656,41 @@ fmt.Printf("推送成功\n")
|
|
|
654
656
|
1. **权限要求**:写入 indexed settings 需要 admin 及以上权限
|
|
655
657
|
2. **签名算法限制**:当前版本仅支持 ECDSA-P256-SHA256,使用其他算法的 AID 无法签名
|
|
656
658
|
3. **CAS 冲突策略**:默认重试 2 次,高并发场景建议增加 `max_attempts`
|
|
657
|
-
4. **`signer` 必须是当前连接身份**:服务端强制校验 `signed_by == actor_aid`,传入其他 AID 会被拒绝
|
|
658
|
-
5. **附件字段只存引用**:`updateRules` / `updateAnnouncement` / `updateSettingWithIndex` 传入 `content` 和 `attachments` 元数据引用;`updateJoinRequirements` 传入结构化入群字段和 `attachments` 元数据引用。附件实体先上传到群自有区,推荐路径为 `group_aid:/.group/attachments/{rules|announcement|join|<keyName>}/...`。群自有区默认允许 `owner/admin` 写入,`member` 默认不可写;`.group/` 不允许授予 `role:member` 写权限。
|
|
659
|
-
|
|
660
|
-
---
|
|
661
|
-
|
|
662
|
-
### 离线推送门面(Python)
|
|
663
|
-
|
|
664
|
-
应用启动时推荐把推送代理和 opaque token 交给 `connect()`:
|
|
665
|
-
|
|
666
|
-
```python
|
|
667
|
-
await client.connect({
|
|
668
|
-
"push_notify_aid": "push.example.com",
|
|
669
|
-
"push_token": token,
|
|
670
|
-
})
|
|
671
|
-
```
|
|
672
|
-
|
|
673
|
-
SDK 先建立 AUN 会话,再调用 `push.register` 登记当前安装。`push_notify_aid` 是 SDK 公共名称,线上 RPC 字段仍是冻结契约中的 `push_server_aid`;它不会被透传给 `auth.connect`。
|
|
674
|
-
|
|
675
|
-
运行期间更换 token 或停止推送时使用 facade:
|
|
676
|
-
|
|
677
|
-
```python
|
|
678
|
-
await client.push.register(push_notify_aid="push.example.com", push_token=new_token)
|
|
679
|
-
await client.push.unregister()
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
- `register()` 可附带 `provider`、`app_id`;身份范围始终由 Gateway 注入的当前 `aid/device_id/slot_id` 决定,调用方不能伪造。
|
|
683
|
-
- `unregister()` 幂等禁用当前安装,不删除历史 delivery。
|
|
684
|
-
- `ack(batch_id, status)` 只供 Push Server 实现使用;V1 status 仅允许 `accepted`、`retryable_failure`、`permanent_failure`。
|
|
685
|
-
- 本轮只修改 Python SDK;Go、TypeScript、JavaScript 尚无专用 Push facade。
|
|
686
|
-
|
|
687
|
-
协议与可靠性边界见 `docs/protocol/15-离线推送通知协议.md`。
|
|
688
|
-
|
|
689
|
-
---
|
|
690
|
-
|
|
691
|
-
## ServiceProxyClient
|
|
659
|
+
4. **`signer` 必须是当前连接身份**:服务端强制校验 `signed_by == actor_aid`,传入其他 AID 会被拒绝
|
|
660
|
+
5. **附件字段只存引用**:`updateRules` / `updateAnnouncement` / `updateSettingWithIndex` 传入 `content` 和 `attachments` 元数据引用;`updateJoinRequirements` 传入结构化入群字段和 `attachments` 元数据引用。附件实体先上传到群自有区,推荐路径为 `group_aid:/.group/attachments/{rules|announcement|join|<keyName>}/...`。群自有区默认允许 `owner/admin` 写入,`member` 默认不可写;`.group/` 不允许授予 `role:member` 写权限。
|
|
661
|
+
|
|
662
|
+
---
|
|
663
|
+
|
|
664
|
+
### 离线推送门面(Python)
|
|
665
|
+
|
|
666
|
+
应用启动时推荐把推送代理和 opaque token 交给 `connect()`:
|
|
667
|
+
|
|
668
|
+
```python
|
|
669
|
+
await client.connect({
|
|
670
|
+
"push_notify_aid": "push.example.com",
|
|
671
|
+
"push_token": token,
|
|
672
|
+
})
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
SDK 先建立 AUN 会话,再调用 `push.register` 登记当前安装。`push_notify_aid` 是 SDK 公共名称,线上 RPC 字段仍是冻结契约中的 `push_server_aid`;它不会被透传给 `auth.connect`。
|
|
676
|
+
|
|
677
|
+
运行期间更换 token 或停止推送时使用 facade:
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
await client.push.register(push_notify_aid="push.example.com", push_token=new_token)
|
|
681
|
+
await client.push.unregister()
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
- `register()` 可附带 `provider`、`app_id`;身份范围始终由 Gateway 注入的当前 `aid/device_id/slot_id` 决定,调用方不能伪造。
|
|
685
|
+
- `unregister()` 幂等禁用当前安装,不删除历史 delivery。
|
|
686
|
+
- `ack(batch_id, status)` 只供 Push Server 实现使用;V1 status 仅允许 `accepted`、`retryable_failure`、`permanent_failure`。
|
|
687
|
+
- 本轮只修改 Python SDK;Go、TypeScript、JavaScript 尚无专用 Push facade。
|
|
688
|
+
|
|
689
|
+
协议与可靠性边界见 `docs/protocol/15-离线推送通知协议.md`。
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
693
|
+
## ServiceProxyClient
|
|
692
694
|
|
|
693
695
|
Service Proxy 用于 provider 通过 AUN 身份暴露本地 HTTP / WebSocket 服务。当前公开封装在 Python SDK 的 `ServiceProxyClient` 中;其它语言可以按 [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) 直接实现同等控制面和隧道消息。
|
|
694
696
|
|
|
@@ -753,113 +755,116 @@ sub.unsubscribe()
|
|
|
753
755
|
| `connection.error` | 连接或重连错误 |
|
|
754
756
|
| `token.refreshed` | token 刷新完成 |
|
|
755
757
|
| `token.refresh_exhausted` | refresh_token 缺失、过期或刷新链耗尽,SDK 已清理本地 token 并等待重新登录 |
|
|
756
|
-
| `message.received` | 收到 P2P 消息 |
|
|
757
|
-
| `message.
|
|
758
|
-
| `
|
|
759
|
-
| `
|
|
760
|
-
| `group.
|
|
761
|
-
| `group.
|
|
762
|
-
| `group.
|
|
763
|
-
| `
|
|
764
|
-
| `
|
|
765
|
-
| `
|
|
766
|
-
| `
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
|
792
|
-
|
|
793
|
-
| `
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
-
|
|
801
|
-
|
|
802
|
-
`
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
| `
|
|
812
|
-
|
|
813
|
-
| `
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
"
|
|
829
|
-
"
|
|
830
|
-
"
|
|
831
|
-
"
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
"
|
|
843
|
-
"
|
|
844
|
-
"
|
|
845
|
-
"
|
|
846
|
-
"
|
|
847
|
-
"
|
|
848
|
-
"
|
|
849
|
-
"
|
|
850
|
-
"
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
758
|
+
| `message.received` | 收到 P2P 消息 |
|
|
759
|
+
| `message.read_changed` / `group.read_changed` | 同一 AID 共享已读位置提交并前进;owner 必须为当前身份、scope 匹配原事件 |
|
|
760
|
+
| `read_state_sync` | SDK 本地已读补偿完成,含本轮 `owner_aid`、去重 `states` 和逐项 `errors`;连接返回不表示补偿完成 |
|
|
761
|
+
| `message.undecryptable` | P2P E2EE 解密失败 |
|
|
762
|
+
| `group.message_created` | 收到群消息 |
|
|
763
|
+
| `group.changed` | 群组事件 |
|
|
764
|
+
| `group.message_recalled` | 群消息撤回 |
|
|
765
|
+
| `group.message_undecryptable` | 群 E2EE 解密失败 |
|
|
766
|
+
| `sync.started` / `sync.progress` / `sync.stopped` | Pull 同步生命周期 |
|
|
767
|
+
| `delivery.changed` | SDK 本地批量交付完成通知,用于合并应用层 UI 刷新 |
|
|
768
|
+
| `push.offline_message` | Push Server 收到离线唤醒批次;普通终端无需订阅 |
|
|
769
|
+
| `storage.object_changed` | Storage 对象变更事件透传 |
|
|
770
|
+
|
|
771
|
+
服务端不产生 `message.ack` 通知;SDK 保留的透传订阅不能作为可依赖的 ACK 或对端已读事件。已读事件只向 owner AID 分发且不进入 Pull 队列;重连查询补偿不受消息 `background_sync=false` 影响。身份切换按代际隔离旧缓存与补偿完成事件,A→B→A 也不能接收旧代际结果;原在途 RPC 仍可返回。详见 [跨设备已读未读 SDK 集成指南](../跨设备已读未读SDK集成指南.md)。
|
|
772
|
+
|
|
773
|
+
`message.received` 的应用层消息保留 `sender_device_id`,表示发送方设备;需要只回复原设备时,将该值作为下一次 `message.send.device_id`。volatile 消息还保留 `storage="volatile"`、`volatile_cursor`、`created_at`、`expires_at`,不含持久 `seq`;内部 slot 不会暴露给应用。
|
|
774
|
+
|
|
775
|
+
### delivery.changed
|
|
776
|
+
|
|
777
|
+
`delivery.changed` 是 Python、Go、JavaScript 和 TypeScript SDK 统一提供的本地事件,用于通知应用层一批消息已经完成交付,可以从业务仓库刷新相关会话 UI。它不替代 `message.received` / `group.message_created`:应用仍应逐条处理和持久化原消息事件,再把 `delivery.changed` 作为批量刷新信号。
|
|
778
|
+
|
|
779
|
+
该事件由 SDK 本地 dispatcher 发布,不是 Gateway 推送事件,不属于 JSON-RPC `event/...` 协议;裸 WebSocket 客户端不会收到它。
|
|
780
|
+
|
|
781
|
+
**Payload**:
|
|
782
|
+
|
|
783
|
+
```json
|
|
784
|
+
{
|
|
785
|
+
"trigger": "pull_drained",
|
|
786
|
+
"changes": [
|
|
787
|
+
{"namespace": "group:team.agentid.pub", "delivered_count": 50},
|
|
788
|
+
{"namespace": "p2p", "delivered_count": 3}
|
|
789
|
+
]
|
|
790
|
+
}
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
| 字段 | 类型 | 说明 |
|
|
794
|
+
|------|------|------|
|
|
795
|
+
| `trigger` | string | `pull_drained`、`push` 或 `inline_push` |
|
|
796
|
+
| `changes` | array | 本次刷新涉及的 namespace,按 `namespace` 稳定排序且不重复 |
|
|
797
|
+
| `changes[].namespace` | string | P2P 固定为 `p2p`;群消息为 `group:<canonical-group-aid>` |
|
|
798
|
+
| `changes[].delivered_count` | integer | 本次合并窗口内已发布到应用层的原消息事件数;不是未读数、服务端页大小或最大 seq |
|
|
799
|
+
|
|
800
|
+
触发与折叠规则:
|
|
801
|
+
|
|
802
|
+
- `pull_drained`:当前 `AUNClient` 上所有已排队或正在执行的 Pull 工作,包括后续分页、continuation、gap fill 和缺失 Sender IK 的延迟补齐,全部排空后最多发布一次。一次通知可以同时包含 P2P 和多个群 namespace,不会按 Pull 页或单个 namespace 分别通知。
|
|
803
|
+
- `push` / `inline_push`:有效实时消息在对应的 `message.received` / `group.message_created` 事件处理完成后通知;同一次有序队列 drain 放行的同类实时消息折叠为一次通知。
|
|
804
|
+
- 有效的 `push` / `inline_push` 可以原子吸收尚未通知的 Pull 变更;这些变更不会在后续 `pull_drained` 中再次通知。
|
|
805
|
+
- 空 Pull、重复或过滤消息、非法 payload、解密失败,以及未带来有效消息交付的 Head 通知,不发布 `delivery.changed`。
|
|
806
|
+
|
|
807
|
+
`delivery.changed` 只表示 UI 刷新时机,不是消息明细或持久化账本。应用仓库仍应按照原消息事件的 `(namespace, message_id)` 做幂等,并按 `seq` 排序。
|
|
808
|
+
|
|
809
|
+
### 消息来源 `source`
|
|
810
|
+
|
|
811
|
+
消息、群消息、群事件、撤回和不可解密事件的 payload 可能带有顶层 `source`,用于区分 SDK 的投递来源:
|
|
812
|
+
|
|
813
|
+
| `source` | 含义 |
|
|
814
|
+
|----------|------|
|
|
815
|
+
| `push` | 普通实时推送 |
|
|
816
|
+
| `pull` | Pull、Tail、Forward 或群事件补洞 |
|
|
817
|
+
| `inline_push` | 已在连续窗口内直接消费的实时载荷 |
|
|
818
|
+
| `pending_retry` | 缺失 Sender IK 后延迟恢复的消息 |
|
|
819
|
+
|
|
820
|
+
直接由应用或无来源内部调用产生的事件不强行添加 `source`。`source` 位于应用事件 payload 顶层,不改变业务 `payload` 内的数据结构;应用应将其视为保留字段。
|
|
821
|
+
|
|
822
|
+
### Pull 同步生命周期
|
|
823
|
+
|
|
824
|
+
四个 SDK 均通过本地 dispatcher 发布以下异步生命周期事件;它们不是 Gateway 的 `event/...` 消息,应用 handler 不会阻塞网络 reader。
|
|
825
|
+
|
|
826
|
+
`sync.started` 在本次 Pull 的第一个 namespace 开始时发布一次:
|
|
827
|
+
|
|
828
|
+
```json
|
|
829
|
+
{
|
|
830
|
+
"run_id": 7,
|
|
831
|
+
"source": "pull",
|
|
832
|
+
"syncing": true,
|
|
833
|
+
"namespaces_pending": 1,
|
|
834
|
+
"namespace": "p2p",
|
|
835
|
+
"received_total": 0,
|
|
836
|
+
"started_at": 1730000000000
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
每完成一页消息或群事件的处理、提交和有序投递,发布一次 `sync.progress`。`page_raw_count` 是本页有效原始条数,`page_pulled_count` 是本页实际投递条数;`pulled_count` / `received_total` 是本次 namespace 的累计投递数。服务端返回合法非负整数时透传 `remaining`,并同步设置 `estimated_remaining`;否则仅在协议可推导时提供估算值。
|
|
841
|
+
|
|
842
|
+
```json
|
|
843
|
+
{
|
|
844
|
+
"run_id": 7,
|
|
845
|
+
"source": "pull",
|
|
846
|
+
"namespace": "p2p",
|
|
847
|
+
"page": 2,
|
|
848
|
+
"page_raw_count": 50,
|
|
849
|
+
"page_pulled_count": 48,
|
|
850
|
+
"pulled_count": 98,
|
|
851
|
+
"received_total": 98,
|
|
852
|
+
"batch_size": 48,
|
|
853
|
+
"has_more": true,
|
|
854
|
+
"remaining": 102,
|
|
855
|
+
"estimated_remaining": 102
|
|
856
|
+
}
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
当所有 namespace、续页、gap fill、ordered drain 和延迟 Sender IK 任务均排空时发布一次 `sync.stopped`。正常终态使用 `reason: "pull_drained"`;异常、取消、断线或连接/身份代次失效使用 `reason: "aborted"`。`sync.stopped` 描述同步生命周期终点,`delivery.changed(trigger="pull_drained")` 只汇总实际投递变化;空 Pull 可以有前者而没有后者。
|
|
860
|
+
|
|
861
|
+
### JS/TS Worker 降级
|
|
862
|
+
|
|
863
|
+
JavaScript 浏览器 SDK 的 Worker 是网络隔离增强:`new Worker()` 同步失败、CSP `worker-src` 阻止、Worker 启动后未及时 `open` 或异步崩溃时,SDK 会销毁 Worker 并回退原生 WebSocket。TypeScript Node.js SDK 在 `worker_threads` 不可用、启动超时或 Network Actor 崩溃时回退本地 transport。两种降级均保留异步 dispatcher 和 Pull 生命周期语义,不要求应用改变消息处理代码。
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
## E2EE 高级 API
|
|
863
868
|
|
|
864
869
|
普通业务无需直接操作 E2EE manager;`message.send` / `group.send` 默认加密并自动解密收到的消息。
|
|
865
870
|
|
|
@@ -881,14 +886,14 @@ JavaScript 浏览器 SDK 的 Worker 是网络隔离增强:`new Worker()` 同
|
|
|
881
886
|
|
|
882
887
|
| 领域 | 手册 | 关键方法 |
|
|
883
888
|
|------|------|----------|
|
|
884
|
-
| 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | `message.send` / `message.pull` / `message.history` / `message.ack` / `message.thought.*` |
|
|
885
|
-
| 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.send` / `group.pull` / `group.history` / `group.v2.*` / `group.fs.*` |
|
|
889
|
+
| 消息 | [09-message-rpc-manual.md](09-message-rpc-manual.md) | `message.send` / `message.pull` / `message.history` / `message.ack` / `message.read` / `message.read_state` / `message.thought.*` |
|
|
890
|
+
| 群组 | [09-group-rpc-manual.md](09-group-rpc-manual.md) | `group.create` / `group.send` / `group.pull` / `group.history` / `group.read` / `group.read_state` / `group.v2.*` / `group.fs.*` |
|
|
886
891
|
| 存储 | [09-storage-rpc-manual.md](09-storage-rpc-manual.md) | `storage.put_object` / `storage.fs.*` / `storage.volume.*` / ACL / token / share link |
|
|
887
892
|
| 协作 | [09-collab-rpc-manual.md](09-collab-rpc-manual.md) | `collab.ls-files` / `collab.show` / `collab.commit` / `collab.merge` / `collab.log` / `collab.diff` / `collab.clone` / `collab.prune` / `collab.gc` / `collab.reflog` / `collab.revert` / `collab.tag.*` / `collab.ls-remote` / `collab.unregister` |
|
|
888
893
|
| 元信息 | [09-meta-rpc-manual.md](09-meta-rpc-manual.md) | `meta.ping` / `meta.status` / `meta.trust_roots` |
|
|
889
|
-
| Stream | [09-stream-rpc-manual.md](09-stream-rpc-manual.md) | `stream.create` / `stream.close` / `stream.list_active` |
|
|
890
|
-
| Offline Push | `docs/protocol/15-离线推送通知协议.md` | `push.register` / `push.unregister` / `push.ack` / `push.offline_message` |
|
|
891
|
-
| Service Proxy | [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) | `proxy.register_services` / `proxy.unregister_services` / `proxy.list_services` |
|
|
894
|
+
| Stream | [09-stream-rpc-manual.md](09-stream-rpc-manual.md) | `stream.create` / `stream.close` / `stream.list_active` |
|
|
895
|
+
| Offline Push | `docs/protocol/15-离线推送通知协议.md` | `push.register` / `push.unregister` / `push.ack` / `push.offline_message` |
|
|
896
|
+
| Service Proxy | [09-proxy-rpc-manual.md](09-proxy-rpc-manual.md) | `proxy.register_services` / `proxy.unregister_services` / `proxy.list_services` |
|
|
892
897
|
|
|
893
898
|
---
|
|
894
899
|
|