mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 08:02:32 +00:00
* fix: wait for OpenCode worker composer before first dispatch Reuse captured composer readiness on local and paired execution hosts and revoke launching-shell paste anchors. Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> * feat(opencode): probe execution-host CLI capabilities * fix(opencode): select plugin default for execution host loader * fix(opencode): limit prompt prefill capability to verified release * feat(opencode): probe launch capabilities on the execution host * fix(opencode): select plugin loader for the launched host binary * fix(opencode): match WSL probe cwd and declared guest environment * fix(opencode): preserve launch environment deletion boundaries * STRICT launch CI contract correction * CAPS launch CI contract correction * test: initialize Claude prompt state in output retention fixture --------- Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
162 lines
5.3 KiB
TypeScript
162 lines
5.3 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
getOpenCodeLaunchExecutable,
|
|
probeOpenCodeLaunchCapabilities
|
|
} from './opencode-launch-capabilities'
|
|
import { getOpenCodeCliCapabilities } from '../../shared/opencode-cli-version'
|
|
|
|
const mocks = vi.hoisted(() => ({ resolve: vi.fn(), probe: vi.fn(), wsl: vi.fn() }))
|
|
vi.mock('../ipc/command-path-resolver', () => ({ resolveCommandOnLocalPath: mocks.resolve }))
|
|
vi.mock('./opencode-cli-version', () => ({ probeOpenCodeCliVersion: mocks.probe }))
|
|
vi.mock('../wsl/wsl-runner', () => ({ runWslProcess: mocks.wsl }))
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
mocks.probe.mockResolvedValue(getOpenCodeCliCapabilities('2.0.16'))
|
|
mocks.wsl.mockResolvedValue({
|
|
code: 0,
|
|
timedOut: false,
|
|
stdout: '1.18.30',
|
|
environmentResolved: true
|
|
})
|
|
})
|
|
|
|
describe('OpenCode execution-host launch capability probe', () => {
|
|
it('refuses a successful version response from an unresolved WSL execution environment', async () => {
|
|
mocks.wsl.mockResolvedValue({
|
|
code: 0,
|
|
timedOut: false,
|
|
stdout: '1.18.30',
|
|
environmentResolved: false
|
|
})
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode',
|
|
env: {},
|
|
wsl: { distro: 'Ubuntu' }
|
|
})
|
|
expect(await mocks.probe.mock.calls[0]?.[0].execute()).toMatchObject({ code: null })
|
|
})
|
|
it('recognizes quoted executables and preserves explicit run commands', () => {
|
|
expect(getOpenCodeLaunchExecutable('"/app dir/opencode" run task')).toBe('/app dir/opencode')
|
|
expect(getOpenCodeLaunchExecutable('custom-launcher --standalone', 'opencode')).toBe(
|
|
'custom-launcher'
|
|
)
|
|
expect(getOpenCodeLaunchExecutable('claude')).toBeNull()
|
|
})
|
|
|
|
it('uses native resolution with the execution environment and cwd', async () => {
|
|
mocks.resolve.mockResolvedValue('/bin/opencode')
|
|
const env = { PATH: '/bin', OPENCODE_CONFIG_DIR: '/private/config' }
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode run task',
|
|
env,
|
|
cwd: '/repo',
|
|
hostIdentity: 'native-test'
|
|
})
|
|
expect(mocks.resolve).toHaveBeenCalledWith('opencode', { env, cwd: '/repo' })
|
|
expect(mocks.probe).toHaveBeenCalledWith({
|
|
executablePath: '/bin/opencode',
|
|
env,
|
|
cwd: '/repo',
|
|
hostIdentity: 'native-test'
|
|
})
|
|
expect(mocks.wsl).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('uses the relay resolver rather than client PATH resolution', async () => {
|
|
const resolveExecutable = vi.fn().mockResolvedValue('/host/opencode')
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode',
|
|
env: {},
|
|
hostIdentity: 'relay:linux',
|
|
resolveExecutable
|
|
})
|
|
expect(resolveExecutable).toHaveBeenCalledWith('opencode')
|
|
expect(mocks.resolve).not.toHaveBeenCalled()
|
|
expect(mocks.probe).toHaveBeenCalledWith(
|
|
expect.objectContaining({ executablePath: '/host/opencode', hostIdentity: 'relay:linux' })
|
|
)
|
|
})
|
|
|
|
it('returns unknown when the host cannot resolve the binary', async () => {
|
|
mocks.resolve.mockResolvedValue(null)
|
|
expect(await probeOpenCodeLaunchCapabilities({ command: 'opencode', env: {} })).toEqual(
|
|
getOpenCodeCliCapabilities(null)
|
|
)
|
|
expect(mocks.probe).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('bounds WSL probes and matches guest cwd plus explicitly imported config roots', async () => {
|
|
const env = {
|
|
HOME: '/native',
|
|
PATH: '/native/bin',
|
|
OPENCODE_CONFIG_DIR: '/guest/config',
|
|
XDG_DATA_HOME: '/guest/data',
|
|
WSLENV: 'OPENCODE_CONFIG_DIR:XDG_DATA_HOME'
|
|
}
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode --standalone',
|
|
env,
|
|
cwd: '\\\\wsl.localhost\\Ubuntu\\home\\user\\repo',
|
|
wsl: { distro: 'Debian' },
|
|
hostIdentity: 'host-a'
|
|
})
|
|
expect(mocks.resolve).not.toHaveBeenCalled()
|
|
const options = mocks.probe.mock.calls[0]?.[0]
|
|
const guestEnv = {
|
|
OPENCODE_CONFIG_DIR: '/guest/config',
|
|
XDG_DATA_HOME: '/guest/data',
|
|
WSLENV: env.WSLENV
|
|
}
|
|
expect(options).toEqual(
|
|
expect.objectContaining({
|
|
executablePath: 'opencode',
|
|
hostIdentity: 'host-a:wsl:Ubuntu',
|
|
cwd: '/home/user/repo',
|
|
env: guestEnv
|
|
})
|
|
)
|
|
await options.execute()
|
|
expect(mocks.wsl).toHaveBeenCalledWith({
|
|
distro: 'Ubuntu',
|
|
loginPath: 'preferred',
|
|
cwd: '/home/user/repo',
|
|
program: 'opencode',
|
|
args: ['--version'],
|
|
env: guestEnv,
|
|
timeoutMs: 5000,
|
|
maxOutputBytes: 4096
|
|
})
|
|
})
|
|
|
|
it('does not import native configuration the actual WSL pane would not receive', async () => {
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode',
|
|
env: { OPENCODE_CONFIG_DIR: 'C:\\native' },
|
|
cwd: 'D:\\repo',
|
|
wsl: { distro: 'Ubuntu' }
|
|
})
|
|
expect(mocks.probe).toHaveBeenCalledWith(
|
|
expect.objectContaining({ cwd: '/mnt/d/repo', env: { WSLENV: '' } })
|
|
)
|
|
})
|
|
|
|
it('keeps WSL path translation flags and skips Windows-only values', async () => {
|
|
await probeOpenCodeLaunchCapabilities({
|
|
command: 'opencode',
|
|
env: {
|
|
WSLENV: 'XDG_DATA_HOME/p:WINDOWS_ONLY/w:HOME',
|
|
XDG_DATA_HOME: 'D:\\data',
|
|
WINDOWS_ONLY: 'private',
|
|
HOME: 'C:\\native'
|
|
},
|
|
wsl: {}
|
|
})
|
|
expect(mocks.probe).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
env: { WSLENV: 'XDG_DATA_HOME/p:WINDOWS_ONLY/w:HOME', XDG_DATA_HOME: 'D:\\data' }
|
|
})
|
|
)
|
|
})
|
|
})
|