Files
OpenHands/scripts/static-build.mjs
T
b9d78d3116 Simplify backend registry selection (#1046)
* Add partial stack modes to agent-canvas

Co-authored-by: openhands <openhands@all-hands.dev>

* Simplify backend registry selection

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix backend selection CI regressions

* Seed local proxy backend in cloud tests

* Stabilize onboarding snapshot navigation

* Stabilize ingress tests on Windows

* Remove local backend fallback for cloud calls

* Fix frontend-only backend proxy target

* Test backend-only launch without build

* Add pending workflow and status updates

* Use active LLM profile for setup banner

* Show backend connection errors before saving

* Validate backend keys in health checks

* Update backend selector health test mock

* Fix frontend-only workspace path

* Preserve OpenHands proxy base URL

* Address backend review comments

* Preserve profile config when switching models

* Fail fast on profile export errors

* Throw AgentServerUnavailableError when backend registry is empty

When no backend is configured (empty registry / NO_BACKEND sentinel),
loadAgentServerInfo() was returning null without throwing, causing
OptionService.getConfig() to succeed silently. root.tsx then rendered
the home page instead of the MissingAgentServerScreen with the manage
backends modal.

Now loadAgentServerInfo() checks for the NO_BACKEND sentinel when
getEffectiveLocalBackend() returns null and throws
AgentServerUnavailableError, which root.tsx already handles by showing
the manage backends modal. The cloud-backend path (also null from
getEffectiveLocalBackend) is preserved — it still returns null.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: Remove PR-only artifacts

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
2026-06-03 15:26:51 +00:00

83 lines
2.5 KiB
JavaScript

import { spawnSync } from "node:child_process";
import { existsSync } from "node:fs";
import { join } from "node:path";
import {
buildAutomationRuntimeServicesInfo,
c,
logError,
logService,
logStep,
logSuccess,
} from "./dev-with-automation.mjs";
import { buildNpmScriptCommand } from "./dev-safe.mjs";
export function buildFrontend(config, args = {}) {
const buildDir = join(config.canvasPath, "build");
if (args.skipBuild) {
if (!existsSync(buildDir)) {
logError(
"--skip-build was passed but build/ does not exist. Run without --skip-build first.",
);
process.exit(1);
}
logStep("build", "Skipping frontend build (--skip-build)");
logService("build", `Reusing existing build/ at ${buildDir}`, c.dim);
logService(
"build",
"Source edits will NOT appear until you run without --skip-build (or `npm run build`).",
c.yellow,
);
return;
}
logStep("build", "Building frontend (npm run build:app)...");
logService(
"build",
"This typically takes 30-60s; cached as build/ for --skip-build reuse",
c.dim,
);
const cmd = buildNpmScriptCommand("build:app");
const buildEnv = {
...process.env,
// Bake the session API key — used by the frontend for both agent-server
// and automation auth via the `X-Session-API-Key` header.
VITE_SESSION_API_KEY: config.sessionApiKey,
// Bake a description of the runtime services in this dev stack so the
// frontend can populate the agent's <RUNTIME_SERVICES> system-prompt
// block when creating a conversation.
VITE_RUNTIME_SERVICES_INFO: JSON.stringify(
buildAutomationRuntimeServicesInfo(config),
),
// Intentionally do NOT set VITE_BACKEND_BASE_URL: leaving it unset makes
// the runtime fall back to window.location.origin, which keeps the build
// portable across localhost, LAN hosts, and tunnels such as ngrok.
};
if (config.viteWorkingDir) {
buildEnv.VITE_WORKING_DIR = config.viteWorkingDir;
}
const result = spawnSync(cmd.command, cmd.args, {
cwd: config.canvasPath,
stdio: "inherit",
env: buildEnv,
});
if (result.status !== 0) {
logError(`Build failed with exit code ${result.status ?? "null"}`);
process.exit(result.status ?? 1);
}
if (!existsSync(join(buildDir, "index.html"))) {
logError(
`Build completed but ${join(buildDir, "index.html")} is missing. ` +
"Did react-router build write somewhere unexpected?",
);
process.exit(1);
}
logSuccess("Build complete");
}