Files
boss/src/lib/execution/backends/claw-backend.ts
2026-04-03 01:36:29 +08:00

104 lines
3.1 KiB
TypeScript

import type { ExecutionBackend } from "@/lib/execution/execution-backend";
import type {
ExecutionBackendDescription,
ExecutionImmediateResult,
ExecutionRequest,
ExecutionRequestKind,
} from "@/lib/execution/types";
import {
getClawBackendConfig,
isClawBackendConfigured,
type ClawBackendConfig,
} from "@/lib/execution/backends/claw-config";
import { runClawCommand } from "@/lib/execution/backends/claw-runner";
export const CLAW_BACKEND_ID = "claw-runtime";
export const CLAW_BACKEND = {
backendId: CLAW_BACKEND_ID,
label: "Claw Runtime",
mode: "local",
} as const satisfies ExecutionBackendDescription;
const SUPPORTED_CLAW_KINDS = new Set<ExecutionRequestKind>([
"master_agent_reply",
"thread_reply",
]);
type ClawRunnerInput = Parameters<typeof runClawCommand>[0];
type ClawRunner = (input: ClawRunnerInput) => Promise<ExecutionImmediateResult>;
export interface ClawBackendSelectionState {
enabled: boolean;
supportsKinds: ExecutionRequestKind[];
}
function createFailedResult(error: string): ExecutionImmediateResult {
return {
status: "failed",
backendId: CLAW_BACKEND_ID,
error,
};
}
export function isClawRequestKindSupported(kind: ExecutionRequestKind) {
return SUPPORTED_CLAW_KINDS.has(kind);
}
export function getClawBackendSelectionState(
config: ClawBackendConfig = getClawBackendConfig(),
): ClawBackendSelectionState {
return {
enabled: isClawBackendConfigured(config),
supportsKinds: [...SUPPORTED_CLAW_KINDS],
};
}
function buildClawPayload(input: ExecutionRequest, config: ClawBackendConfig) {
return {
kind: input.kind,
projectId: input.projectId,
requestMessageId: input.requestMessageId,
body: input.body,
executionPrompt: input.executionPrompt ?? input.body,
model: input.modelOverride ?? config.defaultModel,
reasoningEffort: input.reasoningEffortOverride ?? "medium",
...(input.targetProjectId ? { targetProjectId: input.targetProjectId } : {}),
...(input.targetThreadId ? { targetThreadId: input.targetThreadId } : {}),
...(input.requestedByAccount ? { requestedByAccount: input.requestedByAccount } : {}),
...(input.requestedByLabel ? { requestedByLabel: input.requestedByLabel } : {}),
...(input.taskId ? { taskId: input.taskId } : {}),
};
}
export function createClawBackend(options?: {
config?: ClawBackendConfig;
runner?: ClawRunner;
}): ExecutionBackend {
const config = options?.config ?? getClawBackendConfig();
const runner = options?.runner ?? runClawCommand;
return {
backendId: CLAW_BACKEND_ID,
async canHandle(input) {
return isClawBackendConfigured(config) && isClawRequestKindSupported(input.kind);
},
async execute(input) {
const canHandle = await this.canHandle(input);
if (!canHandle) {
return createFailedResult("CLAW_BACKEND_NOT_AVAILABLE");
}
return runner({
config,
payload: buildClawPayload(input, config),
});
},
async describe() {
return CLAW_BACKEND;
},
};
}
export const CLAW_BACKEND_ADAPTER = createClawBackend();
export const createClawBackendForTesting = createClawBackend;