You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(plugins): wire the plugin service on a headless serve, and turn plugins on from the CLI - #35184
On a headless Orca server (orca serve on the Electron runtime), two things kept plugins from working:
Plugins that run code got "runtime is not available" for everything they asked Orca to do. The link between the plugin system and the rest of Orca was only made when a desktop window opened, and a headless server never opens one.
You couldn't turn plugins on. The plugin_system_disabled error said to click "Turn on plugins" in Settings, which a headless server doesn't have.
Now both work without a window: the link is made at startup, and orca plugin system on (with --environment <server>) turns plugins on.
Builds on #36280/#36288; complements #36647 (install/uninstall), which doesn't cover these two.
pluginService.setRuntimeDelegate(runtime) and the pluginSystemEnabled/devPluginPaths → refresh() settings listener used to live in registerPluginHandlers (src/main/ipc/plugins.ts). That function only runs when a window registers IPC.
They now run in initializeMainProcessPlugins, once per process, window or not. The listener joins the existing plugin onSettingsChanged handler there.
registerPluginHandlers no longer takes runtime. Its one production caller and two test fixtures are updated, and nothing else in that file changes.
Before this, on a windowless serve every worker or panel host call (workspace context, terminals, notifications, storage, secrets) returned unavailable: runtime is not available. devLoad, refresh and the RPC enable path still worked, because they call refresh() themselves. A settings write that didn't come with its own refresh had no effect until the next refresh.
orca plugin system on / orca plugin system off (src/cli/specs/plugin.ts, src/cli/handlers/plugin-runtime.ts):
Two specs in the plugin-runtime handler group, shaped like plugin dev load|reload|unload. They use the same callPlugins, printPluginResult and formatPluginRow.
An older host answers method_not_found, and the existing translation turns that into runtime_unsupported.
on prints the plugin list. off keeps installs, approvals and development folders.
plugin_system_disabled hint: the next step in src/main/plugins/plugin-dev-folders.ts also names orca plugin system on for a host with no window. The orca-cli skill guide (skill-guides/orca-cli/references/plugins.md, regenerated into src/cli/bundled-skill-guides.ts) lists the command and no longer says there is no CLI way.
Root help gets one line. The RPC params catalog is regenerated.
Why
Turning plugins on uses a dedicated host-admin method instead of widening settings.update. settings.update is settings-write, and turning plugins on lets third-party code run on the host, so it sits with plugins.consent, plugins.devLoad and the rest of plugin administration.
The wiring fix moves the two calls to where the plugin service is built, rather than adding a headless-only path. One registration now covers the desktop and serve.
This PR used to also add install from a path or Git URL, remove, list with a trust review, approve --fingerprint and a plugins.administration.v1 capability. #36279/#36280/#36288 and #36647 now cover those with their own approval flow and vocabulary, so this PR drops them.
Linked Issue
Fixes#36709 (refs #35104). With #36647, a headless controller can turn plugins on, install, approve and run a plugin from the CLI.
Visual Proof
N/A: no UI change. The desktop still turns plugins on through Settings → Plugins; this adds a CLI/RPC path and moves main-process wiring.
Testing
src/main/startup/main-process-plugins.test.ts (new) runs the real initializeMainProcessPlugins, with a real PluginService on a temp profile and no window. It checks that the runtime delegate is bound and that exactly one settings→refresh listener fires. It fails on unmodified main (expected "setRuntimeDelegate" to be called). The old listener test in src/main/ipc/plugins.test.ts is removed, since registerPluginHandlers no longer owns that listener.
src/main/runtime/rpc/methods/plugins.test.ts: plugins.setSystemEnabled is host-admin, writes the setting, refreshes after the write, and never consents.
pnpm run build:packages, pnpm tc, the RPC params catalog and bundled-skill-guide checks, check-changed-code-quality.mjs upstream/main, import boundaries, ratchets, and oxlint/oxfmt/anti-slop on the changed files;
CLI spec, parity, vocabulary and handler-group tests, plugin templates, RPC plugin, caller-scope and transport-parity, startup, IPC plugin and panel-lifetime, plugin dev-folders, CLI approval, and uninstall-log tests.
Not run end to end against a live orca serve.
I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Written with Claude (Anthropic) via an agent harness; reviewed by the author.
Review
Cross-platform: no platform-specific code.
Remote/SSH: the CLI targets the host chosen by --environment/--pairing-code. The method is new and additive, so an old host gives runtime_unsupported.
Mobile: host-admin, and not in the mobile allowlist, so phones are refused. No mobile UI.
Back-compat: IPC channels are unchanged. registerPluginHandlers dropped its runtime argument and has one production caller. On the desktop, the listener and delegate now register at plugin startup, just before the first window, instead of at the first window's IPC registration.
Security: host-admin, like plugins.consent/devLoad. Turning plugins on approves nothing, and settings.update's schema is not widened.
Performance: no hot-path change. One listener per process, as before.
Agent skill upstream boundary
Not applicable, or this change copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.
Notes
orca serve defaults to orcad, which has no plugin system. Against orcad, plugin system on fails like every other plugins.* method ("Plugin service is not available on this runtime"). Use ORCA_SERVE_RUNTIME=electron.
Overlap with cli: add orca plugin install, update, uninstall and catalog list/add/rm #36647: a trial merge with its head (b8d4de7) merges src/main/ipc/plugins.ts, src/main/startup/main-process-plugins.ts, the CLI handler, specs and params cleanly. Only the generated rpc-params-catalog.generated.ts, bundled-skill-guides.ts and skill-guides/orca-cli/references/plugins.md conflict. The first two regenerate, and the third needs a hand merge of adjacent lines.
Still open for a headless controller, not here: orca plugin list shows the execution level but none of the desktop consent's warnings or VM recipe commands. Runtime RPC workspace creation doesn't read plugin vmRecipes yet (follow-up port of feat(cli): create workspaces on environment recipes #28439).
Checklist
This PR is small and focused
I explained what changed and why (ELI5, the user-facing before/after, the mechanism, and why over the alternatives)
Before/after screenshots or videos attached for UI changes, or N/A with reason
Self-reviewed for correctness, security, and performance
Cross-platform, SSH/remote, and path/shortcut impact considered (or N/A)
pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)
It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.
No actionable comments were generated in the recent review.
Recent review info
Run configuration
Source mode: Auto
Repository: Unavailable
Branch: Unavailable
File: No configuration file from this repository applied
Commit: Unavailable
Layers: Repository UI
Review profile: CHILL
Plan: Advanced
Run ID: 7e78fb35-1af0-45d2-83de-373b8ad00605
Commits
Reviewing files that changed from the base of the PR and between 5210aa3 and e1a94b6.
Files ignored due to path filters: 1
src/shared/rpc-contract/rpc-params-catalog.generated.ts is excluded by !**/*.generated.*
Files selected for processing: 8
docs/site/content/docs/cli/reference.mdx
src/cli/command-scoped-flag-help.ts
src/cli/handlers/plugin.test.ts
src/cli/handlers/plugin.ts
src/cli/plugin-handler-group.ts
src/cli/root-help-text-primary.ts
src/cli/specs/plugin.ts
src/main/runtime/orca-runtime-get-status.ts
Files skipped from review as they are similar to previous changes: 2
src/cli/command-scoped-flag-help.ts
src/cli/root-help-text-primary.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
Walkthrough
The change adds host-side plugin installation, removal, and system enablement through runtime RPC and CLI commands. Host operations refresh plugin state and keep new installations pending until consent is granted. Runtime capability reporting and host-admin checks gate administration calls. CLI output includes plugin review details and validates approval fingerprints. Shared trust-tier classification supplies labels for the consent dialog and CLI. The change also adds tests, command help, and documentation for headless-server plugin management.
Merge Risk: Low · up to e1a94
A malicious plugin could make its CLI approval review misleading. Approval still requires consent, but the review display should be hardened before relying on it for untrusted plugins.
Pre-merge checks: 3 · 2
Failed checks: 2 warnings
Check name
Status
Explanation
Resolution
Docstring Coverage
Docstring coverage is 31.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta…
Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check
The description follows the required template and explains the headless wiring and system-toggle changes. However, it conflicts with the changeset and objectives by stating that plugin installation, r…
Update the description to accurately cover all changes in the pull request, or remove those changes from the pull request. Align the What Changed, Why, Testing, Notes, and Checklist sections with the actual plugin administration CLI, RPC, h…
Passed checks: 3 passed
Check name
Status
Explanation
Linked Issues check
[#35104] The PR adds host-admin runtime RPC methods for plugin installation, removal, and system enablement. It adds CLI commands for install, list, approve, enable, disable, removal, and system contr…
Out of Scope Changes check
The changes stay within [#35104]. The capability, shared administration code, installation-state handling, headless startup wiring, CLI review and approval flow, trust-tier sharing, documentation, and…
Title check
The title clearly describes two primary changes: wiring plugins for headless orca serve and enabling plugins from the CLI. Both changes are present in the pull request.
Full details: Linked Issues check
Explanation
[#35104] The PR adds host-admin runtime RPC methods for plugin installation, removal, and system enablement. It adds CLI commands for install, list, approve, enable, disable, removal, and system control. Shared host administration reuses the existing installer and refresh path. Installation does not enable or approve a plugin. Approval uses the reviewed fingerprint. Startup wiring now works without a desktop window. The reported tests cover these behaviors, including mobile refusal and headless startup. The documented orcad limitation and deferred VM-recipe runtime support do not create unmet coding requirements for this issue.
Full details: Out of Scope Changes check
Explanation
The changes stay within [#35104]. The capability, shared administration code, installation-state handling, headless startup wiring, CLI review and approval flow, trust-tier sharing, documentation, and tests directly support headless plugin administration. The IPC refactor preserves the desktop path. Mobile administration, orcad plugin support, and runtime VM-recipe consumption are documented exclusions or follow-up work. No unrelated change is demonstrated.
Full details: Docstring Coverage
Explanation
Docstring coverage is 31.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 35 functions across 29 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
Full details: Description check
Explanation
The description follows the required template and explains the headless wiring and system-toggle changes. However, it conflicts with the changeset and objectives by stating that plugin installation, removal, listing, approval, and related administration were dropped, while the pull request still adds those commands, handlers, RPC methods, schemas, documentation, and tests.
Resolution
Update the description to accurately cover all changes in the pull request, or remove those changes from the pull request. Align the What Changed, Why, Testing, Notes, and Checklist sections with the actual plugin administration CLI, RPC, host-service, documentation, and test changes.
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Adds CLI and RPC commands for installing, approving, removing and toggling plugins on Electron hosts without a window.
The CLI can review and manage plugins on the selected host.
Headless servers can install and manage plugins through runtime RPC.
Desktop and CLI plugin reviews use the same trust labels and warnings.
Diagram
sequenceDiagram
participant User
participant CLI
participant Host as Electron host
participant Plugins as PluginService
User->>CLI: Install plugin on selected server
CLI->>Host: plugins.install
Host->>Plugins: Install and refresh without consent
User->>CLI: List and review plugin
CLI->>Host: plugins.list
Host-->>User: Access, warnings and review fingerprint
User->>CLI: Approve reviewed fingerprint
CLI->>Host: plugins.consent
Host->>Plugins: Check fingerprint and save approval
Files skipped from review as they are similar to previous changes: 2
src/cli/root-help-text-primary.ts
src/cli/command-scoped-flag-help.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.
Tests: the changed tests plus every one-hop importer of the changed modules, 44 files. All pass except src/main/plugins/plugin-service-reconciliation.test.ts, which times out on unmodified main too (5 of its 11 tests at 30s, on the same machine). I didn't touch it.
vocabulary-policy.test.ts caught plugin remove. It's now plugin rm, with remove kept as an alias like account rm.
I didn't run the full check:changed related-test step: it selects about 10k test files for this diff, so CI's sharded run is the better place for it.
Rebuilt on top of #35470 (Plugin Alpha) as 17aa47f, rebased on today's main. Plugin Alpha already covered three things here, so I dropped my versions:
the consent trust logic, now shared in src/shared/plugins/approval/plugin-trust.ts and used by both the inline consent and orca plugin list;
listing installed plugins while the system is off;
theme cleanup on removal.
What this PR still adds:
the plugins.install / remove / setSystemEnabled host-admin methods;
plugins.administration.v1, which orcad never advertises;
removing a plugin while the system is off;
the headless wiring fix, where the settings listener and runtime delegate no longer depend on a window;
the orca plugin CLI with fingerprint-bound approval.
One behaviour change: plugins.consent / setEnabled now say "The plugin system is off on this host" instead of "unknown plugin" when it's off.
Local checks: build, typecheck, the RPC catalog check, code quality, import boundaries, ratchets, lint and format pass; 47 test files and 324 tests pass. The reused one-step marketplace install (enableIfData) is untouched, because it auto-approves content-only plugins and this path never approves.
? `Installed ${installed.pluginKey} ${installed.version}. The plugin system is off and approval needs it on: run \`orca plugin system on\`, review it with \`orca plugin list\`, then run ${approve}.`
: `Installed ${installed.pluginKey} ${installed.version}. Review it with \`orca plugin list\`, then run ${approve}.`
The reason will be displayed to describe this comment to others. Learn more.
Install instructions lose the server
After orca plugin install … --environment build-box, the success message tells the user to run bare orca plugin system on and orca plugin list. Without the server selector, those commands target the local host. The user can turn on local plugins and still fail to find the server's new plugin.
Pass targetSuffix(context) into formatInstalled and include it in both commands.
jutaz
changed the title
feat(plugins): install and enable plugins on a headless server
fix(plugins): wire the plugin service on a headless serve, and turn plugins on from the CLI
Oct 31, 2026
Shrunk to b8e45d4 (+188/−56) now that #36280/#36288 landed and #36647 is open, so this no longer duplicates any of nwparker's CLI. Two pieces are left, both tracked in #36709:
Headless wiring bug:setRuntimeDelegate and the settings→refresh listener only ran when a window registered IPC, so on a windowless orca serve every worker or panel host call failed with "runtime is not available". They now run in initializeMainProcessPlugins. A new startup test runs it with no window, and it fails on unmodified main.
plugins.setSystemEnabled (host-admin) plus orca plugin system on|off, in the plugin-runtime group with the existing helpers. plugin_system_disabled now names the command as well as Settings.
A trial merge with #36647's head (b8d4de7) is clean except for the two generated files (regenerate them) and an adjacent-line hand merge in skill-guides/orca-cli/references/plugins.md. I'm happy to rebase onto #36647 after it lands.
Thank you @jutaz, this was the right fix. We rebased your commits onto current main and landed them in our own PR, which let us run CI, with your authorship preserved. Closing this one in favour of that PR. Thanks again!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
On a headless Orca server (
orca serveon the Electron runtime), two things kept plugins from working:plugin_system_disablederror said to click "Turn on plugins" in Settings, which a headless server doesn't have.Now both work without a window: the link is made at startup, and
orca plugin system on(with--environment <server>) turns plugins on.Builds on #36280/#36288; complements #36647 (install/uninstall), which doesn't cover these two.
What Changed
src/main/startup/main-process-plugins.ts):pluginService.setRuntimeDelegate(runtime)and thepluginSystemEnabled/devPluginPaths→refresh()settings listener used to live inregisterPluginHandlers(src/main/ipc/plugins.ts). That function only runs when a window registers IPC.initializeMainProcessPlugins, once per process, window or not. The listener joins the existing pluginonSettingsChangedhandler there.registerPluginHandlersno longer takesruntime. Its one production caller and two test fixtures are updated, and nothing else in that file changes.unavailable: runtime is not available.devLoad,refreshand the RPC enable path still worked, because they callrefresh()themselves. A settings write that didn't come with its own refresh had no effect until the next refresh.plugins.setSystemEnabled(src/main/runtime/rpc/methods/plugins.ts),host-admin, params{ enabled: boolean }:pluginSystemEnabledthrough a closure thatsetPluginServiceForRpcinjects, likesetDevPluginPaths.refresh(), so a failed discovery comes back as an error and not as a stale list. It returns{ enabled, plugins }.approvePluginFromCliand--approve <fingerprint>(plugins: require an explicit approve fingerprint for RPC plugin grants #36279).orca plugin system on/orca plugin system off(src/cli/specs/plugin.ts,src/cli/handlers/plugin-runtime.ts):plugin-runtimehandler group, shaped likeplugin dev load|reload|unload. They use the samecallPlugins,printPluginResultandformatPluginRow.method_not_found, and the existing translation turns that intoruntime_unsupported.onprints the plugin list.offkeeps installs, approvals and development folders.plugin_system_disabledhint: the next step insrc/main/plugins/plugin-dev-folders.tsalso namesorca plugin system onfor a host with no window. Theorca-cliskill guide (skill-guides/orca-cli/references/plugins.md, regenerated intosrc/cli/bundled-skill-guides.ts) lists the command and no longer says there is no CLI way.Why
Turning plugins on uses a dedicated
host-adminmethod instead of wideningsettings.update.settings.updateissettings-write, and turning plugins on lets third-party code run on the host, so it sits withplugins.consent,plugins.devLoadand the rest of plugin administration.The wiring fix moves the two calls to where the plugin service is built, rather than adding a headless-only path. One registration now covers the desktop and serve.
This PR used to also add install from a path or Git URL, remove,
listwith a trust review,approve --fingerprintand aplugins.administration.v1capability. #36279/#36280/#36288 and #36647 now cover those with their own approval flow and vocabulary, so this PR drops them.Linked Issue
Fixes #36709 (refs #35104). With #36647, a headless controller can turn plugins on, install, approve and run a plugin from the CLI.
Visual Proof
N/A: no UI change. The desktop still turns plugins on through Settings → Plugins; this adds a CLI/RPC path and moves main-process wiring.
Testing
src/main/startup/main-process-plugins.test.ts(new) runs the realinitializeMainProcessPlugins, with a realPluginServiceon a temp profile and no window. It checks that the runtime delegate is bound and that exactly one settings→refresh listener fires. It fails on unmodifiedmain(expected "setRuntimeDelegate" to be called). The old listener test insrc/main/ipc/plugins.test.tsis removed, sinceregisterPluginHandlersno longer owns that listener.src/main/runtime/rpc/methods/plugins.test.ts:plugins.setSystemEnabledishost-admin, writes the setting, refreshes after the write, and never consents.Locally, on
upstream/main2487a25:pnpm run build:packages,pnpm tc, the RPC params catalog and bundled-skill-guide checks,check-changed-code-quality.mjs upstream/main, import boundaries, ratchets, and oxlint/oxfmt/anti-slop on the changed files;Not run end to end against a live
orca serve.I manually tested these changes locally
Automated tests added/updated, or explained why not below
AI Disclosure
Written with Claude (Anthropic) via an agent harness; reviewed by the author.
Review
--environment/--pairing-code. The method is new and additive, so an old host givesruntime_unsupported.host-admin, and not in the mobile allowlist, so phones are refused. No mobile UI.registerPluginHandlersdropped itsruntimeargument and has one production caller. On the desktop, the listener and delegate now register at plugin startup, just before the first window, instead of at the first window's IPC registration.host-admin, likeplugins.consent/devLoad. Turning plugins on approves nothing, andsettings.update's schema is not widened.Agent skill upstream boundary
Notes
orca servedefaults to orcad, which has no plugin system. Against orcad,plugin system onfails like every otherplugins.*method ("Plugin service is not available on this runtime"). UseORCA_SERVE_RUNTIME=electron.src/main/ipc/plugins.ts,src/main/startup/main-process-plugins.ts, the CLI handler, specs and params cleanly. Only the generatedrpc-params-catalog.generated.ts,bundled-skill-guides.tsandskill-guides/orca-cli/references/plugins.mdconflict. The first two regenerate, and the third needs a hand merge of adjacent lines.orca plugin listshows the execution level but none of the desktop consent's warnings or VM recipe commands. Runtime RPC workspace creation doesn't read pluginvmRecipesyet (follow-up port of feat(cli): create workspaces on environment recipes #28439).Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)