(runtime: string | null | undefined)
| 124 | * Use this for UI state management (localStorage, form inputs). |
| 125 | */ |
| 126 | export function parseRuntimeModeAndHost(runtime: string | null | undefined): ParsedRuntime | null { |
| 127 | if (!runtime) { |
| 128 | return { mode: RUNTIME_MODE.WORKTREE }; |
| 129 | } |
| 130 | |
| 131 | const trimmed = runtime.trim(); |
| 132 | const lowerTrimmed = trimmed.toLowerCase(); |
| 133 | |
| 134 | if (lowerTrimmed === RUNTIME_MODE.LOCAL) { |
| 135 | return { mode: RUNTIME_MODE.LOCAL }; |
| 136 | } |
| 137 | |
| 138 | if (lowerTrimmed === RUNTIME_MODE.WORKTREE) { |
| 139 | return { mode: RUNTIME_MODE.WORKTREE }; |
| 140 | } |
| 141 | |
| 142 | // Check for "ssh <host>" format |
| 143 | if (lowerTrimmed.startsWith(SSH_RUNTIME_PREFIX)) { |
| 144 | const host = trimmed.substring(SSH_RUNTIME_PREFIX.length).trim(); |
| 145 | if (!host) return null; // "ssh " without host is invalid |
| 146 | return { mode: RUNTIME_MODE.SSH, host }; |
| 147 | } |
| 148 | |
| 149 | // Plain "ssh" without host is invalid |
| 150 | if (lowerTrimmed === RUNTIME_MODE.SSH) { |
| 151 | return null; |
| 152 | } |
| 153 | |
| 154 | // Check for "docker <image>" format |
| 155 | if (lowerTrimmed.startsWith(DOCKER_RUNTIME_PREFIX)) { |
| 156 | const image = trimmed.substring(DOCKER_RUNTIME_PREFIX.length).trim(); |
| 157 | if (!image) return null; // "docker " without image is invalid |
| 158 | return { mode: RUNTIME_MODE.DOCKER, image }; |
| 159 | } |
| 160 | |
| 161 | // Plain "docker" without image is invalid |
| 162 | if (lowerTrimmed === RUNTIME_MODE.DOCKER) { |
| 163 | return null; |
| 164 | } |
| 165 | |
| 166 | // Check for "devcontainer <configPath>" format (config path is optional) |
| 167 | if (lowerTrimmed.startsWith(DEVCONTAINER_RUNTIME_PREFIX)) { |
| 168 | const configPath = trimmed.substring(DEVCONTAINER_RUNTIME_PREFIX.length).trim(); |
| 169 | return { mode: RUNTIME_MODE.DEVCONTAINER, configPath }; |
| 170 | } |
| 171 | |
| 172 | if (lowerTrimmed === RUNTIME_MODE.DEVCONTAINER) { |
| 173 | return { mode: RUNTIME_MODE.DEVCONTAINER, configPath: "" }; |
| 174 | } |
| 175 | |
| 176 | // Try to parse as a plain mode (local/worktree/devcontainer) |
| 177 | const modeResult = RuntimeModeSchema.safeParse(lowerTrimmed); |
| 178 | if (modeResult.success) { |
| 179 | const mode = modeResult.data; |
| 180 | if (mode === "local") return { mode: "local" }; |
| 181 | if (mode === "worktree") return { mode: "worktree" }; |
| 182 | if (mode === "devcontainer") return { mode: "devcontainer", configPath: "" }; |
| 183 | // ssh/docker without args handled above |
no outgoing calls
no test coverage detected