102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import * as plugins from '../../plugins.js';
|
|
import type {
|
|
CliArguments,
|
|
CommandAction,
|
|
IpcCommandOptions,
|
|
} from '../types.js';
|
|
import { handleDaemonError } from '../helpers/errors.js';
|
|
import { unknownError } from '../helpers/formatting.js';
|
|
import { runIpcCommand } from '../utils/ipc.js';
|
|
import { ensureDaemonOrHint } from './daemon-check.js';
|
|
|
|
/**
|
|
* Add an IPC-based CLI command with:
|
|
* - optional daemon preflight
|
|
* - standard error handling
|
|
* - automatic disconnect via runIpcCommand unless keepAlive is true
|
|
*/
|
|
export function registerIpcCommand(
|
|
smartcli: plugins.smartcli.Smartcli,
|
|
name: string | string[],
|
|
action: CommandAction,
|
|
opts: IpcCommandOptions = {},
|
|
) {
|
|
const names = Array.isArray(name) ? name : [name];
|
|
for (const singleName of names) {
|
|
const { actionLabel = singleName, keepAlive = false, requireDaemon = true } = opts;
|
|
|
|
smartcli.addCommand(singleName).subscribe({
|
|
next: async (argv: CliArguments) => {
|
|
// Early preflight for better UX
|
|
const ok = await ensureDaemonOrHint(requireDaemon, actionLabel);
|
|
if (!ok) {
|
|
process.exit(1);
|
|
return;
|
|
}
|
|
|
|
// Evaluate keepAlive - can be boolean or function
|
|
const shouldKeepAlive =
|
|
typeof keepAlive === 'function' ? keepAlive(argv) : keepAlive;
|
|
|
|
if (shouldKeepAlive) {
|
|
// Let action manage its own connection/cleanup lifecycle
|
|
try {
|
|
await action(argv);
|
|
} catch (error) {
|
|
handleDaemonError(error, actionLabel);
|
|
}
|
|
} else {
|
|
// Auto-disconnect pattern for one-shot IPC commands
|
|
await runIpcCommand(async () => {
|
|
try {
|
|
await action(argv);
|
|
} catch (error) {
|
|
handleDaemonError(error, actionLabel);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
error: (err) => {
|
|
// Fallback error path (should be rare with try/catch in next)
|
|
console.error(
|
|
`Unexpected error in command "${singleName}":`,
|
|
unknownError(err),
|
|
);
|
|
process.exit(1);
|
|
},
|
|
complete: () => {},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register local commands that don't require IPC/daemon connection
|
|
* Used for daemon lifecycle, service management, and other local operations
|
|
*/
|
|
export function registerLocalCommand(
|
|
smartcli: plugins.smartcli.Smartcli,
|
|
name: string,
|
|
action: (argv: CliArguments) => Promise<void>,
|
|
opts: { actionLabel?: string } = {},
|
|
) {
|
|
const { actionLabel = name } = opts;
|
|
smartcli.addCommand(name).subscribe({
|
|
next: async (argv: CliArguments) => {
|
|
try {
|
|
await action(argv);
|
|
} catch (error: any) {
|
|
console.error(`Error ${actionLabel}:`, error?.message || String(error));
|
|
process.exit(1);
|
|
}
|
|
},
|
|
error: (err) => {
|
|
console.error(
|
|
`Unexpected error in command "${name}":`,
|
|
unknownError(err),
|
|
);
|
|
process.exit(1);
|
|
},
|
|
complete: () => {},
|
|
});
|
|
}
|