From b027f5686e92f494045fab085f3996edff9e3c0d Mon Sep 17 00:00:00 2001 From: suraj-markup Date: Sun, 1 Mar 2026 19:15:11 +0530 Subject: [PATCH] fix(cli): tell user when default port is busy and suggest alternative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In interactive mode: "Port 3000 is busy — suggesting 3001 instead. Press Enter to accept, or type a different port." In auto mode: "Port 3000 is busy — using 3001 instead." Co-Authored-By: Claude Opus 4.6 --- packages/cli/__tests__/commands/init.test.ts | 28 ++++++++++++++++++++ packages/cli/src/commands/init.ts | 7 +++++ 2 files changed, 35 insertions(+) diff --git a/packages/cli/__tests__/commands/init.test.ts b/packages/cli/__tests__/commands/init.test.ts index 7c91c57d4..1681aca2e 100644 --- a/packages/cli/__tests__/commands/init.test.ts +++ b/packages/cli/__tests__/commands/init.test.ts @@ -87,6 +87,34 @@ describe("init command", () => { } }); + it("auto mode tells user when default port is busy and which port it picked", async () => { + tmpDir = mkdtempSync(join(tmpdir(), "ao-init-test-")); + const outputPath = join(tmpDir, "agent-orchestrator.yaml"); + + // Occupy port 3000 + const blocker = createServer(); + await new Promise((resolve) => { + blocker.listen(3000, "127.0.0.1", () => resolve()); + }); + + try { + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + const program = new Command(); + program.exitOverride(); + registerInit(program); + + await program.parseAsync(["node", "test", "init", "--auto", "--output", outputPath]); + + const logCalls = logSpy.mock.calls.map((args) => args.join(" ")); + const busyMessage = logCalls.find((msg) => msg.includes("Port 3000 is busy")); + expect(busyMessage).toBeDefined(); + expect(busyMessage).toContain("instead"); + } finally { + await new Promise((resolve) => blocker.close(() => resolve())); + } + }); + it("auto mode writes name and sessionPrefix to project config", async () => { tmpDir = mkdtempSync(join(tmpdir(), "ao-init-test-")); const outputPath = join(tmpDir, "agent-orchestrator.yaml"); diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 4b700b3f8..1b77fa9c7 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -252,6 +252,11 @@ export function registerInit(program: Command): void { ), ); console.log(chalk.dim(" Please specify a port manually.\n")); + } else if (freePort !== DEFAULT_PORT) { + console.log( + chalk.yellow(`\n⚠ Port ${DEFAULT_PORT} is busy — suggesting ${freePort} instead.`), + ); + console.log(chalk.dim(" Press Enter to accept, or type a different port.\n")); } const portStr = await prompt(rl, "Dashboard port", String(freePort ?? DEFAULT_PORT)); const port = parseInt(portStr, 10); @@ -462,6 +467,8 @@ async function handleAutoMode(outputPath: string, smart: boolean): Promise ), ); console.log(chalk.dim(" Set the port manually in the config before running ao start.\n")); + } else if (port !== DEFAULT_PORT) { + console.log(chalk.yellow(` ⚠ Port ${DEFAULT_PORT} is busy — using ${port} instead.`)); } const config: Record = { dataDir: "~/.agent-orchestrator",