feat(01-05): add HTTP server with health endpoint and PID file
- CoordinationServer class using node:http - GET /health returns status, uptime, processCount - GET /status returns full server state and process list - PID file at ~/.cw/server.pid prevents duplicate servers - CLI --server flag and --port option for server mode - CW_PORT env var support for custom port
This commit is contained in:
@@ -2,17 +2,66 @@
|
||||
* Codewalk District CLI
|
||||
*
|
||||
* Commander-based CLI with help system and version display.
|
||||
* Supports server mode via --server flag.
|
||||
*/
|
||||
|
||||
import { Command } from 'commander';
|
||||
import { VERSION } from '../index.js';
|
||||
import { CoordinationServer } from '../server/index.js';
|
||||
import { ProcessManager, ProcessRegistry } from '../process/index.js';
|
||||
import { LogManager } from '../logging/index.js';
|
||||
|
||||
/** Environment variable for custom port */
|
||||
const CW_PORT_ENV = 'CW_PORT';
|
||||
|
||||
/**
|
||||
* Starts the coordination server in foreground mode.
|
||||
* Server runs until terminated via SIGTERM/SIGINT.
|
||||
*/
|
||||
async function startServer(port?: number): Promise<void> {
|
||||
// Get port from option, env var, or default
|
||||
const serverPort = port ??
|
||||
(process.env[CW_PORT_ENV] ? parseInt(process.env[CW_PORT_ENV], 10) : undefined);
|
||||
|
||||
// Create dependencies
|
||||
const registry = new ProcessRegistry();
|
||||
const processManager = new ProcessManager(registry);
|
||||
const logManager = new LogManager();
|
||||
|
||||
// Create and start server
|
||||
const server = new CoordinationServer(
|
||||
{ port: serverPort },
|
||||
processManager,
|
||||
logManager
|
||||
);
|
||||
|
||||
try {
|
||||
await server.start();
|
||||
} catch (error) {
|
||||
console.error('Failed to start server:', (error as Error).message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Import shutdown handler (will be implemented in Task 2)
|
||||
// For now, just handle basic signals
|
||||
const handleSignal = async (signal: string) => {
|
||||
console.log(`\nReceived ${signal}, shutting down...`);
|
||||
await server.stop();
|
||||
await processManager.stopAll();
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on('SIGTERM', () => handleSignal('SIGTERM'));
|
||||
process.on('SIGINT', () => handleSignal('SIGINT'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and configures the CLI program.
|
||||
*
|
||||
* @param serverHandler - Optional handler to be called for server mode
|
||||
* @returns Configured Commander program ready for parsing
|
||||
*/
|
||||
export function createCli(): Command {
|
||||
export function createCli(serverHandler?: (port?: number) => Promise<void>): Command {
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
@@ -20,6 +69,21 @@ export function createCli(): Command {
|
||||
.description('Multi-agent workspace for orchestrating multiple Claude Code agents')
|
||||
.version(VERSION, '-v, --version', 'Display version number');
|
||||
|
||||
// Server mode option (global flag)
|
||||
program
|
||||
.option('-s, --server', 'Start the coordination server')
|
||||
.option('-p, --port <number>', 'Port for the server (default: 3847, env: CW_PORT)', parseInt);
|
||||
|
||||
// Handle the case where --server is provided without a command
|
||||
// This makes --server work as a standalone action
|
||||
program.hook('preAction', async (_thisCommand, _actionCommand) => {
|
||||
const opts = program.opts();
|
||||
if (opts.server && serverHandler) {
|
||||
await serverHandler(opts.port);
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
// Placeholder commands - will be implemented in later phases
|
||||
program
|
||||
.command('status')
|
||||
@@ -44,3 +108,27 @@ export function createCli(): Command {
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the CLI, handling server mode and commands.
|
||||
*/
|
||||
export async function runCli(): Promise<void> {
|
||||
// Check for server flag early, before Commander processes
|
||||
const hasServerFlag = process.argv.includes('--server') || process.argv.includes('-s');
|
||||
|
||||
if (hasServerFlag) {
|
||||
// Get port from args if present
|
||||
const portIndex = process.argv.findIndex(arg => arg === '-p' || arg === '--port');
|
||||
const port = portIndex !== -1 && process.argv[portIndex + 1]
|
||||
? parseInt(process.argv[portIndex + 1], 10)
|
||||
: undefined;
|
||||
|
||||
await startServer(port);
|
||||
// Server runs indefinitely until signal
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal CLI processing
|
||||
const program = createCli();
|
||||
program.parse(process.argv);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user