Files
openclaw/extensions/raft/src/setup.ts
Peter Steinberger 9e4381eb1c refactor(channels): unify bundled channel setup contracts (#117106)
* refactor(channels): canonicalize bundled setup contracts

* test(matrix): use scoped environment fixtures
2026-07-31 16:57:29 -07:00

127 lines
4.0 KiB
TypeScript

import { defineChannelSetupContract } from "openclaw/plugin-sdk/channel-setup";
import type { ChannelPlugin } from "openclaw/plugin-sdk/core";
// Raft plugin setup owns only the Raft CLI profile, never Raft credentials.
import {
createPatchedAccountSetupAdapter,
type ChannelSetupInput,
} from "openclaw/plugin-sdk/setup";
import {
createDetectedBinaryStatus,
formatDocsLink,
setSetupChannelEnabled,
} from "openclaw/plugin-sdk/setup";
import { detectBinary } from "openclaw/plugin-sdk/setup-tools";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import {
listRaftAccountIds,
RAFT_CHANNEL_ID,
resolveDefaultRaftAccountId,
resolveRaftAccount,
type ResolvedRaftAccount,
} from "./accounts.js";
type RaftSetupInput = ChannelSetupInput & {
profile?: string;
};
const raftSetupAdapter = createPatchedAccountSetupAdapter({
channelKey: RAFT_CHANNEL_ID,
buildPatch: (input) => {
const profile = normalizeOptionalString((input as RaftSetupInput).profile);
return profile ? { profile } : {};
},
validateInput: ({ cfg, accountId, input }) => {
const setupInput = input as RaftSetupInput;
if (
normalizeOptionalString(setupInput.profile) ??
resolveRaftAccount({ cfg, accountId }).profile
) {
return null;
}
return "Raft requires a CLI profile.";
},
});
const raftSetupContract = defineChannelSetupContract({
fields: {
profile: {
kind: "string",
cli: { flags: "--profile <profile>", description: "Raft CLI profile" },
},
},
legacyAdapter: raftSetupAdapter,
});
export const raftSetupPlugin: ChannelPlugin<ResolvedRaftAccount> = {
id: RAFT_CHANNEL_ID,
meta: {
id: RAFT_CHANNEL_ID,
label: "Raft",
selectionLabel: "Raft (CLI wake bridge)",
docsPath: "/channels/raft",
docsLabel: "raft",
blurb: "Raft CLI wake bridge for human and agent collaboration.",
order: 72,
},
capabilities: {
chatTypes: ["direct"],
},
setupContract: raftSetupContract,
config: {
listAccountIds: listRaftAccountIds,
resolveAccount: (cfg, accountId) => resolveRaftAccount({ cfg, accountId }),
defaultAccountId: resolveDefaultRaftAccountId,
isConfigured: (account) => account.configured,
isEnabled: (account) => account.enabled,
},
setupWizard: {
channel: RAFT_CHANNEL_ID,
resolveShouldPromptAccountIds: () => false,
status: createDetectedBinaryStatus({
channelLabel: "Raft",
binaryLabel: "raft",
configuredLabel: "configured",
unconfiguredLabel: "needs a CLI profile",
configuredHint: "configured",
unconfiguredHint: "install and sign in to the Raft CLI",
configuredScore: 1,
unconfiguredScore: 4,
resolveConfigured: ({ cfg, accountId }) =>
accountId
? resolveRaftAccount({ cfg, accountId }).configured
: listRaftAccountIds(cfg).some(
(resolvedAccountId) =>
resolveRaftAccount({ cfg, accountId: resolvedAccountId }).configured,
),
resolveBinaryPath: () => "raft",
detectBinary,
}),
introNote: {
title: "Raft setup",
lines: [
"Create a Raft External Agent and sign in with the Raft CLI on this Gateway host.",
`Docs: ${formatDocsLink("/channels/raft", "channels/raft")}`,
],
},
credentials: [],
textInputs: [
{
inputKey: "profile",
message: "Raft CLI profile",
currentValue: ({ cfg, accountId }) =>
resolveRaftAccount({ cfg, accountId }).profile ?? undefined,
validate: ({ value }) => (normalizeOptionalString(value) ? undefined : "Required"),
normalizeValue: ({ value }) => normalizeOptionalString(value) ?? "",
},
],
completionNote: {
title: "Raft next steps",
lines: [
"Restart the Gateway, then send a Raft message to wake the agent.",
`Docs: ${formatDocsLink("/channels/raft", "channels/raft")}`,
],
},
disable: (cfg) => setSetupChannelEnabled(cfg, RAFT_CHANNEL_ID, false),
},
};