Files
orca/docs/reference/plugin-host-api.md
T
Neil 4ee788a725 Build-less TS/TSX plugins, typings and authoring tools (#36918)
* feat(plugins): build-less TS/TSX plugins, typings and authoring tools

- Plugin frames transpile .ts/.tsx/.mts/.jsx on load (sucrase on a shared worker thread with a
  2 s budget, cached in main), resolve relative imports to exact files, and refuse bare
  package imports by name. Components and html panel/editor entries may be .ts/.tsx.
- orca plugin types writes typings for the components orca module, window.orca, worker
  activate(orca), host.call and events into .orca/types, from one host-typings generator.
- orca plugin create --template components; templates omit publisher and target 1.4.246.
- orca plugin release-entry, dev inspect and restart; validate:plugin checks named files.
- Guest console errors reach orca plugin logs; dev workers restart on edits; the dev watcher
  ignores .git, .orca and node_modules; Git and folder installs cannot replace catalog installs.
- examples/plugins/hello-tsx, docs, and the plugin-buildless-tsx e2e spec.

* ci: run the Windows parity probes with one tsgo checker

tsconfig.node peaks near 22 GB with tsgo's default four checkers (each holds its own types)
and about 16 GB with one; the 16 GB Windows runner paged the native probe past its 900 s
timeout once this branch's package.json change triggered the job. One checker gives the
same diagnostics (verified locally with --parity-only); the timing benchmark is unchanged.
2026-11-01 01:28:35 -08:00

187 lines
9.7 KiB
Markdown

# Orca plugin host API reference
<!-- Generated by `pnpm generate:plugin-schemas` from src/shared/plugins. Do not edit. -->
Host API 1.1 (`"pluginApi": 1`). Workers call a method with `await orca.host.call(name, params)`; panels and custom editors call the same methods through `window.orca.host.call(name, params)`. Each method lists the surfaces allowed to call it; any other surface gets `not_permitted`. `orca.host.has(name)` answers synchronously whether this Orca offers a method on the current surface, and `orca.api` is `{ major, minor }`. See [Plugin authoring](./plugin-authoring.md) for the frame API.
Every call is checked against the capabilities the user approved, and params are validated before Orca runs anything: params are strict, so a field this Orca does not know is refused with `invalid_params` instead of being dropped. Mutations are recorded in the audit log as `plugin:<publisher>.<id>`.
Methods whose capability is `none` need no capability but still need the plugin enabled and approved. `shell.openExternal` and `files.saveAs` also need transient user activation (a click or key press in the calling frame, within a few seconds), are rate-limited, and run only in the desktop app; frames also reach them as `orca.shell.openExternal(url)` and `orca.files.saveAs({ suggestedName, mimeType, data })`, which resolves with `{ status: "saved" }`, or `{ status: "cancelled" }` when the person cancels the dialog. Saved files carry the OS mark for downloaded files.
## Methods
| Method | Capability | Surfaces | Mutation | Since | Summary |
| --- | --- | --- | --- | --- | --- |
| [`workspace.readContext`](#workspacereadcontext) | `workspace:read` | worker, panel, editor | no | 1.0 | Branch, name and terminal ids of the focused workspace, or null when none is focused. |
| [`terminal.sendText`](#terminalsendtext) | `terminal:send` | worker | yes | 1.0 | Types text into one terminal named by id, optionally pressing Enter. |
| [`notifications.show`](#notificationsshow) | `notifications:show` | worker, panel, editor | yes | 1.0 | Shows a desktop notification. |
| [`storage.get`](#storageget) | `storage:own` | worker | no | 1.0 | Reads a JSON value from the plugin's private store; null when absent. |
| [`storage.set`](#storageset) | `storage:own` | worker | yes | 1.0 | Writes a JSON value to the plugin's private store. |
| [`storage.delete`](#storagedelete) | `storage:own` | worker | yes | 1.0 | Removes a key from the plugin's private store. |
| [`storage.keys`](#storagekeys) | `storage:own` | worker | no | 1.0 | Lists the keys in the plugin's private store. |
| [`secrets.get`](#secretsget) | `secrets:own` | worker | no | 1.0 | Reads a secret from the plugin's encrypted secrets file; null when absent. |
| [`secrets.set`](#secretsset) | `secrets:own` | worker | yes | 1.0 | Stores a secret in the plugin's encrypted secrets file. |
| [`secrets.delete`](#secretsdelete) | `secrets:own` | worker | yes | 1.0 | Removes a secret from the plugin's encrypted secrets file. |
| [`secrets.protection`](#secretsprotection) | `secrets:own` | worker | no | 1.1 | How secrets are protected at rest: `os-keyring`, or `unavailable` (no OS key store, or Linux `basic_text`), where `secrets.set` fails. |
| [`settings.get`](#settingsget) | `settings:own` | worker | no | 1.0 | Reads every setting the plugin owns. |
| [`settings.set`](#settingsset) | `settings:own` | worker | yes | 1.0 | Writes one setting the plugin owns. |
| [`shell.openExternal`](#shellopenexternal) | none | panel, editor | no | 1.1 | Opens an http:, https: or mailto: URL in the default browser. Needs a click or key press in the frame; desktop app only. Orca asks the person before an http(s) host the plugin was not allowed; `opened` is false if they cancel. |
| [`files.saveAs`](#filessaveas) | none | panel, editor | yes | 1.1 | Offers data (a string or ArrayBuffer, up to 64 MiB) in Orca's save dialog; the plugin never learns the path. Needs a click or key press in the frame; desktop app only. |
### `workspace.readContext`
Branch, name and terminal ids of the focused workspace, or null when none is focused.
```ts
(params?: {}) => { branch: string; displayName: string; terminals: { id: string }[] } | null
```
### `terminal.sendText`
Types text into one terminal named by id, optionally pressing Enter.
```ts
(params: { terminalId: string; text: string; enter?: boolean }) => { accepted: boolean }
```
### `notifications.show`
Shows a desktop notification.
```ts
(params: { title: string; body?: string }) => { delivered: boolean }
```
### `storage.get`
Reads a JSON value from the plugin's private store; null when absent.
```ts
(params: { key: string }) => { value: JSON }
```
### `storage.set`
Writes a JSON value to the plugin's private store.
```ts
(params: { key: string; value: JSON }) => { ok: true }
```
### `storage.delete`
Removes a key from the plugin's private store.
```ts
(params: { key: string }) => { ok: true }
```
### `storage.keys`
Lists the keys in the plugin's private store.
```ts
(params?: {}) => { keys: string[] }
```
### `secrets.get`
Reads a secret from the plugin's encrypted secrets file; null when absent.
```ts
(params: { key: string }) => { value: string | null }
```
### `secrets.set`
Stores a secret in the plugin's encrypted secrets file.
```ts
(params: { key: string; value: string }) => { ok: true }
```
### `secrets.delete`
Removes a secret from the plugin's encrypted secrets file.
```ts
(params: { key: string }) => { ok: true }
```
### `secrets.protection`
How secrets are protected at rest: `os-keyring`, or `unavailable` (no OS key store, or Linux `basic_text`), where `secrets.set` fails.
```ts
(params?: {}) => { status: "os-keyring" | "unavailable" }
```
### `settings.get`
Reads every setting the plugin owns.
```ts
(params?: {}) => { settings: Record<string, JSON> }
```
### `settings.set`
Writes one setting the plugin owns.
```ts
(params: { key: string; value: JSON }) => { ok: true }
```
### `shell.openExternal`
Opens an http:, https: or mailto: URL in the default browser. Needs a click or key press in the frame; desktop app only. Orca asks the person before an http(s) host the plugin was not allowed; `opened` is false if they cancel.
```ts
(params: { url: string }) => { opened: boolean }
```
### `files.saveAs`
Offers data (a string or ArrayBuffer, up to 64 MiB) in Orca's save dialog; the plugin never learns the path. Needs a click or key press in the frame; desktop app only.
```ts
(params: { suggestedName?: string; mimeType?: string; data: string | ArrayBuffer }) => { status: "saved" | "cancelled" }
```
## Events
A worker receives the events its manifest lists in `contributes.events` through `orca.events.on(event, handler)`, which returns `{ dispose() }`. Each needs `main`, the `trusted-worker` profile and its domain's capability (`workspace:read` or `agent:read`). `orca plugin types` writes the payloads as `PluginEvents` in `orca/host`.
| Event | Payload | Summary |
| --- | --- | --- |
| `workspace.created` | `{ workspaceId: string; kind: "worktree" \| "folder"; path: string; branch: string }` | A worktree workspace was created. |
| `workspace.removed` | `{ workspaceId: string; kind: "worktree" \| "folder"; path: string }` | A worktree workspace was removed. |
| `agent.status.changed` | `{ workspaceId: string \| null; paneKey: string; state: "working" \| "blocked" \| "waiting" \| "done"; receivedAt: number; mainAgent?: { state: "working" \| "blocked" \| "waiting" \| "done"; outcome?: "success" \| "failure" \| "cancellation" \| "superseded" \| "interruption" \| "unconfirmed"; stateStartedAt: number } }` | An agent in a terminal pane changed status; `workspaceId` is null when Orca cannot tie the pane to a workspace. |
## Errors
A failed call rejects with an `Error` carrying `code`, `message`, `retryable` and, for `rate_limited`, `retryAfterMs`. Treat a code not listed here as `action_failed`:
- `invalid_request`: the call itself is malformed.
- `invalid_params`: params do not match the method, including a field this Orca does not know; the message names it.
- `unknown_method`: the method does not exist in this Orca.
- `capability_denied`: the manifest does not declare the capability.
- `consent_required`: the plugin is disabled or the user has not approved this version.
- `not_permitted`: the method or document is not available here, e.g. a worker-only method from a panel.
- `rate_limited`: calls arrive faster than the budget; `retryAfterMs` says when to try again.
- `unavailable`: the service behind the method is not running yet.
- `action_failed`: the method ran and failed.
- `cancelled`: the caller aborted the call through its `signal`.
- `timeout`: Orca gave up waiting.
- `conflict`: the call was made against a stale version, e.g. an editor edit after an outside change.
- `too_large`: the message is larger than Orca accepts.
## Stability and deprecation
Plugins are in Alpha: the manifest v2 format and catalog format are stable contracts; items listed as experimental in [Plugin authoring](./plugin-authoring.md#stability) may change.
- **Host API 1 is frozen.** Within `pluginApi` 1, methods, params and results only grow: a new method or a new optional param arrives with a higher `Since` minor, and nothing listed here is removed, renamed or narrowed.
- **Deprecation.** A method to be retired is marked deprecated in this table and keeps working for the rest of the major.
- **New majors.** A breaking change ships as a new `pluginApi` major beside the old one. Orca refuses a manifest whose `pluginApi` names a major it does not implement, with "update Orca", instead of loading it against the wrong API.
- **Detect, do not assume.** Check `orca.host.has(name)` or `orca.api.minor` before calling a method newer than the oldest Orca you support; a call that slips through fails with `unknown_method`.