08 · 插件系统¶
插件是 Omarchy Shell 的代码扩展单位。manifest 描述身份和入口,PluginRegistry 负责发现、校验、启用与解析,宿主负责实例化和注入依赖。
manifest 是最小契约¶
{
"schemaVersion": 1,
"id": "acme.weather",
"name": "Weather",
"version": "1.0.0",
"kinds": ["bar-widget", "panel"],
"entryPoints": {
"barWidget": "BarWidget.qml",
"panel": "Panel.qml"
}
}
支持六种 kind:
| kind | 宿主语义 |
|---|---|
bar |
替换整个 active bar,同一时刻一个 |
bar-widget |
放入 bar section,可声明是否多实例 |
service |
无 UI 的常驻 singleton |
panel |
浮动窗口,按需召唤 |
overlay |
全屏或覆盖型 surface |
menu |
可路由召唤的菜单 surface |
一个 manifest 可声明多个 kind,并为每种 kind 映射对应 entrypoint。
发现范围¶
PluginRegistry 扫描两类根:
- first-party:
$OMARCHY_PATH/shell/plugins,允许嵌套到有限层级,也识别 sibling*.manifest.jsonbar widgets; - third-party:
~/.config/omarchy/plugins/<id>,每个插件根含manifest.json。
固定快照有 37 份 manifest。限制扫描深度避免把插件资源树中的任意 JSON 都误当成 manifest,也让发现成本可控。
校验分两层¶
运行时校验¶
Registry 拒绝:
- 非
schemaVersion: 1; - 缺少必需 ID、name、version、kinds 或 entryPoints;
- entrypoint 为空、绝对路径或含
..; - 非法 kind / entrypoint 映射;
- third-party 使用保留的
omarchy.*ID; - third-party 试图覆盖 first-party ID。
entryPointUrl 再做路径解析防御,避免 manifest 验证和真正 URL 拼接之间出现穿越差异。
CLI 安装前校验¶
omarchy-plugin-validate 更严格:验证入口文件存在、kind 与 key 匹配,并拒绝插件树中(排除 .git)的任何符号链接。安装前阻断比等 QML Loader 报错更易理解,也防止 repo 内链接逃出插件根。
flowchart LR
Repo[插件 git repo] --> CLI[plugin validate]
CLI -->|拒绝 symlink/坏 manifest| Stop[不启用]
CLI --> Root[~/.config/omarchy/plugins/id]
Root --> Registry[运行时 parse/merge]
Registry --> URL[安全 entryPointUrl]
URL --> Loader[QML Loader]
enabled 不是一个布尔规则¶
启用语义依 kind 与来源不同:
| 插件 | 启用条件 |
|---|---|
| full bar | shell.json 的 bar.id 指向它 |
| bar widget | 出现在某个 bar.layout.<section> |
| first-party 非 bar | 默认启用,除非在 disabledPlugins[] |
| third-party 非 bar | 明确出现在 plugins[] |
first-party service 默认开,是因为通知、锁屏、idle 等属于桌面基础设施;third-party 默认关,是因为插件在 Shell 进程内以未沙箱化代码运行。
安装流程为何要求人工确认¶
omarchy plugin add clone repo 后保持禁用,让用户先审代码。update 会展示 diff,再 fast-forward。即使参数完整,交互路径也倾向在终端中展示警告、编辑器和确认;自动化必须显式 --yes。
这不是完整供应链沙箱,但清楚表达了授权时刻:clone 只是取得代码,enable 才是允许它在桌面进程中执行。
first-party ID 不能被抢占¶
omarchy.* 是保留命名空间,且 first-party 合并优先。否则用户目录中一个 omarchy.lock 就可能冒充锁屏服务,造成隐蔽而严重的信任替换。
内置插件的 clone 走另一条显式机制:新插件使用用户名命名空间,并用 clonedFrom 保留原有 IPC 身份/路由。当 active clone 删除时,系统可以退回原内置来源。
热重载¶
Registry 对用户插件目录使用 inotify。保存 QML/JS 触发重新发现与组件更新,开发时无需重启整个 Shell。
热加载的边界是“代码身份重新解析”,不是任意状态迁移保证:
- 旧 QML 实例可能被销毁;
- 插件应把必要持久状态放在明确文件或服务中;
- first-party package 内容不需要同样的开发热重载路径;
- manifest ID 与 kind 变化可能改变宿主路由。
宿主注入的能力¶
panel/overlay/menu entrypoint 作为 QML Item 加载,宿主注入:
omarchyPath;shell根对象;manifest;pluginRegistry与barWidgetRegistry。
surface 约定暴露 open(payloadJson) 与 close()。这是一种轻量协议:插件不用继承一个庞大基类,宿主也不用理解插件内部 UI。
威胁模型小结¶
| 风险 | 防线 | 仍需用户承担 |
|---|---|---|
| 路径穿越 | relative entrypoint 校验 + URL 防御 | 审核其他代码行为 |
| symlink 逃逸 | CLI validator 拒绝所有 symlink | 手工安装可绕过 CLI |
| 冒充内置插件 | 保留命名空间 + first-party 优先 | 不启用未知第三方代码 |
| 更新夹带代码 | 更新前展示 diff | 真正阅读关键 diff |
| 插件破坏 Shell | custom bar fallback;其他加载错误隔离 | 插件仍与 Shell 同进程、无沙箱 |
关键源码索引¶
shell/services/PluginRegistry.qml校验与解析shell/services/PluginRegistry.qmlmanifest 合并shell/services/PluginRegistry.qmlinotify/rescanbin/omarchy-plugin-validate— 安装前结构/符号链接校验bin/omarchy-plugin-add— clone、确认与禁用安装docs/omarchy-shell.md— manifest 与 enable 语义
下一章看一个内置插件如何把复杂交互压成数据模型:09 · 菜单的数据模型。