| 315 | } |
| 316 | |
| 317 | async function normalizeInstallRoot(rootPath: string): Promise<string> { |
| 318 | const trimmed = rootPath.trim(); |
| 319 | if (!path.isAbsolute(trimmed)) { |
| 320 | throw new Error(`Plugin root must be an absolute path (got "${rootPath}")`); |
| 321 | } |
| 322 | let resolved: string; |
| 323 | try { |
| 324 | resolved = await realpath(trimmed); |
| 325 | } catch (error) { |
| 326 | throw new Error(`Plugin root does not exist: ${trimmed}`, { cause: error }); |
| 327 | } |
| 328 | if (!(await stat(resolved)).isDirectory()) { |
| 329 | throw new Error(`Plugin root is not a directory: ${trimmed}`); |
| 330 | } |
| 331 | return resolved; |
| 332 | } |
| 333 | |
| 334 | async function copyPluginToManagedRoot( |
| 335 | kimiHomeDir: string, |