import { buildCommand, numberParser } from "@stricli/core"; import type { LocalContext } from "./context.js"; export const command = buildCommand({ docs: { brief: "Start the ACP proxy server", fullDescription: "Starts a WebSocket proxy server that bridges clients to ACP agents. " + "The agent command is spawned as a subprocess and communicates via stdin/stdout.\n\n" + "Use -- to pass arguments to the agent:\n" + " acp-link /path/to/agent -- --verbose --model gpt-4\n\n" + "Use --manager to start the Manager Web UI instead:\n" + " acp-link --manager\n\n" + "For remote access, set ACP_AUTH_TOKEN environment variable or let it auto-generate.", }, parameters: { flags: { port: { kind: "parsed", parse: numberParser, brief: "Port to listen on", default: "9315", }, host: { kind: "parsed", parse: String, brief: "Host to bind to (use 0.0.0.0 for remote access)", default: "localhost", }, debug: { kind: "boolean", brief: "Enable debug logging to file", default: false, }, "no-auth": { kind: "boolean", brief: "DANGEROUS: Disable authentication (not recommended)", default: false, }, https: { kind: "boolean", brief: "Enable HTTPS with auto-generated self-signed certificate", default: false, }, manager: { kind: "boolean", brief: "Start Manager Web UI (no proxy)", default: false, }, group: { kind: "parsed", parse: (value: string) => { if (!/^[a-zA-Z0-9_-]+$/.test(value)) { throw new Error(`Invalid group "${value}": only letters, digits, hyphens, and underscores are allowed`); } return value; }, brief: "Channel group ID for RCS registration (env: ACP_RCS_GROUP)", optional: true, }, }, positional: { kind: "array", parameter: { brief: "Agent command and arguments (use -- before agent flags)", parse: String, placeholder: "command", }, minimum: 0, }, }, func: async function ( this: LocalContext, flags: { port: number; host: string; debug: boolean; "no-auth": boolean; https: boolean; manager: boolean; group: string | undefined }, ...args: readonly string[] ) { const port = flags.port; const host = flags.host; const debug = flags.debug; const noAuth = flags["no-auth"]; const https = flags.https; const manager = flags.manager; const group = flags.group; // Manager mode: start web UI only, no proxy if (manager) { const { startManager } = await import("../manager/index.js"); await startManager(port); return; } // Proxy mode: agent command is required if (args.length === 0) { console.error("Error: agent command is required (or use --manager)"); process.exit(1); } const [command, ...agentArgs] = args; const cwd = process.cwd(); // Determine auth token // Priority: ACP_AUTH_TOKEN env var > auto-generate (unless --no-auth) let token: string | undefined; if (noAuth) { console.warn("⚠️ WARNING: Authentication disabled. This is dangerous for remote access!"); token = undefined; } else { token = process.env.ACP_AUTH_TOKEN; if (!token) { // Auto-generate random token const { randomBytes } = await import("node:crypto"); token = randomBytes(32).toString("hex"); } } // Initialize logger const { initLogger } = await import("../logger.js"); initLogger({ debug }); // Import and run the server const { startServer } = await import("../server.js"); await startServer({ port, host, command: command!, args: [...agentArgs], cwd, debug, token, https, group }); }, });