@bluecadet/launchpad-cli
Version:
CLI for @bluecadet/launchpad utilities
82 lines • 3.82 kB
JavaScript
/**
* 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