Files
openclaw/extensions/workboard/src/gateway-workspace-methods.ts
Dallin Romney 561cf56c53 refactor(workboard): share control ui contract (#102271)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-14 02:33:22 -07:00

177 lines
5.7 KiB
TypeScript

import type { WorkboardCard } from "@openclaw/workboard-contract";
// Workboard Gateway methods that can persist workspace-bearing card metadata.
import type { OpenClawPluginApi } from "../api.js";
import {
readId,
readPatch,
resolveGatewayWorkboardWorkspaceAccess,
respondError,
type GatewayMethodContext,
} from "./gateway-helpers.js";
import type { WorkboardStore } from "./store.js";
import {
assertWorkboardWorkspaceMutationAccess,
canonicalizeWorkboardWorkspaceAccess,
containsWorkboardWorkspaceMutation,
withWorkboardDecomposeWorkspaceAccess,
withWorkboardWorkspaceAccess,
withoutWorkboardWorkspaceAccess,
type WorkboardWorkspaceAccess,
} from "./workspace-access.js";
const WRITE_SCOPE = "operator.write" as const;
async function resolveGatewayWorkspaceMutationAccess(
request: GatewayMethodContext,
value: unknown,
): Promise<WorkboardWorkspaceAccess> {
const access = await canonicalizeWorkboardWorkspaceAccess(
resolveGatewayWorkboardWorkspaceAccess({
context: request.context,
client: request.client,
}),
);
await assertWorkboardWorkspaceMutationAccess(value, access);
return access;
}
type WorkspaceGatewayMethodParams = {
api: OpenClawPluginApi;
store: WorkboardStore;
redactCard: (card: WorkboardCard) => WorkboardCard;
};
export function registerWorkboardWorkspaceCardMethods(params: WorkspaceGatewayMethodParams): void {
const { api, store, redactCard } = params;
api.registerGatewayMethod(
"workboard.cards.create",
async (request) => {
const { params: requestParams, respond } = request;
try {
const input = withoutWorkboardWorkspaceAccess(requestParams);
const access = await resolveGatewayWorkspaceMutationAccess(request, input);
respond(true, {
card: redactCard(await store.create(withWorkboardWorkspaceAccess(input, access))),
});
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
api.registerGatewayMethod(
"workboard.cards.update",
async (request) => {
const { params: requestParams, respond } = request;
try {
const patch = withoutWorkboardWorkspaceAccess(readPatch(requestParams));
const access = await resolveGatewayWorkspaceMutationAccess(request, patch);
respond(true, {
card: redactCard(
await store.update(
readId(requestParams),
containsWorkboardWorkspaceMutation(patch)
? withWorkboardWorkspaceAccess(patch, access)
: patch,
),
),
});
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
}
export function registerWorkboardWorkspaceBulkMethod(params: WorkspaceGatewayMethodParams): void {
const { api, store, redactCard } = params;
api.registerGatewayMethod(
"workboard.cards.bulk",
async (request) => {
const { params: requestParams, respond } = request;
try {
const sanitizedParams = withoutWorkboardWorkspaceAccess(requestParams);
const patch = withoutWorkboardWorkspaceAccess(readPatch(requestParams));
const access = await resolveGatewayWorkspaceMutationAccess(request, patch);
const result = await store.bulkUpdate({
...sanitizedParams,
patch: containsWorkboardWorkspaceMutation(patch)
? withWorkboardWorkspaceAccess(patch, access)
: patch,
});
respond(true, { cards: result.cards.map(redactCard) });
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
}
export function registerWorkboardWorkspaceBoardMethod(params: WorkspaceGatewayMethodParams): void {
const { api, store } = params;
api.registerGatewayMethod(
"workboard.boards.upsert",
async (request) => {
const { params: requestParams, respond } = request;
try {
await resolveGatewayWorkspaceMutationAccess(request, requestParams);
respond(true, { board: await store.upsertBoard(requestParams) });
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
}
export function registerWorkboardWorkspaceWorkflowMethods(
params: WorkspaceGatewayMethodParams,
): void {
const { api, store, redactCard } = params;
api.registerGatewayMethod(
"workboard.cards.specify",
async (request) => {
const { params: requestParams, respond } = request;
try {
const sanitizedParams = withoutWorkboardWorkspaceAccess(requestParams);
const access = await resolveGatewayWorkspaceMutationAccess(request, sanitizedParams);
const input = containsWorkboardWorkspaceMutation(sanitizedParams)
? withWorkboardWorkspaceAccess(sanitizedParams, access)
: sanitizedParams;
respond(true, {
card: redactCard(await store.specify(readId(requestParams), input, null)),
});
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
api.registerGatewayMethod(
"workboard.cards.decompose",
async (request) => {
const { params: requestParams, respond } = request;
try {
const sanitizedParams = withoutWorkboardWorkspaceAccess(requestParams);
const access = await resolveGatewayWorkspaceMutationAccess(request, sanitizedParams);
const result = await store.decompose(
readId(requestParams),
withWorkboardDecomposeWorkspaceAccess(sanitizedParams, access),
null,
);
respond(true, {
parent: redactCard(result.parent),
children: result.children.map(redactCard),
});
} catch (error) {
respondError(respond, error);
}
},
{ scope: WRITE_SCOPE },
);
}