From 31a82259516c09d31340d66e46858750b588ea24 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Mar 2026 03:18:22 +0000 Subject: [PATCH] refactor(imessage): share plugin base config --- extensions/imessage/src/channel.setup.ts | 99 ++----------------- extensions/imessage/src/channel.ts | 103 ++------------------ extensions/imessage/src/shared.ts | 119 +++++++++++++++++++++++ 3 files changed, 137 insertions(+), 184 deletions(-) create mode 100644 extensions/imessage/src/shared.ts diff --git a/extensions/imessage/src/channel.setup.ts b/extensions/imessage/src/channel.setup.ts index 16d758931c2..5587914a0ce 100644 --- a/extensions/imessage/src/channel.setup.ts +++ b/extensions/imessage/src/channel.setup.ts @@ -1,94 +1,11 @@ -import { - buildAccountScopedDmSecurityPolicy, - collectAllowlistProviderRestrictSendersWarnings, -} from "../../../src/plugin-sdk-internal/channel-config.js"; -import { - buildChannelConfigSchema, - DEFAULT_ACCOUNT_ID, - deleteAccountFromConfigSection, - formatTrimmedAllowFromEntries, - getChatChannelMeta, - IMessageConfigSchema, - resolveIMessageConfigAllowFrom, - resolveIMessageConfigDefaultTo, - setAccountEnabledInConfigSection, - type ChannelPlugin, -} from "../../../src/plugin-sdk-internal/imessage.js"; -import { - listIMessageAccountIds, - resolveDefaultIMessageAccountId, - resolveIMessageAccount, - type ResolvedIMessageAccount, -} from "./accounts.js"; -import { imessageSetupWizard } from "./plugin-shared.js"; +import { type ChannelPlugin } from "openclaw/plugin-sdk/imessage"; +import { type ResolvedIMessageAccount } from "./accounts.js"; import { imessageSetupAdapter } from "./setup-core.js"; +import { createIMessagePluginBase, imessageSetupWizard } from "./shared.js"; -export const imessageSetupPlugin: ChannelPlugin = { - id: "imessage", - meta: { - ...getChatChannelMeta("imessage"), - aliases: ["imsg"], - showConfigured: false, +export const imessageSetupPlugin: ChannelPlugin = createIMessagePluginBase( + { + setupWizard: imessageSetupWizard, + setup: imessageSetupAdapter, }, - setupWizard: imessageSetupWizard, - capabilities: { - chatTypes: ["direct", "group"], - media: true, - }, - reload: { configPrefixes: ["channels.imessage"] }, - configSchema: buildChannelConfigSchema(IMessageConfigSchema), - config: { - listAccountIds: (cfg) => listIMessageAccountIds(cfg), - resolveAccount: (cfg, accountId) => resolveIMessageAccount({ cfg, accountId }), - defaultAccountId: (cfg) => resolveDefaultIMessageAccountId(cfg), - setAccountEnabled: ({ cfg, accountId, enabled }) => - setAccountEnabledInConfigSection({ - cfg, - sectionKey: "imessage", - accountId, - enabled, - allowTopLevel: true, - }), - deleteAccount: ({ cfg, accountId }) => - deleteAccountFromConfigSection({ - cfg, - sectionKey: "imessage", - accountId, - clearBaseFields: ["cliPath", "dbPath", "service", "region", "name"], - }), - isConfigured: (account) => account.configured, - describeAccount: (account) => ({ - accountId: account.accountId, - name: account.name, - enabled: account.enabled, - configured: account.configured, - }), - resolveAllowFrom: ({ cfg, accountId }) => resolveIMessageConfigAllowFrom({ cfg, accountId }), - formatAllowFrom: ({ allowFrom }) => formatTrimmedAllowFromEntries(allowFrom), - resolveDefaultTo: ({ cfg, accountId }) => resolveIMessageConfigDefaultTo({ cfg, accountId }), - }, - security: { - resolveDmPolicy: ({ cfg, accountId, account }) => - buildAccountScopedDmSecurityPolicy({ - cfg, - channelKey: "imessage", - accountId, - fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID, - policy: account.config.dmPolicy, - allowFrom: account.config.allowFrom ?? [], - policyPathSuffix: "dmPolicy", - }), - collectWarnings: ({ account, cfg }) => - collectAllowlistProviderRestrictSendersWarnings({ - cfg, - providerConfigPresent: cfg.channels?.imessage !== undefined, - configuredGroupPolicy: account.config.groupPolicy, - surface: "iMessage groups", - openScope: "any member", - groupPolicyPath: "channels.imessage.groupPolicy", - groupAllowFromPath: "channels.imessage.groupAllowFrom", - mentionGated: false, - }), - }, - setup: imessageSetupAdapter, -}; +); diff --git a/extensions/imessage/src/channel.ts b/extensions/imessage/src/channel.ts index 18ae103281a..95cac7d1123 100644 --- a/extensions/imessage/src/channel.ts +++ b/extensions/imessage/src/channel.ts @@ -1,43 +1,25 @@ -import { resolveOutboundSendDep } from "../../../src/infra/outbound/send-deps.js"; +import { buildAccountScopedAllowlistConfigEditor } from "openclaw/plugin-sdk/compat"; +import { buildAgentSessionKey, type RoutePeer } from "openclaw/plugin-sdk/core"; import { - buildAccountScopedAllowlistConfigEditor, - buildAccountScopedDmSecurityPolicy, - collectAllowlistProviderRestrictSendersWarnings, -} from "../../../src/plugin-sdk-internal/channel-config.js"; -import { buildAgentSessionKey, type RoutePeer } from "../../../src/plugin-sdk-internal/core.js"; -import { - buildChannelConfigSchema, collectStatusIssuesFromLastError, DEFAULT_ACCOUNT_ID, - deleteAccountFromConfigSection, formatTrimmedAllowFromEntries, - getChatChannelMeta, - IMessageConfigSchema, looksLikeIMessageTargetId, normalizeIMessageMessagingTarget, PAIRING_APPROVED_MESSAGE, resolveChannelMediaMaxBytes, - resolveIMessageConfigAllowFrom, - resolveIMessageConfigDefaultTo, resolveIMessageGroupRequireMention, resolveIMessageGroupToolPolicy, - setAccountEnabledInConfigSection, type ChannelPlugin, -} from "../../../src/plugin-sdk-internal/imessage.js"; +} from "openclaw/plugin-sdk/imessage"; +import { resolveOutboundSendDep } from "../../../src/infra/outbound/send-deps.js"; import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; -import { - listIMessageAccountIds, - resolveDefaultIMessageAccountId, - resolveIMessageAccount, - type ResolvedIMessageAccount, -} from "./accounts.js"; -import { imessageSetupWizard } from "./plugin-shared.js"; +import { resolveIMessageAccount, type ResolvedIMessageAccount } from "./accounts.js"; import { getIMessageRuntime } from "./runtime.js"; import { imessageSetupAdapter } from "./setup-core.js"; +import { createIMessagePluginBase, imessageSetupWizard } from "./shared.js"; import { normalizeIMessageHandle, parseIMessageTarget } from "./targets.js"; -const meta = getChatChannelMeta("imessage"); - type IMessageSendFn = ReturnType< typeof getIMessageRuntime >["channel"]["imessage"]["sendMessageIMessage"]; @@ -150,55 +132,16 @@ function resolveIMessageOutboundSessionRoute(params: { } export const imessagePlugin: ChannelPlugin = { - id: "imessage", - meta: { - ...meta, - aliases: ["imsg"], - showConfigured: false, - }, - setupWizard: imessageSetupWizard, + ...createIMessagePluginBase({ + setupWizard: imessageSetupWizard, + setup: imessageSetupAdapter, + }), pairing: { idLabel: "imessageSenderId", notifyApproval: async ({ id }) => { await getIMessageRuntime().channel.imessage.sendMessageIMessage(id, PAIRING_APPROVED_MESSAGE); }, }, - capabilities: { - chatTypes: ["direct", "group"], - media: true, - }, - reload: { configPrefixes: ["channels.imessage"] }, - configSchema: buildChannelConfigSchema(IMessageConfigSchema), - config: { - listAccountIds: (cfg) => listIMessageAccountIds(cfg), - resolveAccount: (cfg, accountId) => resolveIMessageAccount({ cfg, accountId }), - defaultAccountId: (cfg) => resolveDefaultIMessageAccountId(cfg), - setAccountEnabled: ({ cfg, accountId, enabled }) => - setAccountEnabledInConfigSection({ - cfg, - sectionKey: "imessage", - accountId, - enabled, - allowTopLevel: true, - }), - deleteAccount: ({ cfg, accountId }) => - deleteAccountFromConfigSection({ - cfg, - sectionKey: "imessage", - accountId, - clearBaseFields: ["cliPath", "dbPath", "service", "region", "name"], - }), - isConfigured: (account) => account.configured, - describeAccount: (account) => ({ - accountId: account.accountId, - name: account.name, - enabled: account.enabled, - configured: account.configured, - }), - resolveAllowFrom: ({ cfg, accountId }) => resolveIMessageConfigAllowFrom({ cfg, accountId }), - formatAllowFrom: ({ allowFrom }) => formatTrimmedAllowFromEntries(allowFrom), - resolveDefaultTo: ({ cfg, accountId }) => resolveIMessageConfigDefaultTo({ cfg, accountId }), - }, allowlist: { supportsScope: ({ scope }) => scope === "dm" || scope === "group" || scope === "all", readConfig: ({ cfg, accountId }) => { @@ -219,31 +162,6 @@ export const imessagePlugin: ChannelPlugin = { }), }), }, - security: { - resolveDmPolicy: ({ cfg, accountId, account }) => { - return buildAccountScopedDmSecurityPolicy({ - cfg, - channelKey: "imessage", - accountId, - fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID, - policy: account.config.dmPolicy, - allowFrom: account.config.allowFrom ?? [], - policyPathSuffix: "dmPolicy", - }); - }, - collectWarnings: ({ account, cfg }) => { - return collectAllowlistProviderRestrictSendersWarnings({ - cfg, - providerConfigPresent: cfg.channels?.imessage !== undefined, - configuredGroupPolicy: account.config.groupPolicy, - surface: "iMessage groups", - openScope: "any member", - groupPolicyPath: "channels.imessage.groupPolicy", - groupAllowFromPath: "channels.imessage.groupAllowFrom", - mentionGated: false, - }); - }, - }, groups: { resolveRequireMention: resolveIMessageGroupRequireMention, resolveToolPolicy: resolveIMessageGroupToolPolicy, @@ -256,7 +174,6 @@ export const imessagePlugin: ChannelPlugin = { hint: "", }, }, - setup: imessageSetupAdapter, outbound: { deliveryMode: "direct", chunker: (text, limit) => getIMessageRuntime().channel.text.chunkText(text, limit), diff --git a/extensions/imessage/src/shared.ts b/extensions/imessage/src/shared.ts new file mode 100644 index 00000000000..c4c62f20494 --- /dev/null +++ b/extensions/imessage/src/shared.ts @@ -0,0 +1,119 @@ +import { + buildAccountScopedDmSecurityPolicy, + collectAllowlistProviderRestrictSendersWarnings, +} from "openclaw/plugin-sdk/compat"; +import { + buildChannelConfigSchema, + DEFAULT_ACCOUNT_ID, + deleteAccountFromConfigSection, + formatTrimmedAllowFromEntries, + getChatChannelMeta, + IMessageConfigSchema, + resolveIMessageConfigAllowFrom, + resolveIMessageConfigDefaultTo, + setAccountEnabledInConfigSection, + type ChannelPlugin, +} from "openclaw/plugin-sdk/imessage"; +import { + listIMessageAccountIds, + resolveDefaultIMessageAccountId, + resolveIMessageAccount, + type ResolvedIMessageAccount, +} from "./accounts.js"; +import { createIMessageSetupWizardProxy } from "./setup-core.js"; + +export const IMESSAGE_CHANNEL = "imessage" as const; + +async function loadIMessageChannelRuntime() { + return await import("./channel.runtime.js"); +} + +export const imessageSetupWizard = createIMessageSetupWizardProxy(async () => ({ + imessageSetupWizard: (await loadIMessageChannelRuntime()).imessageSetupWizard, +})); + +export function createIMessagePluginBase(params: { + setupWizard?: NonNullable["setupWizard"]>; + setup: NonNullable["setup"]>; +}): Pick< + ChannelPlugin, + | "id" + | "meta" + | "setupWizard" + | "capabilities" + | "reload" + | "configSchema" + | "config" + | "security" + | "setup" +> { + return { + id: IMESSAGE_CHANNEL, + meta: { + ...getChatChannelMeta(IMESSAGE_CHANNEL), + aliases: ["imsg"], + showConfigured: false, + }, + setupWizard: params.setupWizard, + capabilities: { + chatTypes: ["direct", "group"], + media: true, + }, + reload: { configPrefixes: ["channels.imessage"] }, + configSchema: buildChannelConfigSchema(IMessageConfigSchema), + config: { + listAccountIds: (cfg) => listIMessageAccountIds(cfg), + resolveAccount: (cfg, accountId) => resolveIMessageAccount({ cfg, accountId }), + defaultAccountId: (cfg) => resolveDefaultIMessageAccountId(cfg), + setAccountEnabled: ({ cfg, accountId, enabled }) => + setAccountEnabledInConfigSection({ + cfg, + sectionKey: IMESSAGE_CHANNEL, + accountId, + enabled, + allowTopLevel: true, + }), + deleteAccount: ({ cfg, accountId }) => + deleteAccountFromConfigSection({ + cfg, + sectionKey: IMESSAGE_CHANNEL, + accountId, + clearBaseFields: ["cliPath", "dbPath", "service", "region", "name"], + }), + isConfigured: (account) => account.configured, + describeAccount: (account) => ({ + accountId: account.accountId, + name: account.name, + enabled: account.enabled, + configured: account.configured, + }), + resolveAllowFrom: ({ cfg, accountId }) => resolveIMessageConfigAllowFrom({ cfg, accountId }), + formatAllowFrom: ({ allowFrom }) => formatTrimmedAllowFromEntries(allowFrom), + resolveDefaultTo: ({ cfg, accountId }) => resolveIMessageConfigDefaultTo({ cfg, accountId }), + }, + security: { + resolveDmPolicy: ({ cfg, accountId, account }) => + buildAccountScopedDmSecurityPolicy({ + cfg, + channelKey: IMESSAGE_CHANNEL, + accountId, + fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID, + policy: account.config.dmPolicy, + allowFrom: account.config.allowFrom ?? [], + policyPathSuffix: "dmPolicy", + }), + collectWarnings: ({ account, cfg }) => + collectAllowlistProviderRestrictSendersWarnings({ + cfg, + providerConfigPresent: cfg.channels?.imessage !== undefined, + configuredGroupPolicy: account.config.groupPolicy, + surface: "iMessage groups", + openScope: "any member", + groupPolicyPath: "channels.imessage.groupPolicy", + groupAllowFromPath: "channels.imessage.groupAllowFrom", + mentionGated: false, + }), + }, + setup: params.setup, + }; +}