Resolve the `openshell-driver-vm` binary path. Resolution order: 1. `{driver_dir}/openshell-driver-vm`, where `driver_dir` comes from `[openshell.drivers.vm].driver_dir`. 2. Conventional install directories: `~/.local/libexec/openshell`, `/usr/libexec/openshell`, `/usr/local/libexec/openshell`, `/usr/local/libexec`. 3. Sibling of the gateway's own executable (last-resort fallback so local develop
(vm_config: &VmComputeConfig)
| 187 | /// 3. Sibling of the gateway's own executable (last-resort fallback so |
| 188 | /// local development builds still work out of the box). |
| 189 | pub fn resolve_compute_driver_bin(vm_config: &VmComputeConfig) -> Result<PathBuf> { |
| 190 | let mut searched: Vec<PathBuf> = Vec::new(); |
| 191 | |
| 192 | // 1. Configured driver directory, or the conventional install locations |
| 193 | // when no explicit override is configured. |
| 194 | for dir in resolve_driver_search_dirs(vm_config) { |
| 195 | let candidate = dir.join(DRIVER_BIN_NAME); |
| 196 | if candidate.is_file() { |
| 197 | return Ok(candidate); |
| 198 | } |
| 199 | push_unique_path(&mut searched, candidate); |
| 200 | } |
| 201 | |
| 202 | // 2. Sibling-of-gateway fallback. |
| 203 | let current_exe = std::env::current_exe() |
| 204 | .map_err(|e| Error::config(format!("failed to resolve current executable: {e}")))?; |
| 205 | let Some(parent) = current_exe.parent() else { |
| 206 | return Err(Error::config(format!( |
| 207 | "current executable '{}' has no parent directory", |
| 208 | current_exe.display() |
| 209 | ))); |
| 210 | }; |
| 211 | let sibling = parent.join(DRIVER_BIN_NAME); |
| 212 | if sibling.is_file() { |
| 213 | return Ok(sibling); |
| 214 | } |
| 215 | push_unique_path(&mut searched, sibling); |
| 216 | |
| 217 | let searched_display = searched |
| 218 | .iter() |
| 219 | .map(|p| format!("'{}'", p.display())) |
| 220 | .collect::<Vec<_>>() |
| 221 | .join(", "); |
| 222 | Err(Error::config(format!( |
| 223 | "vm compute driver binary not found (searched {searched_display}); install it under [openshell.drivers.vm].driver_dir, a conventional libexec path such as ~/.local/libexec/openshell, /usr/libexec/openshell, or /usr/local/libexec{{,/openshell}}, or place it next to the gateway binary" |
| 224 | ))) |
| 225 | } |
| 226 | |
| 227 | fn resolve_driver_search_dirs(vm_config: &VmComputeConfig) -> Vec<PathBuf> { |
| 228 | vm_config.driver_dir.clone().map_or_else( |