@shunirr/cc-glm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bin/cli.ts","../../src/config/loader.ts","../../src/lifecycle/singleton.ts","../../src/utils/process.ts","../../src/lifecycle/tracker.ts","../../src/utils/logger.ts","../../src/utils/claude.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CLI entry point for cc-glm\n * Main entry point that replaces the claude-w bash script\n */\n\nimport { spawn } from \"node:child_process\";\nimport { loadConfig } from \"../config/loader.js\";\nimport { SingletonProxy } from \"../lifecycle/singleton.js\";\nimport { hasClaudeProcess } from \"../lifecycle/tracker.js\";\nimport { Logger } from \"../utils/logger.js\";\nimport { resolveClaudePath } from \"../utils/claude.js\";\n\n/** Main CLI function */\nasync function main(): Promise<void> {\n // Load configuration\n const config = await loadConfig();\n const logger = new Logger(config.logging);\n\n // Resolve claude command path\n let claudePath: string;\n try {\n claudePath = await resolveClaudePath(config.claude.path);\n logger.debug(`Using claude: ${claudePath}`);\n } catch (err) {\n logger.error(`Failed to locate claude: ${err}`);\n process.exit(1);\n return;\n }\n\n // Initialize singleton proxy manager\n const proxy = new SingletonProxy(config);\n\n // Start proxy (singleton - will reuse if already running)\n logger.info(\"Starting proxy...\");\n await proxy.start();\n logger.info(`Proxy ready at ${proxy.getBaseUrl()}`);\n\n // Set environment variable for Claude Code\n const baseUrl = proxy.getBaseUrl();\n process.env.ANTHROPIC_BASE_URL = baseUrl;\n\n // Forward all arguments to claude command\n const args = process.argv.slice(2);\n logger.info(`Starting claude with args: ${args.join(\" \") || \"(no args)\"}`);\n\n // Spawn claude process\n const claude = spawn(claudePath, args, {\n stdio: \"inherit\",\n env: { ...process.env, ANTHROPIC_BASE_URL: baseUrl },\n });\n\n // Wait for claude to exit\n const exitCode = await new Promise<number>((resolve) => {\n claude.on(\"close\", (code) => {\n resolve(code ?? 0);\n });\n\n claude.on(\"error\", (err) => {\n logger.error(`Failed to start claude: ${err.message}`);\n resolve(1);\n });\n });\n\n logger.info(`Claude exited with code ${exitCode}`);\n\n // Stop proxy if no other Claude processes are running\n logger.info(\"Checking for other Claude processes...\");\n const hasClaude = await hasClaudeProcess();\n if (!hasClaude) {\n logger.info(\"No other Claude processes running, stopping proxy...\");\n await proxy.stopIfNoClaude(hasClaudeProcess);\n } else {\n logger.info(\"Other Claude processes still running, keeping proxy alive\");\n }\n\n // Exit with same code as claude\n process.exit(exitCode);\n}\n\n// Run main function\nmain().catch((err) => {\n console.error(\"Fatal error:\", err);\n process.exit(1);\n});\n","/**\n * Configuration loader for cc-glm\n * Handles YAML parsing, environment variable expansion, and validation\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse as parseYaml } from \"yaml\";\nimport type {\n Config,\n ProxyConfig,\n UpstreamConfig,\n LifecycleConfig,\n LoggingConfig,\n RoutingConfig,\n SignatureStoreConfig,\n ClaudeConfig,\n RawConfig,\n} from \"./types.js\";\n\n// Valid upstream names\nconst VALID_UPSTREAMS = new Set([\"anthropic\", \"zai\"]);\n\n// Valid log levels\nconst VALID_LOG_LEVELS = new Set([\"debug\", \"info\", \"warn\", \"error\"]);\n\n/** Default configuration values */\nconst DEFAULTS: Config = {\n proxy: {\n port: 8787,\n host: \"127.0.0.1\",\n },\n upstream: {\n anthropic: {\n url: \"https://api.anthropic.com\",\n },\n zai: {\n url: \"https://api.z.ai/api/anthropic\",\n apiKey: \"\",\n },\n },\n lifecycle: {\n stopGraceSeconds: 8,\n startWaitSeconds: 8,\n stateDir: `${process.env.TMPDIR ?? \"/tmp\"}/claude-code-proxy`,\n },\n logging: {\n level: \"info\",\n },\n routing: {\n rules: [],\n default: \"anthropic\",\n },\n claude: { path: \"\" },\n};\n\n/**\n * Expand environment variables in a string\n * Supports ${VAR} and ${VAR:-default} syntax\n */\nfunction expandEnvVars(str: string): string {\n if (typeof str !== \"string\") return str;\n return str.replace(/\\$\\{([^}:]+)(:-([^}]*))?\\}/g, (_, name, _defaultValue, defaultValue) => {\n return process.env[name] ?? defaultValue ?? \"\";\n });\n}\n\n/**\n * Get the default configuration file path\n */\nexport function getDefaultConfigPath(): string {\n const home = process.env.HOME ?? \"\";\n return join(home, \".config\", \"cc-glm\", \"config.yml\");\n}\n\n/**\n * Load configuration from a YAML file\n */\nexport async function loadConfig(filePath: string = getDefaultConfigPath()): Promise<Config> {\n let raw: RawConfig = {};\n\n // Load from file if exists\n if (existsSync(filePath)) {\n try {\n const content = await readFile(filePath, \"utf-8\");\n const parsed = parseYaml(content) as RawConfig;\n if (parsed && typeof parsed === \"object\") {\n raw = parsed;\n }\n } catch (error) {\n throw new Error(`Failed to parse config file at ${filePath}: ${error}`);\n }\n }\n\n // Merge with defaults and validate\n return mergeAndValidateConfig(raw);\n}\n\n/**\n * Merge raw config with defaults, validate, and apply environment variable expansion\n */\nfunction mergeAndValidateConfig(raw: RawConfig): Config {\n const config: Config = {\n proxy: mergeProxyConfig(raw.proxy),\n upstream: mergeUpstreamConfig(raw.upstream),\n lifecycle: mergeLifecycleConfig(raw.lifecycle),\n logging: mergeLoggingConfig(raw.logging),\n routing: mergeRoutingConfig(raw.routing),\n signatureStore: mergeSignatureStoreConfig(raw.signature_store),\n claude: mergeClaudeConfig(raw.claude),\n };\n\n // Validate configuration\n validateConfig(config);\n\n return config;\n}\n\nfunction mergeProxyConfig(raw?: Partial<ProxyConfig>): ProxyConfig {\n const rawPort = raw?.port;\n\n // Validate port is a valid number\n let port: number;\n if (typeof rawPort === \"number\" && Number.isFinite(rawPort) && !isNaN(rawPort)) {\n port = rawPort;\n } else {\n port = DEFAULTS.proxy.port;\n }\n\n // Validate port is in valid range and is an integer\n if (!Number.isInteger(port)) {\n throw new Error(`Invalid port: ${port}. Must be an integer.`);\n }\n if (port < 1 || port > 65535) {\n throw new Error(`Invalid port: ${port}. Must be between 1 and 65535.`);\n }\n\n // Validate host is a string\n const rawHost = raw?.host;\n const host = typeof rawHost === \"string\" ? rawHost : DEFAULTS.proxy.host;\n if (typeof host !== \"string\" || host.length === 0) {\n throw new Error(`Invalid host: must be a non-empty string.`);\n }\n\n return {\n host,\n port,\n };\n}\n\nfunction mergeUpstreamConfig(raw?: Partial<UpstreamConfig>): UpstreamConfig {\n const rawAnthropic = raw?.anthropic;\n const rawZai = raw?.zai;\n\n return {\n anthropic: {\n url: rawAnthropic?.url ? expandEnvVars(rawAnthropic.url) : DEFAULTS.upstream.anthropic.url,\n },\n zai: {\n url: rawZai?.url ? expandEnvVars(rawZai.url) : DEFAULTS.upstream.zai.url,\n apiKey:\n expandEnvVars(rawZai?.apiKey ?? \"\") ||\n process.env.ZAI_API_KEY ||\n DEFAULTS.upstream.zai.apiKey,\n },\n };\n}\n\nfunction mergeLifecycleConfig(raw?: Partial<LifecycleConfig>): LifecycleConfig {\n // Use user-provided stateDir or default\n const stateDir = raw?.stateDir ?? DEFAULTS.lifecycle.stateDir;\n const expandedStateDir = expandEnvVars(typeof stateDir === \"string\" ? stateDir : DEFAULTS.lifecycle.stateDir);\n\n // Only fall back to default if expansion resulted in empty string\n // Otherwise respect the user's configuration\n const finalStateDir = expandedStateDir || `${process.env.TMPDIR || \"/tmp\"}/claude-code-proxy`;\n\n // Validate stateDir is a non-empty string\n if (typeof finalStateDir !== \"string\" || finalStateDir.length === 0) {\n throw new Error(`Invalid stateDir: must be a non-empty string.`);\n }\n\n // Validate numeric values\n const rawStopGrace = raw?.stopGraceSeconds;\n const rawStartWait = raw?.startWaitSeconds;\n\n // Validate stopGraceSeconds\n let stopGraceSeconds: number;\n if (typeof rawStopGrace === \"number\" && Number.isFinite(rawStopGrace) && !isNaN(rawStopGrace)) {\n stopGraceSeconds = rawStopGrace;\n } else {\n stopGraceSeconds = DEFAULTS.lifecycle.stopGraceSeconds;\n }\n\n if (!Number.isInteger(stopGraceSeconds)) {\n throw new Error(`Invalid stopGraceSeconds: ${stopGraceSeconds}. Must be an integer.`);\n }\n if (stopGraceSeconds < 0 || stopGraceSeconds > 300) {\n throw new Error(`Invalid stopGraceSeconds: ${stopGraceSeconds}. Must be between 0 and 300.`);\n }\n\n // Validate startWaitSeconds\n let startWaitSeconds: number;\n if (typeof rawStartWait === \"number\" && Number.isFinite(rawStartWait) && !isNaN(rawStartWait)) {\n startWaitSeconds = rawStartWait;\n } else {\n startWaitSeconds = DEFAULTS.lifecycle.startWaitSeconds;\n }\n\n if (!Number.isInteger(startWaitSeconds)) {\n throw new Error(`Invalid startWaitSeconds: ${startWaitSeconds}. Must be an integer.`);\n }\n if (startWaitSeconds < 1 || startWaitSeconds > 60) {\n throw new Error(`Invalid startWaitSeconds: ${startWaitSeconds}. Must be between 1 and 60.`);\n }\n\n return {\n stopGraceSeconds,\n startWaitSeconds,\n stateDir: finalStateDir,\n };\n}\n\nfunction mergeLoggingConfig(raw?: Partial<LoggingConfig>): LoggingConfig {\n const level = raw?.level ?? DEFAULTS.logging.level;\n\n // Validate log level\n if (!VALID_LOG_LEVELS.has(level)) {\n throw new Error(`Invalid logging level: ${level}. Must be one of: ${Array.from(VALID_LOG_LEVELS).join(\", \")}`);\n }\n\n return {\n level,\n };\n}\n\nfunction mergeRoutingConfig(raw?: Partial<RoutingConfig>): RoutingConfig {\n const rules = raw?.rules ?? DEFAULTS.routing.rules;\n const defaultUpstream = raw?.default ?? DEFAULTS.routing.default;\n\n // Validate rules is an array\n if (!Array.isArray(rules)) {\n throw new Error(`Invalid routing.rules: must be an array, got ${typeof rules}`);\n }\n\n // Validate each rule\n for (let i = 0; i < rules.length; i++) {\n const rule = rules[i];\n if (!rule || typeof rule !== \"object\") {\n throw new Error(`Invalid routing rule at index ${i}: must be an object`);\n }\n if (typeof rule.match !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: match must be a string`);\n }\n if (typeof rule.upstream !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: upstream must be a string`);\n }\n if (!VALID_UPSTREAMS.has(rule.upstream)) {\n throw new Error(\n `Invalid routing rule at index ${i}: upstream \"${rule.upstream}\" is not valid. Must be one of: ${Array.from(VALID_UPSTREAMS).join(\", \")}`\n );\n }\n if (rule.model !== undefined && typeof rule.model !== \"string\") {\n throw new Error(`Invalid routing rule at index ${i}: model must be a string if provided`);\n }\n }\n\n // Validate default upstream\n if (!VALID_UPSTREAMS.has(defaultUpstream)) {\n throw new Error(\n `Invalid routing.default: \"${defaultUpstream}\" is not valid. Must be one of: ${Array.from(VALID_UPSTREAMS).join(\", \")}`\n );\n }\n\n return {\n rules,\n default: defaultUpstream,\n };\n}\n\nfunction mergeSignatureStoreConfig(raw?: Partial<SignatureStoreConfig>): SignatureStoreConfig {\n const DEFAULT_MAX_SIZE = 1000;\n const rawMaxSize = raw?.maxSize;\n\n let maxSize: number;\n if (typeof rawMaxSize === \"number\" && Number.isFinite(rawMaxSize) && !isNaN(rawMaxSize)) {\n maxSize = rawMaxSize;\n } else {\n maxSize = DEFAULT_MAX_SIZE;\n }\n\n if (!Number.isInteger(maxSize)) {\n throw new Error(`Invalid signatureStore.maxSize: ${maxSize}. Must be an integer.`);\n }\n if (maxSize < 1 || maxSize > 100000) {\n throw new Error(`Invalid signatureStore.maxSize: ${maxSize}. Must be between 1 and 100000.`);\n }\n\n return { maxSize };\n}\n\nfunction mergeClaudeConfig(raw?: Partial<ClaudeConfig>): ClaudeConfig {\n const path = raw?.path ?? DEFAULTS.claude.path;\n if (typeof path !== \"string\") {\n throw new Error(`Invalid claude.path: must be a string`);\n }\n return { path };\n}\n\n/**\n * Validate the complete configuration\n */\nfunction validateConfig(config: Config): void {\n // Validate URLs\n try {\n new URL(config.upstream.anthropic.url);\n } catch {\n throw new Error(`Invalid anthropic URL: ${config.upstream.anthropic.url}`);\n }\n\n try {\n new URL(config.upstream.zai.url);\n } catch {\n throw new Error(`Invalid zai URL: ${config.upstream.zai.url}`);\n }\n\n // Warn if zai API key is empty\n if (!config.upstream.zai.apiKey) {\n console.warn(\"Warning: zai API key is not set. Requests to z.ai will fail without ZAI_API_KEY.\");\n }\n}\n","/**\n * Singleton proxy management\n * Handles proxy lifecycle: start, stop, and status checking\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, openSync, closeSync } from \"node:fs\";\nimport { mkdir, rmdir } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { Config } from \"../config/types.js\";\nimport {\n isPortListening,\n pidIsAlive,\n ensureStateDir,\n readPidFile,\n writePidFile,\n removePidFile,\n sleep,\n execCommand,\n} from \"../utils/process.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/** Singleton proxy manager */\nexport class SingletonProxy {\n private config: Config;\n private pidFile: string;\n private lockDir: string;\n private logFile: string;\n\n constructor(config: Config) {\n this.config = config;\n const stateDir = config.lifecycle.stateDir;\n this.pidFile = join(stateDir, \"proxy.pid\");\n this.lockDir = join(stateDir, \"lock\");\n this.logFile = join(stateDir, \"proxy.log\");\n }\n\n /**\n * Start proxy if not already running\n * Uses lock directory for atomic singleton behavior\n */\n async start(): Promise<void> {\n // Ensure state directory exists before acquiring lock (lock dir is inside stateDir)\n await ensureStateDir(this.config.lifecycle.stateDir);\n\n // Check for stale lock and recover if necessary\n await this.recoverStaleLock();\n\n // Check if port is already listening\n if (await isPortListening(this.config.proxy.port)) {\n // Verify the existing process is actually our proxy\n const pid = readPidFile(this.pidFile);\n if (pid && pid > 0 && pidIsAlive(pid) && (await this.verifyPidOwnsPort(pid))) {\n // Port is listening, PID is alive, and PID owns the port - it's our proxy\n console.log(`Proxy already running on port ${this.config.proxy.port} (PID ${pid})`);\n return;\n } else {\n // Port is listening but it's not our proxy\n throw new Error(\n `Port ${this.config.proxy.port} is already in use by another process. ` +\n `Please stop the other process or configure a different port.`\n );\n }\n }\n\n // Try to acquire lock (mkdir is atomic)\n const lockAcquired = await this.acquireLock();\n if (!lockAcquired) {\n // Another process is starting the proxy, wait for it\n await this.waitForReady();\n return;\n }\n\n try {\n // Double-check after acquiring lock - verify any existing process is actually our proxy\n if (await isPortListening(this.config.proxy.port)) {\n const pid = readPidFile(this.pidFile);\n if (pid && pid > 0 && pidIsAlive(pid) && (await this.verifyPidOwnsPort(pid))) {\n // It's our proxy, already running\n return;\n } else {\n // Port taken by another process\n throw new Error(\n `Port ${this.config.proxy.port} is already in use by another process.`\n );\n }\n }\n\n // Start proxy process\n await this.startProxyProcess();\n\n // Wait for proxy to be ready\n await this.waitForReady();\n } finally {\n await this.releaseLock();\n }\n }\n\n /**\n * Stop proxy if no Claude processes are running\n */\n async stopIfNoClaude(hasClaude: () => Promise<boolean>): Promise<void> {\n const { stopGraceSeconds } = this.config.lifecycle;\n\n // Wait for grace period to ensure no new Claude processes start\n for (let i = 0; i < stopGraceSeconds; i++) {\n if (await hasClaude()) {\n return; // Claude still running, don't stop\n }\n await sleep(1000);\n }\n\n // No Claude processes for grace period, stop proxy\n await this.stop();\n }\n\n /**\n * Stop the proxy process\n * Verifies PID owns the target port before killing to avoid PID reuse issues\n */\n async stop(): Promise<void> {\n const pid = readPidFile(this.pidFile);\n\n if (pid && pid > 0) {\n // Verify the PID is actually listening on the target port\n const isOurProcess = await this.verifyPidOwnsPort(pid);\n\n if (isOurProcess) {\n // Graceful shutdown with exception handling\n try {\n process.kill(pid, \"SIGTERM\");\n } catch (err) {\n const error = err as Error;\n // ESRCH = no such process, EPERM = no permission\n if (\"code\" in error && (error.code === \"ESRCH\" || error.code === \"EPERM\")) {\n console.warn(`Failed to send SIGTERM to PID ${pid}: ${error.message}`);\n } else {\n throw error;\n }\n }\n\n // Wait for process to exit\n const deadline = Date.now() + 3000;\n while (Date.now() < deadline) {\n if (!pidIsAlive(pid)) {\n break;\n }\n await sleep(100);\n }\n\n // Force kill if still alive AND still owns the port (verify to avoid PID reuse)\n if (pidIsAlive(pid) && (await this.verifyPidOwnsPort(pid))) {\n try {\n process.kill(pid, \"SIGKILL\");\n } catch (err) {\n const error = err as Error;\n if (\"code\" in error && (error.code === \"ESRCH\" || error.code === \"EPERM\")) {\n console.warn(`Failed to send SIGKILL to PID ${pid}: ${error.message}`);\n } else {\n throw error;\n }\n }\n }\n } else {\n // PID exists but doesn't own the port - stale PID file\n console.warn(`PID ${pid} does not own port ${this.config.proxy.port}, treating as stale`);\n }\n }\n\n // Clean up PID file regardless\n await removePidFile(this.pidFile);\n }\n\n /**\n * Get the proxy base URL\n */\n getBaseUrl(): string {\n const { host, port } = this.config.proxy;\n return `http://${host}:${port}`;\n }\n\n /**\n * Try to acquire lock directory\n */\n private async acquireLock(): Promise<boolean> {\n try {\n await mkdir(this.lockDir, { recursive: false });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Release lock directory\n */\n private async releaseLock(): Promise<void> {\n try {\n await rmdir(this.lockDir);\n } catch {\n // Ignore\n }\n }\n\n /**\n * Recover from a stale lock directory\n * Handles cases where:\n * - Lock exists but port is not listening and PID is dead\n * - Lock exists but PID is alive but doesn't own the port (PID reuse)\n */\n private async recoverStaleLock(): Promise<void> {\n // If lock doesn't exist, nothing to do\n if (!existsSync(this.lockDir)) {\n return;\n }\n\n // Check if port is listening\n const portListening = await isPortListening(this.config.proxy.port);\n\n // Check if PID is alive\n const pid = readPidFile(this.pidFile);\n const pidAlive = pid !== null && pid > 0 && pidIsAlive(pid);\n\n // Determine if lock is stale\n let lockIsStale = false;\n\n if (!portListening && !pidAlive) {\n // Neither port listening nor PID alive - definitely stale\n lockIsStale = true;\n } else if (pidAlive && !portListening) {\n // PID is alive but port not listening - PID might be for a different process now\n // Verify if this PID actually owns our port\n const ownsPort = await this.verifyPidOwnsPort(pid!);\n if (!ownsPort) {\n // PID doesn't own the port - stale lock (PID reused)\n lockIsStale = true;\n }\n }\n\n if (lockIsStale) {\n console.warn(\"Detected stale lock directory, recovering...\");\n try {\n await rmdir(this.lockDir);\n await removePidFile(this.pidFile);\n console.warn(\"Stale lock recovered\");\n } catch (err) {\n const error = err as Error;\n console.error(`Failed to recover stale lock: ${error.message}`);\n }\n }\n }\n\n /**\n * Verify that a PID is actually listening on the target port\n * This prevents killing a reused PID that doesn't belong to our proxy\n */\n private async verifyPidOwnsPort(pid: number): Promise<boolean> {\n // First check if port is listening at all\n if (!(await isPortListening(this.config.proxy.port))) {\n return false;\n }\n\n // Then check if the PID is alive\n if (!pidIsAlive(pid)) {\n return false;\n }\n\n // Use lsof to verify the specific PID owns the port\n try {\n const output = await execCommand(\"lsof\", [\n \"-nP\",\n `-iTCP:${this.config.proxy.port}`,\n \"-sTCP:LISTEN\",\n \"-p\",\n pid.toString(),\n ]);\n // If lsof succeeds with this PID, it owns the port\n return output.includes(pid.toString());\n } catch {\n // lsof failed - either PID doesn't own port or command failed\n return false;\n }\n }\n\n /**\n * Wait for proxy to be ready (port listening)\n */\n private async waitForReady(): Promise<void> {\n const { startWaitSeconds } = this.config.lifecycle;\n const deadline = Date.now() + startWaitSeconds * 1000;\n\n while (Date.now() < deadline) {\n if (await isPortListening(this.config.proxy.port)) {\n return;\n }\n await sleep(100);\n }\n\n throw new Error(\n `Proxy did not become ready at ${this.getBaseUrl()} (log: ${this.logFile})`\n );\n }\n\n /**\n * Start the proxy server as a detached process\n */\n private async startProxyProcess(): Promise<void> {\n // Find the server entry point (CLI is in dist/bin/, server is in dist/proxy/)\n const serverPath = join(__dirname, \"..\", \"proxy\", \"server.js\");\n\n if (!existsSync(serverPath)) {\n throw new Error(`Proxy entry not found: ${serverPath}`);\n }\n\n // Spawn detached process with log file descriptor\n const logFd = openSync(this.logFile, \"a\");\n const proc = spawn(process.execPath, [serverPath], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n env: { ...process.env }, // Inherit all environment variables\n });\n\n // Validate PID before unrefing and writing to file\n const pid = proc.pid;\n if (!pid || pid < 1) {\n closeSync(logFd);\n throw new Error(\"Failed to get valid PID from spawned process\");\n }\n\n proc.unref();\n await writePidFile(this.pidFile, pid);\n\n // Close log FD in parent process (child has its own copy via dup2)\n closeSync(logFd);\n }\n}\n","/**\n * Process-related utilities\n */\n\nimport { spawn } from \"node:child_process\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { mkdir, writeFile, unlink } from \"node:fs/promises\";\n\n/** Minimum valid PID (PIDs are always > 0) */\nconst MIN_VALID_PID = 1;\n\n/** Execute a command and return its output */\nexport async function execCommand(command: string, args: string[]): Promise<string> {\n return new Promise((resolve, reject) => {\n const proc = spawn(command, args);\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (data) => {\n stdout += data.toString();\n });\n\n proc.stderr?.on(\"data\", (data) => {\n stderr += data.toString();\n });\n\n proc.on(\"close\", (code) => {\n if (code === 0) {\n resolve(stdout.trim());\n } else {\n const error = new Error(`${command} failed with code ${code}: ${stderr}`) as Error & { code?: number | string | null; command?: string };\n error.code = code;\n error.command = command;\n reject(error);\n }\n });\n\n proc.on(\"error\", (err) => {\n // ENOENT = command not found\n const error = err as Error & { code?: string };\n if (error.code === \"ENOENT\") {\n const enhancedError = new Error(`Command not found: ${command}`) as Error & { code?: string; command?: string };\n enhancedError.code = \"ENOENT\";\n enhancedError.command = command;\n reject(enhancedError);\n } else {\n reject(err);\n }\n });\n });\n}\n\n/** Check if a PID is alive */\nexport function pidIsAlive(pid: number): boolean {\n // Guard against pid=0 which would send signal to process group\n if (!Number.isFinite(pid) || pid < MIN_VALID_PID) {\n return false;\n }\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Check if a port is listening using lsof */\nexport async function isPortListening(port: number): Promise<boolean> {\n try {\n await execCommand(\"lsof\", [`-nP`, `-iTCP:${port}`, `-sTCP:LISTEN`]);\n return true;\n } catch (err) {\n const error = err as Error & { code?: string; command?: string };\n // If lsof command is not found, throw a clear error\n if (error.code === \"ENOENT\" && error.command === \"lsof\") {\n throw new Error(\n `lsof command is required but not found. Please install lsof or ensure it's in your PATH.`\n );\n }\n // Other errors (port not listening, etc.) return false\n return false;\n }\n}\n\n/** Ensure state directory exists */\nexport async function ensureStateDir(stateDir: string): Promise<void> {\n if (!existsSync(stateDir)) {\n await mkdir(stateDir, { recursive: true });\n }\n}\n\n/** Read PID from file, returns null if invalid or missing */\nexport function readPidFile(pidFile: string): number | null {\n try {\n if (!existsSync(pidFile)) {\n return null;\n }\n const content = readFileSync(pidFile, \"utf-8\");\n const pid = parseInt(content.trim(), 10);\n // Validate PID is in valid range\n if (isNaN(pid) || pid < MIN_VALID_PID) {\n return null;\n }\n return pid;\n } catch {\n return null;\n }\n}\n\n/** Write PID to file, throws if pid is invalid */\nexport async function writePidFile(pidFile: string, pid: number): Promise<void> {\n // Validate PID before writing\n if (!Number.isFinite(pid) || pid < MIN_VALID_PID) {\n throw new Error(`Invalid PID ${pid}: cannot write to PID file`);\n }\n await writeFile(pidFile, pid.toString(), \"utf-8\");\n}\n\n/** Remove PID file */\nexport async function removePidFile(pidFile: string): Promise<void> {\n try {\n await unlink(pidFile);\n } catch {\n // Ignore if file doesn't exist\n }\n}\n\n/** Sleep for specified milliseconds */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * Claude process tracking using pgrep\n */\n\nimport { execCommand } from \"../utils/process.js\";\n\n/**\n * Check if any Claude process is running for the current user\n * Uses pgrep to find processes named \"claude\"\n */\nexport async function hasClaudeProcess(): Promise<boolean> {\n try {\n const uid = process.getuid?.() ?? process.env.UID ?? \"\";\n await execCommand(\"pgrep\", [`-u`, uid.toString(), `-x`, `claude`]);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Get list of Claude process IDs\n */\nexport async function getClaudePids(): Promise<number[]> {\n try {\n const uid = process.getuid?.() ?? process.env.UID ?? \"\";\n const output = await execCommand(\"pgrep\", [`-u`, uid.toString(), `-x`, `claude`]);\n return output.split(\"\\n\").map((line) => parseInt(line.trim(), 10)).filter((pid) => !isNaN(pid));\n } catch {\n return [];\n }\n}\n","/**\n * Logging utilities\n */\n\nimport chalk from \"chalk\";\nimport type { LoggingConfig } from \"../config/types.js\";\n\n/** Log level values for comparison */\nconst LOG_LEVEL_VALUES: Record<LoggingConfig[\"level\"], number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/** Logger class */\nexport class Logger {\n private config: LoggingConfig;\n\n constructor(config: LoggingConfig) {\n this.config = config;\n }\n\n /** Update log level */\n setLevel(level: LoggingConfig[\"level\"]): void {\n this.config.level = level;\n }\n\n /** Check if a message should be logged */\n private shouldLog(level: LoggingConfig[\"level\"]): boolean {\n return LOG_LEVEL_VALUES[level] >= LOG_LEVEL_VALUES[this.config.level];\n }\n\n /** Format log message */\n private format(level: LoggingConfig[\"level\"], message: string): string {\n const timestamp = new Date().toISOString();\n const levelStr = level.toUpperCase().padEnd(5);\n return `[${timestamp}] ${levelStr} ${message}`;\n }\n\n /** Log debug message */\n debug(message: string): void {\n if (this.shouldLog(\"debug\")) {\n console.log(chalk.gray(this.format(\"debug\", message)));\n }\n }\n\n /** Log info message */\n info(message: string): void {\n if (this.shouldLog(\"info\")) {\n console.log(chalk.white(this.format(\"info\", message)));\n }\n }\n\n /** Log warning message */\n warn(message: string): void {\n if (this.shouldLog(\"warn\")) {\n console.log(chalk.yellow(this.format(\"warn\", message)));\n }\n }\n\n /** Log error message */\n error(message: string): void {\n if (this.shouldLog(\"error\")) {\n console.error(chalk.red(this.format(\"error\", message)));\n }\n }\n}\n","/**\n * Claude command path resolution utilities\n */\n\nimport { existsSync } from \"node:fs\";\nimport { execCommand } from \"./process.js\";\n\n/**\n * Resolve the path to the Claude executable\n * @param configPath - Path from config (empty string = auto-detect)\n * @returns Resolved absolute path to the Claude executable\n * @throws Error if Claude cannot be found\n */\nexport async function resolveClaudePath(configPath: string): Promise<string> {\n // Config path specified - validate it exists\n if (configPath) {\n if (!existsSync(configPath)) {\n throw new Error(`Claude executable not found at: ${configPath}`);\n }\n return configPath;\n }\n\n // Auto-detect using which (Unix/macOS) or where (Windows)\n const detector = process.platform === \"win32\" ? \"where\" : \"which\";\n try {\n return await execCommand(detector, [\"claude\"]);\n } catch (err) {\n const error = err as Error & { code?: string };\n if (error.code === \"ENOENT\") {\n throw new Error(\n `Claude command not found. Install Claude Code CLI or set 'claude.path' in config.yml`\n );\n }\n throw err;\n }\n}\n"],"mappings":"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","names":["spawn","spawn","existsSync","mkdir","join","existsSync","join","mkdir","existsSync","spawn","existsSync","existsSync","spawn"]}