From a849ea5ef51d4d4b3c6541b914f053b0cbb30170 Mon Sep 17 00:00:00 2001 From: Gustavo Madeira Santana Date: Tue, 21 Apr 2026 11:44:14 -0400 Subject: [PATCH] refactor: require explicit read-only plugin options --- src/channels/plugins/read-only.test.ts | 16 +++--- src/channels/plugins/read-only.ts | 68 +------------------------- 2 files changed, 11 insertions(+), 73 deletions(-) diff --git a/src/channels/plugins/read-only.test.ts b/src/channels/plugins/read-only.test.ts index 4d23a71a70d..a8c61df5116 100644 --- a/src/channels/plugins/read-only.test.ts +++ b/src/channels/plugins/read-only.test.ts @@ -366,7 +366,7 @@ describe("listReadOnlyChannelPluginsForConfig", () => { expect(fs.existsSync(fullMarker)).toBe(false); }); - it("treats process env maps with option-like keys as env maps", () => { + it("accepts option-like env keys through the explicit env option", () => { const { pluginDir, fullMarker, setupMarker } = writeExternalSetupChannelPlugin({ pluginId: "external-chat-plugin", channelId: "external-chat", @@ -379,12 +379,14 @@ describe("listReadOnlyChannelPluginsForConfig", () => { }, } as never, { - ...process.env, - cache: "true", - env: "prod", - EXTERNAL_CHAT_TOKEN: "configured", - workspaceDir: "workspace-env-value", - } as NodeJS.ProcessEnv, + env: { + ...process.env, + cache: "true", + env: "prod", + EXTERNAL_CHAT_TOKEN: "configured", + workspaceDir: "workspace-env-value", + }, + }, ); const plugin = plugins.find((entry) => entry.id === "external-chat"); diff --git a/src/channels/plugins/read-only.ts b/src/channels/plugins/read-only.ts index 998e478f8d1..7bbe4ebc0d5 100644 --- a/src/channels/plugins/read-only.ts +++ b/src/channels/plugins/read-only.ts @@ -27,61 +27,6 @@ type ReadOnlyChannelPluginResolution = { missingConfiguredChannelIds: string[]; }; -const READ_ONLY_CHANNEL_PLUGIN_OPTION_KEYS = new Set([ - "env", - "workspaceDir", - "activationSourceConfig", - "includePersistedAuthState", - "cache", -]); - -function hasOwnRecordKey(record: Record, key: string): boolean { - return Object.prototype.hasOwnProperty.call(record, key); -} - -function isRecordLike(value: unknown): value is Record { - return typeof value === "object" && value !== null; -} - -function isReadOnlyChannelPluginOptions( - value: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions, -): value is ReadOnlyChannelPluginOptions { - const record = value as Record; - if (hasOwnRecordKey(record, "env")) { - return record.env === undefined || isRecordLike(record.env); - } - if (hasOwnRecordKey(record, "activationSourceConfig")) { - return ( - record.activationSourceConfig === undefined || isRecordLike(record.activationSourceConfig) - ); - } - if (hasOwnRecordKey(record, "includePersistedAuthState")) { - return ( - record.includePersistedAuthState === undefined || - typeof record.includePersistedAuthState === "boolean" - ); - } - if (hasOwnRecordKey(record, "cache")) { - return record.cache === undefined || typeof record.cache === "boolean"; - } - if (hasOwnRecordKey(record, "workspaceDir")) { - return Object.keys(record).every((key) => READ_ONLY_CHANNEL_PLUGIN_OPTION_KEYS.has(key)); - } - return false; -} - -function resolveReadOnlyChannelPluginOptions( - envOrOptions?: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions, -): ReadOnlyChannelPluginOptions { - if (!envOrOptions) { - return {}; - } - if (isReadOnlyChannelPluginOptions(envOrOptions)) { - return envOrOptions; - } - return { env: envOrOptions }; -} - function addChannelPlugins( byId: Map, plugins: Iterable, @@ -395,26 +340,17 @@ function resolveExternalReadOnlyChannelPluginIds(params: { .toSorted((left, right) => left.localeCompare(right)); } -export function listReadOnlyChannelPluginsForConfig( - cfg: OpenClawConfig, - env?: NodeJS.ProcessEnv, -): ChannelPlugin[]; export function listReadOnlyChannelPluginsForConfig( cfg: OpenClawConfig, options?: ReadOnlyChannelPluginOptions, -): ChannelPlugin[]; -export function listReadOnlyChannelPluginsForConfig( - cfg: OpenClawConfig, - envOrOptions?: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions, ): ChannelPlugin[] { - return resolveReadOnlyChannelPluginsForConfig(cfg, envOrOptions).plugins; + return resolveReadOnlyChannelPluginsForConfig(cfg, options).plugins; } export function resolveReadOnlyChannelPluginsForConfig( cfg: OpenClawConfig, - envOrOptions?: NodeJS.ProcessEnv | ReadOnlyChannelPluginOptions, + options: ReadOnlyChannelPluginOptions = {}, ): ReadOnlyChannelPluginResolution { - const options = resolveReadOnlyChannelPluginOptions(envOrOptions); const env = options.env ?? process.env; const workspaceDir = resolveReadOnlyWorkspaceDir(cfg, options); const externalManifestRecords = listExternalChannelManifestRecords({