[taskboard] add completion sync APIs
This commit is contained in:
117
lib/openclaw-sync.ts
Normal file
117
lib/openclaw-sync.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user