diff --git a/extensions/matrix/index.ts b/extensions/matrix/index.ts index 85813c89d68..6747f244dbc 100644 --- a/extensions/matrix/index.ts +++ b/extensions/matrix/index.ts @@ -5,17 +5,24 @@ import { import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { registerMatrixCliMetadata } from "./cli-metadata.js"; +type MatrixHandlersRuntimeModule = typeof import("./plugin-entry.handlers.runtime.js"); type MatrixSubagentHooksModule = typeof import("./src/matrix/subagent-hooks.js"); +let matrixHandlersRuntimePromise: Promise | null = null; let matrixSubagentHooksPromise: Promise | null = null; +function loadMatrixHandlersRuntimeModule() { + matrixHandlersRuntimePromise ??= import("./plugin-entry.handlers.runtime.js"); + return matrixHandlersRuntimePromise; +} + function loadMatrixSubagentHooksModule() { matrixSubagentHooksPromise ??= import("./src/matrix/subagent-hooks.js"); return matrixSubagentHooksPromise; } export function registerMatrixFullRuntime(api: OpenClawPluginApi): void { - void import("./plugin-entry.handlers.runtime.js") + void loadMatrixHandlersRuntimeModule() .then(({ ensureMatrixCryptoRuntime }) => ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err: unknown) => { const message = formatErrorMessage(err); @@ -28,17 +35,17 @@ export function registerMatrixFullRuntime(api: OpenClawPluginApi): void { }); api.registerGatewayMethod("matrix.verify.recoveryKey", async (ctx) => { - const { handleVerifyRecoveryKey } = await import("./plugin-entry.handlers.runtime.js"); + const { handleVerifyRecoveryKey } = await loadMatrixHandlersRuntimeModule(); await handleVerifyRecoveryKey(ctx); }); api.registerGatewayMethod("matrix.verify.bootstrap", async (ctx) => { - const { handleVerificationBootstrap } = await import("./plugin-entry.handlers.runtime.js"); + const { handleVerificationBootstrap } = await loadMatrixHandlersRuntimeModule(); await handleVerificationBootstrap(ctx); }); api.registerGatewayMethod("matrix.verify.status", async (ctx) => { - const { handleVerificationStatus } = await import("./plugin-entry.handlers.runtime.js"); + const { handleVerificationStatus } = await loadMatrixHandlersRuntimeModule(); await handleVerificationStatus(ctx); }); diff --git a/extensions/matrix/src/matrix/client/config.ts b/extensions/matrix/src/matrix/client/config.ts index f8cdf1f3a88..b2160eb1b40 100644 --- a/extensions/matrix/src/matrix/client/config.ts +++ b/extensions/matrix/src/matrix/client/config.ts @@ -44,12 +44,15 @@ type MatrixCredentialsReadDeps = { credentialsMatchConfig: typeof import("../credentials-read.js").credentialsMatchConfig; }; +type MatrixCredentialsWriteRuntime = typeof import("../credentials-write.runtime.js"); + type MatrixSecretInputDeps = { resolveConfiguredSecretInputString: typeof import("./config-secret-input.runtime.js").resolveConfiguredSecretInputString; }; let matrixAuthClientDepsPromise: Promise | undefined; let matrixCredentialsReadDepsPromise: Promise | undefined; +let matrixCredentialsWriteRuntimePromise: Promise | undefined; let matrixSecretInputDepsPromise: Promise | undefined; let matrixAuthClientDepsForTest: MatrixAuthClientDeps | undefined; @@ -87,6 +90,11 @@ async function loadMatrixCredentialsReadDeps(): Promise { + matrixCredentialsWriteRuntimePromise ??= import("../credentials-write.runtime.js"); + return await matrixCredentialsWriteRuntimePromise; +} + async function loadMatrixSecretInputDeps(): Promise { matrixSecretInputDepsPromise ??= import("./config-secret-input.runtime.js").then((runtime) => ({ resolveConfiguredSecretInputString: runtime.resolveConfiguredSecretInputString, @@ -740,12 +748,6 @@ export async function resolveMatrixAuth(params?: { const homeserver = await resolveValidatedMatrixHomeserverUrl(resolved.homeserver, { dangerouslyAllowPrivateNetwork: resolved.allowPrivateNetwork, }); - let credentialsWriter: typeof import("../credentials-write.runtime.js") | undefined; - const loadCredentialsWriter = async () => { - credentialsWriter ??= await import("../credentials-write.runtime.js"); - return credentialsWriter; - }; - const { loadMatrixCredentials, credentialsMatchConfig } = await loadMatrixCredentialsReadDeps(); const cached = loadMatrixCredentials(env, accountId); const cachedCredentials = @@ -790,7 +792,7 @@ export async function resolveMatrixAuth(params?: { cachedCredentials.userId !== userId || (cachedCredentials.deviceId || undefined) !== knownDeviceId; if (shouldRefreshCachedCredentials) { - const { saveMatrixCredentials } = await loadCredentialsWriter(); + const { saveMatrixCredentials } = await loadMatrixCredentialsWriteRuntime(); await saveMatrixCredentials( { homeserver, @@ -802,7 +804,7 @@ export async function resolveMatrixAuth(params?: { accountId, ); } else if (hasMatchingCachedToken) { - const { touchMatrixCredentials } = await loadCredentialsWriter(); + const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime(); await touchMatrixCredentials(env, accountId); } return { @@ -823,7 +825,7 @@ export async function resolveMatrixAuth(params?: { } if (cachedCredentials) { - const { touchMatrixCredentials } = await loadCredentialsWriter(); + const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime(); await touchMatrixCredentials(env, accountId); return { accountId, @@ -905,7 +907,7 @@ export async function resolveMatrixAuth(params?: { }), }; - const { saveMatrixCredentials } = await loadCredentialsWriter(); + const { saveMatrixCredentials } = await loadMatrixCredentialsWriteRuntime(); await saveMatrixCredentials( { homeserver: auth.homeserver, @@ -974,7 +976,7 @@ export async function backfillMatrixAuthDeviceIdAfterStartup(params: { return undefined; } - const credentialsWriter = await import("../credentials-write.runtime.js"); + const credentialsWriter = await loadMatrixCredentialsWriteRuntime(); const saved = await credentialsWriter.saveBackfilledMatrixDeviceId( { homeserver: params.auth.homeserver, diff --git a/extensions/matrix/src/matrix/credentials-write.runtime.ts b/extensions/matrix/src/matrix/credentials-write.runtime.ts index d18d9ae7aa7..d482d718f45 100644 --- a/extensions/matrix/src/matrix/credentials-write.runtime.ts +++ b/extensions/matrix/src/matrix/credentials-write.runtime.ts @@ -4,23 +4,32 @@ import type { touchMatrixCredentials as touchMatrixCredentialsType, } from "./credentials.js"; +type MatrixCredentialsRuntime = typeof import("./credentials.js"); + +let matrixCredentialsRuntimePromise: Promise | undefined; + +function loadMatrixCredentialsRuntime(): Promise { + matrixCredentialsRuntimePromise ??= import("./credentials.js"); + return matrixCredentialsRuntimePromise; +} + export async function saveMatrixCredentials( ...args: Parameters ): ReturnType { - const runtime = await import("./credentials.js"); + const runtime = await loadMatrixCredentialsRuntime(); return runtime.saveMatrixCredentials(...args); } export async function saveBackfilledMatrixDeviceId( ...args: Parameters ): ReturnType { - const runtime = await import("./credentials.js"); + const runtime = await loadMatrixCredentialsRuntime(); return runtime.saveBackfilledMatrixDeviceId(...args); } export async function touchMatrixCredentials( ...args: Parameters ): ReturnType { - const runtime = await import("./credentials.js"); + const runtime = await loadMatrixCredentialsRuntime(); return runtime.touchMatrixCredentials(...args); } diff --git a/extensions/matrix/src/plugin-entry.runtime.ts b/extensions/matrix/src/plugin-entry.runtime.ts index 2c62b5949eb..719f2d84e14 100644 --- a/extensions/matrix/src/plugin-entry.runtime.ts +++ b/extensions/matrix/src/plugin-entry.runtime.ts @@ -2,6 +2,15 @@ import type { GatewayRequestHandlerOptions } from "openclaw/plugin-sdk/gateway-r import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime"; import { formatMatrixErrorMessage } from "./matrix/errors.js"; +type MatrixVerificationRuntime = typeof import("./matrix/actions/verification.js"); + +let matrixVerificationRuntimePromise: Promise | undefined; + +function loadMatrixVerificationRuntime(): Promise { + matrixVerificationRuntimePromise ??= import("./matrix/actions/verification.js"); + return matrixVerificationRuntimePromise; +} + function sendError(respond: (ok: boolean, payload?: unknown) => void, err: unknown) { respond(false, { error: formatMatrixErrorMessage(err) }); } @@ -18,7 +27,7 @@ export async function handleVerifyRecoveryKey({ respond, }: GatewayRequestHandlerOptions): Promise { try { - const { verifyMatrixRecoveryKey } = await import("./matrix/actions/verification.js"); + const { verifyMatrixRecoveryKey } = await loadMatrixVerificationRuntime(); const key = normalizeOptionalString(params?.key); if (!key) { respond(false, { error: "key required" }); @@ -37,7 +46,7 @@ export async function handleVerificationBootstrap({ respond, }: GatewayRequestHandlerOptions): Promise { try { - const { bootstrapMatrixVerification } = await import("./matrix/actions/verification.js"); + const { bootstrapMatrixVerification } = await loadMatrixVerificationRuntime(); const accountId = normalizeOptionalString(params?.accountId); const recoveryKey = typeof params?.recoveryKey === "string" ? params.recoveryKey : undefined; const forceResetCrossSigning = params?.forceResetCrossSigning === true; @@ -57,7 +66,7 @@ export async function handleVerificationStatus({ respond, }: GatewayRequestHandlerOptions): Promise { try { - const { getMatrixVerificationStatus } = await import("./matrix/actions/verification.js"); + const { getMatrixVerificationStatus } = await loadMatrixVerificationRuntime(); const accountId = normalizeOptionalString(params?.accountId); const includeRecoveryKey = params?.includeRecoveryKey === true; const status = await getMatrixVerificationStatus({ accountId, includeRecoveryKey }); diff --git a/extensions/moonshot/src/kimi-web-search-provider.ts b/extensions/moonshot/src/kimi-web-search-provider.ts index 754ce022e71..0a8fefd1bde 100644 --- a/extensions/moonshot/src/kimi-web-search-provider.ts +++ b/extensions/moonshot/src/kimi-web-search-provider.ts @@ -5,6 +5,15 @@ import { } from "openclaw/plugin-sdk/provider-web-search-config-contract"; const KIMI_CREDENTIAL_PATH = "plugins.entries.moonshot.config.webSearch.apiKey"; +type KimiWebSearchProviderRuntime = typeof import("./kimi-web-search-provider.runtime.js"); + +let kimiWebSearchProviderRuntimePromise: Promise | undefined; + +function loadKimiWebSearchProviderRuntime(): Promise { + kimiWebSearchProviderRuntimePromise ??= import("./kimi-web-search-provider.runtime.js"); + return kimiWebSearchProviderRuntimePromise; +} + const KimiSearchSchema = { type: "object", properties: { @@ -26,7 +35,7 @@ const KimiSearchSchema = { async function runKimiSearchProviderSetup( ctx: WebSearchProviderSetupContext, ): Promise { - const runtime = await import("./kimi-web-search-provider.runtime.js"); + const runtime = await loadKimiWebSearchProviderRuntime(); return await runtime.runKimiSearchProviderSetup(ctx); } @@ -54,8 +63,7 @@ export function createKimiWebSearchProvider(): WebSearchProviderPlugin { "Search the web using Kimi by Moonshot. Returns AI-synthesized answers with citations from native $web_search.", parameters: KimiSearchSchema, execute: async (args) => { - const { executeKimiWebSearchProviderTool } = - await import("./kimi-web-search-provider.runtime.js"); + const { executeKimiWebSearchProviderTool } = await loadKimiWebSearchProviderRuntime(); return await executeKimiWebSearchProviderTool(ctx, args); }, }), diff --git a/extensions/qqbot/src/channel.ts b/extensions/qqbot/src/channel.ts index e9a72cd6674..55087809f4f 100644 --- a/extensions/qqbot/src/channel.ts +++ b/extensions/qqbot/src/channel.ts @@ -12,14 +12,23 @@ import { qqbotSetupWizard } from "./setup-surface.js"; export { chunkText, TEXT_CHUNK_LIMIT } from "./text-utils.js"; import type { ResolvedQQBotAccount } from "./types.js"; +type QQBotOutboundModule = typeof import("./outbound.js"); + // Shared promise so concurrent multi-account startups serialize the dynamic // import of the gateway module, avoiding an ESM circular-dependency race. let _gatewayModulePromise: Promise | undefined; +let _outboundModulePromise: Promise | undefined; + function loadGatewayModule(): Promise { _gatewayModulePromise ??= import("./gateway.js"); return _gatewayModulePromise; } +function loadOutboundModule(): Promise { + _outboundModulePromise ??= import("./outbound.js"); + return _outboundModulePromise; +} + export const qqbotPlugin: ChannelPlugin = { id: "qqbot", setupWizard: qqbotSetupWizard, @@ -91,7 +100,7 @@ export const qqbotPlugin: ChannelPlugin = { textChunkLimit: 5000, sendText: async ({ to, text, accountId, replyToId, cfg }) => { const account = resolveQQBotAccount(cfg, accountId); - const { sendText } = await import("./outbound.js"); + const { sendText } = await loadOutboundModule(); initApiConfig(account.appId, { markdownSupport: account.markdownSupport }); const result = await sendText({ to, text, accountId, replyToId, account }); return { @@ -102,7 +111,7 @@ export const qqbotPlugin: ChannelPlugin = { }, sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => { const account = resolveQQBotAccount(cfg, accountId); - const { sendMedia } = await import("./outbound.js"); + const { sendMedia } = await loadOutboundModule(); initApiConfig(account.appId, { markdownSupport: account.markdownSupport }); const result = await sendMedia({ to, diff --git a/extensions/whatsapp/src/outbound-adapter.ts b/extensions/whatsapp/src/outbound-adapter.ts index dd4ed99e86b..5045e715594 100644 --- a/extensions/whatsapp/src/outbound-adapter.ts +++ b/extensions/whatsapp/src/outbound-adapter.ts @@ -13,6 +13,15 @@ import { shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; import { WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps.js"; import { resolveWhatsAppOutboundTarget } from "./resolve-outbound-target.js"; +type WhatsAppSendModule = typeof import("./send.js"); + +let whatsAppSendModulePromise: Promise | undefined; + +function loadWhatsAppSendModule(): Promise { + whatsAppSendModulePromise ??= import("./send.js"); + return whatsAppSendModulePromise; +} + function trimLeadingWhitespace(text: string | undefined): string { return text?.trimStart() ?? ""; } @@ -54,7 +63,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = { const send = resolveOutboundSendDep(deps, "whatsapp", { legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, - }) ?? (await import("./send.js")).sendMessageWhatsApp; + }) ?? (await loadWhatsAppSendModule()).sendMessageWhatsApp; return await send(to, normalizedText, { verbose: false, cfg, @@ -77,7 +86,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = { const send = resolveOutboundSendDep(deps, "whatsapp", { legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, - }) ?? (await import("./send.js")).sendMessageWhatsApp; + }) ?? (await loadWhatsAppSendModule()).sendMessageWhatsApp; return await send(to, normalizedText, { verbose: false, cfg, @@ -90,7 +99,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = { }, sendPoll: async ({ cfg, to, poll, accountId }) => await ( - await import("./send.js") + await loadWhatsAppSendModule() ).sendPollWhatsApp(to, poll, { verbose: shouldLogVerbose(), accountId: accountId ?? undefined, diff --git a/extensions/xai/web-search.ts b/extensions/xai/web-search.ts index bfbca549cba..e57a97863c3 100644 --- a/extensions/xai/web-search.ts +++ b/extensions/xai/web-search.ts @@ -5,6 +5,15 @@ import { } from "openclaw/plugin-sdk/provider-web-search-config-contract"; const XAI_CREDENTIAL_PATH = "plugins.entries.xai.config.webSearch.apiKey"; +type XaiWebSearchProviderRuntime = typeof import("./src/web-search-provider.runtime.js"); + +let xaiWebSearchProviderRuntimePromise: Promise | undefined; + +function loadXaiWebSearchProviderRuntime(): Promise { + xaiWebSearchProviderRuntimePromise ??= import("./src/web-search-provider.runtime.js"); + return xaiWebSearchProviderRuntimePromise; +} + const GenericXaiSearchSchema = { type: "object", properties: { @@ -22,7 +31,7 @@ const GenericXaiSearchSchema = { async function runXaiSearchProviderSetup( ctx: WebSearchProviderSetupContext, ): Promise { - const runtime = await import("./src/web-search-provider.runtime.js"); + const runtime = await loadXaiWebSearchProviderRuntime(); return await runtime.runXaiSearchProviderSetup(ctx); } @@ -50,8 +59,7 @@ export function createXaiWebSearchProvider(): WebSearchProviderPlugin { "Search the web using xAI Grok. Returns AI-synthesized answers with citations from real-time web search.", parameters: GenericXaiSearchSchema, execute: async (args) => { - const { executeXaiWebSearchProviderTool } = - await import("./src/web-search-provider.runtime.js"); + const { executeXaiWebSearchProviderTool } = await loadXaiWebSearchProviderRuntime(); return await executeXaiWebSearchProviderTool(ctx, args); }, }), diff --git a/src/auto-reply/reply/agent-runner-memory.ts b/src/auto-reply/reply/agent-runner-memory.ts index 2ed953e1232..289aa8275a9 100644 --- a/src/auto-reply/reply/agent-runner-memory.ts +++ b/src/auto-reply/reply/agent-runner-memory.ts @@ -44,19 +44,28 @@ import { refreshQueuedFollowupSession, type FollowupRun } from "./queue.js"; import type { ReplyOperation } from "./reply-run-registry.js"; import { incrementCompactionCount } from "./session-updates.js"; +type PiEmbeddedRuntime = typeof import("../../agents/pi-embedded.js"); + +let piEmbeddedRuntimePromise: Promise | undefined; + +function loadPiEmbeddedRuntime(): Promise { + piEmbeddedRuntimePromise ??= import("../../agents/pi-embedded.js"); + return piEmbeddedRuntimePromise; +} + async function compactEmbeddedPiSessionDefault( ...args: Parameters ): Promise< Awaited> > { - const { compactEmbeddedPiSession } = await import("../../agents/pi-embedded.js"); + const { compactEmbeddedPiSession } = await loadPiEmbeddedRuntime(); return await compactEmbeddedPiSession(...args); } async function runEmbeddedPiAgentDefault( ...args: Parameters ): Promise>> { - const { runEmbeddedPiAgent } = await import("../../agents/pi-embedded.js"); + const { runEmbeddedPiAgent } = await loadPiEmbeddedRuntime(); return await runEmbeddedPiAgent(...args); } diff --git a/src/channels/plugins/stateful-target-builtins.ts b/src/channels/plugins/stateful-target-builtins.ts index 2c9cc45acf8..e78f168eec7 100644 --- a/src/channels/plugins/stateful-target-builtins.ts +++ b/src/channels/plugins/stateful-target-builtins.ts @@ -3,7 +3,15 @@ import { unregisterStatefulBindingTargetDriver, } from "./stateful-target-drivers.js"; +type AcpStatefulTargetDriverModule = typeof import("./acp-stateful-target-driver.js"); + let builtinsRegisteredPromise: Promise | null = null; +let acpDriverModulePromise: Promise | undefined; + +function loadAcpStatefulTargetDriverModule(): Promise { + acpDriverModulePromise ??= import("./acp-stateful-target-driver.js"); + return acpDriverModulePromise; +} export function isStatefulTargetBuiltinDriverId(id: string): boolean { return id.trim() === "acp"; @@ -15,7 +23,7 @@ export async function ensureStatefulTargetBuiltinsRegistered(): Promise { return; } builtinsRegisteredPromise = (async () => { - const { acpStatefulBindingTargetDriver } = await import("./acp-stateful-target-driver.js"); + const { acpStatefulBindingTargetDriver } = await loadAcpStatefulTargetDriverModule(); registerStatefulBindingTargetDriver(acpStatefulBindingTargetDriver); })(); try { @@ -28,6 +36,6 @@ export async function ensureStatefulTargetBuiltinsRegistered(): Promise { export async function resetStatefulTargetBuiltinsForTesting(): Promise { builtinsRegisteredPromise = null; - const { acpStatefulBindingTargetDriver } = await import("./acp-stateful-target-driver.js"); + const { acpStatefulBindingTargetDriver } = await loadAcpStatefulTargetDriverModule(); unregisterStatefulBindingTargetDriver(acpStatefulBindingTargetDriver.id); } diff --git a/src/cli/channels-cli.ts b/src/cli/channels-cli.ts index d2e7bf148f3..af777eafd6e 100644 --- a/src/cli/channels-cli.ts +++ b/src/cli/channels-cli.ts @@ -9,6 +9,8 @@ import { runCommandWithRuntime } from "./cli-utils.js"; import { hasExplicitOptions } from "./command-options.js"; import { formatHelpExamples } from "./help-format.js"; +type ChannelsCommandsModule = typeof import("../commands/channels.js"); + const optionNamesAdd = [ "channel", "account", @@ -49,6 +51,13 @@ const optionNamesAdd = [ const optionNamesRemove = ["channel", "account", "delete"] as const; +let channelsCommandsPromise: Promise | undefined; + +function loadChannelsCommands(): Promise { + channelsCommandsPromise ??= import("../commands/channels.js"); + return channelsCommandsPromise; +} + function runChannelsCommand(action: () => Promise) { return runCommandWithRuntime(defaultRuntime, action); } @@ -89,7 +98,7 @@ export function registerChannelsCli(program: Command) { .option("--json", "Output JSON", false) .action(async (opts) => { await runChannelsCommand(async () => { - const { channelsListCommand } = await import("../commands/channels.js"); + const { channelsListCommand } = await loadChannelsCommands(); await channelsListCommand(opts, defaultRuntime); }); }); @@ -102,7 +111,7 @@ export function registerChannelsCli(program: Command) { .option("--json", "Output JSON", false) .action(async (opts) => { await runChannelsCommand(async () => { - const { channelsStatusCommand } = await import("../commands/channels.js"); + const { channelsStatusCommand } = await loadChannelsCommands(); await channelsStatusCommand(opts, defaultRuntime); }); }); @@ -117,7 +126,7 @@ export function registerChannelsCli(program: Command) { .option("--json", "Output JSON", false) .action(async (opts) => { await runChannelsCommand(async () => { - const { channelsCapabilitiesCommand } = await import("../commands/channels.js"); + const { channelsCapabilitiesCommand } = await loadChannelsCommands(); await channelsCapabilitiesCommand(opts, defaultRuntime); }); }); @@ -132,7 +141,7 @@ export function registerChannelsCli(program: Command) { .option("--json", "Output JSON", false) .action(async (entries, opts) => { await runChannelsCommand(async () => { - const { channelsResolveCommand } = await import("../commands/channels.js"); + const { channelsResolveCommand } = await loadChannelsCommands(); await channelsResolveCommand( { channel: opts.channel as string | undefined, @@ -154,7 +163,7 @@ export function registerChannelsCli(program: Command) { .option("--json", "Output JSON", false) .action(async (opts) => { await runChannelsCommand(async () => { - const { channelsLogsCommand } = await import("../commands/channels.js"); + const { channelsLogsCommand } = await loadChannelsCommands(); await channelsLogsCommand(opts, defaultRuntime); }); }); @@ -200,7 +209,7 @@ export function registerChannelsCli(program: Command) { .option("--use-env", "Use env token (default account only)", false) .action(async (opts, command) => { await runChannelsCommand(async () => { - const { channelsAddCommand } = await import("../commands/channels.js"); + const { channelsAddCommand } = await loadChannelsCommands(); const hasFlags = hasExplicitOptions(command, optionNamesAdd); await channelsAddCommand(opts, defaultRuntime, { hasFlags }); }); @@ -214,7 +223,7 @@ export function registerChannelsCli(program: Command) { .option("--delete", "Delete config entries (no prompt)", false) .action(async (opts, command) => { await runChannelsCommand(async () => { - const { channelsRemoveCommand } = await import("../commands/channels.js"); + const { channelsRemoveCommand } = await loadChannelsCommands(); const hasFlags = hasExplicitOptions(command, optionNamesRemove); await channelsRemoveCommand(opts, defaultRuntime, { hasFlags }); }); diff --git a/src/cli/program/routed-command-definitions.ts b/src/cli/program/routed-command-definitions.ts index 67e18ea0659..d774c1d9d0c 100644 --- a/src/cli/program/routed-command-definitions.ts +++ b/src/cli/program/routed-command-definitions.ts @@ -14,6 +14,8 @@ import { type RouteArgParser = (argv: string[]) => TArgs | null; type ParsedRouteArgs> = Exclude, null>; +type ConfigCliModule = typeof import("../config-cli.js"); +type ModelsCommandsModule = typeof import("../../commands/models.js"); export type RoutedCommandDefinition> = { parseArgs: TParse; @@ -31,6 +33,19 @@ function defineRoutedCommand>( return definition; } +let configCliPromise: Promise | undefined; +let modelsCommandsPromise: Promise | undefined; + +function loadConfigCli(): Promise { + configCliPromise ??= import("../config-cli.js"); + return configCliPromise; +} + +function loadModelsCommands(): Promise { + modelsCommandsPromise ??= import("../../commands/models.js"); + return modelsCommandsPromise; +} + export const routedCommandDefinitions = { health: defineRoutedCommand({ parseArgs: parseHealthRouteArgs, @@ -83,28 +98,28 @@ export const routedCommandDefinitions = { "config-get": defineRoutedCommand({ parseArgs: parseConfigGetRouteArgs, runParsedArgs: async (args) => { - const { runConfigGet } = await import("../config-cli.js"); + const { runConfigGet } = await loadConfigCli(); await runConfigGet(args); }, }), "config-unset": defineRoutedCommand({ parseArgs: parseConfigUnsetRouteArgs, runParsedArgs: async (args) => { - const { runConfigUnset } = await import("../config-cli.js"); + const { runConfigUnset } = await loadConfigCli(); await runConfigUnset(args); }, }), "models-list": defineRoutedCommand({ parseArgs: parseModelsListRouteArgs, runParsedArgs: async (args) => { - const { modelsListCommand } = await import("../../commands/models.js"); + const { modelsListCommand } = await loadModelsCommands(); await modelsListCommand(args, defaultRuntime); }, }), "models-status": defineRoutedCommand({ parseArgs: parseModelsStatusRouteArgs, runParsedArgs: async (args) => { - const { modelsStatusCommand } = await import("../../commands/models.js"); + const { modelsStatusCommand } = await loadModelsCommands(); await modelsStatusCommand(args, defaultRuntime); }, }), diff --git a/src/commands/agents.commands.bind.ts b/src/commands/agents.commands.bind.ts index 865c6c41065..7fa5af4215b 100644 --- a/src/commands/agents.commands.bind.ts +++ b/src/commands/agents.commands.bind.ts @@ -8,6 +8,8 @@ import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { requireValidConfig, requireValidConfigFileSnapshot } from "./agents.command-shared.js"; +type AgentBindingsModule = typeof import("./agents.bindings.js"); + type AgentsBindingsListOptions = { agent?: string; json?: boolean; @@ -26,6 +28,13 @@ type AgentsUnbindOptions = { json?: boolean; }; +let agentBindingsModulePromise: Promise | undefined; + +function loadAgentBindingsModule(): Promise { + agentBindingsModulePromise ??= import("./agents.bindings.js"); + return agentBindingsModulePromise; +} + function describeBinding(binding: AgentRouteBinding): string { const match = binding.match; const parts = [match.channel]; @@ -123,7 +132,7 @@ async function resolveParsedBindingsOrExit(params: { return null; } - const { parseBindingSpecs } = await import("./agents.bindings.js"); + const { parseBindingSpecs } = await loadAgentBindingsModule(); const parsed = parseBindingSpecs({ agentId: params.agentId, specs, config: params.cfg }); if (parsed.errors.length > 0) { params.runtime.error(parsed.errors.join("\n")); @@ -248,7 +257,7 @@ export async function agentsBindCommand( return; } - const { applyAgentBindings } = await import("./agents.bindings.js"); + const { applyAgentBindings } = await loadAgentBindingsModule(); const result = applyAgentBindings(cfg, parsed.bindings); if (result.added.length > 0 || result.updated.length > 0) { await replaceConfigFile({ @@ -368,7 +377,7 @@ export async function agentsUnbindCommand( return; } - const { removeAgentBindings } = await import("./agents.bindings.js"); + const { removeAgentBindings } = await loadAgentBindingsModule(); const result = removeAgentBindings(cfg, parsed.bindings); if (result.removed.length > 0) { await replaceConfigFile({