| 570 | } |
| 571 | |
| 572 | async function startStack() { |
| 573 | const entryUrl = pathToFileURL( |
| 574 | join(scriptsDir, "dev-with-automation.mjs"), |
| 575 | ).href; |
| 576 | const { main } = await import(entryUrl); |
| 577 | |
| 578 | // main() starts agent-server + automation backend + static server + ingress. |
| 579 | // skipNpmCheck: npm is not needed at runtime in static mode. |
| 580 | // agentServerReadyTimeoutMs: dev defaults to 60 s (warm uvx cache); a |
| 581 | // packaged binary on a fresh machine can spend several minutes inside |
| 582 | // uvx the first time, downloading Python + installing openhands- |
| 583 | // agent-server from PyPI. 10 minutes is generous but bounded. |
| 584 | // onServiceLog: stream uvx/agent-server output to the loading window so |
| 585 | // the user sees progress instead of an indefinite spinner. |
| 586 | const result = await main({ |
| 587 | bannerTitle: "Agent Canvas", |
| 588 | staticMode: true, |
| 589 | staticDir: buildDir, |
| 590 | mode: "agent-canvas", |
| 591 | isPublic: false, |
| 592 | skipNpmCheck: true, |
| 593 | agentServerReadyTimeoutMs: 10 * 60_000, |
| 594 | onServiceLog: handleServiceLog, |
| 595 | }); |
| 596 | |
| 597 | // main() returns { config, agentServerReady } — treat a timeout as a fatal |
| 598 | // startup error so the splash shows a clear dialog instead of dropping the |
| 599 | // user into a half-booted UI that will only emit "Request timeout" popups. |
| 600 | if (result?.agentServerReady === false) { |
| 601 | throw new Error( |
| 602 | "The agent server did not finish starting in time. " + |
| 603 | "On first launch this can take several minutes while uvx downloads " + |
| 604 | "Python and the OpenHands agent-server from PyPI. " + |
| 605 | "Check your internet connection and try again.", |
| 606 | ); |
| 607 | } |
| 608 | } |
| 609 | |
| 610 | // ── App lifecycle ───────────────────────────────────────────────────────────── |
| 611 | |