mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
574 lines
15 KiB
TypeScript
574 lines
15 KiB
TypeScript
// Workboard plugin module implements gateway behavior.
|
|
import type { OpenClawPluginApi } from "../api.js";
|
|
import { redactClaimToken } from "./card-redaction.js";
|
|
import {
|
|
assertNoCursorAdvance,
|
|
createWorkboardDispatchHandler,
|
|
listWorkboardCards,
|
|
readId,
|
|
respondError,
|
|
} from "./gateway-helpers.js";
|
|
import {
|
|
registerWorkboardWorkspaceBoardMethod,
|
|
registerWorkboardWorkspaceBulkMethod,
|
|
registerWorkboardWorkspaceCardMethods,
|
|
registerWorkboardWorkspaceWorkflowMethods,
|
|
} from "./gateway-workspace-methods.js";
|
|
import { WorkboardStore } from "./store.js";
|
|
|
|
const READ_SCOPE = "operator.read" as const;
|
|
const WRITE_SCOPE = "operator.write" as const;
|
|
|
|
function redactDiagnosticsRows(result: Awaited<ReturnType<WorkboardStore["diagnostics"]>>) {
|
|
return {
|
|
...result,
|
|
diagnostics: result.diagnostics.map((row) => ({
|
|
...row,
|
|
card: redactClaimToken(row.card),
|
|
})),
|
|
};
|
|
}
|
|
|
|
export function registerWorkboardGatewayMethods(params: {
|
|
api: OpenClawPluginApi;
|
|
store?: WorkboardStore;
|
|
}) {
|
|
const { api } = params;
|
|
const store = params.store ?? WorkboardStore.openSqlite();
|
|
const dispatchCards = createWorkboardDispatchHandler({
|
|
api,
|
|
store,
|
|
redactCard: redactClaimToken,
|
|
});
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.list",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await listWorkboardCards(store, requestParams.boardId, redactClaimToken));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
registerWorkboardWorkspaceCardMethods({ api, store, redactCard: redactClaimToken });
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.move",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(
|
|
await store.move(readId(requestParams), requestParams.status, requestParams.position),
|
|
),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.delete",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.delete(readId(requestParams)));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.comment",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addComment(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.link",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addLink(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.linkDependency",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const parentId = requestParams.parentId;
|
|
const childId = requestParams.childId;
|
|
if (typeof parentId !== "string" || typeof childId !== "string") {
|
|
throw new Error("parentId and childId are required.");
|
|
}
|
|
respond(true, {
|
|
card: redactClaimToken(await store.linkCards(parentId, childId)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.proof",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addProof(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.artifact",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addArtifact(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.claim",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const claimed = await store.claim(readId(requestParams), requestParams);
|
|
respond(true, { ...claimed, card: redactClaimToken(claimed.card) });
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.heartbeat",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.heartbeat(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.release",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.releaseClaim(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.promote",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.promote(readId(requestParams), requestParams, null)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.reassign",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.reassign(readId(requestParams), requestParams, null)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.reclaim",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.reclaim(readId(requestParams), requestParams, null)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.complete",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.complete(readId(requestParams), requestParams, null)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.block",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.block(readId(requestParams), requestParams, null)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.unblock",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.unblock(readId(requestParams))),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
registerWorkboardWorkspaceBulkMethod({ api, store, redactCard: redactClaimToken });
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.diagnostics",
|
|
async ({ respond }) => {
|
|
try {
|
|
respond(true, redactDiagnosticsRows(await store.diagnostics()));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.diagnostics.refresh",
|
|
async ({ respond }) => {
|
|
try {
|
|
respond(true, redactDiagnosticsRows(await store.refreshDiagnostics()));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.dispatch",
|
|
async (context) => await dispatchCards(context, { supportsMaxStarts: false }),
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.dispatchWithOptions",
|
|
async (context) => await dispatchCards(context, { supportsMaxStarts: true }),
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.boards.list",
|
|
async ({ respond }) => {
|
|
try {
|
|
respond(true, await store.listBoards());
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
registerWorkboardWorkspaceBoardMethod({ api, store, redactCard: redactClaimToken });
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.boards.archive",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
board: await store.archiveBoard(requestParams.id, requestParams.archived),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.boards.delete",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.deleteBoard(requestParams.id));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.stats",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.stats({ boardId: requestParams.boardId }));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.runs",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const result = await store.runs(readId(requestParams));
|
|
respond(true, { ...result, card: redactClaimToken(result.card) });
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
registerWorkboardWorkspaceWorkflowMethods({ api, store, redactCard: redactClaimToken });
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.notifications.subscribe",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, { subscription: await store.subscribeNotifications(requestParams) });
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.notifications.list",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.listNotificationSubscriptions(requestParams));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.notifications.delete",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.deleteNotificationSubscription(readId(requestParams)));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.notifications.events",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
assertNoCursorAdvance(requestParams);
|
|
respond(true, await store.notificationEvents(requestParams));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.notifications.advance",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, await store.advanceNotificationEvents(requestParams));
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.attachments.list",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const result = await store.listAttachments(readId(requestParams));
|
|
respond(true, { ...result, card: redactClaimToken(result.card) });
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.attachments.get",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const attachment = await store.getAttachment(readId(requestParams));
|
|
if (!attachment) {
|
|
throw new Error(`attachment not found: ${readId(requestParams)}`);
|
|
}
|
|
respond(true, attachment);
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.attachments.add",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addAttachment(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.attachments.delete",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
const attachmentId = requestParams.attachmentId;
|
|
if (typeof attachmentId !== "string" || !attachmentId.trim()) {
|
|
throw new Error("attachmentId is required.");
|
|
}
|
|
respond(true, {
|
|
card: redactClaimToken(
|
|
await store.deleteAttachment(readId(requestParams), attachmentId.trim()),
|
|
),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.workerLog",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(await store.addWorkerLog(readId(requestParams), requestParams)),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.protocolViolation",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(
|
|
await store.recordProtocolViolation(readId(requestParams), requestParams),
|
|
),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.archive",
|
|
async ({ params: requestParams, respond }) => {
|
|
try {
|
|
respond(true, {
|
|
card: redactClaimToken(
|
|
await store.archive(readId(requestParams), requestParams.archived),
|
|
),
|
|
});
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: WRITE_SCOPE },
|
|
);
|
|
|
|
api.registerGatewayMethod(
|
|
"workboard.cards.export",
|
|
async ({ respond }) => {
|
|
try {
|
|
const exported = await store.exportCards();
|
|
respond(true, { ...exported, cards: exported.cards.map(redactClaimToken) });
|
|
} catch (error) {
|
|
respondError(respond, error);
|
|
}
|
|
},
|
|
{ scope: READ_SCOPE },
|
|
);
|
|
}
|