Skip to content

Commit dcd5473

Browse files
committed
refactor: adopt shared chat plugin builder
1 parent 9946277 commit dcd5473

6 files changed

Lines changed: 412 additions & 391 deletions

File tree

docs/.generated/plugin-sdk-api-baseline.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3059,7 +3059,7 @@
30593059
}
30603060
},
30613061
{
3062-
"declaration": "export function defineChannelPluginEntry<TPlugin extends ChannelPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;",
3062+
"declaration": "export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;",
30633063
"exportName": "defineChannelPluginEntry",
30643064
"kind": "function",
30653065
"source": {

docs/.generated/plugin-sdk-api-baseline.jsonl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@
336336
{"declaration":"export function clearAccountEntryFields<TAccountEntry extends object>(params: { accounts?: Record<string, TAccountEntry> | undefined; accountId: string; fields: string[]; isValueSet?: ((value: unknown) => boolean) | undefined; markClearedOnFieldPresence?: boolean | undefined; }): { ...; };","entrypoint":"core","exportName":"clearAccountEntryFields","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":122,"sourcePath":"src/channels/plugins/config-helpers.ts"}
337337
{"declaration":"export function createChannelPluginBase<TResolvedAccount>(params: CreateChannelPluginBaseOptions<TResolvedAccount>): CreatedChannelPluginBase<TResolvedAccount>;","entrypoint":"core","exportName":"createChannelPluginBase","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":435,"sourcePath":"src/plugin-sdk/core.ts"}
338338
{"declaration":"export function createChatChannelPlugin<TResolvedAccount extends { accountId?: string | null; }, Probe = unknown, Audit = unknown>(params: { base: ChatChannelPluginBase<TResolvedAccount, Probe, Audit>; security?: ChannelSecurityAdapter<TResolvedAccount> | ChatChannelSecurityOptions<...> | undefined; pairing?: ChannelPairingAdapter | ... 1 more ... | undefined; threading?: ChannelThreadingAdapter | ... 1 more ... | undefined; outbound?: ChannelOutboundAdapter | ... 1 more ... | undefined; }): ChannelPlugin<...>;","entrypoint":"core","exportName":"createChatChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":412,"sourcePath":"src/plugin-sdk/core.ts"}
339-
{"declaration":"export function defineChannelPluginEntry<TPlugin extends ChannelPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":244,"sourcePath":"src/plugin-sdk/core.ts"}
339+
{"declaration":"export function defineChannelPluginEntry<TPlugin>({ id, name, description, plugin, configSchema, setRuntime, registerFull, }: DefineChannelPluginEntryOptions<TPlugin>): DefinedPluginEntry;","entrypoint":"core","exportName":"defineChannelPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":244,"sourcePath":"src/plugin-sdk/core.ts"}
340340
{"declaration":"export function definePluginEntry({ id, name, description, kind, configSchema, register, }: DefinePluginEntryOptions): DefinedPluginEntry;","entrypoint":"core","exportName":"definePluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":88,"sourcePath":"src/plugin-sdk/plugin-entry.ts"}
341341
{"declaration":"export function defineSetupPluginEntry<TPlugin>(plugin: TPlugin): { plugin: TPlugin; };","entrypoint":"core","exportName":"defineSetupPluginEntry","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":275,"sourcePath":"src/plugin-sdk/core.ts"}
342342
{"declaration":"export function delegateCompactionToRuntime(params: { sessionId: string; sessionKey?: string | undefined; sessionFile: string; tokenBudget?: number | undefined; force?: boolean | undefined; currentTokenCount?: number | undefined; compactionTarget?: \"budget\" | ... 1 more ... | undefined; customInstructions?: string | undefined; runtimeContext?: ContextEngineRuntimeContext | undefined; }): Promise<...>;","entrypoint":"core","exportName":"delegateCompactionToRuntime","importSpecifier":"openclaw/plugin-sdk/core","kind":"function","recordType":"export","sourceLine":16,"sourcePath":"src/context-engine/delegate.ts"}

extensions/imessage/src/channel.ts

Lines changed: 128 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { buildDmGroupAccountAllowlistAdapter } from "openclaw/plugin-sdk/allowlist-config-edit";
2-
import { createAttachedChannelResultAdapter } from "openclaw/plugin-sdk/channel-send-result";
2+
import { createChatChannelPlugin } from "openclaw/plugin-sdk/core";
33
import { buildPassiveProbedChannelStatusSummary } from "openclaw/plugin-sdk/extension-shared";
44
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
55
import { resolveOutboundSendDep } from "openclaw/plugin-sdk/outbound-runtime";
@@ -17,6 +17,7 @@ import {
1717
resolveIMessageGroupRequireMention,
1818
resolveIMessageGroupToolPolicy,
1919
} from "./group-policy.js";
20+
import type { IMessageProbe } from "./probe.js";
2021
import { getIMessageRuntime } from "./runtime.js";
2122
import { imessageSetupAdapter } from "./setup-core.js";
2223
import {
@@ -104,124 +105,138 @@ function resolveIMessageOutboundSessionRoute(params: {
104105
};
105106
}
106107

107-
export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount> = {
108-
...createIMessagePluginBase({
109-
setupWizard: imessageSetupWizard,
110-
setup: imessageSetupAdapter,
111-
}),
112-
pairing: {
113-
idLabel: "imessageSenderId",
114-
notifyApproval: async ({ id }) =>
115-
await (await loadIMessageChannelRuntime()).notifyIMessageApproval(id),
116-
},
117-
allowlist: buildDmGroupAccountAllowlistAdapter({
118-
channelId: "imessage",
119-
resolveAccount: resolveIMessageAccount,
120-
normalize: ({ values }) => formatTrimmedAllowFromEntries(values),
121-
resolveDmAllowFrom: (account) => account.config.allowFrom,
122-
resolveGroupAllowFrom: (account) => account.config.groupAllowFrom,
123-
resolveDmPolicy: (account) => account.config.dmPolicy,
124-
resolveGroupPolicy: (account) => account.config.groupPolicy,
125-
}),
126-
security: imessageSecurityAdapter,
127-
groups: {
128-
resolveRequireMention: resolveIMessageGroupRequireMention,
129-
resolveToolPolicy: resolveIMessageGroupToolPolicy,
130-
},
131-
messaging: {
132-
normalizeTarget: normalizeIMessageMessagingTarget,
133-
inferTargetChatType: ({ to }) => inferIMessageTargetChatType(to),
134-
resolveOutboundSessionRoute: (params) => resolveIMessageOutboundSessionRoute(params),
135-
targetResolver: {
136-
looksLikeId: looksLikeIMessageExplicitTargetId,
137-
hint: "<handle|chat_id:ID>",
138-
resolveTarget: async ({ normalized }) => {
139-
const to = normalized?.trim();
140-
if (!to) {
141-
return null;
142-
}
143-
const chatType = inferIMessageTargetChatType(to);
144-
if (!chatType) {
145-
return null;
146-
}
147-
return {
148-
to,
149-
kind: chatType === "direct" ? "user" : "group",
150-
source: "normalized" as const,
151-
};
108+
export const imessagePlugin: ChannelPlugin<ResolvedIMessageAccount, IMessageProbe> =
109+
createChatChannelPlugin({
110+
base: {
111+
...createIMessagePluginBase({
112+
setupWizard: imessageSetupWizard,
113+
setup: imessageSetupAdapter,
114+
}),
115+
allowlist: buildDmGroupAccountAllowlistAdapter({
116+
channelId: "imessage",
117+
resolveAccount: resolveIMessageAccount,
118+
normalize: ({ values }) => formatTrimmedAllowFromEntries(values),
119+
resolveDmAllowFrom: (account) => account.config.allowFrom,
120+
resolveGroupAllowFrom: (account) => account.config.groupAllowFrom,
121+
resolveDmPolicy: (account) => account.config.dmPolicy,
122+
resolveGroupPolicy: (account) => account.config.groupPolicy,
123+
}),
124+
groups: {
125+
resolveRequireMention: resolveIMessageGroupRequireMention,
126+
resolveToolPolicy: resolveIMessageGroupToolPolicy,
127+
},
128+
messaging: {
129+
normalizeTarget: normalizeIMessageMessagingTarget,
130+
inferTargetChatType: ({ to }) => inferIMessageTargetChatType(to),
131+
resolveOutboundSessionRoute: (params) => resolveIMessageOutboundSessionRoute(params),
132+
targetResolver: {
133+
looksLikeId: looksLikeIMessageExplicitTargetId,
134+
hint: "<handle|chat_id:ID>",
135+
resolveTarget: async ({ normalized }) => {
136+
const to = normalized?.trim();
137+
if (!to) {
138+
return null;
139+
}
140+
const chatType = inferIMessageTargetChatType(to);
141+
if (!chatType) {
142+
return null;
143+
}
144+
return {
145+
to,
146+
kind: chatType === "direct" ? "user" : "group",
147+
source: "normalized" as const,
148+
};
149+
},
150+
},
151+
},
152+
status: {
153+
defaultRuntime: {
154+
accountId: DEFAULT_ACCOUNT_ID,
155+
running: false,
156+
lastStartAt: null,
157+
lastStopAt: null,
158+
lastError: null,
159+
cliPath: null,
160+
dbPath: null,
161+
},
162+
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("imessage", accounts),
163+
buildChannelSummary: ({ snapshot }) =>
164+
buildPassiveProbedChannelStatusSummary(snapshot, {
165+
cliPath: snapshot.cliPath ?? null,
166+
dbPath: snapshot.dbPath ?? null,
167+
}),
168+
probeAccount: async ({ timeoutMs }) =>
169+
await (await loadIMessageChannelRuntime()).probeIMessageAccount(timeoutMs),
170+
buildAccountSnapshot: ({ account, runtime, probe }) =>
171+
buildComputedAccountStatusSnapshot(
172+
{
173+
accountId: account.accountId,
174+
name: account.name,
175+
enabled: account.enabled,
176+
configured: account.configured,
177+
runtime,
178+
probe,
179+
},
180+
{
181+
cliPath: runtime?.cliPath ?? account.config.cliPath ?? null,
182+
dbPath: runtime?.dbPath ?? account.config.dbPath ?? null,
183+
},
184+
),
185+
resolveAccountState: ({ enabled }) => (enabled ? "enabled" : "disabled"),
186+
},
187+
gateway: {
188+
startAccount: async (ctx) =>
189+
await (await loadIMessageChannelRuntime()).startIMessageGatewayAccount(ctx),
152190
},
153191
},
154-
},
155-
outbound: {
156-
deliveryMode: "direct",
157-
chunker: (text, limit) => getIMessageRuntime().channel.text.chunkText(text, limit),
158-
chunkerMode: "text",
159-
textChunkLimit: 4000,
160-
...createAttachedChannelResultAdapter({
161-
channel: "imessage",
162-
sendText: async ({ cfg, to, text, accountId, deps, replyToId }) =>
163-
await (
164-
await loadIMessageChannelRuntime()
165-
).sendIMessageOutbound({
166-
cfg,
167-
to,
168-
text,
169-
accountId: accountId ?? undefined,
170-
deps,
171-
replyToId: replyToId ?? undefined,
172-
}),
173-
sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId, deps, replyToId }) =>
174-
await (
175-
await loadIMessageChannelRuntime()
176-
).sendIMessageOutbound({
192+
pairing: {
193+
idLabel: "imessageSenderId",
194+
notifyApproval: async ({ id }) =>
195+
await (await loadIMessageChannelRuntime()).notifyIMessageApproval(id),
196+
},
197+
security: imessageSecurityAdapter,
198+
outbound: {
199+
base: {
200+
deliveryMode: "direct",
201+
chunker: (text, limit) => getIMessageRuntime().channel.text.chunkText(text, limit),
202+
chunkerMode: "text",
203+
textChunkLimit: 4000,
204+
},
205+
attachedResults: {
206+
channel: "imessage",
207+
sendText: async ({ cfg, to, text, accountId, deps, replyToId }) =>
208+
await (
209+
await loadIMessageChannelRuntime()
210+
).sendIMessageOutbound({
211+
cfg,
212+
to,
213+
text,
214+
accountId: accountId ?? undefined,
215+
deps,
216+
replyToId: replyToId ?? undefined,
217+
}),
218+
sendMedia: async ({
177219
cfg,
178220
to,
179221
text,
180222
mediaUrl,
181223
mediaLocalRoots,
182-
accountId: accountId ?? undefined,
224+
accountId,
183225
deps,
184-
replyToId: replyToId ?? undefined,
185-
}),
186-
}),
187-
},
188-
status: {
189-
defaultRuntime: {
190-
accountId: DEFAULT_ACCOUNT_ID,
191-
running: false,
192-
lastStartAt: null,
193-
lastStopAt: null,
194-
lastError: null,
195-
cliPath: null,
196-
dbPath: null,
226+
replyToId,
227+
}) =>
228+
await (
229+
await loadIMessageChannelRuntime()
230+
).sendIMessageOutbound({
231+
cfg,
232+
to,
233+
text,
234+
mediaUrl,
235+
mediaLocalRoots,
236+
accountId: accountId ?? undefined,
237+
deps,
238+
replyToId: replyToId ?? undefined,
239+
}),
240+
},
197241
},
198-
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("imessage", accounts),
199-
buildChannelSummary: ({ snapshot }) =>
200-
buildPassiveProbedChannelStatusSummary(snapshot, {
201-
cliPath: snapshot.cliPath ?? null,
202-
dbPath: snapshot.dbPath ?? null,
203-
}),
204-
probeAccount: async ({ timeoutMs }) =>
205-
await (await loadIMessageChannelRuntime()).probeIMessageAccount(timeoutMs),
206-
buildAccountSnapshot: ({ account, runtime, probe }) =>
207-
buildComputedAccountStatusSnapshot(
208-
{
209-
accountId: account.accountId,
210-
name: account.name,
211-
enabled: account.enabled,
212-
configured: account.configured,
213-
runtime,
214-
probe,
215-
},
216-
{
217-
cliPath: runtime?.cliPath ?? account.config.cliPath ?? null,
218-
dbPath: runtime?.dbPath ?? account.config.dbPath ?? null,
219-
},
220-
),
221-
resolveAccountState: ({ enabled }) => (enabled ? "enabled" : "disabled"),
222-
},
223-
gateway: {
224-
startAccount: async (ctx) =>
225-
await (await loadIMessageChannelRuntime()).startIMessageGatewayAccount(ctx),
226-
},
227-
};
242+
});

0 commit comments

Comments
 (0)