Skip to content

Latest commit

 

History

History
367 lines (315 loc) · 10.7 KB

File metadata and controls

367 lines (315 loc) · 10.7 KB

Developing Plugins for xfetch

xfetch plugins are standalone executables that communicate with the core via stdin/stdout using a JSON protocol. This document describes the protocol, the required binary naming conventions, and best practices for creating your own plugin.

The shared protocol crate is maintained in xfetch-cli/api.

Official plugins and third-party plugins are expected to use this crate instead of reimplementing protocol parsing manually.

Table of Contents

How It Works

  1. xfetch discovers the plugin binary (see Discovery).
  2. xfetch spawns the plugin process.
  3. xfetch writes a JSON request to the plugin's stdin.
  4. The plugin processes the request and writes a JSON response to stdout.
  5. xfetch reads the response and uses it for rendering.
  6. Errors are written to stderr; the plugin exits with a non-zero status on failure.

Binary Naming

The plugin binary must follow this naming convention so xfetch can discover it:

xfetch-plugin-<name>
Platform Example
Linux / macOS xfetch-plugin-animate-logo
Windows xfetch-plugin-animate-logo.exe

The Cargo.toml name field should follow the same convention:

[package]
name = "xfetch-plugin-<name>"
version = "0.1.0"
edition = "2024"

Discovery

When running a plugin, xfetch searches for the binary in the following order:

  1. Explicit path from the configuration (if provided).
  2. PATH environment variable, looking for xfetch-plugin-<name>.
  3. User plugin directory: ~/.config/xfetch/plugins/.
  4. Workspace target directory of the plugins repository: target/release/.
  5. Legacy development path: ./plugins/<name>/target/release/.

WebAssembly artifacts use the same discovery: installers place them as xfetch-plugin-<name>.wasm (plus a sidecar manifest), and development builds are looked up under target/wasm32-wasip1/release/.

Protocol

The plugin protocol uses JSON over stdin/stdout. The plugin reads exactly one JSON object from stdin and writes exactly one JSON object to stdout.

Request Format

{
  "version": 1,
  "kind": "<plugin_kind>",
  "lines": ["line 1", "line 2"],
  "frames": [
    ["frame 1 line 1", "frame 1 line 2"],
    ["frame 2 line 1", "frame 2 line 2"]
  ],
  "args": {
    "fps": 12,
    "duration_ms": 1200,
    "loop": false,
    "style": "sweep"
  }
}
Field Type Description
version u32 Protocol version (currently 1).
kind string Plugin type: "logo_animation" or "info_provider".
lines array[string] Logo ASCII art, one line per element.
frames array[array[string]] or null Optional pre-loaded frame sets for frame-based animation.
args object Plugin-specific arguments from the user config.

Response Format

{
  "frames": [
    {
      "delay_ms": 83,
      "lines": ["colored line 1", "colored line 2"]
    }
  ]
}
Field Type Description
frames array[Frame] Array of animation frames (required). Must not be empty.

Frame Object

Field Type Description
delay_ms u64 Milliseconds to display this frame before advancing.
lines array[string] Frame content (may include ANSI escape codes for color).

Example Plugin

The animate-logo plugin is the reference implementation. Its source is at plugins/animate-logo/src/main.rs.

Minimal plugin skeleton in Rust using the shared crate:

use xfetch_plugin_api::{
    read_info_plugin_args_or_default,
    write_info_lines,
};

#[derive(Debug, Default, serde::Deserialize)]
struct PluginArgs {}

fn main() {
    let _args = match read_info_plugin_args_or_default::<PluginArgs>() {
        Ok(value) => value,
        Err(err) => {
            eprintln!("{}", err);
            std::process::exit(1);
        }
    };

    if let Err(err) = write_info_lines(vec!["hello from plugin".to_string()]) {
        eprintln!("{}", err);
        std::process::exit(1);
    }
}

Testing

Test your plugin manually by writing a request file and piping it:

echo '{"version":1,"kind":"logo_animation","lines":["hello"],"args":{"fps":12}}' \
  | ./target/release/xfetch-plugin-my-plugin

You can also install it locally and test it with xfetch:

xfetch plugin install my-plugin
xfetch

WebAssembly Plugins

Plugins can also be WebAssembly artifacts. The core detects them by their binary header and runs them in a sandboxed runtime with the exact same JSON protocol, so the request/response formats above apply unchanged.

  • Core modules (wasm32-wasip1) run as WASI commands; host capabilities (HTTP, processes, filesystem, environment) are exposed through the xfetch.host_call bridge, wrapped by the xfetch-guest-api crate.
  • Components export run from the xfetch:runtime WIT world and use typed host imports; they can be built with componentize-py, componentize-js or wit-bindgen.
  • A sidecar manifest (xfetch-plugin.json in the source, installed as xfetch-plugin-<name>.json) declares deny-by-default capabilities and runtime limits.
  • Tooling: xfetch wasm inspect, xfetch wasm run and xfetch wasm wit.

Reference examples live in plugins/: wasm-crypto (Rust, HTTP), wasm-ip-geo (Python component, HTTP), wasm-pacman (Go, allowlisted exec) and wasm-proc (freestanding C, read-only /proc). The full runtime reference is xfetch/docs/WASM.md.

Contribution Guidelines

Adding a Plugin

  1. Create a new directory under plugins/ named after your plugin (e.g., plugins/my-plugin/).
  2. Include a Cargo.toml with the package name following the xfetch-plugin-<name> convention.
  3. Implement the plugin with a src/main.rs or src/lib.rs that handles the JSON protocol.
  4. Add a README.md documenting the plugin's features, configuration options, and available styles.
  5. Add sample configs and assets if applicable (see plugins/animate-logo/ for reference).

Code Standards

  • Write all code, comments, and documentation in English.
  • Prefer the shared xfetch-plugin-api crate for protocol types and JSON IO.
  • Handle protocol and output errors explicitly — write errors to stderr and exit with non-zero.
  • Do not use external display or terminal libraries; the core handles rendering.
  • Keep the plugin focused on a single responsibility (e.g., logo animation).
  • Minimize dependencies to keep build times fast.

Pull Request Process

  1. Fork the repository and create a feature branch.
  2. Implement your plugin following the guidelines above.
  3. Ensure cargo build --release succeeds for your plugin.
  4. Add an entry for your plugin in README.md.
  5. Submit a pull request with a clear description of your plugin.
  6. Maintainers will review the protocol compliance and code quality.

What Not to Do

  • Do not modify core xfetch source files unless the plugin protocol itself needs changes.
  • Do not use network calls or file I/O in animation plugins (they should be pure transformations).
  • Do not embed binaries or large assets in the plugin source.
  • Do not introduce platform-specific code without fallbacks.