118 lines
3.1 KiB
TypeScript
118 lines
3.1 KiB
TypeScript
import fs from "node:fs";
|
|
|
|
import { SWARM_TASKS_FILE } from "@/lib/fleet-config";
|
|
import { appendTaskEvent, applyTaskCallback, findTask } from "@/lib/tasks";
|
|
|
|
type SwarmRegistryTask = {
|
|
id: string;
|
|
taskboardTaskId?: number | null;
|
|
status?: string;
|
|
tmuxSession?: string;
|
|
worktree?: string;
|
|
pr?: number | null;
|
|
note?: string | null;
|
|
failedAt?: number | null;
|
|
completedAt?: number | null;
|
|
startedAt?: number | null;
|
|
agent?: string | null;
|
|
};
|
|
|
|
function readRegistry() {
|
|
if (!fs.existsSync(SWARM_TASKS_FILE)) {
|
|
return [] as SwarmRegistryTask[];
|
|
}
|
|
|
|
const parsed = JSON.parse(fs.readFileSync(SWARM_TASKS_FILE, "utf8")) as { tasks?: SwarmRegistryTask[] };
|
|
return Array.isArray(parsed.tasks) ? parsed.tasks : [];
|
|
}
|
|
|
|
function statusToDispatchState(status: string | undefined) {
|
|
switch (status) {
|
|
case "running":
|
|
return "acknowledged" as const;
|
|
case "done":
|
|
return "completed" as const;
|
|
case "failed":
|
|
return "failed" as const;
|
|
case "queued":
|
|
case "retrying":
|
|
return "dispatched" as const;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function statusToTaskStatus(status: string | undefined) {
|
|
switch (status) {
|
|
case "running":
|
|
return "In Progress" as const;
|
|
case "done":
|
|
return "Done" as const;
|
|
case "failed":
|
|
return "Backlog" as const;
|
|
case "queued":
|
|
case "retrying":
|
|
return "Todo" as const;
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export async function syncOpenClawTasks() {
|
|
const registryTasks = readRegistry();
|
|
const results: Array<{ taskId: number; registryStatus: string; synced: boolean }> = [];
|
|
|
|
for (const registryTask of registryTasks) {
|
|
if (!registryTask.taskboardTaskId) {
|
|
continue;
|
|
}
|
|
|
|
const task = await findTask(registryTask.taskboardTaskId);
|
|
if (!task) {
|
|
continue;
|
|
}
|
|
|
|
const dispatchState = statusToDispatchState(registryTask.status);
|
|
const taskStatus = statusToTaskStatus(registryTask.status);
|
|
if (!dispatchState) {
|
|
continue;
|
|
}
|
|
|
|
await applyTaskCallback(task.id, {
|
|
status: taskStatus,
|
|
dispatch_state: dispatchState,
|
|
summary:
|
|
registryTask.status === "done"
|
|
? `OpenClaw task ${registryTask.id} completed`
|
|
: registryTask.status === "failed"
|
|
? `OpenClaw task ${registryTask.id} failed`
|
|
: `OpenClaw task ${registryTask.id} ${registryTask.status}`,
|
|
detail:
|
|
registryTask.pr
|
|
? `PR #${registryTask.pr} from ${registryTask.id}`
|
|
: registryTask.note || registryTask.worktree || "",
|
|
completed_by: registryTask.agent || "openclaw-swarm",
|
|
last_error: registryTask.status === "failed" ? registryTask.note || "Swarm task failed" : null,
|
|
});
|
|
|
|
await appendTaskEvent({
|
|
taskId: task.id,
|
|
assignee: task.assignee,
|
|
family: task.family,
|
|
host: task.target_host,
|
|
eventType: "updated",
|
|
state: dispatchState,
|
|
summary: `OpenClaw sync: ${registryTask.status}`,
|
|
detail: registryTask.worktree || "",
|
|
});
|
|
|
|
results.push({
|
|
taskId: task.id,
|
|
registryStatus: registryTask.status || "unknown",
|
|
synced: true,
|
|
});
|
|
}
|
|
|
|
return results;
|
|
}
|