@xiongtao.xt/tau-plugins 0.31.0 → 0.32.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -2
  3. package/client/{chunk-ECEBBIHP.js → chunk-SVY5LBAT.js} +44 -0
  4. package/docs/README.md +43 -0
  5. package/docs/ai.md +169 -0
  6. package/docs/backend.md +168 -0
  7. package/docs/editor.md +457 -0
  8. package/docs/excerpts.md +126 -0
  9. package/docs/flashcards.md +127 -0
  10. package/docs/getting-started.md +134 -0
  11. package/docs/mcp.md +173 -0
  12. package/docs/plugin-basics.md +209 -0
  13. package/docs/providers/README.md +108 -0
  14. package/docs/providers/images.md +65 -0
  15. package/docs/providers/publish.md +144 -0
  16. package/docs/providers/sync.md +75 -0
  17. package/docs/providers/tts.md +62 -0
  18. package/docs/providers/upload.md +77 -0
  19. package/docs/providers/video.md +118 -0
  20. package/docs/release.md +130 -0
  21. package/docs/services.md +159 -0
  22. package/docs/site-data.md +90 -0
  23. package/docs/skills.md +129 -0
  24. package/docs/storage-and-network.md +62 -0
  25. package/docs/ui.md +335 -0
  26. package/docs/user-data.md +389 -0
  27. package/docs/whiteboard.md +290 -0
  28. package/package.js +1 -1
  29. package/package.json +1 -1
  30. package/publish-artifacts.js +1 -1
  31. package/publish-client.js +3 -1
  32. package/types/packages/plugins/src/categories.d.ts +3 -0
  33. package/types/packages/plugins/src/contract.d.ts +0 -2
  34. package/types/packages/plugins/src/index.d.ts +0 -1
  35. package/types/packages/plugins/src/package.d.ts +2 -0
  36. package/AI.md +0 -116
  37. package/API.md +0 -881
  38. package/BACKEND.md +0 -68
  39. package/DEVELOPMENT.md +0 -173
  40. package/EDITOR.md +0 -192
  41. package/FLASHCARDS.md +0 -92
  42. package/IMAGES.md +0 -16
  43. package/MCP.md +0 -118
  44. package/NOTIFICATIONS.md +0 -48
  45. package/PUBLISH-DATA.md +0 -48
  46. package/PUBLISH.md +0 -50
  47. package/SERVICES.md +0 -119
  48. package/SYNC.md +0 -37
  49. package/TTS.md +0 -20
  50. package/UPLOAD.md +0 -35
  51. package/VIDEO.md +0 -65
  52. package/WHITEBOARD.md +0 -115
  53. package/types/packages/plugins/src/skills.d.ts +0 -8
@@ -0,0 +1,290 @@
1
+ # 白板扩展
2
+
3
+ 白板扩展让你在 Tau 的白板上添加自己的元素(如图形、图表、组件),以及创建它们的工具。白板扩展分为两部分:
4
+
5
+ - **元素**:静态声明,描述元素长什么样、能怎么交互。没有打开任何画布时,Tau 也需要读取这些声明,例如生成预览图。
6
+ - **交互贡献**:每块画布单独创建,提供工具、命令、属性面板、快捷键等。
7
+
8
+ ## 第一个元素:方块
9
+
10
+ ### 1. 在 manifest 中声明
11
+
12
+ ```json
13
+ {
14
+ "whiteboard": true
15
+ }
16
+ ```
17
+
18
+ ### 2. 定义扩展
19
+
20
+ ```tsx
21
+ import { definePlugin } from "@tau/plugins";
22
+ import { defineElement, defineWhiteboardExtension } from "@tau/plugins/whiteboard";
23
+
24
+ declare const __PLUGIN_META__: { id: string; name: string; version: string };
25
+
26
+ const BOX = `${__PLUGIN_META__.id}.box`;
27
+
28
+ function BoxIcon({ className }: { className?: string }) {
29
+ return (
30
+ <svg
31
+ className={className}
32
+ viewBox="0 0 24 24"
33
+ fill="none"
34
+ stroke="currentColor"
35
+ strokeWidth="1.5"
36
+ >
37
+ <rect x="3" y="5" width="18" height="14" rx="2" />
38
+ </svg>
39
+ );
40
+ }
41
+
42
+ const boxExtension = defineWhiteboardExtension({
43
+ id: __PLUGIN_META__.id,
44
+ // 元素:静态声明
45
+ elements: [
46
+ defineElement({
47
+ type: BOX,
48
+ presentation: { label: "方块", icon: BoxIcon },
49
+ render: ({ element }) => (
50
+ <rect
51
+ x={element.x}
52
+ y={element.y}
53
+ width={element.width}
54
+ height={element.height}
55
+ rx={8}
56
+ fill="var(--tau-accent-soft)"
57
+ stroke="var(--tau-accent)"
58
+ />
59
+ ),
60
+ create: {
61
+ gesture: "drag",
62
+ defaultSize: [160, 90],
63
+ build: ({ rect }) => ({ id: crypto.randomUUID(), type: BOX, ...rect, data: {} }),
64
+ },
65
+ }),
66
+ ],
67
+ // 交互贡献:每块画布调用一次
68
+ create: () => ({
69
+ tools: [
70
+ {
71
+ id: `${__PLUGIN_META__.id}.box-tool`,
72
+ label: "方块",
73
+ icon: BoxIcon,
74
+ element: BOX,
75
+ group: "drawing",
76
+ order: 90,
77
+ },
78
+ ],
79
+ }),
80
+ });
81
+
82
+ export default definePlugin({ ...__PLUGIN_META__, whiteboardExtensions: [boxExtension] });
83
+ ```
84
+
85
+ ### 3. 构建、安装、重启
86
+
87
+ 白板扩展在 Tau 启动时装配,启用、停用和更新都要**完全退出并重新打开 Tau** 才生效。之后打开白板,在「扩展工具」菜单中就能找到「方块」。
88
+
89
+ ## 命名规则
90
+
91
+ - 扩展 `id` 是你的插件 ID,或以插件 ID 加点开头(如 `yourname.shapes.box`)。
92
+ - 元素 `type` 必须以插件 ID 加点开头(如 `yourname.shapes.box`)。
93
+ - 工具、命令的 `id` 同样以插件 ID 作前缀。
94
+
95
+ 已经有白板用了某个元素类型,就不要再改名,否则已有的元素会显示为未知元素。
96
+
97
+ ## 元素
98
+
99
+ 元素的通用字段(`id`、`type`、`x`、`y`、`width`、`height` 等)在元素顶层,你自己的数据放在 `data` 里。
100
+
101
+ ### 元素声明
102
+
103
+ | 字段 | 说明 |
104
+ | -------------------------- | ------------------------------------------------------------------------------------ |
105
+ | `type` | 元素类型 |
106
+ | `render` | 渲染组件,收到 `element`,返回 SVG 内容 |
107
+ | `presentation` | 可选。`label`(类型名)、`icon`(图标)、`getName(element)`(列表中显示的名称) |
108
+ | `create` | 可选。创建方式,声明后才能注册创建型工具,见[创建工具](#创建工具) |
109
+ | `interaction` | 可选。缩放手柄与连接点,见下文 |
110
+ | `schema` | 可选。约束 `data` 结构的 [Standard Schema](https://standardschema.dev),必须同步校验 |
111
+ | `schemaVersion`、`migrate` | 可选。`data` 结构的版本号,以及从旧版本逐步升级的迁移函数 |
112
+ | `sketch` | 可选。为 `true` 时元素支持手绘画风,见[手绘画风](#手绘画风) |
113
+ | `schematic` | 可选。简略预览,见[简略预览](#简略预览) |
114
+
115
+ 用 `defineElement<TData>` 声明 `data` 的类型,或者传入 `schema` 让类型自动推断:
116
+
117
+ ```ts
118
+ interface StickyData {
119
+ text: string;
120
+ }
121
+
122
+ defineElement<StickyData>({
123
+ type: `${__PLUGIN_META__.id}.sticky`,
124
+ render: ({ element }) => <text x={element.x} y={element.y}>{element.data.text}</text>,
125
+ });
126
+ ```
127
+
128
+ ### 缩放与连接
129
+
130
+ ```ts
131
+ interaction: {
132
+ resize: { handles: "corners", preserveAspectRatio: true },
133
+ connect: "rect",
134
+ },
135
+ ```
136
+
137
+ - `resize.handles` 可选 `corners`(只有四角)或 `all`(默认,四角加四边)。`preserveAspectRatio` 默认 `false`,用户按住 Shift 时仍可等比缩放。
138
+ - 缩放手柄、吸附、撤销都由 Tau 处理,不要在元素里自己画缩放手柄。
139
+ - `connect: "rect"` 让元素有矩形连接点,可以用连线连接。不声明就没有连接点。
140
+
141
+ ### 显示名称
142
+
143
+ `presentation.getName(element)` 返回元素在列表中显示的名称,例如便签显示它的文字。用户手动起的名字优先;返回空时显示 `label`。
144
+
145
+ ### 版本迁移
146
+
147
+ `data` 的结构需要变化时,提高 `schemaVersion`,并在 `migrate` 中提供每一步升级:
148
+
149
+ ```ts
150
+ schemaVersion: 2,
151
+ migrate: {
152
+ // 从版本 1 升到版本 2
153
+ 1: element => ({ ...element, data: { ...element.data, color: "accent" } }),
154
+ },
155
+ ```
156
+
157
+ `migrate` 必须覆盖从 1 到当前版本的每一步。
158
+
159
+ ## 创建工具
160
+
161
+ ### 创建型工具
162
+
163
+ 元素声明了 `create` 后,注册一个 `element` 指向它的工具,用户就能在画布上用手势创建元素。
164
+
165
+ | `create` 字段 | 说明 |
166
+ | ------------- | -------------------------------------------------------------------------------------------------------- |
167
+ | `gesture` | `"click"`(点击放置)或 `"drag"`(拖拽绘制) |
168
+ | `build` | 根据手势生成元素,收到 `rect`、`start`、`end`、`click`、`options` 等,返回 `null` 则不创建;可以是异步的 |
169
+ | `defaultSize` | `click` 时必填,元素的默认尺寸;`drag` 时用于单击创建 |
170
+ | `minSize` | 可选。`drag` 绘制的尺寸小于它时不创建 |
171
+ | `constrain` | 可选。`drag` 时按 Shift 的约束:`box`(等比,默认)或 `line`(45° 吸附) |
172
+ | `after` | 可选。创建后 `select`(选中,默认)、`edit`(进入编辑)或 `none` |
173
+ | `stay` | 可选。为 `true` 时创建后保持当前工具,可以连续创建 |
174
+ | `hint` | 可选。工具激活时画布底部的提示文字 |
175
+
176
+ ### 工具
177
+
178
+ | 工具字段 | 说明 |
179
+ | ---------------- | ------------------------------------------------------ |
180
+ | `id` | 工具 ID |
181
+ | `label` | 名称,显示在菜单和提示中 |
182
+ | `icon` | 图标组件 |
183
+ | `element` | 创建型工具:要创建的元素类型(与 `command` 二选一) |
184
+ | `command` | 命令型工具:点击时执行的命令 ID(与 `element` 二选一) |
185
+ | `group`、`order` | 分组与排序 |
186
+ | `panel` | 可选。参数面板,见下文 |
187
+
188
+ 插件的工具统一放在工具栏的「扩展工具」菜单里。
189
+
190
+ ### 创建前选择参数
191
+
192
+ 工具可以提供 `panel`,让用户先选好参数再执行:
193
+
194
+ ```tsx
195
+ panel: ({ run }) => (
196
+ <div>
197
+ <button onClick={() => run({ color: "accent" })}>强调色</button>
198
+ <button onClick={() => run({ color: "neutral" })}>中性色</button>
199
+ </div>
200
+ ),
201
+ ```
202
+
203
+ - 创建型工具在 `create.build` 的 `options` 中读到 `run` 传入的参数;命令型工具在命令的 `payload` 中读到。
204
+ - 返回、关闭和面板卸载由 Tau 管理,`run` 执行后面板自动关闭。不要在面板里自己监听画布的指针事件。
205
+
206
+ ## 交互贡献
207
+
208
+ `create(api)` 在每块画布上调用一次,返回这块画布上的交互贡献:
209
+
210
+ | 字段 | 说明 |
211
+ | ------------ | -------------------------------------------------------------------------------------------------------- |
212
+ | `tools` | 工具 |
213
+ | `commands` | 命令:`{ id, execute(api, payload), enabled?(api) }` |
214
+ | `hotkeys` | 快捷键:`{ key, command }`,键串格式如 `"mod+shift+b"` |
215
+ | `menus` | 菜单项:`{ id, label, command, order, when? }` |
216
+ | `inspectors` | 属性面板:`{ [元素类型]: 组件 }`。选中单个该类型元素时显示在浮动属性栏,组件收到 `element` 和 `onChange` |
217
+ | `activate` | 可选。画布显示时调用,返回的清理函数在画布隐藏时执行 |
218
+ | `dispose` | 可选。画布销毁时调用 |
219
+
220
+ - 每块画布的状态放在 `create` 的闭包里,不要放在模块顶层,否则会在多块画布之间共享。
221
+ - `elements` 必须写在扩展顶层,不能放进 `create` 的返回值。
222
+ - `mod+k`、`mod+p`、`mod+j`、Alt+方向键和 Esc 由 Tau 保留,不能声明。
223
+
224
+ ### 修改画布
225
+
226
+ 通过 `api` 修改画布,不要直接修改读到的元素对象:
227
+
228
+ | 方法 | 用途 |
229
+ | ----------------------------------------- | ---------------------------------------------- |
230
+ | `api.updateElement(id, patch)` | 修改一个元素的属性 |
231
+ | `api.submit(operations)` | 提交一组结构性操作(增删元素等),作为一步撤销 |
232
+ | `api.select(ids)` | 设置选区 |
233
+ | `api.getElement(id)`、`api.getSnapshot()` | 读取元素与画布快照 |
234
+ | `api.setActiveTool(id)` | 切换当前工具 |
235
+ | `api.readonly` | 画布是否只读 |
236
+
237
+ 这些方法统一处理只读、撤销和生命周期。元素组件中可以用 `useWhiteboardAPI()` 拿到所属扩展的 `api`。
238
+
239
+ ## 手绘画风
240
+
241
+ 声明 `sketch: true` 后,元素可以跟随白板的手绘画风设置:
242
+
243
+ - 在元素组件中调用 `useSketch(element)`,得到 `enabled`、`level`、`seed`,据此决定怎么绘制。页面画风变化时组件会自动更新。
244
+ - `SketchPaths` 渲染手绘路径;`sketch` 命名空间提供手绘算法(`wobble`、`resample`、`inkOutline` 等),直接复用,不要自己复制实现。
245
+ - `usePageSketch()` 读取所在页面的画风;`sketch.hashSeed(text)` 为预览生成稳定的随机种子。
246
+ - 属性面板可以使用 `AttrPopover`、`AttrToggle`、`OptionGrid`、`Seg` 组件,`SketchPopover` 提供与 Tau 一致的笔触设置。
247
+
248
+ ## 简略预览
249
+
250
+ `schematic(element, { dark })` 返回一组简单图元(矩形、路径等,类型见 `SchematicShape`),用于白板列表和导出弹窗里的缩略预览:
251
+
252
+ ```ts
253
+ schematic: element => [
254
+ { kind: "rect", x: element.x, y: element.y, w: element.width, h: element.height, r: 3, fill: "var(--tau-accent)" },
255
+ ],
256
+ ```
257
+
258
+ 它必须是纯函数:只读元素数据,不调用 hook,也不依赖某块画布已经打开。真正导出图片时仍然使用完整的 `render`。
259
+
260
+ ## 记住用户的选择
261
+
262
+ 交互贡献需要保存用户偏好(如上次选的颜色)时,在扩展上声明依赖存储服务:
263
+
264
+ ```ts
265
+ import { IPluginStorage, defineWhiteboardExtension } from "@tau/plugins/whiteboard";
266
+
267
+ defineWhiteboardExtension({
268
+ id: __PLUGIN_META__.id,
269
+ requires: [IPluginStorage],
270
+ elements: [/* ... */],
271
+ create: api => {
272
+ const storage = api.getService(IPluginStorage);
273
+ // storage.get / set / delete,与 context.storage 相同
274
+ return { tools: [/* ... */] };
275
+ },
276
+ });
277
+ ```
278
+
279
+ - 存储按插件和工作区隔离,与 [`context.storage`](storage-and-network.md) 是同一份数据。
280
+ - 只在交互贡献、工具面板、属性面板和 `create.build` 中使用。元素的 `render` 和 `schematic` 只能依据元素数据,不能读存储。
281
+ - 读取失败时不要当成空数据再写回,否则会覆盖用户原有的偏好。
282
+
283
+ ## 样式
284
+
285
+ - 颜色使用 Tau 的主题变量(如 `var(--tau-accent)`),这样会跟随明暗主题。
286
+ - 需要 Tailwind 工具类时,可以导入 `@tau/plugins/tailwind.css`,它提供 Tau 语义类到主题变量的映射。
287
+
288
+ ## 停用与卸载
289
+
290
+ 插件停用或卸载后,白板里已有的元素会保留,显示为未知元素的占位;重新安装并启用后恢复显示。保存、复制白板时也会保留这些元素的数据。
package/package.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  isPluginPermission,
15
15
  validateBackendManifest,
16
16
  validatePluginManifest
17
- } from "./client/chunk-ECEBBIHP.js";
17
+ } from "./client/chunk-SVY5LBAT.js";
18
18
  export {
19
19
  PLUGIN_AI_CAPABILITIES,
20
20
  PLUGIN_CATEGORIES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiongtao.xt/tau-plugins",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  PLUGIN_SHARED_MODULES,
3
3
  validatePluginManifest
4
- } from "./client/chunk-ECEBBIHP.js";
4
+ } from "./client/chunk-SVY5LBAT.js";
5
5
 
6
6
  // packages/plugins/src/rendering.ts
7
7
  var PLUGIN_RENDER_VERSION = 1;
package/publish-client.js CHANGED
@@ -88,7 +88,9 @@ function createClient({ manifestUrl }) {
88
88
  );
89
89
  const keys = /* @__PURE__ */ new Set();
90
90
  assert(
91
- typeof value.site.title === "string" && typeof value.site.description === "string" && typeof value.site.basePath === "string",
91
+ typeof value.site.title === "string" && typeof value.site.basePath === "string" && record(value.site.fields) && Object.values(value.site.fields).every(
92
+ (field) => typeof field === "string" || typeof field === "boolean"
93
+ ),
92
94
  "INVALID_DATA",
93
95
  "\u7AD9\u70B9\u6570\u636E\u7F3A\u5931"
94
96
  );
@@ -20,6 +20,9 @@ export declare const PLUGIN_CATEGORIES: readonly [{
20
20
  }, {
21
21
  readonly id: "ai";
22
22
  readonly label: "AI";
23
+ }, {
24
+ readonly id: "skill";
25
+ readonly label: "Skill";
23
26
  }, {
24
27
  readonly id: "voice";
25
28
  readonly label: "语音";
@@ -1,5 +1,4 @@
1
1
  import type { PluginPages } from "./pages";
2
- import type { PluginSkill } from "./skills";
3
2
  import type { PluginAI, PluginAIFeature } from "./ai";
4
3
  import type { WhiteboardExtension } from "./whiteboard";
5
4
  import type { PluginServices } from "./services";
@@ -97,7 +96,6 @@ export interface PluginDefinition {
97
96
  readonly editorExtensions?: readonly PluginEditorExtension[];
98
97
  readonly whiteboardExtensions?: readonly WhiteboardExtension[];
99
98
  readonly editorCodecs?: readonly MarkdownCodec[];
100
- readonly skills?: readonly PluginSkill[];
101
99
  readonly ai?: {
102
100
  readonly features: readonly PluginAIFeature[];
103
101
  };
@@ -29,7 +29,6 @@ export type { PluginPermission, PluginPermissions, PluginPermissionState, Plugin
29
29
  export type * from "./flashcards";
30
30
  export type * from "./services";
31
31
  export { usePluginServices } from "./service-access";
32
- export type { PluginSkill } from "./skills";
33
32
  export type { PluginMCP, PluginMCPDefinition, PluginMCPTool, PluginMCPTransport } from "./mcp";
34
33
  export type { PluginMCPToolResult } from "./mcp";
35
34
  export type { PluginChat, PluginToolRendererDefinition, PluginToolRendererProps, ToolCallExecutionState, } from "./chat";
@@ -29,6 +29,8 @@ export interface PluginManifest {
29
29
  keywords?: readonly string[];
30
30
  /** 可申请的业务数据权限上限;实际授权由用户在调用时决定。 */
31
31
  permissions?: readonly PluginPermission[];
32
+ /** 包内 `skills/<id>/` 目录,随插件启停。 */
33
+ skills?: readonly string[];
32
34
  integrity: Record<string, string>;
33
35
  }
34
36
  /** 宿主内部传输的已读取产物,不属于作者运行时 API。 */
package/AI.md DELETED
@@ -1,116 +0,0 @@
1
- # AI 服务
2
-
3
- 自定义 AI 功能需要 SDK 0.7.0;申请应用已有的模型分配需要 SDK 0.27.0。两者都需要包含对应实现的 Tau 客户端。
4
-
5
- 自定义 AI 功能由插件声明,用户在插件详情的「设置 → AI 配置」分配已有模型并开启授权。全局「大语言模型」设置管理服务商和密钥;这类功能通过 `ctx.ai` 请求模型,接口不返回密钥、连接地址或用户的聊天历史。
6
-
7
- ## 使用应用已有模型分配
8
-
9
- 无需声明 `ai.features` 或另配模型。先在 manifest 中声明 `ai:<能力名>` 权限,再申请对应客户端:
10
-
11
- ```json
12
- { "permissions": ["ai:titleSummary"] }
13
- ```
14
-
15
- ```ts
16
- const model = await context.ai.requestCapability("titleSummary");
17
- const result = await model.chat({
18
- messages: [{ role: "user", content: "为这段文字拟一个简短标题:……" }],
19
- });
20
- console.log(result.text);
21
- ```
22
-
23
- 申请时由用户选择允许或拒绝;能力分别授权,未声明的申请会被拒绝。返回客户端提供 `chat(request)` 和 `stream(request)`,参数与下文自定义 AI 相同,但不传 `feature`。每次调用复核权限,撤权、停用或切库取消在途生成;额度、并发和用量审计按插件统计。缺少应用模型分配时明确失败,不自动选择其他模型。
24
-
25
- 当前全部 12 项大模型功能分配均可申请,能力清单也可从 `PLUGIN_AI_CAPABILITIES` 导入:
26
-
27
- | 能力名 | 沿用的应用分配 |
28
- | ------------------------------ | -------------- |
29
- | `chat` | 对话功能 |
30
- | `titleSummary` | 标题总结 |
31
- | `webClip` | 网页剪藏 |
32
- | `podcastGeneration` | 播客生成 |
33
- | `flashcardGeneration` | 闪卡生成 |
34
- | `aiJudgement` | 答案评判 |
35
- | `transcriptionOptimization` | 转录断句优化 |
36
- | `videoSummary` | 视频总结 |
37
- | `videoEbookRewrite` | 视频电子书重写 |
38
- | `vibeLearningImageDescription` | 图片理解 |
39
- | `contentMetadata` | 笔记元信息 |
40
- | `contentInsights` | AI 洞见 |
41
-
42
- 这里复用模型分配,不自动执行宿主的业务流程或读取业务数据;插件传入自己的消息。已有闪卡业务流程由 `context.flashcards` 提供,它内部调用同一通用机制,需声明 `ai:flashcardGeneration`,详见 [FLASHCARDS.md](FLASHCARDS.md)。
43
-
44
- 图片理解可在 user 消息中传入 `images: [{ data: imageBase64, mimeType: "image/png" }]`。`data` 不带 data: 前缀;支持 PNG、JPEG、WebP、GIF,每条消息最多 10 张,整次请求的 Base64 总长最多 20 MiB,模型须支持图片。图片与消息正文不会写入用量审计。
45
-
46
- ## 声明自定义 AI 功能
47
-
48
- ```ts
49
- import { definePlugin } from "@tau/plugins";
50
-
51
- declare const __PLUGIN_META__: { id: string; name: string; version: string };
52
-
53
- export default definePlugin({
54
- ...__PLUGIN_META__,
55
- ai: {
56
- features: [{ id: "summary", name: "文章摘要", description: "概括用户选择的文章" }],
57
- },
58
- create: () => ({
59
- activate(ctx) {
60
- ctx.commands.register({
61
- id: "summary",
62
- title: "生成摘要",
63
- async run(input) {
64
- const status = await ctx.ai.getStatus("summary");
65
- if (!status.configured || !status.enabled) {
66
- ctx.ai.openSettings("summary");
67
- return null;
68
- }
69
- const result = await ctx.ai.chat({
70
- feature: "summary",
71
- messages: [
72
- { role: "system", content: "概括主要内容,不添加原文以外的信息。" },
73
- { role: "user", content: typeof input === "string" ? input : "请先选择正文。" },
74
- ],
75
- maxOutputTokens: 1024,
76
- });
77
- return result.text;
78
- },
79
- });
80
- },
81
- }),
82
- });
83
- ```
84
-
85
- ## 接口
86
-
87
- | 接口 | 返回与用途 |
88
- | ------------------------ | ----------------------------------------------------------------------------------- |
89
- | `getStatus(feature)` | `{ configured, enabled }`;模型被删除也视为未配置 |
90
- | `openSettings(feature?)` | 打开当前插件设置,由用户分配模型、管理授权 |
91
- | `chat(request)` | `{ text, usage }`;usage 未知时为 null |
92
- | `stream(request)` | 异步迭代器,依次产生 `{ type: "text", delta }`,最后产生 `{ type: "done", result }` |
93
-
94
- 请求字段:`feature` 必须是插件声明的功能 ID;`messages` 为 system/user/assistant 文本消息数组;`maxOutputTokens` 是可选的最大输出 Token;`signal` 为可选的取消信号。多轮历史由插件传入。多个 system 消息合并为系统提示,不提供工具调用或自动代理执行。
95
-
96
- ```ts
97
- const controller = new AbortController();
98
- for await (const event of ctx.ai.stream({
99
- feature: "summary",
100
- messages: [{ role: "user", content: article }],
101
- signal: controller.signal,
102
- })) {
103
- if (event.type === "text") appendText(event.delta);
104
- else showUsage(event.result.usage);
105
- }
106
- ```
107
-
108
- 退出迭代、取消 signal、停用插件或用户撤销 AI 授权都会请求取消。不要在模块顶层调用 AI,也不要在遇到额度不足或拒绝授权后自动循环重试。
109
-
110
- ## 审计与额度
111
-
112
- 所有模型调用由宿主归属到插件及功能,进入「大语言模型 → 用量统计」,插件设置的「查看用量」会跳转并筛选本插件。记录包含时间、模型、状态、Token、估算费用和耗时,不保存提示词或回复正文。运行中的请求可取消,也可一次关闭插件的 AI 授权。
113
-
114
- 额度按本机自然日统计,所有窗口共用,并在重启后保留。每日请求次数、每日 Token 和并发限制由用户设置;并发达到上限直接拒绝,不排队。宿主按模型上下文容量加输出上限保守预占 Token,结束后按实际已知用量结算;未知用量保留预占。供应商未报告用量时不能当作零消费;费用为估算,不替代账单。取消无法撤销已经产生的消费。
115
-
116
- 错误消息以稳定代码开头:`AI_NOT_CONFIGURED`(未配置)、`AI_DISABLED`(未授权或插件已停用)、`AI_UNDECLARED_FEATURE`(未声明)、`AI_QUOTA_EXCEEDED`(每日额度不足)、`AI_CONCURRENCY_LIMIT`(并发已满)、`AI_CONFIG_CHANGED`(模型分配变化)、`AI_ABORTED`(取消)、`AI_REQUEST_FAILED`(模型或审计失败)。本地 AbortSignal 取消也可能抛出标准 AbortError。