import type { PilotDeckHookInput } from "../protocol/input.js";
import type { PilotDeckHookCommand } from "../protocol/settings.js";
import type { PilotDeckHookOutput } from "../protocol/output.js";
import { parseHookOutput } from "./parseHookOutput.js";
import type { CommandHookExecutionResult } from "./CommandHookExecutor.js";
export type CallbackHookHandler = (input: {
hookInput: PilotDeckHookInput;
signal?: AbortSignal;
}) => Promise<PilotDeckHookOutput | string | void> | PilotDeckHookOutput | string | void;
export class CallbackHookExecutor {
private readonly callbacks = new Map<string, CallbackHookHandler>();
register(name: string, handler: CallbackHookHandler): void {
this.callbacks.set(name, handler);
}
unregister(name: string): void {
this.callbacks.delete(name);
}
async execute(options: {
hook: Extract<PilotDeckHookCommand, { type: "callback" }>;
hookInput: PilotDeckHookInput;
signal?: AbortSignal;
}): Promise<CommandHookExecutionResult> {
const callback = this.callbacks.get(options.hook.name);
if (!callback) {
return {
stdout: "",
stderr: `Callback hook ${options.hook.name} is not registered.`,
outcome: "non_blocking_error",
output: { type: "sync" },
};
}
try {
const result = await callback({ hookInput: options.hookInput, signal: options.signal });
if (typeof result === "string") {
return {
stdout: result,
stderr: "",
exitCode: 0,
outcome: "success",
output: parseHookOutput(result),
};
}
return {
stdout: "",
stderr: "",
exitCode: 0,
outcome: "success",
output: result ?? { type: "sync" },
};
} catch (error) {
return {
stdout: "",
stderr: error instanceof Error ? error.message : String(error),
outcome: "non_blocking_error",
output: { type: "sync" },
};
}
}
}