import path from "node:path"; import fs from "node:fs"; import pkg from "../package.json"; import { runDev } from "./dev"; import { runRelease } from "./run"; import { runStart } from "./start"; import { ensureValidPort } from "./ports"; import { maybePromptForUpdate } from "./update"; type Command = "run" | "dev" | "start"; type CliOptions = { command: Command; version?: string; backendPort?: number; webPort?: number; verbose?: boolean; debug?: boolean; }; function printHelp() { console.log(`kandev launcher Usage: kandev run [--version ] [--backend-port ] [--web-port ] [--verbose] [--debug] kandev dev [--backend-port ] [--web-port ] kandev start [--backend-port ] [--web-port ] [--verbose] [--debug] kandev [--version ] [--backend-port ] [--web-port ] [--verbose] [--debug] kandev --dev [--backend-port ] [--web-port ] Examples: kandev kandev run kandev --dev kandev dev kandev start kandev --version v0.1.0 kandev --backend-port 18080 --web-port 13000 kandev --debug Options: dev Use local repo for dev (make dev + next dev) if available. start Use local production build (make build + next start). run Use release bundles (default). --dev Alias for "dev". --version Release tag to install (default: latest). --backend-port Override backend port (or KANDEV_BACKEND_PORT env var). --web-port Override web port (or KANDEV_WEB_PORT env var). --verbose, -v Show info logs from backend + web. --debug Show debug logs + agent message dumps. --help, -h Show help. `); } function parseArgs(argv: string[]): CliOptions { const opts: CliOptions = { command: "run" }; for (let i = 0; i < argv.length; i += 1) { const arg = argv[i]; if (arg === "--help" || arg === "-h") { printHelp(); process.exit(0); } if (arg === "dev" || arg === "run" || arg === "start") { opts.command = arg; continue; } if (arg === "--version") { opts.version = argv[i + 1]; i += 1; continue; } if (arg.startsWith("--version=")) { opts.version = arg.split("=")[1]; continue; } if (arg === "--dev") { opts.command = "dev"; continue; } if (arg === "--backend-port") { opts.backendPort = Number(argv[i + 1]); i += 1; continue; } if (arg.startsWith("--backend-port=")) { opts.backendPort = Number(arg.split("=")[1]); continue; } if (arg === "--web-port") { opts.webPort = Number(argv[i + 1]); i += 1; continue; } if (arg.startsWith("--web-port=")) { opts.webPort = Number(arg.split("=")[1]); continue; } if (arg === "--verbose" || arg === "-v") { opts.verbose = true; continue; } if (arg === "--debug") { opts.debug = true; continue; } } return opts; } function envPort(name: string): number | undefined { const val = process.env[name]; return val ? Number(val) : undefined; } function findRepoRoot(startDir: string): string | null { let current = path.resolve(startDir); while (true) { if (current.endsWith(`${path.sep}apps`)) { const backendInApps = path.join(current, "backend"); const webInApps = path.join(current, "web"); if (fs.existsSync(backendInApps) && fs.existsSync(webInApps)) { return path.dirname(current); } } const backendDir = path.join(current, "apps", "backend"); const webDir = path.join(current, "apps", "web"); if (fs.existsSync(backendDir) && fs.existsSync(webDir)) { return current; } const parent = path.dirname(current); if (parent === current) { return null; } current = parent; } } async function main(): Promise { const raw = parseArgs(process.argv.slice(2)); const backendPort = ensureValidPort( raw.backendPort ?? envPort("KANDEV_BACKEND_PORT"), "backend port", ); const webPort = ensureValidPort(raw.webPort ?? envPort("KANDEV_WEB_PORT"), "web port"); if (raw.command === "dev") { const repoRoot = findRepoRoot(process.cwd()); if (!repoRoot) { throw new Error("Unable to locate repo root for dev. Run from the repo."); } await runDev({ repoRoot, backendPort, webPort }); return; } if (raw.command === "start") { const repoRoot = findRepoRoot(process.cwd()); if (!repoRoot) { throw new Error("Unable to locate repo root for start. Run from the repo."); } await runStart({ repoRoot, backendPort, webPort, verbose: raw.verbose, debug: raw.debug }); return; } await maybePromptForUpdate(pkg.version, process.argv.slice(2)); await runRelease({ version: raw.version, backendPort, webPort, verbose: raw.verbose, debug: raw.debug, }); } main().catch((err) => { console.error(`[kandev] ${err instanceof Error ? err.message : String(err)}`); process.exit(1); });