Create your first plugin
Create inside Oruva, link a folder you already maintain, or start from the bundled command-line tools.
Inside Oruva (recommended)
- Open Plugin Center → Develop (from the app menu or the asset context menu's Developer options) and press Create Plugin.
- In the Create plugin dialog, type a name, optionally choose an icon, and pick a template card: The plugin ID derives from the name automatically; open Advanced options to override it, add a description, or (for the basic template) set the first action's label and media types.
- Basic asset action — a minimal Python action you fill in.
- Asset report example — a working copy of the SDK example that writes a report file next to the asset.
- The optional icon picker accepts SVG, PNG, JPEG, or WebP up to 512 KiB and previews the choice. Oruva copies it into the new plugin as
iconplus its original extension. Without a choice, the template's startericon.svgis used. - Press Create. Oruva creates the folder in its local plugins directory with a valid schema-2
plugin.json,main.py, icon, andREADME.md. - The new plugin appears under Installed, switched Off. Open its folder, edit it, then switch it on — Oruva shows the approval sheet listing the entry path, the actions, the permissions, and every declared internet destination.
- Right-click any accepted asset → Plugins → your action. Output appears in Oruva, or your modal opens if the action declares
ui.
From a folder you already have
Keep your project anywhere on disk and use Develop → Link Existing Folder. Oruva reads the manifest in place (re-checked every few seconds while the app runs), so you can edit and re-run without copying. Unlinking never deletes your files.
From the command line
The SDK ships inside every installed build at resources/developer/ (plugin-sdk/ plus the oruva_plugin.py CLI; run it with the bundled resources/python/python.exe):
python oruva_plugin.py location # where local plugins live
python oruva_plugin.py validate path\to\plugin # full manifest validation
python oruva_plugin.py link path\to\plugin # develop in place (typed approval)
python oruva_plugin.py dev-list # list linked development plugins
python oruva_plugin.py unlink my.plugin.id # unlink without deleting files
python oruva_plugin.py approve my.plugin.id # typed, explicit approval
python oruva_plugin.py pack path\to\plugin # build a .oruvaplugin package
link and approve ask for a typed confirmation; pass --yes to skip it in scripts you trust.
Developing and debugging
- Link, don't copy. Linked projects are re-read while Oruva runs, so a manifest edit shows up within seconds. Because approval covers the content hash of the whole folder, every saved edit — to
plugin.json,main.py,ui.html, even a comment — flips the plugin to Re-approval required until you switch it on again. That is the trust model working, not a bug; budget for it in your edit-run loop, or approve from the CLI withoruva_plugin.py approve <id> --yesafter each change. - Validate early:
oruva_plugin.py validate <folder>prints the full normalized manifest (including the canonical capabilities) or the exact validation error. The same validator runs in the app, CLI, packager, and catalog installer. - State machine: a plugin is always in one of
error(invalid manifest),incompatible(platform/version gate),disabled,approval_required, orapproved. Only approved and enabled plugins can run. - Internet grants reset with approval. Every re-approval returns each declared origin to ask, so expect the internet prompt again after an edit.
- Test both contracts on purpose: non-zero exit, stderr output, a headless sleep past 30 seconds, a UI sleep past 600 seconds, invalid NDJSON, cancel, and modal close.
- For a UI action, test the ready/context handshake before debugging the entry. Then log every
runId, relay event, andrun-donein your iframe's own development build. Exercise the Deny path of the internet prompt as well as Allow once and Always allow.
Guidelines: be deterministic, do not modify Oruva's database or library structure directly (treat asset files and reconciliation events as the interface), and keep headless output short.