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.
@@ -0,0 +1,229 @@
1
+ # Manifest v1 参考
2
+
3
+ `manifest.json` 是**运行时契约**,必须位于插件包根目录。DBX 与 CLI 都会校验它。
4
+
5
+ - 编辑器 Schema(上游,`main` 分支):`https://raw.githubusercontent.com/t8y2/dbx/main/plugins/manifest.schema.json`
6
+ - Manifest v1 **拒绝任何未声明的字段**(`additionalProperties: false`)。Schema 只是编辑期辅助,**运行时校验才是边界**。
7
+
8
+ > ⚠️ CLI 生成的模板里 `$schema` 指向 `.../t8y2/dbx/plugin-sdk-v1/plugins/manifest.schema.json`,该 ref 当前**不存在(404)**。需要编辑器提示就改成 `main` 或某个版本 tag。
9
+
10
+ ## 1. 顶层字段
11
+
12
+ | 字段 | 必需 | 约束 |
13
+ | --- | --- | --- |
14
+ | `$schema` | 否 | 任意字符串,不参与身份校验 |
15
+ | `manifest_version` | **是** | 必须为数字 `1` |
16
+ | `id` | **是** | `^[a-z0-9][a-z0-9._-]*$`,Schema/商店上限 128 字符;CLI 宽松到 256,但**上架请控制在 128 内**。发布后不可更改 |
17
+ | `name` | **是** | 非空字符串 |
18
+ | `icon` | 否 | 包内相对路径,见 §4 路径规则 |
19
+ | `version` | **是** | 严格 SemVer(CLI 用 semver 解析,如 `1.0.0`、`1.0.0-beta.1`)。相同 `id`+`version` 的正式包不可覆盖 |
20
+ | `publisher` | **是** | 非空字符串。Schema 不限格式,但**上架时商店要求**候选 `publisher` 匹配 `^[a-z0-9][a-z0-9._-]{0,127}$` 且已登记,且与包内 manifest 完全一致。实践:用小写标识符 |
21
+ | `description` | 否 | 字符串 |
22
+ | `source` | 否 | `^https?://\S+$`,源码仓库地址 |
23
+ | `homepage` | 否 | `^https?://\S+$` |
24
+ | `engines` | **是** | 至少含 `host_api`,见 §2 |
25
+ | `permissions` | 否 | 见 §2 |
26
+ | `entrypoints` | 否 | 有贡献点/后端时按需声明,见 §3 |
27
+ | `contributions` | 否 | 见 `contributions.md` |
28
+ | `localizations` | 否 | 见 §5 |
29
+
30
+ **因未知字段被拒的典型**:手写 `signingKeyId`(属于商店 artifact metadata,不是 Manifest 字段)、`verified`、`author`、`license`、`repository` 等自造键。
31
+
32
+ ## 2. `engines` 与 `permissions`
33
+
34
+ ```json
35
+ {
36
+ "engines": { "dbx": ">=0.5.68", "host_api": "1" },
37
+ "permissions": [
38
+ "host.workbench",
39
+ "host.events",
40
+ "host.filesystem",
41
+ "host.binary",
42
+ "host.network:https://s3.example.com:443"
43
+ ]
44
+ }
45
+ ```
46
+
47
+ - `engines.host_api` 必需(`minLength: 1`,推荐 `"1"` 或 `"^1.0"`);`engines.dbx` 可选,是产品版本范围(模板默认 `>=0.5.68`)。
48
+ - 固定权限枚举:`host.events`、`host.binary`、`host.workbench`、`host.filesystem`。
49
+ - 网络权限 `host.network:https://<host>[:port]`:
50
+ - **必须 HTTPS**;主机名只允许 `[A-Za-z0-9._-]`,端口可选数字;
51
+ - **不允许路径、通配符、Token**;
52
+ - **最多 8 个**,重复会被拒绝;
53
+ - 作用仅是把这些 origin 加入沙箱 CSP 的 `connect-src`,**仍受目标服务 CORS 约束**;
54
+ - 它**不是** Sidecar 的防火墙 —— 原生后端不受此限制。
55
+ - `permissions` 数组本身要求 `uniqueItems`。只声明真正用到的权限;reviewer 会逐一核对调用点。
56
+
57
+ ## 3. `entrypoints`
58
+
59
+ ```json
60
+ {
61
+ "entrypoints": {
62
+ "ui": { "root": "ui", "entry": "ui/index.html" },
63
+ "backend": {
64
+ "protocol_versions": [1],
65
+ "transport": "stdio-jsonl",
66
+ "executable": "bin/linux-x64/backend"
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ ### ui
73
+ - 只要求 `entry`(`minLength: 1`);`root` 可选,默认 `ui`。
74
+ - **`entry` 必须位于 `root` 内**:`root` = `ui` 时写 `ui/index.html`,**不是** `index.html`。路径按字符串前缀判断。
75
+ - `entrypoints.ui.kind` 已废弃,打包时**直接报错**(UI 永远是沙箱化)。
76
+
77
+ ### backend
78
+ - 只要求 `executable`(`assetPath`)。
79
+ - `transport` 枚举 `stdio-jsonl`(默认)| `stdio-framed`;需要二进制帧时必须用 `stdio-framed` **并**声明 `host.binary`。
80
+ - `protocol_versions`:正整数数组,`minItems: 1`,unique;当前协议只有 `1`。
81
+ - 已废弃字段会导致打包失败:`binaries`、`protocol`。
82
+
83
+ **打包时 CLI 会重写 backend 入口**(`package` 阶段,只影响包内那份 manifest):
84
+
85
+ 1. `executable` 被强制改写为 `bin/<target>/<binary>`(`<binary>` 来自 `dbx-plugin.toml` 的 `[backend].binary`,Windows 追加 `.exe`);
86
+ 2. `protocol_versions` 恰为 `[1]` 时**删除该字段**;
87
+ 3. `transport` 恰为 `"stdio-jsonl"` 时**删除该字段**。
88
+
89
+ 所以源码 manifest 里写 `bin/<platform>/...` 没有意义,按 §4 的规则,实际由 `target` 决定。
90
+
91
+ ## 4. 包内路径规则(`assetPath`)
92
+
93
+ 所有包内路径(`icon`、`ui.root`、`ui.entry`、`backend.executable`、Contribution 的 `icon`):
94
+
95
+ - **不能**以 `/` 开头;
96
+ - **不能**包含反斜杠 `\`;
97
+ - **不能**包含 `.` 或 `..` 作为路径段;
98
+ - **不能**出现重复斜杠 `//`;
99
+ - 必须相对包根且实际存在。
100
+
101
+ CLI 打包还会校验:`icon` / `ui.entry` 指向的文件必须存在,且**被 `[package].include` 覆盖**,否则报
102
+ `manifest UI entry 'ui/index.html' is not covered by [package].include`。
103
+
104
+ ## 5. `localizations`
105
+
106
+ 覆盖插件级与贡献点级文案。locale key 形如 `^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$`(如 `zh-CN`、`en`)。
107
+
108
+ ```json
109
+ {
110
+ "localizations": {
111
+ "zh-CN": {
112
+ "name": "示例文件",
113
+ "description": "示例插件说明。",
114
+ "contributions": {
115
+ "com.example.files.connection": {
116
+ "label": "示例服务",
117
+ "description": "由插件声明的连接表单。",
118
+ "fields": {
119
+ "host": { "label": "主机", "placeholder": "请输入主机" }
120
+ },
121
+ "actions": {
122
+ "refresh": { "label": "刷新元数据" }
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ ```
130
+
131
+ - 可覆盖:`name`、`description`;每个 Contribution 的 `label`、`description`、`fields`(`label`/`description`/`placeholder`/`options`)、`actions`(`label`/`description`)。
132
+ - `fields.<key>.options` 是 **`value → 显示文案` 的映射对象**(`{"readonly": "只读"}`),不是数组。
133
+ - DBX 匹配顺序:精确 locale → 基础语言 → manifest 默认文案。
134
+ - **它不翻译插件 UI 自己的文本**,UI 文案要按 `window.dbxPlugin.locale` 自行处理。
135
+
136
+ ## 6. 最小可用示例
137
+
138
+ ```json
139
+ {
140
+ "$schema": "https://raw.githubusercontent.com/t8y2/dbx/main/plugins/manifest.schema.json",
141
+ "manifest_version": 1,
142
+ "id": "com.example.files",
143
+ "name": "Example Files",
144
+ "version": "0.1.0",
145
+ "publisher": "example",
146
+ "description": "Browse files from an example service.",
147
+ "icon": "assets/plugin.svg",
148
+ "source": "https://github.com/example/dbx-plugin-files",
149
+ "homepage": "https://github.com/example/dbx-plugin-files",
150
+ "engines": { "host_api": "1", "dbx": ">=0.5.68" },
151
+ "permissions": [],
152
+ "entrypoints": { "ui": { "root": "ui", "entry": "ui/index.html" } },
153
+ "contributions": [
154
+ {
155
+ "type": "workbench",
156
+ "id": "com.example.files.main",
157
+ "label": "Files",
158
+ "icon": "assets/plugin.svg"
159
+ }
160
+ ]
161
+ }
162
+ ```
163
+
164
+ ## 7. 带原生后端的完整示例
165
+
166
+ ```json
167
+ {
168
+ "manifest_version": 1,
169
+ "id": "com.example.files",
170
+ "name": "Example Files",
171
+ "version": "0.1.0",
172
+ "publisher": "example",
173
+ "description": "Browse files from an example service.",
174
+ "icon": "assets/plugin.svg",
175
+ "engines": { "dbx": ">=0.6.0", "host_api": "1" },
176
+ "permissions": ["host.workbench", "host.events", "host.binary", "host.network:https://s3.example.com:443"],
177
+ "entrypoints": {
178
+ "ui": { "root": "ui", "entry": "ui/index.html" },
179
+ "backend": {
180
+ "protocol_versions": [1],
181
+ "transport": "stdio-framed",
182
+ "executable": "bin/darwin-arm64/dbx-plugin-example-files"
183
+ }
184
+ },
185
+ "contributions": [
186
+ {
187
+ "type": "connection-provider",
188
+ "id": "com.example.files.connection",
189
+ "label": "Example Service",
190
+ "icon": "assets/connection.svg",
191
+ "database_type": "example-files",
192
+ "fields": [
193
+ { "key": "display_name", "label": "Name", "type": "text", "binding": "name", "required": true },
194
+ { "key": "endpoint", "label": "Endpoint", "type": "text", "binding": "host", "required": true },
195
+ { "key": "port", "label": "Port", "type": "number", "binding": "port", "default": 443 },
196
+ { "key": "token", "label": "Access token", "type": "password", "binding": "secret", "required": true }
197
+ ],
198
+ "workbench": "com.example.files.main",
199
+ "capabilities": ["test", "connect", "disconnect"]
200
+ },
201
+ {
202
+ "type": "workbench",
203
+ "id": "com.example.files.main",
204
+ "label": "Files",
205
+ "icon": "assets/plugin.svg"
206
+ }
207
+ ]
208
+ }
209
+ ```
210
+
211
+ ## 8. CLI 与 Schema 的差异(容易踩)
212
+
213
+ | 项 | Schema / 运行时 | CLI `package` |
214
+ | --- | --- | --- |
215
+ | `id` 长度 | ≤128 | ≤256(更宽松,但商店按 128 校验) |
216
+ | `version` 格式 | 正则 `^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$` | semver crate 严格解析(`1.0.0-alpha+build` 只在 semver 侧通过) |
217
+ | `icon` 存在性 | 不校验 | **必须存在且被 `[package].include` 覆盖** |
218
+ | UI entry 在 root 内 | 不校验 | **校验** |
219
+ | backend 与 toml 一致 | 不校验 | **校验**(要么都有要么都没有) |
220
+ | 未知字段 | `additionalProperties: false` | 打包时显式拒绝并列出字段名 |
221
+ | `executable` | 按声明 | **改写为 `bin/<target>/<binary>`** |
222
+
223
+ ## 9. 自检
224
+
225
+ ```bash
226
+ node <skill-root>/scripts/check-project.mjs <项目目录>
227
+ ```
228
+
229
+ 它覆盖:未知字段、id/version 格式、`engines.host_api`、权限语法与数量、`ui.entry` 是否在 `root` 内、资源是否存在、是否被 `include` 覆盖、manifest 与 `dbx-plugin.toml` 的后端一致性、已废弃字段。
@@ -0,0 +1,183 @@
1
+ # 打包参考
2
+
3
+ ## 1. `dbx-plugin.toml`(构建配置,不进入插件包)
4
+
5
+ ```toml
6
+ schema_version = 1
7
+
8
+ # 只有原生后端项目才有这一段;纯前端项目必须完全省略
9
+ [backend]
10
+ language = "rust" # rust | go | golang
11
+ directory = "backend" # 相对项目根的相对路径
12
+ binary = "dbx-plugin-my-plugin" # 纯文件名,不能含 / 或 \
13
+
14
+ [package]
15
+ include = ["assets", "ui"] # 只有这里声明的目录才会被打包
16
+
17
+ [dev]
18
+ ui_build = ["npm", "run", "build"]
19
+ ui_watch = ["npm", "run", "build:watch"]
20
+ ```
21
+
22
+ 校验规则:
23
+
24
+ - `schema_version` 必须为 `1`,否则 `Unsupported dbx-plugin.toml schema version N`。
25
+ - `[backend].directory` 必须是安全相对路径;`[backend].binary` **必须是文件名**(不含路径分隔符)。
26
+ - `[package].include` 每一项必须是安全相对路径(不能绝对、不能含 `.`/`..`),且**不能包含 `.dbx-dev` 组件**,否则 `Package input cannot contain .dbx-dev development data`。
27
+ - `[dev]` 的子段是**字符串数组**(`ui_build="npm run build"` 会解析失败)。`[dev]` 使用 `deny_unknown_fields`,写错键会报错。
28
+ - `[backend]` 存在性与 `manifest.json` 的 `entrypoints.backend` **必须一致**,否则
29
+ `dbx-plugin.toml and manifest.json must either both declare a backend or both omit it`。
30
+
31
+ ## 2. 打包流程
32
+
33
+ ```bash
34
+ dbx-plugin package .
35
+ ```
36
+
37
+ 1. 解析 `dbx-plugin.toml`,校验 `schema_version`、backend 与 include 路径。
38
+ 2. 读取并校验 `manifest.json`:拒绝未知顶层字段、校验 `manifest_version`、`id`、`name`、`version`、`publisher`、`engines.host_api`;比对 backend 一致性。
39
+ 3. 决定 target(`--target` → `DBX_PLUGIN_TARGET` → 原生项目当前主机 → `universal`),校验 target 字符集,原生项目还会校验 target 与构建主机一致。
40
+ 4. 校验 `icon` 与 `ui.entry`:文件存在、路径安全、`entry` 在 `root` 内、且**被 `[package].include` 覆盖**。
41
+ 5. 建临时 stage 目录 `dist/.stage-<id>-<target>`,原生项目先构建 Sidecar 到 `bin/<target>/`。
42
+ 6. 生成**包内 manifest**(见 §4),与 include 的内容一起复制进 stage。
43
+ 7. 用 `dbx-plugin-packager` 打成 ZIP 容器 `dist/<id>-<version>-<target>.dbxp`,写入 `checksums.json`。
44
+ 8. 写 `dist/<id>-<version>-<target>.artifact.json`。
45
+ 9. 清理临时 stage / build 目录(成功与失败都会清理)。
46
+
47
+ ## 3. `.dbxp` 内部结构
48
+
49
+ ```
50
+ <id>-<version>-<target>.dbxp # ZIP 容器
51
+ ├── manifest.json # 已被 CLI 重写过的那份
52
+ ├── checksums.json # {"algorithm":"sha256","files":{路径: sha256}}
53
+ ├── signature.json # 仅签名后存在(未签名候选包没有)
54
+ ├── bin/<target>/<binary> # 仅原生项目;保留可执行权限
55
+ ├── assets/… # 由 [package].include 决定
56
+ └── ui/…
57
+ ```
58
+
59
+ 未签名候选包**故意不含 `signature.json`**,其 `.artifact.json` 也**不含 `signingKeyId`**。
60
+
61
+ ## 4. 包内 manifest 的重写规则
62
+
63
+ CLI 在打包时会改写**包内那份** `manifest.json`(你的源码文件不变):
64
+
65
+ 1. 拒绝已废弃字段:`entrypoints.ui.kind`、`entrypoints.backend.binaries`、`entrypoints.backend.protocol`。
66
+ 2. `entrypoints.backend.executable` 被强制设为 `bin/<target>/<binary>`(Windows 加 `.exe`)。
67
+ 3. 若 `protocol_versions` 恰为 `[1]`,删除该字段。
68
+ 4. 若 `transport` 恰为 `"stdio-jsonl"`,删除该字段。
69
+
70
+ 含义:源码里 `backend.executable` 写什么都会被覆盖,**真正决定路径的是 target**;默认值不必显式写出来。
71
+
72
+ ## 5. target 规则
73
+
74
+ | 项目类型 | 默认 target | 说明 |
75
+ | --- | --- | --- |
76
+ | 纯前端(`frontend` / `svelte`) | `universal` | 跨平台单包 |
77
+ | 原生(`rust` / `go`) | 当前主机 | `darwin-arm64`、`darwin-x64`、`linux-x64`、`linux-arm64`、`windows-x64`、`windows-arm64` |
78
+
79
+ - target 只允许 `[a-z0-9-]`,长度 ≤64。
80
+ - **原生项目不能交叉打包**:显式指定与构建主机不同的 target 会报错。正式发布用 CI 矩阵在各自平台构建。
81
+ - 商店目录实际使用的 target 词表:`darwin-arm64`、`darwin-x64`、`linux-arm64`、`linux-x64`、`windows-arm64`、`windows-x64`。
82
+ - 只有「同一个包在所有支持的 DBX 平台都有效」时才用 `universal`。
83
+
84
+ ## 6. 体积与数量限制
85
+
86
+ | 限制 | 值 | 触发时机 |
87
+ | --- | --- | --- |
88
+ | 输出包大小 | 512 MiB | packager,`Output package exceeds N bytes` |
89
+ | 解压后总大小 | 1 GiB | packager,`Package source exceeds N uncompressed bytes` |
90
+ | 单文件 | 256 MiB | packager,`Package file '<name>' exceeds N bytes` |
91
+ | 归档条目数 | 10000 | packager,`Package source contains more than N files` |
92
+ | 商店候选 `size` | 1 … 512 MiB | 商店 `validate.mjs` |
93
+
94
+ > 打包时还会做**读取期间一致性校验**:如果源文件在读取过程中被改动,会报
95
+ > `Package source changed while reading <path>`。所以不要在打包过程中同时改文件。
96
+
97
+ ## 7. 被拒绝的输入
98
+
99
+ - **符号链接**:`Package input cannot contain symbolic link <path>`(packager 也会检查)。
100
+ - **`.dbx-dev`**(含嵌套):`Package input cannot contain .dbx-dev development data`。
101
+ - **越界路径**:绝对路径、含 `.`/`..` 的路径 → `... must be a safe relative path`。
102
+ - **不存在的 include**:`Package include <path> does not exist`。
103
+ - **输出位置不安全**:输出包必须在源目录之外(`Output package must be outside the source directory`);artifact metadata 也必须在源目录之外且不能等于包路径。
104
+ - **扩展名**:输出必须是 `.dbxp`。
105
+ - **源目录缺 manifest**:`<dir> is missing manifest.json`。
106
+ - **未覆盖的资源**:`manifest icon 'assets/plugin.svg' is not covered by [package].include`。
107
+
108
+ **不要把 `dist/` 或 `.dbx-dev/` 再作为 include 输入。**
109
+
110
+ ## 8. `.artifact.json`
111
+
112
+ ```json
113
+ {
114
+ "target": "universal",
115
+ "url": "com.example.my-plugin-0.1.0-universal.dbxp",
116
+ "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
117
+ "size": 123456
118
+ }
119
+ ```
120
+
121
+ - `sha256` / `size` 绑定**确切字节**。任何字节变化都必须递增插件版本并重新审核。
122
+ - `url` 默认是文件名;用 `--artifact-url` 写入发布后的 HTTPS 地址:
123
+
124
+ ```bash
125
+ dbx-plugin package . --artifact-url https://github.com/me/my-plugin/releases/download/v0.1.0/com.example.my-plugin-0.1.0-universal.dbxp
126
+ ```
127
+
128
+ - 签名后 packager 才会补上 `signingKeyId`。**候选包里不要有它。**
129
+ - 上游 `.artifact.json` 与商店侧的最终 `.artifact.json` **同名但不同物**:前者由你生成(用于聚合 `release-candidates.json`),后者由签名步骤生成并随包发布。
130
+
131
+ ## 9. Release 工作流
132
+
133
+ `dbx-plugin create` 生成的 `.github/workflows/plugin-release.yml` 在发布 GitHub Release 时构建候选包:
134
+
135
+ ```yaml
136
+ name: Release DBX plugin
137
+ on:
138
+ release:
139
+ types: [published]
140
+ permissions:
141
+ contents: write
142
+ jobs:
143
+ release:
144
+ uses: t8y2/dbx/.github/workflows/plugin-release-reusable.yml@plugin-sdk-v1
145
+ with:
146
+ release-tag: ${{ github.event.release.tag_name }}
147
+ package-command: dbx-plugin package .
148
+ package-path: dist/*.dbxp
149
+ metadata-path: dist/*.artifact.json
150
+ plugin-cli-version: 0.1.6
151
+ # 纯前端插件加这一行,只构建一个 universal 包:
152
+ build-matrix: '{"include":[{"runner":"ubuntu-24.04","target":"universal"}]}'
153
+ ```
154
+
155
+ - **同时 pin 住 reusable workflow 的 ref 与 `plugin-cli-version`**,保证本地与 CI 用同一套 SDK 契约。
156
+ - 原生项目的默认矩阵覆盖 `darwin-arm64`、`darwin-x64`、`windows-x64`、`linux-x64`、`linux-arm64`。
157
+ - 工作流会:安装 pin 的 CLI → 各 target 构建 → 拒绝含 `signature.json` 或含 `signingKeyId` 的候选 → 校验 target/sha256/size/包名与统一 Manifest 身份 → 上传候选包与合并的 `release-candidates.json`。
158
+ - **官方作者不需要配置任何签名 Secret 或 Key ID。**
159
+
160
+ ## 10. 打包后自检
161
+
162
+ ```bash
163
+ node <skill-root>/scripts/check-project.mjs . # 打包前:项目自洽
164
+ node <skill-root>/scripts/inspect-dbxp.mjs dist/*.dbxp # 打包后:包内容 + checksums 校验
165
+ node <skill-root>/scripts/make-candidate.mjs . # 生成候选 JSON 并复核 sha256/size
166
+ ```
167
+
168
+ `inspect-dbxp.mjs` 会逐条重算 `checksums.json` 里的 SHA-256,并检查 `bin/` 下的 target 是否与文件名一致、是否存在 `signature.json`。
169
+
170
+ ## 11. 常见错误
171
+
172
+ | 报错 | 原因 |
173
+ | --- | --- |
174
+ | `Invalid dbx-plugin.toml: ...` | TOML 语法错,或 `[dev]` 里写了未支持的键 |
175
+ | `Unsupported dbx-plugin.toml schema version N` | `schema_version` 不是 1 |
176
+ | `dbx-plugin.toml and manifest.json must either both declare a backend or both omit it` | 两边不一致 |
177
+ | `manifest.json contains unknown top-level field(s): X` | 写了 Manifest v1 不接受的字段 |
178
+ | `manifest UI entry 'ui/index.html' is not covered by [package].include` | include 里没有 `ui` |
179
+ | `manifest icon 'assets/plugin.svg' does not exist at ...` | 图标文件缺失 |
180
+ | `Package include <path> does not exist` | include 声明了不存在的目录 |
181
+ | `Native plugin target 'X' does not match build host 'Y'` | 交叉打包;请在目标平台构建 |
182
+ | `Target directory is not empty; use --force`(create) | 与打包无关,但常一起遇到 |
183
+ | `Package source cannot contain symbolic link ...` | 删掉符号链接或改成真实文件 |