refactor(imessage): share setup wizard base

This commit is contained in:
Peter Steinberger
2026-03-17 03:40:41 +00:00
parent a0e7e3c3cd
commit 9c48321176
2 changed files with 55 additions and 149 deletions

View File

@@ -155,9 +155,18 @@ export const imessageSetupAdapter: ChannelSetupAdapter = {
},
};
export function createIMessageSetupWizardProxy(
loadWizard: () => Promise<{ imessageSetupWizard: ChannelSetupWizard }>,
) {
type IMessageSetupWizardHandlers = {
resolveStatusLines: NonNullable<ChannelSetupWizard["status"]>["resolveStatusLines"];
resolveSelectionHint: NonNullable<ChannelSetupWizard["status"]>["resolveSelectionHint"];
resolveQuickstartScore: NonNullable<ChannelSetupWizard["status"]>["resolveQuickstartScore"];
shouldPromptCliPath: NonNullable<
NonNullable<ChannelSetupWizard["textInputs"]>[number]["shouldPrompt"]
>;
};
export function createIMessageSetupWizardBase(
handlers: IMessageSetupWizardHandlers,
): ChannelSetupWizard {
const imessageDmPolicy: ChannelSetupDmPolicy = {
label: "iMessage",
channel,
@@ -193,12 +202,9 @@ export function createIMessageSetupWizardProxy(
account.config.region,
);
}),
resolveStatusLines: async (params) =>
(await loadWizard()).imessageSetupWizard.status.resolveStatusLines?.(params) ?? [],
resolveSelectionHint: async (params) =>
await (await loadWizard()).imessageSetupWizard.status.resolveSelectionHint?.(params),
resolveQuickstartScore: async (params) =>
await (await loadWizard()).imessageSetupWizard.status.resolveQuickstartScore?.(params),
resolveStatusLines: handlers.resolveStatusLines,
resolveSelectionHint: handlers.resolveSelectionHint,
resolveQuickstartScore: handlers.resolveQuickstartScore,
},
credentials: [],
textInputs: [
@@ -209,12 +215,7 @@ export function createIMessageSetupWizardProxy(
resolveIMessageAccount({ cfg, accountId }).config.cliPath ?? "imsg",
currentValue: ({ cfg, accountId }) =>
resolveIMessageAccount({ cfg, accountId }).config.cliPath ?? "imsg",
shouldPrompt: async (params) => {
const input = (await loadWizard()).imessageSetupWizard.textInputs?.find(
(entry) => entry.inputKey === "cliPath",
);
return (await input?.shouldPrompt?.(params)) ?? false;
},
shouldPrompt: handlers.shouldPromptCliPath,
confirmCurrentValue: false,
applyCurrentValue: true,
helpTitle: "iMessage",
@@ -235,3 +236,22 @@ export function createIMessageSetupWizardProxy(
disable: (cfg: OpenClawConfig) => setSetupChannelEnabled(cfg, channel, false),
} satisfies ChannelSetupWizard;
}
export function createIMessageSetupWizardProxy(
loadWizard: () => Promise<{ imessageSetupWizard: ChannelSetupWizard }>,
) {
return createIMessageSetupWizardBase({
resolveStatusLines: async (params) =>
(await loadWizard()).imessageSetupWizard.status.resolveStatusLines?.(params) ?? [],
resolveSelectionHint: async (params) =>
await (await loadWizard()).imessageSetupWizard.status.resolveSelectionHint?.(params),
resolveQuickstartScore: async (params) =>
await (await loadWizard()).imessageSetupWizard.status.resolveQuickstartScore?.(params),
shouldPromptCliPath: async (params) => {
const input = (await loadWizard()).imessageSetupWizard.textInputs?.find(
(entry) => entry.inputKey === "cliPath",
);
return (await input?.shouldPrompt?.(params)) ?? false;
},
});
}

View File

@@ -1,137 +1,23 @@
import {
DEFAULT_ACCOUNT_ID,
detectBinary,
formatDocsLink,
type OpenClawConfig,
parseSetupEntriesAllowingWildcard,
promptParsedAllowFromForScopedChannel,
setChannelDmPolicyWithAllowFrom,
setSetupChannelEnabled,
type WizardPrompter,
} from "../../../src/plugin-sdk-internal/setup.js";
import type {
ChannelSetupDmPolicy,
ChannelSetupWizard,
} from "../../../src/plugin-sdk-internal/setup.js";
import {
listIMessageAccountIds,
resolveDefaultIMessageAccountId,
resolveIMessageAccount,
} from "./accounts.js";
import { imessageSetupAdapter, parseIMessageAllowFromEntries } from "./setup-core.js";
import { detectBinary } from "../../../src/plugin-sdk-internal/setup.js";
import { createIMessageSetupWizardBase, imessageSetupAdapter } from "./setup-core.js";
const channel = "imessage" as const;
async function promptIMessageAllowFrom(params: {
cfg: OpenClawConfig;
prompter: WizardPrompter;
accountId?: string;
}): Promise<OpenClawConfig> {
return promptParsedAllowFromForScopedChannel({
cfg: params.cfg,
channel,
accountId: params.accountId,
defaultAccountId: resolveDefaultIMessageAccountId(params.cfg),
prompter: params.prompter,
noteTitle: "iMessage allowlist",
noteLines: [
"Allowlist iMessage DMs by handle or chat target.",
"Examples:",
"- +15555550123",
"- user@example.com",
"- chat_id:123",
"- chat_guid:... or chat_identifier:...",
"Multiple entries: comma-separated.",
`Docs: ${formatDocsLink("/imessage", "imessage")}`,
],
message: "iMessage allowFrom (handle or chat_id)",
placeholder: "+15555550123, user@example.com, chat_id:123",
parseEntries: parseIMessageAllowFromEntries,
getExistingAllowFrom: ({ cfg, accountId }) =>
resolveIMessageAccount({ cfg, accountId }).config.allowFrom ?? [],
});
}
const imessageDmPolicy: ChannelSetupDmPolicy = {
label: "iMessage",
channel,
policyKey: "channels.imessage.dmPolicy",
allowFromKey: "channels.imessage.allowFrom",
getCurrent: (cfg) => cfg.channels?.imessage?.dmPolicy ?? "pairing",
setPolicy: (cfg, policy) =>
setChannelDmPolicyWithAllowFrom({
cfg,
channel,
dmPolicy: policy,
}),
promptAllowFrom: promptIMessageAllowFrom,
};
export const imessageSetupWizard: ChannelSetupWizard = {
channel,
status: {
configuredLabel: "configured",
unconfiguredLabel: "needs setup",
configuredHint: "imsg found",
unconfiguredHint: "imsg missing",
configuredScore: 1,
unconfiguredScore: 0,
resolveConfigured: ({ cfg }) =>
listIMessageAccountIds(cfg).some((accountId) => {
const account = resolveIMessageAccount({ cfg, accountId });
return Boolean(
account.config.cliPath ||
account.config.dbPath ||
account.config.allowFrom ||
account.config.service ||
account.config.region,
);
}),
resolveStatusLines: async ({ cfg, configured }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
const cliDetected = await detectBinary(cliPath);
return [
`iMessage: ${configured ? "configured" : "needs setup"}`,
`imsg: ${cliDetected ? "found" : "missing"} (${cliPath})`,
];
},
resolveSelectionHint: async ({ cfg }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
return (await detectBinary(cliPath)) ? "imsg found" : "imsg missing";
},
resolveQuickstartScore: async ({ cfg }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
return (await detectBinary(cliPath)) ? 1 : 0;
},
export const imessageSetupWizard = createIMessageSetupWizardBase({
resolveStatusLines: async ({ cfg, configured }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
const cliDetected = await detectBinary(cliPath);
return [
`iMessage: ${configured ? "configured" : "needs setup"}`,
`imsg: ${cliDetected ? "found" : "missing"} (${cliPath})`,
];
},
credentials: [],
textInputs: [
{
inputKey: "cliPath",
message: "imsg CLI path",
initialValue: ({ cfg, accountId }) =>
resolveIMessageAccount({ cfg, accountId }).config.cliPath ?? "imsg",
currentValue: ({ cfg, accountId }) =>
resolveIMessageAccount({ cfg, accountId }).config.cliPath ?? "imsg",
shouldPrompt: async ({ currentValue }) => !(await detectBinary(currentValue ?? "imsg")),
confirmCurrentValue: false,
applyCurrentValue: true,
helpTitle: "iMessage",
helpLines: ["imsg CLI path required to enable iMessage."],
},
],
completionNote: {
title: "iMessage next steps",
lines: [
"This is still a work in progress.",
"Ensure OpenClaw has Full Disk Access to Messages DB.",
"Grant Automation permission for Messages when prompted.",
"List chats with: imsg chats --limit 20",
`Docs: ${formatDocsLink("/imessage", "imessage")}`,
],
resolveSelectionHint: async ({ cfg }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
return (await detectBinary(cliPath)) ? "imsg found" : "imsg missing";
},
dmPolicy: imessageDmPolicy,
disable: (cfg) => setSetupChannelEnabled(cfg, channel, false),
};
export { imessageSetupAdapter, parseIMessageAllowFromEntries };
resolveQuickstartScore: async ({ cfg }) => {
const cliPath = cfg.channels?.imessage?.cliPath ?? "imsg";
return (await detectBinary(cliPath)) ? 1 : 0;
},
shouldPromptCliPath: async ({ currentValue }) => !(await detectBinary(currentValue ?? "imsg")),
});
export { imessageSetupAdapter, parseIMessageAllowFromEntries } from "./setup-core.js";