125 lines
3.8 KiB
TypeScript
125 lines
3.8 KiB
TypeScript
/**
|
|
* 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.
|
|
*
|
|
* All checks throw on failure so callers can catch and handle uniformly.
|
|
*/
|
|
|
|
import { existsSync } from "node:fs";
|
|
import { resolve, dirname } from "node:path";
|
|
import { isPortAvailable } from "./web-dir.js";
|
|
import { exec } from "./shell.js";
|
|
import { isInstalledUnderNodeModules } from "./dashboard-rebuild.js";
|
|
|
|
/**
|
|
* Check that the dashboard port is free.
|
|
* Throws if the port is already in use.
|
|
*/
|
|
async function checkPort(port: number): Promise<void> {
|
|
const free = await isPortAvailable(port);
|
|
if (!free) {
|
|
throw new Error(
|
|
`Port ${port} is already in use. Free it or change 'port' in agent-orchestrator.yaml.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check that workspace packages have been compiled (TypeScript → JavaScript).
|
|
* Locates @aoagents/ao-core by walking up from webDir, handling both pnpm
|
|
* workspaces (symlinked deps in webDir/node_modules) and npm/yarn global
|
|
* installs (hoisted to a parent node_modules).
|
|
*/
|
|
async function checkBuilt(webDir: string): Promise<void> {
|
|
const isNpmInstall = isInstalledUnderNodeModules(webDir);
|
|
const corePkgDir = findPackageUp(webDir, "@aoagents", "ao-core");
|
|
if (!corePkgDir) {
|
|
const hint = isNpmInstall
|
|
? "Run: npm install -g @aoagents/ao@latest"
|
|
: "Run: pnpm install && pnpm build";
|
|
throw new Error(`Dependencies not installed. ${hint}`);
|
|
}
|
|
const coreEntry = resolve(corePkgDir, "dist", "index.js");
|
|
if (!existsSync(coreEntry)) {
|
|
const hint = isNpmInstall
|
|
? "Run: npm install -g @aoagents/ao@latest"
|
|
: "Run: pnpm build";
|
|
throw new Error(`Packages not built. ${hint}`);
|
|
}
|
|
|
|
const webBuildId = resolve(webDir, ".next", "BUILD_ID");
|
|
const startAllEntry = resolve(webDir, "dist-server", "start-all.js");
|
|
if (!existsSync(webBuildId) || !existsSync(startAllEntry)) {
|
|
const hint = isNpmInstall
|
|
? "Run: npm install -g @aoagents/ao@latest"
|
|
: "Run: pnpm build";
|
|
throw new Error(`Packages not built. ${hint}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walk up from startDir looking for node_modules/<segments>.
|
|
* Mirrors Node's module resolution: checks each ancestor directory until
|
|
* the package is found or the filesystem root is reached.
|
|
*/
|
|
function findPackageUp(startDir: string, ...segments: string[]): string | null {
|
|
let dir = resolve(startDir);
|
|
while (true) {
|
|
const candidate = resolve(dir, "node_modules", ...segments);
|
|
if (existsSync(candidate)) return candidate;
|
|
const parent = dirname(dir);
|
|
if (parent === dir) break;
|
|
dir = parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Check that tmux is installed.
|
|
* Throws with platform-appropriate manual install instructions when missing.
|
|
*/
|
|
async function checkTmux(): Promise<void> {
|
|
try {
|
|
await exec("tmux", ["-V"]);
|
|
return;
|
|
} catch {
|
|
// tmux not found
|
|
}
|
|
|
|
const hint =
|
|
process.platform === "darwin"
|
|
? "brew install tmux"
|
|
: process.platform === "win32"
|
|
? "tmux is not available on Windows. Use WSL: wsl --install, then: sudo apt install tmux"
|
|
: "sudo apt install tmux (Debian/Ubuntu) or sudo dnf install tmux (Fedora)";
|
|
throw new Error(`tmux is not installed. Install it: ${hint}`);
|
|
}
|
|
|
|
/**
|
|
* 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<void> {
|
|
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 {
|
|
throw new Error("GitHub CLI is not authenticated. Run: gh auth login");
|
|
}
|
|
}
|
|
|
|
export const preflight = {
|
|
checkPort,
|
|
checkBuilt,
|
|
checkTmux,
|
|
checkGhAuth,
|
|
};
|