diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index e4fff6d5b..1a4947e5a 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -149,11 +149,11 @@ export function registerStart(program: Command): void { if (opts?.dashboard !== false) { // Pre-flight: only check port/build when actually starting the dashboard await preflight.checkPort(port); - await preflight.checkBuilt(); const webDir = findWebDir(); if (!existsSync(resolve(webDir, "package.json"))) { throw new Error("Could not find @composio/ao-web package. Run: pnpm install"); } + await preflight.checkBuilt(); if (opts?.rebuild) { await cleanNextCache(webDir); diff --git a/packages/cli/src/lib/preflight.ts b/packages/cli/src/lib/preflight.ts index 8a808a4ba..faaa5e451 100644 --- a/packages/cli/src/lib/preflight.ts +++ b/packages/cli/src/lib/preflight.ts @@ -3,46 +3,43 @@ * * Validates runtime prerequisites before entering the main command flow, * giving clear errors instead of cryptic failures. + * + * All checks throw on failure so callers can catch and handle uniformly. */ import { existsSync } from "node:fs"; import { resolve } from "node:path"; -import chalk from "chalk"; import { isPortAvailable, findWebDir } from "./web-dir.js"; import { exec } from "./shell.js"; /** * Check that the dashboard port is free. - * Exits with a clear message if it's already in use. + * Throws if the port is already in use. */ async function checkPort(port: number): Promise { const free = await isPortAvailable(port); if (!free) { - console.error( - chalk.red( - `Port ${port} is already in use. Free it or change 'port' in agent-orchestrator.yaml.`, - ), + throw new Error( + `Port ${port} is already in use. Free it or change 'port' in agent-orchestrator.yaml.`, ); - process.exit(1); } } /** * Check that packages have been built (the web .next directory exists). - * Exits with a clear message if not. + * Throws if not built. */ async function checkBuilt(): Promise { const webDir = findWebDir(); const buildId = resolve(webDir, ".next", "BUILD_ID"); if (!existsSync(buildId)) { - console.error(chalk.red("Packages not built. Run: pnpm build")); - process.exit(1); + throw new Error("Packages not built. Run: pnpm build"); } } /** * Check that tmux is installed (required for the default runtime). - * Throws with a clear message if not. + * Throws if not installed. */ async function checkTmux(): Promise { try { @@ -53,11 +50,17 @@ async function checkTmux(): Promise { } /** - * Check that the GitHub CLI is authenticated. - * Only relevant when the project uses the github tracker plugin. - * Throws with a clear message if not authenticated. + * Check that the GitHub CLI is installed and authenticated. + * Distinguishes between "not installed" and "not authenticated" + * so the user gets the right troubleshooting guidance. */ async function checkGhAuth(): Promise { + try { + await exec("gh", ["--version"]); + } catch { + throw new Error("GitHub CLI (gh) is not installed. Install it: https://cli.github.com/"); + } + try { await exec("gh", ["auth", "status"]); } catch {