@vetta-org/plugin-sdk 0.3.4 → 0.3.7

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 (48) hide show
  1. package/dist/hooks.d.ts +8 -0
  2. package/dist/hooks.d.ts.map +1 -1
  3. package/dist/hooks.js +9 -0
  4. package/dist/hooks.js.map +1 -1
  5. package/dist/host-bridge.d.ts +3 -0
  6. package/dist/host-bridge.d.ts.map +1 -1
  7. package/dist/host-bridge.js.map +1 -1
  8. package/dist/images.d.ts +2 -0
  9. package/dist/images.d.ts.map +1 -1
  10. package/dist/images.js.map +1 -1
  11. package/dist/index.d.ts +6 -4
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +2 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/logger.d.ts +11 -0
  16. package/dist/logger.d.ts.map +1 -0
  17. package/dist/logger.js +18 -0
  18. package/dist/logger.js.map +1 -0
  19. package/dist/logging.d.ts +26 -0
  20. package/dist/logging.d.ts.map +1 -0
  21. package/dist/logging.js +38 -0
  22. package/dist/logging.js.map +1 -0
  23. package/dist/media.d.ts +20 -0
  24. package/dist/media.d.ts.map +1 -1
  25. package/dist/media.js.map +1 -1
  26. package/dist/models.d.ts +4 -0
  27. package/dist/models.d.ts.map +1 -1
  28. package/dist/models.js.map +1 -1
  29. package/dist/official.d.ts +18 -1
  30. package/dist/official.d.ts.map +1 -1
  31. package/dist/official.js.map +1 -1
  32. package/dist/service-provider.d.ts +1 -0
  33. package/dist/service-provider.d.ts.map +1 -1
  34. package/dist/service-provider.js.map +1 -1
  35. package/dist/ui.d.ts +34 -0
  36. package/dist/ui.d.ts.map +1 -1
  37. package/dist/ui.js.map +1 -1
  38. package/docs/.source.json +1 -0
  39. package/docs/README.md +3 -1
  40. package/docs/getting-started.md +19 -10
  41. package/docs/logging.md +41 -0
  42. package/docs/manifest.md +12 -8
  43. package/docs/media.md +19 -2
  44. package/docs/permissions.md +4 -0
  45. package/docs/styling-and-pitfalls.md +11 -2
  46. package/docs/system-plugins.md +2 -2
  47. package/docs/ui-slots.md +43 -0
  48. package/package.json +5 -1
@@ -104,7 +104,7 @@ dist/
104
104
  }
105
105
  ```
106
106
 
107
- > `react` / `react-dom` 仅用于类型与本地构建——运行时由**宿主作为共享单例提供**,不会打进你的 bundle(见 [styling-and-pitfalls.md](./styling-and-pitfalls.md))。`@vetta-org/plugin-sdk` 同理:构建时被 external 化,运行时由宿主提供。可选 UI primitives `@vetta-org/ui`(`Button` / `Dialog` / `Switch`…)同样由宿主单例提供,需要时在 `devDependencies` 加类型依赖即可。仓库内插件用 `workspace:*` 直链源码;仓库外插件改用发布版本号。
107
+ > `react` / `react-dom` 仅用于类型与本地构建——运行时由**宿主作为共享单例提供**,不会打进你的 bundle(见 [styling-and-pitfalls.md](./styling-and-pitfalls.md))。`@vetta-org/plugin-sdk` 同理:构建时被 external 化,运行时由宿主提供。可选 UI primitives `@vetta-org/ui`(`Button` / `Dialog` / `Switch`…)需要同时设置 `hostUi: true` 并在 `devDependencies` 声明;没有使用时两者都不要添加。仓库内插件用 `workspace:*` 直链源码;仓库外插件改用发布版本号。
108
108
 
109
109
  ## 3. vite.config.ts
110
110
 
@@ -122,14 +122,15 @@ export default defineConfig({
122
122
  name: "my_plugin", // MF remoteName,与 plugin.json.moduleFederation.remoteName 一致
123
123
  entry: "./src/index.tsx", // 入口(默认即此)
124
124
  expose: "./plugin", // 暴露名(默认 "./plugin",与 plugin.json.moduleFederation.expose 一致)
125
- // package: true, // §5:构建后自动产出 release/<id>-<version>.zip
125
+ // hostUi: true, // 仅在导入 @vetta-org/ui 时开启
126
+ // package: true, // 见 §5:构建后自动产出 release/<id>-<version>.vettapkg
126
127
  }),
127
128
  ],
128
129
  esbuild: { jsx: "automatic", jsxImportSource: "react" },
129
130
  });
130
131
  ```
131
132
 
132
- `vettaPluginFederation` 自动:把 `react` / `react-dom` / `@vetta-org/plugin-sdk` / `@vetta-org/ui` 设为 `singleton`、`import:false`(用宿主的),生产构建时把 SDK UI external 化,产出 `mf-manifest.json` + `remoteEntry.js`,CSS 落 `dist/style.css`。
133
+ `vettaPluginFederation` 默认把 `react` / `react-dom` / `@vetta-org/plugin-sdk` 设为 `singleton`、`import:false`(用宿主的),生产构建时 external 化 SDK。设置 `hostUi: true` 后才会以相同方式共享并 external `@vetta-org/ui`。构建产出 `mf-manifest.json` + `remoteEntry.js`,CSS 落 `dist/style.css`。
133
134
 
134
135
  它还会在插件 Tailwind 编译前自动接入 plugin-sdk 的宿主主题 Token 契约,因此
135
136
  `text-foreground`、`text-muted-foreground/50`、`bg-card` 等语义类可以直接使用;
@@ -194,13 +195,13 @@ export default definePlugin({
194
195
  bunx vite build # 产出 dist/(mf-manifest.json + remoteEntry.js + style.css)
195
196
  ```
196
197
 
197
- 发布需要一个 **zip**:根目录放 `plugin.json`,其下 `dist/`。两种方式:
198
+ 发布需要一个 **`.vettapkg` 插件包**。它使用 ZIP 容器,根目录放 `plugin.json`,其下 `dist/`。两种方式:
198
199
 
199
- - **自动**:`vettaPluginFederation({ ..., package: true })`,`vite build` 后自动产出 `release/<id>-<version>.zip`(打包 `plugin.json` + `dist/` + 清单声明的 `styles` / `agent.promptPaths` / `agent.skillPaths`;存在 `ability.json` 时也打包它和 `presentation/`)。
200
- - **手动**:自行把 `plugin.json` 与 `dist/` 一起 zip:
200
+ - **自动**:`vettaPluginFederation({ ..., package: true })`,`vite build` 后自动产出 `release/<id>-<version>.vettapkg`(打包 `plugin.json` + `dist/` + 清单声明的 `styles` / `agent.promptPaths` / `agent.skillPaths`;存在 `ability.json` 时也打包它和 `presentation/`)。
201
+ - **手动**:自行用 ZIP 容器打包 `plugin.json` 与 `dist/`,并使用 `.vettapkg` 扩展名:
201
202
 
202
203
  ```text
203
- my-plugin.zip
204
+ my-plugin.vettapkg
204
205
  plugin.json
205
206
  ability.json # 可选
206
207
  presentation/ # 使用 ability.json 时可选
@@ -214,13 +215,21 @@ bunx vite build # 产出 dist/(mf-manifest.json + remoteEntry.js + style.
214
215
  > 归档根目录必须有 `plugin.json`,或只含**一个**顶层文件夹、`plugin.json` 在其中。
215
216
  > 能力详情是可选的;需要 showcase、功能网格、图片或长篇 Markdown 时见 [ability-details.md](./ability-details.md)。
216
217
 
218
+ GitHub 能力市场有两种分发合同:schema v1/v2 从 `source.path` 目录直接安装,
219
+ 所以该目录必须包含构建后的 `dist/`;schema v3 从 `releases[]` 指向的固定 `.vettapkg`
220
+ 安装,市场仓库的 `source.path` 只放详情资源,`dist/` 和插件包留在制品存储。
221
+ 每个新版本写明已经发布的最低 App 版本、实际使用的 `pluginApiVersion`、插件包 URL
222
+ 和 SHA-256;市场会按用户 App 与宿主 API 版本选择可安装的版本。见仓库的
223
+ [`docs/open-marketplace.md`](../open-marketplace.md#pluginmcp-与-bundle) 和
224
+ [ADR-0120](../adr/0120-plugin-marketplace-releases-are-versioned-artifacts.md)。
225
+
217
226
  ## 7. 安装
218
227
 
219
228
  ### GUI
220
229
 
221
230
  通过桌面 App **设置 → 插件**(或独立插件页)安装:
222
231
 
223
- - **本地 zip**:选择本地 `.zip` 文件(`installFromArchive`)。
232
+ - **本地插件包**:选择本地 `.vettapkg` 文件(`installFromArchive`)。旧 `.zip` 插件包仍可导入,但新发布应使用专用扩展名。
224
233
  - **远程 URL**:填写 zip 下载地址(`installFromUrl`)。
225
234
 
226
235
  安装后用户插件落在:
@@ -240,11 +249,11 @@ bunx vite build # 产出 dist/(mf-manifest.json + remoteEntry.js + style.
240
249
  ```json
241
250
  {
242
251
  "operation": "install-from-path",
243
- "path": "/abs/path/to/my-plugin-0.1.2.zip"
252
+ "path": "/abs/path/to/my-plugin-0.1.2.vettapkg"
244
253
  }
245
254
  ```
246
255
 
247
- - 路径:本机可读 **`.zip` 绝对路径**(不限 cwd)。
256
+ - 路径:本机可读 **`.vettapkg` 绝对路径**(不限 cwd;兼容旧 `.zip`)。
248
257
  - 用户确认后:按 `plugin.json` **一次授予声明权限**并默认**启用**。
249
258
  - Desktop API:`window.vetta.plugins.installFromPath(path, { grantedPermissions?, enable? })`。
250
259
  - 不可覆盖系统插件 id。
@@ -0,0 +1,41 @@
1
+ # 插件日志
2
+
3
+ Plugin API 2.5.0 起,插件可以从独立 SDK 子路径导入已经绑定身份的 logger:
4
+
5
+ ```ts
6
+ import { logger } from "@vetta-org/plugin-sdk/logger";
7
+
8
+ logger.info("Model synchronization completed", { modelCount: 12 });
9
+ logger.error("Model synchronization failed", { channel: "codex", error });
10
+ ```
11
+
12
+ logger 的 `pluginId` 与版本来自构建时校验过的 `plugin.json`。插件不传 `ctx`,也不能自行声明或覆盖日志身份。`@vetta-org/plugin-vite` 在生产构建与开发服务器中把该子路径替换成当前插件专属的 facade;没有经过兼容构建工具处理时,调用会给出明确错误,不会写出无法归属的日志。
13
+
14
+ 需要区分模块时使用子作用域:
15
+
16
+ ```ts
17
+ const log = logger.child("models").child("sync");
18
+
19
+ log.debug("Catalog request started");
20
+ log.info("Catalog published", { modelCount: 12 });
21
+ ```
22
+
23
+ 可用级别为 `debug`、`info`、`warn`、`error`。第二个参数必须是字段对象;异常放进 `error` 字段,宿主会保留名称、消息、堆栈和 cause 链。
24
+
25
+ ## 持久化与隐私
26
+
27
+ 日志进入 Desktop 的 Renderer 日志管线,由宿主统一持久化、轮转并纳入诊断信息。宿主限制消息和字段大小、处理循环引用,并对常见敏感字段名、Bearer、JWT、敏感 URL 参数和邮箱做防御性脱敏。
28
+
29
+ 脱敏不是插件泄露秘密的许可证。不要记录 token、Cookie、Authorization header、OAuth 内容、完整用户文件或凭据;对象字段名不明显时,宿主无法判断其中是否包含秘密。
30
+
31
+ logger 无需权限:它只能写宿主管理的诊断通道,不能选择路径、读取日志或关闭轮转。它也不替代 `ctx.ui.notify()`——用户需要知道并采取行动的失败仍应通知;logger 用于开发者诊断和事后定位。
32
+
33
+ ## 版本要求
34
+
35
+ 使用该入口的插件需要:
36
+
37
+ - `@vetta-org/plugin-sdk >= 0.3.7 < 0.4.0`
38
+ - `@vetta-org/plugin-vite >= 0.2.3 < 0.3.0`
39
+ - `plugin.json#pluginApiVersion` 声明 `^2.5.0`
40
+
41
+ 旧插件不导入 logger 时保持原行为。
package/docs/manifest.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 清单参考(plugin.json)
2
2
 
3
- `plugin.json` 是插件的唯一清单,位于 zip 归档根(或唯一顶层文件夹内)。
3
+ `plugin.json` 是插件的唯一清单,位于 `.vettapkg` 包的 ZIP 容器根目录(或唯一顶层文件夹内)。
4
4
 
5
5
  它描述插件的运行时合同。能力页的可选长详情使用独立的 `ability.json`,见
6
6
  [能力详情页](./ability-details.md);不要把 showcase、长 Markdown 或展示图片塞进 `plugin.json`。
@@ -61,7 +61,7 @@ Schema 只描述 `plugin.json` 数据本身;Plugin API 版本是否兼容、
61
61
  | `id` | ✅ | string | 全局唯一插件 id。决定安装目录、id 冲突时的去重,建议小写短横线。 |
62
62
  | `name` | ✅ | string | 展示名。可用 `%key%`(见 [i18n](#i18n))。 |
63
63
  | `version` | ✅ | string | 语义化版本。**bump 它可强制宿主重新拉取**绕过缓存(见 [styling-and-pitfalls.md](./styling-and-pitfalls.md))。 |
64
- | `pluginApiVersion` | ✅ | string | 兼容的 SDK API 版本范围(当前为 `^2.0.0`)。 |
64
+ | `pluginApiVersion` | ✅ | string | 兼容的宿主 Plugin API 版本范围;基础插件可用 `^2.0.0`,使用较新能力时按对应文档提高下限。 |
65
65
  | `entry` | ✅ | string | Module Federation 清单路径,通常为 `dist/mf-manifest.json`。 |
66
66
  | `moduleFederation` | ✅ | `{ remoteName, expose }` | `remoteName` 与 vite 配置 `name` 一致;`expose` 与 vite `expose` 一致(默认 `./plugin`)。 |
67
67
  | `styles` | ❌ | string[] | 要注入的 CSS 文件路径(相对插件根)。 |
@@ -220,6 +220,8 @@ revision 读取;不要依次调用多次 `writeFile()` 冒充多文件事务
220
220
 
221
221
  `agent.agents` / `agent.teams` 让插件把**自己的人设**带进产品:宿主在插件启用时把它们铺进用户的智能体库与团队列表,与用户自建的档案并列出现在智能体中心、新会话选择器和 `@` 提及里。
222
222
 
223
+ **这些资产由你 1:1 维护**:清单是唯一的真相源,宿主每次同步都按它重铺一遍——你改名、换阵容、改任务书、撤掉一支团队,用户升级后都会如实生效。对应地,用户改不动也删不掉它们(UI 只读展示);想要一份自己调教的版本,用户复制一份自建档案即可。
224
+
223
225
  宿主**不再内置任何人设**——装机自带的那几位现在也由 `preset-agent` 这个预置插件提供,所以你写的插件与它们走的是同一条路径、同一套字段。
224
226
 
225
227
  - **不需要权限**:这是清单声明面,不是运行时 API。用户对「装了什么插件」本身知情,因此没有单独的授权开关。
@@ -327,7 +329,7 @@ revision 读取;不要依次调用多次 `writeFile()` 冒充多文件事务
327
329
  - 任务书**挂在你的团队上,不碰对方的人设**。同一个设计师在别处照旧,在你的团队里按你交待的来。
328
330
  - 与 `responsibility` 分工不同:后者是一句全队可见的职责摘要(进共享名册),前者是只给这名成员看的做事方式。
329
331
  - 用 `instructionsPath` 指向 Markdown:任务书值得单独 diff,和人设提示词一个道理。**记得文件要能被打包**——路径会自动登记为插件资源,但源文件得真的在包里,缺了这名成员会让整支团队被跳过并打 warn。
330
- - 用户之后可以在团队设置里改这份任务书,改过的不会被插件升级盖掉。
332
+ - 任务书随插件升级整体更新:用户改不动它,下一版写什么用户看到的就是什么。
331
333
 
332
334
  ### 被别人引用(roles)
333
335
 
@@ -372,16 +374,18 @@ revision 读取;不要依次调用多次 `writeFile()` 冒充多文件事务
372
374
 
373
375
  ### 生命周期
374
376
 
375
- - **启用插件**:宿主把缺失的档案补齐——判据是「用户文档里现在有没有」,不是「历史上铺过没有」。因此用户误删、旧版本数据缺失都会被补回来。
376
- - **升级插件**:没被用户手改过的档案跟着提供方走(铺档案时**不落 `systemPrompt`**,人设升级才能自动生效);用户改过的字段保留。
377
- - **禁用插件**:档案**灰着留在原地**,既不隐藏也不从团队里摘掉,并标出「插件已禁用」。重新启用后一切原样回来——中途不动用户的档案。
378
- - **供货方后到**:铺团队时解析不到的槽位,会在提供方装上之后**自动补进阵容**。前提是阵容还是提供方铺的那一份——用户自己加过人就整支不动,宁可少补一个也不往用户编辑过的阵容里插队。
377
+ - **启用插件**:宿主把缺失的档案补齐——判据是「用户文档里现在有没有」,不是「历史上铺过没有」。因此旧版本数据缺失、一次异常都会被补回来。
378
+ - **升级插件**:清单里现在写的就是用户拿到的:名称、说明、能力、阵容与任务书按新清单整体重铺(档案里**不落 `systemPrompt`**,人设升级同样自动生效)。资源 id 与成员 id 不变,用户的团队绑定与会话引用因此不受影响。
379
+ - **撤掉资产**:新版本不再声明的智能体与团队会从用户那里消失,并从用户自建的团队里摘掉相应成员。这是「作者收得回自己发出去的东西」的那一条,与升级走的是同一次同步。
380
+ - **禁用插件**:档案**灰着留在原地**,既不隐藏也不从团队里摘掉,并标出「插件已禁用」。禁用不等于撤掉:清单还声明着它,就不会被当成残骸清理。重新启用后一切原样回来。
381
+ - **热重载**:开发态改完 manifest 重载插件,智能体与团队当场更新,不需要重启 App。
382
+ - **供货方后到**:铺团队时解析不到的槽位,会在提供方装上之后**自动补进阵容**——阵容本就按清单重排,不需要额外的补员规则。
379
383
  - **跨插件引用的能力面**:别的插件的智能体自带 `pinnedPlugins`,进了你的团队就等于隐式拉起那个插件的能力,用户在能力面板里关不掉。引用之前想清楚这一点。
380
384
  - **贡献出错**:单个智能体/团队解析失败(提示词读不到、头像超限、成员引用非法)只跳过它自己并打 warn,不影响同插件的其它贡献。
381
385
 
382
386
  ### 接管与升级(legacyIds)
383
387
 
384
- `legacyIds` 用于「人设从别处迁进插件」:宿主解析不到这些历史 id 时折算到本智能体,铺档案时也据此**认领**用户已有的同角色档案,而不是再铺一份新的。用户的 `@handle`、能力勾选与团队绑定因此不会被重置。
388
+ `legacyIds` 用于「人设从别处迁进插件」:宿主解析不到这些历史 id 时折算到本智能体,铺档案时也据此**认领**用户已有的同角色档案,而不是再铺一份新的。认领只保留**身份**(档案 id 与团队绑定),内容按你的清单重铺。
385
389
 
386
390
  装机自带人设迁进 `preset-agent` 走的正是这条路径(`executor` → `developer` 等)。宿主自己不需要知道是哪个插件接管了哪个老角色。
387
391
 
package/docs/media.md CHANGED
@@ -24,6 +24,7 @@ const submitted = await ctx.media.submit({
24
24
  kind: "image",
25
25
  mode: "text-to-image",
26
26
  prompt: "a red fox in snow",
27
+ modelId: "openai/gpt-image-2",
27
28
  dimensions: { width: 1024, height: 1024 },
28
29
  inputs: [],
29
30
  });
@@ -77,6 +78,12 @@ try {
77
78
 
78
79
  消费插件可用 `onProvidersChanged()` 监听 Provider 增删,并重新执行能力发现。插件并行激活时不能依赖固定加载顺序。
79
80
 
81
+ 官方系统插件如果需要遵循宿主 Agent 的图片 Provider 与模型选择,应使用 `ctx.official.agent.getImageGeneration()` 读取
82
+ `textToImageProviderId` / `textToImageModelId` 与 `imageToImageProviderId` / `imageToImageModelId`。只保存了 Provider 的旧配置
83
+ 继续使用该 Provider 的 `defaultModelId`。已保存但当前不存在或不支持对应模式的 Provider/模型必须报告
84
+ `provider-unavailable`,不能静默切换到另一条路由。
85
+ Provider 的凭据、模型和服务端参数仍由 Provider 自己管理,不应复制到 Agent 设置。
86
+
80
87
  ## 注册 Provider
81
88
 
82
89
  Provider 插件需要 `media.provider.register`。只有远程传输才需要 `network.fetch`;本地渲染输出可使用插件 Blob 或工作区文件。
@@ -92,6 +99,14 @@ ctx.media.registerProvider({
92
99
  aspectRatios: ["16:9", "9:16"],
93
100
  resolutions: ["efficient", "balanced", "quality"],
94
101
  defaultResolution: "balanced",
102
+ models: [{
103
+ id: "acme/video-v2",
104
+ displayName: "Video v2",
105
+ sourceId: "acme",
106
+ sourceDisplayName: "Acme",
107
+ modes: ["image-to-video"],
108
+ }],
109
+ defaultModelId: "acme/video-v2",
95
110
  durationsSeconds: [5, 10],
96
111
  }],
97
112
  async submit(request, context) {
@@ -124,13 +139,15 @@ ctx.media.registerProvider({
124
139
  });
125
140
  ```
126
141
 
127
- Provider 收到的 `inputs` 只有不透明 ID、媒体类型和 MIME,不包含插件 Blob 命名空间或工作区路径。只有当前任务上下文能用 `uploadInput()` 把对应文件流式上传到 HTTP(S) 服务。Provider 输出 source 支持 `remote-url`、`plugin-blob` 和 `workspace-file`,宿主会按 Provider 权限读取并导入为消费方临时产物。
142
+ Provider 收到的 `inputs` 只有不透明 ID、媒体类型和 MIME,不包含插件 Blob 命名空间或工作区路径。只有当前任务上下文能读取它们:`uploadInput()` 把文件流式上传到 HTTP(S) 服务;`readInput()` 返回 `{ mimeType, data }`,用于 Gemini `inlineData` 一类必须把字节放进 JSON 的 API。`readInput()` 上限 32 MB,调用结束、取消或 Provider 卸载后句柄立即失效,不能读取未列入本次请求的文件。Provider 输出 source 支持 `remote-url`、`plugin-blob` 和 `workspace-file`,宿主会按 Provider 权限读取并导入为消费方临时产物。
143
+
144
+ `models` 是可选的兼容字段:省略时按旧版 Provider 级能力工作。提供时,模型 `id` 在同一 capability 内必须唯一,模型的 `modes` 必须是 Provider `modes` 的子集,`defaultModelId` 必须引用目录中的模型。消费者没有传 `modelId` 时宿主使用 `defaultModelId`;显式模型缺失或不支持当前模式时返回 `invalid-request`,不会静默替换。模型可用 `sourceId` / `sourceDisplayName` 表达同一 Provider 后面的上游供应商分组。
128
145
 
129
146
  `resolutions` 是 Provider 自定义的稳定选项 ID,不保证具有 `720p`、`2K` 等固定视频制式语义;例如本地模型可以用它表达像素预算档位,再在 Provider 内转换为最终宽高。若声明 `defaultResolution`,该值必须同时出现在 `resolutions` 中。消费者在没有已保存值或已有值不受当前模型支持时优先采用这个显式默认值。
130
147
 
131
148
  ## Provider SPI
132
149
 
133
- 通用媒体契约和 capability token 定义在 `@vetta-org/capability-sdk`,当前协议版本为 4。注册表、通用任务、临时产物存储、输入解析与网络传输位于 desktop 主进程。插件 Provider 通过受控 IPC 回调桥接到同一个 Registry,注销时会中止仍在执行的调用。
150
+ 通用媒体契约和 capability token 定义在 `@vetta-org/capability-sdk`,当前协议版本为 5。注册表、通用任务、临时产物存储、输入解析与网络传输位于 desktop 主进程。插件 Provider 通过受控 IPC 回调桥接到同一个 Registry,注销时会中止仍在执行的调用。使用模型目录或 `readInput()` 的插件应声明 `pluginApiVersion: ^2.4.0`;旧 Provider 不声明模型目录时继续按原行为运行。
134
151
 
135
152
  需要宿主凭据或其它主进程特权的实现仍应注册为宿主 Provider;普通远端服务、本地模型或 sidecar 可用 Provider 插件适配。两者对消费者暴露同一契约。
136
153
 
@@ -113,6 +113,10 @@ ctx.permissions.require("fs.read"); // 缺则抛 Plugin permission denied: fs.r
113
113
  - `replaceOwnedProviders(providers)` 是**原子快照**:省略即删除。所以每次写入都得重建完整真相。
114
114
  - 正因如此,写之前先 `listOwnedProviders()` 读回宿主当前持有的状态并做对账——否则上游一时没返回的
115
115
  模型会被当成用户丢失的模型抹掉。读回的 `apiKey` 是掩码,下次写入要带上真凭据。
116
+ - SDK 0.3.7 的模型定义可声明 `reasoning: true`、`reasoningLevels`(上游原始档位字符串数组)和
117
+ `defaultReasoningLevel`。例如 CPA 可发布 `["low", "medium", "high", "xhigh", "max"]`。
118
+ 档位省略或为空时回退到宿主的 API 类型预设;显式列表的默认值无效或省略时使用第一项,用户已选择的档位优先。
119
+ 需搭配保留这两个字段的 Desktop 模型写入合同;旧宿主可能静默清除它们,单独升级插件不能修复宿主。
116
120
 
117
121
  ### ctx.ocr(`ai.ocr.recognize` / `ai.ocr.provider.register`)
118
122
 
@@ -152,19 +152,28 @@ function Icon() {
152
152
  import { Button, Switch, Slider, Dialog, DialogContent, cn } from "@vetta-org/ui";
153
153
  ```
154
154
 
155
+ 同时在 Vite 配置中显式开启宿主 UI 共享:
156
+
157
+ ```ts
158
+ vettaPluginFederation({
159
+ name: "my_plugin",
160
+ hostUi: true,
161
+ });
162
+ ```
163
+
155
164
  约定:
156
165
 
157
166
  | 项 | 说明 |
158
167
  | --- | --- |
159
168
  | 运行时 | 由宿主单例提供(MF share + `vetta-host://ui`),**不要**打进插件 bundle |
160
- | 构建 | `vettaPluginFederation` 已把 `@vetta-org/ui` 设为 `shared.singleton + import:false`,并 rollup external |
169
+ | 构建 | `hostUi: true` 会把 `@vetta-org/ui` 设为 `shared.singleton + import:false`,并 rollup external |
161
170
  | `package.json` | 仅作类型 / 本地 tsc:`devDependencies` 里 `@vetta-org/ui`(仓库内 `workspace:*`,仓库外按发布版本) |
162
171
  | 样式 | 组件 class 走宿主全局 token / Tailwind;插件 scoped CSS **管不到** Dialog 等 portal 到 `document.body` 的浮层(浮层依赖宿主已加载的全局样式,这是预期行为) |
163
172
  | 宿主版本 | 需要宿主提供 `vetta-host://ui` shim:desktop **>= 0.5.31**,且 `@vetta-org/plugin-vite` **>= 0.0.5**。旧宿主上 import `@vetta-org/ui` 会在加载插件时解析失败(模块找不到,整个插件不激活)——若你的插件要兼容更早的 App,就别用这条通道,自写 JSX + 语义 class |
164
173
  | 稳定性 | **半稳定、可选**。宿主会尽量不无故破坏,但不对跨 App 大版本做 semver 承诺;props / 导出变更时官方插件随 monorepo 同改 |
165
174
  | 不在此列 | `@vetta-org/theme-ui/plugin-ui` 是独立的按需共享合同,见下节;不要从其它 `@vetta-org/theme-ui/*` 入口导入宿主业务 View |
166
175
 
167
- 默认路径仍是:自写 JSX + 语义 class(`bg-background` / `text-foreground`…)。`@vetta-org/ui` 适合按钮、开关、对话框等控件统一,不是强制。
176
+ 默认路径仍是:自写 JSX + 语义 class(`bg-background` / `text-foreground`…)。`@vetta-org/ui` 适合按钮、开关、对话框等控件统一,不是强制。未导入它的插件不要开启 `hostUi`,也不要声明该依赖。
168
177
 
169
178
  顶层不要对 `@vetta-org/ui` 做立即求值(与 React 相同,见上文「MF 顶层 JSX 陷阱」)——在组件函数内使用即可。
170
179
 
@@ -23,9 +23,9 @@ packages/plugins/presets/
23
23
 
24
24
  ## 构建与集成
25
25
 
26
- - **构建制品**:`bun run build:presets` 先构建根 workspace 中的插件 SDK / 构建包,再逐个产出 `release/<id>-<version>.zip`。`dev` / `start` / 打包流程都会先跑它。
26
+ - **构建制品**:`bun run build:presets` 先构建根 workspace 中的插件 SDK / 构建包,再逐个产出 `release/<id>-<version>.vettapkg`。`dev` / `start` / 打包流程都会先跑它。
27
27
  - **依赖管理**:presets 与其它 monorepo 包统一属于根 workspace、共用根 `bun.lock`;`@vetta-org/plugin-sdk`、`@vetta-org/plugin-vite` 等本地包经 `workspace:*` 直链仓库源码。
28
- - **校验**:Desktop 按 preset 的 `plugin.json` 精确定位 zip,拒绝路径穿越、id/version 不一致、入口或样式缺失的归档。
28
+ - **校验**:Desktop 按 preset 的 `plugin.json` 精确定位 `.vettapkg`,拒绝路径穿越、id/version 不一致、入口或样式缺失的归档。
29
29
  - **dev**:zip 解压到 `apps/desktop/.artifacts/system-plugins/<id>/`,主进程只读该 staging,不直接读 preset 源码或 `dist/`。
30
30
  - **打包**:`prepare-pack.js` 从 zip 解压到打包 staging 的 `system-plugins/<id>/`,再随 `extraResources` 进入 `Resources/system-plugins/<id>/`。
31
31
 
package/docs/ui-slots.md CHANGED
@@ -106,6 +106,7 @@ interface PluginGlobalSlotContribution {
106
106
  - 导航入口默认落在侧边栏的「更多」收纳里;用户可以拖动排序,也可以 **pin 到左上方置顶区**(含「新会话」最多 5 个),布局按 key 持久化
107
107
  - 组件收到 `{ pluginId, viewId }`,一个组件可以服务多个注册
108
108
  - `icon` 是 **iconify class 字符串**(如 `"icon-[solar--widget-4-linear]"`),不是 ReactNode——宿主要把它渲染进自己的导航按钮,并按 key 持久化布局
109
+ - **图标 class 必须由你自己的 CSS 生成**:宿主只是把字符串挂到按钮上,Tailwind 只生成它扫得到的字面量,而你的源码不在宿主扫描范围内——漏了这步导航项就是个空格子,且不会有任何报错。在插件的 CSS 里加一行 `@plugin "@iconify/tailwind4";`(并把用到的图标集如 `@iconify-json/mdi` 装成 devDependency),规则会连同内联 SVG 一起进入 `dist/style.css`,宿主激活插件时加载。另外图标名要在图标集里**真实存在**:例如 solar 没有任何 git 图标,写 `solar:git-branch-bold` 同样是空格子
109
110
  - **不写 `icon` 就用插件自己的 Logo**:宿主回落到 `plugin.json` 的 `icon`。包内图片(`svg` / `png` / `webp` 等)默认按主题前景色 mask 成**单色**,因此自带图形的插件不必去图标集里找一个近似的;Iconify 名照常当 class 用。两者都不存在时才落到宿主默认图标
110
111
  - **`iconTint: false` 保留原图色彩**:导航项改用 `<img>` 渲染。选之前先掂量:入口只有 16px、与内置单色图标并排,且固定色彩无法跟随主题——深色 logo 会在深色侧边栏里消失。**只对彩色 logo 有意义**:单色图形 tint 后反而更清晰统一,而整块不透明的彩色图 tint 后会糊成一个纯色块。对 Iconify class 图标无效(它们始终跟随主题色)
111
112
  - **`sidebar: false` 不占导航位**:视图只出现在「设置 → 更多选项」,宿主在设置壳内打开它(两层侧栏保留,切换其它插件页面是一次点击)。配置页、安装引导、诊断台这类「装完就不常回来」的 surface 应该选它——侧边栏是用户自己策划的稀缺空间,每个插件都常驻一格,会把用户真正高频的入口挤进收纳菜单
@@ -196,6 +197,47 @@ useEffect(() => () => ctx.ui.setWorkspaceViewHeader("board", null), []);
196
197
  从窗口第一像素开始的沉浸式整页;页头里放了 `left`/`right` 工具栏时不要开——工具栏
197
198
  会压在内容上
198
199
 
200
+ ### 感知侧边栏 useSidebarState
201
+
202
+ 沉浸式页头有个绕不开的副作用:侧边栏收起时,宿主会在页头左上角长出「展开侧边栏」
203
+ 按钮,压在视图自己画的那一带上。视图要让位,就得知道侧边栏此刻什么形态。
204
+
205
+ ```tsx
206
+ import { useSidebarState } from "@vetta-org/plugin-sdk";
207
+
208
+ function Hero() {
209
+ const { collapsed, narrow, visible } = useSidebarState();
210
+ // 侧边栏不在位 = 宿主页头有展开按钮占着左上角,标题往右让 36px
211
+ return <h1 style={{ paddingLeft: visible ? 0 : 36 }}>设计画廊</h1>;
212
+ }
213
+ ```
214
+
215
+ - `collapsed`:用户手动收起了侧边栏。窄屏下这一位仍只反映用户意愿
216
+ - `narrow`:窗口窄到侧边栏改走悬浮覆盖,不再占据左侧一栏
217
+ - `visible`:侧边栏此刻是否实际占着左边那一栏,等价于 `!collapsed && !narrow`。
218
+ 多数自适应只需要这一位
219
+
220
+ 拿不到 hook 的地方(`activate()` 内、工具处理器、命令式绘制的画布)用命令式的一对:
221
+
222
+ ```ts
223
+ const state = ctx.ui.getSidebarState();
224
+ const sub = ctx.ui.onSidebarStateChanged((next) => redraw(next.visible));
225
+ // 插件失活时宿主会兜底摘掉监听,但自己持有生命周期的地方仍应显式 sub.dispose()
226
+ ```
227
+
228
+ 回调按值去重,拖窗口不会把它打成回调风暴——只有三元组真的变了才通知。无需权限:
229
+ 这是纯布局信息,不含任何用户数据。
230
+
231
+ **纯视觉自适应优先用 CSS**:宿主把同一份状态挂在整帧根节点上,插件不必订阅、不必
232
+ 重渲染:
233
+
234
+ ```css
235
+ :root [data-sidebar-visible="false"] .my-hero { padding-left: 36px; }
236
+ ```
237
+
238
+ 可用属性:`data-sidebar-collapsed` / `data-sidebar-narrow` / `data-sidebar-visible`,
239
+ 值恒为 `"true"` / `"false"`。
240
+
199
241
  **该用哪个插槽**
200
242
 
201
243
  | 场景 | 用 |
@@ -303,6 +345,7 @@ pdfjs.getDocument({ url: file.getUrl() });
303
345
 
304
346
  向活动面板注册一个 tab。
305
347
 
348
+ - **`order` 决定默认排位**(越小越靠前,缺省 100 即排在全部内置之后)。内置取值可作标尺:文件 0、批量 10、浏览器 15、计划 18、待办 20、后台任务 30。宿主把下限钳到 10,「文件」永远第一;用户拖出来的顺序优先于它
306
349
  - 权限:`ui.slot.activity-tab`(注册 **warn+noop**;`openActivityTab` / `setActivityTabVisible` **抛错**)
307
350
  - **`scope_use` fail-closed**(必写,否则任何场景不显示)
308
351
  - **默认注册即上栏**(`initiallyVisible` 缺省 `true`)。声明 `initiallyVisible: false` 表示「出现条件我自己管」:注册只入池,之后用 `setActivityTabVisible` 静默上栏/下栏(如 git 只在仓库目录上栏、工作台跟随输入栏 toggle),或用 `openActivityTab` 上栏并抢焦点打开(如图像生成完成后跳到历史)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vetta-org/plugin-sdk",
3
- "version": "0.3.4",
3
+ "version": "0.3.7",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -17,6 +17,10 @@
17
17
  "types": "./dist/npm-package.d.ts",
18
18
  "import": "./dist/npm-package.js"
19
19
  },
20
+ "./logger": {
21
+ "types": "./dist/logger.d.ts",
22
+ "import": "./dist/logger.js"
23
+ },
20
24
  "./tailwind-theme.css": "./src/tailwind-theme.css"
21
25
  },
22
26
  "files": [