Find a file
heunghingwan 1d4f77869e
refactor(server): route background-process prompt through the instance client factory (#630)
## Summary

PR #561 introduced `createInstanceClient` so server modules would stop
hand-assembling the OpenCode loopback URL and use the generated SDK
client. Three call sites adopted it; the background-process completion
prompt was the remaining holdout — it still hand-built
`http://127.0.0.1:{port}/session/{id}/prompt_async` with manual
auth/content-type wiring. This PR routes it through the factory +
`client.session.promptAsync`, and consolidates the loopback host
constant.

## Why

- **Consistency** — the last hand-built OpenCode-instance loopback URL
(explicitly flagged in #561 as a future adoption target) now uses the
same version-correct SDK path as the permission replier, yolo metadata,
and the opencode updater.
- **Correctness** — the old call sent no directory at all;
`notify.directory` was stored but never used (dead data). The factory
now scopes the prompt to the session's own directory, which for a
worktree/subdirectory session is the correct project context (a normal
session coincides with the workspace root, so no change there).

## What changed

**Migration** — `background-processes/manager.ts`:
`sendCompletionPrompt` → `createInstanceClient(...)` +
`client.session.promptAsync({sessionID, parts:[{type:"text", text,
synthetic:true}]}, {throwOnError:true})`. Synthetic `<system-message>`
text + `synthetic:true` preserved. Side effect: the call now carries the
factory's 10s loopback timeout (the old fetch had none; the failure is
swallowed + warn-logged, so finalization is unaffected).

**Factory** — `workspaces/instance-client.ts`: new optional `directory`
override in `InstanceClientOptions` (defaults to workspace root); adopts
shared `LOOPBACK_HOST`. Fetch wrapper unchanged from `dev`.

**Consolidation** — new `workspaces/loopback.ts` exporting
`LOOPBACK_HOST = "127.0.0.1"`, adopted by `instance-client.ts`,
`instance-events.ts`, and the workspace manager's health + TCP probes
(`manager.ts`). (Other `127.0.0.1` uses — remote-access proxy, sidecars
— are different concerns, left alone.)

**Tests**
- `workspaces/instance-client.test.ts` (8 cases): null-when-no-port,
loopback host/port targeting, auth header attach/omit, directory scoping
present/absent, explicit directory override, timeout aborts stuck call.
- `background-processes/manager.test.ts` (2 cases, first test for this
manager): drives the real lifecycle (spawn + exit) against a mocked
transport; asserts the `prompt_async` POST
URL/body/authorization/`x-opencode-directory` (= session dir, distinct
from workspace root), and that a failed prompt is swallowed +
warn-logged without aborting finalization.

## Behavior parity

| Aspect | Before | After |
|---|---|---|
| Route / body | `/session/{id}/prompt_async`,
`{parts:[{type,text,synthetic}]}` | **same** (via SDK) |
| Auth header | manual | via factory |
| Directory scoping | none | session's `notify.directory` |
| Loopback timeout | none | 10s (factory default) |
| Error handling | throw on non-2xx → caught + warn-logged |
`throwOnError` → caught + warn-logged (**same**) |

## Testing

10 new tests pass. The background-process integration test is stable
12/12 under `--test-concurrency=4` and is mutation-killed if the
`directory` override or `throwOnError` is removed. Full server suite:
255 pass / 1 fail — the single failure is pre-existing
(`git-clone.test.ts` Chinese-locale git message, confirmed failing
identically on `dev`). Server typecheck clean.

## Risk / rollback

Additive refactor; the factory is production-proven by #561's consumers.
Rollback = revert the single commit.

## Notes

- Per the file-length guideline:
`packages/server/src/background-processes/manager.ts` is ~684 lines
(above the 500 warn threshold, under the 800 limit); this PR shrank it
by ~16 lines net.
2026-07-30 08:33:06 +02:00
.github fix(permissions): ignore stale permission updates (#621) 2026-07-26 17:57:48 +01:00
.opencode feat(yolo): move permission auto-accept (Yolo) to the server (#561) 2026-07-08 21:13:44 +02:00
dev-docs build: publish deb and portable tar.gz Linux artifacts (#492) 2026-07-12 19:29:33 +02:00
docs feat(ui): support bracket math delimiters (#588) 2026-07-14 14:00:26 +02:00
images Move screenshots to correct folder 2025-11-21 21:59:58 +00:00
packages refactor(server): route background-process prompt through the instance client factory (#630) 2026-07-30 08:33:06 +02:00
scripts chore: TASK-075 automate Winget updates on release (#513) 2026-06-03 09:03:46 +02:00
temp Bump to v0.11.2 2026-02-17 18:47:21 +00:00
.gitignore chore: ignore local artifacts and add cloudflare lockfile 2026-01-22 16:42:47 +00:00
AGENTS.md Improve compact mobile UI controls (#564) 2026-06-19 13:52:26 +01:00
BUILD.md build: publish deb and portable tar.gz Linux artifacts (#492) 2026-07-12 19:29:33 +02:00
CONTRIBUTING.md docs: add CONTRIBUTING.md guide for new contributors (#484) 2026-06-13 14:01:04 +02:00
LICENSE chore(license): add MIT license 2026-02-02 11:22:49 +00:00
package-lock.json feat(desktop): persist and restore UI state across restarts (#578) 2026-07-15 10:22:23 +01:00
package.json Bump version to 0.18.0 2026-06-19 13:55:00 +01:00
README.md build: publish deb and portable tar.gz Linux artifacts (#492) 2026-07-12 19:29:33 +02:00
THIRD_PARTY_NOTICES.md feat(usage): show provider quota in session status (#584) 2026-07-15 10:25:00 +01:00

CodeNomad

The AI Coding Cockpit for OpenCode

CodeNomad transforms OpenCode from a terminal tool into a premium desktop workspace — built for developers who live inside AI coding sessions for hours and need control, speed, and clarity.

OpenCode gives you the engine. CodeNomad gives you the cockpit.

Multi-instance workspace


Features

  • 🚀 Multi-Instance Workspace
  • 🌐 Remote Access
  • 🧠 Session Management
  • 🎙️ Voice Input & Speech
  • 🌳 Git Worktrees
  • 💬 Rich Message Experience
  • 🧩 SideCars
  • ⌨️ Command Palette
  • 📁 File System Browser
  • 🔐 Authentication & Security
  • 🔔 Notifications
  • 🎨 Theming
  • 🌍 Internationalization

Getting Started

🖥️ Desktop App

Available as both Electron and Tauri builds — choose based on your preference.

Download the latest installer for your platform from Releases.

Platform Formats
macOS DMG, ZIP (Universal: Intel + Apple Silicon)
Windows NSIS Installer, ZIP (x64, ARM64)
Linux Tauri deb, Electron portable tar.gz (x64)

The Tauri deb is currently built and installation-tested on Ubuntu 24.04. Compatibility with older Debian-based distributions is not yet guaranteed.

💻 CodeNomad Server

Run as a local server and access via browser. Perfect for remote development.

npx @neuralnomads/codenomad --password <your-password> --launch

Authentication required: The server requires a password on first run. You can pass it via --password, the CODENOMAD_SERVER_PASSWORD environment variable, or create an auth.json file (see Server Documentation).

Self-signed certificate: On first launch with HTTPS enabled (the default), your browser will show a "Your connection is not private" warning. This is expected — the server generates a local self-signed certificate automatically. Click Advanced → Proceed to localhost to continue. For local-only use without the warning, run with --https=false --http=true.

See Server Documentation for flags, TLS, auth, and remote access.

🧪 Dev Releases

Bleeding-edge builds from the dev branch:

npx @neuralnomads/codenomad-dev --password <your-password> --launch

SideCars

SideCars let you open local web tools inside CodeNomad as tabs.

Configuration
  • Name: Display name used in CodeNomad
  • Port: Local HTTP or HTTPS service running on 127.0.0.1:<port>
  • Base path: Mounted under /sidecars/:id
  • Prefix mode:
    • Preserve prefix forwards the full /sidecars/:id/... path upstream
    • Strip prefix removes /sidecars/:id before forwarding the request upstream
VSCode (OpenVSCode Server)

Run with Docker:

docker run -it --init -p 8000:3000 -v "${HOME}:${HOME}:cached" -e HOME=${HOME} gitpod/openvscode-server --server-base-path /sidecars/vscode

Add SideCar as:

  • Name: VSCode
  • Port: http://127.0.0.1:8000
  • Base path: /sidecars/vscode
  • Prefix mode: Preserve prefix
Terminal (ttyd)

Run with:

ttyd --writable zsh

Add SideCar as:

  • Name: Terminal
  • Port: http://127.0.0.1:7681
  • Base path: /sidecars/terminal
  • Prefix mode: Strip prefix

Requirements

  • OpenCode CLI — must be installed and in your PATH
  • Node.js 18+ — for server mode or building from source

Development

CodeNomad is a monorepo built with:

Package Description
packages/server Core logic & CLI — workspaces, OpenCode proxy, API, auth, speech
packages/ui SolidJS frontend — reactive, fast, beautiful
packages/electron-app Desktop shell — process management, IPC, native dialogs
packages/tauri-app Tauri desktop shell (experimental)

Quick Start

git clone https://github.com/NeuralNomadsAI/CodeNomad.git
cd CodeNomad
npm install
npm run dev

Troubleshooting

macOS: "CodeNomad.app is damaged and can't be opened"

Gatekeeper flag due to missing notarization. Clear the quarantine attribute:

xattr -dr com.apple.quarantine /Applications/CodeNomad.app

On Intel Macs, also check System Settings → Privacy & Security on first launch.

Linux (Wayland + NVIDIA): Tauri App closes immediately

WebKitGTK DMA-BUF/GBM issue. Run with:

WEBKIT_DISABLE_DMABUF_RENDERER=1 codenomad-tauri

See full workaround in the original README.


Community

Star History


Built with ♥ by Neural Nomads · MIT License