# Writing an Orca plugin From an empty folder to a plugin anyone can install from a catalog. The field-by-field rules live in the generated references: [manifest](plugin-manifest.md), [catalog](plugin-catalog.md), [host API](plugin-host-api.md), [themes](plugin-themes.md) and [theme tokens](plugin-theme-tokens.md), [syntaxes](plugin-syntax.md). Listed plugins must follow the [plugin listing policy](plugin-listing-policy.md). ## 1. Write A plugin is a folder (in practice its own Git repository) with `orca-plugin.json` at the root. Start from one of these, or scaffold one with `orca plugin create` (see [Validate](#3-validate)). Add the `$schema` line from `orca-gleam-syntax` to your manifest for editor completion. | Example | Execution | Shows | | ------------------------------------ | ---------------- | ---------------------------------------------------------------------------- | | `examples/plugins/orca-gleam-syntax` | `data` | A syntax, no code | | `examples/plugins/hello-tsx` | `sandboxed-ui` | A [components](plugin-declarative-ui.md) TSX panel with no build step | | `examples/plugins/hello-components` | `sandboxed-ui` | The same kind of [components](plugin-declarative-ui.md) panel, bundled first | | `examples/plugins/hello-orca` | `trusted-worker` | A panel, a worker command and events | Leave `publisher` out. Orca names the publisher after where the plugin comes from, the way a [Go module path](https://go.dev/ref/mod#module-path) is its repository path: the owner of the repository, lowercased (`acme` for `github.com/Acme/gleam-syntax`), so the plugin installs as `acme.gleam-syntax`. Nobody can choose a publisher name that is not theirs. | Installed from | Publisher | | --------------------------------- | ------------------------------------------------------------ | | The official or community catalog | the repository owner, set by the catalog in the entry's `id` | | Another catalog | whatever that catalog's entry `id` says | | A Git URL | the URL's owner (`.` and `_` become `-`) | | A local or development folder | `local` | `publisher` is deprecated: Orca ignores it for catalog and Git installs. Leaving it out needs `engines.orca` of at least `>=1.4.246`, because older Orca requires the field and refuses a value that differs from the catalog's. To keep supporting older Orca, keep `publisher` and set it to the repository owner; `validate:plugin` warns about it either way. An install made before 1.4.246 keeps its key, approvals and data. A local or development folder that still declares `publisher` keeps using it until you remove it. When copying the Gleam example, also drop its `stablyai` publisher and `orca-` id prefix: both are reserved for Orca's own plugins, as are owners named `official`, `anthropic`, `openai`, `github` or starting with `orca`. Pick the lowest `execution` profile that covers what you contribute; it decides what the user is asked to approve: - `data`: themes, terminal themes, icon themes, syntaxes, translations. Installs in one click. - `declarative`: commands that run Orca actions, shortcuts, VM recipes. Reviewed before they are enabled. - `sandboxed-ui`: panels and custom editors, in a sandboxed frame that talks to Orca only through `window.orca` (see [Frames](#frames-windoworca)) and can call only the host methods whose surfaces list `panel` or `editor`. Orca's colors reach frame CSS as `--orca-background`, `--orca-foreground`, `--orca-primary` and the rest of its [frame variables](plugin-theme-tokens.md#frame-css-variables), so they never collide with a frame's own variables. - `trusted-worker`: `main` runs in a separate Node process and can call every host method its `capabilities` allow. There is no global switch for these full-access plugins: each one runs only after the user approves it on install. An enterprise policy to block full-access plugins may come later. Set `engines.orca` to the newest `Since` of any field you use, and `pluginApi` (currently `1`) only when a panel or worker calls the host API. ### Frames: `window.orca` Before any plugin code runs, the frame shell defines a frozen `window.orca`. It is the only supported way to talk to Orca; the messages underneath are internal and change without notice. ```js orca.api // { major: 1, minor: 1 }: the host API this Orca implements if (orca.host.has('workspace.readContext')) { // Synchronous: is the method offered to this surface by this Orca? const controller = new AbortController() try { const context = await orca.host.call('workspace.readContext', {}, { signal: controller.signal }) } catch (error) { // error.code, error.message, error.retryable, error.retryAfterMs (rate_limited only) } } const subscription = orca.on('theme', ({ colorScheme }) => {}) subscription.dispose() ``` Two calls need a click or key press in the frame (they reject with `not_permitted` otherwise) and work only in the desktop app (in browser clients `orca.shell` and `orca.files` are undefined and `orca.host.has` answers false): ```js button.onclick = () => orca.shell.openExternal('https://example.com/docs') // http:, https:, mailto: // Orca asks the person before a web host this plugin was not allowed; { opened: false } if they cancel. const { status } = await orca.files.saveAs({ suggestedName: 'chart.png', mimeType: 'image/png', data: arrayBuffer }) // status is 'saved' or 'cancelled' (the person closed the dialog). The plugin never learns the // path, and the file is marked as downloaded so the OS warns before opening it. Any mimeType is // accepted; the dialog filters by the suggested name's extension. Executable, script, installer, // shortcut and disk-image types (.exe, .js, .sh, .app, .lnk, .iso…) are refused. ``` `orca.locale` is Orca's UI language as a BCP-47 tag (`navigator.language` follows the operating system, not Orca). `` opens the system file picker. Methods, their surfaces and their errors are in the [host API reference](plugin-host-api.md). Aborting the `signal` rejects the call with `cancelled`. Treat an error `code` you do not know as `action_failed`. Params are strict: a field the running Orca does not know is refused with `invalid_params`, so check `orca.host.has` or `orca.api.minor` before using a newer one. #### Files and assets In the desktop app each plugin's frames run in their own isolated process, storage and origin, `orca-plugin-frame:///`, served from your plugin folder. Load scripts, styles, images, fonts, WASM and workers by relative URL (`