Browse Source

fix config merge for webfetch settings

Alvin Unreal 1 week ago
parent
commit
e8b90becb8
2 changed files with 87 additions and 1 deletions
  1. 54 0
      src/config/loader.test.ts
  2. 33 1
      src/config/loader.ts

+ 54 - 0
src/config/loader.test.ts

@@ -66,6 +66,60 @@ describe('loadPluginConfig', () => {
     expect(config.autoUpdate).toBe(false);
   });
 
+  test('deep-merges webfetch settings across user and project configs', () => {
+    const userConfigPath = path.join(userConfigDir, 'opencode');
+    const projectDir = path.join(tempDir, 'project');
+    const projectConfigDir = path.join(projectDir, '.opencode');
+    fs.mkdirSync(userConfigPath, { recursive: true });
+    fs.mkdirSync(projectConfigDir, { recursive: true });
+    fs.writeFileSync(
+      path.join(userConfigPath, 'oh-my-opencode-slim.json'),
+      JSON.stringify({
+        webfetch: { model: 'user/provider-model' },
+      }),
+    );
+    fs.writeFileSync(
+      path.join(projectConfigDir, 'oh-my-opencode-slim.json'),
+      JSON.stringify({
+        webfetch: { enabled: true },
+      }),
+    );
+
+    const config = loadPluginConfig(projectDir, { silent: true });
+
+    expect(config.webfetch).toEqual({
+      enabled: true,
+      model: 'user/provider-model',
+    });
+  });
+
+  test('does not let a defaulted project webfetch enabled override user false', () => {
+    const userConfigPath = path.join(userConfigDir, 'opencode');
+    const projectDir = path.join(tempDir, 'project');
+    const projectConfigDir = path.join(projectDir, '.opencode');
+    fs.mkdirSync(userConfigPath, { recursive: true });
+    fs.mkdirSync(projectConfigDir, { recursive: true });
+    fs.writeFileSync(
+      path.join(userConfigPath, 'oh-my-opencode-slim.json'),
+      JSON.stringify({
+        webfetch: { enabled: false },
+      }),
+    );
+    fs.writeFileSync(
+      path.join(projectConfigDir, 'oh-my-opencode-slim.json'),
+      JSON.stringify({
+        webfetch: { model: 'project/provider-model' },
+      }),
+    );
+
+    const config = loadPluginConfig(projectDir, { silent: true });
+
+    expect(config.webfetch).toEqual({
+      enabled: false,
+      model: 'project/provider-model',
+    });
+  });
+
   test('validates auto image routing after project enables Observer', () => {
     const userConfigPath = path.join(userConfigDir, 'opencode');
     const projectDir = path.join(tempDir, 'project');

+ 33 - 1
src/config/loader.ts

@@ -3,7 +3,11 @@ import * as path from 'node:path';
 import { stripJsonComments } from '../cli/config-io';
 import { getConfigSearchDirs } from '../cli/paths';
 import { DEFAULT_DISABLED_AGENTS } from './constants';
-import { type PluginConfig, PluginConfigSchema } from './schema';
+import {
+  type PluginConfig,
+  PluginConfigSchema,
+  WebfetchConfigSchema,
+} from './schema';
 
 /**
  * Warning kinds produced during config loading.
@@ -141,6 +145,26 @@ function loadConfigFromPath(
       return null;
     }
 
+    // Zod applies webfetch.enabled's default while parsing each layer. Keep
+    // that default from masquerading as an explicitly configured override;
+    // the merged webfetch config is normalized after all layers are merged.
+    if (
+      result.data.webfetch &&
+      typeof rawConfig === 'object' &&
+      rawConfig !== null &&
+      'webfetch' in rawConfig &&
+      typeof rawConfig.webfetch === 'object' &&
+      rawConfig.webfetch !== null &&
+      !Array.isArray(rawConfig.webfetch) &&
+      !Object.hasOwn(rawConfig.webfetch, 'enabled')
+    ) {
+      const { enabled: _enabled, ...webfetch } = result.data.webfetch;
+      return {
+        ...result.data,
+        webfetch: webfetch as PluginConfig['webfetch'],
+      };
+    }
+
     return result.data;
   } catch (error) {
     // File doesn't exist or isn't readable - this is expected and fine
@@ -283,6 +307,10 @@ export function mergePluginConfigs(
     backgroundJobs: deepMerge(base.backgroundJobs, override.backgroundJobs),
     fallback: deepMerge(base.fallback, override.fallback),
     council: deepMerge(base.council, override.council),
+    webfetch: deepMerge(
+      base.webfetch as Record<string, unknown> | undefined,
+      override.webfetch as Record<string, unknown> | undefined,
+    ) as PluginConfig['webfetch'],
     acpAgents: deepMerge(base.acpAgents, override.acpAgents),
     companion: deepMerge(
       base.companion as Record<string, unknown> | undefined,
@@ -364,6 +392,10 @@ export function loadPluginConfig(
     config = mergePluginConfigs(config, projectConfig);
   }
 
+  if (config.webfetch) {
+    config.webfetch = WebfetchConfigSchema.parse(config.webfetch);
+  }
+
   // Override preset from environment variable if set
   const envPreset = process.env.OH_MY_OPENCODE_SLIM_PRESET;
   if (envPreset) {