Improve missing agent-server setup guidance (#42)

Add actionable dev-safe output when agent-server is missing, including README and uv installation guidance, and update tests plus quickstart docs.

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Graham Neubig
2026-04-27 10:40:06 -04:00
committed by GitHub
co-authored by openhands
parent fbfd3015ab
commit 3b754b5703
3 changed files with 69 additions and 2 deletions
+2
View File
@@ -31,6 +31,8 @@ If you do not already have the backend installed, install `uv` first (OpenHands
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Need Windows or another install method? See the official uv installation guide: <https://docs.astral.sh/uv/getting-started/installation/>
Then install or upgrade the agent server package together with the tool/workspace dependencies it needs:
```sh
+34 -1
View File
@@ -5,13 +5,38 @@ import process from "node:process";
import { setTimeout as delay } from "node:timers/promises";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { buildSafeDevConfig } from "../../scripts/dev-safe.mjs";
import {
buildSafeDevConfig,
formatMissingAgentServerGuidance,
} from "../../scripts/dev-safe.mjs";
const repoRoot = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../..",
);
describe("formatMissingAgentServerGuidance", () => {
it("includes install, PATH, README, and fallback workflow hints", () => {
const guidance = formatMissingAgentServerGuidance(
"/workspace/project/agent-server-gui",
);
expect(guidance).toContain(
"uv tool install -U --with openhands-tools --with openhands-workspace openhands-agent-server",
);
expect(guidance).toContain('export PATH="$HOME/.local/bin:$PATH"');
expect(guidance).toContain(
"/workspace/project/agent-server-gui/README.md",
);
expect(guidance).toContain(
"https://docs.astral.sh/uv/getting-started/installation/",
);
expect(guidance).toContain("npm run dev:frontend");
expect(guidance).toContain("npm run dev:mock");
});
});
describe("buildSafeDevConfig", () => {
it("builds isolated default paths and ports", () => {
const cwd = "/workspace/project/agent-server-gui";
@@ -89,6 +114,14 @@ describe("dev-safe CLI startup", () => {
expect(exitResult.timedOut).toBe(false);
expect(exitResult.code).toBe(1);
expect(output).toContain("Failed to start agent-server");
expect(output).toContain(
"uv tool install -U --with openhands-tools --with openhands-workspace openhands-agent-server",
);
expect(output).toContain(
"https://docs.astral.sh/uv/getting-started/installation/",
);
expect(output).toContain("README.md");
expect(output).toContain("npm run dev:mock");
expect(output).toContain("spawn agent-server ENOENT");
});
});
+33 -1
View File
@@ -8,6 +8,36 @@ import { pathToFileURL } from "node:url";
const DEFAULT_BACKEND_PORT = 18000;
const DEFAULT_WAIT_TIMEOUT_MS = 30_000;
function isEnoentError(error) {
return Boolean(
(error && typeof error === "object" && "code" in error && error.code === "ENOENT") ||
/ENOENT/.test(String(error)),
);
}
export function formatMissingAgentServerGuidance(cwd = process.cwd()) {
const readmePath = path.join(cwd, "README.md");
return [
"Failed to start agent-server. Make sure it is installed and on your PATH.",
"",
"To fix this:",
"1. Install the backend CLI:",
" uv tool install -U --with openhands-tools --with openhands-workspace openhands-agent-server",
"2. Make sure the uv tool bin dir is on your PATH:",
' export PATH="$HOME/.local/bin:$PATH"',
" command -v agent-server",
"",
"Need Windows or another install method? https://docs.astral.sh/uv/getting-started/installation/",
`See the local Quickstart for details: ${readmePath}`,
"- README > Quickstart > 2. Install OpenHands Agent Server",
"",
"Other options:",
"- npm run dev:frontend # use an already running backend",
"- npm run dev:mock # run the frontend with mock APIs",
].join("\n");
}
function parsePort(value, fallback) {
if (value == null || value === "") {
return fallback;
@@ -66,7 +96,9 @@ function spawnProcess(command, args, options) {
const child = spawn(command, args, { stdio: "inherit", ...options });
child.once("error", (error) => {
if ((error && "code" in error && error.code === "ENOENT") || /ENOENT/.test(String(error))) {
if (isEnoentError(error) && command === "agent-server") {
console.error(formatMissingAgentServerGuidance(options?.cwd));
} else if (isEnoentError(error)) {
console.error(`Failed to start ${command}. Make sure it is installed and on your PATH.`);
} else {
console.error(`Failed to start ${command}:`, error);