mirror of
https://github.com/stablyai/orca.git
synced 2026-10-08 00:02:38 +00:00
* fix(opencode): keep OpenCode 2 panes Working across plugin reloads OpenCode 2 disposes and re-sets-up every plugin whenever its plugins dir changes, while sessions keep running. The status plugin published a final Idle on dispose, so a pane read Done mid-turn. Orca also rewrote the plugin file on every PTY spawn, so opening any terminal triggered that reload. Dispose now releases the factory's bookkeeping without publishing a verdict; the next lifecycle event settles the pane, and Orca's ended-process reconciliation still retires panes whose agent exited. The plugin file is written only when its bytes differ. * fix(opencode): skip rewriting an unchanged plugin in the SSH relay install too The relay's canonical-config install still unlinked and rewrote the status plugin on every OpenCode launch over SSH, which restarts every plugin in a remote OpenCode 2 server. Share one install-currency check (lstat + the existing byte comparison) between the local and relay writers, and pin write-if-changed with mtime so the tests also fail on filesystems that reuse a freed inode. * fix(opencode): keep the final Idle when OpenCode 1 tears its instance down OpenCode 1 disposes a plugin only when it tears the instance down, and that teardown cancels every running session, so the Idle published on dispose is true there; the cancelled run's own idle may never reach the plugin. Only OpenCode 2 disposes on a hot reload while turns keep running. The generated module serves both hosts, so the OpenCode 2 setup() entry point now tells the shared factory that sessions outlive disposal; the server() path keeps the previous disposal behaviour, including the hand-off to a surviving factory. * fix(opencode): compare a symlinked plugin by its target before rewriting OpenCode 2 loads plugins through file-level symlinks and reads the revision from the target's mtime, so a user whose Orca plugin file is a symlink (per-file dotfile managers) failed the regular-file check and got a write through the link, and a reload, on every spawn. The config-dir and relay installs now skip the write when the resolved target already has Orca's bytes; when stale they behave as before. Only the per-source overlay keeps the regular-file check, since a link there mirrors a user entry. Installers also skip the write inside a guarded block rather than returning early, so later install steps still run. * test(opencode): skip the plugin symlink tests on Windows like their neighbours Creating a file symlink on Windows needs Developer Mode or admin rights. * test(opencode): stub fetch without a type assertion in the dispose host test
48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
|
import {
|
|
isInstalledOpenCodePluginCurrent,
|
|
isOverlayOpenCodePluginCurrent
|
|
} from './opencode-installed-plugin'
|
|
|
|
describe('installed OpenCode plugin currency', () => {
|
|
let dir: string
|
|
let pluginPath: string
|
|
|
|
beforeEach(() => {
|
|
dir = mkdtempSync(join(tmpdir(), 'orca-opencode-installed-plugin-'))
|
|
pluginPath = join(dir, 'orca-opencode-status.js')
|
|
})
|
|
|
|
afterEach(() => {
|
|
rmSync(dir, { recursive: true, force: true })
|
|
})
|
|
|
|
it('treats a missing or different file as stale', () => {
|
|
expect(isInstalledOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(false)
|
|
expect(isOverlayOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(false)
|
|
writeFileSync(pluginPath, 'older plugin')
|
|
expect(isInstalledOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(false)
|
|
expect(isOverlayOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(false)
|
|
})
|
|
|
|
it('treats a regular file with the same bytes as current', () => {
|
|
writeFileSync(pluginPath, 'plugin')
|
|
expect(isInstalledOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(true)
|
|
expect(isOverlayOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(true)
|
|
})
|
|
|
|
it.skipIf(process.platform === 'win32')(
|
|
'follows a symlink except in an overlay, where a link is a mirrored user entry',
|
|
() => {
|
|
const targetPath = join(dir, 'target.js')
|
|
writeFileSync(targetPath, 'plugin')
|
|
symlinkSync(targetPath, pluginPath)
|
|
expect(isInstalledOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(true)
|
|
expect(isOverlayOpenCodePluginCurrent(pluginPath, 'plugin')).toBe(false)
|
|
}
|
|
)
|
|
})
|