# plugin-hello **Repository Path**: vcms/plugin-hello ## Basic Information - **Project Name**: plugin-hello - **Description**: 基于HiWorker-OS桌面工作台的插件开发示例。 - **Primary Language**: Unknown - **License**: GPL-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-11-02 - **Last Updated**: 2026-11-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # plugin-hello —— HiWorker 模板级示例插件 > 这是什么:HiWorker 工作台(宿主 / 基座)插件机制的**可复制模板 + 活文档**。 > 它把「一个插件如何与基座通信」的全过程摊开成能点击、能实测的样例: > 主进程侧逐项演示宿主 `ctx` 全 API,渲染端演示 IPC 三种通信(invoke / send / on), > 还自带一份 Vue3 + Arco 的标准列表 / 表单模板。新插件开发直接 `cp -R plugin-hello plugin-{id}`。 > > 契约文档(宿主侧权威):[HiWorker-OS/docs/plugin-dev-guide.md](../HiWorker-OS/docs/plugin-dev-guide.md)、 > [HiWorker-OS/docs/plugin-design.md](../HiWorker-OS/docs/plugin-design.md)。本 README 是「照着敲」的实操版。 ## 1. 目录结构 ``` plugin-hello/ ├── manifest.json # 插件声明:id/devPort/windows/permissions/publicChannels/launcher ├── package.json # type:module;dev / build / pack 脚本 ├── main/ # 主进程侧(纯 ESM JS,不构建,随 dist 原样拷贝) │ ├── index.js # activate(ctx)/deactivate():装配顺序模板 │ ├── db.js # PluginDB:包装 ctx.db / ctx.dbp,提供 promise + 分页 + 事务 │ ├── schema.js # 建表(IF NOT EXISTS 幂等)+ 增量加列迁移声明 │ ├── ipc.js # registerHelloIpc:所有 hello-* 通道集中注册 │ ├── http-server.js # HelloApiServer:ctx.http 起 express 服务(默认不启动) │ └── services/ │ ├── base.js # BaseService:字段白名单 + 分页 + 排序白名单防注入 │ └── book.js # BookService:示例业务(hello_book 表) ├── renderer/ # 渲染端:独立 vite 工程、自包含,不 import 宿主 src/ │ ├── index.html │ ├── vite.config.js # base:'./'、outDir ../dist/renderer、server.port=5199 strictPort │ └── src/ │ ├── main.js # 先 win-get-config 挂 window.config,再挂 Vue/Router/Pinia/Arco │ ├── App.vue / style.css │ ├── router/ # hash 路由,与 manifest.windows 一一对应 │ ├── pinia/ # 极简状态(皮肤变量),无持久化 │ ├── windows/actions.js # 窗口控制(launchApp/winSet) │ ├── utils/ipc.js # invoke/on 封装:序列化兜底 + 错误归一 │ ├── components/ │ │ ├── winbar/ # 宿主导航条复制件(index.vue/btns.vue/index.scss,图标改 Arco) │ │ ├── DataTable.vue # 通用列表:搜索条 + 表格 + 外置分页 + 服务端排序 │ │ └── FormModal.vue # 通用弹层:声明式字段驱动的新增/编辑表单 │ └── views/ │ ├── layout.vue # WinBar + 皮肤变量 + 左侧导航 │ ├── home/ # ctx API 能力总览(逐卡片实测) │ ├── books/ # 标准列表页 CRUD 模板 │ ├── http-api/ # ctx.http 服务开关 + curl 示例 │ └── settings/ # 环境自查 + manifest 回显 + 复制指引/checklist ├── dist/ # 构建产物(git 忽略):manifest + package.json + main/ + renderer/ └── data/hello.db # 私有库(git 忽略) ``` ## 2. 与基座的接口:ctx API ↔ 通道对照表 `activate(ctx)` 收到的 `ctx` 由宿主按 `manifest.permissions` 挂载(**fail-closed:未声明即不存在**)。 每条 `hello-*` 通道就是某项 ctx 能力的活例子,在「能力总览」页都能点着实测: | 通道 | 演示的宿主接口 | 说明 | |---|---|---| | `hello-ping` | `ctx.registerIpc` + `ctx.oplog.record` + `ctx.db` | 最简请求-响应;落私库 `hello_log`,记宿主操作日志 | | `hello-book-page` | `ctx.dbp`(promise 版)+ service 分层 | 列表分页 / 关键词搜索 / 服务端排序 | | `hello-book-get` | `ctx.dbp` | 单条读取 | | `hello-book-save` | service + `event.sender.send` | 有 id 走 update 无则 create;保存后回推 `hello-events` | | `hello-book-delete` | service | 删除 + 回推事件 | | `hello-notify-send` | `ctx.notify.send` | 向宿主通知中心发通知(需 `notify` 权限)| | `hello-env` | `ctx.id` / `ctx.manifest` / `ctx.log` | 环境自查:回带 manifest 声明与权限挂载情况 | | `hello-call-dam` | `ctx.call` | 跨插件调用(演示依赖链校验与友好降级,不崩溃)| | `hello-public-info` | 被调方 | 列入 `publicChannels`,供其他插件 `ctx.call` | | `hello-user-list` | `ctx.user.list` | user.read:只回脱敏资料 | | `hello-user-verify` | `ctx.user.verify` | user.verify:**仅主进程可用**,渲染端须自包通道透传 | | `hello-api-start/stop/status` | `ctx.http` | express 服务生命周期(默认不启动)| | `hello-win-open` | `ctx.winCreate` | 主进程开本插件新窗口(route 对应 manifest.windows)| | `hello-event-echo` | `event.sender` + 渲染端 `on` | 主进程回推 `hello-events`,渲染端监听 | 渲染端通信一律走 preload 暴露的 `window.electron`(宿主 `buildAcl` 按权限生成白名单),**绝不自建 ipcRenderer**: - `invoke` → 请求-响应(`{id}-*` 与部分 `win-*` 通道); - `send` → fire-and-forget(`win-set` 窗口动作等); - `on` → 监听主进程回推(`{id}-*`、`win-*` 事件)。 ## 3. 常用模板与组件 - **主进程装配顺序**(`main/index.js`):建表 → 增量迁移 → service → HTTP 实例 → 注册通道。业务插件照抄即可。 - **BaseService**(`main/services/base.js`):字段白名单写入、`textCols` 关键词 LIKE、`filters` 声明式等值、`sortable` 白名单防 ORDER BY 注入、`paginate` 分页。加密/掩码等高级能力见 plugin-dam 的 BaseService。 - **DataTable + FormModal**(`renderer/src/components/`):`views/books/index.vue` 是二者拼出完整 CRUD 的范例——搜索、分页、服务端排序(点表头升/降/取消)、新增/编辑弹层、删除确认。 - **WinBar 复制件**(`renderer/src/components/winbar/`):从宿主复制、图标改用 Arco(插件包不含宿主 iconfont),配 `layout.vue` 的皮肤变量注入即与宿主窗口一致。 - **utils/ipc.js**:`invoke` 对参数做 `JSON.parse(JSON.stringify)` 兜底,剥掉 Vue reactive Proxy,防「An object could not be cloned」。 ## 4. 本地联调(dev) ```bash # 终端 1:宿主 cd HiWorker-OS && npm run dev # 终端 2:本插件渲染端热更 cd plugin-hello && npm run dev # vite 固定监听 5199(strictPort,绝不漂移) ``` 然后宿主「系统配置」开启**开发者模式**,把 `hi-work/plugin-hello` 加入 dev 插件路径 → **重启宿主** → 桌面出现 Hello 图标。 - renderer 改动即时热更;main 侧改动需在插件管理页点「重载插件」或重启宿主。 - ⚠️ **devMode 下 dev 源插件恒走 `http://localhost:`,绝不回退已构建的 `dist/renderer`**(见宿主 `manager.js resolveWindow`:dev 插件仅当 `devMode=true` 才被发现,一旦发现即优先用 devPort URL)。因此**只要开着 devMode,本插件的 dev server(终端 2)就必须一直跑着**——一旦 `npm run dev` 被关掉,即使 `dist` 完好,打开插件窗口也是**整页空白**。快速判定:`curl -s -o /dev/null -w '%{http_code}' http://localhost:5199/` 应秒回 **200**;回 `000` / 连接失败即 dev server 没起(或被端口漂移占了)。详见 [docs/known-pitfalls.md](docs/known-pitfalls.md)。 ## 5. 打包与安装 ```bash cd plugin-hello npm run build # vite build renderer → cp manifest.json package.json dist/ → cp -R main dist/main npm run pack # dist 打成 plugin-hello.zip ``` - dist **必须含 `package.json`(`"type":"module"`)**——缺失时 installed 形态 Node 按 CJS 解析插件 main,激活报 "Cannot use import statement outside a module"。build 脚本已 `cp`。 - 宿主 `/plugins` 管理页 → 安装 → 选 zip → 核对权限 → 确认。卸载保留 `data/`,重装同名自动恢复数据。 ## 6. HTTP API 服务 `ctx.http` 提供宿主的 `express/cors`(zip 无需打包)。默认**不启动**,「HTTP API」页手动开启,仅监听 `127.0.0.1:5198`、无鉴权(示例定位,生产插件请参照 plugin-finance 加 apiKey)。 也可用环境变量 `HIWORKER_HELLO_API=1` 让插件 activate 时自启。路由复用同一个 `BookService`,与 IPC 两入口共享业务: ```bash curl http://127.0.0.1:5198/api/hello/v1/health curl "http://127.0.0.1:5198/api/hello/v1/books?page=1&pageSize=10" ``` ## 7. 新建插件(复制五步) 1. 工作区根 `cp -R plugin-hello plugin-{id}`(改目录名 = 去 `plugin-` 前缀后的 id,全小写英文,发布后不可改); 2. 改 `manifest.json`:id/name/version/**devPort(独占,勿与宿主 5173 及其他插件冲突)**/windows/permissions 最小化; 3. 改 `main/`:`schema.js` 建表 → `services/` 业务 → `ipc.js` 通道(全部 `{id}-` 前缀); 4. 改 `renderer/`:抄 `views/books` 拼业务页,`vite.config.js` 端口同步 manifest.devPort; 5. 宿主开开发者模式 + 加入 dev 路径 → 重启 → 出图标。 ## 8. 发布检查清单 - [ ] manifest:id 与目录一致、version 递增、permissions 最小化、depends 完整 - [ ] 通道全部 `{id}-` 前缀;publicChannels 已列出需对外通道 - [ ] renderer `base:'./'`、devPort 独占且 strictPort、WinBar 复制件 + 皮肤变量齐备 - [ ] 建表幂等(IF NOT EXISTS);含迁移时旧库可平滑升级(只增列不删列) - [ ] 主进程不引 sqlite3;渲染端只用 `window.electron`,本地图片走 data URL - [ ] `npm run pack` 产物在干净宿主上安装、启停、卸载、重装(数据保留)四项通过 ## 9. 常见坑(本仓专用清单见 [docs/known-pitfalls.md](docs/known-pitfalls.md);更全见宿主 [docs/known-pitfalls.md](../HiWorker-OS/docs/known-pitfalls.md)) - **devMode 下白屏九成是 dev server 未启动**:dev 源插件恒走 `http://localhost:`、不回退 dist,关掉 `npm run dev` 即整页白屏(即使 dist 完好);先 `curl` 5199 看是否 200; - Vue reactive 对象直接过 IPC → "could not be cloned"(`utils/ipc.js` 已兜底,仍请传纯对象); - devPort 冲突 / vite 静默漂移端口 → 插件窗口白屏(`strictPort:true` 已防); - 主进程路径处理用 `fileURLToPath`,禁止手拼 `file://`; - Arco 图标组件传字符串名会渲染空白,需 `markRaw` / 直接引组件; - `ctx.user.verify` 只在主进程存在,渲染端拿不到,须插件自包一条 `{id}-*` 通道透传。