From 2d6519dcb908b461f0e7f6b4f01a0f235a8ebe90 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 11 Apr 2026 05:42:01 +0100 Subject: [PATCH] perf: defer bundled channel presence lookups --- src/channels/config-presence.test.ts | 37 +++++++++++-------- src/channels/config-presence.ts | 11 ++++-- src/channels/plugins/bundled-ids.ts | 9 +++-- src/config/channel-configured.ts | 6 +-- .../plugin-auto-enable.channels.test.ts | 1 + 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/channels/config-presence.test.ts b/src/channels/config-presence.test.ts index 06018f4639b..769aaa7d1a2 100644 --- a/src/channels/config-presence.test.ts +++ b/src/channels/config-presence.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../config/config.js"; import { hasMeaningfulChannelConfig, @@ -9,6 +9,21 @@ import { listPotentialConfiguredChannelIds, } from "./config-presence.js"; +vi.mock("./plugins/bundled-ids.js", () => ({ + listBundledChannelPluginIds: () => ["matrix"], +})); + +vi.mock("../channels/plugins/persisted-auth-state.js", () => ({ + listBundledChannelIdsWithPersistedAuthState: () => ["matrix"], + hasBundledChannelPersistedAuthState: ({ + channelId, + env, + }: { + channelId: string; + env?: NodeJS.ProcessEnv; + }) => channelId === "matrix" && env?.OPENCLAW_STATE_DIR?.includes("persisted-matrix"), +})); + const tempDirs: string[] = []; function makeTempStateDir() { @@ -45,8 +60,7 @@ describe("config presence", () => { }); it("ignores enabled-only matrix config when listing configured channels", () => { - const stateDir = makeTempStateDir(); - const env = { OPENCLAW_STATE_DIR: stateDir } as NodeJS.ProcessEnv; + const env = {} as NodeJS.ProcessEnv; const cfg = { channels: { matrix: { enabled: false } } }; expectPotentialConfiguredChannelCase({ @@ -58,9 +72,7 @@ describe("config presence", () => { }); it("detects env-only channel config", () => { - const stateDir = makeTempStateDir(); const env = { - OPENCLAW_STATE_DIR: stateDir, MATRIX_ACCESS_TOKEN: "token", } as NodeJS.ProcessEnv; @@ -73,17 +85,12 @@ describe("config presence", () => { }); it("detects persisted Matrix credentials without config or env", () => { - const stateDir = makeTempStateDir(); - fs.mkdirSync(path.join(stateDir, "credentials", "matrix"), { recursive: true }); - fs.writeFileSync( - path.join(stateDir, "credentials", "matrix", "credentials.json"), - JSON.stringify({ - homeserver: "https://matrix.example.org", - userId: "@bot:example.org", - accessToken: "token", - }), - "utf8", + const stateDir = makeTempStateDir().replace( + "openclaw-channel-config-presence-", + "persisted-matrix-", ); + fs.mkdirSync(stateDir, { recursive: true }); + tempDirs.push(stateDir); const env = { OPENCLAW_STATE_DIR: stateDir } as NodeJS.ProcessEnv; expectPotentialConfiguredChannelCase({ diff --git a/src/channels/config-presence.ts b/src/channels/config-presence.ts index 087ce410857..84eaa56413f 100644 --- a/src/channels/config-presence.ts +++ b/src/channels/config-presence.ts @@ -36,7 +36,12 @@ function hasPersistedChannelState(env: NodeJS.ProcessEnv): boolean { return fs.existsSync(resolveStateDir(env, os.homedir)); } -const PERSISTED_AUTH_STATE_CHANNEL_IDS = listBundledChannelIdsWithPersistedAuthState(); +let persistedAuthStateChannelIds: string[] | null = null; + +function getPersistedAuthStateChannelIds(): string[] { + persistedAuthStateChannelIds ??= listBundledChannelIdsWithPersistedAuthState(); + return persistedAuthStateChannelIds; +} export function listPotentialConfiguredChannelIds( cfg: OpenClawConfig, @@ -70,7 +75,7 @@ export function listPotentialConfiguredChannelIds( } if (options.includePersistedAuthState !== false && hasPersistedChannelState(env)) { - for (const channelId of PERSISTED_AUTH_STATE_CHANNEL_IDS) { + for (const channelId of getPersistedAuthStateChannelIds()) { if (hasBundledChannelPersistedAuthState({ channelId, cfg, env })) { configuredChannelIds.add(channelId); } @@ -98,7 +103,7 @@ function hasEnvConfiguredChannel( if (options.includePersistedAuthState === false || !hasPersistedChannelState(env)) { return false; } - return PERSISTED_AUTH_STATE_CHANNEL_IDS.some((channelId) => + return getPersistedAuthStateChannelIds().some((channelId) => hasBundledChannelPersistedAuthState({ channelId, cfg, env }), ); } diff --git a/src/channels/plugins/bundled-ids.ts b/src/channels/plugins/bundled-ids.ts index 2b4b32d036f..7edbf2ded49 100644 --- a/src/channels/plugins/bundled-ids.ts +++ b/src/channels/plugins/bundled-ids.ts @@ -1,9 +1,10 @@ import { listChannelCatalogEntries } from "../../plugins/channel-catalog-registry.js"; -export const BUNDLED_CHANNEL_PLUGIN_IDS = listChannelCatalogEntries({ origin: "bundled" }) - .map((entry) => entry.pluginId) - .toSorted((left, right) => left.localeCompare(right)); +let bundledChannelPluginIds: string[] | null = null; export function listBundledChannelPluginIds(): string[] { - return [...BUNDLED_CHANNEL_PLUGIN_IDS]; + bundledChannelPluginIds ??= listChannelCatalogEntries({ origin: "bundled" }) + .map((entry) => entry.pluginId) + .toSorted((left, right) => left.localeCompare(right)); + return [...bundledChannelPluginIds]; } diff --git a/src/config/channel-configured.ts b/src/config/channel-configured.ts index 900f35e7c7c..1f0ea0e4e6f 100644 --- a/src/config/channel-configured.ts +++ b/src/config/channel-configured.ts @@ -12,6 +12,9 @@ export function isChannelConfigured( channelId: string, env: NodeJS.ProcessEnv = process.env, ): boolean { + if (hasMeaningfulChannelConfigShallow(resolveChannelConfigRecord(cfg, channelId))) { + return true; + } if (hasBundledChannelConfiguredState({ channelId, cfg, env })) { return true; } @@ -19,9 +22,6 @@ export function isChannelConfigured( if (pluginPersistedAuthState) { return true; } - if (hasMeaningfulChannelConfigShallow(resolveChannelConfigRecord(cfg, channelId))) { - return true; - } const plugin = getBootstrapChannelPlugin(channelId); return Boolean(plugin?.config?.hasConfiguredState?.({ cfg, env })); } diff --git a/src/config/plugin-auto-enable.channels.test.ts b/src/config/plugin-auto-enable.channels.test.ts index 1c6d06b0bc2..ffa244d6731 100644 --- a/src/config/plugin-auto-enable.channels.test.ts +++ b/src/config/plugin-auto-enable.channels.test.ts @@ -81,6 +81,7 @@ describe("applyPluginAutoEnable channels", () => { env: { ...makeIsolatedEnv(), OPENCLAW_STATE_DIR: stateDir, + OPENCLAW_BUNDLED_PLUGINS_DIR: "/nonexistent/bundled/plugins", }, manifestRegistry: makeRegistry([]), });