Skip to content

feat(runtime): add execution profiles#408

Draft
pascalandr wants to merge 19 commits into
NeuralNomadsAI:devfrom
Pagecran:feat/execution-connection-profiles
Draft

feat(runtime): add execution profiles#408
pascalandr wants to merge 19 commits into
NeuralNomadsAI:devfrom
Pagecran:feat/execution-connection-profiles

Conversation

@pascalandr
Copy link
Copy Markdown
Contributor

@pascalandr pascalandr commented May 8, 2026

Summary

  • Add server-scoped execution profiles for local, wsl, docker, command, and ssh runtimes so new workspaces can choose how the OpenCode runtime is launched.
  • Add Execution Profile management in Settings, a Home screen selector for new workspaces started by this CodeNomad server, active runtime metadata in Instance Information, and duplicate actions for saved execution profiles.
  • Add Preview command and Test profile flows so users can inspect launch commands and validate local/WSL/Docker/custom command/SSH profiles before using them.
  • Harden runtime handling with WSL distro-aware launches/previews/tests, Docker-aware URL/env rewriting and localhost port publishing, SSH remote-shell port forwarding plus OpenCode config sync, sensitive environment/argument redaction in logs and previews, safer profile normalization, and cleanup when a spawned runtime fails readiness.

Scope

  • This PR is scoped to Execution Profiles only: local binary / WSL / Docker / custom command / SSH remote shell launch modes for workspace runtimes started by the current CodeNomad server.
  • SSH execution profiles copy the packaged OpenCode config template to the remote host, run opencode serve on the configured remote host through ssh, forward the remote OpenCode runtime back to the current CodeNomad server, and reverse-forward callback traffic back to the current server.
  • Connecting to another CodeNomad server remains covered by the existing Connect CodeNomad Server flow; network transport such as VPN, SSH tunnel, Tailscale, or reverse proxy stays external to CodeNomad.

Validation

  • npm run typecheck --workspace @neuralnomads/codenomad
  • npm run typecheck --workspace @codenomad/ui
  • npm run build --workspace @codenomad/ui
  • node --import tsx --test "packages/server/src/settings/binaries.test.ts" "packages/server/src/workspaces/execution-launch.test.ts" "packages/server/src/workspaces/__tests__/spawn.test.ts" "packages/server/src/workspaces/__tests__/git-clone.test.ts"

Notes

pascalandr added 10 commits May 8, 2026 19:18
Introduce server-scoped execution profiles for local, WSL, Docker, and command runtimes so workspace creation can choose a runtime strategy instead of relying on a single default binary path.

Add device-scoped connection profile foundations for remote server shortcuts and SSH bootstrap metadata, then surface both profile systems in the home screen and settings flows so runtime selection and remote reconnect UX can evolve together.

Implement initial Docker and custom-command launch builders plus SSH bootstrap and tunnel session management for remote connections. Validation ran with the server and UI typechecks, the UI production build, and targeted tests for execution profile resolution and launch command generation.
Add a first-class SSH connection flow to the home screen so users can save an SSH bootstrap target, reconnect from recent connections, and open the remote server window without dropping into settings first.

Expose the active execution profile in instance information and add the required localized strings so the selected runtime and SSH connection metadata remain visible after launch. Validation: npm run typecheck --workspace @codenomad/ui; npm run typecheck --workspace @neuralnomads/codenomad; npm run build --workspace @codenomad/ui.
Add a first-class 'Connect SSH Host' flow to the home screen so users can save SSH bootstrap targets, reconnect them from the shared connections list, and open the remote window without detouring through settings.

Rename the home-screen remote tab from 'Servers' to 'Connections' so it accurately covers both remote server URLs and SSH-based entries, and expose the active execution profile inside instance information for easier runtime verification. Validation: npm run typecheck --workspace @codenomad/ui; npm run typecheck --workspace @neuralnomads/codenomad; npm run build --workspace @codenomad/ui.
Add a Preview command flow to Execution Profiles so operators can inspect the exact launcher command, working directory, and injected environment before targeting custom runtimes. The settings UI now requests a server-generated preview and renders the resolved command line alongside an optional sample workspace path for Docker mounts and cwd resolution.

Build previews from the same launch and spawn pipeline used at runtime so WSL profiles show the real wsl.exe wrapper, WSLENV propagation, and quoting behavior instead of a simplified placeholder command. Reuse current server config for log level, environment variables, auth endpoints, and config paths while redacting secrets in the response.

Validation: npm run typecheck --workspace @neuralnomads/codenomad; npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui; node --import tsx --test packages/server/src/workspaces/execution-launch.test.ts
Add a lightweight Test profile action beside the execution profile preview so operators can verify that a runtime is actually reachable from the CodeNomad host before using it for new workspaces. The settings screen now reports a pass/fail result and reuses the resolved preview payload for context.

Use a server-side test endpoint that stays intentionally minimal: local and WSL profiles validate the target binary, custom command profiles validate the wrapper executable, and Docker profiles validate both the Docker CLI and the configured local image. This keeps the first test pass fast and low-risk without spawning an OpenCode server.

Validation: npm run typecheck --workspace @neuralnomads/codenomad; npm run typecheck --workspace @codenomad/ui; npm run build --workspace @codenomad/ui
Add copy actions for execution and connection profiles so users can create variants without rebuilding existing SSH or runtime settings from scratch.

The duplicated records receive fresh ids and connection copies clear last-connected metadata while preserving the original launch or SSH fields. Locale keys are added for the new actions and shared copy suffix.

Validated earlier with the targeted UI typecheck and build for the duplicate profile changes.
Show the last successful connection time in the Home connection list and in Settings so saved SSH and remote server profiles communicate which targets were used recently.

The display reuses persisted lastConnectedAt metadata, keeps invalid timestamps hidden in Settings, and adds locale keys for both Home and Settings surfaces without changing connection behavior.

Validated with npm run typecheck --workspace @codenomad/ui and npm run build --workspace @codenomad/ui.
Internationalize the saved connection type badges on the Home screen and show SSH targets with their configured port so similar hosts are easier to distinguish before connecting.

The change keeps the existing connection flow intact, centralizes the display target formatting in the Home view, and adds locale keys for every registered UI language.

Validated with npm run typecheck --workspace @codenomad/ui and npm run build --workspace @codenomad/ui.
Format saved SSH connection summaries without a stray space before the port so Settings matches the Home connection list and hosts with custom ports are easier to scan.

The change is display-only and keeps remote path rendering unchanged.

Validated with npm run typecheck --workspace @codenomad/ui.
Address review risks around execution and connection profiles before taking the draft PR out of validation. Docker runtime secrets now flow through process environment instead of command-line KEY=value args, runtime/request logs redact sensitive profile payloads, and WSL profiles carry their distro into spawn/test/preview resolution so Linux paths launch through wsl.exe.

SSH connection handling now defaults to the HTTP listener port, uses bounded non-interactive SSH options, validates option-like host/user inputs, and avoids saving duplicate profiles when Save & Connect fails. Config normalization now preserves valid execution profiles when a malformed entry is present.

UI polish localizes desktop-only errors and execution profile kinds, updates SSH remote port defaults, and keeps remote window errors on the i18n path.

Validated with server/UI typechecks, targeted execution launch/spawn/binary resolver tests, and the UI build.
@shantur
Copy link
Copy Markdown
Collaborator

shantur commented May 8, 2026

@pascalandr - We need to discuss this as it won't work as it is. It depends on the opencode-config folder that resides on the server.

pascalandr added 3 commits May 8, 2026 22:33
Clarify in the SSH connection dialog and settings form that bootstrap scripts must start a remote CodeNomad server installation with its own server-side opencode-config available.

This avoids implying that the SSH flow copies the local plugin config to the remote host; it only opens a tunnel to a remote CodeNomad server.

Validated with npm run typecheck --workspace @codenomad/ui.
Resolve PR NeuralNomadsAI#408 conflicts after upstream dev added clone-repository workspace support. The only manual conflict was in the Home view imports, where both the connection-profile additions and the new clone-workspace GitBranch icon are preserved.

Validated with server and UI typechecks, targeted server tests including git clone coverage, and the UI build.
Refocus PR NeuralNomadsAI#408 on choosing how workspaces launch the OpenCode runtime: local, WSL, Docker, and custom command profiles. This removes the managed SSH tunnel/bootstrap-to-remote-CodeNomad-server path because remote server transport is already handled outside CodeNomad and by the existing Connect CodeNomad Server flow.

The cleanup removes connection-profile abstractions, SSH tunnel API routes/session management, and SSH bootstrap UI/copy while preserving saved remote server behavior. It also redacts sensitive preview args and keeps readiness-failure runtime cleanup from the earlier hardening work.

Validated with server and UI typechecks, targeted execution/spawn/clone tests, and the UI production build.
@pascalandr pascalandr changed the title feat(runtime): add execution and connection profile foundations feat(runtime): add execution profiles May 8, 2026
pascalandr added 4 commits May 9, 2026 01:33
Adds SSH as a first-class execution profile so the current CodeNomad server can launch OpenCode on a remote host without introducing a second remote CodeNomad server lifecycle.

The launcher reserves local runtime and callback ports, starts OpenCode through ssh with forward and reverse tunnels, and rewrites callback URLs for the remote shell environment. Settings schemas, preview/test routes, UI form fields, i18n messages, and instance metadata now understand the ssh profile kind.

Validation covers resolver behavior and launch command generation for SSH alongside existing WSL, Docker, command, spawn, and clone workspace tests. Verified with server and UI typechecks plus the UI production build.
Keeps Docker and SSH execution profiles reachable from the current CodeNomad server by reserving host ports before launch and using fixed OpenCode runtime ports for forwarded transports.

Docker profiles now publish the reserved runtime port on localhost instead of relying on an unexposed container port. SSH profiles send the remote shell script over stdin, validate environment variable names before interpolation, and avoid placing callback secrets in the local ssh argv.

The UI preferences normalizer now preserves saved SSH execution profiles so settings round-trips do not drop them. Coverage adds Docker reserved-port assertions and an unsafe SSH environment-name regression test.
Docker execution profiles publish a reserved localhost port on the host, so OpenCode must bind to all container interfaces rather than the default loopback address inside the container.

Adds --hostname 0.0.0.0 only for Docker launches while leaving local, WSL, command, and SSH behavior unchanged. The launch test asserts the fixed port, publish mapping, and container bind host stay aligned.
SSH execution profiles run OpenCode on a different filesystem, so the server-local OpenCode config template cannot be referenced directly by OPENCODE_CONFIG_DIR.

Before launching the remote runtime, CodeNomad now archives the packaged OpenCode config template and streams it over ssh into a per-workspace directory under /tmp on the remote host. The SSH launch environment then points OPENCODE_CONFIG_DIR at that remote directory and best-effort cleanup removes it when the runtime exits or startup fails.

This keeps SSH as an execution profile for the current CodeNomad server while addressing the config locality issue raised in review. Verified with server/UI typechecks, focused runtime tests, and the UI production build.
@pascalandr
Copy link
Copy Markdown
Contributor Author

@pascalandr - We need to discuss this as it won't work as it is. It depends on the opencode-config folder that resides on the server.

Resolved in latest push. The SSH execution profile no longer points the remote OpenCode process at the server-local config path. Before launching the SSH runtime, CodeNomad now archives the packaged OpenCode config template and streams it over ssh into a per-workspace directory on the remote host under /tmp, then sets OPENCODE_CONFIG_DIR to that remote path for the launched opencode serve process. Cleanup is best-effort on exit/startup failure. Docker/WSL/local continue using their existing local/mapped config paths.

pascalandr added 2 commits May 9, 2026 10:14
Use scp to copy the packaged OpenCode config directory to SSH execution hosts before launch instead of streaming a tar archive. This keeps SSH profiles independent from a local-only OPENCODE_CONFIG_DIR while avoiding a tar dependency on either side of the connection.

The remote temp directory is still cleaned before copy and after runtime shutdown, and the SSH target validation is shared between ssh and scp command construction. Validated with the server typecheck and focused workspace/settings tests.
@pascalandr
Copy link
Copy Markdown
Contributor Author

Updated the SSH config sync to avoid the tar dependency. SSH profiles now clean the remote temp config directory with ssh, copy the packaged OpenCode config with scp -r, and keep using that remote path for OPENCODE_CONFIG_DIR.

Validated after rebasing on the latest branch:

  • npm run typecheck --workspace @neuralnomads/codenomad
  • node --import tsx --test "packages/server/src/settings/binaries.test.ts" "packages/server/src/workspaces/execution-launch.test.ts" "packages/server/src/workspaces/tests/spawn.test.ts" "packages/server/src/workspaces/tests/git-clone.test.ts"

@shantur
Copy link
Copy Markdown
Collaborator

shantur commented May 10, 2026

I was thinking about how to make opencode_config implementation better. Essentially it is an opencode plugin that is used for codenomad.
I will take it up as part of #419 but the idea is

  1. Opencode_config will be bundled as a npm package in CodeNomad
  2. When starting opencode, CodeNomad will inject OPENCODE_CONFIG_CONTENT env var to add CodeNomad plugin npm package. It can be via file:// or http:// depending on the requirement. If OPENCODE_CONFIG_CONTENT is present, it will be easy to inject another plugin.
  3. We free up the OPENCODE_CONFIG_DIR for users and plugin can be provided to other execution profiles too

@shantur
Copy link
Copy Markdown
Collaborator

shantur commented May 11, 2026

opencode-config features is being worked in #433

@pascalandr
Copy link
Copy Markdown
Contributor Author

pascalandr commented May 11, 2026

@shantur
Thanks, with #433, #408 can stop copying opencode-config and only make the packaged plugin .tgz reachable from the target runtime: local/command use it directly,
WSL keeps the path rewrite, Docker mounts it into the container, and SSH copies it to /tmp then points OPENCODE_CONFIG_CONTENT at that remote file.

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.

2 participants