|
1 | | -import * as Cause from "effect/Cause" |
2 | 1 | import * as Log from "@opencode-ai/core/util/log" |
3 | 2 | import { serviceUse } from "@opencode-ai/core/effect/service-use" |
4 | 3 | import path from "path" |
@@ -73,6 +72,23 @@ function normalizeLoadedConfig(data: unknown, source: string) { |
73 | 72 | return copy |
74 | 73 | } |
75 | 74 |
|
| 75 | +const infoKeys = new Set([ |
| 76 | + "$schema", "shell", "logLevel", "server", "command", "skills", |
| 77 | + "reference", "watcher", "snapshot", "plugin", "share", "autoshare", |
| 78 | + "autoupdate", "disabled_providers", "enabled_providers", "model", |
| 79 | + "small_model", "default_agent", "username", "mode", "agent", |
| 80 | + "instructions", "account_token", "url", "headers", |
| 81 | +]) |
| 82 | + |
| 83 | +function stripUnknownKeys(data: unknown): unknown { |
| 84 | + if (typeof data !== "object" || data === null || Array.isArray(data)) return data |
| 85 | + const result: Record<string, unknown> = {} |
| 86 | + for (const [key, value] of Object.entries(data)) { |
| 87 | + if (infoKeys.has(key)) result[key] = value |
| 88 | + } |
| 89 | + return result |
| 90 | +} |
| 91 | + |
76 | 92 | async function substituteWellKnownRemoteConfig(input: { |
77 | 93 | value: unknown |
78 | 94 | dir: string |
@@ -118,9 +134,11 @@ const WellKnownConfig = Schema.Struct({ |
118 | 134 | async function resolveLoadedPlugins<T extends { plugin?: ConfigPlugin.Spec[] }>(config: T, filepath: string) { |
119 | 135 | if (!config.plugin) return config |
120 | 136 | for (let i = 0; i < config.plugin.length; i++) { |
121 | | - // Normalize path-like plugin specs while we still know which config file declared them. |
122 | | - // This prevents `./plugin.ts` from being reinterpreted relative to some later merge location. |
123 | | - config.plugin[i] = await ConfigPlugin.resolvePluginSpec(config.plugin[i], filepath) |
| 137 | + try { |
| 138 | + config.plugin[i] = await ConfigPlugin.resolvePluginSpec(config.plugin[i], filepath) |
| 139 | + } catch { |
| 140 | + log.error("plugin resolution failed", { spec: config.plugin[i], path: filepath }) |
| 141 | + } |
124 | 142 | } |
125 | 143 | return config |
126 | 144 | } |
@@ -424,23 +442,24 @@ export const layer = Layer.effect( |
424 | 442 | let parsedOk = false |
425 | 443 | const data = yield* Effect.sync(() => { |
426 | 444 | const parsed = ConfigParse.jsonc(expanded, source) |
427 | | - const result = ConfigParse.schema(Info, normalizeLoadedConfig(parsed, source), source) |
| 445 | + const cleaned = stripUnknownKeys(normalizeLoadedConfig(parsed, source)) |
| 446 | + const result = ConfigParse.schema(Info, cleaned, source) |
428 | 447 | parsedOk = true |
429 | 448 | return result |
430 | 449 | }).pipe( |
431 | 450 | Effect.catchCause((cause) => |
432 | 451 | Effect.sync(() => { |
433 | | - log.error("invalid config: config file could not be parsed", { path: source, cause: Cause.pretty(cause) }) |
| 452 | + log.error("invalid config: config file could not be parsed", { path: source }) |
434 | 453 | return {} as Info |
435 | 454 | }), |
436 | 455 | ), |
437 | 456 | ) |
438 | 457 | if (!("path" in options)) return data |
439 | 458 |
|
440 | 459 | yield* Effect.promise(() => resolveLoadedPlugins(data, options.path)).pipe( |
441 | | - Effect.catchCause((cause) => |
| 460 | + Effect.catchCause(() => |
442 | 461 | Effect.sync(() => { |
443 | | - log.error("plugin resolution failed", { path: source, cause: Cause.pretty(cause) }) |
| 462 | + log.error("plugin resolution failed", { path: source }) |
444 | 463 | }), |
445 | 464 | ), |
446 | 465 | ) |
|
0 commit comments