Explore发现 / Widget pluginWidget 插件

Guide · Widget plugin教程 · Widget 插件

Write your first plugin in ten minutes.十分钟写出你的第一个插件

A plugin is one executable file. CrazeCode writes two JSON lines to its standard input and reads two JSON lines back. That is the whole protocol — there is no SDK to install and no framework to learn.一个插件就是一个可执行文件。疯码往它的标准输入写两行 JSON,再从标准输出读两行 JSON。整个协议就这么多——不用装 SDK,也没有框架要学。

Every command and every warning below was run end to end before it was written down. The four traps in step 5 are the ones that actually cost time.下面每一条命令和每一个提醒,都是先真跑通再写下来的。第 5 步那四个坑,是真正会浪费你时间的地方。

1 · Create a Dashboard to hold it1 · 先建一个面板放它

A plugin always targets a Dashboard. Skip this and the preview stops, naming the command to run: craze dashboard create main.插件必须挂在某个面板上。跳过这步,预览会停下并告诉你要运行的命令:craze dashboard create main。

On Linux, plugins run inside the bubblewrap sandbox, so install it once first: sudo apt install bubblewrap (or your distribution’s package). macOS uses its built-in sandbox. craze doctor tells you whether plugins can run.在 Linux 上,插件运行在 bubblewrap 沙箱里,请先安装一次:sudo apt install bubblewrap(或你的发行版对应的软件包)。macOS 使用系统自带沙箱。craze doctor 会告诉你插件能否运行。

$ craze dashboard create main
DASHBOARD CREATED · id=main · title=main · default=true

$ craze dashboard list
  main (default) · title=main · widgets=0

2 · Two files, one folder2 · 两个文件,一个目录

The manifest declares identity and limits; the script does the work. executable is resolved relative to the manifest, so both files sit together.manifest 声明身份与限额,脚本负责干活。executable 是相对 manifest 解析的,所以两个文件放在一起。

$ mkdir -p ~/my-widget && cd ~/my-widget
$ curl -fsSLo manifest.json https://crazecode.com/auth/assets/acec54ab179d/plugin-demo.json
$ curl -fsSLo plugin.sh    https://crazecode.com/auth/assets/acec54ab179d/plugin-demo.sh
$ chmod +x plugin.sh

3 · What the host sends you3 · 宿主发给你什么

Two lines arrive on stdin. Note the tag key: requests use method, your responses use type.stdin 上会来两行。注意标签字段不一样:请求用 method,你的回复用 type。

// line 1// 第一行
{"protocol_version":1,"request_id":"handshake",
 "request":{"method":"handshake","supported_versions":[1],
            "capabilities":["typed_value"],"permissions":["widget_identity"]}}

// line 2// 第二行
{"protocol_version":1,"request_id":"refresh",
 "request":{"method":"refresh","attempt_revision":7,
            "widget_id":"w-demo","provider_id":"p-demo","now_ms":null}}

Permissions decide what you can see. Without widget_identity the ids are null; without attempt_clock so is now_ms. Ask for nothing you do not use.权限决定你能看见什么。不申请 widget_identity,两个 id 就是 null;不申请 attempt_clock,now_ms 也是 null。用不到的就别申请。

And what you must send back而你必须回什么

// line 1: accept the protocol// 第一行:接受协议
{"protocol_version":1,"request_id":"handshake",
 "response":{"type":"handshake","selected_version":1,
             "capabilities":["typed_value"],"permissions":["widget_identity"]}}

// line 2: return the value. revision must equal attempt_revision above// 第二行:返回数据。revision 必须等于上面的 attempt_revision
{"protocol_version":1,"request_id":"refresh",
 "response":{"type":"value","value":{"schema_version":1,
   "widget_id":"w-demo","revision":7,"kind":"metric","title":"Demo",
   "updated_at_ms":0,"fresh_until_ms":0,"status":"ok",
   "summary":{"value":"42"},
   "data":{"value":"42","unit":null,"change":null},"actions":[],
   "provenance":{"provider_id":"p-demo","source_class":"plugin"},
   "truncated":false}}}

4 · Import it4 · 导入

Preview writes nothing. It says what the plugin will run and what it is told, then prints the one import command for exactly those bytes: its preview_sha256 binds what you just read, and import only proceeds with it.preview 不写任何东西。它说明插件会运行什么、会得到什么,最后打印只对这些确切字节有效的导入命令:命令里的 preview_sha256 绑定你刚读到的内容,import 只认这个摘要。

$ craze dashboard plugin preview manifest.json
  Runs plugin demo-widget 1.0.0 (…/plugin.sh) in a sandbox with no network, files or credentials.在沙箱中运行插件 demo-widget 1.0.0(…/plugin.sh),不能联网、读写文件或使用凭据。
  It is told only its Widget's name.它只会得到它的 Widget 名称。
To import exactly what this preview shows:确认无误后导入(只对这份预览有效):
  craze dashboard plugin import manifest.json --yes --preview-sha256 d3cbd7f0…

$ craze dashboard plugin import manifest.json --yes --preview-sha256 d3cbd7f0…
$ craze dashboard plugin refresh demo-widget
Refreshed demo-widget: 42已刷新 demo-widget:42

5 · The four traps5 · 四个坑

Each of these produced a real failure while this page was being written.这四条都是写这一页时真撞出来的。

Echo the attempt number必须回显 attempt 号
Your value.revision must equal the request’s attempt_revision. A hard-coded 1 passes the first refresh and fails every one after it: “the value it returned could not be read” (the Widget’s details say “invalid value”).返回值的 value.revision 必须等于请求里的 attempt_revision。写死 1 的话第一次会过,之后每次都失败:「返回的值无法读取」(Widget 详情中显示「值无效」)。
No fork, no pipes不能 fork,不能用管道
The sandbox denies fork(). A shell plugin cannot use $(…), pipes, or sed/awk/jq — only builtins. The demo parses JSON with parameter expansion alone.沙箱禁止 fork()。shell 插件不能用 $(…)、管道,或 sed/awk/jq,只能用内建语法。示例仅用参数展开就解析出了 JSON。
Changed a byte? Preview again改了任何字节?重新 preview
The printed command carries that preview’s preview_sha256. Edit the script or manifest and it no longer matches; run preview again and use the new command. --json prints the full plan for scripts.打印出的命令带着那一次预览的 preview_sha256。改了脚本或清单后它就不再匹配;重新 preview,使用新命令。脚本需要完整计划时加 --json。
Failure means backoff失败后会退避
After a failed attempt the provider backs off for a few seconds and refuses to run. Fix the script, re-import, then wait for the retry window.一次失败之后,provider 会退避几秒并拒绝执行。改好脚本、重新导入,然后等退避窗口过去。

What a plugin can never do插件永远做不到的事

These are boundaries of the sandbox, not conventions you are asked to respect.这些是沙箱的硬边界,不是靠自觉遵守的约定。

Reach outside its package越出自己的包

Reading an undeclared user file fails. The executable path must be package-relative with no parent traversal.读取未声明的用户文件会失败。可执行文件路径必须是包内相对路径,不允许向上穿越。

Own the terminal占用终端

A plugin returns structured values only. It never writes raw escape sequences, never owns a key binding, and draws nothing while a native client holds the screen.插件只返回结构化数据。它不写任何转义序列、不拥有按键,原生客户端占屏时它一个字都不画。

Run unbounded无限制运行

timeout_ms and max_output_bytes are enforced by the host. A crash, a hang or a cancellation stays local to the plugin.timeout_ms 与 max_output_bytes 由宿主强制执行。崩溃、卡死或取消都只影响这个插件自己。

The complete demo完整示例

Both files are served from this server and are the exact ones verified above. The other official widgets use the same two-file shape. Each link saves the file under the name its manifest expects; put the two files of one widget in one folder.两个文件由本服务器提供,就是上面验证通过的那两个。其他官方 Widget 也是同样的两文件结构。每个链接都按清单里写的文件名保存;同一个 Widget 的两个文件放在同一个目录。

A downloaded script is never executable. Before the preview, run chmod +x on it, for example:下载的脚本不会自带执行权限。预览之前先对它运行 chmod +x,例如:

$ chmod +x focus-pulse.sh
$ craze dashboard plugin preview focus-pulse.json