diff --git a/AGENTS.md b/AGENTS.md index 9bc9753f5..46b9ff051 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -527,7 +527,9 @@ When adding code that needs a new string, decide up front which rule it falls un - `/api/automation/*` → automation backend (:18001) - `/api/*`, `/sockets`, etc. → agent server (:18000) - `/*` (default) → frontend server (:3001), either Vite or static depending on launcher mode - - Environment variables: `PORT` (ingress port, default: 8000), `OH_AUTOMATION_GIT_REF` (git ref, overrides default version), `OH_AUTOMATION_VERSION` (default: `1.0.0a3`), `AUTOMATION_LOCAL_API_KEY` (optional, use a fixed key; default: persisted generated key), `OH_AUTOMATION_API_KEY_PATH` (override the persisted default key path) + - Environment variables: `PORT` (ingress port, default: 8000), `OH_AUTOMATION_GIT_REF` (git ref, overrides default version), `OH_AUTOMATION_VERSION` (default: `1.0.0a3`), `AUTOMATION_DB_URL` (optional override for the automation SQLite DB), `AUTOMATION_LOCAL_API_KEY` (optional, use a fixed key; default: persisted generated key), `OH_AUTOMATION_API_KEY_PATH` (override the persisted default key path) + - For local cross-repo test harnesses, prefer overriding `AUTOMATION_DB_URL` rather than `OH_CANVAS_SAFE_STATE_DIR` when you need a clean automation scheduler state but want to keep the user's existing LLM profiles/secrets. A fresh state dir also changes `OH_PERSISTENCE_DIR`, which can make the stack look like LLM setup was lost. + - The launcher should wait for both the automation backend (`/api/automation/health`) and the frontend (`/`) to become reachable before starting ingress and printing the ready banner; otherwise first-load requests hit proxy errors (`/api/automation/health`, `/`, `/favicon.ico`) and the Automations page can incorrectly show Backend Not Configured on a healthy-but-still-booting stack. - `scripts/check-sdk-version-sync.mjs` checks the released `openhands-automation` package against `versions.automationSdk` in `config/defaults.json`; that value may intentionally lag `versions.agentServer` while automation has not yet published a matching release. - Access points: `http://localhost:8000/` (main UI), `http://localhost:8000/api/automation/docs` (API docs) - Security: `AUTOMATION_LOCAL_API_KEY` defaults to a generated key persisted across restarts because static frontend builds bake it into `VITE_AUTOMATION_API_KEY`. Set the env var explicitly to rotate or pin it. The cipher key (`OH_SECRET_KEY`) is persisted at `~/.openhands/agent-canvas/secret-key.txt` (same file used by `docker/entrypoint.sh`); both modes share the same key automatically when using the same `~/.openhands` directory. diff --git a/package-lock.json b/package-lock.json index 31e38ba26..f69c5651c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "@heroui/react": "2.8.10", "@microlink/react-json-view": "1.31.20", "@monaco-editor/react": "4.7.0", - "@openhands/extensions": "0.3.0", + "@openhands/extensions": "git+https://github.com/jamiechicago312/extensions.git#1566b45055779dd2ef33070e76c0976c4175ebfc", "@openhands/typescript-client": "1.24.3", "@react-router/node": "7.17.0", "@react-router/serve": "7.17.0", @@ -3469,8 +3469,8 @@ }, "node_modules/@openhands/extensions": { "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@openhands/extensions/-/extensions-0.3.0.tgz", - "integrity": "sha512-6xewbbmrDG6GbnI/Gga+o3+cw1jUpPkEW4wJKtL1mncL9f1qpI93YOBQMlrdQnu0JMRnLAMYSeURksqToUw2rw==", + "resolved": "git+https://github.com/jamiechicago312/extensions.git#1566b45055779dd2ef33070e76c0976c4175ebfc", + "integrity": "sha512-kNqzQc+DonfhI3ZqVWC6pw5FeYps9SY2ad7qUKRpT9826G2s/PxOQrIe/zG0ogoMzIVl7QIU3sUe/Y1rLyUmMA==", "license": "MIT", "engines": { "node": ">=18.20.0" diff --git a/package.json b/package.json index 197203563..e74a85d3a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@heroui/react": "2.8.10", "@microlink/react-json-view": "1.31.20", "@monaco-editor/react": "4.7.0", - "@openhands/extensions": "0.3.0", + "@openhands/extensions": "git+https://github.com/jamiechicago312/extensions.git#1566b45055779dd2ef33070e76c0976c4175ebfc", "@openhands/typescript-client": "1.24.3", "@react-router/node": "7.17.0", "@react-router/serve": "7.17.0", @@ -70,7 +70,9 @@ "zustand": "5.0.12" }, "scripts": { - "dev": "node --env-file-if-exists=.env scripts/dev-with-automation.mjs", + "dev": "node --env-file-if-exists=.env scripts/dev-github-windows-stack.mjs", + "dev:default": "node --env-file-if-exists=.env scripts/dev-with-automation.mjs", + "dev:test-github-windows-stack": "node --env-file-if-exists=.env scripts/dev-github-windows-stack.mjs", "dev:static": "node --env-file-if-exists=.env scripts/dev-static.mjs", "dev:extra-backend": "node --env-file-if-exists=.env scripts/dev-extra-backend.mjs", "dev:minimal": "node --env-file-if-exists=.env scripts/dev-safe.mjs", diff --git a/scripts/dev-github-windows-stack.mjs b/scripts/dev-github-windows-stack.mjs new file mode 100644 index 000000000..0913a2b30 --- /dev/null +++ b/scripts/dev-github-windows-stack.mjs @@ -0,0 +1,26 @@ +import { homedir } from "node:os"; +import { dirname, join } from "node:path"; + +import { main } from "./dev-with-automation.mjs"; + +const DEFAULT_STATE_DIR = join(homedir(), ".openhands", "agent-canvas"); +const stateDir = + process.env.OH_CANVAS_SAFE_STATE_DIR || + process.env.STATE_DIR || + DEFAULT_STATE_DIR; +const automationDbPath = join( + dirname(stateDir), + "automation", + "github-windows-stack.db", +); + +process.env.OH_AUTOMATION_REPO ??= + "https://github.com/jamiechicago312/automation.git"; +process.env.OH_AUTOMATION_GIT_REF ??= + "d20a68c1ef581370d1297da42a9b43a1de3c87b4"; +process.env.AUTOMATION_DB_URL ??= + `sqlite+aiosqlite:///${automationDbPath}`; + +await main({ + bannerTitle: "Agent Canvas + Automation Development Stack (GitHub Windows test)", +}); diff --git a/scripts/dev-with-automation.mjs b/scripts/dev-with-automation.mjs index 9779e8ec8..809627df7 100644 --- a/scripts/dev-with-automation.mjs +++ b/scripts/dev-with-automation.mjs @@ -29,6 +29,7 @@ * Environment variables: * - PORT: Ingress port (default: 8000) * - OH_AUTOMATION_GIT_REF: Git ref for automation (default: main) + * - AUTOMATION_DB_URL: Optional sqlite URL override for the automation DB * - OH_AGENT_SERVER_LOCAL_PATH: Absolute path to a local software-agent-sdk * checkout. Highest precedence for agent-server source selection: rebuilds * the agent-server from local source and installs openhands-sdk, @@ -250,6 +251,7 @@ ENVIRONMENT VARIABLES: PORT Alternative to --port OH_AUTOMATION_GIT_REF Git ref for automation (overrides default version) OH_AUTOMATION_VERSION Specific PyPI version for automation (default: ${DEFAULT_AUTOMATION_VERSION}) + AUTOMATION_DB_URL Override the automation SQLite DB path/URL OH_AGENT_SERVER_LOCAL_PATH Absolute path to a local software-agent-sdk checkout (highest precedence) OH_AGENT_SERVER_GIT_REF Git ref for agent-server SDK (overrides default version) OH_AGENT_SERVER_VERSION Specific PyPI version for agent-server @@ -819,8 +821,13 @@ function startAutomationBackend(config) { } : {}), AUTOMATION_AGENT_SERVER_API_KEY: config.sessionApiKey, - // ~/.openhands/automation/automations.db — matches docker/entrypoint.sh. - AUTOMATION_DB_URL: `sqlite+aiosqlite:///${join(dirname(config.stateDir), SHARED_DEFAULTS.paths.automationDb)}`, + // ~/.openhands/automation/automations.db by default — matches docker/entrypoint.sh. + // Allow callers to override it so local test harnesses can avoid stale + // scheduled automations while still reusing the user's existing LLM + // profiles and secrets. + AUTOMATION_DB_URL: + process.env.AUTOMATION_DB_URL || + `sqlite+aiosqlite:///${join(dirname(config.stateDir), SHARED_DEFAULTS.paths.automationDb)}`, // The automation backend uses this as its publicly-reachable base // URL: it's appended to callback URLs and injected into each // sandbox as `AUTOMATION_API_URL` (consumed by setup.sh for @@ -1319,8 +1326,35 @@ async function main(options = {}) { } } - // 5. Wait for services to be ready - await delay(2000); + // 5. Wait for launched services to become reachable before exposing ingress. + // Without this the browser can hit / or /api/automation/health while Vite or + // the automation backend is still booting, which surfaces misleading proxy + // errors and an empty automations screen on first load. + const readinessChecks = []; + if (config.launchAutomation) { + readinessChecks.push( + waitForService( + "automation", + `http://localhost:${config.autoBackendPort}${AUTOMATION_ROUTE_PREFIX}/health`, + 60000, + ), + ); + } + if (config.launchFrontend) { + const frontendUrl = getFrontendBackend(config); + if (frontendUrl) { + readinessChecks.push( + waitForService( + useStaticMode ? "static" : "vite", + frontendUrl, + 60000, + ), + ); + } + } + if (readinessChecks.length > 0) { + await Promise.all(readinessChecks); + } // 6. Start ingress proxy (routes traffic only to running services) startIngress(config);