fix(setup): improve setup.sh with prereq validation and add ao start/spawn pre-flight checks

- Rewrite setup.sh with hard stops for Node<20 and git<2.25, soft warns
  with interactive fix for tmux/gh-auth/claude, corepack-based pnpm
  install, and PATH verification after npm link
- Add pre-flight checks to `ao start`: verify dashboard port is free and
  packages are built before proceeding
- Add pre-flight checks to `ao spawn`: verify tmux is installed and gh
  is authenticated (when using github tracker) before session creation
- Extract shared preflight utilities into packages/cli/src/lib/preflight.ts

Closes #245

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
suraj-markup 2026-03-01 19:39:29 +05:30
parent 4cda43795b
commit e40275b53c
4 changed files with 214 additions and 15 deletions

View File

@ -5,6 +5,7 @@ import { loadConfig, type OrchestratorConfig } from "@composio/ao-core";
import { exec } from "../lib/shell.js";
import { banner } from "../lib/format.js";
import { getSessionManager } from "../lib/create-session-manager.js";
import { preflight } from "../lib/preflight.js";
async function spawnSession(
config: OrchestratorConfig,
@ -13,6 +14,15 @@ async function spawnSession(
openTab?: boolean,
agent?: string,
): Promise<string> {
// Pre-flight: ensure tmux is available (default runtime)
await preflight.checkTmux();
// Pre-flight: ensure gh is authenticated if using github tracker
const project = config.projects[projectId];
if (project?.tracker?.plugin === "github") {
await preflight.checkGhAuth();
}
const spinner = ora("Creating session").start();
try {

View File

@ -22,6 +22,7 @@ import { exec } from "../lib/shell.js";
import { getSessionManager } from "../lib/create-session-manager.js";
import { findWebDir, buildDashboardEnv } from "../lib/web-dir.js";
import { cleanNextCache } from "../lib/dashboard-rebuild.js";
import { preflight } from "../lib/preflight.js";
/**
* Resolve project from config.
@ -138,6 +139,10 @@ export function registerStart(program: Command): void {
const sessionId = `${project.sessionPrefix}-orchestrator`;
const port = config.port ?? 3000;
// Pre-flight checks
await preflight.checkPort(port);
await preflight.checkBuilt();
console.log(chalk.bold(`\nStarting orchestrator for ${chalk.cyan(project.name)}\n`));
// Start dashboard (unless --no-dashboard)

View File

@ -0,0 +1,73 @@
/**
* Pre-flight checks for `ao start` and `ao spawn`.
*
* Validates runtime prerequisites before entering the main command flow,
* giving clear errors instead of cryptic failures.
*/
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.
*/
async function checkPort(port: number): Promise<void> {
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.`,
),
);
process.exit(1);
}
}
/**
* Check that packages have been built (the web .next directory exists).
* Exits with a clear message if not.
*/
async function checkBuilt(): Promise<void> {
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);
}
}
/**
* Check that tmux is installed (required for the default runtime).
* Throws with a clear message if not.
*/
async function checkTmux(): Promise<void> {
try {
await exec("tmux", ["-V"]);
} catch {
throw new Error("tmux is not installed. Install it: brew install tmux");
}
}
/**
* 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.
*/
async function checkGhAuth(): Promise<void> {
try {
await exec("gh", ["auth", "status"]);
} catch {
throw new Error("GitHub CLI is not authenticated. Run: gh auth login");
}
}
export const preflight = {
checkPort,
checkBuilt,
checkTmux,
checkGhAuth,
};

View File

@ -1,38 +1,149 @@
#!/bin/bash
# Agent Orchestrator setup script
# Installs dependencies, builds packages, and links the CLI globally
# Validates prerequisites, installs dependencies, builds packages, and links the CLI globally
set -e # Exit on error
echo "🤖 Agent Orchestrator Setup"
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
echo "Agent Orchestrator Setup"
echo ""
# Check for pnpm
if ! command -v pnpm &> /dev/null; then
echo "❌ pnpm not found. Installing pnpm..."
npm install -g pnpm
# ─── Hard requirements (exit 1 if missing) ────────────────────────────────────
# Node.js >= 20
if ! command -v node &> /dev/null; then
echo "ERROR: Node.js is not installed."
echo " Install Node.js 20+: https://nodejs.org/en/download"
exit 1
fi
echo "📦 Installing dependencies..."
NODE_MAJOR=$(node -e "process.stdout.write(String(process.versions.node.split('.')[0]))")
if [ "$NODE_MAJOR" -lt 20 ]; then
echo "ERROR: Node.js $NODE_MAJOR.x detected, but 20+ is required."
echo " Install Node.js 20+: https://nodejs.org/en/download"
exit 1
fi
echo "[ok] Node.js $(node --version)"
# git >= 2.25 (required for worktree support)
if ! command -v git &> /dev/null; then
echo "ERROR: git is not installed."
echo " Install git 2.25+: https://git-scm.com/downloads"
exit 1
fi
GIT_VERSION=$(git --version | grep -oE '[0-9]+\.[0-9]+' | head -1)
GIT_MAJOR=$(echo "$GIT_VERSION" | cut -d. -f1)
GIT_MINOR=$(echo "$GIT_VERSION" | cut -d. -f2)
if [ "$GIT_MAJOR" -lt 2 ] || { [ "$GIT_MAJOR" -eq 2 ] && [ "$GIT_MINOR" -lt 25 ]; }; then
echo "ERROR: git $GIT_VERSION detected, but 2.25+ is required (worktree support)."
echo " Upgrade git: https://git-scm.com/downloads"
exit 1
fi
echo "[ok] git $GIT_VERSION"
# ─── Soft requirements (warn + offer interactive fix) ─────────────────────────
# tmux
if ! command -v tmux &> /dev/null; then
echo ""
echo "WARNING: tmux is not installed (default runtime requires it)."
if command -v brew &> /dev/null; then
read -r -p " Install tmux via Homebrew? [Y/n] " response
response=${response:-Y}
if [[ "$response" =~ ^[Yy]$ ]]; then
brew install tmux
echo "[ok] tmux installed"
else
echo " Skipping. Install later: brew install tmux"
fi
else
echo " Install tmux: https://github.com/tmux/tmux/wiki/Installing"
fi
else
echo "[ok] tmux $(tmux -V | grep -oE '[0-9]+\.[0-9a-z]+')"
fi
# gh CLI authentication
if ! command -v gh &> /dev/null; then
echo ""
echo "WARNING: GitHub CLI (gh) is not installed."
echo " Install: https://cli.github.com/"
else
if ! gh auth status &> /dev/null; then
echo ""
echo "WARNING: GitHub CLI is not authenticated."
read -r -p " Run 'gh auth login' now? [Y/n] " response
response=${response:-Y}
if [[ "$response" =~ ^[Yy]$ ]]; then
gh auth login
else
echo " Skipping. Authenticate later: gh auth login"
fi
else
echo "[ok] gh authenticated"
fi
fi
# claude CLI
if ! command -v claude &> /dev/null; then
echo ""
echo "WARNING: Claude CLI is not installed (required for claude-code agent)."
echo " Install: npm install -g @anthropic-ai/claude-code"
fi
echo ""
# ─── Install pnpm via corepack ───────────────────────────────────────────────
echo "Enabling corepack and preparing pnpm..."
corepack enable
corepack prepare --activate
echo "[ok] pnpm $(pnpm --version)"
# ─── Install, build, link ────────────────────────────────────────────────────
echo ""
echo "Installing dependencies..."
pnpm install
echo "🧹 Cleaning stale build artifacts..."
echo ""
echo "Cleaning stale build artifacts..."
rm -rf packages/web/.next
echo "🔨 Building all packages..."
echo ""
echo "Building all packages..."
pnpm build
echo "🔗 Linking CLI globally..."
echo ""
echo "Linking CLI globally..."
cd packages/cli
npm link
cd ../..
cd "$REPO_ROOT"
# ─── Verify ao is in PATH ────────────────────────────────────────────────────
echo ""
echo "✅ Setup complete! The 'ao' command is now available."
if command -v ao &> /dev/null; then
echo "[ok] 'ao' command is available in PATH"
else
NPM_BIN="$(npm bin -g 2>/dev/null || npm config get prefix)/bin"
echo "WARNING: 'ao' is not in your PATH."
echo " Add this to your shell profile (~/.zshrc or ~/.bashrc):"
echo ""
echo " export PATH=\"$NPM_BIN:\$PATH\""
echo ""
echo " Then restart your terminal or run: source ~/.zshrc"
fi
# ─── Done ─────────────────────────────────────────────────────────────────────
echo ""
echo "Setup complete!"
echo ""
echo "Next steps:"
echo " 1. cd /path/to/your/project"
echo " 1. cd /path/to/your-project"
echo " 2. ao init --auto"
echo " 3. gh auth login"
echo " 4. ao start"
echo " 3. ao start"
echo ""