From 9648e7fecb282a5ea7d7b28c0760afafe07cb0b7 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 17 Mar 2026 00:58:46 -0700 Subject: [PATCH] refactor: consolidate lazy runtime surfaces --- extensions/bluebubbles/src/actions.runtime.ts | 98 ++------- extensions/bluebubbles/src/actions.ts | 11 +- extensions/bluebubbles/src/channel.runtime.ts | 54 +---- extensions/bluebubbles/src/channel.ts | 11 +- extensions/feishu/src/channel.runtime.ts | 120 ++--------- extensions/feishu/src/channel.test.ts | 38 ++-- extensions/feishu/src/channel.ts | 10 +- extensions/googlechat/src/channel.runtime.ts | 40 +--- extensions/googlechat/src/channel.ts | 10 +- extensions/matrix/src/channel.runtime.ts | 55 +---- extensions/matrix/src/channel.ts | 10 +- extensions/msteams/src/channel.runtime.ts | 47 +---- extensions/msteams/src/channel.ts | 10 +- extensions/zalo/src/actions.runtime.ts | 8 +- extensions/zalo/src/actions.ts | 11 +- src/agents/auth-profiles.runtime.ts | 10 +- src/agents/command-poll-backoff.runtime.ts | 10 +- src/agents/model-suppression.runtime.ts | 11 +- .../pi-embedded-runner/compact.runtime.ts | 10 +- .../pi-tools.before-tool-call.runtime.ts | 14 +- src/agents/pi-tools.before-tool-call.ts | 12 +- src/agents/skills/env-overrides.runtime.ts | 10 +- .../plugins/setup-wizard-helpers.runtime.ts | 9 +- src/cli/deps.ts | 4 +- src/commands/model-picker.runtime.ts | 14 +- src/commands/model-picker.test.ts | 12 +- src/commands/model-picker.ts | 14 +- .../auth-choice.plugin-providers.runtime.ts | 10 +- .../auth-choice.plugin-providers.test.ts | 9 +- .../local/auth-choice.plugin-providers.ts | 8 +- src/commands/status.scan.runtime.ts | 9 +- src/commands/status.scan.test.ts | 6 +- src/commands/status.scan.ts | 16 +- src/commands/status.summary.runtime.ts | 10 +- src/commands/status.summary.ts | 12 +- src/plugin-sdk/index.ts | 4 +- src/plugins/provider-api-key-auth.runtime.ts | 2 +- src/plugins/provider-api-key-auth.ts | 13 +- src/plugins/runtime/runtime-discord.ts | 188 +++++++++--------- src/plugins/runtime/runtime-slack.ts | 95 +++++---- src/plugins/runtime/runtime-telegram.ts | 154 +++++++------- src/plugins/runtime/runtime-whatsapp.ts | 58 +++--- src/security/audit-channel.collect.runtime.ts | 11 +- src/security/audit-channel.runtime.ts | 20 +- src/security/audit-channel.ts | 13 +- src/security/audit.runtime.ts | 10 +- src/shared/lazy-runtime.ts | 21 ++ 47 files changed, 602 insertions(+), 730 deletions(-) create mode 100644 src/shared/lazy-runtime.ts diff --git a/extensions/bluebubbles/src/actions.runtime.ts b/extensions/bluebubbles/src/actions.runtime.ts index 00d0bc00efd..6b4112547d1 100644 --- a/extensions/bluebubbles/src/actions.runtime.ts +++ b/extensions/bluebubbles/src/actions.runtime.ts @@ -15,87 +15,17 @@ import { sendMessageBlueBubbles as sendMessageBlueBubblesImpl, } from "./send.js"; -type SendBlueBubblesAttachment = typeof import("./attachments.js").sendBlueBubblesAttachment; -type AddBlueBubblesParticipant = typeof import("./chat.js").addBlueBubblesParticipant; -type EditBlueBubblesMessage = typeof import("./chat.js").editBlueBubblesMessage; -type LeaveBlueBubblesChat = typeof import("./chat.js").leaveBlueBubblesChat; -type RemoveBlueBubblesParticipant = typeof import("./chat.js").removeBlueBubblesParticipant; -type RenameBlueBubblesChat = typeof import("./chat.js").renameBlueBubblesChat; -type SetGroupIconBlueBubbles = typeof import("./chat.js").setGroupIconBlueBubbles; -type UnsendBlueBubblesMessage = typeof import("./chat.js").unsendBlueBubblesMessage; -type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId; -type SendBlueBubblesReaction = typeof import("./reactions.js").sendBlueBubblesReaction; -type ResolveChatGuidForTarget = typeof import("./send.js").resolveChatGuidForTarget; -type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles; - -export function sendBlueBubblesAttachment( - ...args: Parameters -): ReturnType { - return sendBlueBubblesAttachmentImpl(...args); -} - -export function addBlueBubblesParticipant( - ...args: Parameters -): ReturnType { - return addBlueBubblesParticipantImpl(...args); -} - -export function editBlueBubblesMessage( - ...args: Parameters -): ReturnType { - return editBlueBubblesMessageImpl(...args); -} - -export function leaveBlueBubblesChat( - ...args: Parameters -): ReturnType { - return leaveBlueBubblesChatImpl(...args); -} - -export function removeBlueBubblesParticipant( - ...args: Parameters -): ReturnType { - return removeBlueBubblesParticipantImpl(...args); -} - -export function renameBlueBubblesChat( - ...args: Parameters -): ReturnType { - return renameBlueBubblesChatImpl(...args); -} - -export function setGroupIconBlueBubbles( - ...args: Parameters -): ReturnType { - return setGroupIconBlueBubblesImpl(...args); -} - -export function unsendBlueBubblesMessage( - ...args: Parameters -): ReturnType { - return unsendBlueBubblesMessageImpl(...args); -} - -export function resolveBlueBubblesMessageId( - ...args: Parameters -): ReturnType { - return resolveBlueBubblesMessageIdImpl(...args); -} - -export function sendBlueBubblesReaction( - ...args: Parameters -): ReturnType { - return sendBlueBubblesReactionImpl(...args); -} - -export function resolveChatGuidForTarget( - ...args: Parameters -): ReturnType { - return resolveChatGuidForTargetImpl(...args); -} - -export function sendMessageBlueBubbles( - ...args: Parameters -): ReturnType { - return sendMessageBlueBubblesImpl(...args); -} +export const blueBubblesActionsRuntime = { + sendBlueBubblesAttachment: sendBlueBubblesAttachmentImpl, + addBlueBubblesParticipant: addBlueBubblesParticipantImpl, + editBlueBubblesMessage: editBlueBubblesMessageImpl, + leaveBlueBubblesChat: leaveBlueBubblesChatImpl, + removeBlueBubblesParticipant: removeBlueBubblesParticipantImpl, + renameBlueBubblesChat: renameBlueBubblesChatImpl, + setGroupIconBlueBubbles: setGroupIconBlueBubblesImpl, + unsendBlueBubblesMessage: unsendBlueBubblesMessageImpl, + resolveBlueBubblesMessageId: resolveBlueBubblesMessageIdImpl, + sendBlueBubblesReaction: sendBlueBubblesReactionImpl, + resolveChatGuidForTarget: resolveChatGuidForTargetImpl, + sendMessageBlueBubbles: sendMessageBlueBubblesImpl, +}; diff --git a/extensions/bluebubbles/src/actions.ts b/extensions/bluebubbles/src/actions.ts index 4e6476afa3f..47eedf97511 100644 --- a/extensions/bluebubbles/src/actions.ts +++ b/extensions/bluebubbles/src/actions.ts @@ -11,18 +11,19 @@ import { type ChannelMessageActionAdapter, type ChannelMessageActionName, } from "openclaw/plugin-sdk/bluebubbles"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { resolveBlueBubblesAccount } from "./accounts.js"; import { getCachedBlueBubblesPrivateApiStatus, isMacOS26OrHigher } from "./probe.js"; import { normalizeSecretInputString } from "./secret-input.js"; import { normalizeBlueBubblesHandle, parseBlueBubblesTarget } from "./targets.js"; import type { BlueBubblesSendTarget } from "./types.js"; -let actionsRuntimePromise: Promise | null = null; +type BlueBubblesActionsRuntime = typeof import("./actions.runtime.js").blueBubblesActionsRuntime; -function loadBlueBubblesActionsRuntime() { - actionsRuntimePromise ??= import("./actions.runtime.js"); - return actionsRuntimePromise; -} +const loadBlueBubblesActionsRuntime = createLazyRuntimeSurface( + () => import("./actions.runtime.js"), + ({ blueBubblesActionsRuntime }) => blueBubblesActionsRuntime, +); const providerId = "bluebubbles"; diff --git a/extensions/bluebubbles/src/channel.runtime.ts b/extensions/bluebubbles/src/channel.runtime.ts index d318943d3f2..b8b4066c4cd 100644 --- a/extensions/bluebubbles/src/channel.runtime.ts +++ b/extensions/bluebubbles/src/channel.runtime.ts @@ -6,52 +6,14 @@ import { } from "./monitor.js"; import { probeBlueBubbles as probeBlueBubblesImpl } from "./probe.js"; import { sendMessageBlueBubbles as sendMessageBlueBubblesImpl } from "./send.js"; -import { blueBubblesSetupWizard as blueBubblesSetupWizardImpl } from "./setup-surface.js"; export type { BlueBubblesProbe } from "./probe.js"; -type SendBlueBubblesMedia = typeof import("./media-send.js").sendBlueBubblesMedia; -type ResolveBlueBubblesMessageId = typeof import("./monitor.js").resolveBlueBubblesMessageId; -type MonitorBlueBubblesProvider = typeof import("./monitor.js").monitorBlueBubblesProvider; -type ResolveWebhookPathFromConfig = typeof import("./monitor.js").resolveWebhookPathFromConfig; -type ProbeBlueBubbles = typeof import("./probe.js").probeBlueBubbles; -type SendMessageBlueBubbles = typeof import("./send.js").sendMessageBlueBubbles; -type BlueBubblesSetupWizard = typeof import("./setup-surface.js").blueBubblesSetupWizard; - -export function sendBlueBubblesMedia( - ...args: Parameters -): ReturnType { - return sendBlueBubblesMediaImpl(...args); -} - -export function resolveBlueBubblesMessageId( - ...args: Parameters -): ReturnType { - return resolveBlueBubblesMessageIdImpl(...args); -} - -export function monitorBlueBubblesProvider( - ...args: Parameters -): ReturnType { - return monitorBlueBubblesProviderImpl(...args); -} - -export function resolveWebhookPathFromConfig( - ...args: Parameters -): ReturnType { - return resolveWebhookPathFromConfigImpl(...args); -} - -export function probeBlueBubbles( - ...args: Parameters -): ReturnType { - return probeBlueBubblesImpl(...args); -} - -export function sendMessageBlueBubbles( - ...args: Parameters -): ReturnType { - return sendMessageBlueBubblesImpl(...args); -} - -export const blueBubblesSetupWizard: BlueBubblesSetupWizard = { ...blueBubblesSetupWizardImpl }; +export const blueBubblesChannelRuntime = { + sendBlueBubblesMedia: sendBlueBubblesMediaImpl, + resolveBlueBubblesMessageId: resolveBlueBubblesMessageIdImpl, + monitorBlueBubblesProvider: monitorBlueBubblesProviderImpl, + resolveWebhookPathFromConfig: resolveWebhookPathFromConfigImpl, + probeBlueBubbles: probeBlueBubblesImpl, + sendMessageBlueBubbles: sendMessageBlueBubblesImpl, +}; diff --git a/extensions/bluebubbles/src/channel.ts b/extensions/bluebubbles/src/channel.ts index 9550c1166ed..f3f3cdd7eb3 100644 --- a/extensions/bluebubbles/src/channel.ts +++ b/extensions/bluebubbles/src/channel.ts @@ -18,6 +18,7 @@ import { buildAccountScopedDmSecurityPolicy, collectOpenGroupPolicyRestrictSendersWarnings, } from "openclaw/plugin-sdk/channel-policy"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { listBlueBubblesAccountIds, type ResolvedBlueBubblesAccount, @@ -37,12 +38,12 @@ import { parseBlueBubblesTarget, } from "./targets.js"; -let blueBubblesChannelRuntimePromise: Promise | null = null; +type BlueBubblesChannelRuntime = typeof import("./channel.runtime.js").blueBubblesChannelRuntime; -function loadBlueBubblesChannelRuntime() { - blueBubblesChannelRuntimePromise ??= import("./channel.runtime.js"); - return blueBubblesChannelRuntimePromise; -} +const loadBlueBubblesChannelRuntime = createLazyRuntimeSurface( + () => import("./channel.runtime.js"), + ({ blueBubblesChannelRuntime }) => blueBubblesChannelRuntime, +); const meta = { id: "bluebubbles", diff --git a/extensions/feishu/src/channel.runtime.ts b/extensions/feishu/src/channel.runtime.ts index c8a742942ea..ef13b721a4e 100644 --- a/extensions/feishu/src/channel.runtime.ts +++ b/extensions/feishu/src/channel.runtime.ts @@ -26,104 +26,22 @@ import { sendMessageFeishu as sendMessageFeishuImpl, } from "./send.js"; -type ListFeishuDirectoryGroupsLive = typeof import("./directory.js").listFeishuDirectoryGroupsLive; -type ListFeishuDirectoryPeersLive = typeof import("./directory.js").listFeishuDirectoryPeersLive; -type FeishuOutbound = typeof import("./outbound.js").feishuOutbound; -type CreatePinFeishu = typeof import("./pins.js").createPinFeishu; -type ListPinsFeishu = typeof import("./pins.js").listPinsFeishu; -type RemovePinFeishu = typeof import("./pins.js").removePinFeishu; -type ProbeFeishu = typeof import("./probe.js").probeFeishu; -type AddReactionFeishu = typeof import("./reactions.js").addReactionFeishu; -type ListReactionsFeishu = typeof import("./reactions.js").listReactionsFeishu; -type RemoveReactionFeishu = typeof import("./reactions.js").removeReactionFeishu; -type GetChatInfo = typeof import("./chat.js").getChatInfo; -type GetChatMembers = typeof import("./chat.js").getChatMembers; -type GetFeishuMemberInfo = typeof import("./chat.js").getFeishuMemberInfo; -type EditMessageFeishu = typeof import("./send.js").editMessageFeishu; -type GetMessageFeishu = typeof import("./send.js").getMessageFeishu; -type SendCardFeishu = typeof import("./send.js").sendCardFeishu; -type SendMessageFeishu = typeof import("./send.js").sendMessageFeishu; - -export function listFeishuDirectoryGroupsLive( - ...args: Parameters -): ReturnType { - return listFeishuDirectoryGroupsLiveImpl(...args); -} - -export function listFeishuDirectoryPeersLive( - ...args: Parameters -): ReturnType { - return listFeishuDirectoryPeersLiveImpl(...args); -} - -export const feishuOutbound: FeishuOutbound = { ...feishuOutboundImpl }; - -export function createPinFeishu(...args: Parameters): ReturnType { - return createPinFeishuImpl(...args); -} - -export function listPinsFeishu(...args: Parameters): ReturnType { - return listPinsFeishuImpl(...args); -} - -export function removePinFeishu(...args: Parameters): ReturnType { - return removePinFeishuImpl(...args); -} - -export function probeFeishu(...args: Parameters): ReturnType { - return probeFeishuImpl(...args); -} - -export function addReactionFeishu( - ...args: Parameters -): ReturnType { - return addReactionFeishuImpl(...args); -} - -export function listReactionsFeishu( - ...args: Parameters -): ReturnType { - return listReactionsFeishuImpl(...args); -} - -export function removeReactionFeishu( - ...args: Parameters -): ReturnType { - return removeReactionFeishuImpl(...args); -} - -export function getChatInfo(...args: Parameters): ReturnType { - return getChatInfoImpl(...args); -} - -export function getChatMembers(...args: Parameters): ReturnType { - return getChatMembersImpl(...args); -} - -export function getFeishuMemberInfo( - ...args: Parameters -): ReturnType { - return getFeishuMemberInfoImpl(...args); -} - -export function editMessageFeishu( - ...args: Parameters -): ReturnType { - return editMessageFeishuImpl(...args); -} - -export function getMessageFeishu( - ...args: Parameters -): ReturnType { - return getMessageFeishuImpl(...args); -} - -export function sendCardFeishu(...args: Parameters): ReturnType { - return sendCardFeishuImpl(...args); -} - -export function sendMessageFeishu( - ...args: Parameters -): ReturnType { - return sendMessageFeishuImpl(...args); -} +export const feishuChannelRuntime = { + listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveImpl, + listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveImpl, + feishuOutbound: { ...feishuOutboundImpl }, + createPinFeishu: createPinFeishuImpl, + listPinsFeishu: listPinsFeishuImpl, + removePinFeishu: removePinFeishuImpl, + probeFeishu: probeFeishuImpl, + addReactionFeishu: addReactionFeishuImpl, + listReactionsFeishu: listReactionsFeishuImpl, + removeReactionFeishu: removeReactionFeishuImpl, + getChatInfo: getChatInfoImpl, + getChatMembers: getChatMembersImpl, + getFeishuMemberInfo: getFeishuMemberInfoImpl, + editMessageFeishu: editMessageFeishuImpl, + getMessageFeishu: getMessageFeishuImpl, + sendCardFeishu: sendCardFeishuImpl, + sendMessageFeishu: sendMessageFeishuImpl, +}; diff --git a/extensions/feishu/src/channel.test.ts b/extensions/feishu/src/channel.test.ts index 826ca1c26fb..7c4ae5d877a 100644 --- a/extensions/feishu/src/channel.test.ts +++ b/extensions/feishu/src/channel.test.ts @@ -28,22 +28,28 @@ vi.mock("./client.js", () => ({ })); vi.mock("./channel.runtime.js", () => ({ - addReactionFeishu: addReactionFeishuMock, - createPinFeishu: createPinFeishuMock, - editMessageFeishu: editMessageFeishuMock, - getChatInfo: getChatInfoMock, - getChatMembers: getChatMembersMock, - getFeishuMemberInfo: getFeishuMemberInfoMock, - getMessageFeishu: getMessageFeishuMock, - listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveMock, - listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveMock, - listPinsFeishu: listPinsFeishuMock, - listReactionsFeishu: listReactionsFeishuMock, - probeFeishu: probeFeishuMock, - removePinFeishu: removePinFeishuMock, - removeReactionFeishu: removeReactionFeishuMock, - sendCardFeishu: sendCardFeishuMock, - sendMessageFeishu: sendMessageFeishuMock, + feishuChannelRuntime: { + addReactionFeishu: addReactionFeishuMock, + createPinFeishu: createPinFeishuMock, + editMessageFeishu: editMessageFeishuMock, + getChatInfo: getChatInfoMock, + getChatMembers: getChatMembersMock, + getFeishuMemberInfo: getFeishuMemberInfoMock, + getMessageFeishu: getMessageFeishuMock, + listFeishuDirectoryGroupsLive: listFeishuDirectoryGroupsLiveMock, + listFeishuDirectoryPeersLive: listFeishuDirectoryPeersLiveMock, + listPinsFeishu: listPinsFeishuMock, + listReactionsFeishu: listReactionsFeishuMock, + probeFeishu: probeFeishuMock, + removePinFeishu: removePinFeishuMock, + removeReactionFeishu: removeReactionFeishuMock, + sendCardFeishu: sendCardFeishuMock, + sendMessageFeishu: sendMessageFeishuMock, + feishuOutbound: { + sendText: vi.fn(), + sendMedia: vi.fn(), + }, + }, })); import { feishuPlugin } from "./channel.js"; diff --git a/extensions/feishu/src/channel.ts b/extensions/feishu/src/channel.ts index 1964331e7e0..c2df79e0028 100644 --- a/extensions/feishu/src/channel.ts +++ b/extensions/feishu/src/channel.ts @@ -12,6 +12,7 @@ import { PAIRING_APPROVED_MESSAGE, } from "openclaw/plugin-sdk/feishu"; import type { ChannelMessageActionName } from "openclaw/plugin-sdk/feishu"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { resolveFeishuAccount, resolveFeishuCredentials, @@ -41,9 +42,12 @@ const meta: ChannelMeta = { order: 70, }; -async function loadFeishuChannelRuntime() { - return await import("./channel.runtime.js"); -} +type FeishuChannelRuntime = typeof import("./channel.runtime.js").feishuChannelRuntime; + +const loadFeishuChannelRuntime = createLazyRuntimeSurface( + () => import("./channel.runtime.js"), + ({ feishuChannelRuntime }) => feishuChannelRuntime, +); function setFeishuNamedAccountEnabled( cfg: ClawdbotConfig, diff --git a/extensions/googlechat/src/channel.runtime.ts b/extensions/googlechat/src/channel.runtime.ts index 1e41376c8f5..81f000f95e7 100644 --- a/extensions/googlechat/src/channel.runtime.ts +++ b/extensions/googlechat/src/channel.runtime.ts @@ -8,36 +8,10 @@ import { startGoogleChatMonitor as startGoogleChatMonitorImpl, } from "./monitor.js"; -type ProbeGoogleChat = typeof import("./api.js").probeGoogleChat; -type SendGoogleChatMessage = typeof import("./api.js").sendGoogleChatMessage; -type UploadGoogleChatAttachment = typeof import("./api.js").uploadGoogleChatAttachment; -type ResolveGoogleChatWebhookPath = typeof import("./monitor.js").resolveGoogleChatWebhookPath; -type StartGoogleChatMonitor = typeof import("./monitor.js").startGoogleChatMonitor; - -export function probeGoogleChat(...args: Parameters): ReturnType { - return probeGoogleChatImpl(...args); -} - -export function sendGoogleChatMessage( - ...args: Parameters -): ReturnType { - return sendGoogleChatMessageImpl(...args); -} - -export function uploadGoogleChatAttachment( - ...args: Parameters -): ReturnType { - return uploadGoogleChatAttachmentImpl(...args); -} - -export function resolveGoogleChatWebhookPath( - ...args: Parameters -): ReturnType { - return resolveGoogleChatWebhookPathImpl(...args); -} - -export function startGoogleChatMonitor( - ...args: Parameters -): ReturnType { - return startGoogleChatMonitorImpl(...args); -} +export const googleChatChannelRuntime = { + probeGoogleChat: probeGoogleChatImpl, + sendGoogleChatMessage: sendGoogleChatMessageImpl, + uploadGoogleChatAttachment: uploadGoogleChatAttachmentImpl, + resolveGoogleChatWebhookPath: resolveGoogleChatWebhookPathImpl, + startGoogleChatMonitor: startGoogleChatMonitorImpl, +}; diff --git a/extensions/googlechat/src/channel.ts b/extensions/googlechat/src/channel.ts index faa1b4e4930..84715321ce8 100644 --- a/extensions/googlechat/src/channel.ts +++ b/extensions/googlechat/src/channel.ts @@ -27,6 +27,7 @@ import { type OpenClawConfig, } from "openclaw/plugin-sdk/googlechat"; import { GoogleChatConfigSchema } from "openclaw/plugin-sdk/googlechat"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { buildPassiveProbedChannelStatusSummary } from "../../shared/channel-status-summary.js"; import { listGoogleChatAccountIds, @@ -47,9 +48,12 @@ import { const meta = getChatChannelMeta("googlechat"); -async function loadGoogleChatChannelRuntime() { - return await import("./channel.runtime.js"); -} +type GoogleChatChannelRuntime = typeof import("./channel.runtime.js").googleChatChannelRuntime; + +const loadGoogleChatChannelRuntime = createLazyRuntimeSurface( + () => import("./channel.runtime.js"), + ({ googleChatChannelRuntime }) => googleChatChannelRuntime, +); const formatAllowFromEntry = (entry: string) => entry diff --git a/extensions/matrix/src/channel.runtime.ts b/extensions/matrix/src/channel.runtime.ts index df56d07ff2c..475d53629e1 100644 --- a/extensions/matrix/src/channel.runtime.ts +++ b/extensions/matrix/src/channel.runtime.ts @@ -7,49 +7,12 @@ import { probeMatrix as probeMatrixImpl } from "./matrix/probe.js"; import { sendMessageMatrix as sendMessageMatrixImpl } from "./matrix/send.js"; import { matrixOutbound as matrixOutboundImpl } from "./outbound.js"; import { resolveMatrixTargets as resolveMatrixTargetsImpl } from "./resolve-targets.js"; - -type ListMatrixDirectoryGroupsLive = - typeof import("./directory-live.js").listMatrixDirectoryGroupsLive; -type ListMatrixDirectoryPeersLive = - typeof import("./directory-live.js").listMatrixDirectoryPeersLive; -type ResolveMatrixAuth = typeof import("./matrix/client.js").resolveMatrixAuth; -type ProbeMatrix = typeof import("./matrix/probe.js").probeMatrix; -type SendMessageMatrix = typeof import("./matrix/send.js").sendMessageMatrix; -type ResolveMatrixTargets = typeof import("./resolve-targets.js").resolveMatrixTargets; -type MatrixOutbound = typeof import("./outbound.js").matrixOutbound; - -export function listMatrixDirectoryGroupsLive( - ...args: Parameters -): ReturnType { - return listMatrixDirectoryGroupsLiveImpl(...args); -} - -export function listMatrixDirectoryPeersLive( - ...args: Parameters -): ReturnType { - return listMatrixDirectoryPeersLiveImpl(...args); -} - -export function resolveMatrixAuth( - ...args: Parameters -): ReturnType { - return resolveMatrixAuthImpl(...args); -} - -export function probeMatrix(...args: Parameters): ReturnType { - return probeMatrixImpl(...args); -} - -export function sendMessageMatrix( - ...args: Parameters -): ReturnType { - return sendMessageMatrixImpl(...args); -} - -export function resolveMatrixTargets( - ...args: Parameters -): ReturnType { - return resolveMatrixTargetsImpl(...args); -} - -export const matrixOutbound: MatrixOutbound = { ...matrixOutboundImpl }; +export const matrixChannelRuntime = { + listMatrixDirectoryGroupsLive: listMatrixDirectoryGroupsLiveImpl, + listMatrixDirectoryPeersLive: listMatrixDirectoryPeersLiveImpl, + resolveMatrixAuth: resolveMatrixAuthImpl, + probeMatrix: probeMatrixImpl, + sendMessageMatrix: sendMessageMatrixImpl, + resolveMatrixTargets: resolveMatrixTargetsImpl, + matrixOutbound: { ...matrixOutboundImpl }, +}; diff --git a/extensions/matrix/src/channel.ts b/extensions/matrix/src/channel.ts index d82d3eb2bdb..03007151d18 100644 --- a/extensions/matrix/src/channel.ts +++ b/extensions/matrix/src/channel.ts @@ -15,6 +15,7 @@ import { PAIRING_APPROVED_MESSAGE, type ChannelPlugin, } from "openclaw/plugin-sdk/matrix"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { buildTrafficStatusSummary } from "../../shared/channel-status-summary.js"; import { matrixMessageActions } from "./actions.js"; import { MatrixConfigSchema } from "./config-schema.js"; @@ -38,9 +39,12 @@ import type { CoreConfig } from "./types.js"; // Mutex for serializing account startup (workaround for concurrent dynamic import race condition) let matrixStartupLock: Promise = Promise.resolve(); -async function loadMatrixChannelRuntime() { - return await import("./channel.runtime.js"); -} +type MatrixChannelRuntime = typeof import("./channel.runtime.js").matrixChannelRuntime; + +const loadMatrixChannelRuntime = createLazyRuntimeSurface( + () => import("./channel.runtime.js"), + ({ matrixChannelRuntime }) => matrixChannelRuntime, +); const meta = { id: "matrix", diff --git a/extensions/msteams/src/channel.runtime.ts b/extensions/msteams/src/channel.runtime.ts index c55d0fc626a..bc6c36a101b 100644 --- a/extensions/msteams/src/channel.runtime.ts +++ b/extensions/msteams/src/channel.runtime.ts @@ -8,42 +8,11 @@ import { sendAdaptiveCardMSTeams as sendAdaptiveCardMSTeamsImpl, sendMessageMSTeams as sendMessageMSTeamsImpl, } from "./send.js"; - -type ListMSTeamsDirectoryGroupsLive = - typeof import("./directory-live.js").listMSTeamsDirectoryGroupsLive; -type ListMSTeamsDirectoryPeersLive = - typeof import("./directory-live.js").listMSTeamsDirectoryPeersLive; -type MSTeamsOutbound = typeof import("./outbound.js").msteamsOutbound; -type ProbeMSTeams = typeof import("./probe.js").probeMSTeams; -type SendAdaptiveCardMSTeams = typeof import("./send.js").sendAdaptiveCardMSTeams; -type SendMessageMSTeams = typeof import("./send.js").sendMessageMSTeams; - -export function listMSTeamsDirectoryGroupsLive( - ...args: Parameters -): ReturnType { - return listMSTeamsDirectoryGroupsLiveImpl(...args); -} - -export function listMSTeamsDirectoryPeersLive( - ...args: Parameters -): ReturnType { - return listMSTeamsDirectoryPeersLiveImpl(...args); -} - -export const msteamsOutbound: MSTeamsOutbound = { ...msteamsOutboundImpl }; - -export function probeMSTeams(...args: Parameters): ReturnType { - return probeMSTeamsImpl(...args); -} - -export function sendAdaptiveCardMSTeams( - ...args: Parameters -): ReturnType { - return sendAdaptiveCardMSTeamsImpl(...args); -} - -export function sendMessageMSTeams( - ...args: Parameters -): ReturnType { - return sendMessageMSTeamsImpl(...args); -} +export const msTeamsChannelRuntime = { + listMSTeamsDirectoryGroupsLive: listMSTeamsDirectoryGroupsLiveImpl, + listMSTeamsDirectoryPeersLive: listMSTeamsDirectoryPeersLiveImpl, + msteamsOutbound: { ...msteamsOutboundImpl }, + probeMSTeams: probeMSTeamsImpl, + sendAdaptiveCardMSTeams: sendAdaptiveCardMSTeamsImpl, + sendMessageMSTeams: sendMessageMSTeamsImpl, +}; diff --git a/extensions/msteams/src/channel.ts b/extensions/msteams/src/channel.ts index d61a377dd4d..e337566e483 100644 --- a/extensions/msteams/src/channel.ts +++ b/extensions/msteams/src/channel.ts @@ -14,6 +14,7 @@ import { MSTeamsConfigSchema, PAIRING_APPROVED_MESSAGE, } from "openclaw/plugin-sdk/msteams"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { resolveMSTeamsGroupToolPolicy } from "./policy.js"; import type { ProbeMSTeamsResult } from "./probe.js"; import { @@ -56,9 +57,12 @@ const TEAMS_GRAPH_PERMISSION_HINTS: Record = { "Files.Read.All": "files (OneDrive)", }; -async function loadMSTeamsChannelRuntime() { - return await import("./channel.runtime.js"); -} +type MSTeamsChannelRuntime = typeof import("./channel.runtime.js").msTeamsChannelRuntime; + +const loadMSTeamsChannelRuntime = createLazyRuntimeSurface( + () => import("./channel.runtime.js"), + ({ msTeamsChannelRuntime }) => msTeamsChannelRuntime, +); export const msteamsPlugin: ChannelPlugin = { id: "msteams", diff --git a/extensions/zalo/src/actions.runtime.ts b/extensions/zalo/src/actions.runtime.ts index d463edc5b24..0fd0a2c6f58 100644 --- a/extensions/zalo/src/actions.runtime.ts +++ b/extensions/zalo/src/actions.runtime.ts @@ -1,7 +1,5 @@ import { sendMessageZalo as sendMessageZaloImpl } from "./send.js"; -type SendMessageZalo = typeof import("./send.js").sendMessageZalo; - -export function sendMessageZalo(...args: Parameters): ReturnType { - return sendMessageZaloImpl(...args); -} +export const zaloActionsRuntime = { + sendMessageZalo: sendMessageZaloImpl, +}; diff --git a/extensions/zalo/src/actions.ts b/extensions/zalo/src/actions.ts index 6f8572b01cd..b492a57a6dc 100644 --- a/extensions/zalo/src/actions.ts +++ b/extensions/zalo/src/actions.ts @@ -4,14 +4,15 @@ import type { OpenClawConfig, } from "openclaw/plugin-sdk/zalo"; import { extractToolSend, jsonResult, readStringParam } from "openclaw/plugin-sdk/zalo"; +import { createLazyRuntimeSurface } from "../../../src/shared/lazy-runtime.js"; import { listEnabledZaloAccounts } from "./accounts.js"; -let zaloActionsRuntimePromise: Promise | null = null; +type ZaloActionsRuntime = typeof import("./actions.runtime.js").zaloActionsRuntime; -async function loadZaloActionsRuntime() { - zaloActionsRuntimePromise ??= import("./actions.runtime.js"); - return zaloActionsRuntimePromise; -} +const loadZaloActionsRuntime = createLazyRuntimeSurface( + () => import("./actions.runtime.js"), + ({ zaloActionsRuntime }) => zaloActionsRuntime, +); const providerId = "zalo"; diff --git a/src/agents/auth-profiles.runtime.ts b/src/agents/auth-profiles.runtime.ts index 5c25bb97c84..7e2da31c058 100644 --- a/src/agents/auth-profiles.runtime.ts +++ b/src/agents/auth-profiles.runtime.ts @@ -1 +1,9 @@ -export { ensureAuthProfileStore } from "./auth-profiles.js"; +import { ensureAuthProfileStore as ensureAuthProfileStoreImpl } from "./auth-profiles.js"; + +type EnsureAuthProfileStore = typeof import("./auth-profiles.js").ensureAuthProfileStore; + +export function ensureAuthProfileStore( + ...args: Parameters +): ReturnType { + return ensureAuthProfileStoreImpl(...args); +} diff --git a/src/agents/command-poll-backoff.runtime.ts b/src/agents/command-poll-backoff.runtime.ts index 1667abba083..87494f4013f 100644 --- a/src/agents/command-poll-backoff.runtime.ts +++ b/src/agents/command-poll-backoff.runtime.ts @@ -1 +1,9 @@ -export { pruneStaleCommandPolls } from "./command-poll-backoff.js"; +import { pruneStaleCommandPolls as pruneStaleCommandPollsImpl } from "./command-poll-backoff.js"; + +type PruneStaleCommandPolls = typeof import("./command-poll-backoff.js").pruneStaleCommandPolls; + +export function pruneStaleCommandPolls( + ...args: Parameters +): ReturnType { + return pruneStaleCommandPollsImpl(...args); +} diff --git a/src/agents/model-suppression.runtime.ts b/src/agents/model-suppression.runtime.ts index 472a662b810..7d39bf2b8a3 100644 --- a/src/agents/model-suppression.runtime.ts +++ b/src/agents/model-suppression.runtime.ts @@ -1 +1,10 @@ -export { shouldSuppressBuiltInModel } from "./model-suppression.js"; +import { shouldSuppressBuiltInModel as shouldSuppressBuiltInModelImpl } from "./model-suppression.js"; + +type ShouldSuppressBuiltInModel = + typeof import("./model-suppression.js").shouldSuppressBuiltInModel; + +export function shouldSuppressBuiltInModel( + ...args: Parameters +): ReturnType { + return shouldSuppressBuiltInModelImpl(...args); +} diff --git a/src/agents/pi-embedded-runner/compact.runtime.ts b/src/agents/pi-embedded-runner/compact.runtime.ts index 33c4ed7066a..f6230265bac 100644 --- a/src/agents/pi-embedded-runner/compact.runtime.ts +++ b/src/agents/pi-embedded-runner/compact.runtime.ts @@ -1 +1,9 @@ -export { compactEmbeddedPiSessionDirect } from "./compact.js"; +import { compactEmbeddedPiSessionDirect as compactEmbeddedPiSessionDirectImpl } from "./compact.js"; + +type CompactEmbeddedPiSessionDirect = typeof import("./compact.js").compactEmbeddedPiSessionDirect; + +export function compactEmbeddedPiSessionDirect( + ...args: Parameters +): ReturnType { + return compactEmbeddedPiSessionDirectImpl(...args); +} diff --git a/src/agents/pi-tools.before-tool-call.runtime.ts b/src/agents/pi-tools.before-tool-call.runtime.ts index b78a58231a2..95126670e31 100644 --- a/src/agents/pi-tools.before-tool-call.runtime.ts +++ b/src/agents/pi-tools.before-tool-call.runtime.ts @@ -1,7 +1,15 @@ -export { getDiagnosticSessionState } from "../logging/diagnostic-session-state.js"; -export { logToolLoopAction } from "../logging/diagnostic.js"; -export { +import { getDiagnosticSessionState } from "../logging/diagnostic-session-state.js"; +import { logToolLoopAction } from "../logging/diagnostic.js"; +import { detectToolCallLoop, recordToolCall, recordToolCallOutcome, } from "./tool-loop-detection.js"; + +export const beforeToolCallRuntime = { + getDiagnosticSessionState, + logToolLoopAction, + detectToolCallLoop, + recordToolCall, + recordToolCallOutcome, +}; diff --git a/src/agents/pi-tools.before-tool-call.ts b/src/agents/pi-tools.before-tool-call.ts index 99a470e8bd0..62bf0e0fb59 100644 --- a/src/agents/pi-tools.before-tool-call.ts +++ b/src/agents/pi-tools.before-tool-call.ts @@ -2,6 +2,7 @@ import type { ToolLoopDetectionConfig } from "../config/types.tools.js"; import type { SessionState } from "../logging/diagnostic-session-state.js"; import { createSubsystemLogger } from "../logging/subsystem.js"; import { getGlobalHookRunner } from "../plugins/hook-runner-global.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import { isPlainObject } from "../utils.js"; import { normalizeToolName } from "./tool-policy.js"; import type { AnyAgentTool } from "./tools/common.js"; @@ -23,14 +24,11 @@ const adjustedParamsByToolCallId = new Map(); const MAX_TRACKED_ADJUSTED_PARAMS = 1024; const LOOP_WARNING_BUCKET_SIZE = 10; const MAX_LOOP_WARNING_KEYS = 256; -let beforeToolCallRuntimePromise: Promise< - typeof import("./pi-tools.before-tool-call.runtime.js") -> | null = null; -function loadBeforeToolCallRuntime() { - beforeToolCallRuntimePromise ??= import("./pi-tools.before-tool-call.runtime.js"); - return beforeToolCallRuntimePromise; -} +const loadBeforeToolCallRuntime = createLazyRuntimeSurface( + () => import("./pi-tools.before-tool-call.runtime.js"), + ({ beforeToolCallRuntime }) => beforeToolCallRuntime, +); function buildAdjustedParamsKey(params: { runId?: string; toolCallId: string }): string { if (params.runId && params.runId.trim()) { diff --git a/src/agents/skills/env-overrides.runtime.ts b/src/agents/skills/env-overrides.runtime.ts index ab8c4b305fb..6f5ebf3947a 100644 --- a/src/agents/skills/env-overrides.runtime.ts +++ b/src/agents/skills/env-overrides.runtime.ts @@ -1 +1,9 @@ -export { getActiveSkillEnvKeys } from "./env-overrides.js"; +import { getActiveSkillEnvKeys as getActiveSkillEnvKeysImpl } from "./env-overrides.js"; + +type GetActiveSkillEnvKeys = typeof import("./env-overrides.js").getActiveSkillEnvKeys; + +export function getActiveSkillEnvKeys( + ...args: Parameters +): ReturnType { + return getActiveSkillEnvKeysImpl(...args); +} diff --git a/src/channels/plugins/setup-wizard-helpers.runtime.ts b/src/channels/plugins/setup-wizard-helpers.runtime.ts index 8c1808f5d40..9fcdf661643 100644 --- a/src/channels/plugins/setup-wizard-helpers.runtime.ts +++ b/src/channels/plugins/setup-wizard-helpers.runtime.ts @@ -1 +1,8 @@ -export { promptResolvedAllowFrom } from "./setup-wizard-helpers.js"; +import type { promptResolvedAllowFrom as promptResolvedAllowFromType } from "./setup-wizard-helpers.js"; + +export async function promptResolvedAllowFrom( + ...args: Parameters +): ReturnType { + const runtime = await import("./setup-wizard-helpers.js"); + return runtime.promptResolvedAllowFrom(...args); +} diff --git a/src/cli/deps.ts b/src/cli/deps.ts index 9996c155288..1d9d6885fe2 100644 --- a/src/cli/deps.ts +++ b/src/cli/deps.ts @@ -1,4 +1,5 @@ import type { OutboundSendDeps } from "../infra/outbound/send-deps.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import { createOutboundSendDepsFromCliSource } from "./outbound-send-mapping.js"; /** @@ -24,10 +25,11 @@ function createLazySender( channelId: string, loader: () => Promise, ): (...args: unknown[]) => Promise { + const loadRuntimeSend = createLazyRuntimeSurface(loader, ({ runtimeSend }) => runtimeSend); return async (...args: unknown[]) => { let cached = senderCache.get(channelId); if (!cached) { - cached = loader().then(({ runtimeSend }) => runtimeSend); + cached = loadRuntimeSend(); senderCache.set(channelId, cached); } const runtimeSend = await cached; diff --git a/src/commands/model-picker.runtime.ts b/src/commands/model-picker.runtime.ts index 3d033fa3e80..f527f0c5cf8 100644 --- a/src/commands/model-picker.runtime.ts +++ b/src/commands/model-picker.runtime.ts @@ -1,7 +1,15 @@ -export { +import { runProviderPluginAuthMethod } from "../plugins/provider-auth-choice.js"; +import { resolveProviderModelPickerEntries, resolveProviderPluginChoice, runProviderModelSelectedHook, } from "../plugins/provider-wizard.js"; -export { resolvePluginProviders } from "../plugins/providers.js"; -export { runProviderPluginAuthMethod } from "../plugins/provider-auth-choice.js"; +import { resolvePluginProviders } from "../plugins/providers.js"; + +export const modelPickerRuntime = { + resolveProviderModelPickerEntries, + resolveProviderPluginChoice, + runProviderModelSelectedHook, + resolvePluginProviders, + runProviderPluginAuthMethod, +}; diff --git a/src/commands/model-picker.test.ts b/src/commands/model-picker.test.ts index a4eb89e066c..fc09d5a7f3c 100644 --- a/src/commands/model-picker.test.ts +++ b/src/commands/model-picker.test.ts @@ -40,11 +40,13 @@ const runProviderModelSelectedHook = vi.hoisted(() => vi.fn(async () => {})); const resolvePluginProviders = vi.hoisted(() => vi.fn(() => [])); const runProviderPluginAuthMethod = vi.hoisted(() => vi.fn()); vi.mock("./model-picker.runtime.js", () => ({ - resolveProviderModelPickerEntries, - resolveProviderPluginChoice, - runProviderModelSelectedHook, - resolvePluginProviders, - runProviderPluginAuthMethod, + modelPickerRuntime: { + resolveProviderModelPickerEntries, + resolveProviderPluginChoice, + runProviderModelSelectedHook, + resolvePluginProviders, + runProviderPluginAuthMethod, + }, })); const OPENROUTER_CATALOG = [ diff --git a/src/commands/model-picker.ts b/src/commands/model-picker.ts index c0b67ea7d7c..cea263f7e58 100644 --- a/src/commands/model-picker.ts +++ b/src/commands/model-picker.ts @@ -13,6 +13,7 @@ import type { OpenClawConfig } from "../config/config.js"; import { resolveAgentModelPrimaryValue } from "../config/model-input.js"; import { applyPrimaryModel } from "../plugins/provider-model-primary.js"; import type { ProviderPlugin } from "../plugins/types.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import type { WizardPrompter, WizardSelectOption } from "../wizard/prompts.js"; import { formatTokenK } from "./models/shared.js"; @@ -49,6 +50,11 @@ async function loadModelPickerRuntime() { return import("./model-picker.runtime.js"); } +const loadResolvedModelPickerRuntime = createLazyRuntimeSurface( + loadModelPickerRuntime, + ({ modelPickerRuntime }) => modelPickerRuntime, +); + function hasAuthForProvider( provider: string, cfg: OpenClawConfig, @@ -284,7 +290,7 @@ export async function promptDefaultModel( options.push({ value: MANUAL_VALUE, label: "Enter model manually" }); } if (includeProviderPluginSetups && agentDir) { - const { resolveProviderModelPickerEntries } = await loadModelPickerRuntime(); + const { resolveProviderModelPickerEntries } = await loadResolvedModelPickerRuntime(); options.push( ...resolveProviderModelPickerEntries({ config: cfg, @@ -343,7 +349,7 @@ export async function promptDefaultModel( if (selection.startsWith("provider-plugin:")) { pluginResolution = selection; } else if (!selection.includes("/")) { - const { resolvePluginProviders } = await loadModelPickerRuntime(); + const { resolvePluginProviders } = await loadResolvedModelPickerRuntime(); pluginProviders = resolvePluginProviders({ config: cfg, workspaceDir: params.workspaceDir, @@ -368,7 +374,7 @@ export async function promptDefaultModel( resolveProviderPluginChoice, runProviderModelSelectedHook, runProviderPluginAuthMethod, - } = await loadModelPickerRuntime(); + } = await loadResolvedModelPickerRuntime(); if (pluginProviders.length === 0) { pluginProviders = resolvePluginProviders({ config: cfg, @@ -404,7 +410,7 @@ export async function promptDefaultModel( return { model: applied.defaultModel, config: applied.config }; } const model = String(selection); - const { runProviderModelSelectedHook } = await loadModelPickerRuntime(); + const { runProviderModelSelectedHook } = await loadResolvedModelPickerRuntime(); await runProviderModelSelectedHook({ config: cfg, model, diff --git a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts index a02dd2f2ee2..05422a839fb 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.runtime.ts @@ -1,5 +1,11 @@ -export { resolveProviderPluginChoice } from "../../../plugins/provider-wizard.js"; -export { +import { resolveProviderPluginChoice } from "../../../plugins/provider-wizard.js"; +import { resolveOwningPluginIdsForProvider, resolvePluginProviders, } from "../../../plugins/providers.js"; + +export const authChoicePluginProvidersRuntime = { + resolveOwningPluginIdsForProvider, + resolveProviderPluginChoice, + resolvePluginProviders, +}; diff --git a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.test.ts b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.test.ts index 3ccee9bbfd3..bea20a66764 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.test.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.test.ts @@ -11,10 +11,11 @@ const resolveOwningPluginIdsForProvider = vi.hoisted(() => vi.fn(() => undefined const resolveProviderPluginChoice = vi.hoisted(() => vi.fn()); const resolvePluginProviders = vi.hoisted(() => vi.fn(() => [])); vi.mock("./auth-choice.plugin-providers.runtime.js", () => ({ - resolveOwningPluginIdsForProvider, - resolveProviderPluginChoice, - resolvePluginProviders, - PROVIDER_PLUGIN_CHOICE_PREFIX: "provider-plugin:", + authChoicePluginProvidersRuntime: { + resolveOwningPluginIdsForProvider, + resolveProviderPluginChoice, + resolvePluginProviders, + }, })); beforeEach(() => { diff --git a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts index b7a369e4674..ad6cb853955 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.plugin-providers.ts @@ -14,6 +14,7 @@ import type { ProviderResolveNonInteractiveApiKeyParams, } from "../../../plugins/types.js"; import type { RuntimeEnv } from "../../../runtime.js"; +import { createLazyRuntimeSurface } from "../../../shared/lazy-runtime.js"; import type { OnboardOptions } from "../../onboard-types.js"; const PROVIDER_PLUGIN_CHOICE_PREFIX = "provider-plugin:"; @@ -22,6 +23,11 @@ async function loadPluginProviderRuntime() { return import("./auth-choice.plugin-providers.runtime.js"); } +const loadAuthChoicePluginProvidersRuntime = createLazyRuntimeSurface( + loadPluginProviderRuntime, + ({ authChoicePluginProvidersRuntime }) => authChoicePluginProvidersRuntime, +); + function buildIsolatedProviderResolutionConfig( cfg: OpenClawConfig, providerId: string | undefined, @@ -81,7 +87,7 @@ export async function applyNonInteractivePluginProviderChoice(params: { preferredProviderId, ); const { resolveOwningPluginIdsForProvider, resolveProviderPluginChoice, resolvePluginProviders } = - await loadPluginProviderRuntime(); + await loadAuthChoicePluginProvidersRuntime(); const owningPluginIds = preferredProviderId ? resolveOwningPluginIdsForProvider({ provider: preferredProviderId, diff --git a/src/commands/status.scan.runtime.ts b/src/commands/status.scan.runtime.ts index 372b31f4803..a783d0a94d6 100644 --- a/src/commands/status.scan.runtime.ts +++ b/src/commands/status.scan.runtime.ts @@ -1,2 +1,7 @@ -export { collectChannelStatusIssues } from "../infra/channels-status-issues.js"; -export { buildChannelsTable } from "./status-all/channels.js"; +import { collectChannelStatusIssues } from "../infra/channels-status-issues.js"; +import { buildChannelsTable } from "./status-all/channels.js"; + +export const statusScanRuntime = { + collectChannelStatusIssues, + buildChannelsTable, +}; diff --git a/src/commands/status.scan.test.ts b/src/commands/status.scan.test.ts index 168c2f55017..899aea2b267 100644 --- a/src/commands/status.scan.test.ts +++ b/src/commands/status.scan.test.ts @@ -42,8 +42,10 @@ vi.mock("./status-all/channels.js", () => ({ })); vi.mock("./status.scan.runtime.js", () => ({ - buildChannelsTable: mocks.buildChannelsTable, - collectChannelStatusIssues: vi.fn(() => []), + statusScanRuntime: { + buildChannelsTable: mocks.buildChannelsTable, + collectChannelStatusIssues: vi.fn(() => []), + }, })); vi.mock("./status.update.js", () => ({ diff --git a/src/commands/status.scan.ts b/src/commands/status.scan.ts index 3eb6fc8ed3d..e7d05542743 100644 --- a/src/commands/status.scan.ts +++ b/src/commands/status.scan.ts @@ -6,14 +6,13 @@ import { withProgress } from "../cli/progress.js"; import type { OpenClawConfig } from "../config/config.js"; import { readBestEffortConfig } from "../config/config.js"; import { callGateway } from "../gateway/call.js"; +import type { collectChannelStatusIssues as collectChannelStatusIssuesFn } from "../infra/channels-status-issues.js"; import { resolveOsSummary } from "../infra/os-summary.js"; import { runExec } from "../process/exec.js"; import type { RuntimeEnv } from "../runtime.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; +import type { buildChannelsTable as buildChannelsTableFn } from "./status-all/channels.js"; import { getAgentLocalStatuses } from "./status.agent-local.js"; -import type { - buildChannelsTable as buildChannelsTableFn, - collectChannelStatusIssues as collectChannelStatusIssuesFn, -} from "./status.scan.runtime.js"; import { buildTailscaleHttpsUrl, pickGatewaySelfPresence, @@ -30,7 +29,6 @@ import { getUpdateCheckResult } from "./status.update.js"; type DeferredResult = { ok: true; value: T } | { ok: false; error: unknown }; let pluginRegistryModulePromise: Promise | undefined; -let statusScanRuntimeModulePromise: Promise | undefined; let statusScanDepsRuntimeModulePromise: | Promise | undefined; @@ -40,10 +38,10 @@ function loadPluginRegistryModule() { return pluginRegistryModulePromise; } -function loadStatusScanRuntimeModule() { - statusScanRuntimeModulePromise ??= import("./status.scan.runtime.js"); - return statusScanRuntimeModulePromise; -} +const loadStatusScanRuntimeModule = createLazyRuntimeSurface( + () => import("./status.scan.runtime.js"), + ({ statusScanRuntime }) => statusScanRuntime, +); function loadStatusScanDepsRuntimeModule() { statusScanDepsRuntimeModulePromise ??= import("./status.scan.deps.runtime.js"); diff --git a/src/commands/status.summary.runtime.ts b/src/commands/status.summary.runtime.ts index df1ae881d4f..e4b08a49856 100644 --- a/src/commands/status.summary.runtime.ts +++ b/src/commands/status.summary.runtime.ts @@ -1,2 +1,8 @@ -export { resolveContextTokensForModel } from "../agents/context.js"; -export { classifySessionKey, resolveSessionModelRef } from "../gateway/session-utils.js"; +import { resolveContextTokensForModel } from "../agents/context.js"; +import { classifySessionKey, resolveSessionModelRef } from "../gateway/session-utils.js"; + +export const statusSummaryRuntime = { + resolveContextTokensForModel, + classifySessionKey, + resolveSessionModelRef, +}; diff --git a/src/commands/status.summary.ts b/src/commands/status.summary.ts index c5c3f174547..c235765b406 100644 --- a/src/commands/status.summary.ts +++ b/src/commands/status.summary.ts @@ -10,14 +10,12 @@ import { listGatewayAgentsBasic } from "../gateway/agent-list.js"; import { resolveHeartbeatSummaryForAgent } from "../infra/heartbeat-summary.js"; import { peekSystemEvents } from "../infra/system-events.js"; import { parseAgentSessionKey } from "../routing/session-key.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import { resolveRuntimeServiceVersion } from "../version.js"; import type { HeartbeatStatus, SessionStatus, StatusSummary } from "./status.types.js"; let channelSummaryModulePromise: Promise | undefined; let linkChannelModulePromise: Promise | undefined; -let statusSummaryRuntimeModulePromise: - | Promise - | undefined; let configIoModulePromise: Promise | undefined; function loadChannelSummaryModule() { @@ -30,10 +28,10 @@ function loadLinkChannelModule() { return linkChannelModulePromise; } -function loadStatusSummaryRuntimeModule() { - statusSummaryRuntimeModulePromise ??= import("./status.summary.runtime.js"); - return statusSummaryRuntimeModulePromise; -} +const loadStatusSummaryRuntimeModule = createLazyRuntimeSurface( + () => import("./status.summary.runtime.js"), + ({ statusSummaryRuntime }) => statusSummaryRuntime, +); function loadConfigIoModule() { configIoModulePromise ??= import("../config/io.js"); diff --git a/src/plugin-sdk/index.ts b/src/plugin-sdk/index.ts index 45465f2f68e..16720cf8961 100644 --- a/src/plugin-sdk/index.ts +++ b/src/plugin-sdk/index.ts @@ -40,13 +40,13 @@ export type { export type { OpenClawConfig } from "../config/config.js"; /** @deprecated Use OpenClawConfig instead */ export type { OpenClawConfig as ClawdbotConfig } from "../config/config.js"; -export { registerContextEngine } from "../context-engine/index.js"; export * from "./image-generation.js"; export type { SecretInput, SecretRef } from "../config/types.secrets.js"; export type { RuntimeEnv } from "../runtime.js"; export type { HookEntry } from "../hooks/types.js"; export type { ReplyPayload } from "../auto-reply/types.js"; export type { WizardPrompter } from "../wizard/prompts.js"; -export type { ContextEngineFactory } from "../context-engine/index.js"; +export type { ContextEngineFactory } from "../context-engine/registry.js"; export { emptyPluginConfigSchema } from "../plugins/config-schema.js"; +export { registerContextEngine } from "../context-engine/registry.js"; diff --git a/src/plugins/provider-api-key-auth.runtime.ts b/src/plugins/provider-api-key-auth.runtime.ts index ad37b986b91..40404f512af 100644 --- a/src/plugins/provider-api-key-auth.runtime.ts +++ b/src/plugins/provider-api-key-auth.runtime.ts @@ -6,7 +6,7 @@ import { } from "./provider-auth-input.js"; import { applyPrimaryModel } from "./provider-model-primary.js"; -export { +export const providerApiKeyAuthRuntime = { applyAuthProfileConfig, applyPrimaryModel, buildApiKeyCredential, diff --git a/src/plugins/provider-api-key-auth.ts b/src/plugins/provider-api-key-auth.ts index aa3805aea8f..183c8c4f5f0 100644 --- a/src/plugins/provider-api-key-auth.ts +++ b/src/plugins/provider-api-key-auth.ts @@ -1,6 +1,7 @@ import { upsertAuthProfile } from "../agents/auth-profiles/profiles.js"; import type { OpenClawConfig } from "../config/config.js"; import type { SecretInput } from "../config/types.secrets.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js"; import type { ProviderAuthMethod, @@ -29,14 +30,10 @@ type ProviderApiKeyAuthMethodOptions = { applyConfig?: (cfg: OpenClawConfig) => OpenClawConfig; }; -let providerApiKeyAuthRuntimePromise: - | Promise - | undefined; - -function loadProviderApiKeyAuthRuntime() { - providerApiKeyAuthRuntimePromise ??= import("./provider-api-key-auth.runtime.js"); - return providerApiKeyAuthRuntimePromise; -} +const loadProviderApiKeyAuthRuntime = createLazyRuntimeSurface( + () => import("./provider-api-key-auth.runtime.js"), + ({ providerApiKeyAuthRuntime }) => providerApiKeyAuthRuntime, +); function resolveStringOption(opts: Record | undefined, optionKey: string) { return normalizeOptionalSecretInput(opts?.[optionKey]); diff --git a/src/plugins/runtime/runtime-discord.ts b/src/plugins/runtime/runtime-discord.ts index 033c1631828..4878bff3d81 100644 --- a/src/plugins/runtime/runtime-discord.ts +++ b/src/plugins/runtime/runtime-discord.ts @@ -9,123 +9,121 @@ import { setThreadBindingMaxAgeBySessionKey, unbindThreadBindingsBySessionKey, } from "../../../extensions/discord/src/monitor/thread-bindings.js"; +import { createLazyRuntimeMethod, createLazyRuntimeSurface } from "../../shared/lazy-runtime.js"; import { createDiscordTypingLease } from "./runtime-discord-typing.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; type RuntimeDiscordOps = typeof import("./runtime-discord-ops.runtime.js").runtimeDiscordOps; -let runtimeDiscordOpsPromise: Promise | null = null; +const loadRuntimeDiscordOps = createLazyRuntimeSurface( + () => import("./runtime-discord-ops.runtime.js"), + ({ runtimeDiscordOps }) => runtimeDiscordOps, +); -function loadRuntimeDiscordOps() { - runtimeDiscordOpsPromise ??= import("./runtime-discord-ops.runtime.js").then( - ({ runtimeDiscordOps }) => runtimeDiscordOps, - ); - return runtimeDiscordOpsPromise; -} +const auditChannelPermissionsLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.auditChannelPermissions); -const auditChannelPermissionsLazy: PluginRuntimeChannel["discord"]["auditChannelPermissions"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.auditChannelPermissions(...args); - }; +const listDirectoryGroupsLiveLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.listDirectoryGroupsLive); -const listDirectoryGroupsLiveLazy: PluginRuntimeChannel["discord"]["listDirectoryGroupsLive"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.listDirectoryGroupsLive(...args); - }; +const listDirectoryPeersLiveLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.listDirectoryPeersLive); -const listDirectoryPeersLiveLazy: PluginRuntimeChannel["discord"]["listDirectoryPeersLive"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.listDirectoryPeersLive(...args); - }; +const probeDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.probeDiscord); -const probeDiscordLazy: PluginRuntimeChannel["discord"]["probeDiscord"] = async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.probeDiscord(...args); -}; +const resolveChannelAllowlistLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.resolveChannelAllowlist); -const resolveChannelAllowlistLazy: PluginRuntimeChannel["discord"]["resolveChannelAllowlist"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.resolveChannelAllowlist(...args); - }; +const resolveUserAllowlistLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.resolveUserAllowlist); -const resolveUserAllowlistLazy: PluginRuntimeChannel["discord"]["resolveUserAllowlist"] = async ( - ...args -) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.resolveUserAllowlist(...args); -}; +const sendComponentMessageLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.sendComponentMessage); -const sendComponentMessageLazy: PluginRuntimeChannel["discord"]["sendComponentMessage"] = async ( - ...args -) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.sendComponentMessage(...args); -}; +const sendMessageDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.sendMessageDiscord); -const sendMessageDiscordLazy: PluginRuntimeChannel["discord"]["sendMessageDiscord"] = async ( - ...args -) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.sendMessageDiscord(...args); -}; +const sendPollDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.sendPollDiscord); -const sendPollDiscordLazy: PluginRuntimeChannel["discord"]["sendPollDiscord"] = async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.sendPollDiscord(...args); -}; +const monitorDiscordProviderLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.monitorDiscordProvider); -const monitorDiscordProviderLazy: PluginRuntimeChannel["discord"]["monitorDiscordProvider"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.monitorDiscordProvider(...args); - }; +const sendTypingDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.typing.pulse); -const sendTypingDiscordLazy: PluginRuntimeChannel["discord"]["typing"]["pulse"] = async ( - ...args -) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.typing.pulse(...args); -}; +const editMessageDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.editMessage); -const editMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["editMessage"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.editMessage(...args); - }; +const deleteMessageDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>( + loadRuntimeDiscordOps, + (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.deleteMessage, +); -const deleteMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["deleteMessage"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.deleteMessage(...args); - }; +const pinMessageDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.pinMessage); -const pinMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["pinMessage"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.pinMessage(...args); - }; +const unpinMessageDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.unpinMessage); -const unpinMessageDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["unpinMessage"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.unpinMessage(...args); - }; +const createThreadDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.createThread); -const createThreadDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["createThread"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.createThread(...args); - }; - -const editChannelDiscordLazy: PluginRuntimeChannel["discord"]["conversationActions"]["editChannel"] = - async (...args) => { - const runtimeDiscordOps = await loadRuntimeDiscordOps(); - return runtimeDiscordOps.conversationActions.editChannel(...args); - }; +const editChannelDiscordLazy = createLazyRuntimeMethod< + RuntimeDiscordOps, + Parameters, + ReturnType +>(loadRuntimeDiscordOps, (runtimeDiscordOps) => runtimeDiscordOps.conversationActions.editChannel); export function createRuntimeDiscord(): PluginRuntimeChannel["discord"] { return { diff --git a/src/plugins/runtime/runtime-slack.ts b/src/plugins/runtime/runtime-slack.ts index 23d34a7e5f4..30742195ad6 100644 --- a/src/plugins/runtime/runtime-slack.ts +++ b/src/plugins/runtime/runtime-slack.ts @@ -1,65 +1,60 @@ +import { createLazyRuntimeMethod, createLazyRuntimeSurface } from "../../shared/lazy-runtime.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; type RuntimeSlackOps = typeof import("./runtime-slack-ops.runtime.js").runtimeSlackOps; -let runtimeSlackOpsPromise: Promise | null = null; +const loadRuntimeSlackOps = createLazyRuntimeSurface( + () => import("./runtime-slack-ops.runtime.js"), + ({ runtimeSlackOps }) => runtimeSlackOps, +); -function loadRuntimeSlackOps() { - runtimeSlackOpsPromise ??= import("./runtime-slack-ops.runtime.js").then( - ({ runtimeSlackOps }) => runtimeSlackOps, - ); - return runtimeSlackOpsPromise; -} +const listDirectoryGroupsLiveLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.listDirectoryGroupsLive); -const listDirectoryGroupsLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryGroupsLive"] = - async (...args) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.listDirectoryGroupsLive(...args); - }; +const listDirectoryPeersLiveLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.listDirectoryPeersLive); -const listDirectoryPeersLiveLazy: PluginRuntimeChannel["slack"]["listDirectoryPeersLive"] = async ( - ...args -) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.listDirectoryPeersLive(...args); -}; +const probeSlackLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.probeSlack); -const probeSlackLazy: PluginRuntimeChannel["slack"]["probeSlack"] = async (...args) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.probeSlack(...args); -}; +const resolveChannelAllowlistLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.resolveChannelAllowlist); -const resolveChannelAllowlistLazy: PluginRuntimeChannel["slack"]["resolveChannelAllowlist"] = - async (...args) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.resolveChannelAllowlist(...args); - }; +const resolveUserAllowlistLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.resolveUserAllowlist); -const resolveUserAllowlistLazy: PluginRuntimeChannel["slack"]["resolveUserAllowlist"] = async ( - ...args -) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.resolveUserAllowlist(...args); -}; +const sendMessageSlackLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.sendMessageSlack); -const sendMessageSlackLazy: PluginRuntimeChannel["slack"]["sendMessageSlack"] = async (...args) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.sendMessageSlack(...args); -}; +const monitorSlackProviderLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.monitorSlackProvider); -const monitorSlackProviderLazy: PluginRuntimeChannel["slack"]["monitorSlackProvider"] = async ( - ...args -) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.monitorSlackProvider(...args); -}; - -const handleSlackActionLazy: PluginRuntimeChannel["slack"]["handleSlackAction"] = async ( - ...args -) => { - const runtimeSlackOps = await loadRuntimeSlackOps(); - return runtimeSlackOps.handleSlackAction(...args); -}; +const handleSlackActionLazy = createLazyRuntimeMethod< + RuntimeSlackOps, + Parameters, + ReturnType +>(loadRuntimeSlackOps, (runtimeSlackOps) => runtimeSlackOps.handleSlackAction); export function createRuntimeSlack(): PluginRuntimeChannel["slack"] { return { diff --git a/src/plugins/runtime/runtime-telegram.ts b/src/plugins/runtime/runtime-telegram.ts index d0d71d08c4e..b83df21670f 100644 --- a/src/plugins/runtime/runtime-telegram.ts +++ b/src/plugins/runtime/runtime-telegram.ts @@ -5,94 +5,106 @@ import { setTelegramThreadBindingMaxAgeBySessionKey, } from "../../../extensions/telegram/src/thread-bindings.js"; import { resolveTelegramToken } from "../../../extensions/telegram/src/token.js"; +import { createLazyRuntimeMethod, createLazyRuntimeSurface } from "../../shared/lazy-runtime.js"; import { createTelegramTypingLease } from "./runtime-telegram-typing.js"; import type { PluginRuntimeChannel } from "./types-channel.js"; type RuntimeTelegramOps = typeof import("./runtime-telegram-ops.runtime.js").runtimeTelegramOps; -let runtimeTelegramOpsPromise: Promise | null = null; +const loadRuntimeTelegramOps = createLazyRuntimeSurface( + () => import("./runtime-telegram-ops.runtime.js"), + ({ runtimeTelegramOps }) => runtimeTelegramOps, +); -function loadRuntimeTelegramOps() { - runtimeTelegramOpsPromise ??= import("./runtime-telegram-ops.runtime.js").then( - ({ runtimeTelegramOps }) => runtimeTelegramOps, - ); - return runtimeTelegramOpsPromise; -} +const auditGroupMembershipLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.auditGroupMembership); -const auditGroupMembershipLazy: PluginRuntimeChannel["telegram"]["auditGroupMembership"] = async ( - ...args -) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.auditGroupMembership(...args); -}; +const probeTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.probeTelegram); -const probeTelegramLazy: PluginRuntimeChannel["telegram"]["probeTelegram"] = async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.probeTelegram(...args); -}; +const sendMessageTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.sendMessageTelegram); -const sendMessageTelegramLazy: PluginRuntimeChannel["telegram"]["sendMessageTelegram"] = async ( - ...args -) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.sendMessageTelegram(...args); -}; +const sendPollTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.sendPollTelegram); -const sendPollTelegramLazy: PluginRuntimeChannel["telegram"]["sendPollTelegram"] = async ( - ...args -) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.sendPollTelegram(...args); -}; +const monitorTelegramProviderLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.monitorTelegramProvider); -const monitorTelegramProviderLazy: PluginRuntimeChannel["telegram"]["monitorTelegramProvider"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.monitorTelegramProvider(...args); - }; +const sendTypingTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>(loadRuntimeTelegramOps, (runtimeTelegramOps) => runtimeTelegramOps.typing.pulse); -const sendTypingTelegramLazy: PluginRuntimeChannel["telegram"]["typing"]["pulse"] = async ( - ...args -) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.typing.pulse(...args); -}; +const editMessageTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.editMessage, +); -const editMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editMessage"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.editMessage(...args); - }; +const editMessageReplyMarkupTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.editReplyMarkup, +); -const editMessageReplyMarkupTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["editReplyMarkup"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.editReplyMarkup(...args); - }; +const deleteMessageTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.deleteMessage, +); -const deleteMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["deleteMessage"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.deleteMessage(...args); - }; +const renameForumTopicTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.renameTopic, +); -const renameForumTopicTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["renameTopic"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.renameTopic(...args); - }; +const pinMessageTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.pinMessage, +); -const pinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["pinMessage"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.pinMessage(...args); - }; - -const unpinMessageTelegramLazy: PluginRuntimeChannel["telegram"]["conversationActions"]["unpinMessage"] = - async (...args) => { - const runtimeTelegramOps = await loadRuntimeTelegramOps(); - return runtimeTelegramOps.conversationActions.unpinMessage(...args); - }; +const unpinMessageTelegramLazy = createLazyRuntimeMethod< + RuntimeTelegramOps, + Parameters, + ReturnType +>( + loadRuntimeTelegramOps, + (runtimeTelegramOps) => runtimeTelegramOps.conversationActions.unpinMessage, +); export function createRuntimeTelegram(): PluginRuntimeChannel["telegram"] { return { diff --git a/src/plugins/runtime/runtime-whatsapp.ts b/src/plugins/runtime/runtime-whatsapp.ts index 10f8e9e6a94..63871bc08f8 100644 --- a/src/plugins/runtime/runtime-whatsapp.ts +++ b/src/plugins/runtime/runtime-whatsapp.ts @@ -6,6 +6,7 @@ import { readWebSelfId, webAuthExists, } from "../../../extensions/whatsapp/src/auth-store.js"; +import { createLazyRuntimeMethod, createLazyRuntimeSurface } from "../../shared/lazy-runtime.js"; import { createRuntimeWhatsAppLoginTool } from "./runtime-whatsapp-login-tool.js"; import type { PluginRuntime } from "./types.js"; @@ -14,24 +15,33 @@ type RuntimeWhatsAppOutbound = type RuntimeWhatsAppLogin = typeof import("./runtime-whatsapp-login.runtime.js").runtimeWhatsAppLogin; -const sendMessageWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendMessageWhatsApp"] = async ( - ...args -) => { - const runtimeWhatsAppOutbound = await loadWebOutbound(); - return runtimeWhatsAppOutbound.sendMessageWhatsApp(...args); -}; +const loadWebOutbound = createLazyRuntimeSurface( + () => import("./runtime-whatsapp-outbound.runtime.js"), + ({ runtimeWhatsAppOutbound }) => runtimeWhatsAppOutbound, +); -const sendPollWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendPollWhatsApp"] = async ( - ...args -) => { - const runtimeWhatsAppOutbound = await loadWebOutbound(); - return runtimeWhatsAppOutbound.sendPollWhatsApp(...args); -}; +const loadWebLogin = createLazyRuntimeSurface( + () => import("./runtime-whatsapp-login.runtime.js"), + ({ runtimeWhatsAppLogin }) => runtimeWhatsAppLogin, +); -const loginWebLazy: PluginRuntime["channel"]["whatsapp"]["loginWeb"] = async (...args) => { - const runtimeWhatsAppLogin = await loadWebLogin(); - return runtimeWhatsAppLogin.loginWeb(...args); -}; +const sendMessageWhatsAppLazy = createLazyRuntimeMethod< + RuntimeWhatsAppOutbound, + Parameters, + ReturnType +>(loadWebOutbound, (runtimeWhatsAppOutbound) => runtimeWhatsAppOutbound.sendMessageWhatsApp); + +const sendPollWhatsAppLazy = createLazyRuntimeMethod< + RuntimeWhatsAppOutbound, + Parameters, + ReturnType +>(loadWebOutbound, (runtimeWhatsAppOutbound) => runtimeWhatsAppOutbound.sendPollWhatsApp); + +const loginWebLazy = createLazyRuntimeMethod< + RuntimeWhatsAppLogin, + Parameters, + ReturnType +>(loadWebLogin, (runtimeWhatsAppLogin) => runtimeWhatsAppLogin.loginWeb); const startWebLoginWithQrLazy: PluginRuntime["channel"]["whatsapp"]["startWebLoginWithQr"] = async ( ...args @@ -64,26 +74,10 @@ let webLoginQrPromise: Promise< typeof import("../../../extensions/whatsapp/src/login-qr.js") > | null = null; let webChannelPromise: Promise | null = null; -let webOutboundPromise: Promise | null = null; -let webLoginPromise: Promise | null = null; let whatsappActionsPromise: Promise< typeof import("../../agents/tools/whatsapp-actions.js") > | null = null; -function loadWebOutbound() { - webOutboundPromise ??= import("./runtime-whatsapp-outbound.runtime.js").then( - ({ runtimeWhatsAppOutbound }) => runtimeWhatsAppOutbound, - ); - return webOutboundPromise; -} - -function loadWebLogin() { - webLoginPromise ??= import("./runtime-whatsapp-login.runtime.js").then( - ({ runtimeWhatsAppLogin }) => runtimeWhatsAppLogin, - ); - return webLoginPromise; -} - function loadWebLoginQr() { webLoginQrPromise ??= import("../../../extensions/whatsapp/src/login-qr.js"); return webLoginQrPromise; diff --git a/src/security/audit-channel.collect.runtime.ts b/src/security/audit-channel.collect.runtime.ts index 6a33ff6a93a..bed24a7f73e 100644 --- a/src/security/audit-channel.collect.runtime.ts +++ b/src/security/audit-channel.collect.runtime.ts @@ -1 +1,10 @@ -export { collectChannelSecurityFindings } from "./audit-channel.js"; +import { collectChannelSecurityFindings as collectChannelSecurityFindingsImpl } from "./audit-channel.js"; + +type CollectChannelSecurityFindings = + typeof import("./audit-channel.js").collectChannelSecurityFindings; + +export function collectChannelSecurityFindings( + ...args: Parameters +): ReturnType { + return collectChannelSecurityFindingsImpl(...args); +} diff --git a/src/security/audit-channel.runtime.ts b/src/security/audit-channel.runtime.ts index 867f0a91162..de2d666cb87 100644 --- a/src/security/audit-channel.runtime.ts +++ b/src/security/audit-channel.runtime.ts @@ -1,9 +1,17 @@ -export { readChannelAllowFromStore } from "../pairing/pairing-store.js"; -export { - isDiscordMutableAllowEntry, - isZalouserMutableGroupEntry, -} from "./mutable-allowlist-detectors.js"; -export { +import { readChannelAllowFromStore } from "../pairing/pairing-store.js"; +import { isNumericTelegramUserId, normalizeTelegramAllowFromEntry, } from "../plugin-sdk/telegram.js"; +import { + isDiscordMutableAllowEntry, + isZalouserMutableGroupEntry, +} from "./mutable-allowlist-detectors.js"; + +export const auditChannelRuntime = { + readChannelAllowFromStore, + isDiscordMutableAllowEntry, + isZalouserMutableGroupEntry, + isNumericTelegramUserId, + normalizeTelegramAllowFromEntry, +}; diff --git a/src/security/audit-channel.ts b/src/security/audit-channel.ts index 44b83c28cc3..dd920e77818 100644 --- a/src/security/audit-channel.ts +++ b/src/security/audit-channel.ts @@ -11,18 +11,15 @@ import { resolveNativeCommandsEnabled, resolveNativeSkillsEnabled } from "../con import type { OpenClawConfig } from "../config/config.js"; import { isDangerousNameMatchingEnabled } from "../config/dangerous-name-matching.js"; import { formatErrorMessage } from "../infra/errors.js"; +import { createLazyRuntimeSurface } from "../shared/lazy-runtime.js"; import { normalizeStringEntries } from "../shared/string-normalization.js"; import type { SecurityAuditFinding, SecurityAuditSeverity } from "./audit.js"; import { resolveDmAllowState } from "./dm-policy-shared.js"; -let auditChannelRuntimeModulePromise: - | Promise - | undefined; - -function loadAuditChannelRuntimeModule() { - auditChannelRuntimeModulePromise ??= import("./audit-channel.runtime.js"); - return auditChannelRuntimeModulePromise; -} +const loadAuditChannelRuntimeModule = createLazyRuntimeSurface( + () => import("./audit-channel.runtime.js"), + ({ auditChannelRuntime }) => auditChannelRuntime, +); function normalizeAllowFromList(list: Array | undefined | null): string[] { return normalizeStringEntries(Array.isArray(list) ? list : undefined); diff --git a/src/security/audit.runtime.ts b/src/security/audit.runtime.ts index 349d2f26fe5..f36d23de14d 100644 --- a/src/security/audit.runtime.ts +++ b/src/security/audit.runtime.ts @@ -1 +1,9 @@ -export { runSecurityAudit } from "./audit.js"; +import { runSecurityAudit as runSecurityAuditImpl } from "./audit.js"; + +type RunSecurityAudit = typeof import("./audit.js").runSecurityAudit; + +export function runSecurityAudit( + ...args: Parameters +): ReturnType { + return runSecurityAuditImpl(...args); +} diff --git a/src/shared/lazy-runtime.ts b/src/shared/lazy-runtime.ts new file mode 100644 index 00000000000..3edaa865f50 --- /dev/null +++ b/src/shared/lazy-runtime.ts @@ -0,0 +1,21 @@ +export function createLazyRuntimeSurface( + importer: () => Promise, + select: (module: TModule) => TSurface, +): () => Promise { + let cached: Promise | null = null; + return () => { + cached ??= importer().then(select); + return cached; + }; +} + +export function createLazyRuntimeMethod( + load: () => Promise, + select: (surface: TSurface) => (...args: TArgs) => TResult, +): (...args: TArgs) => Promise> { + const invoke = async (...args: TArgs): Promise> => { + const method = select(await load()); + return await method(...args); + }; + return invoke; +}