| 124 | * - "docker": Docker container runtime |
| 125 | */ |
| 126 | export function createRuntime(config: RuntimeConfig, options?: CreateRuntimeOptions): Runtime { |
| 127 | // Check for incompatible configs from newer versions |
| 128 | if (isIncompatibleRuntimeConfig(config)) { |
| 129 | throw new IncompatibleRuntimeError( |
| 130 | `This workspace uses a runtime configuration from a newer version of mux. ` + |
| 131 | `Please upgrade mux to use this workspace.` |
| 132 | ); |
| 133 | } |
| 134 | |
| 135 | const runtimeIdentity = { |
| 136 | projectPath: options?.projectPath, |
| 137 | workspaceName: options?.workspaceName, |
| 138 | workspacePath: options?.workspacePath, |
| 139 | }; |
| 140 | |
| 141 | switch (config.type) { |
| 142 | case "local": |
| 143 | // Check if this is legacy "local" with srcBaseDir (= worktree semantics) |
| 144 | // or new "local" without srcBaseDir (= project-dir semantics) |
| 145 | if (hasSrcBaseDir(config)) { |
| 146 | // Legacy: "local" with srcBaseDir is treated as worktree |
| 147 | return new WorktreeRuntime(config.srcBaseDir, runtimeIdentity); |
| 148 | } |
| 149 | // Project-dir: uses project path directly, no isolation |
| 150 | if (!runtimeIdentity.projectPath) { |
| 151 | throw new Error( |
| 152 | "LocalRuntime requires projectPath in options for project-dir config (type: 'local' without srcBaseDir)" |
| 153 | ); |
| 154 | } |
| 155 | return new LocalRuntime(runtimeIdentity.projectPath); |
| 156 | |
| 157 | case "worktree": |
| 158 | return new WorktreeRuntime(config.srcBaseDir, runtimeIdentity); |
| 159 | |
| 160 | case "ssh": { |
| 161 | // Normalize Coder host before transport creation so both transport |
| 162 | // and runtime use the canonical *.mux--coder hostname from the start. |
| 163 | const sshHost = resolveCoderSSHHost(config.host, config.coder?.workspaceName); |
| 164 | const sshConfig = { |
| 165 | host: sshHost, |
| 166 | srcBaseDir: config.srcBaseDir, |
| 167 | bgOutputDir: config.bgOutputDir, |
| 168 | identityFile: config.identityFile, |
| 169 | port: config.port, |
| 170 | }; |
| 171 | |
| 172 | const useSSH2 = shouldUseSSH2Runtime(); |
| 173 | const transport = createSSHTransport(sshConfig, useSSH2); |
| 174 | |
| 175 | // Use a Coder SSH runtime for SSH+Coder when coderService is available (explicit or global) |
| 176 | const coderService = options?.coderService ?? globalCoderService; |
| 177 | |
| 178 | if (config.coder) { |
| 179 | if (!coderService) { |
| 180 | throw new Error("Coder runtime requested but CoderService is not initialized"); |
| 181 | } |
| 182 | return new CoderSSHRuntime( |
| 183 | { ...sshConfig, coder: config.coder }, |