(pythonPath: string)
| 311 | } |
| 312 | |
| 313 | export async function findUvBinary(pythonPath: string): Promise<string | null> { |
| 314 | const buildImageUv = findUvOnBuildImage(); |
| 315 | if (buildImageUv) return buildImageUv; |
| 316 | |
| 317 | const found = which.sync('uv', { nothrow: true }); |
| 318 | if (found) return found; |
| 319 | |
| 320 | try { |
| 321 | const globalScriptsDir = await getGlobalScriptsDir(pythonPath); |
| 322 | if (globalScriptsDir) { |
| 323 | const uvPath = join(globalScriptsDir, uvExec); |
| 324 | if (fs.existsSync(uvPath)) return uvPath; |
| 325 | } |
| 326 | } catch (err) { |
| 327 | debug('Failed to resolve Python global scripts directory', err); |
| 328 | } |
| 329 | |
| 330 | try { |
| 331 | const userScriptsDir = await getUserScriptsDir(pythonPath); |
| 332 | if (userScriptsDir) { |
| 333 | const uvPath = join(userScriptsDir, uvExec); |
| 334 | if (fs.existsSync(uvPath)) return uvPath; |
| 335 | } |
| 336 | } catch (err) { |
| 337 | debug('Failed to resolve Python user scripts directory', err); |
| 338 | } |
| 339 | |
| 340 | try { |
| 341 | const candidates: string[] = []; |
| 342 | if (!isWin) { |
| 343 | candidates.push(join(os.homedir(), '.local', 'bin', 'uv')); |
| 344 | candidates.push('/usr/local/bin/uv'); |
| 345 | candidates.push('/opt/homebrew/bin/uv'); |
| 346 | } else { |
| 347 | candidates.push('C:\\Users\\Public\\uv\\uv.exe'); |
| 348 | } |
| 349 | for (const p of candidates) { |
| 350 | if (fs.existsSync(p)) return p; |
| 351 | } |
| 352 | } catch (err) { |
| 353 | debug('Failed to resolve uv fallback paths', err); |
| 354 | } |
| 355 | |
| 356 | return null; |
| 357 | } |
| 358 | |
| 359 | /** |
| 360 | * Verify the uv binary at `uvPath` is at least {@link MIN_UV_VERSION}. |
no test coverage detected