Developers
Your first plugin, from folder to approval.
With OmniConflux 0.3.0 or later: make the folder, write the manifest, answer one command, check it, install it and approve it in the app. The plugin on this page counts finished turns and shows the count when you choose its command.
Before you start
- You need OmniConflux 0.3.0 or later. 0.3.0 is released for Omarchy, macOS on Apple silicon and Windows, in a closed beta with a waitlist, and plugins and the SDK ship inside the app from that version.
- The app brings omniconfluxd, the command line these steps use, and the SDK, so there is nothing to fetch from a package registry.
- Plugins run on the Node.js that runs OmniConflux, version 22.15 or later.
Eight steps
Make the folder
init makes the folder hello-desk with a manifest, a main.mjs that answers one command, a short read-me and its own copy of the SDK at node_modules/@omniconflux/sdk. The plugin's id is the folder's name; give another with --id. It needs no running daemon and refuses a folder that is not empty.
omniconfluxd plugin init hello-deskWrite the manifest
Open omniconflux-plugin.json and say what the plugin is, what it asks for and the command it adds. This plugin asks to see events, to add a command and to keep its own data. A plugin with a command must ask for commands.add.
omniconflux-plugin.json { "manifestVersion": 1, "api": "1.0", "id": "hello-desk", "name": "Hello Desk", "version": "0.1.0", "description": "Counts finished turns and shows the count.", "main": "main.mjs", "permissions": [ "events.read", "commands.add", "storage.own" ], "commands": [ { "id": "count", "title": "Show finished turns" } ] }Answer one command
Put this in main.mjs. connect answers the host's first message and returns once the plugin runs. The handler for count returns the text the app shows, and the subscription adds one to a stored count each time a turn ends.
main.mjs import { connect } from '@omniconflux/sdk'; const host = await connect({ commands: { count: async () => `Turns finished: ${(await host.storage.get('turns')) ?? '0'}`, }, }); await host.events.subscribe(async () => { const turns = Number((await host.storage.get('turns')) ?? '0') + 1; await host.storage.set('turns', String(turns)); }, ['turn.end']);Check it
check runs the folder and manifest checks without a daemon and without copying anything, reports each problem with its code, and prints the snapshot hash. It exits with 0 when there is no problem. With --json it prints one object: ok, plugin, version, snapshotHash and problems.
omniconfluxd plugin check hello-deskInstall it
install copies the folder into OmniConflux, checks the copy, and leaves it waiting for your approval. It approves nothing and starts nothing.
omniconfluxd plugin install hello-deskApprove it in the app
Open the Plugins list in the app and read the prompt: the plugin's name, id and version, its three permissions with their sentences, its command and the first 12 characters of its hash. Approve it, and it starts. There is no approval on the command line.
Choose the command
Show finished turns is in the app's command list, under the plugin's name. After three finished turns it answers: Turns finished: 3.
Change it, then approve again
After any change to a file or the manifest, run check and install again and approve the new copy in the app. The approved copy keeps running until you do.
Know this before you write
- The SDK owns standard output. It writes only messages there and sends console.log, console.info and console.debug to the error output, because one stray line on standard output stops the plugin.
- A plugin imports only its own files and Node's built-in modules. A package installed elsewhere is not found, native addons and WebAssembly are not loaded, and links in the folder are refused, so bundle what you need into your own files. A folder holds at most 10,000 files and 64 MiB.
- The host stops a plugin with shutdown, and the handler you give onShutdown gets at most 1.5 seconds. When its input ends, the plugin exits.
- The SDK is a convenience; the wire is the contract. A program that speaks it on standard input and output, one JSON message per line, is a valid plugin: see the four message shapes.
The command line
Each command prints text for a person; with --json it prints one JSON object. There is no approve and no enable: you approve a plugin and switch it on in the app.
omniconfluxd plugin init <folder> [--id <id>]Makes a new plugin folder with a manifest, a main script and its own copy of the SDK.
omniconfluxd plugin check <folder>Runs the folder and manifest checks without a daemon and prints the snapshot hash.
omniconfluxd plugin install <folder>Copies the folder into OmniConflux, where it waits for approval in the app.
omniconfluxd plugin listLists the installed plugins.
omniconfluxd plugin disable <id>Switches a plugin off.
omniconfluxd plugin remove <id> [--keep-tasks]Removes a plugin and its data after asking on a terminal. With --keep-tasks the tasks it started go on.
Plugins ship inside the app
OmniConflux 0.3.0 is released for Omarchy, macOS on Apple silicon and Windows, in a closed beta with a waitlist. Plugins and the SDK ship inside the app from 0.3.0, and the SDK is not on a package registry.