Skip to content

feat(server): headless, Electron-free @stablyai/orca-server#6844

Open
Jinwoo-H wants to merge 12 commits into
mainfrom
Jinwoo-H/headless-orca-server
Open

feat(server): headless, Electron-free @stablyai/orca-server#6844
Jinwoo-H wants to merge 12 commits into
mainfrom
Jinwoo-H/headless-orca-server

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds @stablyai/orca-server: an npm-installable, headless Orca runtime server that runs under plain Node without Electron or X11. After install, the supported cross-platform command is:

npm install -g @stablyai/orca-server
orca-ide serve

The npm package intentionally exposes only the orca-ide bin. It does not publish orca, to avoid shadowing GNOME Orca on Linux or colliding with desktop-app CLI shims on macOS/Windows. orca-server.js remains the package main/direct bundle entry, not a public global command.

Screenshots

No visual change.

Testing

  • pnpm exec oxlint --format github (0 errors; existing unrelated warnings remain)
  • pnpm typecheck
  • pnpm vitest run config/scripts/package-electron-runtime-contract.test.mjs src/main/server/node-server-args.test.ts src/main/server/node-app-environment.test.ts src/main/speech/openai-api-key-store.test.ts src/main/providers/local-pty-shell-ready.test.ts src/cli/index.test.ts src/cli/runtime/launch.test.ts src/main/server/node-server-main.test.ts src/main/server/shared-user-data-guard.test.ts src/shared/secure-file.test.ts
  • pnpm vitest run src/main/persistence.test.ts -t "Kagi session"
  • pnpm run build:server
  • node config/scripts/headless-server-smoke-client.mjs "$PWD/out/server/orca-ide.js"
  • Added/updated regression coverage for npm package invariants, serve arg parsing, node app paths, secret persistence, secure-file exclusive writes, and headless smoke behavior

AI Review Report

Reviewed the PR with CodeRabbit plus an additional AI pass focused on the npm CLI surface and release flow. Main findings addressed:

  • Keep the public npm bin as orca-ide only; do not publish orca or orca-server as global bins.
  • Sync @stablyai/orca-server releases with desktop stable and RC releases: stable publishes to npm latest, RC publishes to npm alpha.
  • Pin Node 24 for host prebuilds and verify node-pty ABI compatibility before copying prebuilts.
  • Run Docker images as the non-root node user and keep healthchecks aligned with the published bin.
  • Harden dynamic imports, serve arg parsing, app path resolution, and release workflow secret scoping.
  • Preserve existing node secret-store keys on corrupt/read/race failures instead of regenerating and orphaning ciphertext.

Cross-platform review explicitly covered macOS, Linux, and Windows command naming, npm bin collision behavior, Node ABI/prebuild coverage, path handling, Docker/Linux behavior, and Electron-free runtime shims.

Security Audit

The server continues to rely on pairing plus E2EE RPC for API access; the Docker/default bind behavior should not be treated as an unauthenticated open control plane. Secret storage uses AES-256-GCM on Node hosts and now refuses to overwrite an existing invalid key; first-run key creation uses exclusive secure-file publication so competing server processes re-read the winner instead of replacing it.

Publishing uses the repo-level NPM_TOKEN GitHub secret, which is configured in stablyai/orca; the workflow passes only that secret into the reusable npm publish job. No npm credential or token value is stored in the repo.

Notes

  • Browser panes are unavailable in the headless package by design after the Electron/browser decoupling; clients should fall back to a local browser tab. A separate browser sidecar/add-on package remains possible future work.
  • The npm package version is derived from the desktop release tag and must match package.json, so normal stable/RC release cuts keep npm aligned with Orca app versions.
  • Linux users should run orca-ide serve; this matches the existing Linux desktop CLI naming and avoids the GNOME Orca command collision.

Jinwoo-H and others added 2 commits June 30, 2026 01:38
Adds an npm-installable headless Orca runtime server that boots under plain
Node — no Electron, no X11 — so the runtime can run in lightweight containers
and microVMs. Pairs with desktop/mobile/CLI clients over the existing E2EE RPC
protocol and exposes a pairing URL on stdout.

How it works:
- Host abstractions (AppEnvironment, SecretStore, managed fetch) replace direct
  electron `app`/`safeStorage`/`net` usage in the runtime core. Desktop installs
  Electron-backed impls (behavior unchanged); the node server installs
  Node-backed impls. SecretStore uses AES-256-GCM at rest (not plaintext) on
  keychain-less hosts.
- A new `orca-server` bin (src/server) boots the runtime + RPC + headless PTY
  host. The bundle is built with esbuild (electron aliased to a throwing/
  delegating shim, native addons external).
- node-pty is the only ABI-sensitive native dep. The publish flow ships a
  prebuilt matrix (linux x64/arm64 glibc+musl, darwin x64/arm64) so installs are
  toolchain-free; node-pty's source build is the fallback.
- Browser panes / tray / notifications / auto-updater degrade gracefully via the
  existing capability flags; clients fall back to a local browser tab.

Coexistence: warns if sharing a desktop install's userData (set
ORCA_USER_DATA_PATH to isolate).

CI: .github/workflows/publish-orca-server.yml builds the 6-cell prebuilt matrix,
verifies the tarball ships every slot (pty.node + spawn-helper), and publishes
@stablyai/orca-server. Requires an NPM_TOKEN repo secret (publish step no-ops
without it). Triggered by an `orca-server-v*` tag or workflow_dispatch.

Verified: real CLI client ↔ node server (status, repo add, terminal
create/read/send full-duplex over E2EE); toolchain-free install + PTY spawn on
glibc + musl (x64 and arm64) in CI; tarball install boots + spawns a PTY.
…fix conflicts)

Co-authored-by: Orca <help@stably.ai>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds shared host abstractions for app environment, secret storage, and managed fetch, then migrates main-process code and tests to those abstractions. It also introduces a headless Node server entrypoint and CLI bridge, plus build, Docker, prebuild, smoke-test, and GitHub Actions publishing tooling for @stablyai/orca-server.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.37% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main change: a headless, Electron-free orca-server package.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description follows the required template and includes summary, screenshots, testing, AI review, security, and notes with cross-platform coverage.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…mjs (lint)

Co-authored-by: Orca <help@stably.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/providers/local-pty-shell-ready.ts (1)

51-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Build the shell-wrapper paths with path.join()
getShellReadyWrapperRoot() and getRequiredShellReadyWrapperPaths() still concatenate runtime filesystem paths with /, which can produce mixed separators on Windows.

Source: Coding guidelines

🧹 Nitpick comments (1)
src/main/terminal-history.test.ts (1)

76-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Forward the requested path name through the mock.

getPath currently drops the name argument, so this test can pass even if terminal-history asks for the wrong app path. Mirror the AppEnvironment.getPath(name) contract.

Proposed test stub adjustment
     setAppEnvironment({
-      getPath: () => getPathMock(),
+      getPath: (name) => getPathMock(name),
       getAppPath: () => '/fake/app',

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6213cf3a-e32f-498b-ab5e-40ab7d808ed0

📥 Commits

Reviewing files that changed from the base of the PR and between 8e06ee1 and 3779553.

📒 Files selected for processing (52)
  • .github/workflows/publish-orca-server.yml
  • config/docker/Dockerfile.server
  • config/docker/orca-server-with-xvfb.sh
  • config/scripts/build-server-prebuilds.mjs
  • config/scripts/build-server.mjs
  • config/scripts/headless-server-smoke-client.mjs
  • config/scripts/install-node-pty-prebuilt.mjs
  • package.json
  • src/main/automations/service.test.ts
  • src/main/browser/browser-session-registry.persistence.test.ts
  • src/main/browser/browser-session-registry.ts
  • src/main/daemon/daemon-init.test.ts
  • src/main/daemon/daemon-init.ts
  • src/main/index.ts
  • src/main/integration-credential-file.ts
  • src/main/ipc/pty.test.ts
  • src/main/ipc/pty.ts
  • src/main/jira/client.test.ts
  • src/main/jira/client.ts
  • src/main/linear/client.test.ts
  • src/main/linear/client.ts
  • src/main/memory/collector.test.ts
  • src/main/memory/collector.ts
  • src/main/network/electron-managed-fetch.ts
  • src/main/persistence.test.ts
  • src/main/persistence.ts
  • src/main/providers/local-pty-shell-ready.test.ts
  • src/main/providers/local-pty-shell-ready.ts
  • src/main/runtime/electron-app-environment.ts
  • src/main/runtime/electron-secret-store.ts
  • src/main/runtime/file-watcher-host.ts
  • src/main/runtime/mobile-pairing-userdata-path.test.ts
  • src/main/runtime/orca-runtime.ts
  • src/main/server/electron-shim.ts
  • src/main/server/node-app-environment.test.ts
  • src/main/server/node-app-environment.ts
  • src/main/server/node-secret-store.ts
  • src/main/server/node-server-args.test.ts
  • src/main/server/node-server-args.ts
  • src/main/server/node-server-main.ts
  • src/main/server/shared-user-data-guard.test.ts
  • src/main/server/shared-user-data-guard.ts
  • src/main/speech/openai-api-key-store.test.ts
  • src/main/speech/openai-api-key-store.ts
  • src/main/telemetry/client.ts
  • src/main/terminal-history.test.ts
  • src/main/terminal-history.ts
  • src/main/terminal-scrollback-snapshots.ts
  • src/server/index.ts
  • src/shared/app-environment.ts
  • src/shared/managed-fetch.ts
  • src/shared/secret-store.ts

Comment thread .github/workflows/publish-orca-server.yml Outdated
Comment thread .github/workflows/publish-orca-server.yml
Comment thread config/docker/Dockerfile.server Outdated
Comment thread config/docker/Dockerfile.server Outdated
Comment thread config/scripts/build-server.mjs
Comment thread src/main/server/node-secret-store.ts Outdated
Comment thread src/main/server/node-server-args.ts Outdated
Comment thread src/main/server/node-server-main.ts
Comment thread src/main/speech/openai-api-key-store.ts
Comment thread src/shared/app-environment.ts
@stage-review

stage-review Bot commented Jul 1, 2026

Copy link
Copy Markdown

Co-authored-by: Orca <help@stably.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1a1deb3b-80a8-4e2c-b281-0536d2694e16

📥 Commits

Reviewing files that changed from the base of the PR and between a94f64e and e01d9a9.

📒 Files selected for processing (10)
  • .github/workflows/publish-orca-server.yml
  • config/docker/Dockerfile.server
  • config/docker/orca-server-with-xvfb.sh
  • config/scripts/build-server.mjs
  • config/scripts/headless-server-smoke-client.mjs
  • src/cli/index.ts
  • src/cli/runtime/launch.test.ts
  • src/cli/runtime/launch.ts
  • src/main/server/node-server-main.ts
  • src/server/cli.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • config/docker/Dockerfile.server
  • config/docker/orca-server-with-xvfb.sh
  • config/scripts/headless-server-smoke-client.mjs
  • src/main/server/node-server-main.ts
  • config/scripts/build-server.mjs
  • .github/workflows/publish-orca-server.yml

Comment thread src/cli/index.ts
Comment thread src/cli/runtime/launch.ts
Comment thread src/server/cli.ts Outdated
Co-authored-by: Orca <help@stably.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
.github/workflows/publish-orca-server.yml (2)

96-101: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

macos-13 runner is retired — pipeline will fail on darwin-x64 slot.

GitHub retired the macos-13 hosted runner image by December 4, 2025; jobs using this label now terminate immediately. This was flagged in a prior review (recommending macos-15-intel) but the runner label here is unchanged.

🐛 Proposed fix
-          - { runner: macos-13,         slot: darwin-x64,        kind: darwin }
+          - { runner: macos-15-intel,   slot: darwin-x64,        kind: darwin }
Is the macos-13 GitHub Actions runner label still usable in 2026?

103-121: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Host-toolchain prebuild slots aren't pinned to Node 24.

resolve/publish jobs explicitly pin NODE_VERSION, and musl slots build inside node:24-alpine, but the glibc/darwin host-toolchain step here has no actions/setup-node step, so node-gyp builds against whichever Node ABI the runner image ships by default — risking an ABI mismatch with the Node 24 target used elsewhere.

🔧 Proposed fix
       - uses: actions/checkout@v6
         with:
           ref: refs/tags/${{ needs.resolve.outputs.tag }}
+
+      - uses: actions/setup-node@v6
+        if: matrix.kind != 'musl'
+        with:
+          node-version: ${{ env.NODE_VERSION }}

       # glibc + darwin: build natively on the host runner.
       - name: Build prebuilt (host toolchain)

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 571e09f9-e2f3-40c7-9e5f-1bfdfebbaac8

📥 Commits

Reviewing files that changed from the base of the PR and between e01d9a9 and 23a78f5.

📒 Files selected for processing (5)
  • .github/workflows/publish-orca-server.yml
  • .github/workflows/release-cut.yml
  • config/scripts/package-electron-runtime-contract.test.mjs
  • config/scripts/publish-complete-draft-releases.mjs
  • config/scripts/publish-complete-draft-releases.test.mjs

Co-authored-by: Orca <help@stably.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
.github/workflows/publish-orca-server.yml (1)

164-169: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid persisting write-scoped Git credentials before install/build.

contents: write is only needed for the final gh release edit, but actions/checkout persists its token by default before dependency install and build steps. Disable persisted credentials here, or split the release edit into a separate write-scoped job.

Proposed fix
       - uses: actions/checkout@v6
         with:
           ref: refs/tags/${{ needs.resolve.outputs.tag }}
+          persist-credentials: false
.github/workflows/release-cut.yml (1)

1255-1265: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Gate the Homebrew bump on the publish job succeeding

This if: replaces the default success() guard for needs, so homebrew-bump-published-rc-draft can still run after publish-orca-server-published-rc-drafts fails and point Homebrew at an unpublished RC.

🔒 Suggested fix
-    if: ${{ needs.cut.outputs.latest_publishable_rc_tag != '' }}
+    if: ${{ needs.cut.outputs.latest_publishable_rc_tag != '' && needs.publish-orca-server-published-rc-drafts.result == 'success' }}
♻️ Duplicate comments (2)
.github/workflows/publish-orca-server.yml (2)

118-130: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Pin host prebuilds to the workflow Node version.

The non-musl prebuild path still invokes the runner’s default node; native node-pty artifacts can be built against a different ABI than NODE_VERSION: 24.

Proposed fix
       - uses: actions/checkout@v6
         with:
           ref: refs/tags/${{ needs.resolve.outputs.tag }}
+
+      - uses: actions/setup-node@v6
+        if: matrix.kind != 'musl'
+        with:
+          node-version: ${{ env.NODE_VERSION }}
 
       # glibc + darwin: build natively on the host runner.

106-111: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Use a supported Intel macOS runner label.

Line 111 still uses macos-13, which actionlint reports as an unknown hosted runner label. Use a currently supported Intel macOS label such as macos-15-intel.

Source: Linters/SAST tools

🧹 Nitpick comments (3)
src/cli/runtime/launch.ts (1)

5-14: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Share the serve options type with the server bridge.

src/server/cli.ts redeclares the same ServeOrcaAppOptions shape, so future flags can drift between the injected hook contract and the server argument mapper. Prefer importing this exported type there instead of maintaining two copies.

config/scripts/publish-complete-draft-releases.mjs (1)

118-169: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

published/published_* naming is now misleading when publish: false.

published.push(tag) and the derived published_count/published_tags/published_tags_json/latest_published_tag outputs are computed the same way regardless of whether the PATCH (draft: false) actually ran. When called with publish: false (as the release-cut workflow's "Find complete release-cut RC drafts" step always does), these outputs claim tags are "published" while the GitHub release is still a draft. The new publishable_* fields duplicate the exact same values and are the ones the workflow now actually consumes — the old published_* fields are effectively vestigial and could mislead a future consumer into thinking a release was actually made public.

♻️ Suggested clarification
-    published.push(tag)
+    // "published" here means "publish-eligible / prepared"; only reflects an
+    // actual GitHub PATCH when `publish` is true.
+    published.push(tag)

Consider renaming the internal variable/outputs (e.g. preparedOrPublished) or dropping the now-duplicate published_* output keys once no consumer depends on them, to avoid the two names silently diverging in meaning.

.github/workflows/release-cut.yml (1)

1223-1239: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Scope secrets: inherit for the new reusable-workflow calls.

Both new jobs pass all repository/organization secrets to the called workflows rather than only what's required (e.g. NPM_TOKEN, release token). This broadens blast radius if the reusable workflow is ever compromised or misconfigured, per the static analysis hint.

Also applies to: 1255-1265

Source: Linters/SAST tools


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4353e9b3-f41c-4dc5-8012-8d2ebaf6555d

📥 Commits

Reviewing files that changed from the base of the PR and between 23a78f5 and 9e77b19.

📒 Files selected for processing (14)
  • .github/workflows/publish-orca-server.yml
  • .github/workflows/release-cut.yml
  • config/scripts/package-electron-runtime-contract.test.mjs
  • config/scripts/publish-complete-draft-releases.mjs
  • config/scripts/publish-complete-draft-releases.test.mjs
  • src/cli/handlers/core.ts
  • src/cli/help.ts
  • src/cli/index.test.ts
  • src/cli/runtime/launch.test.ts
  • src/cli/runtime/launch.ts
  • src/cli/specs/serve.ts
  • src/main/server/node-server-main.test.ts
  • src/main/server/node-server-main.ts
  • src/server/cli.ts
✅ Files skipped from review due to trivial changes (1)
  • src/cli/help.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/server/cli.ts
  • config/scripts/package-electron-runtime-contract.test.mjs
  • src/cli/runtime/launch.test.ts
  • src/main/server/node-server-main.ts

Comment thread .github/workflows/publish-orca-server.yml Outdated
Jinwoo-H and others added 4 commits July 1, 2026 23:54
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Orca <help@stably.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant