Files
orca/src/main/opencode/legacy-shared-config-dir.ts
T
OrcaWinandm4air 813aff8f8a fix(opencode): stop OpenCode 2 loading a stale plugin from the retired shared hooks dir (#23500)
* fix(opencode): stop OpenCode 2 loading a stale plugin from the retired shared hooks dir

Before 1.4.209 Orca pointed OPENCODE_CONFIG_DIR at <userData>/opencode-hooks/shared
and wrote a server()-only status plugin there. 1.4.209 moved the plugin to OpenCode's
global config dir and 1.4.210 added the v2 setup() export, but nothing rewrote the
old file. Shells, daemon-persisted panes and OpenCode 2 background services that
still carry that OPENCODE_CONFIG_DIR load only that dir under OpenCode 2 (it replaces
the global dir), so the v2 loader rejects the stale plugin with "Plugin must export a
default definition with an id and an effect or setup function" and pane status dies.

- Refresh the plugin in the retired shared dir (only when it already exists and its
  content differs) so OpenCode processes started later from old shells load the dual
  v1/v2 export. Runs on OpenCode pane spawns and on any spawn that inherits the
  retired dir, even with agent status hooks off.
- Drop an inherited OPENCODE_CONFIG_DIR / ORCA_OPENCODE_* marker that points at the
  retired dir when building a new pane env, so new panes use global discovery.

Limitation: an OpenCode 2 background service already running from an old pane keeps
its cached copy of the stale module even after the file is rewritten (verified with
opencode2 v2.0.18). It must be restarted (`opencode service restart`); a restart from
a new Orca pane then picks up the global config because the env is stripped.

* fix(opencode): harden legacy plugin repair and inherited config cleanup

* fix(opencode): preserve daemon-owned user config during legacy cleanup

* fix(opencode): sanitize inherited sources and repair unseen legacy copies

* test(opencode): update shared PTY mocks for legacy repair

* test(opencode): annotate shared repair mock signature

---------

Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
2026-09-28 14:55:42 -07:00

49 lines
1.6 KiB
TypeScript

import { join, resolve } from 'node:path'
export const OPENCODE_LEGACY_HOOKS_DIR = 'opencode-hooks'
export const OPENCODE2_LEGACY_HOOKS_DIR = 'opencode2-hooks'
// Why: before 1.4.209 Orca pointed OPENCODE_CONFIG_DIR at this dir; shells and OpenCode 2 background services from then can still load it.
export function getOpenCodeLegacySharedConfigDir(
userDataPath: string,
legacyHooksDir: string
): string {
return join(userDataPath, legacyHooksDir, 'shared')
}
export function isOpenCodeLegacySharedConfigDir(
configDir: string | undefined,
userDataPath: string
): boolean {
return (
configDir !== undefined &&
[OPENCODE_LEGACY_HOOKS_DIR, OPENCODE2_LEGACY_HOOKS_DIR].some(
(hooksDir) =>
normalizeConfigPath(configDir) ===
normalizeConfigPath(getOpenCodeLegacySharedConfigDir(userDataPath, hooksDir))
)
)
}
function normalizeConfigPath(path: string): string {
const resolved = resolve(path)
return process.platform === 'win32' ? resolved.toLowerCase() : resolved
}
export const OPENCODE_CONFIG_DIR_ENV_KEYS = [
'OPENCODE_CONFIG_DIR',
'ORCA_OPENCODE_CONFIG_DIR',
'ORCA_OPENCODE_SOURCE_CONFIG_DIR'
] as const
/** Carries retired inherited paths through providers that merge their environment later. */
export function getLegacyOpenCodeEnvKeysToDelete(
env: Record<string, string | undefined> | undefined,
userDataPath: string,
inherited: Record<string, string | undefined> = process.env
): string[] {
return OPENCODE_CONFIG_DIR_ENV_KEYS.filter((key) =>
isOpenCodeLegacySharedConfigDir(env?.[key] ?? inherited[key], userDataPath)
)
}