@plumeai/plugin-sdk 0.1.3 → 0.1.4

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.
@@ -6,7 +6,7 @@
6
6
  * plume-plugin init <目录> [--name 插件名] [--display 市场里的名字]
7
7
  * plume-plugin check [目录] 类型检查和插件规则(公开接口、样式、文案),缺省当前目录
8
8
  * plume-plugin build [目录]
9
- * plume-plugin install [目录] [--to 目录] 装进 ~/.plume/plugins,Plume 开着就当场加载
9
+ * plume-plugin install [目录] [--to 目录] 装进 ~/.plume/local-plugins,Plume 开着就当场加载
10
10
  * plume-plugin dev [目录] [--to 目录] 构建、装上,之后一改源码就重新来一遍
11
11
  * plume-plugin link [目录] 重新链到这份 SDK(Plume 挪了位置、npm 删了链接时)
12
12
  */
@@ -7,7 +7,7 @@
7
7
  * 开和关是同一颗按钮、同一个左内边距:左右 6px 让关着时正好是控件边长的方块,一点图标不横跳,颜色也能过渡。
8
8
  */
9
9
  import type { ReactNode } from "react";
10
- export declare function ComposerToggle({ on, onChange, icon, label, tip, size, ...data }: {
10
+ export declare function ComposerToggle({ on, onChange, icon, label, tip, size, iconOnly, ...data }: {
11
11
  on: boolean;
12
12
  onChange: (on: boolean) => void;
13
13
  /** 16px 的图标,颜色跟着文字走。 */
@@ -18,6 +18,11 @@ export declare function ComposerToggle({ on, onChange, icon, label, tip, size, .
18
18
  tip: string;
19
19
  /** `sm` 放在 `Button size="sm"` 旁边:26px;主输入框的控件跟着 `--plume-composer-control`。 */
20
20
  size?: "composer" | "sm";
21
+ /**
22
+ * 开着时也只有图标,开没开只看颜色,名字和点了会怎样都在悬停提示里。给卡片上挨着一颗「开始」的开关:那里开着时带上的字
23
+ * 读起来像又一颗要按的按钮。主输入框里不用它,那一排的字说的是下一轮在什么模式里。
24
+ */
25
+ iconOnly?: boolean;
21
26
  } & {
22
27
  [key: `data-${string}`]: string | undefined;
23
28
  }): import("react").JSX.Element;
@@ -11,9 +11,11 @@
11
11
  * 放在基础组件里而不是某个功能里,因为有两处要问这个问题——主输入框,问正在写的这次对话;建议的
12
12
  * 对话卡片,问它要开始的那一次。
13
13
  */
14
- export declare function WorktreeToggle({ on, onChange, size, }: {
14
+ export declare function WorktreeToggle({ on, onChange, size, iconOnly, }: {
15
15
  on: boolean;
16
16
  onChange: (on: boolean) => void;
17
17
  /** `sm` 放在 `Button size="sm"` 旁边,比如建议卡片上:26px,主输入框的控件是 28px。 */
18
18
  size?: "composer" | "sm";
19
+ /** 开着时也只有图标,见 `ComposerToggle` 的 `iconOnly`。 */
20
+ iconOnly?: boolean;
19
21
  }): import("react").JSX.Element;
@@ -819,6 +819,8 @@ export interface SessionLaunch {
819
819
  message: string;
820
820
  /** 用哪个模型(`SessionModel.id`);不给用默认模型。不在目录里时抛错,不悄悄换成别的。 */
821
821
  model?: string;
822
+ /** 思考档位;不给用设置里的默认推理强度。不认识的档位抛错;模型没有这一档时,跑的时候就近落到它有的那一档(和输入框里选的一样)。 */
823
+ thinking?: ThinkingLevel;
822
824
  /**
823
825
  * 从哪个会话开出来的,记进新会话摘要的 `spawnedFrom`;那个会话要在、不能是子会话。不给就不记。
824
826
  * 要经 `reportBack` 交回时,`toolCallId` 得是本插件注册的工具在那个会话里的一次调用(提出开新会话的那次)。
@@ -940,7 +942,7 @@ export interface SessionAccess {
940
942
  * `idle` 在它第一次真闲下来时兑现:第一轮结束,它派出去的后台运行都回来了、送回的结果唤起的几轮也做完了,它经 `ToolRun.letter`
941
943
  * 问出去的请求(`request: true`)也都有了答复、答复唤起的几轮也做完了。请求有了答复:经回执的 `answer` 回了信,或对方读到以后
942
944
  * 寄回一封带 `answer` 的信(答掉它读到的全部);对方删掉、寄信的插件停用了也不再等。
943
- * 项目、目录、模型、`spawnedFrom` 不对时抛错,message 是给人看的话(按界面语言)。
945
+ * 项目、目录、模型、思考档位、`spawnedFrom` 不对时抛错,message 是给人看的话(按界面语言)。
944
946
  */
945
947
  launch(options: SessionLaunch): Promise<LaunchedSession>;
946
948
  /** 可用模型目录,只含名称、窗口和思考档位。 */
@@ -483,6 +483,19 @@ export interface ModelSelectProps {
483
483
  */
484
484
  compact?: boolean;
485
485
  }
486
+ /**
487
+ * 主输入框右下角那两枚:模型和思考档位,样子、菜单都和输入框里的一样。给开新会话之前先选好它怎么跑的地方用(建议新会话的卡片)。
488
+ * 不改哪个会话,只把选择交出来。没选时显示默认模型和设置里的默认推理强度,和新会话发第一句之前的输入框一样。
489
+ */
490
+ export interface ModelControlsProps {
491
+ /** 选中的模型 id(`SessionModel.id`);空串是默认模型。 */
492
+ model: string;
493
+ /** 选中的思考档位;不给是默认推理强度。模型没有这一档时显示的是就近落到的那一档,和跑的时候一致。 */
494
+ thinking?: ThinkingLevel;
495
+ onModelChange(modelId: string): void;
496
+ onThinkingChange(thinking: ThinkingLevel): void;
497
+ disabled?: boolean;
498
+ }
486
499
  /** 「跟随」时会落到哪个模型上,给设置页的下拉算继承的那一项。 */
487
500
  export interface ModelDefaults {
488
501
  /** 新会话的默认模型;一个可用的都没有时为空。 */
@@ -995,6 +1008,8 @@ export interface UiContext {
995
1008
  readonly Markdown: ComponentType<MarkdownProps>;
996
1009
  /** 产品的模型下拉,见 `ModelSelectProps`。 */
997
1010
  readonly ModelSelect: ComponentType<ModelSelectProps>;
1011
+ /** 输入框里的模型和思考档位那两枚,见 `ModelControlsProps`。 */
1012
+ readonly ModelControls: ComponentType<ModelControlsProps>;
998
1013
  /** React hook:「跟随」会落到的模型和默认思考档位,见 `ModelDefaults`;设置改了跟着变。只在组件里调。 */
999
1014
  useModelDefaults(): ModelDefaults;
1000
1015
  /** React hook:登记过的项目,按侧栏的顺序;增删改跟着变。只在组件里调。 */
package/lib/install.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
- * 把构建好的插件装进本机的 Plume:复制 package.json、README.md、images/、icon.png、dist/、随包的 skills/、.mcp.json 和 LICENSE 到 `~/.plume/plugins/<目录名>/`,
3
- * 和插件市场装的放在同一个地方。Plume 监视这个目录,开着的话当场加载(已经装过的就换成新的)。
2
+ * 把构建好的插件装进本机的 Plume:复制 package.json、README.md、images/、icon.png、dist/、随包的 skills/、.mcp.json 和 LICENSE 到 `~/.plume/local-plugins/<目录名>/`,
3
+ * 和 Plume 里「从本地上传」的放在同一个地方:不跟市场更新,和市场装的同名时这一份生效。Plume 监视这个目录,开着的话当场加载(已经装过的就换成新的)。
4
4
  *
5
5
  * 先复制进旁边的暂存目录再整个换上去:Plume 看到的要么是旧的一份,要么是新的一份,不会是复制到一半的。
6
6
  *
@@ -17,10 +17,10 @@ import { DIR_NAME, PLUGIN_ENTRY, SHIPPED } from "./manifest.mjs";
17
17
 
18
18
  /**
19
19
  * @param {string} source 插件目录
20
- * @param {string} [root] 装到哪个目录下,缺省 Plume 数据目录(`PLUME_HOME`,没设是 ~/.plume)下的 plugins
20
+ * @param {string} [root] 装到哪个目录下,缺省 Plume 数据目录(`PLUME_HOME`,没设是 ~/.plume)下的 local-plugins
21
21
  * @returns {string} 装到的目录
22
22
  */
23
- export function installPlugin(source, root = join(process.env.PLUME_HOME || join(homedir(), ".plume"), "plugins")) {
23
+ export function installPlugin(source, root = join(process.env.PLUME_HOME || join(homedir(), ".plume"), "local-plugins")) {
24
24
  const dir = resolve(source);
25
25
  const key = basename(dir);
26
26
  if (!DIR_NAME.test(key)) throw new Error(`目录名 ${key} 不行:只能用小写字母、数字和连字符`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeai/plugin-sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "写 Plume 插件用的类型、命令(新建、检查、构建、安装)、插件模板和给 agent 的 skill。随 Plume 应用带一份,也发在 npm 上(插件仓库按版本依赖)。",
5
5
  "license": "MIT",
6
6
  "repository": {
package/skill/SKILL.md CHANGED
@@ -76,8 +76,8 @@ description: 写、改、排查 Plume 插件:工具、钩子(tool.execute、
76
76
  4. **界面入口**(要画东西时):读 [references/ui.md](references/ui.md)。
77
77
  5. **样式和文案**(有界面或给人看的文字时):读 [references/style-i18n.md](references/style-i18n.md)。
78
78
  6. **构建和加载**:
79
- - 独立项目:`plume-plugin dev` 构建、装进 `~/.plume/plugins/<目录名>/`,之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install`。也可以把插件的上级目录放进环境变量 `PLUME_PLUGIN_PATHS`,Plume 同样盯着那里。
80
- - Plume 的插件仓库里(`plugins/<目录名>`):`pnpm plugin dev plugins/<目录名>`,和独立项目一样构建、装进本机的 Plume;要连市场一起试(安装、更新),`pnpm market` 起一个本机市场,Plume 启动前设它打印的 `PLUME_MARKET_URL`。
79
+ - 独立项目:`plume-plugin dev` 构建、装进 `~/.plume/local-plugins/<目录名>/`(和市场装的同名时这一份生效,不被市场更新换掉,卸掉就回到市场那份),之后一改源码就重新构建、装上;Plume 开着的话每次都当场加载,不用重启。只装一次用 `plume-plugin build` 再 `plume-plugin install`。也可以把插件的上级目录放进环境变量 `PLUME_PLUGIN_PATHS`,Plume 同样盯着那里。
80
+ - Plume 的插件仓库里(`plugins/<目录名>`):`pnpm plugin dev plugins/<目录名>`,和独立项目一样构建、装进本机的 Plume。
81
81
  7. **自检**:对照文末清单,再跑检查(独立项目 `plume-plugin check`:类型检查加插件规则;仓库里 `pnpm check`)。
82
82
 
83
83
  ## 目录骨架
@@ -342,6 +342,7 @@ export function ComposerToggle({
342
342
  label,
343
343
  tip,
344
344
  size = "composer",
345
+ iconOnly = false,
345
346
  ...data
346
347
  }: {
347
348
  on: boolean;
@@ -354,6 +355,11 @@ export function ComposerToggle({
354
355
  tip: string;
355
356
  /** `sm` 放在 `Button size="sm"` 旁边:26px;主输入框的控件跟着 `--plume-composer-control`。 */
356
357
  size?: "composer" | "sm";
358
+ /**
359
+ * 开着时也只有图标,开没开只看颜色,名字和点了会怎样都在悬停提示里。给卡片上挨着一颗「开始」的开关:那里开着时带上的字
360
+ * 读起来像又一颗要按的按钮。主输入框里不用它,那一排的字说的是下一轮在什么模式里。
361
+ */
362
+ iconOnly?: boolean;
357
363
  } & { [key: `data-${string}`]: string | undefined })
358
364
  ```
359
365
 
@@ -366,11 +372,14 @@ export function WorktreeToggle({
366
372
  on,
367
373
  onChange,
368
374
  size = "composer",
375
+ iconOnly = false,
369
376
  }: {
370
377
  on: boolean;
371
378
  onChange: (on: boolean) => void;
372
379
  /** `sm` 放在 `Button size="sm"` 旁边,比如建议卡片上:26px,主输入框的控件是 28px。 */
373
380
  size?: "composer" | "sm";
381
+ /** 开着时也只有图标,见 `ComposerToggle` 的 `iconOnly`。 */
382
+ iconOnly?: boolean;
374
383
  })
375
384
  ```
376
385
 
@@ -45,7 +45,7 @@ export default {
45
45
  | `ctx.sessions.create({ parentId?, title, model?, thinking?, project?, cwd? })` / `send(id, message, { signal })` / `stop(id)` / `delete(id)` | 插件自己的会话(给 `parentId` 是子会话,不给是独立会话,见「子会话」「独立会话」):建、发一句并等结束、停、删。message 可为字符串或 `SessionMessage`(完整内容与附件;派发任务带 `origin: "parent"`),只能动自己建的 |
46
46
  | `ctx.sessions.start(id, message)` / `wait(runId)` / `report(runId, text, { frame?, delivery? })` | 后台运行立即交回 `{ id, sessionId }`;等待该次结束后,把结果送给它的真实父会话。父忙时安全步骤边界读入,闲时唤起;停止、过期、重复的结果返回 false,不能任意向会话发消息。`frame`(`{ head, tail?() }`):同一次送达里本插件带 `frame` 的几份合成一段,`head` 写一次,接着各份正文,最后是最后一份的 `tail()`(送出那一刻才取,比如「还在等谁」)。`delivery`(`{ id, agent, description, status, incomplete? }`)写进送达消息,宿主记下是哪个插件交的,界面把这一次送达里本插件的几份交给本插件经 `ui.agentDelivery` 登记的组件画成一行;不给就只有模型看得到 |
47
47
  | `ctx.sessions.steer(id, message)` / `onStopped(listener)` | 给正在运行的自有子会话插话,空闲时拒绝;明确停止会话时通知插件,清理尚未启动的排队工作,监听随插件卸载 |
48
- | `ctx.sessions.launch({ project?, cwd?, message, model?, spawnedFrom?, title? })` | 开一个新的顶层会话并发出第一句话,返回 `{ session, done, idle, reportBack }`(`done` 是第一轮为什么结束,`idle` 在它第一次真闲下来时兑现成 `{ stop?, reply }`(最近一轮的收场、最后一段回复),`reportBack(text, status)` 把结果作为一条运行时送达交回 `spawnedFrom` 那个会话一次,画成「来自新会话」一行,来处删了、归档了返回 false;`spawnedFrom.toolCallId` 不是你的工具在那个会话里的调用、或你的插件停用了时抛错);会话进侧栏、不记在插件名下,人能接着聊,插件之后不能再往里发。只能开新的,碰不到已有会话。见下文「开新会话」 |
48
+ | `ctx.sessions.launch({ project?, cwd?, message, model?, thinking?, spawnedFrom?, title? })` | 开一个新的顶层会话并发出第一句话(`model`、`thinking` 不给用默认模型、默认推理强度;不认识的模型、档位抛错),返回 `{ session, done, idle, reportBack }`(`done` 是第一轮为什么结束,`idle` 在它第一次真闲下来时兑现成 `{ stop?, reply }`(最近一轮的收场、最后一段回复),`reportBack(text, status)` 把结果作为一条运行时送达交回 `spawnedFrom` 那个会话一次,画成「来自新会话」一行,来处删了、归档了返回 false;`spawnedFrom.toolCallId` 不是你的工具在那个会话里的调用、或你的插件停用了时抛错);会话进侧栏、不记在插件名下,人能接着聊,插件之后不能再往里发。只能开新的,碰不到已有会话。见下文「开新会话」 |
49
49
  | `ctx.sessions.models()` | 无凭据的模型目录:`id`、`name`、`contextWindow`、`thinking`;用于插件选择子会话模型,密钥不出宿主 |
50
50
  | `ctx.pages` | 驱动界面入口画的网页(`<webview>`):`attach` 认下、`load`、`go`、`evaluate`、`input`、`insertText`、`capture`、`setZoom`、`emulate`、`devtools`、`answerDialogs`,`onEvent` 收页面的事件。见下文「网页」 |
51
51
  | `ctx.commands.provide(provider)` | 给输入框的 `/` 菜单供命令(见下面「`/` 命令」),停用时撤下 |
@@ -79,6 +79,7 @@ export default {
79
79
  | `ui.openLink(url, sessionId)` | 用户点击后像回复里的网址那样打开:有插件接手点击(`takesClicks`,内置浏览器)时开在它那里,否则系统浏览器;限制同 `ui.openExternal` |
80
80
  | `ui.settingsSection(id, Component, options)` | 加一节设置:默认画进本插件详情的「设置」一节;`page: "nav"` 时单独占设置导航一行(`group` 选「Agent 能力」「数据」「代码与版本控制」组),给别的 `page` 时画进产品的那一页(「个性化」「智能体」,或「智能体」里「会话」那张卡的一行,或「插件市场」的「技能」「连接器」两类),见下文 |
81
81
  | `ui.ModelSelect` | 产品的模型下拉:`{ value, onChange(modelId), ariaLabel, inheritLabel, inheritDetail?, inheritedModelId?, inheritedSource?, disabled?, compact? }`;`value` 为空串是「跟随」,菜单顶上那一项就是它,选了 `onChange("")`;`compact` 只剩一枚箭头,贴在它管的按钮旁边当分体按钮(计划卡的「在新会话里执行」),选了什么由那颗按钮自己说 |
82
+ | `ui.ModelControls` | 输入框右下角那两枚(模型、思考档位),样子和菜单都和输入框里的一样:`{ model, thinking?, onModelChange(modelId), onThinkingChange(level), disabled? }`;`model` 为空串、`thinking` 不给时显示默认模型和默认推理强度。不改哪个会话,只把选择交出来,给开新会话前先选好怎么跑的地方用(建议新会话的卡片),选的再交给 `ctx.sessions.launch` |
82
83
  | `ui.useModelDefaults()` | React hook:`{ defaultModel?, sessionModel?, thinking }`——新会话的默认模型、当前会话的模型、默认思考档位,「跟随」时写哪个模型名用它 |
83
84
  | `ui.useProjects()` | React hook:侧栏里的项目 `{ id, name, path }[]`,随增删更新;几个项目可能登记在同一个目录上 |
84
85
  | `ui.screen` | 截屏的宿主能力:开截图浮层、全局快捷键、选文件夹 |
@@ -10,7 +10,7 @@
10
10
  | --- | --- |
11
11
  | `~/.plume/bin/plume-plugin check` | 类型检查,加上插件规则:只引用公开接口、不写死色值和 Tailwind 类、界面文字不写死中文 |
12
12
  | `~/.plume/bin/plume-plugin build` | 打出 `dist/index.mjs`(和 `dist/ui.mjs`) |
13
- | `~/.plume/bin/plume-plugin install` | 把 `package.json`、`README.md`、`dist/` 复制进 `~/.plume/plugins/<目录名>/`,Plume 开着的话当场加载 |
13
+ | `~/.plume/bin/plume-plugin install` | 把 `package.json`、`README.md`、`dist/` 复制进 `~/.plume/local-plugins/<目录名>/`,Plume 开着的话当场加载 |
14
14
  | `~/.plume/bin/plume-plugin dev` | 构建、装上,之后一改源码就重新构建、装上 |
15
15
  | `~/.plume/bin/plume-plugin link` | `node_modules` 里指向 SDK 的链接坏了(Plume 挪了位置、npm 删了链接)时重建 |
16
16