UNPKG

@bluecadet/launchpad-cli

Version:
82 lines 3.82 kB
/** * Clean API for executing commands via controller or daemon. * Handles daemon detection and routing automatically. */ import path from "node:path"; import { LaunchpadController } from "@bluecadet/launchpad-controller"; import { IPCClient } from "@bluecadet/launchpad-controller/ipc-client"; import { getDaemonPid } from "@bluecadet/launchpad-controller/pid-utils"; import { errAsync } from "neverthrow"; import { DaemonNotRunningError, IPCConnectionError } from "../errors.js"; import { cliLogger } from "./cli-logger.js"; export { DaemonNotRunningError, IPCConnectionError }; /** * Execute a function with a connected IPC client if daemon is running. * If daemon is not running, returns an error. * Use this for commands that REQUIRE a daemon (status, stop). */ export function withDaemon(baseDir, controllerConfig, relayLogs, operation, deps) { const pidFile = path.resolve(baseDir, controllerConfig.pidFile); const socketPath = path.resolve(baseDir, controllerConfig.socketPath); // Check if daemon is running const daemonPidResult = (deps?.resolvePid ?? getDaemonPid)(pidFile); if (daemonPidResult.isErr() || daemonPidResult.value === null) { return errAsync(new DaemonNotRunningError()); } const pid = daemonPidResult.value; const client = new IPCClient(); if (relayLogs) { addLogListeners(client); } // Connect and execute operation, disconnecting on both success and error return client.connect(socketPath).andThen(() => { return withCleanup(operation(client, pid), () => client.disconnect()); }); } /** * Execute commands either via daemon (if running) or local controller (if not). * Use this for commands that can work either way (content, monitor). */ export function withDaemonOrController(baseDir, controllerConfig, options) { const pidFile = path.resolve(baseDir, controllerConfig.pidFile); const socketPath = path.resolve(baseDir, controllerConfig.socketPath); // Check if daemon is running const daemonPidResult = getDaemonPid(pidFile); const isDaemonRunning = daemonPidResult.isOk() && daemonPidResult.value !== null; if (isDaemonRunning) { // Use daemon via IPC const pid = daemonPidResult.value; cliLogger.info("Daemon is running, delegating to daemon via IPC"); const client = new IPCClient(); addLogListeners(client); return client.connect(socketPath).andThen(() => { return withCleanup(options.ifDaemon(client, pid), () => client.disconnect()); }); } // Create local controller cliLogger.info(`Daemon is not running, starting controller in ${options.mode} mode`); const controller = new LaunchpadController(controllerConfig, baseDir, options.mode); addLogListeners(controller.getEventBus()); return controller.start().andThen(() => { const result = options.otherwise(controller); // Stop controller after commands complete (unless persistent mode) if (options.mode === "task") { return withCleanup(result, () => controller.stop()); } return result; }); } function withCleanup(result, cleanup) { return result.andTee(() => cleanup()).orTee(() => cleanup()); } // TODO: nicer log formatting function addLogListeners(bus) { bus.on("log:error", cliLogger.fromPayload.bind(null, "error")); bus.on("log:warn", cliLogger.fromPayload.bind(null, "warn")); bus.on("log:info", cliLogger.fromPayload.bind(null, "info")); bus.on("log:debug", cliLogger.fromPayload.bind(null, "debug")); bus.on("log:verbose", cliLogger.fromPayload.bind(null, "verbose")); bus.on("log:tty", (data) => cliLogger.fixed(data.message)); bus.on("log:tty:close", () => cliLogger.fixed(null)); } //# sourceMappingURL=controller-execution.js.map