Pin the server and UI client to the installed beta runtime and reject mismatched CLI versions before shared-service startup. Delegate proven shared-service shutdown to Service.stop while retaining CodeNomad lease, peer, registration, endpoint, and process-identity checks.
Adopt native Forms and the client/solid data reducer for live messages, tools, permissions, and input forms. Propagate internal stream generations into authoritative reconnect reconciliation and replace directory-wide session scans with native project cursor pagination.
Fail worktree deletion when session evacuation fails and deduplicate canonical workspace folders instead of exposing non-isolated duplicate instances. Update migration notes and CI coverage for the reviewed contract.
Validated with server/UI/Electron typechecks, 244 UI partition tests, 122 Electron native tests, 248 server tests plus 3 platform skips (the sole Windows cleanup race passed in isolation), UI/server/Electron builds, Tauri cargo check --locked, and git diff --check.
Treat DEV-v2 as an authorized development base in build, artifact-comment, and PR restriction workflows. GitHub evaluates pull_request policy from the base branch, so child PR changes cannot authorize themselves.
This allows PRs 648 and 649 to run validation without weakening restrictions for unrelated target branches. Workflow diffs and git diff checks pass.
Integrate the current native V2 runtime, inline form flow, pruned validation matrix, and shared desktop/runtime fixes without restoring the removed delta-buffer transport.
Keep PR 648's bounded transcript rendering and inactive-memory budget, then replace oversized transcript rejection with stale-safe 200-message window paging. Consolidate duplicate caches, account native and pending state, preserve paging cursors through scroll snapshots, and keep authoritative active content untruncated.
Reduce feature-specific tests to representative memory, eviction, rendering, lazy-copy, paging, authority, and cleanup invariants. UI typecheck and build pass; focused standard/browser suites, the server suite, Electron native suite, and diff checks pass.
Reduce the V2 test matrix to CodeNomad-owned behavior instead of repeating generated OpenCode request shapes, mock choreography, equivalent endpoint permutations, and source-level helper wiring.
Retain representative happy paths and the boundaries that protect workspace ownership, traversal and credentials, PID and WSL lifecycle proof, shutdown timeouts, permission isolation, restore handoff, deletion authority, streaming reconciliation, and persisted client state.
This removes 2,818 net test and CI lines. The full server suite passes with 249 tests and 3 platform skips; the PR UI matrices pass with 204 standard and 53 browser-conditioned tests, both server and UI typechecks pass, and git diff checks are clean.
Delete the obsolete pre-MVP roadmap and task references now that Git preserves that development history, while retaining current V2 operational and architecture guidance.
Remove behavior-neutral server and UI plumbing that has no remaining caller: unused workspace options and cached endpoint state, redundant auto-accept metadata, an unused PTY get adapter, and a one-use message-loading helper. Keep lifecycle proof, restore gates, timeline projection, and targeted invalidation because they still enforce runtime behavior.
Validated with server and UI typechecks, the full server suite (308 passed, 3 skipped), focused UI matrices, browser-conditioned UI tests, production server/UI build, diff checks, caller searches, and an independent regression review.
The Tauri client-state tests exercise process-global file locks and invoke PowerShell/CIM for process identity. Running them concurrently on the shared Windows runner can starve those probes, causing both cross-host election and bounded registration timing assertions to fail together despite passing independently.
Run the 74-test Windows Tauri suite with one test thread. Linux coverage remains parallel, and product-side participant reconciliation remains covered by repeated cross-host tests.
Validated with the exact serial Windows cargo command: 74 passed, 0 failed.
Preserve the last authoritative non-empty tab snapshot through native shutdown, make shutdown capture terminal, and restore the exact saved active tab. Seed scroll snapshots before session selection and wait for usable Virtua measurements so project, tab, session, and anchor position survive A/B restarts.
Render native runtime forms inline only when their active-session tool target is resolved, retain the floating fallback for unresolved requests, share form schema visibility and URL validation with provider auth, and remove the unused session form-list proxy route and obsolete V1 request wrapper.
Return the native V2 session.active map without a legacy data envelope, avoid full-history refreshes for deltas already applied locally, and bound timeline projection refreshes during streaming. Coverage includes UI restoration, form targeting, proxy contracts, native streaming, and CI test registration.
Validated with root typecheck, 529 UI tests, 305 passing server tests with 3 skips, 74 Tauri tests, 122 Electron native tests, production A/B builds, a no-seed native handoff, and runtime verification of the active-session proxy shape.
Keep follow ownership tied to explicit user intent so Virtua measurement corrections cannot silently escape an actively followed response. Large key jumps now land immediately, while explicit timeline and top navigation cancel submit pinning without confusing programmatic movement for user scrolling.
Rebuild timeline segments from cached V2 structural signatures instead of part-count and same-length replacement heuristics. This handles provisional-to-authoritative part replacement, type and renderability changes, terminal status updates, locale changes, duplicate part IDs, and the V2 time.completed contract without rebuilding streamed text on every token.
Add follow-state, timeline projection, and duplicate-part regression coverage to the PR workflow. Validated with 264 standard UI tests, 72 browser/integration tests, full UI and Electron typechecks, production UI build, and live Tauri WebView navigation/follow scenarios.
Apply OpenCode V2 text and reasoning delta events directly to the normalized message store so assistant responses appear as they are generated instead of waiting for periodic HTTP snapshots.
Deduplicate replayed events, preserve ordinal part ordering, reconcile stale or superseded snapshots, settle terminal messages, retry failed terminal refreshes, and clear streaming state when sessions or workspaces are removed. This keeps snapshot refreshes as authoritative recovery without erasing visible streamed content or recreating removed stores.
Add focused streaming regression coverage and extend native event tests for immediate text, reasoning, periodic refresh behavior, replay handling, deletion cleanup, and terminal reconciliation. Validated with 223 standard UI tests, 64 browser/integration tests, full workspace typechecks, a production Tauri build, and a live CDP smoke covering progressive rendering and reload restoration.
Keep established conversations mounted while V2 performs authoritative message refreshes. Background loads no longer insert the initial loader or clear timeline segments, preventing the measured prompt displacement and timeline flicker during native streaming.
Restore saved sessions directly before the expensive all-worktree session scan, while preserving every referenced draft, attachment, scroll, expansion, idle, and generation record. Make workspace readiness follow newer reconnect loads without leaking waits after cancellation, and exclude synthetic views from native session hydration.
Harden question interruption races by rebinding requests when authoritative tool parts arrive late or replace optimistic parts, and clear inline question state immediately after reply or rejection. Isolate PTY loading reads from Solid effects to prevent recursive reload storms.
Add focused regressions to the PR workflow. Validated with 223 standard UI tests, 58 browser-condition tests, full TypeScript checks, 307 server tests, 74 Rust tests, 28 Windows spawn tests, a production Tauri build, and real cold-start state restoration.
Merge upstream dev into the OpenCode V2 foundation while preserving native V2 session, provider, proxy, PTY, and explicit database behavior.
Resolve desktop workspace-open and provider visibility conflicts across Electron and Tauri, bound local API requests, refresh standalone dependency locks, and keep grouped integration preferences keyed by native provider IDs.
Validated with desktop typechecks, 267 UI tests, 308 server tests, 122 Electron tests, 74 Tauri tests, packaged builds, and an exact OpenCode 0.0.0-next-17353 Tauri smoke covering prompt, reload, agent fallback, and PTY lifecycle.
Restore reliable V2 catalogs, agent identifiers, cursor pagination, prompts, variants, attachments, forms, native event reconciliation, Yolo persistence, and location-aware PTY controls. Prevent completed control records from appearing as streaming placeholders and keep live output refreshing during sustained token streams.
Harden the shared service and workspace proxy with explicit OPENCODE_DB isolation, launch-signature validation, traversal protection, WSL namespace translation, ownership-scoped routes, bounded SSE backpressure, reconnect recovery, and safe process-proof transfer and shutdown semantics.
Align provider, VCS, LSP, update, documentation, and CI behavior with the experimental next-17353 contract. Validation includes server/UI/Electron typechecks, 252 workflow UI tests, focused server suites, production UI/server builds, 68 Tauri tests, and a real V2 workspace/session/prompt smoke.
Re-scope the renderer-memory fix on the native V2 baseline after removal of the duplicate Tauri event transport. Bound Markdown, reasoning, tool output, diagnostics, diffs, task steps, todos, and per-message part rendering while retaining complete authoritative content behind lazy copy actions.
Coordinate message records and derived render caches under one 64 MiB byte-aware LRU. Protect mounted, loading, and live transcripts; cap pending parts and prompt display overrides; fence stale cache writes; and conservatively account measurement failures.
Reload evicted transcripts through bounded complete V2 pagination, reject stale lifecycle completions, purge all per-instance session state, and prevent delayed SSE/delta work from recreating removed stores. Keep DEV-v2 PR validation enabled without reintroducing native Tauri transport changes.
Validated with full UI and Electron typecheck, UI production build, 248 standard UI tests, 71 browser-condition UI tests, 118 Electron native tests, and git diff checks. The long-duration WebKit soak remains pending while the PR stays draft.
## Summary
- add per-provider model visibility controls to Settings > Providers and
the floating provider dialog
- persist exact hidden model IDs as CodeNomad presentation preferences
- keep large plugin catalogs manageable with search and Show all / Hide
all actions
## Behavior
- all current and newly reported models remain visible by default
- unchecked models are hidden only from model picker lists
- active/default models remain usable even when hidden
- favorites remain persisted and return when their models are shown
again
- the same management UI is available from Settings and the floating
model-picker dialog
This intentionally mirrors OpenChamber/OpenCode Desktop client-side
hiding. It does not rewrite OpenCode configuration or change model
execution/default semantics.
## Validation
- npm run typecheck
- node --import tsx --test packages/ui/src/lib/model-visibility.test.ts
- npm run build --workspace @codenomad/ui
- Gatekeeper reviews: PASS
Closes#636
## Summary
- identify remote CodeNomad webviews explicitly as Tauri hosts
- grant remote-* windows the minimum native notification permissions
- keep dialogs, opener access, menus, and other desktop privileges
restricted to the main window
## Cause
Remote windows were marked with the remote window context but not with
the Tauri runtime host. The UI therefore selected the Web Notification
API inside WebView2, where requesting permission had no effect. Those
windows also did not match a Tauri capability that allowed notification
plugin commands.
## Implementation
The initialization script now sets both runtime host and window context
before remote UI scripts run. A dedicated capability permits only
permission checks, permission requests, and notification delivery for
configured HTTP/HTTPS remote origins.
## Validation
- cargo test --manifest-path packages/tauri-app/src-tauri/Cargo.toml: 85
passed
- npm run typecheck --workspace @codenomad/ui
- npm run build:ui
- git diff --check
- two independent review rounds: zero findings after least-privilege
cleanup
Fixes#640
Replace the V1 SDK, custom plugin, and per-workspace runtimes with the pinned OpenCode V2 client and one shared service. Model workspaces through native locations and route sessions, messages, events, files, VCS, permissions, questions, providers, commands, and MCP directly through V2 APIs.
Remove the legacy plugin and background-process layers, use native Shell and PTY support, preserve server-side Git mutations where V2 lacks parity, and update the UI, packaging, CI, architecture documentation, and translations for the new model.
Add focused coverage for shared-service ownership, Windows and WSL startup, proxy boundaries, worktree event routing, native event normalization, provider authentication, voice instructions, and location-scoped requests. Server and UI typechecks pass; remaining security review items and the real opencode2 smoke test are documented in MIGRATION_V2.md and will be completed before the draft PR is marked ready.
## Summary
Follow-up to #639. On mobile, validating the reconnect refresh from #639
surfaced a bandwidth regression: the active session's initial
message-load effect re-fires on **every** mutation of the reactive
sessions map, and one of those re-fires issues a redundant `force:false`
fetch that races the authoritative `force:true` reload for the same —
often very large — session.
## What happens (validated live on a real mobile device, 2 workspaces
open)
The active-session effect in `session-view.tsx` reads the whole session
object:
```ts
const session = () => props.activeSessions.get(props.sessionId)
```
Reading `session()` inside the `createEffect` subscribes it to the
entire sessions map. During a foreground/reconnect refresh, each of the
~10 concurrent `loadMessages()` completions calls `setSessions`,
re-firing the effect **20-40× per reconnect** (21 firings logged within
a single millisecond).
Most re-runs no-op via the `isLoading && !force` guard, but the firing
that lands in the open window (`isLoading:false` + freshly invalidated
flag) issues a real network fetch. On a ~6,578-message active session
that produced a duplicate full download racing the authoritative reload:
```
loadMessages HTTP fetch (6578 msgs) force:false (5565ms) ← from this effect
loadMessages HTTP fetch (6578 msgs) force:true (7802ms) ← authoritative reload
```
both in flight ~300ms apart, saturating the mobile link (sidebar fetches
degraded from 1-4s to 10-11s; bounded retries could exhaust against
`FOREGROUND_REFRESH_TIMEOUT_MS`).
## Fix
Derive the active session id through a value-diffed `createMemo` and
drive the effect from that memo instead of the full session object. The
memo only notifies downstream when the id string actually changes,
collapsing the storm to a single run per real session change while
preserving existing behavior (load on activation and on session switch).
This intentionally does **not** touch the authoritative `force:true`
reload contract guarded by `session-request-authority.test.ts` — a
generic in-flight dedupe was tried and correctly rejected by that test
(force:true must always re-fetch), so narrowing the effect's reactive
dependency is the right lever.
## Validation
- `tsc --noEmit` clean
- UI tests: 34/34 (group 1), 18/18 (authoritative group 2,
`--conditions=browser --test-force-exit`, incl.
`session-request-authority`)
- Production UI build succeeds
Root cause was captured live via a portable debug overlay (branch
`debug/mobile-overlay`). Context: NeuralNomadsAI/CodeNomad#639
(comment).
## Problem
On mobile, backgrounding CodeNomad while the AI is working suspends JS
execution. The SSE connection eventually times out (~45s), and any
events that arrived while suspended (a tool call finishing, a message
completing) are lost. Returning to the app could leave the UI
permanently stuck showing "running" even though the work had actually
finished on the server — the only way out was aborting the request.
## Fix
**1. `useForegroundRefresh` hook**
(`packages/ui/src/lib/hooks/use-foreground-refresh.ts`)
Subscribes to the SSE transport's `disconnected -> connected` transition
(not `visibilitychange`) and re-fetches session status + force-reloads
messages only when a reconnect follows a real disconnect. An earlier
attempt triggered on `visibilitychange` after a fixed delay, but that
force-reloaded even when the connection had stayed alive the whole time,
clearing in-flight "sending" state and making it look like the AI never
responded to a message the user had just sent.
Also fixes a latent bug in `server-events.ts`: `onPing` could fire for a
stale SSE generation after a reconnect, sending a pong tied to the wrong
connection. Guarded with the existing `connectGeneration` counter.
**2. Two `hydrateMessages` bugs** found while testing the hook against
real mobile background/foreground cycles
(`packages/ui/src/stores/message-v2/instance-store.ts`):
- **Unnecessary full re-render on every reload.** The force-reload path
always bumped every message's revision regardless of whether the server
returned identical content, invalidating render caches and re-rendering
the entire visible session on every reconnect. Several reconnects in a
row produced perceptible lag. Fixed by only bumping revision when a
message's parts or status actually changed — reload time dropped from
~280-320ms to ~10ms for unchanged content.
- **Duplicated message bubble on a race.** If the user sends a message
right as a reconnect happens, the optimistic "sending" bubble
(client-side temp id) isn't in the REST snapshot yet and was silently
dropped from the visible list — without being deleted from the store.
When the real SSE echo for it later arrived, the code could no longer
find it to swap cleanly, so it created a new record instead, and a
subsequent reload could leave both the orphaned bubble and the new one
visible at once. Fixed by preserving pending "sending" messages across a
reload until they're actually confirmed.
## Validation
- `npm run typecheck` (workspace `@codenomad/ui`): clean
- `node --test src/stores/message-v2/instance-store.test.ts`: 4/4 pass
(2 new tests covering the duplicate-message race)
- Full UI test suite run file-by-file: 57/66 pass; the 9 that don't fail
identically on a pristine `upstream/dev` checkout with none of this PR's
changes applied (pre-existing solid-toast SSR issue in the test
environment, unrelated to this change)
- `npm run build` (workspace `@codenomad/ui`): succeeds
- Manually verified over ~1.7 hours of real mobile background/foreground
cycles (14s glances up to two 40+ minute backgrounds) — refresh fires
only on genuine reconnects, completes in ~200-450ms, no stuck "running"
state and no duplicated messages on return
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- validate the authenticated OpenCode process configuration before
publishing a workspace as ready
- stop and remove instances that report invalid configuration
- show localized configuration diagnostics, affected paths, validation
issues, and typo suggestions in the existing launch-error dialog
## Existing coverage
PR #195 already handles binary spawn and early-exit failures. PR #582
surfaces session-list loading failures. This change closes the remaining
case where OpenCode reports healthy while configuration-dependent
endpoints fail.
## Validation
- `bun test ./packages/server/src/workspaces/manager.test.ts`
- `node --import tsx --test packages/ui/src/lib/launch-errors.test.ts`
- server and UI typechecks
- UI build
- manual reproduction with OpenCode 1.18.5
- independent Gatekeeper reviews: PASS
Closes#508
## Summary
- show the file or directory name first in each @ picker result
- show the immediate parent and complete root-aligned directory on
separate lines
- preserve horizontal inspection for long directories and reset it when
results change
- retain the complete path for assistive technology without changing
selection values
- run the focused path-splitting regression test in PR CI
## Context
PR #623 replaced end truncation with horizontal scrolling. This
follow-up keeps that behavior while moving the relevant filename and
parent to the start of each result, matching the CLI-style expectation
from the report.
## Validation
- workspace typecheck passes
- UI production build passes
- focused path test passes
- git diff check passes
- Gatekeeper round 3: zero findings
Closes#603
## Summary
- Preserve the original V2 source when permission.updated changes an
already-pending request, so approval continues through the V2 reply API
instead of the legacy endpoint.
- Keep standalone permission.updated events as valid legacy requests for
older supported OpenCode binaries.
- Continue suppressing delayed post-reply updates through the existing
replied-request tombstone, preventing an approved request from
reappearing.
## Reproduced failure
The regression test fails on dev before the fix: permission.v2.asked
followed by permission.updated changes the request source to legacy, so
approval calls the legacy endpoint and never reaches the V2 reply API.
## Compatibility coverage
- V2 request plus permission.updated replies through V2
- delayed permission.updated after a successful local reply does not
re-enter the queue
- standalone legacy permission.updated remains visible and replies
through legacy
## Scope
This PR intentionally does not add speculative timeout, reconnect, or
modal-rendering changes. It only fixes the reproduced source-routing bug
and preserves verified legacy behavior.
## Validation
- npm run typecheck
- npm run build --workspace @codenomad/ui
- 144 existing runnable UI tests passed
- browser integration tests passed, including all permission lifecycle
regressions
Closes#566
## Summary
- place CodeNomad selection actions below the final selected line on
touch-only devices
- keep desktop and hybrid fine-pointer positioning unchanged
- fall back to the message stream's top edge when bottom placement would
be clipped
## Validation
- 146 UI Node tests
- npm run typecheck
- npm run build --workspace @codenomad/ui
- git diff --check
Closes#598
## Summary
- do not mount SessionList while the temporary left drawer is floating
and closed
- keep the session-list error state mutually exclusive with virtualized
rows
- register focused visibility-policy tests in the PR workflow
## Root cause
SUID constructs temporary Drawer children while open is false. This
occurs both after restarting with a persisted closed panel and when
narrowing the window into mobile mode, which forces the left panel to
become unpinned and closed. SessionSidebar then mounted the virtua
Virtualizer in a detached staging document. virtua resolves
ResizeObserver through ownerDocument.defaultView, which is null for that
document.
The failure is timing-dependent: if session hydration publishes rows
while that closed mobile Drawer is detached, the synchronous render
exception escapes through setSessionPage and is caught by fetchSessions
as if the successful API request had failed. Opening the panel later
therefore reveals an empty list or the misleading Unable to load
sessions error. If hydration finishes under a different drawer
lifecycle, the bug does not appear.
Because the error UI and virtualized rows were both mounted, Retry
cleared the error and immediately hit the same poisoned lifecycle again.
## Behavior
Session fetching and startup restore continue while the panel is closed.
The virtualized DOM is created only after the panel is open or pinned.
Genuine list errors dispose the rows; Retry can then mount a clean
virtualizer after succeeding.
## Reproduction
1. Narrow the window until CodeNomad enters mobile mode and the left
panel can no longer remain pinned.
2. Leave the sessions panel closed while sessions hydrate, or restart in
that state.
3. Open the left panel.
4. Before this fix, the list may be empty or show Unable to load
sessions with a ResizeObserver null error.
## Validation
- 19 focused session visibility, tree, and pagination tests
- UI TypeScript typecheck
- production Vite build
- full Windows Tauri release build
- NSIS installer bundle
- regression test included in PR CI
## Summary
- Publish one supported installer on Linux: a Tauri .deb package for
x64.
- Publish one portable fallback: an Electron .tar.gz archive for x64.
- Remove the temporary Flatpak path and stop publishing Linux AppImage,
RPM, and zip variants.
- Remove obsolete Linux assets from reused releases before uploading
replacements.
- Closes#487, Closes#488
## Artifact validation
- Extract the Electron archive and verify its executable, bundled server
resources, app.asar renderer entry, referenced assets, and
shared-library closure.
- Verify Tauri Debian metadata, bundled resources, desktop entry, shared
libraries, and package installation in Ubuntu 24.04.
- Synchronize Tauri package versions before every platform build so
generated metadata follows release inputs.
## Validation
- Parsed the workflow YAML and Electron package JSON.
- Verified the local @electron/asar APIs used by CI.
- Ran the Tauri version synchronization script.
- Ran git diff --check.
- Reviewed the final diff against current dev; full Linux packaging and
Docker installation are delegated to PR CI.
## Compatibility
The Tauri deb is built and installation-tested on Ubuntu 24.04. Older
Debian-based distributions are not yet guaranteed.
---------
Co-authored-by: Pascal André <pascalandr@gmail.com>
## Summary
- wire Winget submission into the stable release pipeline instead of
relying on a separate `release.published` workflow
- keep the existing release asset resolution and Komac-backed submission
flow
- document the new trigger model and manual fallback path
## Validation
- `git diff --check origin/dev...HEAD`
- `node --check scripts/winget/resolve-release-asset.cjs`
- `node scripts/winget/resolve-release-asset.cjs --help`
- live release metadata and asset-resolution dry-run against stable
`v0.17.0`
## Notes
- this fixes the case where a release created by GitHub Actions with the
default `GITHUB_TOKEN` does not fan out into a second workflow run
- assumes the existing Winget repo secret/variables remain configured
- refs #462
## Summary
- add a release-published workflow that prepares and submits Winget
manifest updates automatically
- poll the GitHub Release API for the stable Windows Tauri asset and
compute its SHA-256 before submission
- document the maintainer secret and repository variables needed for the
Winget automation flow
## Validation
- `node --check "scripts/winget/resolve-release-asset.cjs"`
- `node "scripts/winget/resolve-release-asset.cjs" --help`
- dry-run resolver against the published `v0.16.0` release asset
## Notes
- skips draft and prerelease GitHub releases
- uses the maintainer fork submission flow for `microsoft/winget-pkgs`
- live PR submission still depends on configuring `WINGET_GITHUB_TOKEN`
- Fixes#462
## Problem
The `Comment PR Artifacts` workflow consistently times out before the
`PR Build Validation` run can complete. The build pipeline typically
takes 14–25 minutes (especially the Tauri macOS build), but the comment
workflow only polled for ~12 minutes (30 attempts × 10-second intervals
plus API overhead).
This has been causing the `comment` check to fail on every PR — see PR
#463 where it failed 3 consecutive times.
## Fix
- Increase polling attempts from **30 → 90**
- Increase sleep interval from **10s → 20s**
- Effective maximum wait: ~30 minutes of sleep + API overhead ≈ 45+
minutes total
This gives ample headroom for the full build matrix to complete,
including slower runners like `build-tauri-macos`.
## Why this needs to merge first
The `comment-pr-artifacts.yml` workflow uses `pull_request_target`,
which means it runs **from the base branch (dev)**, not the PR branch.
Changes to this file in PR #463 cannot take effect until this fix lands
on `dev`. Once merged, the comment workflow will stop timing out on PR
#463 and all future PRs.
---
_This PR was created by an AI agent (OpenHands) on behalf of the user to
unblock PR #463._
Co-authored-by: openhands <openhands@all-hands.dev>
## Summary
- revert the Bun standalone desktop packaging path and restore the
server's original `dist/bin.js` bootstrap flow
- add a managed Node runtime for Electron and Tauri that downloads only
the current platform/arch artifact into `~/.config/codenomad`
- update desktop startup and packaging scripts so packaged apps use the
managed runtime consistently, and clean up Electron's expected
navigation-abort log noise
## Testing
- npm run typecheck --workspace @neuralnomads/codenomad-electron-app
- cargo check
- npm run build --workspace @neuralnomads/codenomad
- npm run build:mac --workspace @neuralnomads/codenomad-electron-app
- launch
`packages/electron-app/release/mac-arm64/CodeNomad.app/Contents/MacOS/CodeNomad`
and verify the packaged server reaches ready with the managed Node
runtime
## Summary
- package `packages/server` as a standalone desktop executable so
Electron and Tauri no longer depend on a system-installed Node runtime
in production
- align Electron and Tauri startup logic around launching the packaged
server, resolving binaries from the user shell, and bundling the same
server resources into both desktop apps
- replace the workspace instance proxy path that used
`@fastify/reply-from` with a direct streaming proxy so packaged
standalone builds can talk to spawned `opencode` instances correctly
## Why
Desktop production builds were still depending on a user-provided Node
runtime to launch `packages/server`, which made packaging less
self-contained and created different behavior across machines. While
moving to a standalone server executable, we also found that
Bun-compiled standalone builds could start `opencode` successfully but
failed when proxying requests to those instances through `reply-from`.
The goal of this change is to make desktop production startup
self-contained, keep Electron and Tauri behavior aligned, and restore
correct communication with local `opencode` instances in packaged
builds.
## What Changed
- added a standalone build path for `packages/server` and bundle
`codenomad-server` into desktop resources
- updated Electron production startup to resolve and launch the
standalone server executable
- updated Tauri production startup to resolve and launch the standalone
server executable with matching cwd and shell behavior
- added runtime path helpers so the packaged server can reliably find
its bundled UI, auth templates, config template, and package metadata
- improved bare binary resolution so commands like `opencode` can be
resolved from the user's login shell environment
- upgraded the server stack to newer Fastify-compatible packages needed
for the standalone/runtime work
- replaced the workspace instance proxy implementation with a direct
streaming proxy for requests to spawned `opencode` instances
- updated Electron and Tauri build/prebuild scripts to generate and
package the standalone server, while also repairing missing
platform-specific optional binaries during packaging
## Benefits
- desktop production builds no longer require Node to be installed on
the user's system
- Electron and Tauri now use the same packaged server model in
production, reducing platform drift
- packaged desktop apps can successfully create workspaces, launch
`opencode`, and proxy health/session traffic to those instances
- the server bundle is more self-contained and resilient to different
launch environments
- desktop packaging is more predictable because the required server
executable is built and bundled as part of the app build flow
- Add build scripts for platform-specific builds with zip bundles
- Update CI workflow to use --bundles flag for explicit target selection
- macOS: use app,zip (removed dmg)
- Windows: use nsis,zip
- Linux: use appimage,deb,rpm
Restrict non-dev pull requests to an allowlisted set of actors and skip cross-platform PR builds unless that authorization check passes. Keep dev open for general contributions while guiding other PRs back to the dev branch.