构建扩展
无头命令
无 UI 的后台命令——可手动触发、定时执行或事件驱动。
无头命令运行逻辑而不显示 UI:它们可手动触发、定时执行或由事件驱动,完成工作后可选地显示提示,然后退出。
| 模式 | 运行时 | Node.js | 使用场景 |
|---|---|---|---|
worker-headless | Web Worker | 否 | 简单的后台工作,无需文件系统 |
node-headless | Node.js / Deno | 是 | 系统操作、文件系统、外部 API |
生命周期入口
使用 startKunkunHeadlessPlugin 注册你的命令。宿主驱动 init → onTrigger → destroy:
// src/index.ts
import { startKunkunHeadlessPlugin, type TriggerContext } from "@kunkunsh/sdk/runtime";
import { showToast, Toast, LocalStorage } from "@kunkunsh/sdk";
startKunkunHeadlessPlugin({
async init() {
// 一次性设置:加载偏好设置、打开连接
},
async onTrigger(context: TriggerContext) {
const raw = await LocalStorage.getItem("count");
const count = (parseInt(raw ?? "0", 10) || 0) + 1;
await LocalStorage.setItem("count", String(count));
await showToast({ title: "已运行", message: `#${count}`, style: Toast.Style.Success });
},
async destroy() {
// 清理:刷新、关闭连接
},
});context 描述了命令触发的原因(手动启动、定时任务或订阅的事件)。
构建
// build.ts — worker-headless
import { kunkunCommandPlugin } from "@kunkunsh/sdk/build";
import type { BunPlugin } from "bun";
await Bun.build({
entrypoints: ["./src/index.ts"],
outdir: "./dist",
target: "browser", format: "esm", minify: true,
plugins: [kunkunCommandPlugin({ mode: "no-view" }) as BunPlugin],
});对于 node-headless,使用 target: "node" 和 outdir: "./dist/node"。
Tarball 安装不会填充 node_modules——请将 JS 依赖打包到输出中。原生 .node 插件:打包加载程序 + 平台文件,或惰性加载。
清单
{
"name": "sync",
"title": "立即同步",
"mode": "node-headless",
"main": "dist/node/index.js",
"permissions": [{ "permission": "fs-read", "allow": ["$HOME/**"] }, "notifications"]
}调度与触发器
无头命令可以在用户未启动的情况下运行:
{ "name": "poll", "mode": "node-headless", "main": "dist/node/index.js", "interval": "5m" }{ "name": "nightly", "mode": "node-headless", "main": "dist/node/index.js", "cron": "0 3 * * *" }interval—"30s" | "5m" | "1h" | "1d"或秒数(数字)。cron— 标准 cron 表达式。
你也可以在无头 worker 中通过事件 API 订阅系统事件(如 clipboard:change);sample-headless-worker 扩展演示了这一点。
Node 访问权限
在 node-headless 中你拥有 fs、shell、path、dialog 和网络访问权限:
import { fs, shell, path } from "@kunkunsh/sdk";
const home = await path.homeDir();
const entries = await fs.readDir(home);
const { stdout } = await shell.execute("echo", ["hi"]);运行时选择
node-headless 按以下顺序选择运行时:Deno → Node v20+ → utilityProcess(自动)。强制指定:
{ "name": "deno-only", "mode": "node-headless", "main": "dist/node/index.js", "runtime": "deno" }最佳实践
- 保持运行快速且幂等。
- 在适当的时候用
showToast提供反馈。 - 捕获并报告错误——不要让定时任务静默失败。
- 使用
LocalStorage或记录存储持久化计数器/缓存。
实际示例
sample-headless-node— cron 风格的 Node 任务sample-headless-worker— 由clipboard:change触发的 worker