-
Notifications
You must be signed in to change notification settings - Fork 42
🤖 feat: Coder workspace integration for SSH runtime #1617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
0370ec2 to
40b0be4
Compare
There was a problem hiding this 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.
|
@codex review |
There was a problem hiding this 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.
- 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.
- 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.
18f7442 to
a497d9e
Compare
|
@codex review |
There was a problem hiding this 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: a7eb311717
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this 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: c829cd0e20
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
There was a problem hiding this 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: 18731b53e4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
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.
- Add getWorkspaceStatus mock to runtime.test.ts for CoderSSHRuntime tests - Add splashScreens mock to storybook orpc mock client - Fix false retry barrier appearing after init hooks complete - Add templateDisplayName to Coder workspace picker UI
- Add missing getWorkspaceStatus and waitForStartupScripts mocks to CoderSSHRuntime test for postCreateSetup after fork test - Return viewed splash screen ID in storybook mock to prevent modal from blocking interactions with pointer-events: none
Root cause: vi.mock() in coderService.test.ts polluted the module cache globally, causing execAsync to return undefined for all other tests. This is a known Bun bug (oven-sh/bun#12823). Fixes: - Replace vi.mock with spyOn pattern in coderService.test.ts - Fix Storybook test selector (Radix Select doesn't use native options) - Use targeted mockRestore() instead of global mock.restore() - Also includes coder token deletion fix (remove --yes flag)
The preset dropdown is always visible but disabled when there are 0 presets, not hidden. Updated test to check for data-disabled attribute instead of asserting the element doesn't exist.
When multiple Coder templates share the same name across different organizations, the UI now preserves organization context to ensure the correct template/presets are used for workspace creation. Changes: - Add templateOrg to CoderWorkspaceConfig schema for disambiguation - Pass org parameter through preset listing and workspace creation APIs - Select value now matches option encoding when duplicates exist/disappear - Auto-select first preset when 2+ exist and none is default (keeps UI/config in sync) - Fix stale-response guard to include org in comparison --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$352.09`_
Prevents users from selecting workspaces that are being deleted or already deleted in the Coder existing workspace dropdown. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
3e28694 to
4334980
Compare
There was a problem hiding this 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: 3e286940ec
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
ensureReady() was using the raw abortSignal parameter (usually undefined) instead of combinedAbortSignal. This meant user cancellation via stopStream() during Coder workspace startup wouldn't abort the CLI—the workspace would keep starting even after the user cancelled. Now uses combinedAbortSignal, matching waitForInit() behavior. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high`_
|
@codex review |
There was a problem hiding this 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: b38b285f9e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
When Coder is enabled with no explicit SSH host, use 'coder://' as a placeholder so the runtime string round-trips correctly. Previously, empty host serialized as 'ssh ' which parseRuntimeModeAndHost rejected, silently falling back to worktree. The placeholder ensures defaultRuntimeMode restores as 'ssh', and the full Coder config is restored from lastRuntimeConfigs.ssh.coderEnabled.
|
@codex review |
There was a problem hiding this 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: 403406d641
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The stale-response guard prevented setPresets updates but the finally block always cleared loadingPresets. This caused a race where switching templates quickly could show stale presets while the new request was still in-flight. Now the finally block also checks if template/org still matches before clearing the loading flag.
|
@codex review |
There was a problem hiding this 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: 709651b5ad
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
@codex review |
Simplify the Coder workspace deletion guard to a single signal: - existingWorkspace=true → mux didn't create it → never delete it - Remove redundant ownership field from schema and all call sites - Extract applyForkRuntimeUpdates helper to share fork config persistence logic between WorkspaceService and TaskService - Ensure forked runtimeConfig is persisted for queued tasks at dequeue time - Add test for fork runtime config propagation during task dequeue --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$376.90`_
240a9fa to
2d45515
Compare
There was a problem hiding this 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: 240a9facfb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The rich-parameter parser was coercing non-string default_value to empty string, causing: - validateRequiredParams to reject required params with numeric 0 or boolean false defaults - computeExtraParams to emit --parameter name= for optional params Fix: Add serializeParameterDefault() helper that properly converts: - null/undefined → "" - strings → unchanged - numbers/booleans → String(value) - arrays/objects → JSON.stringify(value) Example: A template param with default_value: 0 (number) now becomes "0" instead of "", so validation passes and the CLI receives the correct --parameter name=0 flag. --- _Generated with `mux` • Model: `anthropic:claude-opus-4-5` • Thinking: `high` • Cost: `$382.69`_
|
@codex review Addressed P2 feedback: Preserve non-string Coder parameter defaults The issue:
Fix: Added
Now a template param with Added 5 tests covering numeric, boolean, and array defaults. |
|
Codex Review: Didn't find any major issues. Nice work! ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Summary
Adds first-class support for Coder workspaces as SSH runtimes in 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)codercommands (version check, templates, presets, workspaces)getWorkspaceStatus()(ok/not_found/error)--orgflag when templates with same name exist across orgsCoderSSHRuntime (
src/node/runtime/CoderSSHRuntime.ts)ensureReady()handles workspace startup with status polling and retry logicruntime-statusevents for UI progress feedback (checking → starting → ready)mux-{branch-name}with Coder-compatible normalizationFrontend: Coder Controls UI
CoderControls (
src/browser/components/ChatInput/CoderControls.tsx)useCoderWorkspace (
src/browser/hooks/useCoderWorkspace.ts)RuntimeBadge - Shows Coder icon for Coder-backed workspaces with tooltip
Runtime Readiness Events
New
runtime-statusstream event type shows progress during workspace startup:checking- Querying Coder workspace statusstarting- Starting stopped workspacewaiting- Waiting for workspace to be readyready- Workspace is runningerror- Startup failedStreamingBarrier displays status text to users during startup.
Retry Barrier Fix
Fixed false retry barrier appearing immediately after init hooks complete:
pendingStreamStartTimeoninit-endevent to restart the 15s grace periodruntimeStatusinhasInterruptedStream()- suppress barrier while runtime is startingSchemas & API
CoderWorkspaceConfigSchemawithtemplateOrgfor organization disambiguationcoder.getInfo,listTemplates,listPresets,listWorkspaceslistPresetsaccepts optionalorgparameter for disambiguationRuntimeStatusEventSchemafor streaming runtime startup progressKey Implementation Details
Non-interactive
coder createThe 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:
coder whoami --output jsoncoder templates list/api/v2/templateversions/{id}/rich-parameters)--parameter name=valueflagsOrganization Disambiguation
When multiple Coder organizations have templates with the same name:
org/nameonly when duplicates existtemplateOrgis persisted in config for preset lookup and workspace creation--orgflag tocoder createandcoder templates presets listDeletion Robustness
Deleting a mux workspace could hang if the Coder workspace was already deleted (SSH would try to connect to non-existent host). Now:
not_found,deleted, ordeletingAbort 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.Agent Discovery Race Condition
Fixed race where
agents.listwould call SSH before init hooks finished:waitForInit()calls inagents.listandagents.gethandlersTesting
Generated with
mux• Model:anthropic:claude-opus-4-5• Thinking:high