refactor: cache repeated lazy imports

This commit is contained in:
Peter Steinberger
2026-04-18 16:31:48 +01:00
parent d13869aab9
commit cdaa70facb
13 changed files with 161 additions and 50 deletions

View File

@@ -5,17 +5,24 @@ import {
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { registerMatrixCliMetadata } from "./cli-metadata.js"; import { registerMatrixCliMetadata } from "./cli-metadata.js";
type MatrixHandlersRuntimeModule = typeof import("./plugin-entry.handlers.runtime.js");
type MatrixSubagentHooksModule = typeof import("./src/matrix/subagent-hooks.js"); type MatrixSubagentHooksModule = typeof import("./src/matrix/subagent-hooks.js");
let matrixHandlersRuntimePromise: Promise<MatrixHandlersRuntimeModule> | null = null;
let matrixSubagentHooksPromise: Promise<MatrixSubagentHooksModule> | null = null; let matrixSubagentHooksPromise: Promise<MatrixSubagentHooksModule> | null = null;
function loadMatrixHandlersRuntimeModule() {
matrixHandlersRuntimePromise ??= import("./plugin-entry.handlers.runtime.js");
return matrixHandlersRuntimePromise;
}
function loadMatrixSubagentHooksModule() { function loadMatrixSubagentHooksModule() {
matrixSubagentHooksPromise ??= import("./src/matrix/subagent-hooks.js"); matrixSubagentHooksPromise ??= import("./src/matrix/subagent-hooks.js");
return matrixSubagentHooksPromise; return matrixSubagentHooksPromise;
} }
export function registerMatrixFullRuntime(api: OpenClawPluginApi): void { export function registerMatrixFullRuntime(api: OpenClawPluginApi): void {
void import("./plugin-entry.handlers.runtime.js") void loadMatrixHandlersRuntimeModule()
.then(({ ensureMatrixCryptoRuntime }) => .then(({ ensureMatrixCryptoRuntime }) =>
ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err: unknown) => { ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err: unknown) => {
const message = formatErrorMessage(err); const message = formatErrorMessage(err);
@@ -28,17 +35,17 @@ export function registerMatrixFullRuntime(api: OpenClawPluginApi): void {
}); });
api.registerGatewayMethod("matrix.verify.recoveryKey", async (ctx) => { api.registerGatewayMethod("matrix.verify.recoveryKey", async (ctx) => {
const { handleVerifyRecoveryKey } = await import("./plugin-entry.handlers.runtime.js"); const { handleVerifyRecoveryKey } = await loadMatrixHandlersRuntimeModule();
await handleVerifyRecoveryKey(ctx); await handleVerifyRecoveryKey(ctx);
}); });
api.registerGatewayMethod("matrix.verify.bootstrap", async (ctx) => { api.registerGatewayMethod("matrix.verify.bootstrap", async (ctx) => {
const { handleVerificationBootstrap } = await import("./plugin-entry.handlers.runtime.js"); const { handleVerificationBootstrap } = await loadMatrixHandlersRuntimeModule();
await handleVerificationBootstrap(ctx); await handleVerificationBootstrap(ctx);
}); });
api.registerGatewayMethod("matrix.verify.status", async (ctx) => { api.registerGatewayMethod("matrix.verify.status", async (ctx) => {
const { handleVerificationStatus } = await import("./plugin-entry.handlers.runtime.js"); const { handleVerificationStatus } = await loadMatrixHandlersRuntimeModule();
await handleVerificationStatus(ctx); await handleVerificationStatus(ctx);
}); });

View File

@@ -44,12 +44,15 @@ type MatrixCredentialsReadDeps = {
credentialsMatchConfig: typeof import("../credentials-read.js").credentialsMatchConfig; credentialsMatchConfig: typeof import("../credentials-read.js").credentialsMatchConfig;
}; };
type MatrixCredentialsWriteRuntime = typeof import("../credentials-write.runtime.js");
type MatrixSecretInputDeps = { type MatrixSecretInputDeps = {
resolveConfiguredSecretInputString: typeof import("./config-secret-input.runtime.js").resolveConfiguredSecretInputString; resolveConfiguredSecretInputString: typeof import("./config-secret-input.runtime.js").resolveConfiguredSecretInputString;
}; };
let matrixAuthClientDepsPromise: Promise<MatrixAuthClientDeps> | undefined; let matrixAuthClientDepsPromise: Promise<MatrixAuthClientDeps> | undefined;
let matrixCredentialsReadDepsPromise: Promise<MatrixCredentialsReadDeps> | undefined; let matrixCredentialsReadDepsPromise: Promise<MatrixCredentialsReadDeps> | undefined;
let matrixCredentialsWriteRuntimePromise: Promise<MatrixCredentialsWriteRuntime> | undefined;
let matrixSecretInputDepsPromise: Promise<MatrixSecretInputDeps> | undefined; let matrixSecretInputDepsPromise: Promise<MatrixSecretInputDeps> | undefined;
let matrixAuthClientDepsForTest: MatrixAuthClientDeps | undefined; let matrixAuthClientDepsForTest: MatrixAuthClientDeps | undefined;
@@ -87,6 +90,11 @@ async function loadMatrixCredentialsReadDeps(): Promise<MatrixCredentialsReadDep
return await matrixCredentialsReadDepsPromise; return await matrixCredentialsReadDepsPromise;
} }
async function loadMatrixCredentialsWriteRuntime(): Promise<MatrixCredentialsWriteRuntime> {
matrixCredentialsWriteRuntimePromise ??= import("../credentials-write.runtime.js");
return await matrixCredentialsWriteRuntimePromise;
}
async function loadMatrixSecretInputDeps(): Promise<MatrixSecretInputDeps> { async function loadMatrixSecretInputDeps(): Promise<MatrixSecretInputDeps> {
matrixSecretInputDepsPromise ??= import("./config-secret-input.runtime.js").then((runtime) => ({ matrixSecretInputDepsPromise ??= import("./config-secret-input.runtime.js").then((runtime) => ({
resolveConfiguredSecretInputString: runtime.resolveConfiguredSecretInputString, resolveConfiguredSecretInputString: runtime.resolveConfiguredSecretInputString,
@@ -740,12 +748,6 @@ export async function resolveMatrixAuth(params?: {
const homeserver = await resolveValidatedMatrixHomeserverUrl(resolved.homeserver, { const homeserver = await resolveValidatedMatrixHomeserverUrl(resolved.homeserver, {
dangerouslyAllowPrivateNetwork: resolved.allowPrivateNetwork, 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 { loadMatrixCredentials, credentialsMatchConfig } = await loadMatrixCredentialsReadDeps();
const cached = loadMatrixCredentials(env, accountId); const cached = loadMatrixCredentials(env, accountId);
const cachedCredentials = const cachedCredentials =
@@ -790,7 +792,7 @@ export async function resolveMatrixAuth(params?: {
cachedCredentials.userId !== userId || cachedCredentials.userId !== userId ||
(cachedCredentials.deviceId || undefined) !== knownDeviceId; (cachedCredentials.deviceId || undefined) !== knownDeviceId;
if (shouldRefreshCachedCredentials) { if (shouldRefreshCachedCredentials) {
const { saveMatrixCredentials } = await loadCredentialsWriter(); const { saveMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await saveMatrixCredentials( await saveMatrixCredentials(
{ {
homeserver, homeserver,
@@ -802,7 +804,7 @@ export async function resolveMatrixAuth(params?: {
accountId, accountId,
); );
} else if (hasMatchingCachedToken) { } else if (hasMatchingCachedToken) {
const { touchMatrixCredentials } = await loadCredentialsWriter(); const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await touchMatrixCredentials(env, accountId); await touchMatrixCredentials(env, accountId);
} }
return { return {
@@ -823,7 +825,7 @@ export async function resolveMatrixAuth(params?: {
} }
if (cachedCredentials) { if (cachedCredentials) {
const { touchMatrixCredentials } = await loadCredentialsWriter(); const { touchMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await touchMatrixCredentials(env, accountId); await touchMatrixCredentials(env, accountId);
return { return {
accountId, accountId,
@@ -905,7 +907,7 @@ export async function resolveMatrixAuth(params?: {
}), }),
}; };
const { saveMatrixCredentials } = await loadCredentialsWriter(); const { saveMatrixCredentials } = await loadMatrixCredentialsWriteRuntime();
await saveMatrixCredentials( await saveMatrixCredentials(
{ {
homeserver: auth.homeserver, homeserver: auth.homeserver,
@@ -974,7 +976,7 @@ export async function backfillMatrixAuthDeviceIdAfterStartup(params: {
return undefined; return undefined;
} }
const credentialsWriter = await import("../credentials-write.runtime.js"); const credentialsWriter = await loadMatrixCredentialsWriteRuntime();
const saved = await credentialsWriter.saveBackfilledMatrixDeviceId( const saved = await credentialsWriter.saveBackfilledMatrixDeviceId(
{ {
homeserver: params.auth.homeserver, homeserver: params.auth.homeserver,

View File

@@ -4,23 +4,32 @@ import type {
touchMatrixCredentials as touchMatrixCredentialsType, touchMatrixCredentials as touchMatrixCredentialsType,
} from "./credentials.js"; } from "./credentials.js";
type MatrixCredentialsRuntime = typeof import("./credentials.js");
let matrixCredentialsRuntimePromise: Promise<MatrixCredentialsRuntime> | undefined;
function loadMatrixCredentialsRuntime(): Promise<MatrixCredentialsRuntime> {
matrixCredentialsRuntimePromise ??= import("./credentials.js");
return matrixCredentialsRuntimePromise;
}
export async function saveMatrixCredentials( export async function saveMatrixCredentials(
...args: Parameters<typeof saveMatrixCredentialsType> ...args: Parameters<typeof saveMatrixCredentialsType>
): ReturnType<typeof saveMatrixCredentialsType> { ): ReturnType<typeof saveMatrixCredentialsType> {
const runtime = await import("./credentials.js"); const runtime = await loadMatrixCredentialsRuntime();
return runtime.saveMatrixCredentials(...args); return runtime.saveMatrixCredentials(...args);
} }
export async function saveBackfilledMatrixDeviceId( export async function saveBackfilledMatrixDeviceId(
...args: Parameters<typeof saveBackfilledMatrixDeviceIdType> ...args: Parameters<typeof saveBackfilledMatrixDeviceIdType>
): ReturnType<typeof saveBackfilledMatrixDeviceIdType> { ): ReturnType<typeof saveBackfilledMatrixDeviceIdType> {
const runtime = await import("./credentials.js"); const runtime = await loadMatrixCredentialsRuntime();
return runtime.saveBackfilledMatrixDeviceId(...args); return runtime.saveBackfilledMatrixDeviceId(...args);
} }
export async function touchMatrixCredentials( export async function touchMatrixCredentials(
...args: Parameters<typeof touchMatrixCredentialsType> ...args: Parameters<typeof touchMatrixCredentialsType>
): ReturnType<typeof touchMatrixCredentialsType> { ): ReturnType<typeof touchMatrixCredentialsType> {
const runtime = await import("./credentials.js"); const runtime = await loadMatrixCredentialsRuntime();
return runtime.touchMatrixCredentials(...args); return runtime.touchMatrixCredentials(...args);
} }

View File

@@ -2,6 +2,15 @@ import type { GatewayRequestHandlerOptions } from "openclaw/plugin-sdk/gateway-r
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime"; import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
import { formatMatrixErrorMessage } from "./matrix/errors.js"; import { formatMatrixErrorMessage } from "./matrix/errors.js";
type MatrixVerificationRuntime = typeof import("./matrix/actions/verification.js");
let matrixVerificationRuntimePromise: Promise<MatrixVerificationRuntime> | undefined;
function loadMatrixVerificationRuntime(): Promise<MatrixVerificationRuntime> {
matrixVerificationRuntimePromise ??= import("./matrix/actions/verification.js");
return matrixVerificationRuntimePromise;
}
function sendError(respond: (ok: boolean, payload?: unknown) => void, err: unknown) { function sendError(respond: (ok: boolean, payload?: unknown) => void, err: unknown) {
respond(false, { error: formatMatrixErrorMessage(err) }); respond(false, { error: formatMatrixErrorMessage(err) });
} }
@@ -18,7 +27,7 @@ export async function handleVerifyRecoveryKey({
respond, respond,
}: GatewayRequestHandlerOptions): Promise<void> { }: GatewayRequestHandlerOptions): Promise<void> {
try { try {
const { verifyMatrixRecoveryKey } = await import("./matrix/actions/verification.js"); const { verifyMatrixRecoveryKey } = await loadMatrixVerificationRuntime();
const key = normalizeOptionalString(params?.key); const key = normalizeOptionalString(params?.key);
if (!key) { if (!key) {
respond(false, { error: "key required" }); respond(false, { error: "key required" });
@@ -37,7 +46,7 @@ export async function handleVerificationBootstrap({
respond, respond,
}: GatewayRequestHandlerOptions): Promise<void> { }: GatewayRequestHandlerOptions): Promise<void> {
try { try {
const { bootstrapMatrixVerification } = await import("./matrix/actions/verification.js"); const { bootstrapMatrixVerification } = await loadMatrixVerificationRuntime();
const accountId = normalizeOptionalString(params?.accountId); const accountId = normalizeOptionalString(params?.accountId);
const recoveryKey = typeof params?.recoveryKey === "string" ? params.recoveryKey : undefined; const recoveryKey = typeof params?.recoveryKey === "string" ? params.recoveryKey : undefined;
const forceResetCrossSigning = params?.forceResetCrossSigning === true; const forceResetCrossSigning = params?.forceResetCrossSigning === true;
@@ -57,7 +66,7 @@ export async function handleVerificationStatus({
respond, respond,
}: GatewayRequestHandlerOptions): Promise<void> { }: GatewayRequestHandlerOptions): Promise<void> {
try { try {
const { getMatrixVerificationStatus } = await import("./matrix/actions/verification.js"); const { getMatrixVerificationStatus } = await loadMatrixVerificationRuntime();
const accountId = normalizeOptionalString(params?.accountId); const accountId = normalizeOptionalString(params?.accountId);
const includeRecoveryKey = params?.includeRecoveryKey === true; const includeRecoveryKey = params?.includeRecoveryKey === true;
const status = await getMatrixVerificationStatus({ accountId, includeRecoveryKey }); const status = await getMatrixVerificationStatus({ accountId, includeRecoveryKey });

View File

@@ -5,6 +5,15 @@ import {
} from "openclaw/plugin-sdk/provider-web-search-config-contract"; } from "openclaw/plugin-sdk/provider-web-search-config-contract";
const KIMI_CREDENTIAL_PATH = "plugins.entries.moonshot.config.webSearch.apiKey"; const KIMI_CREDENTIAL_PATH = "plugins.entries.moonshot.config.webSearch.apiKey";
type KimiWebSearchProviderRuntime = typeof import("./kimi-web-search-provider.runtime.js");
let kimiWebSearchProviderRuntimePromise: Promise<KimiWebSearchProviderRuntime> | undefined;
function loadKimiWebSearchProviderRuntime(): Promise<KimiWebSearchProviderRuntime> {
kimiWebSearchProviderRuntimePromise ??= import("./kimi-web-search-provider.runtime.js");
return kimiWebSearchProviderRuntimePromise;
}
const KimiSearchSchema = { const KimiSearchSchema = {
type: "object", type: "object",
properties: { properties: {
@@ -26,7 +35,7 @@ const KimiSearchSchema = {
async function runKimiSearchProviderSetup( async function runKimiSearchProviderSetup(
ctx: WebSearchProviderSetupContext, ctx: WebSearchProviderSetupContext,
): Promise<WebSearchProviderSetupContext["config"]> { ): Promise<WebSearchProviderSetupContext["config"]> {
const runtime = await import("./kimi-web-search-provider.runtime.js"); const runtime = await loadKimiWebSearchProviderRuntime();
return await runtime.runKimiSearchProviderSetup(ctx); 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.", "Search the web using Kimi by Moonshot. Returns AI-synthesized answers with citations from native $web_search.",
parameters: KimiSearchSchema, parameters: KimiSearchSchema,
execute: async (args) => { execute: async (args) => {
const { executeKimiWebSearchProviderTool } = const { executeKimiWebSearchProviderTool } = await loadKimiWebSearchProviderRuntime();
await import("./kimi-web-search-provider.runtime.js");
return await executeKimiWebSearchProviderTool(ctx, args); return await executeKimiWebSearchProviderTool(ctx, args);
}, },
}), }),

View File

@@ -12,14 +12,23 @@ import { qqbotSetupWizard } from "./setup-surface.js";
export { chunkText, TEXT_CHUNK_LIMIT } from "./text-utils.js"; export { chunkText, TEXT_CHUNK_LIMIT } from "./text-utils.js";
import type { ResolvedQQBotAccount } from "./types.js"; import type { ResolvedQQBotAccount } from "./types.js";
type QQBotOutboundModule = typeof import("./outbound.js");
// Shared promise so concurrent multi-account startups serialize the dynamic // Shared promise so concurrent multi-account startups serialize the dynamic
// import of the gateway module, avoiding an ESM circular-dependency race. // import of the gateway module, avoiding an ESM circular-dependency race.
let _gatewayModulePromise: Promise<typeof import("./gateway.js")> | undefined; let _gatewayModulePromise: Promise<typeof import("./gateway.js")> | undefined;
let _outboundModulePromise: Promise<QQBotOutboundModule> | undefined;
function loadGatewayModule(): Promise<typeof import("./gateway.js")> { function loadGatewayModule(): Promise<typeof import("./gateway.js")> {
_gatewayModulePromise ??= import("./gateway.js"); _gatewayModulePromise ??= import("./gateway.js");
return _gatewayModulePromise; return _gatewayModulePromise;
} }
function loadOutboundModule(): Promise<QQBotOutboundModule> {
_outboundModulePromise ??= import("./outbound.js");
return _outboundModulePromise;
}
export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = { export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
id: "qqbot", id: "qqbot",
setupWizard: qqbotSetupWizard, setupWizard: qqbotSetupWizard,
@@ -91,7 +100,7 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
textChunkLimit: 5000, textChunkLimit: 5000,
sendText: async ({ to, text, accountId, replyToId, cfg }) => { sendText: async ({ to, text, accountId, replyToId, cfg }) => {
const account = resolveQQBotAccount(cfg, accountId); const account = resolveQQBotAccount(cfg, accountId);
const { sendText } = await import("./outbound.js"); const { sendText } = await loadOutboundModule();
initApiConfig(account.appId, { markdownSupport: account.markdownSupport }); initApiConfig(account.appId, { markdownSupport: account.markdownSupport });
const result = await sendText({ to, text, accountId, replyToId, account }); const result = await sendText({ to, text, accountId, replyToId, account });
return { return {
@@ -102,7 +111,7 @@ export const qqbotPlugin: ChannelPlugin<ResolvedQQBotAccount> = {
}, },
sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => { sendMedia: async ({ to, text, mediaUrl, accountId, replyToId, cfg }) => {
const account = resolveQQBotAccount(cfg, accountId); const account = resolveQQBotAccount(cfg, accountId);
const { sendMedia } = await import("./outbound.js"); const { sendMedia } = await loadOutboundModule();
initApiConfig(account.appId, { markdownSupport: account.markdownSupport }); initApiConfig(account.appId, { markdownSupport: account.markdownSupport });
const result = await sendMedia({ const result = await sendMedia({
to, to,

View File

@@ -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 { WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS } from "./outbound-send-deps.js";
import { resolveWhatsAppOutboundTarget } from "./resolve-outbound-target.js"; import { resolveWhatsAppOutboundTarget } from "./resolve-outbound-target.js";
type WhatsAppSendModule = typeof import("./send.js");
let whatsAppSendModulePromise: Promise<WhatsAppSendModule> | undefined;
function loadWhatsAppSendModule(): Promise<WhatsAppSendModule> {
whatsAppSendModulePromise ??= import("./send.js");
return whatsAppSendModulePromise;
}
function trimLeadingWhitespace(text: string | undefined): string { function trimLeadingWhitespace(text: string | undefined): string {
return text?.trimStart() ?? ""; return text?.trimStart() ?? "";
} }
@@ -54,7 +63,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = {
const send = const send =
resolveOutboundSendDep<typeof import("./send.js").sendMessageWhatsApp>(deps, "whatsapp", { resolveOutboundSendDep<typeof import("./send.js").sendMessageWhatsApp>(deps, "whatsapp", {
legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS,
}) ?? (await import("./send.js")).sendMessageWhatsApp; }) ?? (await loadWhatsAppSendModule()).sendMessageWhatsApp;
return await send(to, normalizedText, { return await send(to, normalizedText, {
verbose: false, verbose: false,
cfg, cfg,
@@ -77,7 +86,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = {
const send = const send =
resolveOutboundSendDep<typeof import("./send.js").sendMessageWhatsApp>(deps, "whatsapp", { resolveOutboundSendDep<typeof import("./send.js").sendMessageWhatsApp>(deps, "whatsapp", {
legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS, legacyKeys: WHATSAPP_LEGACY_OUTBOUND_SEND_DEP_KEYS,
}) ?? (await import("./send.js")).sendMessageWhatsApp; }) ?? (await loadWhatsAppSendModule()).sendMessageWhatsApp;
return await send(to, normalizedText, { return await send(to, normalizedText, {
verbose: false, verbose: false,
cfg, cfg,
@@ -90,7 +99,7 @@ export const whatsappOutbound: ChannelOutboundAdapter = {
}, },
sendPoll: async ({ cfg, to, poll, accountId }) => sendPoll: async ({ cfg, to, poll, accountId }) =>
await ( await (
await import("./send.js") await loadWhatsAppSendModule()
).sendPollWhatsApp(to, poll, { ).sendPollWhatsApp(to, poll, {
verbose: shouldLogVerbose(), verbose: shouldLogVerbose(),
accountId: accountId ?? undefined, accountId: accountId ?? undefined,

View File

@@ -5,6 +5,15 @@ import {
} from "openclaw/plugin-sdk/provider-web-search-config-contract"; } from "openclaw/plugin-sdk/provider-web-search-config-contract";
const XAI_CREDENTIAL_PATH = "plugins.entries.xai.config.webSearch.apiKey"; const XAI_CREDENTIAL_PATH = "plugins.entries.xai.config.webSearch.apiKey";
type XaiWebSearchProviderRuntime = typeof import("./src/web-search-provider.runtime.js");
let xaiWebSearchProviderRuntimePromise: Promise<XaiWebSearchProviderRuntime> | undefined;
function loadXaiWebSearchProviderRuntime(): Promise<XaiWebSearchProviderRuntime> {
xaiWebSearchProviderRuntimePromise ??= import("./src/web-search-provider.runtime.js");
return xaiWebSearchProviderRuntimePromise;
}
const GenericXaiSearchSchema = { const GenericXaiSearchSchema = {
type: "object", type: "object",
properties: { properties: {
@@ -22,7 +31,7 @@ const GenericXaiSearchSchema = {
async function runXaiSearchProviderSetup( async function runXaiSearchProviderSetup(
ctx: WebSearchProviderSetupContext, ctx: WebSearchProviderSetupContext,
): Promise<WebSearchProviderSetupContext["config"]> { ): Promise<WebSearchProviderSetupContext["config"]> {
const runtime = await import("./src/web-search-provider.runtime.js"); const runtime = await loadXaiWebSearchProviderRuntime();
return await runtime.runXaiSearchProviderSetup(ctx); 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.", "Search the web using xAI Grok. Returns AI-synthesized answers with citations from real-time web search.",
parameters: GenericXaiSearchSchema, parameters: GenericXaiSearchSchema,
execute: async (args) => { execute: async (args) => {
const { executeXaiWebSearchProviderTool } = const { executeXaiWebSearchProviderTool } = await loadXaiWebSearchProviderRuntime();
await import("./src/web-search-provider.runtime.js");
return await executeXaiWebSearchProviderTool(ctx, args); return await executeXaiWebSearchProviderTool(ctx, args);
}, },
}), }),

View File

@@ -44,19 +44,28 @@ import { refreshQueuedFollowupSession, type FollowupRun } from "./queue.js";
import type { ReplyOperation } from "./reply-run-registry.js"; import type { ReplyOperation } from "./reply-run-registry.js";
import { incrementCompactionCount } from "./session-updates.js"; import { incrementCompactionCount } from "./session-updates.js";
type PiEmbeddedRuntime = typeof import("../../agents/pi-embedded.js");
let piEmbeddedRuntimePromise: Promise<PiEmbeddedRuntime> | undefined;
function loadPiEmbeddedRuntime(): Promise<PiEmbeddedRuntime> {
piEmbeddedRuntimePromise ??= import("../../agents/pi-embedded.js");
return piEmbeddedRuntimePromise;
}
async function compactEmbeddedPiSessionDefault( async function compactEmbeddedPiSessionDefault(
...args: Parameters<typeof import("../../agents/pi-embedded.js").compactEmbeddedPiSession> ...args: Parameters<typeof import("../../agents/pi-embedded.js").compactEmbeddedPiSession>
): Promise< ): Promise<
Awaited<ReturnType<typeof import("../../agents/pi-embedded.js").compactEmbeddedPiSession>> Awaited<ReturnType<typeof import("../../agents/pi-embedded.js").compactEmbeddedPiSession>>
> { > {
const { compactEmbeddedPiSession } = await import("../../agents/pi-embedded.js"); const { compactEmbeddedPiSession } = await loadPiEmbeddedRuntime();
return await compactEmbeddedPiSession(...args); return await compactEmbeddedPiSession(...args);
} }
async function runEmbeddedPiAgentDefault( async function runEmbeddedPiAgentDefault(
...args: Parameters<typeof import("../../agents/pi-embedded.js").runEmbeddedPiAgent> ...args: Parameters<typeof import("../../agents/pi-embedded.js").runEmbeddedPiAgent>
): Promise<Awaited<ReturnType<typeof import("../../agents/pi-embedded.js").runEmbeddedPiAgent>>> { ): Promise<Awaited<ReturnType<typeof import("../../agents/pi-embedded.js").runEmbeddedPiAgent>>> {
const { runEmbeddedPiAgent } = await import("../../agents/pi-embedded.js"); const { runEmbeddedPiAgent } = await loadPiEmbeddedRuntime();
return await runEmbeddedPiAgent(...args); return await runEmbeddedPiAgent(...args);
} }

View File

@@ -3,7 +3,15 @@ import {
unregisterStatefulBindingTargetDriver, unregisterStatefulBindingTargetDriver,
} from "./stateful-target-drivers.js"; } from "./stateful-target-drivers.js";
type AcpStatefulTargetDriverModule = typeof import("./acp-stateful-target-driver.js");
let builtinsRegisteredPromise: Promise<void> | null = null; let builtinsRegisteredPromise: Promise<void> | null = null;
let acpDriverModulePromise: Promise<AcpStatefulTargetDriverModule> | undefined;
function loadAcpStatefulTargetDriverModule(): Promise<AcpStatefulTargetDriverModule> {
acpDriverModulePromise ??= import("./acp-stateful-target-driver.js");
return acpDriverModulePromise;
}
export function isStatefulTargetBuiltinDriverId(id: string): boolean { export function isStatefulTargetBuiltinDriverId(id: string): boolean {
return id.trim() === "acp"; return id.trim() === "acp";
@@ -15,7 +23,7 @@ export async function ensureStatefulTargetBuiltinsRegistered(): Promise<void> {
return; return;
} }
builtinsRegisteredPromise = (async () => { builtinsRegisteredPromise = (async () => {
const { acpStatefulBindingTargetDriver } = await import("./acp-stateful-target-driver.js"); const { acpStatefulBindingTargetDriver } = await loadAcpStatefulTargetDriverModule();
registerStatefulBindingTargetDriver(acpStatefulBindingTargetDriver); registerStatefulBindingTargetDriver(acpStatefulBindingTargetDriver);
})(); })();
try { try {
@@ -28,6 +36,6 @@ export async function ensureStatefulTargetBuiltinsRegistered(): Promise<void> {
export async function resetStatefulTargetBuiltinsForTesting(): Promise<void> { export async function resetStatefulTargetBuiltinsForTesting(): Promise<void> {
builtinsRegisteredPromise = null; builtinsRegisteredPromise = null;
const { acpStatefulBindingTargetDriver } = await import("./acp-stateful-target-driver.js"); const { acpStatefulBindingTargetDriver } = await loadAcpStatefulTargetDriverModule();
unregisterStatefulBindingTargetDriver(acpStatefulBindingTargetDriver.id); unregisterStatefulBindingTargetDriver(acpStatefulBindingTargetDriver.id);
} }

View File

@@ -9,6 +9,8 @@ import { runCommandWithRuntime } from "./cli-utils.js";
import { hasExplicitOptions } from "./command-options.js"; import { hasExplicitOptions } from "./command-options.js";
import { formatHelpExamples } from "./help-format.js"; import { formatHelpExamples } from "./help-format.js";
type ChannelsCommandsModule = typeof import("../commands/channels.js");
const optionNamesAdd = [ const optionNamesAdd = [
"channel", "channel",
"account", "account",
@@ -49,6 +51,13 @@ const optionNamesAdd = [
const optionNamesRemove = ["channel", "account", "delete"] as const; const optionNamesRemove = ["channel", "account", "delete"] as const;
let channelsCommandsPromise: Promise<ChannelsCommandsModule> | undefined;
function loadChannelsCommands(): Promise<ChannelsCommandsModule> {
channelsCommandsPromise ??= import("../commands/channels.js");
return channelsCommandsPromise;
}
function runChannelsCommand(action: () => Promise<void>) { function runChannelsCommand(action: () => Promise<void>) {
return runCommandWithRuntime(defaultRuntime, action); return runCommandWithRuntime(defaultRuntime, action);
} }
@@ -89,7 +98,7 @@ export function registerChannelsCli(program: Command) {
.option("--json", "Output JSON", false) .option("--json", "Output JSON", false)
.action(async (opts) => { .action(async (opts) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsListCommand } = await import("../commands/channels.js"); const { channelsListCommand } = await loadChannelsCommands();
await channelsListCommand(opts, defaultRuntime); await channelsListCommand(opts, defaultRuntime);
}); });
}); });
@@ -102,7 +111,7 @@ export function registerChannelsCli(program: Command) {
.option("--json", "Output JSON", false) .option("--json", "Output JSON", false)
.action(async (opts) => { .action(async (opts) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsStatusCommand } = await import("../commands/channels.js"); const { channelsStatusCommand } = await loadChannelsCommands();
await channelsStatusCommand(opts, defaultRuntime); await channelsStatusCommand(opts, defaultRuntime);
}); });
}); });
@@ -117,7 +126,7 @@ export function registerChannelsCli(program: Command) {
.option("--json", "Output JSON", false) .option("--json", "Output JSON", false)
.action(async (opts) => { .action(async (opts) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsCapabilitiesCommand } = await import("../commands/channels.js"); const { channelsCapabilitiesCommand } = await loadChannelsCommands();
await channelsCapabilitiesCommand(opts, defaultRuntime); await channelsCapabilitiesCommand(opts, defaultRuntime);
}); });
}); });
@@ -132,7 +141,7 @@ export function registerChannelsCli(program: Command) {
.option("--json", "Output JSON", false) .option("--json", "Output JSON", false)
.action(async (entries, opts) => { .action(async (entries, opts) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsResolveCommand } = await import("../commands/channels.js"); const { channelsResolveCommand } = await loadChannelsCommands();
await channelsResolveCommand( await channelsResolveCommand(
{ {
channel: opts.channel as string | undefined, channel: opts.channel as string | undefined,
@@ -154,7 +163,7 @@ export function registerChannelsCli(program: Command) {
.option("--json", "Output JSON", false) .option("--json", "Output JSON", false)
.action(async (opts) => { .action(async (opts) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsLogsCommand } = await import("../commands/channels.js"); const { channelsLogsCommand } = await loadChannelsCommands();
await channelsLogsCommand(opts, defaultRuntime); await channelsLogsCommand(opts, defaultRuntime);
}); });
}); });
@@ -200,7 +209,7 @@ export function registerChannelsCli(program: Command) {
.option("--use-env", "Use env token (default account only)", false) .option("--use-env", "Use env token (default account only)", false)
.action(async (opts, command) => { .action(async (opts, command) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsAddCommand } = await import("../commands/channels.js"); const { channelsAddCommand } = await loadChannelsCommands();
const hasFlags = hasExplicitOptions(command, optionNamesAdd); const hasFlags = hasExplicitOptions(command, optionNamesAdd);
await channelsAddCommand(opts, defaultRuntime, { hasFlags }); await channelsAddCommand(opts, defaultRuntime, { hasFlags });
}); });
@@ -214,7 +223,7 @@ export function registerChannelsCli(program: Command) {
.option("--delete", "Delete config entries (no prompt)", false) .option("--delete", "Delete config entries (no prompt)", false)
.action(async (opts, command) => { .action(async (opts, command) => {
await runChannelsCommand(async () => { await runChannelsCommand(async () => {
const { channelsRemoveCommand } = await import("../commands/channels.js"); const { channelsRemoveCommand } = await loadChannelsCommands();
const hasFlags = hasExplicitOptions(command, optionNamesRemove); const hasFlags = hasExplicitOptions(command, optionNamesRemove);
await channelsRemoveCommand(opts, defaultRuntime, { hasFlags }); await channelsRemoveCommand(opts, defaultRuntime, { hasFlags });
}); });

View File

@@ -14,6 +14,8 @@ import {
type RouteArgParser<TArgs> = (argv: string[]) => TArgs | null; type RouteArgParser<TArgs> = (argv: string[]) => TArgs | null;
type ParsedRouteArgs<TParse extends RouteArgParser<unknown>> = Exclude<ReturnType<TParse>, null>; type ParsedRouteArgs<TParse extends RouteArgParser<unknown>> = Exclude<ReturnType<TParse>, null>;
type ConfigCliModule = typeof import("../config-cli.js");
type ModelsCommandsModule = typeof import("../../commands/models.js");
export type RoutedCommandDefinition<TParse extends RouteArgParser<unknown>> = { export type RoutedCommandDefinition<TParse extends RouteArgParser<unknown>> = {
parseArgs: TParse; parseArgs: TParse;
@@ -31,6 +33,19 @@ function defineRoutedCommand<TParse extends RouteArgParser<unknown>>(
return definition; return definition;
} }
let configCliPromise: Promise<ConfigCliModule> | undefined;
let modelsCommandsPromise: Promise<ModelsCommandsModule> | undefined;
function loadConfigCli(): Promise<ConfigCliModule> {
configCliPromise ??= import("../config-cli.js");
return configCliPromise;
}
function loadModelsCommands(): Promise<ModelsCommandsModule> {
modelsCommandsPromise ??= import("../../commands/models.js");
return modelsCommandsPromise;
}
export const routedCommandDefinitions = { export const routedCommandDefinitions = {
health: defineRoutedCommand({ health: defineRoutedCommand({
parseArgs: parseHealthRouteArgs, parseArgs: parseHealthRouteArgs,
@@ -83,28 +98,28 @@ export const routedCommandDefinitions = {
"config-get": defineRoutedCommand({ "config-get": defineRoutedCommand({
parseArgs: parseConfigGetRouteArgs, parseArgs: parseConfigGetRouteArgs,
runParsedArgs: async (args) => { runParsedArgs: async (args) => {
const { runConfigGet } = await import("../config-cli.js"); const { runConfigGet } = await loadConfigCli();
await runConfigGet(args); await runConfigGet(args);
}, },
}), }),
"config-unset": defineRoutedCommand({ "config-unset": defineRoutedCommand({
parseArgs: parseConfigUnsetRouteArgs, parseArgs: parseConfigUnsetRouteArgs,
runParsedArgs: async (args) => { runParsedArgs: async (args) => {
const { runConfigUnset } = await import("../config-cli.js"); const { runConfigUnset } = await loadConfigCli();
await runConfigUnset(args); await runConfigUnset(args);
}, },
}), }),
"models-list": defineRoutedCommand({ "models-list": defineRoutedCommand({
parseArgs: parseModelsListRouteArgs, parseArgs: parseModelsListRouteArgs,
runParsedArgs: async (args) => { runParsedArgs: async (args) => {
const { modelsListCommand } = await import("../../commands/models.js"); const { modelsListCommand } = await loadModelsCommands();
await modelsListCommand(args, defaultRuntime); await modelsListCommand(args, defaultRuntime);
}, },
}), }),
"models-status": defineRoutedCommand({ "models-status": defineRoutedCommand({
parseArgs: parseModelsStatusRouteArgs, parseArgs: parseModelsStatusRouteArgs,
runParsedArgs: async (args) => { runParsedArgs: async (args) => {
const { modelsStatusCommand } = await import("../../commands/models.js"); const { modelsStatusCommand } = await loadModelsCommands();
await modelsStatusCommand(args, defaultRuntime); await modelsStatusCommand(args, defaultRuntime);
}, },
}), }),

View File

@@ -8,6 +8,8 @@ import { type RuntimeEnv, writeRuntimeJson } from "../runtime.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { requireValidConfig, requireValidConfigFileSnapshot } from "./agents.command-shared.js"; import { requireValidConfig, requireValidConfigFileSnapshot } from "./agents.command-shared.js";
type AgentBindingsModule = typeof import("./agents.bindings.js");
type AgentsBindingsListOptions = { type AgentsBindingsListOptions = {
agent?: string; agent?: string;
json?: boolean; json?: boolean;
@@ -26,6 +28,13 @@ type AgentsUnbindOptions = {
json?: boolean; json?: boolean;
}; };
let agentBindingsModulePromise: Promise<AgentBindingsModule> | undefined;
function loadAgentBindingsModule(): Promise<AgentBindingsModule> {
agentBindingsModulePromise ??= import("./agents.bindings.js");
return agentBindingsModulePromise;
}
function describeBinding(binding: AgentRouteBinding): string { function describeBinding(binding: AgentRouteBinding): string {
const match = binding.match; const match = binding.match;
const parts = [match.channel]; const parts = [match.channel];
@@ -123,7 +132,7 @@ async function resolveParsedBindingsOrExit(params: {
return null; return null;
} }
const { parseBindingSpecs } = await import("./agents.bindings.js"); const { parseBindingSpecs } = await loadAgentBindingsModule();
const parsed = parseBindingSpecs({ agentId: params.agentId, specs, config: params.cfg }); const parsed = parseBindingSpecs({ agentId: params.agentId, specs, config: params.cfg });
if (parsed.errors.length > 0) { if (parsed.errors.length > 0) {
params.runtime.error(parsed.errors.join("\n")); params.runtime.error(parsed.errors.join("\n"));
@@ -248,7 +257,7 @@ export async function agentsBindCommand(
return; return;
} }
const { applyAgentBindings } = await import("./agents.bindings.js"); const { applyAgentBindings } = await loadAgentBindingsModule();
const result = applyAgentBindings(cfg, parsed.bindings); const result = applyAgentBindings(cfg, parsed.bindings);
if (result.added.length > 0 || result.updated.length > 0) { if (result.added.length > 0 || result.updated.length > 0) {
await replaceConfigFile({ await replaceConfigFile({
@@ -368,7 +377,7 @@ export async function agentsUnbindCommand(
return; return;
} }
const { removeAgentBindings } = await import("./agents.bindings.js"); const { removeAgentBindings } = await loadAgentBindingsModule();
const result = removeAgentBindings(cfg, parsed.bindings); const result = removeAgentBindings(cfg, parsed.bindings);
if (result.removed.length > 0) { if (result.removed.length > 0) {
await replaceConfigFile({ await replaceConfigFile({