mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* plugins: contributes.hooks runtime with terminal.send and workspace.create hooks
Workers register orca.hooks.on(hook, (event, next) => ...). Orca runs listed
plugins in order before it acts: next(event) continues with allowed fields
changed, { deny } stops, nothing passes. A plugin past its 2 s deadline, or
one that throws, crashes or is disabled, counts as passing and is logged with
plugin, hook and duration. First hooks: terminal.send (runtime sendTerminal),
workspace.create.before (IPC and runtime creates, folders included) and
observe-only workspace.create.after. orca/testing mountWorker gains
hooks.fire. Needs main + trusted-worker and engines.orca >=1.4.248.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: keep hook plugins' workers started; bound start-up separately from the hook deadline
Workers of enabled plugins that list hooks start as soon as they are approved
and are never idle-reaped; crash restarts follow the manager's policy. A hook
waits up to the worker ready timeout for start-up, then gives the handler its
full 2 s, so a cold worker no longer lets the first hooked action through.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: give hook workers their own soft cap instead of the active-worker cap
Always-running hook workers no longer hold slots of the shared 5-worker cap, so
many hook plugins cannot starve the rest. They have a soft cap of 16; past it a
hook worker waits for another to stop, logged once.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(plugins): type the hook warmer's ensureWorker mock
* plugins: address hook review: ordering, human input, overflow, revalidation, folder creates
- Hook workers past the soft cap start as overflow (logged once) instead of
waiting, so reconciliation, disable and uninstall never hang.
- Writes to one terminal stay in order while terminal.send decides, so a later
Enter cannot submit the previous line.
- terminal.send runs only for programmatic sends (main, plugins, the CLI);
desktop, mobile and legacy client input is a person's typing.
- Text a hook rewrote passes the AI Vault resume check again.
- Project-group folder workspace creates (RPC, agent launch, window) run the
workspace.create hooks through one boundary.
- A decision from a plugin disabled or changed while it ran is discarded.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(plugins): drive hook start-up timing with fake-timer deferreds, no real sleeps
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: only the hook decision holds a terminal's write order
A delivery that waits (a permission prompt, the launch composer) no longer
blocks later writes, and a person's input, interrupts and query replies never
wait behind a pending decision.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: hook DX: onError deny, denied_by_plugin, a Hooks reference, --template hook
- contributes.hooks[].onError: "deny" makes a throw, timeout, crash or bad
answer refuse the action, naming the plugin; the default still passes.
orca/testing mountWorker honours it, and mockHost answers fire-and-forget
methods (result { ok: true }) with success by default.
- A plugin denial reaches RPC clients as denied_by_plugin with the plugin and
reason, and the CLI no longer suggests a retry for it.
- The skill's host-api reference gains a generated Hooks section (event, what
next may change, stop result); the recipe passes sends through unchanged and
warns against awaiting other calls before deciding.
- orca plugin create --template hook: a worker-only terminal.send guard that
fails closed, with a test.
Co-Authored-By: Claude <noreply@anthropic.com>
* plugins: regenerate hook docs on main; bind the plugin runtime and its hook runner in one call
Keeps main-process-plugins.ts under 300 lines now that status bar items landed.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
164 lines
15 KiB
Markdown
164 lines
15 KiB
Markdown
# Orca plugin catalog reference
|
||
|
||
<!-- Generated by `pnpm generate:plugin-schemas` from src/shared/plugins. Do not edit. -->
|
||
|
||
The orca-catalog.json file that lists installable plugins and their releases.
|
||
|
||
Add `"$schema": "https://raw.githubusercontent.com/stablyai/orca/main/docs/schemas/plugins/orca-catalog.v2.schema.json"` to `orca-catalog.json` for completion and inline errors in your editor.
|
||
`Since` is the first Orca release that reads the field. Older releases ignore fields they do not know.
|
||
|
||
## Fields
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `$schema` | string | no | 1.4.245 | JSON Schema URL for editor completion. Ignored by Orca. |
|
||
| `catalogVersion` | `2` | yes | 1.4.245 | Catalog format. Raised only for a change older Orca cannot ignore; a newer Orca reads this one. |
|
||
| `name` | string | yes | 1.4.245 | Catalog display name. Example: `"Orca Plugins"` |
|
||
| `owner` | string | yes | 1.4.245 | Account that publishes the catalog. Example: `"stablyai"` |
|
||
| `plugins` | [CatalogEntry](#catalogentry)[] | yes | 1.4.245 | Listed plugins, at most 2048. Each `id` should appear once; Orca keeps the first. An entry this Orca cannot read is skipped, never fatal. |
|
||
|
||
## CatalogEntry
|
||
|
||
One installable plugin.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `id` | string | yes | 1.4.245 | `<publisher>.<id>`: the publisher is the repository owner, lowercased (Orca installs under this key, whatever the manifest's deprecated `publisher` says), and the id is the manifest's `id`. Each half is at most 64 characters, and the reserved names `constructor` and `prototype` are refused. Example: `"acme.gleam-syntax"` |
|
||
| `source` | [CatalogGitSource](#cataloggitsource) | yes | | Where the plugin source lives. |
|
||
| `ownerId` | integer | no | 1.4.246 | GitHub's numeric id of the repository owner, recorded the first time the plugin was listed. Names can be renamed or re-registered; this id cannot. Informational: Orca does not read it yet, and older Orca ignores it. Example: `1234567` |
|
||
| `description` | string | no | 1.4.245 | Listing text shown in the catalog. |
|
||
| `categories` | string[] | no | 1.4.245 | Lowercase slugs used to filter the catalog, each listed once. At most 16; extras are ignored. |
|
||
| `releases` | [CatalogRelease](#catalogrelease)[] | yes | 1.4.245 | Installable releases, newest first. Orca installs the first one whose `minimumOrca` it meets, so keep older releases listed when you raise `engines.orca`. At most 32. |
|
||
|
||
## CatalogGitSource
|
||
|
||
Where the plugin source lives.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `kind` | `"git"` | yes | 1.4.245 | Source kind. Only `git` is supported. |
|
||
| `url` | string | yes | 1.4.245 | HTTPS or SSH URL of the plugin repository. Example: `"https://github.com/acme/orca-gleam-syntax.git"` |
|
||
| `ref` | string | yes | 1.4.245 | Branch or tag the author publishes from. Each release pins an exact commit. Example: `"main"` |
|
||
| `path` | string | no | 1.4.245 | Directory inside the repository that holds `orca-plugin.json`, so one repository can publish many plugins. Use `/` separators, with no leading `/`, `.` or `..`. Omit when the plugin is the repository root. Example: `"plugins/gleam-syntax"` |
|
||
|
||
## CatalogRelease
|
||
|
||
The exact release the catalog vouches for. Orca refuses bytes that differ.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `version` | string | yes | 1.4.245 | The manifest's `version` for this release. Example: `"1.2.0"` |
|
||
| `commit` | string | yes | 1.4.245 | Full SHA-1 commit Orca checks out for this release. |
|
||
| `contentHash` | string | yes | 1.4.245 | SHA-256 of the plugin's installed files, as Orca computes it at install. |
|
||
| `execution` | `"data"` \| `"declarative"` \| `"sandboxed-ui"` \| `"trusted-worker"` | yes | 1.4.245 | The manifest's `execution` profile. Example: `"data"` |
|
||
| `minimumOrca` | string | yes | 1.4.245 | The manifest's `engines.orca` without `>=`. Older Orca lists the plugin but will not install it. Example: `"1.4.245"` |
|
||
| `types` | (`"interface-language"` \| `"app-theme"` \| `"terminal-theme"` \| `"icon-theme"` \| `"syntax"` \| `"instructions"` \| `"panel"` \| `"editor"` \| `"worker"`)[] | no | 1.4.245 | Optional; Orca derives it from `contributes` and `execution`. When present, every kind of contribution the release carries, matching its manifest exactly. `instructions` needs the `declarative` profile or higher, `panel` and `editor` need `sandboxed-ui`, and `worker` needs `trusted-worker`. Example: `["syntax"]` |
|
||
| `name` | string | no | 1.4.245 | The manifest's `name`, so Orca can suggest the plugin before downloading it. Example: `"Gleam"` |
|
||
| `license` | string | no | 1.4.245 | SPDX license expression. Example: `"MIT"` |
|
||
| `repository` | string | no | 1.4.245 | Source repository page, https only. Orca links it when it is a github.com or gitlab.com repository of the plugin's publisher. Example: `"https://github.com/acme/orca-gleam-syntax"` |
|
||
| `sourceUnavailable` | `true` | no | 1.4.246 | Set when the release's `repository` is gone and `source` serves an archived copy, so Orca links `source` instead. Example: `true` |
|
||
| `homepage` | string | no | 1.4.245 | Project homepage, https only. Example: `"https://example.com/gleam"` |
|
||
| `updatedAt` | string | no | 1.4.245 | When this release was published, as an ISO 8601 date or date-time. Example: `"2026-10-20"` |
|
||
| `changelog` | string | no | 1.4.245 | What changed in this release, as plain text. At most 2000 characters. Example: `"Adds highlighting for use expressions."` |
|
||
| `images` | [CatalogImages](#catalogimages) | no | 1.4.245 | Images extracted from the release by catalog CI, which re-encodes them and strips metadata. Same rules as the manifest: icon PNG, WEBP, static, at least 128×128, square, at most 256 KB. Cover PNG, JPEG, WEBP, static, at least 800×500, at most 1024 KB. Screenshots PNG, JPEG, WEBP, static, at least 1280×800, at most 1536 KB. Images must not imitate Orca's trust badges. |
|
||
| `themePreview` | [CatalogThemePreview](#catalogthemepreview)[] | no | 1.4.245 | Up to 4 theme palettes for listing art. Display only; the installed theme files decide the real colors. |
|
||
| `contributes` | [CatalogContribution](#catalogcontribution)[] | no | 1.4.245 | What the release adds, as the listing shows it. Generate it with the catalog tooling; Orca refuses an install whose manifest says otherwise. |
|
||
| `capabilities` | (`"workspace:read"` \| `"agent:read"` \| `"terminal:send"` \| `"notifications:show"` \| `"storage:own"` \| `"secrets:own"` \| `"settings:own"`)[] | no | 1.4.245 | Every capability the release asks for. Orca refuses an install whose manifest asks for different ones. Example: `["notifications:show"]` |
|
||
| `fileTypes` | string[] | no | 1.4.245 | Extensions, without the dot, that this release opens. Orca suggests the plugin when such a file has no viewer. |
|
||
| `editorFileTypes` | string[] | no | 1.4.245 | The `fileTypes` this release opens in a custom editor. Orca suggests the plugin for such a file even when it already opens it as text. |
|
||
|
||
## CatalogImages
|
||
|
||
Images extracted from the release by catalog CI, which re-encodes them and strips metadata. Same rules as the manifest: icon PNG, WEBP, static, at least 128×128, square, at most 256 KB. Cover PNG, JPEG, WEBP, static, at least 800×500, at most 1024 KB. Screenshots PNG, JPEG, WEBP, static, at least 1280×800, at most 1536 KB. Images must not imitate Orca's trust badges.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `icon` | [CatalogImage](#catalogimage) | no | | An image stored in the catalog repository. |
|
||
| `cover` | [CatalogImage](#catalogimage) | no | | An image stored in the catalog repository. |
|
||
| `screenshots` | [CatalogImage](#catalogimage)[] | no | 1.4.245 | Screenshots, at most 6. |
|
||
|
||
## CatalogImage
|
||
|
||
An image stored in the catalog repository.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `path` | string | yes | 1.4.245 | File in the catalog repository, relative to its root. Relative to the plugin root; `..` segments and Windows device names such as `CON` are refused. Example: `"assets/acme.gleam/cover.png"` |
|
||
| `alt` | string | yes | 1.4.245 | What the image shows, for screen readers. Required. Example: `"The Dusk theme on a terminal"` |
|
||
| `sha256` | string | yes | 1.4.245 | SHA-256 of the file's bytes; Orca refuses bytes that differ. |
|
||
| `width` | integer | yes | 1.4.245 | Width in pixels. Example: `1600` |
|
||
| `height` | integer | yes | 1.4.245 | Height in pixels. Example: `1000` |
|
||
|
||
## CatalogThemePreview
|
||
|
||
Colors to draw a theme card before install.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `label` | string | yes | 1.4.245 | Theme name. Example: `"Dusk"` |
|
||
| `base` | `"light"` \| `"dark"` | yes | 1.4.245 | Light or dark theme. |
|
||
| `background` | string | yes | 1.4.245 | Window background. Example: `"#1b1430"` |
|
||
| `foreground` | string | yes | 1.4.245 | Main text. Example: `"#1b1430"` |
|
||
| `accent` | string | yes | 1.4.245 | Accent, such as buttons. Example: `"#1b1430"` |
|
||
| `surface` | string | yes | 1.4.245 | Cards and sidebars. Example: `"#1b1430"` |
|
||
|
||
## CatalogContribution
|
||
|
||
One thing the release adds to Orca.
|
||
|
||
| Field | Type | Required | Since | Description |
|
||
| --- | --- | --- | --- | --- |
|
||
| `kind` | `"panels"` \| `"commands"` \| `"events"` \| `"hooks"` \| `"languagePacks"` \| `"keybindings"` \| `"vmRecipes"` \| `"themes"` \| `"terminalThemes"` \| `"iconThemes"` \| `"syntaxes"` \| `"customEditors"` \| `"newFiles"` \| `"settings"` \| `"statusBarItems"` | yes | 1.4.245 | Contribution point. Example: `"themes"` |
|
||
| `name` | string | yes | 1.4.245 | What the item is called in Orca. Example: `"Dusk"` |
|
||
| `detail` | string | no | 1.4.245 | Extra context, such as file extensions. |
|
||
|
||
## Versions and compatibility
|
||
|
||
- **What only lists is tolerant.** A catalog never decides what runs (installing re-checks the exact bytes), so Orca keeps whatever it can read: unknown keys are ignored at every level, an entry it cannot read is skipped, an invalid display-only field is dropped, a list over its limit is trimmed, and a repeated `id` keeps the first entry. Validation in CI is where these become errors.
|
||
- **The file name never changes.** `catalogVersion` (now 2) rises only for a change older clients cannot ignore; an Orca that sees a higher number refuses the catalog with "update Orca to browse this catalog" rather than misreading it.
|
||
- **Releases are a list, newest first.** Orca offers the first release whose `minimumOrca` it meets, so keep the previous release listed when a new one raises `engines.orca`; Orca shows "Needs Orca X" only when none fits.
|
||
|
||
## Publisher names and trust
|
||
|
||
- **Publisher is the repository owner.** Like a Go module path, a plugin's publisher is where it lives, never what its manifest says: the official and community catalogs set each entry's `id` to `<owner>.<manifest id>` from the GitHub user or organization that owns the repository (`acme` for `github.com/acme/gleam-syntax`), and Orca installs under that `id`. A Git URL install uses the URL's owner and a folder uses `local`. The manifest's `publisher` is deprecated and ignored. Orca does not check the owner for catalogs you add yourself; their `id` is what they say.
|
||
- **Listed names are pinned to GitHub ids.** The community catalog records the numeric owner and repository ids when a plugin is first listed, pauses new versions after a rename or transfer until Stably confirms it, and refuses a repository that was deleted and recreated under the same name. Each listed version is permanent: a moved tag never replaces it.
|
||
- **Reserved names install only from Orca's official catalog.** The publishers `stablyai`, `official`, `anthropic`, `openai`, `github` and any starting `orca` (any case), and ids starting `orca-`, are refused from any other catalog, a Git URL or a local folder. A catalog that lists one is refused whole. `pnpm validate:plugin` rejects these publishers except `stablyai`.
|
||
- **Trust comes from the catalog's address.** Orca labels a plugin with where it came from: the Official mark for Orca's catalog, otherwise the catalog's name and the owner in its Git URL (`github.com/acme`, `gitlab.com/acme/tools`), never the manifest's text. Development folders show `Dev · unverified`, Git URL installs `Unverified`.
|
||
- **An install stays with its catalog.** Orca records the catalog each plugin was installed from and updates it only from there. When another catalog lists the same id, the plugin page says so, and switching means removing the plugin and installing it again.
|
||
|
||
## One repository can hold many plugins
|
||
|
||
Give each plugin its own directory and set `source.path` to it. Orca downloads the pinned commit but installs only that directory, and `contentHash` covers only its files, so a commit that changes a sibling plugin leaves this plugin's hash, and its users, untouched. Moving a plugin to another directory counts as moving repositories: users review the update before it installs.
|
||
|
||
```json
|
||
{ "id": "acme.gleam-syntax", "source": { "kind": "git", "url": "https://github.com/acme/orca-plugins.git", "ref": "main", "path": "plugins/gleam-syntax" } }
|
||
```
|
||
|
||
Run `pnpm validate:plugin plugins/gleam-syntax` in that repository to print the directory's `path` and its release entry. Listing images stay relative to the catalog repository.
|
||
|
||
## Install links
|
||
|
||
An install link opens Orca at a plugin's page in Settings → Plugins. It never installs: the user reads the page and presses Install, and the usual review applies. If plugins are off, the page waits behind **Turn on plugins**.
|
||
|
||
```text
|
||
orca://plugins/<publisher>.<id>?catalog=<official | community | catalog Git URL>
|
||
```
|
||
|
||
- `catalog` defaults to `official`. `community` is Orca's community catalog. Any other value must be an HTTPS or SSH Git URL of at most 512 characters, read at its `main` branch, without a password or token. Orca asks **Add catalog …?** before fetching a catalog the user hasn't added, and that includes `community`.
|
||
- Reserved publishers (above) are accepted only with the official catalog. A link longer than 2048 characters, with another query parameter, or with an id that isn't `<publisher>.<id>` shows a short error and does nothing else. Repeats of the same link within a few seconds are ignored.
|
||
- Browsers and README viewers often block custom schemes, so link to the directory page instead. It opens the `orca://` link and shows install steps when Orca is missing: `https://onorca.dev/plugins/<publisher>.<id>?catalog=<same value>`.
|
||
|
||
For a README:
|
||
|
||
```md
|
||
[Open in Orca](https://onorca.dev/plugins/acme.gleam-syntax?catalog=community)
|
||
```
|
||
|
||
## Clients before format 2
|
||
|
||
Orca reads `orca-catalog.json` first and falls back to `orca-marketplace.json`. That older file is frozen: shipped clients reject any key they do not know, so it carries only `name`, `owner` and each plugin's `id`, `source`, `description` and `categories`, and leaves out plugins with a `source.path`, which those clients would install from the repository root. Generate it from `orca-catalog.json` rather than editing two files:
|
||
|
||
```sh
|
||
node config/scripts/export-legacy-plugin-catalog.mjs orca-catalog.json orca-marketplace.json
|
||
```
|