Files
orca/src/main/opencode/legacy-shared-config-dir.test.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

39 lines
1.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { join } from 'node:path'
import { isOpenCodeLegacySharedConfigDir } from './legacy-shared-config-dir'
describe('retired config directory recognition', () => {
const root = join(process.cwd(), 'user-data')
it.each(['opencode-hooks', 'opencode2-hooks'])('normalizes %s paths', (hooks) => {
const path = join(root, hooks, 'shared')
expect(isOpenCodeLegacySharedConfigDir(`${path}/`, root)).toBe(true)
expect(isOpenCodeLegacySharedConfigDir(`${path}/../shared`, root)).toBe(true)
expect(isOpenCodeLegacySharedConfigDir(`${path}/../mine`, root)).toBe(false)
expect(isOpenCodeLegacySharedConfigDir(`${path}-custom`, root)).toBe(false)
expect(isOpenCodeLegacySharedConfigDir(undefined, root)).toBe(false)
})
it.skipIf(process.platform !== 'win32')('handles Windows casing and separators', () => {
expect(isOpenCodeLegacySharedConfigDir('C:/ORCA/opencode-hooks/shared/', 'c:\\orca')).toBe(true)
})
})
describe('isOpenCodeLegacySharedConfigDir', () => {
const userData = join('fixture', 'user-data')
it('matches only the retired shared dirs of both OpenCode variants', () => {
expect(
isOpenCodeLegacySharedConfigDir(join(userData, 'opencode-hooks', 'shared'), userData)
).toBe(true)
expect(
isOpenCodeLegacySharedConfigDir(join(userData, 'opencode2-hooks', 'shared'), userData)
).toBe(true)
expect(
isOpenCodeLegacySharedConfigDir(join(userData, 'opencode-hooks', 'mine'), userData)
).toBe(false)
expect(
isOpenCodeLegacySharedConfigDir(join('other', 'opencode-hooks', 'shared'), userData)
).toBe(false)
expect(isOpenCodeLegacySharedConfigDir(undefined, userData)).toBe(false)
})
})