Parcourir la source

fix: preserve mixed plugin entries in config updates

Avoid silently deleting object-style plugin entries when refreshing the oh-my-opencode-slim plugin entry. Keep mixed plugin arrays type-safe so non-string values are filtered before string-only checks.
Ruben Beuker il y a 3 mois
Parent
commit
794dc977c7

+ 25 - 0
src/cli/config-io.test.ts

@@ -203,6 +203,31 @@ describe('config-io', () => {
     expect(saved.plugin).toEqual(['other', packageRoot]);
   });
 
+  test('addPluginToOpenCodeConfig preserves non-string plugin entries when refreshing', async () => {
+    const configPath = join(tmpDir, 'opencode', 'opencode.json');
+    paths.ensureConfigDir();
+    process.argv[1] = '';
+
+    const objectPlugin = { name: 'some-config-plugin', enabled: true };
+    writeFileSync(
+      configPath,
+      JSON.stringify({
+        plugin: ['other-plugin', objectPlugin, 'oh-my-opencode-slim@1.0.0'],
+      }),
+    );
+
+    const result = await addPluginToOpenCodeConfig();
+    expect(result.success).toBe(true);
+
+    const saved = JSON.parse(readFileSync(configPath, 'utf-8'));
+    expect(saved.plugin).toContain('oh-my-opencode-slim');
+    expect(saved.plugin).toContain('other-plugin');
+    expect(saved.plugin).not.toContain('oh-my-opencode-slim@1.0.0');
+    // Non-string entries (objects) must survive the plugin refresh
+    expect(saved.plugin).toContainEqual(objectPlugin);
+    expect(saved.plugin.length).toBe(3);
+  });
+
   test('writeLiteConfig writes lite config with OpenAI preset', () => {
     const litePath = join(tmpDir, 'opencode', 'oh-my-opencode-slim.json');
     paths.ensureConfigDir();

+ 9 - 3
src/cli/config-io.ts

@@ -27,8 +27,12 @@ function isString(value: unknown): value is string {
   return typeof value === 'string';
 }
 
+function getPlugins(config: OpenCodeConfig): unknown[] {
+  return Array.isArray(config.plugin) ? config.plugin : [];
+}
+
 function getPluginEntries(config: OpenCodeConfig): string[] {
-  return Array.isArray(config.plugin) ? config.plugin.filter(isString) : [];
+  return getPlugins(config).filter(isString);
 }
 
 function normalizePathForMatch(path: string): string {
@@ -212,12 +216,14 @@ export async function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult> {
       };
     }
     const config = parsedConfig ?? {};
-    const plugins = getPluginEntries(config);
+    const plugins = getPlugins(config);
 
     const pluginEntry = getPluginEntry();
 
     // Remove existing oh-my-opencode-slim entries
-    const filteredPlugins = plugins.filter((p) => !isPluginEntry(p));
+    const filteredPlugins = plugins.filter(
+      (plugin) => !isString(plugin) || !isPluginEntry(plugin),
+    );
 
     // Add fresh entry
     filteredPlugins.push(pluginEntry);

+ 1 - 1
src/cli/types.ts

@@ -8,7 +8,7 @@ export interface InstallArgs {
 }
 
 export interface OpenCodeConfig {
-  plugin?: string[];
+  plugin?: unknown[];
   provider?: Record<string, unknown>;
   agent?: Record<string, unknown>;
   [key: string]: unknown;

+ 10 - 2
src/hooks/auto-update-checker/checker.ts

@@ -18,6 +18,14 @@ import type {
   PluginEntryInfo,
 } from './types';
 
+function isString(value: unknown): value is string {
+  return typeof value === 'string';
+}
+
+function getPluginEntries(config: OpencodeConfig): string[] {
+  return Array.isArray(config.plugin) ? config.plugin.filter(isString) : [];
+}
+
 /**
  * Checks if a version string indicates a prerelease (contains a hyphen).
  */
@@ -75,7 +83,7 @@ function getLocalDevPath(directory: string): string | null {
       if (!fs.existsSync(configPath)) continue;
       const content = fs.readFileSync(configPath, 'utf-8');
       const config = JSON.parse(stripJsonComments(content)) as OpencodeConfig;
-      const plugins = config.plugin ?? [];
+      const plugins = getPluginEntries(config);
 
       for (const entry of plugins) {
         if (entry.startsWith('file://') && entry.includes(PACKAGE_NAME)) {
@@ -162,7 +170,7 @@ export function findPluginEntry(directory: string): PluginEntryInfo | null {
       if (!fs.existsSync(configPath)) continue;
       const content = fs.readFileSync(configPath, 'utf-8');
       const config = JSON.parse(stripJsonComments(content)) as OpencodeConfig;
-      const plugins = config.plugin ?? [];
+      const plugins = getPluginEntries(config);
 
       for (const entry of plugins) {
         if (entry === PACKAGE_NAME) {

+ 1 - 1
src/hooks/auto-update-checker/types.ts

@@ -4,7 +4,7 @@ export interface NpmDistTags {
 }
 
 export interface OpencodeConfig {
-  plugin?: string[];
+  plugin?: unknown[];
   [key: string]: unknown;
 }