fix(openclaw): detect uv-installed headroom binary in ~/.local/bin (#1459)

## Description
When `headroom-ai` is installed via `uv tool install headroom-ai`, the
binary lands at `~/.local/bin/headroom`. The plugin's autoStart launcher
detection did not find it because the PATH check used `sh -lc` which may
not source user shell config (`.zshrc`, `.bash_profile`) on all systems.
This PR adds explicit uv path detection and fixes the shell invocation
flag.

Related to #419

## Type of Change
- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring

## Changes Made
- Switch PATH check from `sh -lc` to `sh -c` in `proxy-manager.ts`
- Add explicit uv tool install detection checking
`~/.local/bin/headroom`

## Testing
- [ ] Unit tests pass (`pytest`)
- [ ] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [x] Manual testing performed

### Test Output
```text
Status: loaded
Version: 0.27.0
Capabilities:
  context-engine: headroom
Tools:
  headroom_retrieve
```

## Real Behavior Proof
- Environment: EndeavourOS x86_64, OpenClaw 2026.6.10, headroom-ai
0.27.0 via uv
- Exact command / steps: `uv tool install headroom-ai` then `openclaw
gateway restart`
- Observed result: Before fix — autoStart failed with "Headroom proxy
not detected on default endpoints" even though binary exists at
`~/.local/bin/headroom`. After fix — plugin loads and connects
correctly.
- Not tested: Windows, Docker runtime

## Review Readiness
- [x] I have performed a self-review
- [x] This PR is ready for human review

## Additional Notes
Reproduced while debugging the npm package staleness issue in #419.
Affects any user following the standard `uv tool install` workflow on
Linux/macOS.
This commit is contained in:
Krishna 2026-07-01 00:48:06 +05:30 committed by GitHub
parent 75427bbd4a
commit adaeb88a4d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -11,6 +11,7 @@ import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import os from "os";
export interface ProxyManagerConfig {
proxyUrl?: string;
@ -258,12 +259,27 @@ export class ProxyManager {
checkCommand: process.platform === "win32" ? "where.exe" : "sh",
checkArgs: process.platform === "win32"
? ["headroom"]
: ["-lc", "command -v headroom >/dev/null 2>&1"],
: ["-c", "command -v headroom >/dev/null 2>&1"],
useShell: process.platform === "win32",
checkUseShell: false,
});
// 4) Local npm install (inside plugin install path)
// 4) uv tool install path (~/.local/bin/headroom)
const uvBin = join(
os.homedir(),
".local", "bin", "headroom"
);
if (existsSync(uvBin)) {
specs.push({
label: `uv tool: ${uvBin}`,
command: uvBin,
args: commonArgs,
checkCommand: uvBin,
checkArgs: ["--version"],
});
}
// 5) Local npm install (inside plugin install path)
const moduleDir = dirname(fileURLToPath(import.meta.url)); // .../dist
const packageRoot = dirname(moduleDir);
const localBinDir = join(packageRoot, "node_modules", ".bin");