Skip to content

Conversation

@ethanndickson
Copy link
Member

Summary

Adds support for Coder workspaces as a first-class runtime option within mux. Users can create new Coder workspaces or connect to existing ones directly from the workspace creation UI.

Features

Backend: CoderService + CoderSSHRuntime

CoderService (src/node/services/coderService.ts)

  • CLI wrapper for coder commands (version check, templates, presets, workspaces)
  • Workspace creation with pre-fetched template parameters to avoid interactive prompts
  • CSV-encoding for parameter values containing quotes/commas (matches CLI's StringArray parsing)
  • Short-lived API tokens for fetching rich parameters (auto-cleanup)
  • Graceful process termination with SIGKILL escalation on abort
  • Discriminated union return type for getWorkspaceStatus() (ok/not_found/error)

CoderSSHRuntime (src/node/runtime/CoderSSHRuntime.ts)

  • Extends SSHRuntime with Coder-specific provisioning lifecycle
  • ensureReady() handles workspace startup with status polling and retry logic
  • Emits runtime-status events for UI progress feedback (checking → starting → ready)
  • Workspace name derivation: mux-{branch-name} with Coder-compatible normalization
  • Collision detection with bounded retry loop
  • Deletion pre-checks status to avoid SSH hangs when workspace is already gone

Frontend: Coder Controls UI

CoderControls (src/browser/components/ChatInput/CoderControls.tsx)

  • Checkbox to enable Coder workspace mode
  • New/Existing toggle for creating vs connecting
  • Template dropdown (fetched from Coder deployment)
  • Preset dropdown (shown only when template has 2+ presets)
  • Existing workspace dropdown with status indicators

useCoderWorkspace (src/browser/hooks/useCoderWorkspace.ts)

  • Async data fetching for templates, presets, workspaces
  • Stale response guards to prevent race conditions
  • Proper cleanup on unmount

RuntimeBadge - Shows Coder icon for Coder-backed workspaces with tooltip

Schemas & API

  • CoderWorkspaceConfigSchema with optional workspaceName (backend derives for new)
  • ORPC endpoints: coder.getInfo, listTemplates, listPresets, listWorkspaces
  • RuntimeStatusEventSchema for streaming runtime startup progress
  • Known status enum derived from schema to prevent validation errors

Runtime Readiness Events

New runtime-status stream event type shows progress during workspace startup:

  • checking - Querying Coder workspace status
  • starting - Starting stopped workspace
  • waiting - Waiting for workspace to be ready
  • ready - Workspace is running
  • error - Startup failed

StreamingBarrier displays status text to users during startup.

Key Implementation Details

Non-interactive coder create

The original approach tried to detect interactive prompts and retry with defaults, but this failed because prompt display names don't match CLI parameter keys. The solution:

  1. Fetch deployment URL via coder whoami --output json
  2. Get active template version ID from coder templates list
  3. Query preset-covered parameters
  4. Fetch all rich parameters via API (/api/v2/templateversions/{id}/rich-parameters)
  5. Validate required params have values
  6. Pass defaults via --parameter name=value flags
  7. Stream build logs in real-time

Deletion Robustness

Deleting a mux workspace could hang if the Coder workspace was already deleted (SSH would try to connect to non-existent host). Now:

  • Check Coder workspace status before SSH cleanup
  • Skip SSH if not_found, deleted, or deleting
  • Proceed with SSH on API errors (let it fail naturally rather than assuming workspace is gone)

Abort Signal Handling

All async operations respect abort signals, including the status check in ensureReady() which now properly returns failure on abort rather than optimistically proceeding.

Testing

  • Unit tests for CoderService (CLI interactions, parameter encoding, validation)
  • Unit tests for CoderSSHRuntime (finalizeConfig, deleteWorkspace, ensureReady)
  • Storybook stories for Coder UI states (App.coder.stories.tsx)
  • Integration coverage in tests/runtime/runtime.test.ts

Commits

  • feat: Coder workspace integration for SSH runtime (initial implementation)
  • refactor: remove Coder-specific branching from WorkspaceService
  • fix: stabilize Coder controls layout during loading
  • fix: parse Coder templates/presets list JSON wrapper structure
  • feat: runtime readiness events + Coder sidebar icon
  • tests: cover Coder runtime readiness + runtime-status
  • fix: remove coderWorkspaceReady flag that bricked workspaces after restart
  • fix: replace mock.module with spyOn for test isolation
  • fix: add stale response guard to preset fetching
  • refactor: remove redundant running-only workspace filter
  • fix: non-interactive coder create + robust deletion
  • fix: respect abort signal in ensureReady status check

Generated with mux • Model: anthropic:claude-opus-4-5 • Thinking: high • Cost: $207.48

Add support for Coder workspaces as a sub-option of SSH runtime:

Backend:
- Add CoderService for CLI interactions (version check, templates, presets, workspaces)
- Add CoderSSHRuntime extending SSHRuntime with Coder-specific provisioning
- Workspace creation derives Coder name from mux branch, transforms to valid format
- Handle collision detection, parent dir creation, abort with SIGKILL escalation

Frontend:
- Add CoderControls component with New/Existing mode toggle
- Template and preset dropdowns (presets shown only when 2+)
- useCoderWorkspace hook manages async data fetching
- Allow disabling Coder when CLI becomes unavailable

Schema:
- CoderWorkspaceConfigSchema with optional workspaceName (backend derives for new)
- ORPC endpoints: coder.getInfo, listTemplates, listPresets, listWorkspaces
- Filter unknown statuses to prevent validation errors

Fixes applied:
- Bounded collision retry loop
- Derive KNOWN_STATUSES from schema
- Clear SIGKILL timeout on normal exit
- Pass coderProps when enabled OR available
Introduce runtime hooks to customize workspace creation flow:
- createFlags.deferredHost: skip srcBaseDir resolution for runtimes
  where the host does not exist yet (e.g., Coder)
- createFlags.configLevelCollisionDetection: use config-based collision
  check when runtime cannot reach host
- finalizeConfig(): derive names and compute host after collision handling
- validateBeforePersist(): external validation before persisting metadata

CoderSSHRuntime now owns the full name derivation and collision preflight
logic, keeping policy local to the runtime.

UI improvements:
- Tri-state Coder CLI detection: loading (spinner), unavailable, available
- Extract CoderCheckbox helper to reduce duplication

Add unit tests for CoderSSHRuntime:
- finalizeConfig: name derivation, normalization, validation, pass-through
- deleteWorkspace: existingWorkspace handling, force flag, error combining

---
_Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$92.78`_
- Add fixed h-7 height to template/preset rows so spinner does not
  cause vertical shift when dropdown appears
- Always show preset dropdown (disabled with "No presets" when empty)
  instead of hiding it, preventing layout jump
CLI returns [{Template: {...}}, ...] but we were expecting flat [{...}, ...].
Unwrap the Template field when parsing.
CLI returns [{TemplatePreset: {ID, Name, ...}}, ...] with PascalCase fields.
Unwrap and map to expected schema.
Add runtime-status events for ensureReady() progress:
- New stream event type for runtime startup progress (checking/starting/waiting/ready/error)
- CoderSSHRuntime emits status events during workspace start/wait
- DockerRuntime returns typed error results (runtime_not_ready vs runtime_start_failed)
- Frontend StreamingBarrier shows runtime-specific status text
- New runtime_start_failed error type for transient failures (retryable)

Add Coder sidebar icon:
- CoderIcon placeholder SVG in RuntimeIcons.tsx
- RuntimeBadge detects Coder workspaces (SSH + coder config) and shows CoderIcon
- Uses existing SSH blue styling, tooltip shows "Coder: {workspaceName}"

Other improvements:
- workspaceExists() search-based collision check (avoids listing all workspaces)
- Refactored coderService process management (graceful termination, runCoderCommand helper)
- Updated test fixtures for Coder CLI JSON wrapper structures

---
_Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$152.30`_
Add coverage for Coder-backed SSH runtime readiness and runtime-status event plumbing.

- CoderSSHRuntime: validateBeforePersist / postCreateSetup / ensureReady
- CoderService: getWorkspaceStatus / startWorkspaceAndWait
- Frontend: runtime-status routing + StreamingMessageAggregator lifecycle
- Retry policy: runtime_not_ready (non-retryable) vs runtime_start_failed (retryable)

Also drops brittle execAsync string snapshot tests for deleteWorkspace/ensureSSHConfig.

---
_Generated with  • Model: openai:gpt-5.2 • Thinking: high • Cost: 65.97_
…start

The in-memory flag was set during postCreateSetup() but never persisted.
After app restart, workspaces created in 'New' mode would fail ensureReady()
without ever checking the Coder server.

The flag was redundant - getWorkspaceStatus() already returns 'workspace not
found' for missing workspaces, which triggers the same runtime_not_ready error.

Existing throttling (lastActivityAtMs with 5-minute threshold) prevents
constant network calls - only one status check per session or after inactivity.
mock.module affects all tests globally in Bun's test runner, causing
failures in BackgroundProcessManager, hooks, and other tests that depend
on the real execBuffered function.

Using spyOn with mock.restore() in afterEach provides proper per-test
isolation without affecting other test files.
Prevents race condition where changing templates quickly could apply
presets from the previous template to the newly selected one.
Backend CoderService.listWorkspaces() already defaults to filterRunning=true.
Workspace creation:
- Pre-fetch template rich parameters via API before spawning coder create
- Pass defaults via --parameter flags to avoid interactive prompts
- CSV-encode parameter values containing quotes/commas for CLI parsing
- Validate required params have values (from preset or defaults)
- Stream build logs in real-time via async queue

Workspace status:
- Change getWorkspaceStatus() return to discriminated union (ok/not_found/error)
- Prevents treating transient errors as "workspace gone"

Workspace deletion:
- Check Coder workspace status before SSH cleanup
- Skip SSH when workspace is not_found/deleted/deleting (avoids hang)
- Proceed with SSH on API errors (let it fail naturally)

Other:
- Prefix new Coder workspace names with mux-
- Replace placeholder Coder icon with official shorthand logo

---
_Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$205.71`_
When getWorkspaceStatus returns an error due to abort, properly return
a failed EnsureReadyResult instead of optimistically proceeding.
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ecc7975e30

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Remove filterRunning parameter from listWorkspaces() - return all statuses
- Show status in dropdown options (e.g., 'my-workspace (template) • stopped')
- Change empty state text from 'No running workspaces' to 'No workspaces found'
- Make coder controls container w-fit instead of full width

ensureReady() already handles starting stopped workspaces, so users should
be able to see and select them in the existing workspace picker.

Addresses Codex review comment on PR #1617.
@ethanndickson
Copy link
Member Author

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06a3653ba6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Add runtime-status handler to bufferedEventHandlers in WorkspaceStore
  so Coder startup progress is displayed in StreamingBarrier
- Add safety check in coderService.deleteWorkspace() to refuse deleting
  workspaces without 'mux-' prefix (defense in depth)
- Add tests for delete safeguard

Addresses Codex review comment on PR #1617.
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