跳转到内容

开发环境与调试

宿主启动时按优先级顺序扫描多个根目录,每个含 plugin.json 的子目录是一个插件:

  1. 仓库源码根(plugins/builtin/、plugins/marketplace/ —— 从源码跑宿主时)
  2. 用户安装目录 ~/xinghan/plugins/(市场安装与手动放置都落这里)

同 id 去重:先见者胜。你在源码里开发一个与安装版同 id 的插件,源码版生效 —— 这正是为了”改内置插件不用先卸载”。

单个插件的 manifest 不合法只会被跳过并打日志(fail-soft),不影响其它插件加载。所以”我的插件没出现”第一步永远是看日志。

本地开发用 dev. 前缀 id(如 dev.hello),与正式发布的 reverse-DNS id(如 xinghan.pomodoro)区分开,避免与市场安装版撞车。

改了什么需要做什么
ui/ 静态文件刷新面板即可(iframe 重新加载)
plugin.json(manifest)重启应用,或调内部 reload 端点(见下)
node 插件的后端代码同上 —— 后端是独立进程,改代码要重启该进程

内部 reload 端点(进阶):plugin-host 暴露 POST /_internal/reload 做对账 —— 注意它当前的行为是重启对账范围内的所有插件(不是只重启版本变化的),全量 reload 会把每个插件都重启一遍:

Terminal window
# 端口是动态的,先从宿主日志里找 "[plugin-host] listening on http://127.0.0.1:<port>"
curl -X POST 'http://127.0.0.1:<port>/_internal/reload?id=dev.hello'
# → {"ok":true,"data":{"added":[],"removed":[],"reloaded":["dev.hello"]}}

不带 ?id= 是全量对账。日常开发重启应用最省心,写脚本自动化迭代时再用它。

runtime: "node" 的插件有两个入口字段:

{
"entry": "dist/server.js",
"devEntry": "src/server.ts"
}
  • entry 是生产入口(构建产物);
  • devEntry 是开发入口 —— 宿主 dev 模式下用它启动(配合 tsx 直接跑 TypeScript 源码,免构建)。缺省时开发和生产都用 entry。

前端同理:视图声明里的 devUiUrl 可以指向你自己的 vite dev server(如 http://localhost:3001),宿主会把 iframe 反代到它而不是静态文件 —— 于是你的插件 UI 也有热更新。

plugin-host 与插件进程的 stdout/stderr 都汇入宿主日志。打包发行的应用日志在 ~/.XH-Editor/logs/;源码开发(pnpm dev)直接打在终端(无日志目录环境变量时才退到 ~/xinghan/logs/):

Terminal window
# 打包应用:实时盯宿主日志,过滤自己的插件
tail -F ~/.XH-Editor/logs/*.log | grep -i "dev.hello\|plugin-host"

加载被跳过的插件会打一行 skip <路径>/plugin.json: <原因> —— “我的插件没出现”先搜这个。

其它两处:

  • 插件后端可以用 cap.log(append-only 日志流:append/range)记录结构化事件,方便回看;
  • iframe 里的前端日志用浏览器 DevTools:桌面端菜单打开开发者工具,Console 面板左上角的上下文下拉里选中你的 iframe。
  • manifest 校验规则:id/name/version 必填;runtime 只认 node/static;node 必须有 entry;static 必须有 ui;capabilities 必须是数组(哪怕为空)。任何一条不满足整个插件被跳过。
  • Docker 环境插件默认关:run-mongo.sh 起的容器默认 SKIP_PLUGINS=1。要在容器里验证插件,显式带 SKIP_PLUGINS=0。
  • capability 声明如实填:这个数组是市场与用户看到的授权申报面。运行时强制目前只覆盖卫星作用域(satellite:<kind> 未声明访问 403),别依赖”没声明会被拦”做安全假设。
  • 卫星项目访问:要读写卫星库(如 Agent 会话库)必须声明 satellite:<kind>,否则一律 403。