A plugin is any executable that follows the JSON protocol:
- Read the request from stdin.
- Write the response to stdout.
- Exit with a non-zero status on failure.
{
"version": 1,
"kind": "widget",
"plugin": "timezone",
"args": { "zones": [{ "label": "Tokyo", "utc_offset": 9 }] },
"context": { "now": "2026-09-10T18:22:01+02:00", "width": 0, "height": 0 }
}Static lines:
{ "version": 1, "lines": ["Tokyo 01:22"] }Animation frames:
{
"version": 1,
"lines": [],
"frames": [
{ "delay_ms": 80, "lines": ["frame 1"] },
{ "delay_ms": 80, "lines": ["frame 2"] }
]
}Error:
{ "version": 1, "lines": [], "error": "network unavailable" }- Declare a runtime budget and return a fallback response when it elapses.
- Never block forever: the kernel kills plugins that exceed its timeout.
- Keep responses small: the kernel renders them inside a widget.
- Handle missing or invalid
argswith sensible defaults.
[package]
name = "xclock-plugin-example"
version = "0.1.0"
edition = "2024"
license = "MIT"
[[bin]]
name = "example"
path = "src/main.rs"
[dependencies]
xclock-plugin-api = { git = "https://github.com/xclock-cli/api" }use xclock_plugin_api::{read_request, write_response, Response};
fn main() -> std::io::Result<()> {
let request = read_request()?;
let label = request
.args
.get("label")
.and_then(|value| value.as_str())
.unwrap_or("example");
write_response(&Response::lines(vec![format!("hello from {label}")]))
}