dbx-plugin-skill 0.1.0
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/LICENSE +201 -0
- package/README.md +238 -0
- package/bin/dbx-plugin-skill.mjs +330 -0
- package/lib/installer.mjs +256 -0
- package/package.json +49 -0
- package/skill/SKILL.md +268 -0
- package/skill/references/cli.md +225 -0
- package/skill/references/contributions.md +229 -0
- package/skill/references/debugging.md +210 -0
- package/skill/references/host-api.md +195 -0
- package/skill/references/manifest.md +229 -0
- package/skill/references/packaging.md +183 -0
- package/skill/references/publishing.md +374 -0
- package/skill/references/sidecar-protocol.md +242 -0
- package/skill/references/troubleshooting.md +171 -0
- package/skill/scripts/check-project.mjs +915 -0
- package/skill/scripts/dev-logs.mjs +191 -0
- package/skill/scripts/inspect-dbxp.mjs +400 -0
- package/skill/scripts/make-candidate.mjs +413 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# 贡献点(Contributions)参考
|
|
2
|
+
|
|
3
|
+
所有贡献点声明在 `manifest.json` 的 `contributions` 数组里。Schema 用 `oneOf` 校验,所以每一项必须**精确匹配**其中一种类型。
|
|
4
|
+
|
|
5
|
+
| 类型 | 必需字段 | 用途 |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `connection-provider` | `type`、`id`、`database_type`、`fields` | 声明连接表单与连接生命周期 |
|
|
8
|
+
| `workbench` | `type`、`id`、`label` | 从侧边栏/插件入口打开的工作台 |
|
|
9
|
+
| `filesystem-provider` | `type`、`id`、`label`、`schemes` | 接入 DBX 通用文件管理器 |
|
|
10
|
+
| `context-menu` | `type`、`id`、`label`、`menu` | 连接右键菜单项(v1 仅 `connection`) |
|
|
11
|
+
| `result-view` | `type`、`id`、`label` | 查询/任务结果的插件视图 |
|
|
12
|
+
|
|
13
|
+
通用可选字段:`description`、`icon`(包内相对路径,缺失时回退到插件级 `icon`)。
|
|
14
|
+
|
|
15
|
+
> 声明了贡献点**不等于**实现了业务逻辑。`filesystem-provider` 必须真的实现对应后端方法;`context-menu` 必须真的有后端。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. `connection-provider`
|
|
20
|
+
|
|
21
|
+
负责声明连接表单;**DBX 负责渲染表单和保存生命周期**,插件只处理自己的连接协议。
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"type": "connection-provider",
|
|
26
|
+
"id": "com.example.files.connection",
|
|
27
|
+
"label": "Example Service",
|
|
28
|
+
"icon": "assets/connection.svg",
|
|
29
|
+
"database_type": "example-files",
|
|
30
|
+
"description": "Connect to an Example Service.",
|
|
31
|
+
"fields": [
|
|
32
|
+
{ "key": "display_name", "label": "Name", "type": "text", "binding": "name", "required": true },
|
|
33
|
+
{ "key": "endpoint", "label": "Endpoint", "type": "text", "binding": "host", "required": true },
|
|
34
|
+
{ "key": "port", "label": "Port", "type": "number", "binding": "port", "default": 443 },
|
|
35
|
+
{ "key": "region", "label": "Region", "type": "radio",
|
|
36
|
+
"options": [{ "label": "US", "value": "us" }, { "label": "EU", "value": "eu" }], "binding": "config" },
|
|
37
|
+
{ "key": "token", "label": "Access token", "type": "password", "binding": "secret", "required": true }
|
|
38
|
+
],
|
|
39
|
+
"workbench": "com.example.files.main",
|
|
40
|
+
"capabilities": ["test", "connect", "disconnect"],
|
|
41
|
+
"actions": [
|
|
42
|
+
{ "id": "refresh", "label": "Refresh metadata", "variant": "outline",
|
|
43
|
+
"when": "edit", "requires_valid_form": true, "timeout_ms": 30000 }
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### `database_type`
|
|
49
|
+
插件自定义的连接类型标识(如 `ssh`、`example-files`)。**它不会给 DBX 内置数据库枚举添加成员**;保存后存放在 `plugin_connection_type`。
|
|
50
|
+
|
|
51
|
+
### `fields`
|
|
52
|
+
每个字段必须有 `key`(`identifier`)、`label`(非空)、`type`。
|
|
53
|
+
|
|
54
|
+
- `type` 枚举:`text`、`password`、`number`、`boolean`、`select`、`radio`、`textarea`。
|
|
55
|
+
- `select` / `radio` **必须**提供非空 `options`;每项 `{ "label": 非空, "value": 字符串 }`。其它类型**不允许**出现 `options`。
|
|
56
|
+
- 可选:`description`、`placeholder`、`required`(bool)、`default`(string|number|boolean|null,必须符合字段类型)、`binding`。
|
|
57
|
+
- 条件字段:
|
|
58
|
+
- `visible_when` / `required_when`:`{ "field": "<ident>", "one_of": ["v1", "v2"] }`(`one_of` 至少 1 项)。
|
|
59
|
+
- `binding: "port"` **必须** `type: "number"`。
|
|
60
|
+
- `binding` 为 `secret` / `name` / `host` / `username` / `password` / `database` 时,`type` 只能是 `text`、`password`、`select`、`radio`、`textarea`(不能是 `number`/`boolean`)。
|
|
61
|
+
|
|
62
|
+
### `binding` 语义(决定存储位置)
|
|
63
|
+
|
|
64
|
+
| binding | 存储 |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `name`、`host`、`port`、`username`、`password`、`database` | 映射到标准连接字段 |
|
|
67
|
+
| `config` | 写入 `external_config[field.key]`(非敏感) |
|
|
68
|
+
| `secret` | 写入 Secret Store 的 `connection_secrets[field.key]`(**不落 `config_json`**) |
|
|
69
|
+
| 省略且 `type: "password"` | 默认按 `secret` 处理 |
|
|
70
|
+
| 省略其它类型 | 不持久化到上述任一位置(仅表单值) |
|
|
71
|
+
|
|
72
|
+
> **绝对不要**把密码、Token、私钥放到 `config`、Workbench context、事件或日志里。非敏感的补充配置才用 `config`。
|
|
73
|
+
|
|
74
|
+
插件连接的 `external_config` 会在新建、编辑、保存和重新连接流程中保留;升级插件时要显式迁移自己拥有的 `external_config`。
|
|
75
|
+
|
|
76
|
+
### `capabilities`
|
|
77
|
+
枚举 `test`、`connect`、`disconnect`,unique。声明什么就要实现什么(见 §2)。
|
|
78
|
+
|
|
79
|
+
### `workbench` / `filesystem_provider`
|
|
80
|
+
- `workbench`:指向本插件已声明的 workbench。打开该连接时进入自定义工作台。
|
|
81
|
+
- `filesystem_provider`:指向本插件已声明的 filesystem-provider。打开该连接时连接生命周期 + 打开 DBX 通用文件管理器。
|
|
82
|
+
- **同时声明两者时默认打开工作台**;沙箱 UI 可用 `openFilesystem(providerId, context)`(需 `host.filesystem`)主动切到文件管理器。
|
|
83
|
+
|
|
84
|
+
### `actions`(连接对话框自定义动作)
|
|
85
|
+
- 只声明按钮元数据。点击后 DBX 调用固定的 `connection/action`,参数含 `action: { id }`;**插件不能自定义 RPC 方法名**。
|
|
86
|
+
- `id`(identifier)、`label`(非空)必需;可选 `description`、`variant`(`default` | `outline` | `secondary` | `destructive` | `ghost`)、`when`(`always` | `create` | `edit`)、`close_on_success`(bool)、`requires_valid_form`(bool)、`timeout_ms`(1–120000)。
|
|
87
|
+
- `test`、`save`、`save-and-connect` 是**宿主拥有**的动作,由 capabilities 与对话框模式生成,不走 `actions`。
|
|
88
|
+
- 只有 `requires_valid_form: false` 才允许表单不完整时执行;但 DBX 仍会校验已声明字段的类型、secret key 与传输配置。
|
|
89
|
+
|
|
90
|
+
**`connection/action` 返回值**:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"success": true,
|
|
95
|
+
"message": "Endpoint discovered",
|
|
96
|
+
"fieldValues": { "host": "db.internal", "port": 5432 }
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- `fieldValues` 只能包含**该 provider 已声明**的字段,且必须符合声明类型;`null` 表示清空。
|
|
101
|
+
- 返回 `success: false` 表示失败。
|
|
102
|
+
- 插件不能写任意 `ConnectionConfig` 键,也不能绕过 DBX 的保存、Secret 持久化、传输层和连接生命周期。
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 2. 连接生命周期方法(后端必需)
|
|
107
|
+
|
|
108
|
+
固定方法名:`connection/test`、`connection/connect`、`connection/disconnect`;自定义动作走 `connection/action`。
|
|
109
|
+
|
|
110
|
+
**请求参数**:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"provider": { "id": "com.example.files.connection", "databaseType": "example-files" },
|
|
115
|
+
"connection": { "id": "...", "db_type": "plugin", "...": "..." },
|
|
116
|
+
"runtime": { "host": "127.0.0.1", "port": 49152 },
|
|
117
|
+
"action": { "id": "refresh" }
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `connection` 只在**后端生命周期请求**中携带补齐的 Secret;前端只能拿到 `connectionId` 和非敏感导航上下文。
|
|
122
|
+
- `runtime.host` / `runtime.port` 是**经过 DBX 隧道/代理转换后的最终端点**。协议插件必须连这里,**不要自己重建 DBX 隧道**。
|
|
123
|
+
- 后端用 `connection.id` 保存会话。连接/断开应当**幂等**;长任务要有超时、取消与分块确认。
|
|
124
|
+
- `test` 通常返回 `{ "success": true, "message": "..." }`。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 3. `workbench`
|
|
129
|
+
|
|
130
|
+
注册一个可从侧边栏或插件入口打开的工作台。UI 在 `sandbox="allow-scripts"` 的 iframe 中运行,**无父页面 DOM 访问、无 Tauri 对象、默认无网络**。宿主注入 `window.dbxPlugin`,详见 `host-api.md`。
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{ "type": "workbench", "id": "com.example.files.main", "label": "Files", "icon": "assets/plugin.svg" }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- 打开工作台时把 context 以 **2 MiB 内的 JSON 快照**传给插件(递归去除 Vue 响应式包装)。
|
|
137
|
+
- 切换 Tab 时 iframe 保留(不重载),所以插件 UI 状态可跨导航保留。
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 4. `filesystem-provider`
|
|
142
|
+
|
|
143
|
+
让 DBX 通用文件管理器接管浏览/分页/预览,插件只实现存储协议。
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"type": "filesystem-provider",
|
|
148
|
+
"id": "com.example.files.fs",
|
|
149
|
+
"label": "Object storage",
|
|
150
|
+
"icon": "assets/filesystem.svg",
|
|
151
|
+
"schemes": ["s3"],
|
|
152
|
+
"root_uri": "s3://bucket/",
|
|
153
|
+
"capabilities": ["read", "write", "delete", "rename", "mkdir"]
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- `schemes`:非空、unique 的字符串数组(`capability` 形态:`^[a-z0-9][a-z0-9._:-]*$`)。
|
|
158
|
+
- `root_uri`:可选,`^[a-z0-9._-]+:.+$`,长度 2–4096,例如 `s3://bucket`。
|
|
159
|
+
- `capabilities` 枚举 `read`、`write`、`delete`、`rename`、`mkdir`。**写操作未声明对应 capability 会被拒绝。**
|
|
160
|
+
|
|
161
|
+
### 后端 RPC 方法
|
|
162
|
+
|
|
163
|
+
所有方法都携带 `providerId` 与可选 `connectionId`。
|
|
164
|
+
|
|
165
|
+
| 方法 | 参数 | 返回 |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `filesystem/list` | `uri`、可选 `cursor`、有界 `limit` | `{ entries, nextCursor? }` |
|
|
168
|
+
| `filesystem/read` | `uri`、有界 `maxBytes` | `{ dataBase64, contentType?, truncated, etag? }` |
|
|
169
|
+
| `filesystem/write` | `uri`、`dataBase64`、`create`、`overwrite`、可选 `etag` | `{ success, message?, entry? }` |
|
|
170
|
+
| `filesystem/createDirectory` | `uri` | `{ success, message?, entry? }` |
|
|
171
|
+
| `filesystem/delete` | `uri`、`recursive` | `{ success, message?, entry? }` |
|
|
172
|
+
| `filesystem/rename` | `sourceUri`、`targetUri`、`overwrite` | `{ success, message?, entry? }` |
|
|
173
|
+
|
|
174
|
+
### 目录项与分页规则
|
|
175
|
+
|
|
176
|
+
- 每项包含:`name`(**单个文件名**,不含 `/`、`\`,不能是 `.` 或 `..`)、完整 `uri`、`kind`(`file` | `directory` | `symlink` | `other`),可选 `size`、`modifiedAt`、`contentType`。
|
|
177
|
+
- 完整路径放在 `uri`,不要把路径塞进 `name`。
|
|
178
|
+
- **最后一页必须省略 `nextCursor`**;不要返回已经使用过的 cursor。
|
|
179
|
+
- 图片预览要返回**真实 MIME + base64 字节**,不要把二进制当 UTF-8 文本。
|
|
180
|
+
- **内联读写上限 4 MiB**。大文件传输要用 `stdio-framed` 二进制通道 + 自定义传输方法(分块、进度、取消、确认),**不能**塞进一个巨大的 JSON/base64。
|
|
181
|
+
- DBX 会在前端拿到结果前校验 scheme、响应大小、base64、cursor 和条目元数据。
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## 5. `context-menu`
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{ "type": "context-menu", "id": "com.example.inspect", "label": "Inspect endpoint", "menu": "connection" }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- `menu` 目前只能是 `"connection"`(已保存连接的侧边栏右键菜单)。
|
|
192
|
+
- 由 DBX **原生渲染**(无 iframe,跟随原生主题与键盘行为)。
|
|
193
|
+
- 点击后向后端发送 `contextMenu/<contribution-id>`,payload 是非敏感连接摘要 `{ id, dbType, name, database }`。
|
|
194
|
+
- **必须有后端入口**。返回 `{ "message": "..." }` 会在界面上弹 toast。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 6. `result-view`
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{ "type": "result-view", "id": "com.example.graph", "label": "Graph" }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
- 在结果网格旁加一个工具栏按钮;点击后用当前结果作为 context 打开**本插件的 workbench**(因此需要 UI 入口)。
|
|
205
|
+
- `context.result` 是**有界快照**:`{ columns, rows (≤ 500), truncated }`,外加 `sql`、`connectionId`、`database`。
|
|
206
|
+
- 需要完整/流式结果时,用 `sql` + 连接引用通过自己的后端重新执行查询。
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
## 7. 组合与引用规则
|
|
211
|
+
|
|
212
|
+
- 同一插件内通过 `id` 互相引用:Connection Provider 的 `workbench` 必须指向已声明的 workbench;`filesystem_provider` 必须指向已声明的 filesystem-provider。悬空引用会在运行时校验失败。
|
|
213
|
+
- 图标优先级:Contribution 的 `icon` → 插件级 `icon` → DBX 内置通用图标。
|
|
214
|
+
- 声明图标文件必须留在包内,可用 SVG、PNG、JPEG、GIF、WebP、ICO。**商店目录只接受 SVG / PNG**(见 `publishing.md`)。SVG 以图片 URL 渲染,不会注入 DBX 文档。
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 8. 常见错误
|
|
219
|
+
|
|
220
|
+
| 现象 | 原因 |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| `select`/`radio` 没有 `options`,或 `text` 出现 `options` | Schema 的 `allOf` 条件校验 |
|
|
223
|
+
| `binding: "port"` 但 `type` 不是 `number` | 同上 |
|
|
224
|
+
| `binding: "secret"` 配 `type: "number"` | 同上(secret 只允许文本类字段) |
|
|
225
|
+
| 连接表单能保存但后端收不到值 | 字段没声明 `binding`(非 password 类型不会持久化) |
|
|
226
|
+
| 打开连接后菜单/文件管理器没反应 | 声明了 Contribution 但没实现 `contextMenu/<id>` 或 `filesystem/*` |
|
|
227
|
+
| 文件列表翻页死循环或重复项 | 最后一页没有省略 `nextCursor`,或复用了旧 cursor |
|
|
228
|
+
| 大文件上传失败/超时 | 用了内联 `filesystem/write`(4 MiB 上限)而非 framed 分块传输 |
|
|
229
|
+
| 结果视图拿不到完整数据 | `context.result` 是有界快照(≤500 行),需要重新执行查询 |
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# 本地调试参考(`dbx-plugin dev`)
|
|
2
|
+
|
|
3
|
+
`dbx-plugin dev` 启动一个**独立的浏览器开发宿主**,加载声明的 workbench、运行可选的 Rust/Go Sidecar,**不会启动 DBX 桌面端**。
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd my-plugin
|
|
7
|
+
dbx-plugin dev --path . --port 5190
|
|
8
|
+
# 打开终端输出的 http://127.0.0.1:5190/
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
- 要求 **Node.js 22+**。纯前端插件不需要 Rust/Go;原生插件需要各自的工具链和已安装的依赖。
|
|
12
|
+
- 只监听 `127.0.0.1`;校验 Host、Origin、浏览器会话、CSRF、资源越界与符号链接边界。
|
|
13
|
+
- 端口被占用会自动选空闲端口;`--port 0` 显式要求随机端口。**用实际打印的端口**。
|
|
14
|
+
|
|
15
|
+
## 1. 项目配置
|
|
16
|
+
|
|
17
|
+
CLI 读取 `manifest.json` 与 `dbx-plugin.toml`:
|
|
18
|
+
|
|
19
|
+
- 存在 `[backend]` 时按 Rust/Go 构建并启动 Sidecar;缺失时视作纯前端。
|
|
20
|
+
- Manifest 声明了 backend 而 toml 没有(或反之)会直接报错。
|
|
21
|
+
- Rust 使用缓存的 debug target 目录;Go 使用缓存的输出目录。
|
|
22
|
+
- Rust 依赖若显式写了 `git`/`path` 源,**不会**被 crates.io patch 覆盖。
|
|
23
|
+
|
|
24
|
+
可选前端构建命令:
|
|
25
|
+
|
|
26
|
+
```toml
|
|
27
|
+
[dev]
|
|
28
|
+
ui_build = ["npm", "run", "build"]
|
|
29
|
+
ui_watch = ["npm", "run", "build:watch"]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- 命令是**可执行文件 + 参数数组,不经过 shell**,在插件目录下执行。
|
|
33
|
+
- **不会做框架探测,也不会自动安装依赖** —— 先自己 `npm install`。
|
|
34
|
+
- 未配置这些选项时,直接服务并监听已有的 UI 静态文件。
|
|
35
|
+
|
|
36
|
+
## 2. `DBX_UI_BUILD_SUCCESS` 契约(最容易踩)
|
|
37
|
+
|
|
38
|
+
配置了 `ui_watch` 时,监听命令必须在**每次完整成功的构建之后**,向 stdout 打印**独立一行**:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
DBX_UI_BUILD_SUCCESS
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- 只有这个信号会触发 UI 自动重载;部分输出、构建失败都**不会**触发。
|
|
45
|
+
- 必须挂在构建工具的**成功完成回调**上,**不要**放在失败路径或无条件的退出钩子里。
|
|
46
|
+
- 未配置 `ui_watch` 时,静态 UI 文件仍按文件变化做防抖重载。
|
|
47
|
+
- 该协议与前端框架无关。Svelte 模板已在 `vite.config.js` 里用 `closeBundle` 钩子实现。
|
|
48
|
+
|
|
49
|
+
> 改完 UI 页面不刷新,**九成是这里**:要么没配 `ui_watch`,要么构建脚本没打印(或打印在了失败分支)。
|
|
50
|
+
|
|
51
|
+
Manifest 或运行时变更需要**重启 `dev`**。
|
|
52
|
+
|
|
53
|
+
## 3. 调试面板
|
|
54
|
+
|
|
55
|
+
点击「重载页面」右侧的「调试」,底部面板显示最近 **500 条**内存日志(重启服务即清空),支持级别筛选、清空视图、自动滚动。终端同步输出 `[dbx-dev]` 前缀日志。
|
|
56
|
+
|
|
57
|
+
包含:监听端口、项目/UI/backend 路径、HTTP 路由与状态、RPC ID/方法/耗时、可展开的 JSON 入参出参、结构化错误数据、事件、会话拒绝原因。
|
|
58
|
+
|
|
59
|
+
脱敏规则:密码/令牌/凭据字段与 Manifest 声明的 secret 字段**递归脱敏**;二进制/base64 内容省略;过长/过深的值截断。**普通业务值仍然可见**(含连接名、SQL 等),所以只用开发数据。
|
|
60
|
+
|
|
61
|
+
Sidecar 的 stderr 会被消费但**不转发到日志**(因为任意插件可能打印凭据)。
|
|
62
|
+
|
|
63
|
+
## 4. 自动重载
|
|
64
|
+
|
|
65
|
+
- 默认**关闭**,开关对所有连接的浏览器生效;启用时会提示可能丢失草稿。
|
|
66
|
+
- 稳定 UI 产物变化 → 重载所有插件 frame;**编译型 UI 仍需要 `[dev].ui_watch`**。
|
|
67
|
+
- backend 目录下的 `.rs`/`.go` 与 Cargo/Go module 文件变化 → 防抖、串行化的重建 + 重启。
|
|
68
|
+
- 忽略 `target`、`.dbx-dev`、`vendor`、`node_modules`。
|
|
69
|
+
- 已保存的连接配置保留,但**后端重启后需要重新连接**。
|
|
70
|
+
- 构建失败会让后端保持停止,**不会回退到旧二进制**;写入不会被重放。
|
|
71
|
+
- 关闭开关只会取消待处理工作,**不会中断已开始的构建**;重启调试服务后回到关闭状态。
|
|
72
|
+
- 这是**重载/重启,不是保留状态的 HMR**。
|
|
73
|
+
|
|
74
|
+
## 5. 语言与主题
|
|
75
|
+
|
|
76
|
+
「调试」左侧的语言按钮同时切换**开发外壳与插件**的 locale(`zh-CN` / `en`):外壳控件、对话框、诊断标签、Manifest 本地化文案一起更新。已保存的连接名与业务数据**不翻译**。
|
|
77
|
+
|
|
78
|
+
有活动页面时会确认(可能丢失草稿)后自动重载该页面;取消则语言不变。其它已打开的 frame 收到标准 `env` 消息。新页面用选中的 locale。
|
|
79
|
+
|
|
80
|
+
**插件内容必须自带翻译** —— Manifest 的 `localizations` 只覆盖声明元数据,不翻译插件 UI 文本。
|
|
81
|
+
|
|
82
|
+
## 6. 导入连接配置
|
|
83
|
+
|
|
84
|
+
用 UI 的 JSON 文件选择器导入:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"connections": [
|
|
89
|
+
{
|
|
90
|
+
"providerId": "example.connection",
|
|
91
|
+
"values": { "display_name": "Example", "host": "localhost", "port": 0 },
|
|
92
|
+
"readOnly": false
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
字段名来自插件 manifest,不是运行时。导入的记录会分配新 ID。**没有内置协议预设、文件系统 RPC 或插件专用适配器。**
|
|
99
|
+
|
|
100
|
+
## 7. 诊断 API(脚本 / Agent 读取)
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
curl -sS 'http://127.0.0.1:5190/api/diagnostics?after=0&limit=100'
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
- **只支持 `GET`**;不需要 cookie 或自定义 header(Host/Origin 校验仍然生效,也不开放 CORS)。
|
|
107
|
+
- 端口用 `dev` 实际打印的值。
|
|
108
|
+
|
|
109
|
+
**查询参数**
|
|
110
|
+
|
|
111
|
+
| 参数 | 默认 | 说明 |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `after` | `0` | 排他性条目标 ID(上一页的 `nextAfter`) |
|
|
114
|
+
| `limit` | `100` | 1–500 |
|
|
115
|
+
| `level` | 全部 | `debug` / `info` / `error`,**精确匹配** |
|
|
116
|
+
| `instanceId` | — | 上一页响应里的实例 ID |
|
|
117
|
+
|
|
118
|
+
**响应字段**:`entries`、`nextAfter`、`hasMore`、`instanceId`、`reset`、`truncated`、`oldestId`、`latestId`、`plugin`、`backendState`、`port`。
|
|
119
|
+
|
|
120
|
+
**轮询配方**
|
|
121
|
+
|
|
122
|
+
1. 首次:`?after=0&limit=100`(需要就加 `level=error`)。
|
|
123
|
+
2. 下一次:带上上一页的 `nextAfter`(作为 `after`)与 `instanceId`,**保持同样的筛选条件**。
|
|
124
|
+
3. `hasMore` 为真 → 立刻取下一页;否则以温和间隔轮询(例如每秒一次)。
|
|
125
|
+
4. `reset` → 服务实例变了或游标超出当前历史,响应已从可用历史开始。
|
|
126
|
+
5. `truncated` → 旧条目已从 500 条环形缓冲中丢弃。
|
|
127
|
+
6. **更改筛选条件要重新从 `after=0` 开始。**
|
|
128
|
+
|
|
129
|
+
轮询本身不会产生诊断条目;历史不持久化;该接口**不能调用插件操作**。
|
|
130
|
+
|
|
131
|
+
用随附脚本便捷读取(自动沿用 `nextAfter` + `instanceId`):
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
node <skill-root>/scripts/dev-logs.mjs --port 5190 --level error --follow
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 8. 支持边界(dev 不模拟什么)
|
|
138
|
+
|
|
139
|
+
**支持**
|
|
140
|
+
|
|
141
|
+
- 声明式连接表单:字段、默认值、选项、binding、端口 0(框架层允许,语义由插件自己校验)。
|
|
142
|
+
- 连接生命周期 `connection/test` / `connect` / `disconnect`,`success: false` 按真实宿主规则视为失败。
|
|
143
|
+
- 工作台 Tab:按贡献点 + 可选连接 ID 建键,切换时保留 iframe,重复打开复用已有 Tab;关闭某个连接的最后一个 Tab 会在确认后断开连接。
|
|
144
|
+
- 自定义 RPC 方法与结果原样转发(不解释业务含义)。
|
|
145
|
+
- Host API 子集:`ready`、`context`、`locale`、`theme`、`request`、`invoke`、`notify`、`onInit`、`onContext`、`onEvent`、`onBinary`、`sendBinary`、资源读取、`openWorkbench`。
|
|
146
|
+
- 后端传输:默认 `stdio-jsonl` 与显式 `stdio-framed`,协议 v1;初始化会校验插件身份与版本。二进制通道需要 framed。
|
|
147
|
+
- 权限强制执行:event、binary、workbench 导航权限会被检查。
|
|
148
|
+
- 图标:workbench 条目/Tab/连接行使用贡献点 `icon`,回退到插件级 `icon`;图标路径相对**插件项目根**(不是 UI root),拒绝远程 URL 与越界路径;缺失时用宿主通用图标。
|
|
149
|
+
|
|
150
|
+
**不支持 / 必须在真实 DBX 复验**
|
|
151
|
+
|
|
152
|
+
- `host.openFilesystem` 等未实现方法**直接返回错误**。
|
|
153
|
+
- 原生连接动作(connection actions)、query-result 贡献点、完整 DBX 组件 kit **不模拟**。
|
|
154
|
+
- 安装、签名、真实 Secret Store、桌面端生命周期、生产权限**不模拟**。
|
|
155
|
+
- 不读取 DBX 用户 profile,不模拟 Keychain / 桌面端 Tab 恢复。
|
|
156
|
+
- 不启用直接的插件 UI 网络访问。
|
|
157
|
+
|
|
158
|
+
## 9. 数据与隔离
|
|
159
|
+
|
|
160
|
+
- 开发配置(**含凭据,明文**)默认存 `<项目>/.dbx-dev/connections.json`;支持的平台会设 0700/0600 权限。
|
|
161
|
+
- 自定义 `--data-dir` 必须**留在 `[package].include` 与 UI 资源根之外**,并从源码控制中排除。打包(含嵌套目录)会拒绝 `.dbx-dev`。
|
|
162
|
+
- 凭据不会出现在列表摘要与 iframe context 中;诊断会按规则脱敏。
|
|
163
|
+
- 页面的事件流断开后,其 frame 保留 **30 秒**重连宽限期,然后移除;仍被其它页面使用的连接保持打开。已保存的连接配置不受影响。
|
|
164
|
+
- **这不是 OS 沙箱**:Sidecar 以当前用户权限运行。
|
|
165
|
+
|
|
166
|
+
## 10. 在真实 DBX 中做最终验收
|
|
167
|
+
|
|
168
|
+
未签名包只用于自己构建的本地测试:
|
|
169
|
+
|
|
170
|
+
1. DBX 顶部工具栏 → **插件中心**。
|
|
171
|
+
2. 切到 **设置**。
|
|
172
|
+
3. 展开 **第三方与开发者选项**。
|
|
173
|
+
4. 开启 **允许安装未签名开发包**。
|
|
174
|
+
5. 点 **安装 `.dbxp`**,选择 `dist/` 里的文件。
|
|
175
|
+
6. 切到 **已安装**,打开插件的工作台或入口。
|
|
176
|
+
|
|
177
|
+
本地开发包允许用相同版本重新安装(DBX 会替换当前开发版本并重启插件运行时);**正式签名包仍不允许覆盖同版本**。测试完建议关闭该开关 —— 它只影响手动本地安装,不会放宽官方商店的签名校验。
|
|
178
|
+
|
|
179
|
+
### 本地验收清单
|
|
180
|
+
|
|
181
|
+
- [ ] 关闭未使用的权限,确认 Manifest 权限与实际调用一致。
|
|
182
|
+
- [ ] 用空配置、错误凭据、超时、断网、Sidecar 重启分别测失败路径。
|
|
183
|
+
- [ ] 测 DBX 浅色/深色主题与 `zh-CN`/`en`;确认插件 UI 自己做了翻译。
|
|
184
|
+
- [ ] 测重新连接、关闭工作台、重复打开同一工作台、context 为空的情况。
|
|
185
|
+
- [ ] 检查包内资源路径、可执行文件权限,以及最终包是否包含不该发布的文件。
|
|
186
|
+
- [ ] 大文件传输、长任务、取消路径。
|
|
187
|
+
|
|
188
|
+
## 11. 排查流程
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
# 1) 项目本身是否自洽
|
|
192
|
+
node <skill-root>/scripts/check-project.mjs .
|
|
193
|
+
|
|
194
|
+
# 2) dev 是否起来
|
|
195
|
+
dbx-plugin dev --path . --port 5190
|
|
196
|
+
|
|
197
|
+
# 3) 看错误日志
|
|
198
|
+
node <skill-root>/scripts/dev-logs.mjs --port 5190 --level error --follow
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
| 现象 | 排查方向 |
|
|
202
|
+
| --- | --- |
|
|
203
|
+
| 页面白屏 | 调试面板看 HTTP 404 / CSP 报错;确认 UI 已构建进 `ui.root` |
|
|
204
|
+
| `dev requires a UI entrypoint` | manifest 缺 `entrypoints.ui.entry` |
|
|
205
|
+
| `UI entry must be inside its declared root` | `entry` 没落在 `root` 内(root=`ui` 时要写 `ui/index.html`) |
|
|
206
|
+
| `Project declares a backend but manifest does not` | `dbx-plugin.toml` 的 `[backend]` 与 manifest 的 `entrypoints.backend` 不一致 |
|
|
207
|
+
| `dev supports Rust and Go backends` | `[backend].language` 写了别的值 |
|
|
208
|
+
| 改了 UI 不刷新 | 见 §2 `DBX_UI_BUILD_SUCCESS` |
|
|
209
|
+
| 改了后端代码没重启 | 确认改的是 `[backend].directory` 内的 `.rs`/`.go`/module 文件;构建失败会让后端保持停止 |
|
|
210
|
+
| Sidecar 启动即退出 | 后端把日志写到了 stdout;日志必须走 stderr |
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Host API 参考(`window.dbxPlugin`)
|
|
2
|
+
|
|
3
|
+
插件 UI 运行在**隔离的 iframe** 中:`sandbox="allow-scripts"`、严格 CSP、**无父页面 DOM 访问**、**无 Tauri 对象**、**默认完全无网络**。DBX 通过 `window.dbxPlugin` 提供桥接对象。
|
|
4
|
+
|
|
5
|
+
> **不要**导入 DBX 的 Vue/Tauri 模块,也**不要**假设页面能直接访问 Node.js、文件系统或任意网络。所有能力必须通过 Manifest 权限 + Host API 显式暴露。
|
|
6
|
+
|
|
7
|
+
## 1. 启动顺序
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// ✅ 所有启动逻辑放在 ready 之后
|
|
11
|
+
await window.dbxPlugin.ready;
|
|
12
|
+
|
|
13
|
+
const context = window.dbxPlugin.context;
|
|
14
|
+
const locale = window.dbxPlugin.locale;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`ready` 在 Host 完成初始化后 resolve。不要在模块顶层同步读取 `context`/`theme` —— 此时桥接可能还没就绪。
|
|
18
|
+
|
|
19
|
+
## 2. API 全表
|
|
20
|
+
|
|
21
|
+
| API | 作用 | 需要的权限 |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| `ready` | 等待 Host 完成初始化 | — |
|
|
24
|
+
| `context` | 读取当前工作台 context | — |
|
|
25
|
+
| `onContext(fn)` | 监听 context 变化,返回取消订阅函数 | — |
|
|
26
|
+
| `locale` | 当前语言(`en`、`zh-CN` 等) | — |
|
|
27
|
+
| `theme` | `{ appearance: "light" \| "dark", tokens }` | — |
|
|
28
|
+
| `request(method, params)` | 调用 **Host API** | — |
|
|
29
|
+
| `invoke(method, params, { timeoutMs })` | 调用**自己 Sidecar** 的 RPC(有响应) | — |
|
|
30
|
+
| `notify(method, params)` | 向自己 Sidecar 发通知(无业务返回值) | — |
|
|
31
|
+
| `sendBinary(channel, data)` | 发送二进制(`ArrayBuffer`,可 transfer) | `host.binary` + `stdio-framed` |
|
|
32
|
+
| `onBinary(fn)` | 接收二进制,回调参数 `{ channel, data: Uint8Array }` | `host.binary` |
|
|
33
|
+
| `readAsset(path)` | 读取包内资源 | — |
|
|
34
|
+
| `readAssetUrl(path)` | 读取包内资源并返回对象 URL | — |
|
|
35
|
+
| `openWorkbench(id, context)` | 打开本插件另一个工作台 | `host.workbench` |
|
|
36
|
+
| `openFilesystem(id, context)` | 打开本插件的文件系统入口 | `host.filesystem` |
|
|
37
|
+
| `onInit(fn)` | 监听初始化/环境变化 | — |
|
|
38
|
+
| `onEvent(fn)` | 监听后端事件 | `host.events` |
|
|
39
|
+
|
|
40
|
+
常用 Host 内部方法(通过 `request` 调用):`host.getContext`、`ui.readAsset`。
|
|
41
|
+
**只调用协议中声明的方法**,不要调用未公开的 DBX 内部函数。
|
|
42
|
+
|
|
43
|
+
## 3. context 与快照规则
|
|
44
|
+
|
|
45
|
+
跨边界传递的 context 是 **JSON 数据快照**,宿主会递归剥离 Vue 响应式包装并发送独立副本。因此插件**不能**依赖 Vue ref、Proxy、DOM 节点、函数、组件实例或凭据。
|
|
46
|
+
|
|
47
|
+
- **允许**:`null`、布尔、有限数字、字符串、数组、普通对象。
|
|
48
|
+
- **拒绝**(返回错误):`Date`、`Map`、`Set`、`Symbol`、`BigInt`、非有限数字、循环引用、自定义 class 实例。
|
|
49
|
+
- 对象中的 `undefined` 字段被省略;数组中的 `undefined` 变成 `null`。
|
|
50
|
+
- UTF-8 编码后**上限 2 MiB**。
|
|
51
|
+
|
|
52
|
+
`onContext` 推送变化时**不重载 iframe**,所以插件 UI 状态可以跨导航保留。组件销毁时调用 `onContext`/`onEvent`/`onBinary` 返回的取消订阅函数,避免重复监听。
|
|
53
|
+
|
|
54
|
+
不同贡献点拿到的 context 不同:普通工作台是打开的导航上下文;`result-view` 额外带 `context.result`(有界快照)。
|
|
55
|
+
|
|
56
|
+
## 4. 主题与样式
|
|
57
|
+
|
|
58
|
+
DBX 把设计 tokens 注入文档根元素,并更新 `data-dbx-theme`。**不要**依赖父页面 CSS 自动穿透 iframe。
|
|
59
|
+
|
|
60
|
+
```css
|
|
61
|
+
body {
|
|
62
|
+
margin: 0;
|
|
63
|
+
background: var(--color-background, #fff);
|
|
64
|
+
color: var(--color-foreground, #18181b);
|
|
65
|
+
}
|
|
66
|
+
button {
|
|
67
|
+
background: var(--color-primary, #2563eb);
|
|
68
|
+
color: var(--color-primary-foreground, #fff);
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
官方内置轻量组件类(用同一套 token,随明暗主题与自定义调色板自动适配):
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<button class="dbx-btn dbx-btn--primary">Connect</button>
|
|
77
|
+
<input class="dbx-input" placeholder="Endpoint" />
|
|
78
|
+
<span class="dbx-badge">Ready</span>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
可用类:`dbx-card`、`dbx-section-title`、`dbx-btn`(`--primary` / `--danger` / `--ghost`)、`dbx-label`、`dbx-input`、`dbx-select`、`dbx-textarea`、`dbx-hint`、`dbx-row`、`dbx-table`、`dbx-badge`、`dbx-link`。
|
|
82
|
+
|
|
83
|
+
`document.documentElement.dataset.dbxTheme` 反映当前外观。
|
|
84
|
+
|
|
85
|
+
**环境变化事件**:先读 `dbxPlugin.locale`/`theme` 初始化,再监听:
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
window.addEventListener("dbx-plugin-env", () => {
|
|
89
|
+
document.body.dataset.theme = window.dbxPlugin.theme.appearance;
|
|
90
|
+
// 重新渲染本地化文案
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> 开发宿主(`dbx-plugin dev`)**不模拟完整组件 kit**。用自定义 token 样式的插件在 dev 与真实宿主中表现更一致;用内置类的插件要在真实 DBX 里复验外观。
|
|
95
|
+
|
|
96
|
+
## 5. 国际化(两层)
|
|
97
|
+
|
|
98
|
+
1. **Manifest 层**:`manifest.json > localizations` 覆盖插件名称、说明、连接字段、按钮、贡献点文案(见 `manifest.md` §5)。
|
|
99
|
+
2. **插件 UI 层**:DBX **不会替你翻译插件 UI 文本**。按 `window.dbxPlugin.locale` 选择文案或接入自己的 i18n 库。
|
|
100
|
+
|
|
101
|
+
```js
|
|
102
|
+
const copy = {
|
|
103
|
+
en: { title: "Files" },
|
|
104
|
+
zh: { title: "文件" }
|
|
105
|
+
};
|
|
106
|
+
const text = window.dbxPlugin.locale.toLowerCase().startsWith("zh") ? copy.zh : copy.en;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 6. 资源读取
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
await window.dbxPlugin.ready;
|
|
113
|
+
|
|
114
|
+
const assetUrl = await window.dbxPlugin.readAssetUrl("assets/empty-state.svg");
|
|
115
|
+
img.src = assetUrl;
|
|
116
|
+
// 不用时释放
|
|
117
|
+
URL.revokeObjectURL(assetUrl);
|
|
118
|
+
|
|
119
|
+
const text = await window.dbxPlugin.readAsset("assets/config.json");
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**路径相对于 `ui.root`**:`assets/empty-state.svg` 对应包内 `ui/assets/empty-state.svg`。路径不能越出插件资源根目录。
|
|
123
|
+
|
|
124
|
+
UI 资源必须是**内联**或通过 `readAssetUrl` 加载的。发行包里的相对模块 URL 不是资源桥的替代品 —— 宿主把入口及资源转换成沙箱可加载内容;CSP 报错时先检查构建产物是否存在、路径是否落在 `ui.root` 内。
|
|
125
|
+
|
|
126
|
+
**不要把 Vite dev server 或 CDN 的脚本地址写进发行包。**
|
|
127
|
+
|
|
128
|
+
## 7. 二进制通道
|
|
129
|
+
|
|
130
|
+
需要二进制帧时必须同时满足:`manifest.json` 的 `backend.transport = "stdio-framed"`、声明 `host.binary` 权限、Sidecar 实现 framed 传输。
|
|
131
|
+
|
|
132
|
+
```js
|
|
133
|
+
await window.dbxPlugin.ready;
|
|
134
|
+
|
|
135
|
+
const off = window.dbxPlugin.onBinary(({ channel, data }) => {
|
|
136
|
+
// data 是 Uint8Array
|
|
137
|
+
});
|
|
138
|
+
window.dbxPlugin.sendBinary("transfer", buffer); // 单条消息 8 MiB
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- UI 侧单条二进制消息 **8 MiB**;更大传输要自己分块(偏移、确认、取消、进度事件)。
|
|
142
|
+
- 默认 `stdio-jsonl` 传输**不支持**二进制。
|
|
143
|
+
|
|
144
|
+
## 8. 导航到本插件其它入口
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
await window.dbxPlugin.openWorkbench("com.example.files.main", { path: "/" }); // 需 host.workbench
|
|
148
|
+
await window.dbxPlugin.openFilesystem("com.example.files.fs", { uri: "s3://b/" }); // 需 host.filesystem
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
只能打开**本插件**声明的工作台/文件系统。所有后端调用会被宿主重新绑定到所属插件 ID,**插件 UI 无法调用另一个插件**。
|
|
152
|
+
|
|
153
|
+
## 9. 网络访问
|
|
154
|
+
|
|
155
|
+
沙箱默认**完全无网络**。要访问外部服务,在 Manifest 里声明精确 origin:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{ "permissions": ["host.network:https://api.vendor.com"] }
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
- 只有 HTTPS;主机 + 可选端口;**无路径、无通配符、无 Token**;最多 8 个。
|
|
162
|
+
- 声明后这些 origin 才被加入 `connect-src`。**仍受目标服务 CORS 约束。**
|
|
163
|
+
- 该权限**不影响**原生 Sidecar 的网络能力(Sidecar 不受浏览器 CSP 限制)。
|
|
164
|
+
|
|
165
|
+
## 10. 错误处理与 UI 约定
|
|
166
|
+
|
|
167
|
+
- 失败以 **Promise rejection** 返回。要展示可理解的错误并允许重试,不要静默吞掉。
|
|
168
|
+
- 用插件自己的对话框组件处理删除/重命名/确认 —— **不要用沙箱里的原生 `alert` / `confirm` / `prompt`**(不可靠)。
|
|
169
|
+
- `request` 的宿主方法、参数和返回值以当前 Host API 版本为准。
|
|
170
|
+
|
|
171
|
+
## 11. 开发宿主的能力边界
|
|
172
|
+
|
|
173
|
+
`dbx-plugin dev` **只模拟受支持的 Host API 子集**:
|
|
174
|
+
|
|
175
|
+
| 支持 | 不支持 / 需真实 DBX |
|
|
176
|
+
| --- | --- |
|
|
177
|
+
| `ready`、`context`、`locale`、`theme`、`request`、`invoke`、`notify`、`onInit`、`onContext`、`onEvent`、`onBinary`、`sendBinary`、资源读取、`openWorkbench` | `host.openFilesystem` 等未实现方法会直接报错 |
|
|
178
|
+
| 事件、二进制、工作台导航权限会被强制执行 | 原生连接动作、query-result 贡献点、完整 DBX 组件 kit **不模拟** |
|
|
179
|
+
| 声明式连接表单、工作台 Tab、RPC、主题切换 | 安装、签名、真实 Secret Store、桌面端生命周期、生产权限 |
|
|
180
|
+
|
|
181
|
+
桥接载荷上限(与真实宿主基线一致):JSON 桥参数 2 MiB、UI 二进制消息 8 MiB、Sidecar JSON 8 MiB、Sidecar 二进制 64 MiB;显式超时被 clamp 到 1–120000 ms。
|
|
182
|
+
|
|
183
|
+
**生产行为必须在真实 DBX 里复验。**
|
|
184
|
+
|
|
185
|
+
## 12. 常见错误
|
|
186
|
+
|
|
187
|
+
| 现象 | 原因 |
|
|
188
|
+
| --- | --- |
|
|
189
|
+
| `dbxPlugin` 是 `undefined` | 在 `await dbxPlugin.ready` 之前访问;或该页面不是沙箱工作台 |
|
|
190
|
+
| context 报「不支持的类型」 | 传了 `Date`/`Map`/`Set`/class 实例/循环引用 |
|
|
191
|
+
| 图片/CSS 404 或 CSP 报错 | 资源路径没落在 `ui.root` 内,或没构建进包 |
|
|
192
|
+
| `连接被拒绝` / CORS 错误 | 没有声明 `host.network:https://...`,或目标服务未放行 CORS |
|
|
193
|
+
| 自定义组件在 dev 正常、真实 DBX 里样式错乱 | 依赖了父页面 CSS 穿透或未使用 token 变量 |
|
|
194
|
+
| 重复收到事件 | 没有调用取消订阅函数 |
|
|
195
|
+
| 内存增长 | 未 `URL.revokeObjectURL` 释放 `readAssetUrl` 结果 |
|