blob: e6cd4489d2ed1b84c3e0fb95788d0cd0f02c9c3b [file]
import {
manageRuntimeHostService,
resolveRuntimeHostManagedServiceId,
RuntimeHostServiceManagerError,
type RuntimeHostManagedServiceInput,
type RuntimeHostManagedServiceResult,
type RuntimeHostServiceBackend,
} from './runtime-host-service-manager.js';
import { createSystemdUserRuntimeHostService } from './runtime-host-systemd-service.js';
export interface RuntimeHostServiceManagementCliOptions extends RuntimeHostManagedServiceInput {
readonly json: boolean;
}
export interface RuntimeHostServiceManagementCliDeps {
readonly manage: typeof manageRuntimeHostService;
readonly createBackend: (serviceId: string) => RuntimeHostServiceBackend;
readonly writeOutput: (value: string) => unknown;
readonly writeError: (value: string) => unknown;
}
export async function runManagedRuntimeHostServiceCli(
options: RuntimeHostServiceManagementCliOptions,
overrides: Partial<RuntimeHostServiceManagementCliDeps> = {},
): Promise<number> {
const deps: RuntimeHostServiceManagementCliDeps = {
manage: manageRuntimeHostService,
createBackend: createPlatformRuntimeHostServiceBackend,
writeOutput: (value) => process.stdout.write(value),
writeError: (value) => process.stderr.write(value),
...overrides,
};
try {
const { json: _json, ...input } = options;
const serviceId = resolveRuntimeHostManagedServiceId(options.clientDataRoot);
const result = await deps.manage(input, deps.createBackend(serviceId));
deps.writeOutput(
options.json ? `${JSON.stringify({ ...result, ok: true })}\n` : formatHumanResult(result),
);
return 0;
} catch (error) {
const code =
error instanceof RuntimeHostServiceManagerError ? error.code : 'internal_service_error';
const message = error instanceof Error ? error.message : String(error);
if (options.json) {
deps.writeOutput(
`${JSON.stringify({ schemaVersion: 1, ok: false, action: options.action, error: { code, message } })}\n`,
);
} else {
deps.writeError(`${message}\n`);
}
return 1;
}
}
function formatHumanResult(result: RuntimeHostManagedServiceResult): string {
const service = result.service;
if (result.action === 'uninstall') {
return result.retainedStateRoot
? `Runtime Host service is uninstalled. Data was retained at ${result.retainedStateRoot}\n`
: 'Runtime Host service is uninstalled.\n';
}
if (result.action === 'install') {
return service.active
? `Runtime Host service is installed and running at ${websocketUrl(service)}\n`
: 'Runtime Host service is installed but is not running. Check its status and journal.\n';
}
if (result.action === 'status') {
if (!service.installed) return 'Runtime Host service is not installed.\n';
return `Runtime Host service is ${service.state} at ${websocketUrl(service)}\n`;
}
return `Runtime Host service is ${service.state}.\n`;
}
export function createPlatformRuntimeHostServiceBackend(
serviceId: string,
): RuntimeHostServiceBackend {
if (process.platform === 'linux') return createSystemdUserRuntimeHostService(serviceId);
throw new RuntimeHostServiceManagerError(
'unsupported_platform',
'Managed Runtime Host services currently require Linux',
);
}
function websocketUrl(service: RuntimeHostManagedServiceResult['service']): string {
const websocket = service.config?.websocket;
return websocket
? `ws://${websocket.host}:${websocket.port}${websocket.path}`
: 'an unknown endpoint';
}