From 807c7cb89648622b06e6fbae510f7b376f208ee6 Mon Sep 17 00:00:00 2001 From: Chris Scott <99081550+chriswritescode-dev@users.noreply.github.com> Date: Fri, 6 Nov 2026 09:26:56 -0500 Subject: [PATCH 1/2] chore(opencode): move the OpenCode pin to 2.2.5 from a workspace catalog Manager builds against and requires OpenCode 2.2.5. Every @opencode package, plus effect and the OpenTUI/solid dependencies they share, resolves from one pnpm workspace catalog instead of separate pins. The built-against version and the supported floor are now separate settings, so a future OpenCode release can move the pin before raising the floor. Docker and the dev setup script read the floor from its own argument. ocm-cli moves to 0.6.0 on the same 2.2.5 and OpenTUI 0.8 baseline. A linked server on a different version is still connectable and now shows a neutral version notice instead of an unsupported badge. --- Dockerfile | 4 +- .../services/opencode-server-registry.test.ts | 12 +- backend/test/helpers/opencode-binary.test.ts | 2 +- backend/test/routes/settings.test.ts | 106 +-- .../test/scripts/docker-entrypoint.test.ts | 5 +- .../test/services/opencode-installer.test.ts | 66 +- .../services/opencode-single-server.test.ts | 62 +- backend/test/shared/opencode-contract.test.ts | 61 +- .../settings/OpenCodeServerDialog.tsx | 2 +- .../settings/OpenCodeServersSettings.tsx | 2 +- ocm-cli/README.md | 16 +- ocm-cli/package.json | 18 +- ocm-cli/test/package-compat.test.ts | 46 +- pnpm-lock.yaml | 698 +++++++++--------- pnpm-workspace.yaml | 10 + scripts/docker-entrypoint.sh | 4 +- scripts/setup-dev.sh | 2 +- shared/package.json | 8 +- shared/src/opencode/index.ts | 1 + shared/src/opencode/release.ts | 20 +- 20 files changed, 583 insertions(+), 562 deletions(-) diff --git a/Dockerfile b/Dockerfile index f326643ff..ab217a4cb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,7 +63,8 @@ FROM base AS runner # an arm64 host builds the amd64 platform; 0.12.7 is the newest verified-good # release, so re-verify a bump there before moving it. ARG UV_VERSION=0.12.7 -ARG OPENCODE_VERSION=2.2.1 +ARG OPENCODE_VERSION=2.2.5 +ARG OPENCODE_MIN_VERSION=2.2.5 ARG MICROSANDBOX_VERSION=0.7.2 ARG PLAYWRIGHT_VERSION=1.63.0 # Bump TOOLS_CACHEBUST (e.g. via --build-arg) to force a fresh uv/opencode @@ -120,6 +121,7 @@ ENV DATABASE_PATH=/app/data/opencode.db ENV WORKSPACE_PATH=/workspace ENV XDG_CACHE_HOME=/home/node/.cache ENV OPENCODE_BUNDLED_VERSION=${OPENCODE_VERSION} +ENV OPENCODE_MIN_VERSION=${OPENCODE_MIN_VERSION} ENV MSB_PATH=/usr/local/bin/msb ENV MSB_LIBKRUNFW_PATH=/opt/microsandbox/lib/libkrunfw.so ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright diff --git a/backend/src/services/opencode-server-registry.test.ts b/backend/src/services/opencode-server-registry.test.ts index 219939b1c..81a476ecf 100644 --- a/backend/src/services/opencode-server-registry.test.ts +++ b/backend/src/services/opencode-server-registry.test.ts @@ -449,7 +449,7 @@ describe('OpenCodeServerRegistry', () => { describe('test', () => { it('reports a supported version', async () => { - fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.1' })) + fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.5' })) const result = await registry.test({ baseUrl: 'https://studio.example.com/', @@ -457,7 +457,7 @@ describe('OpenCodeServerRegistry', () => { password: 'super-secret', }) - expect(result).toEqual({ ok: true, version: '2.2.1', versionSupported: true, error: null }) + expect(result).toEqual({ ok: true, version: '2.2.5', versionSupported: true, error: null }) expect(fetchFn.mock.calls[0]?.[0]).toBe('https://studio.example.com/api/info') const headers = new Headers(fetchFn.mock.calls[0]?.[1]?.headers) expect(headers.get('authorization')).toBe(`Basic ${btoa('alice:super-secret')}`) @@ -478,7 +478,7 @@ describe('OpenCodeServerRegistry', () => { username: 'alice', password: 'super-secret', }) - fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.1' })) + fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.5' })) const result = await registry.test({ serverId: server.id }) @@ -493,7 +493,7 @@ describe('OpenCodeServerRegistry', () => { baseUrl: 'https://studio.example.com', password: 'super-secret', }) - fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.1' })) + fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.5' })) const result = await registry.test({ baseUrl: 'https://studio.example.com/opencode', @@ -513,7 +513,7 @@ describe('OpenCodeServerRegistry', () => { baseUrl: 'https://studio.example.com', password: 'super-secret', }) - fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.1' })) + fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.5' })) const result = await registry.test({ baseUrl: 'https://relocated.example.com', @@ -533,7 +533,7 @@ describe('OpenCodeServerRegistry', () => { baseUrl: 'https://studio.example.com', password: 'super-secret', }) - fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.1' })) + fetchFn.mockResolvedValueOnce(jsonResponse({ version: '2.2.5' })) await registry.test({ baseUrl: 'https://studio.example.com', diff --git a/backend/test/helpers/opencode-binary.test.ts b/backend/test/helpers/opencode-binary.test.ts index 8b5c50ed6..2b91b84ca 100644 --- a/backend/test/helpers/opencode-binary.test.ts +++ b/backend/test/helpers/opencode-binary.test.ts @@ -16,7 +16,7 @@ vi.mock('child_process', () => ({ spawn: childProcessMock.spawn, })) -const SUPPORTED_VERSION = '2.2.1' +const SUPPORTED_VERSION = '2.2.5' const OPENCODE_BIN_FIXTURE = '/fixture/opencode-2' const PATH_CANDIDATE = 'opencode' const ABSOLUTE_CANDIDATES = ['/usr/local/bin/opencode', '/opt/opencode/bin/opencode'] diff --git a/backend/test/routes/settings.test.ts b/backend/test/routes/settings.test.ts index 0d927b9db..04b480e76 100644 --- a/backend/test/routes/settings.test.ts +++ b/backend/test/routes/settings.test.ts @@ -382,7 +382,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { mockInstallOpenCodeVersion.mockResolvedValue('/home/node/.opencode/bin/opencode') clearOpenCodeVersionCache() vi.stubGlobal('fetch', vi.fn().mockResolvedValue(new Response(JSON.stringify({ - versions: { '2.2.1': {} }, + versions: { '2.2.5': {} }, time: {}, }), { status: 200 }))) forceProcessAttestation(true) @@ -913,7 +913,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { describe('successful upgrade scenarios', () => { it('should install the latest version and respond with success', async () => { mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const req = new Request('http://localhost/opencode-upgrade', { @@ -926,13 +926,13 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(json.success).toBe(true) expect(json.upgraded).toBe(true) expect(json.oldVersion).toBe('2.1.1') - expect(json.newVersion).toBe('2.2.1') - expect(json.message).toBe('OpenCode upgraded from v2.1.1 to v2.2.1 and restarted') - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(json.newVersion).toBe('2.2.5') + expect(json.message).toBe('OpenCode upgraded from v2.1.1 to v2.2.5 and restarted') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) it('should not install when the latest version is not newer', async () => { - mockGetVersion.mockReturnValueOnce('2.2.1') + mockGetVersion.mockReturnValueOnce('2.2.5') const req = new Request('http://localhost/opencode-upgrade', { method: 'POST' @@ -950,7 +950,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { it('should replace an unsupported newer major with the latest supported release', async () => { mockGetVersion.mockReturnValueOnce('3.0.0') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const res = await settingsApp.fetch(new Request('http://localhost/opencode-upgrade', { method: 'POST' })) @@ -958,12 +958,12 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(res.status).toBe(200) expect(json.upgraded).toBe(true) - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) it('should restart directly after a successful upgrade', async () => { mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const req = new Request('http://localhost/opencode-upgrade', { @@ -977,13 +977,13 @@ describe('Settings Routes - OpenCode Upgrade', () => { it('allows upgrading while sandbox enforcement is active', async () => { mockIsSandboxEnforced.mockReturnValue(true) mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const res = await settingsApp.fetch(new Request('http://localhost/opencode-upgrade', { method: 'POST' })) expect(res.status).toBe(200) - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') expect(mockRestart).toHaveBeenCalled() }) }) @@ -1052,7 +1052,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { describe('version handling', () => { it('should install and restart when getVersion returns null', async () => { mockGetVersion.mockReturnValueOnce(null) - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const req = new Request('http://localhost/opencode-upgrade', { @@ -1065,8 +1065,8 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(json.success).toBe(true) expect(json.upgraded).toBe(true) expect(json.oldVersion).toBe(null) - expect(json.newVersion).toBe('2.2.1') - expect(json.message).toBe('OpenCode v2.2.1 installed and restarted') + expect(json.newVersion).toBe('2.2.5') + expect(json.message).toBe('OpenCode v2.2.5 installed and restarted') expect(mockFetchVersion).toHaveBeenCalled() expect(mockClearStartupError).toHaveBeenCalled() expect(mockRestart).toHaveBeenCalledTimes(1) @@ -1088,7 +1088,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(json.upgraded).toBe(false) expect(json.recovered).toBe(true) expect(json.error).toBe('Upgrade failed but server recovered') - expect(json.details).toContain('did not result in version 2.2.1') + expect(json.details).toContain('did not result in version 2.2.5') expect(mockRestart).toHaveBeenCalledTimes(1) }) @@ -1105,7 +1105,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(json.upgraded).toBe(false) expect(json.recovered).toBe(true) expect(json.error).toBe('Upgrade failed but server recovered') - expect(json.details).toContain('did not result in version 2.2.1') + expect(json.details).toContain('did not result in version 2.2.5') expect(mockRestart).toHaveBeenCalledTimes(1) }) }) @@ -1115,12 +1115,12 @@ describe('Settings Routes - OpenCode Upgrade', () => { describe('successful installation', () => { it('should install specific version successfully', async () => { mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const req = new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), headers: { 'Content-Type': 'application/json' } }) const res = await settingsApp.fetch(req) @@ -1128,8 +1128,8 @@ describe('Settings Routes - OpenCode Upgrade', () => { expect(res.status).toBe(200) expect(json.success).toBe(true) - expect(json.newVersion).toBe('2.2.1') - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(json.newVersion).toBe('2.2.5') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) it('does not report success when the requested version was not installed', async () => { @@ -1139,46 +1139,46 @@ describe('Settings Routes - OpenCode Upgrade', () => { const res = await settingsApp.fetch(new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), headers: { 'Content-Type': 'application/json' } })) const json = await res.json() as Record expect(res.status).toBe(400) expect(json.success).toBe(false) - expect(json.details).toContain('did not result in version 2.2.1') + expect(json.details).toContain('did not result in version 2.2.5') expect(json.newVersion).toBe('2.1.1') }) it('allows installing any version while sandbox enforcement is active', async () => { mockIsSandboxEnforced.mockReturnValue(true) mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const res = await settingsApp.fetch(new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), headers: { 'Content-Type': 'application/json' } })) expect(res.status).toBe(200) - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) it('should install the bare version when the request carries a v prefix', async () => { mockGetVersion.mockReturnValueOnce('2.1.1') - mockFetchVersion.mockResolvedValueOnce('2.2.1') + mockFetchVersion.mockResolvedValueOnce('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const req = new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: 'v2.2.1' }), + body: JSON.stringify({ version: 'v2.2.5' }), headers: { 'Content-Type': 'application/json' } }) await settingsApp.fetch(req) - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) }) @@ -1191,7 +1191,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { const req = new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), headers: { 'Content-Type': 'application/json' } }) const res = await settingsApp.fetch(req) @@ -1208,7 +1208,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { const req = new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), headers: { 'Content-Type': 'application/json' } }) const res = await settingsApp.fetch(req) @@ -1275,11 +1275,11 @@ describe('Settings Routes - OpenCode Upgrade', () => { const json = await res.json() as Record expect(res.status).toBe(400) - expect(json.error).toBe('OpenCode 1.18.32 is not supported; OpenCode Manager requires OpenCode >=2.2.1 <3.0.0') + expect(json.error).toBe('OpenCode 1.18.32 is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0') expect(mockInstallOpenCodeVersion).not.toHaveBeenCalled() }) - it.each(['2.2.0', '3.0.0'])('should reject %s outside the supported range before installing anything', async (version) => { + it.each(['2.2.4', '3.0.0'])('should reject %s outside the supported range before installing anything', async (version) => { const res = await settingsApp.fetch(new Request('http://localhost/opencode-install-version', { method: 'POST', body: JSON.stringify({ version }), @@ -1288,14 +1288,14 @@ describe('Settings Routes - OpenCode Upgrade', () => { const json = await res.json() as Record expect(res.status).toBe(400) - expect(json.error).toBe(`OpenCode ${version} is not supported; OpenCode Manager requires OpenCode >=2.2.1 <3.0.0`) + expect(json.error).toBe(`OpenCode ${version} is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0`) expect(mockInstallOpenCodeVersion).not.toHaveBeenCalled() }) it('should reject a prerelease version before installing anything', async () => { const res = await settingsApp.fetch(new Request('http://localhost/opencode-install-version', { method: 'POST', - body: JSON.stringify({ version: '2.2.1-beta.1' }), + body: JSON.stringify({ version: '2.2.5-beta.1' }), headers: { 'Content-Type': 'application/json' } })) @@ -1332,7 +1332,7 @@ describe('Settings Routes - OpenCode Upgrade', () => { const res = await settingsApp.fetch(new Request('http://localhost/opencode-install-version', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), })) const json = await res.json() as Record @@ -2109,8 +2109,8 @@ describe('Settings Routes - versions, directory files, skills, MCP and maintenan fetchMock = vi.fn() vi.stubGlobal('fetch', fetchMock) clearOpenCodeVersionCache() - mockGetVersion.mockReturnValue('2.2.1') - mockFetchVersion.mockResolvedValue('2.2.1') + mockGetVersion.mockReturnValue('2.2.5') + mockFetchVersion.mockResolvedValue('2.2.5') mockInstallOpenCodeVersion.mockReset() mockInstallOpenCodeVersion.mockResolvedValue('/home/node/.opencode/bin/opencode') mockClearStartupError.mockReturnValue(undefined) @@ -2133,14 +2133,14 @@ describe('Settings Routes - versions, directory files, skills, MCP and maintenan versions: { '1.18.32': {}, '2.1.1': {}, - '2.2.1': {}, - '2.2.2': {}, - '2.2.1-beta.1': {}, + '2.2.5': {}, + '2.2.6': {}, + '2.2.5-beta.1': {}, '3.0.0': {}, }, time: { - '2.2.1': '2026-01-02T00:00:00Z', - '2.2.2': '2026-01-03T00:00:00Z', + '2.2.5': '2026-01-02T00:00:00Z', + '2.2.6': '2026-01-03T00:00:00Z', }, }), { status: 200 })) @@ -2148,16 +2148,16 @@ describe('Settings Routes - versions, directory files, skills, MCP and maintenan const json = await res.json() as { versions: Array<{ version: string; tag: string; name: string; publishedAt: string | null }>; currentVersion: string } expect(res.status).toBe(200) - expect(json.currentVersion).toBe('2.2.1') + expect(json.currentVersion).toBe('2.2.5') expect(json.versions).toEqual([ - { version: '2.2.2', tag: 'v2.2.2', name: 'v2.2.2', publishedAt: '2026-01-03T00:00:00Z' }, - { version: '2.2.1', tag: 'v2.2.1', name: 'v2.2.1', publishedAt: '2026-01-02T00:00:00Z' }, + { version: '2.2.6', tag: 'v2.2.6', name: 'v2.2.6', publishedAt: '2026-01-03T00:00:00Z' }, + { version: '2.2.5', tag: 'v2.2.5', name: 'v2.2.5', publishedAt: '2026-01-02T00:00:00Z' }, ]) expect(fetchMock).toHaveBeenCalledWith('https://registry.npmjs.org/@opencode/cli') }) it('serves repeated requests from the installer cache', async () => { - fetchMock.mockImplementation(async () => new Response(JSON.stringify({ versions: { '2.2.1': {} }, time: {} }), { status: 200 })) + fetchMock.mockImplementation(async () => new Response(JSON.stringify({ versions: { '2.2.5': {} }, time: {} }), { status: 200 })) await app.request(new Request('http://localhost/opencode-versions')) const res = await app.request(new Request('http://localhost/opencode-versions')) @@ -2195,35 +2195,35 @@ describe('Settings Routes - versions, directory files, skills, MCP and maintenan describe('POST /opencode-install-version', () => { it('installs the requested version and restarts the server', async () => { mockGetVersion.mockReturnValue('2.1.1') - mockFetchVersion.mockResolvedValue('2.2.1') + mockFetchVersion.mockResolvedValue('2.2.5') mockInstallOpenCodeVersion.mockResolvedValueOnce('/home/node/.opencode/bin/opencode') const res = await app.request(new Request('http://localhost/opencode-install-version', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), })) const json = await res.json() as { success: boolean; message: string; oldVersion: string; newVersion: string } expect(res.status).toBe(200) expect(json.success).toBe(true) expect(json.oldVersion).toBe('2.1.1') - expect(json.newVersion).toBe('2.2.1') - expect(json.message).toContain('changed from v2.1.1 to v2.2.1') + expect(json.newVersion).toBe('2.2.5') + expect(json.message).toContain('changed from v2.1.1 to v2.2.5') expect(supervisor.restart).toHaveBeenCalledTimes(1) - expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.1') + expect(mockInstallOpenCodeVersion).toHaveBeenCalledWith('2.2.5') }) it('returns 500 without restarting when the version install fails before the binary swap', async () => { mockGetVersion.mockReturnValue('2.1.1') - mockFetchVersion.mockResolvedValue('2.2.1') + mockFetchVersion.mockResolvedValue('2.2.5') mockInstallOpenCodeVersion.mockRejectedValueOnce(new Error('download failed')) supervisor.restart.mockResolvedValue({ healthy: false }) const res = await app.request(new Request('http://localhost/opencode-install-version', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ version: '2.2.1' }), + body: JSON.stringify({ version: '2.2.5' }), })) const json = await res.json() as { recovered: boolean; newVersion: string } diff --git a/backend/test/scripts/docker-entrypoint.test.ts b/backend/test/scripts/docker-entrypoint.test.ts index ec7dcb18d..cad3ae0f1 100644 --- a/backend/test/scripts/docker-entrypoint.test.ts +++ b/backend/test/scripts/docker-entrypoint.test.ts @@ -240,6 +240,7 @@ const runOpenCodeSection = (snippet: string, env: Record = {}) = OCM_STUB_RUNUSER_EXEC: '1', HOME: homeDir, OPENCODE_BUNDLED_VERSION: '2.0.15', + OPENCODE_MIN_VERSION: '2.0.15', ...env, }, }) @@ -459,11 +460,11 @@ exit 1`) expect(curlLog()).toHaveLength(0) }) - it('treats every persisted binary as unsupported when the bundled version is unknown', () => { + it('treats every persisted binary as unsupported when the minimum version is unknown', () => { mkdirSync(join(stubDir, 'home/.opencode/bin'), { recursive: true }) writeBinary(join(stubDir, 'home/.opencode/bin'), '2.0.15') const res = runOpenCodeSection(`${installPrelude()}\nreconcile_persisted_opencode`, { - OPENCODE_BUNDLED_VERSION: '', + OPENCODE_MIN_VERSION: '', }) expect(res.status).toBe(0) expect(existsSync(homeBinPath())).toBe(false) diff --git a/backend/test/services/opencode-installer.test.ts b/backend/test/services/opencode-installer.test.ts index a358f22e7..a10c4301e 100644 --- a/backend/test/services/opencode-installer.test.ts +++ b/backend/test/services/opencode-installer.test.ts @@ -131,46 +131,46 @@ describe('resolveOpenCodeInstallTarget', () => { describe('installOpenCodeVersion', () => { it('downloads, verifies and installs the requested version', async () => { const homeDirectory = join(workDirectory, 'home') - const archivePath = createArchive(workDirectory, '2.2.1') + const archivePath = createArchive(workDirectory, '2.2.5') const { calls, fetchFn } = createFetch(archivePath) - const installedPath = await installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget }) + const installedPath = await installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget }) expect(installedPath).toBe(resolveOpenCodeBinaryPath(homeDirectory)) - expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.1/opencode-linux-x64.tar.gz']) + expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.5/opencode-linux-x64.tar.gz']) expect(statSync(installedPath).mode & 0o777).toBe(0o755) - expect(readFileSync(installedPath, 'utf-8')).toContain('2.2.1') + expect(readFileSync(installedPath, 'utf-8')).toContain('2.2.5') }) it('requests the musl asset for a musl linux target', async () => { const homeDirectory = join(workDirectory, 'home') - const archivePath = createArchive(workDirectory, '2.2.1') + const archivePath = createArchive(workDirectory, '2.2.5') const { calls, fetchFn } = createFetch(archivePath) - await installOpenCodeVersion('2.2.1', { + await installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: { platform: 'linux', arch: 'arm64', musl: true }, }) - expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.1/opencode-linux-arm64-musl.tar.gz']) + expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.5/opencode-linux-arm64-musl.tar.gz']) }) it('extracts the darwin zip asset and installs it', async () => { const homeDirectory = join(workDirectory, 'home') - const archivePath = createDarwinArchive(workDirectory, '2.2.1') + const archivePath = createDarwinArchive(workDirectory, '2.2.5') const { calls, fetchFn } = createFetch(archivePath) - const installedPath = await installOpenCodeVersion('2.2.1', { + const installedPath = await installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: { platform: 'darwin', arch: 'arm64', musl: false }, }) - expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.1/opencode-darwin-arm64.zip']) + expect(calls).toEqual(['https://opencode.ai/files/bin/2.2.5/opencode-darwin-arm64.zip']) expect(installedPath).toBe(resolveOpenCodeBinaryPath(homeDirectory)) expect(statSync(installedPath).mode & 0o777).toBe(0o755) - expect(readFileSync(installedPath, 'utf-8')).toContain('2.2.1') + expect(readFileSync(installedPath, 'utf-8')).toContain('2.2.5') }) it('rejects an OpenCode 1.x version before downloading anything', async () => { @@ -178,18 +178,18 @@ describe('installOpenCodeVersion', () => { const { calls, fetchFn } = createFetch(null) await expect(installOpenCodeVersion('1.18.32', { fetch: fetchFn, homeDirectory, target: linuxTarget })) - .rejects.toThrow('OpenCode 1.18.32 is not supported; OpenCode Manager requires OpenCode >=2.2.1 <3.0.0') + .rejects.toThrow('OpenCode 1.18.32 is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0') expect(calls).toEqual([]) }) - it.each(['2.2.0', '3.0.0', '2.2.1-beta.1', '2.2.1; rm -rf /'])( + it.each(['2.2.4', '3.0.0', '2.2.5-beta.1', '2.2.5; rm -rf /'])( 'rejects %s outside the supported stable range before downloading anything', async (version) => { const homeDirectory = join(workDirectory, 'home') const { calls, fetchFn } = createFetch(null) await expect(installOpenCodeVersion(version, { fetch: fetchFn, homeDirectory, target: linuxTarget })) - .rejects.toThrow(/requires OpenCode >=2\.2\.1 <3\.0\.0/) + .rejects.toThrow(/requires OpenCode >=2\.2\.5 <3\.0\.0/) expect(calls).toEqual([]) }, ) @@ -198,18 +198,18 @@ describe('installOpenCodeVersion', () => { const homeDirectory = join(workDirectory, 'home') const { fetchFn } = createFetch(null, 404) - await expect(installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget })) + await expect(installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget })) .rejects.toThrow(/404/) expect(() => statSync(resolveOpenCodeBinaryPath(homeDirectory))).toThrow() }) it('fails and leaves no binary behind when the archive reports a different version', async () => { const homeDirectory = join(workDirectory, 'home') - const archivePath = createArchive(workDirectory, '2.2.2') + const archivePath = createArchive(workDirectory, '2.2.6') const { fetchFn } = createFetch(archivePath) - await expect(installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget })) - .rejects.toThrow(/2\.2\.1/) + await expect(installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget })) + .rejects.toThrow(/2\.2\.5/) expect(() => statSync(resolveOpenCodeBinaryPath(homeDirectory))).toThrow() }) @@ -218,21 +218,21 @@ describe('installOpenCodeVersion', () => { mkdirSync(join(homeDirectory, '.opencode', 'bin'), { recursive: true }) writeFileSync(resolveOpenCodeBinaryPath(homeDirectory), '#!/bin/sh\necho "opencode v2.1.1"\n') - const archivePath = createArchive(workDirectory, '2.2.1') + const archivePath = createArchive(workDirectory, '2.2.5') const { fetchFn } = createFetch(archivePath) - await installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget }) + await installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget }) - expect(readFileSync(resolveOpenCodeBinaryPath(homeDirectory), 'utf-8')).toContain('2.2.1') + expect(readFileSync(resolveOpenCodeBinaryPath(homeDirectory), 'utf-8')).toContain('2.2.5') }) it('cleans up its staging directory after a successful install', async () => { const homeDirectory = join(workDirectory, 'home') - const archivePath = createArchive(workDirectory, '2.2.1') + const archivePath = createArchive(workDirectory, '2.2.5') const { fetchFn } = createFetch(archivePath) const before = countStagingDirectories() - await installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget }) + await installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget }) expect(countStagingDirectories()).toBe(before) }) @@ -244,15 +244,15 @@ describe('listOpenCodeVersions', () => { '1.18.32': {}, '2.0.14': {}, '2.1.1': {}, - '2.2.1': {}, - '2.2.2': {}, - '2.2.1-beta.1': {}, + '2.2.5': {}, + '2.2.6': {}, + '2.2.5-beta.1': {}, '3.0.0': {}, 'not-a-version': {}, }, time: { - '2.2.1': '2026-02-01T00:00:00.000Z', - '2.2.2': '2026-03-01T00:00:00.000Z', + '2.2.5': '2026-02-01T00:00:00.000Z', + '2.2.6': '2026-03-01T00:00:00.000Z', }, } @@ -262,8 +262,8 @@ describe('listOpenCodeVersions', () => { const versions = await listOpenCodeVersions({ fetch: fetchFn }) expect(versions).toEqual([ - { version: '2.2.2', publishedAt: '2026-03-01T00:00:00.000Z' }, - { version: '2.2.1', publishedAt: '2026-02-01T00:00:00.000Z' }, + { version: '2.2.6', publishedAt: '2026-03-01T00:00:00.000Z' }, + { version: '2.2.5', publishedAt: '2026-02-01T00:00:00.000Z' }, ]) expect(calls).toEqual(['https://registry.npmjs.org/@opencode/cli']) }) @@ -331,7 +331,7 @@ describe('OpenCodeInstallError swap signalling', () => { const homeDirectory = join(workDirectory, 'home') const { fetchFn } = createFetch(null, 404) - const promise = installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget }) + const promise = installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget }) await expect(promise).rejects.toBeInstanceOf(OpenCodeInstallError) await expect(promise).rejects.toMatchObject({ swapStarted: false }) @@ -341,10 +341,10 @@ describe('OpenCodeInstallError swap signalling', () => { it('marks a failure while replacing the binary as having started the binary swap', async () => { const homeDirectory = join(workDirectory, 'home') mkdirSync(resolveOpenCodeBinaryPath(homeDirectory), { recursive: true }) - const archivePath = createArchive(workDirectory, '2.2.1') + const archivePath = createArchive(workDirectory, '2.2.5') const { fetchFn } = createFetch(archivePath) - const promise = installOpenCodeVersion('2.2.1', { fetch: fetchFn, homeDirectory, target: linuxTarget }) + const promise = installOpenCodeVersion('2.2.5', { fetch: fetchFn, homeDirectory, target: linuxTarget }) await expect(promise).rejects.toBeInstanceOf(OpenCodeInstallError) await expect(promise).rejects.toMatchObject({ swapStarted: true }) diff --git a/backend/test/services/opencode-single-server.test.ts b/backend/test/services/opencode-single-server.test.ts index 1f2c4d62c..08deb2df6 100644 --- a/backend/test/services/opencode-single-server.test.ts +++ b/backend/test/services/opencode-single-server.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach, afterEach, beforeAll } from 'vite const createOpenCodeClientMock = vi.hoisted(() => vi.fn(() => ({ api: { server: { - info: vi.fn().mockResolvedValue({ version: '2.2.1', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } }), + info: vi.fn().mockResolvedValue({ version: '2.2.5', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } }), }, }, forwardRaw: vi.fn(), @@ -354,7 +354,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) setOpenCodeEnv({ host: '0.0.0.0', password: 'envpassword123' }) @@ -382,7 +382,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) setOpenCodeEnv({ host: '0.0.0.0', password: '' }) @@ -420,7 +420,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -539,7 +539,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const savedEnv = snapshotMicrosandboxEnv() @@ -782,7 +782,7 @@ describe('OpenCodeServerManager - server auth', () => { let capturedSignal: AbortSignal | undefined const info = vi.fn(async (options: { signal?: AbortSignal }) => { capturedSignal = options.signal - return { version: '2.2.1', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } } + return { version: '2.2.5', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } } }) createOpenCodeClientMock.mockImplementationOnce(() => ({ api: { server: { info } }, @@ -812,7 +812,7 @@ describe('OpenCodeServerManager - server auth', () => { capturedSignal = options.signal queueMicrotask(() => emitExit(1, null)) return new Promise((resolve) => { - setTimeout(() => resolve({ version: '2.2.1', pid: 1234, urls: [], paths: { tmp: '/tmp' } }), 0) + setTimeout(() => resolve({ version: '2.2.5', pid: 1234, urls: [], paths: { tmp: '/tmp' } }), 0) }) }) createOpenCodeClientMock.mockImplementationOnce(() => ({ @@ -861,7 +861,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -949,7 +949,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -1035,7 +1035,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) manager.setDatabase(createPasswordDb(null)) @@ -1148,7 +1148,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -1198,7 +1198,7 @@ describe('OpenCodeServerManager - server auth', () => { if (cmd.includes('lsof')) { return spawnMock.mock.calls.length > 0 ? '1234\n' : '9999\n' } - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -1945,7 +1945,7 @@ describe('OpenCodeServerManager - server auth', () => { if (cmd.includes('lsof')) { return spawnMock.mock.calls.length > 0 ? '1234\n' : '9999\n' } - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) readFileSyncMock.mockReturnValue(procStatStringWithGroup(9999, '42')) @@ -2002,7 +2002,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) readdirSyncMock.mockReturnValue(['10001']) @@ -2081,7 +2081,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) readdirSyncMock.mockReturnValue(['10001']) @@ -2144,7 +2144,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) readFileSyncMock.mockImplementation((filePath: string) => { @@ -2200,7 +2200,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '9999\n' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) readFileSyncMock.mockReturnValue(procStatStringWithGroup(9999, '42')) @@ -2242,7 +2242,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '9998\n' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) killSpy.mockImplementation(((pid: number, signal?: number | string) => { @@ -2279,7 +2279,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '9997\n' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) killSpy.mockImplementation(((pid: number, signal?: number | string) => { @@ -2311,7 +2311,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '8888\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) killSpy.mockImplementation(((pid: number, signal?: number | string) => { @@ -2986,7 +2986,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) installManagedPluginsMock.mockRejectedValueOnce(new Error('readonly filesystem')) @@ -3018,7 +3018,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -3049,7 +3049,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -3077,7 +3077,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) restoreQuarantinedOpenCodePluginsMock.mockRejectedValueOnce(new Error('readonly filesystem')) @@ -3108,7 +3108,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) installManagedPluginsMock.mockRejectedValueOnce(new Error('readonly filesystem')) @@ -3140,7 +3140,7 @@ describe('OpenCodeServerManager - server auth', () => { })) execSyncMock.mockImplementation((cmd: string) => { if (cmd.includes('lsof')) return spawnMock.mock.calls.length > 0 ? '1234\n' : '' - if (cmd.includes('opencode --version')) return '2.2.1\n' + if (cmd.includes('opencode --version')) return '2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -3176,7 +3176,7 @@ describe('OpenCodeServerManager - server auth', () => { await expect(manager.start()).rejects.toThrow('OpenCode 1.18.32 is not supported') expect(spawnMock).not.toHaveBeenCalled() - expect(manager.getLastStartupError()).toContain('requires OpenCode >=2.2.1 <3.0.0') + expect(manager.getLastStartupError()).toContain('requires OpenCode >=2.2.5 <3.0.0') expect(manager.isLastStartupErrorNonRecoverable()).toBe(true) }) @@ -3185,7 +3185,7 @@ describe('OpenCodeServerManager - server auth', () => { isEnabled: () => false, })) execSyncMock.mockImplementation((cmd: string) => { - if (cmd.includes('opencode --version')) return 'opencode v2.2.1\n' + if (cmd.includes('opencode --version')) return 'opencode v2.2.5\n' throw new Error('not found') }) const { OpenCodeServerManager } = await import('../../src/services/opencode-single-server') @@ -3195,7 +3195,7 @@ describe('OpenCodeServerManager - server auth', () => { await manager.start() expect(spawnMock).toHaveBeenCalled() - expect(manager.getVersion()).toBe('2.2.1') + expect(manager.getVersion()).toBe('2.2.5') expect(manager.isVersionSupported()).toBe(true) }) @@ -3216,7 +3216,7 @@ describe('OpenCodeServerManager - server auth', () => { marked = true manager.markRestartPending() } - return { version: '2.2.1', pid: 1234, urls: [], paths: { tmp: '/tmp' } } + return { version: '2.2.5', pid: 1234, urls: [], paths: { tmp: '/tmp' } } }), }, }, @@ -3438,7 +3438,7 @@ describe('ConfigReloadError', () => { }) describe('OpenCodeServerManager - checkHealth', () => { - const serverInfo = { version: '2.2.1', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } } + const serverInfo = { version: '2.2.5', pid: 1234, urls: ['http://127.0.0.1:5551'], paths: { tmp: '/tmp' } } it('probes GET /api/info through the API client and returns true for a ServerInfo response', async () => { const { opencodeServerManager } = await import('../../src/services/opencode-single-server') diff --git a/backend/test/shared/opencode-contract.test.ts b/backend/test/shared/opencode-contract.test.ts index 471d225e7..0b00dd666 100644 --- a/backend/test/shared/opencode-contract.test.ts +++ b/backend/test/shared/opencode-contract.test.ts @@ -2,6 +2,7 @@ import { readdirSync, readFileSync } from 'fs' import { join } from 'path' import { describe, expect, it } from 'vitest' import { + OPENCODE_MIN_VERSION, OPENCODE_PINNED_VERSION, OPENCODE_SERVER_USERNAME, OPENCODE_SUPPORTED_VERSION_RANGE, @@ -49,9 +50,11 @@ function createFetchSpy() { describe('OpenCode v2 contract', () => { describe('version policy', () => { - it('pins the client version, derives the floor from it and fixes the V2 server username', () => { - expect(OPENCODE_PINNED_VERSION).toBe('2.2.1') - expect(OPENCODE_SUPPORTED_VERSION_RANGE).toBe('>=2.2.1 <3.0.0') + it('pins the built-against version, keeps a separate supported floor and fixes the V2 server username', () => { + expect(OPENCODE_PINNED_VERSION).toBe('2.2.5') + expect(OPENCODE_MIN_VERSION).toBe('2.2.5') + expect(compareOpenCodeVersions(OPENCODE_PINNED_VERSION, OPENCODE_MIN_VERSION)).toBeGreaterThanOrEqual(0) + expect(OPENCODE_SUPPORTED_VERSION_RANGE).toBe('>=2.2.5 <3.0.0') expect(OPENCODE_SERVER_USERNAME).toBe('opencode') }) @@ -61,48 +64,49 @@ describe('OpenCode v2 contract', () => { expect(isSupportedOpenCodeVersion('2.0.15')).toBe(false) expect(isSupportedOpenCodeVersion('2.1.0')).toBe(false) expect(isSupportedOpenCodeVersion('2.1.1')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.0')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1')).toBe(true) - expect(isSupportedOpenCodeVersion('2.2.2')).toBe(true) + expect(isSupportedOpenCodeVersion('2.2.1')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.4')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5')).toBe(true) + expect(isSupportedOpenCodeVersion('2.2.6')).toBe(true) expect(isSupportedOpenCodeVersion('2.3.0')).toBe(true) - expect(isSupportedOpenCodeVersion('v2.2.1')).toBe(true) - expect(isSupportedOpenCodeVersion(' v2.2.1 ')).toBe(true) + expect(isSupportedOpenCodeVersion('v2.2.5')).toBe(true) + expect(isSupportedOpenCodeVersion(' v2.2.5 ')).toBe(true) expect(isSupportedOpenCodeVersion('3.0.0')).toBe(false) expect(isSupportedOpenCodeVersion('3.1.0')).toBe(false) expect(isSupportedOpenCodeVersion('not-a-version')).toBe(false) }) it('rejects prereleases of the pinned version', () => { - expect(isSupportedOpenCodeVersion('2.2.1-alpha.1')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1-alpha')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1-rc.1+build.5')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1-0')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5-alpha.1')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5-alpha')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5-rc.1+build.5')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5-0')).toBe(false) }) it('accepts prereleases of higher numeric cores within the pinned major', () => { expect(isSupportedOpenCodeVersion('2.3.0-beta.1')).toBe(true) - expect(isSupportedOpenCodeVersion('2.2.2-rc.2')).toBe(true) + expect(isSupportedOpenCodeVersion('2.2.6-rc.2')).toBe(true) expect(isSupportedOpenCodeVersion('3.0.0-beta.1')).toBe(false) }) it('ignores build metadata for precedence', () => { - expect(isSupportedOpenCodeVersion('2.2.1+build')).toBe(true) - expect(isSupportedOpenCodeVersion('2.2.1+2.0.0')).toBe(true) + expect(isSupportedOpenCodeVersion('2.2.5+build')).toBe(true) + expect(isSupportedOpenCodeVersion('2.2.5+2.0.0')).toBe(true) }) it('rejects malformed and incomplete versions', () => { expect(isSupportedOpenCodeVersion('2garbage')).toBe(false) expect(isSupportedOpenCodeVersion('2')).toBe(false) expect(isSupportedOpenCodeVersion('2.0')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1.1')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5.1')).toBe(false) expect(isSupportedOpenCodeVersion('02.2.1')).toBe(false) - expect(isSupportedOpenCodeVersion('2.2.1-alpha..1')).toBe(false) + expect(isSupportedOpenCodeVersion('2.2.5-alpha..1')).toBe(false) expect(isSupportedOpenCodeVersion('')).toBe(false) }) it('describes the supported range for unsupported versions', () => { expect(describeUnsupportedOpenCodeVersion('3.0.0')).toBe( - 'OpenCode 3.0.0 is not supported; OpenCode Manager requires OpenCode >=2.2.1 <3.0.0', + 'OpenCode 3.0.0 is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0', ) }) }) @@ -144,25 +148,34 @@ describe('OpenCode v2 contract', () => { }) describe('pinned version sources', () => { - it('keeps the Dockerfile OPENCODE_VERSION default in sync with the pin', () => { + it('keeps the Dockerfile OPENCODE_VERSION and OPENCODE_MIN_VERSION defaults in sync with the pin and floor', () => { const dockerfile = readFileSync(join(REPO_ROOT, 'Dockerfile'), 'utf8') expect(dockerfile).toMatch(new RegExp(`^ARG OPENCODE_VERSION=${OPENCODE_PINNED_VERSION.replace(/\./g, '\\.')}$`, 'm')) + expect(dockerfile).toMatch(new RegExp(`^ARG OPENCODE_MIN_VERSION=${OPENCODE_MIN_VERSION.replace(/\./g, '\\.')}$`, 'm')) expect(dockerfile).toMatch(/^ENV OPENCODE_BUNDLED_VERSION=\$\{OPENCODE_VERSION\}$/m) + expect(dockerfile).toMatch(/^ENV OPENCODE_MIN_VERSION=\$\{OPENCODE_MIN_VERSION\}$/m) }) - it('keeps the shared SDK dependency in sync with the pin', () => { + it('resolves every @opencode package from the workspace catalog at the pin', () => { + const workspace = readFileSync(join(REPO_ROOT, 'pnpm-workspace.yaml'), 'utf8') + const catalogVersion = (name: string) => + workspace.match(new RegExp(`^\\s+'${name.replace(/[/.]/g, '\\$&')}':\\s*(\\S+)$`, 'm'))?.[1] + for (const name of ['@opencode/client', '@opencode/schema', '@opencode/plugin', '@opencode/theme']) { + expect(catalogVersion(name)).toBe(OPENCODE_PINNED_VERSION) + } const manifest = JSON.parse( readFileSync(join(REPO_ROOT, 'shared', 'package.json'), 'utf8'), ) as PackageManifest - expect(manifest.dependencies['@opencode/client']).toBe(OPENCODE_PINNED_VERSION) + expect(manifest.dependencies['@opencode/client']).toBe('catalog:') + expect(manifest.dependencies['@opencode/schema']).toBe('catalog:') }) - it('makes the shell scripts derive the pin from the Dockerfile or the bundled version instead of hardcoding it', () => { + it('makes the shell scripts derive the floor from the Dockerfile or the environment instead of hardcoding it', () => { const entrypoint = readFileSync(join(REPO_ROOT, 'scripts', 'docker-entrypoint.sh'), 'utf8') const setupDev = readFileSync(join(REPO_ROOT, 'scripts', 'setup-dev.sh'), 'utf8') const releaseHelper = readFileSync(join(REPO_ROOT, 'scripts', 'lib', 'opencode-release.sh'), 'utf8') - expect(entrypoint).toContain('OPENCODE_SUPPORTED_FLOOR="${OPENCODE_BUNDLED_VERSION:-}"') - expect(setupDev).toContain("sed -n 's/^ARG OPENCODE_VERSION=//p' \"$REPO_ROOT/Dockerfile\"") + expect(entrypoint).toContain('OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-}"') + expect(setupDev).toContain("sed -n 's/^ARG OPENCODE_MIN_VERSION=//p' \"$REPO_ROOT/Dockerfile\"") for (const script of [entrypoint, setupDev, releaseHelper]) { expect(script).not.toMatch(/(?Supported ) : ( - Unsupported version + Different version: some features may not work )} ) diff --git a/frontend/src/components/settings/OpenCodeServersSettings.tsx b/frontend/src/components/settings/OpenCodeServersSettings.tsx index e0084ab3e..3b6e081fd 100644 --- a/frontend/src/components/settings/OpenCodeServersSettings.tsx +++ b/frontend/src/components/settings/OpenCodeServersSettings.tsx @@ -34,7 +34,7 @@ function ServerTestResult({ result }: { result: OpenCodeServerTestResult }) { {result.versionSupported ? ( Supported ) : ( - Unsupported version + Different version: some features may not work )} ) diff --git a/ocm-cli/README.md b/ocm-cli/README.md index 182a605c2..a9d75128f 100644 --- a/ocm-cli/README.md +++ b/ocm-cli/README.md @@ -10,11 +10,12 @@ to the local working tree. | ocm | OpenCode Manager | Local OpenCode | |---|---|---| +| 0.6.x | >= 0.19.0 | >= 2.2.3, < 3 | | 0.5.x | >= 0.19.0 | >= 2.2.0, < 3 | | 0.4.x | >= 0.19.0 | >= 2.0.15, < 3 | | 0.3.x | >= 0.19.0 | >= 2.0.15, < 3 | -ocm 0.5.x, 0.4.x and 0.3.x attach through the repo-scoped Manager proxy +ocm 0.6.x, 0.5.x, 0.4.x and 0.3.x attach through the repo-scoped Manager proxy (`/api/opencode-proxy/repos/:repoId`), load the plugin from the OpenCode 2 `cli.json` `plugins` list, and move sessions with OpenCode 2 session export/import. Older Managers lack that route; `ocm` then fails with @@ -24,10 +25,13 @@ Use ocm 0.2.x with OpenCode Manager < 0.19.0 and OpenCode 1.x, installed pinned `@opencode-manager/ocm-cli@0.2`. ocm is published together with each OpenCode Manager release. -From ocm 0.5.0, the TUI plugin is built against `@opencode/plugin` 2.2.x and -OpenTUI 0.7, so it requires a local OpenCode >= 2.2.0: OpenCode 2.2.0 refuses -plugins built for OpenTUI 0.6, and older OpenCode builds may refuse the OpenTUI -0.7 peers. Stay on ocm 0.4.x with a local OpenCode older than 2.2.0. +From ocm 0.6.0, the TUI plugin is built against `@opencode/plugin` 2.2.5 and +OpenTUI 0.8, so it requires a local OpenCode >= 2.2.3. OpenCode 2.2.0 through +2.2.2 ship OpenTUI 0.7; stay on ocm 0.5.x with those builds. ocm 0.5.0 itself +is built against `@opencode/plugin` 2.2.1 and OpenTUI 0.7 and requires a local +OpenCode >= 2.2.0: OpenCode 2.2.0 refuses plugins built for OpenTUI 0.6, and +older OpenCode builds may refuse the 0.7 peers. Stay on ocm 0.4.x with a local +OpenCode older than 2.2.0. ocm 0.5.0 removes `/ocm-goal`; session goals use OpenCode's built-in `/goal` command, and OpenCode Manager no longer serves `/api/internal/session-goals`. @@ -320,7 +324,7 @@ global installs); the plugin surface is TUI-only. ## Requirements - macOS or Linux (Windows is unsupported) -- OpenCode >= 2.2.0 (same major, 2.x) available on `PATH` for the TUI plugin +- OpenCode >= 2.2.3 (same major, 2.x) available on `PATH` for the TUI plugin - Node available on `PATH` (runs the `ocm` binary) - OpenCode Manager >= 0.19.0 - `git` and `tar` (with gzip support, i.e. the `-z` flag) available on `PATH` diff --git a/ocm-cli/package.json b/ocm-cli/package.json index 28cce15ff..39e75b8ab 100644 --- a/ocm-cli/package.json +++ b/ocm-cli/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-manager/ocm-cli", - "version": "0.5.0", + "version": "0.6.0", "description": "OpenCode Manager CLI: attach a local OpenCode TUI to a Manager-hosted repo.", "license": "MIT", "repository": { @@ -18,9 +18,9 @@ } }, "peerDependencies": { - "@opencode/theme": ">=2.2.1 <3.0.0", - "@opentui/core": ">=0.7.0 <0.8.0", - "@opentui/solid": ">=0.7.0 <0.8.0", + "@opencode/theme": ">=2.2.5 <3.0.0", + "@opentui/core": ">=0.8.0 <0.9.0", + "@opentui/solid": ">=0.8.0 <0.9.0", "solid-js": ">=1.9.0" }, "peerDependenciesMeta": { @@ -58,16 +58,16 @@ }, "devDependencies": { "@eslint/js": "^9.36.0", - "@opencode/plugin": "2.2.1", - "@opencode/theme": "2.2.1", "@opencode-manager/shared": "workspace:*", - "@opentui/core": "0.7.0", - "@opentui/solid": "0.7.0", + "@opencode/plugin": "catalog:", + "@opencode/theme": "catalog:", + "@opentui/core": "catalog:", + "@opentui/solid": "catalog:", "@types/bun": "^1.3.14", "@types/node": "^22.0.0", "eslint": "^9.39.1", "jsonc-parser": "^3.3.1", - "solid-js": "1.9.12", + "solid-js": "catalog:", "typescript": "^5.5.0", "typescript-eslint": "^8.45.0", "uqr": "^0.1.3", diff --git a/ocm-cli/test/package-compat.test.ts b/ocm-cli/test/package-compat.test.ts index ba91278f9..e45c82eb3 100644 --- a/ocm-cli/test/package-compat.test.ts +++ b/ocm-cli/test/package-compat.test.ts @@ -1,6 +1,6 @@ import { readFileSync } from 'node:fs' import { join } from 'node:path' -import { parseSemver } from '@opencode-manager/shared/utils' +import { compareSemver, parseSemver } from '@opencode-manager/shared/utils' import { describe, expect, it } from 'vitest' interface PackageManifest { @@ -20,21 +20,40 @@ function readManifest(path: string): PackageManifest { } /** - * Derives the minor-line peer range for a plugin's exact dependency pin. + * Reads the version of a dependency installed in ocm-cli's node_modules. + */ +function installedVersion(name: string): string { + return readManifest(join(__dirname, '..', 'node_modules', name, 'package.json')).version +} + +/** + * Derives the minor-line peer range for an exact dependency version. * - * A `0.x.y` pin is constrained to its minor line; a pin at major `1` or above + * A `0.x.y` version is constrained to its minor line; a version at major `1` or above * is constrained to its major line. */ -function deriveMinorLineRange(pin: string): string { - const parsed = parseSemver(pin) +function deriveMinorLineRange(version: string): string { + const parsed = parseSemver(version) if (!parsed || parsed.prerelease.length > 0) { - throw new Error(`Cannot derive a peer range from pin "${pin}"`) + throw new Error(`Cannot derive a peer range from version "${version}"`) } const { major, minor } = parsed if (major === 0) { return `>=0.${minor}.0 <0.${minor + 1}.0` } - return `>=${pin} <${major + 1}.0.0` + return `>=${version} <${major + 1}.0.0` +} + +/** + * Asserts that an installed version satisfies a plugin peer declaration, + * accepting both an exact peer pin and an open `>=` floor. + */ +function expectVersionSatisfiesPeer(version: string, peer: string): void { + if (peer.startsWith('>=')) { + expect(compareSemver(version, peer.slice(2).trim())).toBeGreaterThanOrEqual(0) + return + } + expect(version).toBe(peer) } const ocmManifest = readManifest(join(__dirname, '..', 'package.json')) @@ -44,15 +63,16 @@ const pluginManifest = readManifest( describe('ocm-cli package compatibility with the installed @opencode/plugin', () => { for (const name of PINNED_PACKAGES) { - it(`${name} matches the plugin peer pin`, () => { - const pluginPin = pluginManifest.peerDependencies[name] - expect(ocmManifest.devDependencies[name]).toBe(pluginPin) - expect(ocmManifest.peerDependencies[name]).toBe(deriveMinorLineRange(pluginPin)) + it(`${name} satisfies the plugin peer and derives the published peer range`, () => { + const version = installedVersion(name) + expectVersionSatisfiesPeer(version, pluginManifest.peerDependencies[name]!) + expect(ocmManifest.devDependencies[name]).toBe('catalog:') + expect(ocmManifest.peerDependencies[name]).toBe(deriveMinorLineRange(version)) expect(ocmManifest.peerDependenciesMeta[name]?.optional).toBe(true) }) } - it('@opencode/plugin devDependency matches the installed plugin version', () => { - expect(ocmManifest.devDependencies['@opencode/plugin']).toBe(pluginManifest.version) + it('resolves @opencode/plugin from the workspace catalog', () => { + expect(ocmManifest.devDependencies['@opencode/plugin']).toBe('catalog:') }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 80fb8e5a4..0b2ca7000 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,33 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@opencode/client': + specifier: 2.2.5 + version: 2.2.5 + '@opencode/plugin': + specifier: 2.2.5 + version: 2.2.5 + '@opencode/schema': + specifier: 2.2.5 + version: 2.2.5 + '@opencode/theme': + specifier: 2.2.5 + version: 2.2.5 + '@opentui/core': + specifier: 0.8.2 + version: 0.8.2 + '@opentui/solid': + specifier: 0.8.2 + version: 0.8.2 + effect: + specifier: 4.0.6 + version: 4.0.6 + solid-js: + specifier: 1.9.18 + version: 1.9.18 + packageExtensionsChecksum: sha256-MiHZ3mgtOUb60KfpkAkui5ad/9eLmcQda0MiZKaTRqA= importers: @@ -28,7 +55,7 @@ importers: dependencies: '@better-auth/passkey': specifier: ^1.7.12 - version: 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5) + version: 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5) '@hono/node-server': specifier: ^1.19.17 version: 1.19.17(hono@4.13.13) @@ -43,7 +70,7 @@ importers: version: 7.0.1 better-auth: specifier: ^1.7.12 - version: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4) + version: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4) croner: specifier: ^10.0.1 version: 10.0.1 @@ -101,7 +128,7 @@ importers: dependencies: '@better-auth/passkey': specifier: ^1.7.12 - version: 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5) + version: 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5) '@dnd-kit/core': specifier: ^6.3.1 version: 6.3.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -167,7 +194,7 @@ importers: version: 6.0.0 better-auth: specifier: ^1.7.12 - version: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4) + version: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -305,17 +332,17 @@ importers: specifier: workspace:* version: link:../shared '@opencode/plugin': - specifier: 2.2.1 - version: 2.2.1(@opencode/theme@2.2.1(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentelemetry/semantic-conventions@1.43.0)(@opentui/core@0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentui/solid@0.7.0(solid-js@1.9.12)(typescript@5.9.3)(web-tree-sitter@0.25.10))(redis@6.2.1(@opentelemetry/api@1.9.0))(solid-js@1.9.12)(vite@7.3.0(@types/node@22.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0)) + specifier: 'catalog:' + version: 2.2.5(@opencode/theme@2.2.5(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentelemetry/semantic-conventions@1.43.0)(@opentui/core@0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentui/solid@0.8.2(solid-js@1.9.18)(typescript@5.9.3)(web-tree-sitter@0.25.10))(redis@6.2.1(@opentelemetry/api@1.9.0))(solid-js@1.9.18)(vite@7.3.0(@types/node@22.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0)) '@opencode/theme': - specifier: 2.2.1 - version: 2.2.1(typescript@5.9.3)(web-tree-sitter@0.25.10) + specifier: 'catalog:' + version: 2.2.5(typescript@5.9.3)(web-tree-sitter@0.25.10) '@opentui/core': - specifier: 0.7.0 - version: 0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10) + specifier: 'catalog:' + version: 0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10) '@opentui/solid': - specifier: 0.7.0 - version: 0.7.0(solid-js@1.9.12)(typescript@5.9.3)(web-tree-sitter@0.25.10) + specifier: 'catalog:' + version: 0.8.2(solid-js@1.9.18)(typescript@5.9.3)(web-tree-sitter@0.25.10) '@types/bun': specifier: ^1.3.14 version: 1.3.14 @@ -329,8 +356,8 @@ importers: specifier: ^3.3.1 version: 3.3.1 solid-js: - specifier: 1.9.12 - version: 1.9.12 + specifier: 'catalog:' + version: 1.9.18 typescript: specifier: ^5.5.0 version: 5.9.3 @@ -347,14 +374,14 @@ importers: shared: dependencies: '@opencode/client': - specifier: 2.2.1 - version: 2.2.1(effect@4.0.1)(solid-js@1.9.15) + specifier: 'catalog:' + version: 2.2.5(effect@4.0.6)(solid-js@1.9.18) '@opencode/schema': - specifier: 2.2.1 - version: 2.2.1 + specifier: 'catalog:' + version: 2.2.5 effect: - specifier: 4.0.1 - version: 4.0.1 + specifier: 'catalog:' + version: 4.0.6 jsonc-parser: specifier: ^3.3.1 version: 3.3.1 @@ -491,18 +518,10 @@ packages: resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} engines: {node: '>=18.0.0'} - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} @@ -515,10 +534,6 @@ packages: resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} - engines: {node: '>=6.9.0'} - '@babel/generator@7.29.7': resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} @@ -527,10 +542,6 @@ packages: resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} @@ -557,12 +568,6 @@ packages: resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.29.7': resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} @@ -573,10 +578,6 @@ packages: resolution: {integrity: sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.29.7': resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} @@ -591,47 +592,22 @@ packages: resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.29.11': resolution: {integrity: sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.10': resolution: {integrity: sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.29.7': resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} @@ -687,34 +663,18 @@ packages: resolution: {integrity: sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.7': resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} - engines: {node: '>=6.9.0'} - '@babel/types@7.29.11': resolution: {integrity: sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} - engines: {node: '>=6.9.0'} - '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -877,8 +837,8 @@ packages: peerDependencies: react: '>=16.8.0' - '@effect/opentelemetry@4.0.1': - resolution: {integrity: sha512-MZ7M1a1dxMqTqeNwIGSECjO7RrReEgeEwHo8BnbOqP+8IfVpdiJi/iPCmkFVWlAxanMl4qhsq69e0Hkul9t+bA==} + '@effect/opentelemetry@4.0.6': + resolution: {integrity: sha512-xhLR1EN4ZNaKM7Bx59SCFiRNduFzgH8LZXDrCpPN8MpQ93V0GQcFHTBah5T3EXO2v+5HTEacdTgetIA0Creqvw==} engines: {node: '>=18.0.0'} peerDependencies: '@opentelemetry/api': '>=1.9.0 <2.0.0' @@ -890,7 +850,7 @@ packages: '@opentelemetry/sdk-trace-node': '>=2.0.0 <3.0.0' '@opentelemetry/sdk-trace-web': '>=2.0.0 <3.0.0' '@opentelemetry/semantic-conventions': '>=1.33.0 <2.0.0' - effect: ^4.0.1 + effect: ^4.0.6 peerDependenciesMeta: '@opentelemetry/api': optional: true @@ -909,17 +869,17 @@ packages: '@opentelemetry/sdk-trace-web': optional: true - '@effect/platform-node-shared@4.0.1': - resolution: {integrity: sha512-VanaejudEhKJ4mpmqnEzdncVUhN6sGSO9ZDND54mRxAlZxj+4dYAWjDTYAy9o4tJo9aALeP2U8cVcOFzEdSeLg==} + '@effect/platform-node-shared@4.0.6': + resolution: {integrity: sha512-0PhyC4uG30xGtsajnxjkcBWfTjNRzUh4ZKl1Kvo/MNyCTb+W2O+fBzf7wCRCoqt+nLudiq4V7vX40Q8XIBuMaQ==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.1 + effect: ^4.0.6 - '@effect/platform-node@4.0.1': - resolution: {integrity: sha512-FT0Fu5LO0/XP9zBpj0HqrtJNLluK4StNKqf6TSulUsjp8FPBsDVXGJiDuyoN5J2yVMT7tgvhxSwaDcObWVRIaQ==} + '@effect/platform-node@4.0.6': + resolution: {integrity: sha512-gXvGHAy0/8PZbWvMMguloDUyeSP+rzYEo3FSEqUuGduxo9ORcGioDxPI+GOLCIFfam7VuNkmSUxBOiRMIrMrBw==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.1 + effect: ^4.0.6 redis: '>=5.0.0 <7.0.0' '@esbuild/aix-ppc64@0.27.2': @@ -1301,13 +1261,13 @@ packages: resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} engines: {node: ^20.17.0 || >=22.9.0} - '@opencode/ai@2.2.1': - resolution: {integrity: sha512-CjCIrqnh+GCWFJejWoRPu8M+yghX3eIVFeDBdMjvQrLkoJ3Hv4RjxD2InolHnRFK8lAEq+IJ6D8gwiTZMgRSzg==} + '@opencode/ai@2.2.5': + resolution: {integrity: sha512-TolUvNR615vpkc6FPf2np5PbAbO7k1gaVlrtZ5Wx5tN7Y8K2uSer0BZEiajJ2uBsF6RXJFfjjX1GZbcf85GtGQ==} - '@opencode/client@2.2.1': - resolution: {integrity: sha512-7QWtocDRR9eUsCJpyaj6k+aAEVDWO2CIzn9lwDNBJhiyt2hMf+yOyV/BKeK/9R7qKcXEOZ0tlGDvQC8HEtyjQg==} + '@opencode/client@2.2.5': + resolution: {integrity: sha512-EjYrFGWHURsIbjtmQdIsGSPmYZGaulxqwEfYxQH5t9838508BCIvDX9f7/ZsgNGHYXM0punA3W8hTt6GjxIEaw==} peerDependencies: - effect: 4.0.1 + effect: 4.0.6 solid-js: '>=1.9.0' peerDependenciesMeta: effect: @@ -1315,12 +1275,12 @@ packages: solid-js: optional: true - '@opencode/plugin@2.2.1': - resolution: {integrity: sha512-m5va0KJpG6CpyhKRQL/lKIP7tP0LgUvcuezpmob0e8spDOMtXh/+1Idb2zKxuIq14mKjwbQd0nz+BJbsqw1fqA==} + '@opencode/plugin@2.2.5': + resolution: {integrity: sha512-DHtmC55UM5NbtZwyayLxI/ygTMt3n+6xXYYBkOdNxmZ+rLcIbpjmeJvySqdxFmT3K7Pk/2pHsuocxhquu6sysA==} peerDependencies: - '@opencode/theme': 2.2.1 - '@opentui/core': 0.7.0 - '@opentui/solid': 0.7.0 + '@opencode/theme': 2.2.5 + '@opentui/core': '>=0.8.0' + '@opentui/solid': '>=0.8.0' electron: '*' solid-js: '>=1.9.0' vite: '>=6' @@ -1341,17 +1301,17 @@ packages: vite-plugin-solid: optional: true - '@opencode/protocol@2.2.1': - resolution: {integrity: sha512-lqkkRhrckEKmiSJi249rvfX/fXX8JeiMS7adtXonO1Ol3IqnEqY4uSZgvQiQ11SBR5RyDQaRW9KGoOjaTY4PAQ==} + '@opencode/protocol@2.2.5': + resolution: {integrity: sha512-T3ET0kS1kj8sv7/d+c1T2he7TXoZ4Rki7zTqHuv8b0kWhg3Vk6o0t+T9KsEQdEat7PFksgH5haC2iSSdJOB7/w==} - '@opencode/schema@2.2.1': - resolution: {integrity: sha512-TicxPQC1UKqq954X1yUKmBlXfJYl5CRr52FKwLZbYOfTxJd406Hoqt2arM4YI3c+abs17b9sumoq5uqFLhiGFA==} + '@opencode/schema@2.2.5': + resolution: {integrity: sha512-hlKk6cX28BwvOMOVG3Mtt4tEHxwY68Z6Gz5/cL32N8qfZexykCKsZai49Jms4/2gD8BzvVuBERnnlLDbdLstUA==} - '@opencode/theme@2.2.1': - resolution: {integrity: sha512-YAKT/TacMHM04rLi9o1zyVpgEhy3hpuHZKGs/VC6+wigRrieLvyTvS7/FmcOVEeophNmwL0FYI1sD/NCTLlxlA==} + '@opencode/theme@2.2.5': + resolution: {integrity: sha512-mUW5YKCxJQHnhrCepsyMoEqWZrQFipF5WrRmos9WB/iehZiUL6ix6OXjUmv5W5gctKzgQd8tYVi7x90H5/grHQ==} - '@opencode/util@2.2.1': - resolution: {integrity: sha512-MgJ8oJ/S+L5gRBmYk8bEHRVR99eEvUUDGg8fZLG/UD1+RPisJ6Ce1ZO++pBjTvkdS+Okk6lPxhbjYemIQtSciQ==} + '@opencode/util@2.2.5': + resolution: {integrity: sha512-RqbJ2EwxOs6oQMH1P4Yenkejit0oiAyxBTPXhzx4n6j4SOV0+Duz6lhftlJkAjTbq7Rw2b0Mzx4duOJUdw6s5A==} '@opentelemetry/api-logs@0.219.0': resolution: {integrity: sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==} @@ -1427,56 +1387,106 @@ packages: resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} engines: {node: '>=14'} - '@opentui/core-darwin-arm64@0.7.0': - resolution: {integrity: sha512-meEyZVUeCIauqHLWo/IsSlKzZPTnGIEjRLigefSZbrSAcOiMryd3OcUWvSByIXpYb9UPOpW0YxKKfVwepb9u2A==} + '@opentui/core-darwin-arm64@0.8.0': + resolution: {integrity: sha512-Cp+BL1DsRZhUG56AuyXlH8DA4dsKfZ3Qgh1XMYDGokkC4p9uv8kdNNhyzmURAFa2LK77XqVv1Zhwr/6DqqpMlw==} + cpu: [arm64] + os: [darwin] + + '@opentui/core-darwin-arm64@0.8.2': + resolution: {integrity: sha512-/8pFK289hi+jtoUgJYZJEABTreLoQ3tNVQuMFLF0xDBkREJaR9xvh2RHKzhVLut9bhKf9oY2bgD37pMgYNqwqA==} cpu: [arm64] os: [darwin] - '@opentui/core-darwin-x64@0.7.0': - resolution: {integrity: sha512-hZQ4OfAua5CtUv3hpZ81zhNTTLULbg0leQ/3eupjefityGP6163eS56IzymTWuiM/mVD/7Ad5bicH0I2JXhr1g==} + '@opentui/core-darwin-x64@0.8.0': + resolution: {integrity: sha512-Jp4pzdIuKsS3XYq9xuWuAO52tTj9gnWeeoI3bg456CF6XnY2yQMasWOd/WmjfvYfAaJzGDFa4DNXhByBMPVuTw==} cpu: [x64] os: [darwin] - '@opentui/core-linux-arm64-musl@0.7.0': - resolution: {integrity: sha512-BkTn+WLf9OPR4KhBnuOki+R/cgk2qwbL49eQn5fEYPeiJD4HPalGbEZDea59UjwNEJ7rqKq2yuPAXQjkDZ6y3w==} + '@opentui/core-darwin-x64@0.8.2': + resolution: {integrity: sha512-AN3QB5yIGQyKs4XKgGuA1vSLA++jonTB6NR2fNLbw4+wizgETlQ77ur30175yEVysaN2tQNO2dJasvHMx05WQg==} + cpu: [x64] + os: [darwin] + + '@opentui/core-linux-arm64-musl@0.8.0': + resolution: {integrity: sha512-8ZPl/2DChcS7XIVRPIazigXaMNyxa2bwvuzPprNbzT0+cVJLIoGC19pjiV3bI3oVXBPVMNqgIcppOrEHBhlouw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@opentui/core-linux-arm64-musl@0.8.2': + resolution: {integrity: sha512-nB/RtlowKo9mlybfL1iHr0Wr4ZF/n4bQ8i7UaphUPNopXSq6YoWT7D4lyjubLBqjJ4KPkaXXHAiReSZ0e1V7ZA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@opentui/core-linux-arm64@0.8.0': + resolution: {integrity: sha512-b6D/VP9y0Z18tO86GSTp9BBykgR6r5CQM1ol9UMJfUt+PjN5HGksbOCDTMIQufXvt4WIUFHrSBProysJRFzbYw==} cpu: [arm64] os: [linux] - '@opentui/core-linux-arm64@0.7.0': - resolution: {integrity: sha512-/u+Kalzkz8NntUW5zjypgt9fnzeKohLG9YMEas7n98KvyaYScrtO4Mdj5iv/STNB3IfocJOChzYAbO6C8TmlPQ==} + '@opentui/core-linux-arm64@0.8.2': + resolution: {integrity: sha512-Inkhg/P5UBNPySqeh0+4K8NbqUIolJ3WNgvX9I25x2nT/DFA8kUN1Y7fbQAaX2CrHa7rWu54xOmJYbYfOPsX8A==} cpu: [arm64] os: [linux] - '@opentui/core-linux-x64-musl@0.7.0': - resolution: {integrity: sha512-HrJbcAIuayzQmWVzv+cjez37fNHL9FDoHmjwxAb4iK5GTjl89FZCxeKopyua14RA6WpP8/zUJQi8U16mm6XptA==} + '@opentui/core-linux-x64-musl@0.8.0': + resolution: {integrity: sha512-xGEYw6wkX0sPwPx6RosQZanuWF0+5cIup2alhHHQQfKJkcLS8r8NBWlcy2LK9CoEWH6SBtNXC+9Y5IrryCOvJQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@opentui/core-linux-x64-musl@0.8.2': + resolution: {integrity: sha512-vicHTwx+jUFNcI39e6pK+hkjAhv33ebqmm0YpHHek9t3tY1PoWY6YfgI4jZVqVzcckJ7i3r3Nnl3x2YB94UUhw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@opentui/core-linux-x64@0.8.0': + resolution: {integrity: sha512-aXBNIdE6iMf58/Tdnd6UzXeFjYfaHzQ6E0nukjLe2ir41/meJJYEAHUQ62EBr/gxvuCNZHTBQB4T9BzF7PdENQ==} cpu: [x64] os: [linux] - '@opentui/core-linux-x64@0.7.0': - resolution: {integrity: sha512-OD2tGl1r27zcgCsuc+TYU5e+qhrbsRSI0YUBEb0ebQo8P6Ps2FL9LFenH9om4fddCh06G9Zd45ekJMhlDrGD3Q==} + '@opentui/core-linux-x64@0.8.2': + resolution: {integrity: sha512-R6SUsvcQdeVuVSdGIOYcAFrCdGGeAxzT2pJ7KT9LK6Tg0pELszpnC+FmRDiofMUz88VSjXP95NygBZx+SAvnuA==} cpu: [x64] os: [linux] - '@opentui/core-win32-arm64@0.7.0': - resolution: {integrity: sha512-8yqBoRsDbvKl2kGX0O8t5kpnVEnbzs+2qUZlJ8sbeA3Z+73WFj36G+clUIMVxpspOu78iKXlD84NIwTxjZUPKw==} + '@opentui/core-win32-arm64@0.8.0': + resolution: {integrity: sha512-XtK0QMyIiqeDmrIyyAXqqp7TKaRGaWnBlbnEsNrkRLrgP+aLwRhRSHdafxi+RMqj3h9qOlb1MgnPal1kx7wP4g==} + cpu: [arm64] + os: [win32] + + '@opentui/core-win32-arm64@0.8.2': + resolution: {integrity: sha512-pGXmxoPcoNMRVm5DGX0+U7kwtm21Ubh+qJ5tz+kVk5LnuBiBhpkWDp2L+OjYmu9D57NrQbKhj0Tvrtu/SzdXaA==} cpu: [arm64] os: [win32] - '@opentui/core-win32-x64@0.7.0': - resolution: {integrity: sha512-OfNAX7fNFHhOiiojUkIjhVOZf+IDjpbpT5G7HrxGqhlZ+n4jITrrJzACHctteWbMUYcibieJzIIoKuLdmD5I+w==} + '@opentui/core-win32-x64@0.8.0': + resolution: {integrity: sha512-P5X2RcDDntIJ4q1WvQawGWCae5BzHrm50PMy7+PZ3Su8GGwnaCQ5xRHvDIQfhki8pZMo2SoTOkpStR0Wj1QoXQ==} + cpu: [x64] + os: [win32] + + '@opentui/core-win32-x64@0.8.2': + resolution: {integrity: sha512-Tc7eNcNSlkHPB1p/9NBqQkexq/vznOPzB6V7G/kGq08y5LtOXPL0zLMJoIMNV/l52gNvv/YEi6mcBLLnpkD9Rg==} cpu: [x64] os: [win32] - '@opentui/core@0.7.0': - resolution: {integrity: sha512-2iWBw9oo+ZJfPbOWeTLXfDV63yTDRfclxltI4IgTa6n7OB90x5xplLMH3y9DXhnWibzTui00GbK3ZCwKfUU1SQ==} + '@opentui/core@0.8.0': + resolution: {integrity: sha512-LwnIT7k+rGelridv7390enJXZpNuQYKIS3NdttJyoLPjbRFPyHnAKkCHk6X8MRS64vcSjOeVq9sl7XLWII16jQ==} + engines: {bun: '>=1.3.14', node: '>=26.4.0'} + peerDependencies: + web-tree-sitter: 0.25.10 + + '@opentui/core@0.8.2': + resolution: {integrity: sha512-Ko6BfyIiCWjm/15nfgBW7aWS+jkZ+Lh16PaEecawWRnhKo7H0BsJGhnpOe/BVcrnBI7bzLgMIP5cWyDffWEP7w==} engines: {bun: '>=1.3.14', node: '>=26.4.0'} peerDependencies: web-tree-sitter: 0.25.10 - '@opentui/solid@0.7.0': - resolution: {integrity: sha512-S6SNZGRykV02UjNbM+f/6lSeAPN3dfEbjcK1jAAdFCfa+EVhJqcJVyaJgtwHLU9fI6c/O1VKXPzbbFYj2RhNVQ==} + '@opentui/solid@0.8.2': + resolution: {integrity: sha512-VfIp83/Qgb040xwNQqeqe4pxDK1bmV4L2QfMPENEggXTkEcN+ZvDpsRhrfQjI0L27JWwDr9o44fXI9Ws/vo2sA==} peerDependencies: - solid-js: 1.9.12 + solid-js: ^1.9.12 '@peculiar/asn1-android@2.6.0': resolution: {integrity: sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ==} @@ -2024,56 +2034,67 @@ packages: resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.54.0': resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.54.0': resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.54.0': resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.54.0': resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.54.0': resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.54.0': resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.54.0': resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.54.0': resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.54.0': resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.54.0': resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.54.0': resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} @@ -2134,6 +2155,7 @@ packages: '@smithy/core@3.35.0': resolution: {integrity: sha512-zRMhfkByhT2snNdr1si24vJitU6Cr9ix2MikUfWmkAgp4jrNP0GcKSP5YvwQ+TlI8AZXER5QOGJn3JsVtSD9/A==} engines: {node: '>=18.0.0'} + deprecated: Deprecated due to a bug in custom logger data redaction. See https://github.com/aws/aws-sdk-js-v3/issues/8393. '@smithy/credential-provider-imds@4.5.2': resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} @@ -2227,24 +2249,28 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.18': resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] + libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.18': resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.18': resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] + libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.18': resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} @@ -2515,8 +2541,8 @@ packages: '@types/web-push@3.6.4': resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} '@typescript-eslint/eslint-plugin@8.51.0': resolution: {integrity: sha512-XtssGWJvypyM2ytBnSnKtHYOGT+4ZwTnBVl36TA4nRO2f4PRNGz5/1OszHzcZCvcBMh+qb7I06uoCmLTRdR9og==} @@ -2738,19 +2764,19 @@ packages: react-native-b4a: optional: true - babel-plugin-jsx-dom-expressions@0.40.7: - resolution: {integrity: sha512-/O6JWUmjv03OI9lL2ry9bUjpD5S3PclM55RRJEyCdcFZ5W2SEA/59d+l2hNsk3gI6kiWRdRPdOtqZmsQzFN1pQ==} + babel-plugin-jsx-dom-expressions@0.40.11: + resolution: {integrity: sha512-k7TkOv7uVYnrq/bYyVvG5cWOC9IObAGfmyW3aMkG8l2sq2qt/5THkUBja69PMLEGGi8G+fXB2Fh7eS4OwZRBoQ==} peerDependencies: '@babel/core': ^7.20.12 babel-plugin-module-resolver@5.0.2: resolution: {integrity: sha512-9KtaCazHee2xc0ibfqsDeamwDps6FZNo5S0Q81dUqEuFzVwPhcT4J5jOqIVvgCA3Q/wO9hKYxN/Ds3tIsp5ygg==} - babel-preset-solid@1.9.12: - resolution: {integrity: sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg==} + babel-preset-solid@1.9.16: + resolution: {integrity: sha512-+0h40Qk2oBfkQ4OGDBHIDtvq/XQiOnCfzjNHmMxriAr8MmdsaIbyJgqYglDs4d70sF8O08sSpx61TKdpUt3oRw==} peerDependencies: '@babel/core': ^7.0.0 - solid-js: ^1.9.12 + solid-js: ^1.9.16 peerDependenciesMeta: solid-js: optional: true @@ -3358,8 +3384,8 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - effect@4.0.1: - resolution: {integrity: sha512-b1VlQG9g8fwxE5QnIZuPoOP/0MsqHJSRKxUijjoM80E5q95FXrX5yWtY5XI8G+GJsBQAVSDbLS458z9a++uVvw==} + effect@4.0.6: + resolution: {integrity: sha512-Tc3mkzuLR6t6P9dhLt0D2GrxYtmU5td4XEJEiXCmILz53IRIfJyHsKZWjTA3xEjiwpNTtXBgI4iqYPxMztBDrw==} electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -4034,24 +4060,28 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [glibc] lightningcss-linux-arm64-musl@1.30.2: resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] + libc: [musl] lightningcss-linux-x64-gnu@1.30.2: resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [glibc] lightningcss-linux-x64-musl@1.30.2: resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] + libc: [musl] lightningcss-win32-arm64-msvc@1.30.2: resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} @@ -4841,24 +4871,14 @@ packages: engines: {node: '>=10'} hasBin: true - seroval-plugins@1.5.4: - resolution: {integrity: sha512-S0xQPhUTefAhNvNWFg0c1J8qJArHt5KdtJ/cFAofo06KD1MVSeFWyl4iiu+ApDIuw0WhjpOfCdgConOfAnLgkw==} - engines: {node: '>=10'} - peerDependencies: - seroval: ^1.0 - - seroval-plugins@1.5.6: - resolution: {integrity: sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ==} + seroval-plugins@1.6.12: + resolution: {integrity: sha512-+reiwp/lYxiyctSrdvX7OQ1YLrcbWYgBiZrhSFL+V43b0/DGRLNVSDyZINbQe+tWplPOI2fw2dPrdv6hcc09kQ==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.5.4: - resolution: {integrity: sha512-46uFvgrXTVxZcUorgSSRZ4y+ieqLLQRMlG4bnCZKW3qI6BZm7Rg4ntMW4p1mILEEBZWrFlcpp0AyIIlM6jD9iw==} - engines: {node: '>=10'} - - seroval@1.5.6: - resolution: {integrity: sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA==} + seroval@1.6.12: + resolution: {integrity: sha512-CqbIVfk/7GL3ITwZI55lcPbpQbjHZ2DD5cUj4iH7E9YsCfHNJV1mx7yNp86Z84w5IpmkZZS/2s5vPOWmVWSdQg==} engines: {node: '>=10'} set-cookie-parser@2.7.2: @@ -4912,11 +4932,8 @@ packages: resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - solid-js@1.9.12: - resolution: {integrity: sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw==} - - solid-js@1.9.15: - resolution: {integrity: sha512-EeiY2xfpZJqPLjXspVEKjAII4yv8NyG//NxZ3IpOFHdUNnnTyL0uJOeS9LWGvA7cFCz5y94cjFwYlmw5Luncsg==} + solid-js@1.9.18: + resolution: {integrity: sha512-CHGO5YFk6Y9m/1YSxVtKnXfrW7JD3Tmof8HmR4z6TmCPsS8efo8LnzIR8CQh3cUvffqYm6DWyBFfk8dQw1CNQQ==} sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} @@ -5781,33 +5798,25 @@ snapshots: '@aws/lambda-invoke-store@0.3.0': {} - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.29.7 - js-tokens: 4.0.0 - picocolors: 1.1.1 - '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.5': {} - '@babel/compat-data@7.29.7': {} '@babel/core@7.28.5': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) + '@babel/helpers': 7.29.10 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.11 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -5827,7 +5836,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -5837,33 +5846,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - jsesc: 3.1.0 - '@babel/generator@7.29.7': dependencies: '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.29.7': dependencies: - '@babel/types': 7.29.7 - - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/types': 7.29.11 '@babel/helper-compilation-targets@7.29.7': dependencies: @@ -5891,22 +5884,22 @@ snapshots: '@babel/helper-member-expression-to-functions@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 '@babel/helper-module-imports@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.29.7 @@ -5926,9 +5919,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.29.7': dependencies: - '@babel/types': 7.29.7 - - '@babel/helper-plugin-utils@7.27.1': {} + '@babel/types': 7.29.11 '@babel/helper-plugin-utils@7.29.7': {} @@ -5944,41 +5935,24 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 transitivePeerDependencies: - supports-color - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-string-parser@7.29.11': {} - '@babel/helper-string-parser@7.29.7': {} - - '@babel/helper-validator-identifier@7.28.5': {} - '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-validator-option@7.29.7': {} - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.29.7 - '@babel/types': 7.29.7 - '@babel/helpers@7.29.10': dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.11 - '@babel/parser@7.28.5': - dependencies: - '@babel/types': 7.28.5 - '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: @@ -6001,12 +5975,12 @@ snapshots: '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': dependencies: @@ -6023,7 +5997,7 @@ snapshots: dependencies: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/helper-validator-option': 7.27.1 + '@babel/helper-validator-option': 7.29.7 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-modules-commonjs': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) @@ -6034,29 +6008,11 @@ snapshots: '@babel/runtime@7.29.10': {} - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 - '@babel/helper-globals': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/template': 7.29.7 - '@babel/types': 7.29.7 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color + '@babel/types': 7.29.11 '@babel/traverse@7.29.7': dependencies: @@ -6065,26 +6021,16 @@ snapshots: '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.5': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/types@7.29.11': dependencies: '@babel/helper-string-parser': 7.29.11 '@babel/helper-validator-identifier': 7.29.7 - '@babel/types@7.29.7': - dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@bcoe/v8-coverage@1.0.2': {} '@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5)': @@ -6122,14 +6068,14 @@ snapshots: '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5) '@better-auth/utils': 0.5.0 - '@better-auth/passkey@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5)': + '@better-auth/passkey@1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4))(better-call@1.4.4(zod@4.3.2))(nanostores@1.5.5)': dependencies: '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5) '@better-auth/utils': 0.5.0 '@better-fetch/fetch': 1.3.2 '@simplewebauthn/browser': 13.3.0 '@simplewebauthn/server': 13.3.3 - better-auth: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4) + better-auth: 1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4) better-call: 1.4.4(zod@4.3.2) nanostores: 1.5.5 zod: 4.6.7 @@ -6217,10 +6163,10 @@ snapshots: react: 19.2.3 tslib: 2.8.1 - '@effect/opentelemetry@4.0.1(@opentelemetry/api-logs@0.219.0)(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.43.0)(effect@4.0.1)': + '@effect/opentelemetry@4.0.6(@opentelemetry/api-logs@0.219.0)(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.43.0)(effect@4.0.6)': dependencies: '@opentelemetry/semantic-conventions': 1.43.0 - effect: 4.0.1 + effect: 4.0.6 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.219.0 @@ -6230,19 +6176,19 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.8.0(@opentelemetry/api@1.9.0) - '@effect/platform-node-shared@4.0.1(effect@4.0.1)': + '@effect/platform-node-shared@4.0.6(effect@4.0.6)': dependencies: - '@types/ws': 8.18.1 - effect: 4.0.1 + '@types/ws': 8.18.2 + effect: 4.0.6 ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.0))': + '@effect/platform-node@4.0.6(effect@4.0.6)(redis@6.2.1(@opentelemetry/api@1.9.0))': dependencies: - '@effect/platform-node-shared': 4.0.1(effect@4.0.1) - effect: 4.0.1 + '@effect/platform-node-shared': 4.0.6(effect@4.0.6) + effect: 4.0.6 redis: 6.2.1(@opentelemetry/api@1.9.0) undici: 8.12.2 transitivePeerDependencies: @@ -6615,48 +6561,40 @@ snapshots: node-gyp: 12.4.0 proc-log: 6.1.0 - '@opencode/ai@2.2.1': + '@opencode/ai@2.2.5': dependencies: '@aws-sdk/credential-providers': 3.1057.0 - '@opencode/schema': 2.2.1 + '@opencode/schema': 2.2.5 '@smithy/eventstream-codec': 4.2.14 '@smithy/util-utf8': 4.2.2 aws4fetch: 1.0.20 - effect: 4.0.1 + effect: 4.0.6 google-auth-library: 10.5.0 transitivePeerDependencies: - supports-color - '@opencode/client@2.2.1(effect@4.0.1)(solid-js@1.9.12)': - dependencies: - '@opencode/protocol': 2.2.1 - '@opencode/schema': 2.2.1 - optionalDependencies: - effect: 4.0.1 - solid-js: 1.9.12 - - '@opencode/client@2.2.1(effect@4.0.1)(solid-js@1.9.15)': + '@opencode/client@2.2.5(effect@4.0.6)(solid-js@1.9.18)': dependencies: - '@opencode/protocol': 2.2.1 - '@opencode/schema': 2.2.1 + '@opencode/protocol': 2.2.5 + '@opencode/schema': 2.2.5 optionalDependencies: - effect: 4.0.1 - solid-js: 1.9.15 + effect: 4.0.6 + solid-js: 1.9.18 - '@opencode/plugin@2.2.1(@opencode/theme@2.2.1(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentelemetry/semantic-conventions@1.43.0)(@opentui/core@0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentui/solid@0.7.0(solid-js@1.9.12)(typescript@5.9.3)(web-tree-sitter@0.25.10))(redis@6.2.1(@opentelemetry/api@1.9.0))(solid-js@1.9.12)(vite@7.3.0(@types/node@22.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0))': + '@opencode/plugin@2.2.5(@opencode/theme@2.2.5(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentelemetry/semantic-conventions@1.43.0)(@opentui/core@0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10))(@opentui/solid@0.8.2(solid-js@1.9.18)(typescript@5.9.3)(web-tree-sitter@0.25.10))(redis@6.2.1(@opentelemetry/api@1.9.0))(solid-js@1.9.18)(vite@7.3.0(@types/node@22.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0))': dependencies: '@ai-sdk/provider': 3.0.8 - '@opencode/ai': 2.2.1 - '@opencode/client': 2.2.1(effect@4.0.1)(solid-js@1.9.12) - '@opencode/protocol': 2.2.1 - '@opencode/schema': 2.2.1 - '@opencode/util': 2.2.1(@opentelemetry/semantic-conventions@1.43.0)(redis@6.2.1(@opentelemetry/api@1.9.0)) - effect: 4.0.1 + '@opencode/ai': 2.2.5 + '@opencode/client': 2.2.5(effect@4.0.6)(solid-js@1.9.18) + '@opencode/protocol': 2.2.5 + '@opencode/schema': 2.2.5 + '@opencode/util': 2.2.5(@opentelemetry/semantic-conventions@1.43.0)(redis@6.2.1(@opentelemetry/api@1.9.0)) + effect: 4.0.6 optionalDependencies: - '@opencode/theme': 2.2.1(typescript@5.9.3)(web-tree-sitter@0.25.10) - '@opentui/core': 0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10) - '@opentui/solid': 0.7.0(solid-js@1.9.12)(typescript@5.9.3)(web-tree-sitter@0.25.10) - solid-js: 1.9.12 + '@opencode/theme': 2.2.5(typescript@5.9.3)(web-tree-sitter@0.25.10) + '@opentui/core': 0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10) + '@opentui/solid': 0.8.2(solid-js@1.9.18)(typescript@5.9.3)(web-tree-sitter@0.25.10) + solid-js: 1.9.18 vite: 7.3.0(@types/node@22.19.19)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0) transitivePeerDependencies: - '@opentelemetry/sdk-trace-web' @@ -6666,29 +6604,29 @@ snapshots: - supports-color - utf-8-validate - '@opencode/protocol@2.2.1': + '@opencode/protocol@2.2.5': dependencies: - '@opencode/schema': 2.2.1 - effect: 4.0.1 + '@opencode/schema': 2.2.5 + effect: 4.0.6 - '@opencode/schema@2.2.1': + '@opencode/schema@2.2.5': dependencies: '@standard-schema/spec': 1.1.0 - effect: 4.0.1 + effect: 4.0.6 - '@opencode/theme@2.2.1(typescript@5.9.3)(web-tree-sitter@0.25.10)': + '@opencode/theme@2.2.5(typescript@5.9.3)(web-tree-sitter@0.25.10)': dependencies: - '@opentui/core': 0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10) - effect: 4.0.1 + '@opentui/core': 0.8.0(typescript@5.9.3)(web-tree-sitter@0.25.10) + effect: 4.0.6 transitivePeerDependencies: - typescript - web-tree-sitter - '@opencode/util@2.2.1(@opentelemetry/semantic-conventions@1.43.0)(redis@6.2.1(@opentelemetry/api@1.9.0))': + '@opencode/util@2.2.5(@opentelemetry/semantic-conventions@1.43.0)(redis@6.2.1(@opentelemetry/api@1.9.0))': dependencies: - '@effect/opentelemetry': 4.0.1(@opentelemetry/api-logs@0.219.0)(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.43.0)(effect@4.0.1) - '@effect/platform-node': 4.0.1(effect@4.0.1)(redis@6.2.1(@opentelemetry/api@1.9.0)) - '@effect/platform-node-shared': 4.0.1(effect@4.0.1) + '@effect/opentelemetry': 4.0.6(@opentelemetry/api-logs@0.219.0)(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.8.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.43.0)(effect@4.0.6) + '@effect/platform-node': 4.0.6(effect@4.0.6)(redis@6.2.1(@opentelemetry/api@1.9.0)) + '@effect/platform-node-shared': 4.0.6(effect@4.0.6) '@npmcli/arborist': 9.4.0 '@npmcli/config': 10.8.1 '@opentelemetry/api': 1.9.0 @@ -6701,7 +6639,7 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.8.0(@opentelemetry/api@1.9.0) cross-spawn: 7.0.6 - effect: 4.0.1 + effect: 4.0.6 glob: 13.0.5 mime-types: 3.0.2 minimatch: 10.2.5 @@ -6792,31 +6730,55 @@ snapshots: '@opentelemetry/semantic-conventions@1.43.0': {} - '@opentui/core-darwin-arm64@0.7.0': + '@opentui/core-darwin-arm64@0.8.0': + optional: true + + '@opentui/core-darwin-arm64@0.8.2': + optional: true + + '@opentui/core-darwin-x64@0.8.0': + optional: true + + '@opentui/core-darwin-x64@0.8.2': + optional: true + + '@opentui/core-linux-arm64-musl@0.8.0': optional: true - '@opentui/core-darwin-x64@0.7.0': + '@opentui/core-linux-arm64-musl@0.8.2': optional: true - '@opentui/core-linux-arm64-musl@0.7.0': + '@opentui/core-linux-arm64@0.8.0': optional: true - '@opentui/core-linux-arm64@0.7.0': + '@opentui/core-linux-arm64@0.8.2': optional: true - '@opentui/core-linux-x64-musl@0.7.0': + '@opentui/core-linux-x64-musl@0.8.0': optional: true - '@opentui/core-linux-x64@0.7.0': + '@opentui/core-linux-x64-musl@0.8.2': optional: true - '@opentui/core-win32-arm64@0.7.0': + '@opentui/core-linux-x64@0.8.0': optional: true - '@opentui/core-win32-x64@0.7.0': + '@opentui/core-linux-x64@0.8.2': optional: true - '@opentui/core@0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10)': + '@opentui/core-win32-arm64@0.8.0': + optional: true + + '@opentui/core-win32-arm64@0.8.2': + optional: true + + '@opentui/core-win32-x64@0.8.0': + optional: true + + '@opentui/core-win32-x64@0.8.2': + optional: true + + '@opentui/core@0.8.0(typescript@5.9.3)(web-tree-sitter@0.25.10)': dependencies: bun-ffi-structs: 0.3.1(typescript@5.9.3) diff: 9.0.0 @@ -6824,27 +6786,46 @@ snapshots: string-width: 7.2.0 web-tree-sitter: 0.25.10 optionalDependencies: - '@opentui/core-darwin-arm64': 0.7.0 - '@opentui/core-darwin-x64': 0.7.0 - '@opentui/core-linux-arm64': 0.7.0 - '@opentui/core-linux-arm64-musl': 0.7.0 - '@opentui/core-linux-x64': 0.7.0 - '@opentui/core-linux-x64-musl': 0.7.0 - '@opentui/core-win32-arm64': 0.7.0 - '@opentui/core-win32-x64': 0.7.0 + '@opentui/core-darwin-arm64': 0.8.0 + '@opentui/core-darwin-x64': 0.8.0 + '@opentui/core-linux-arm64': 0.8.0 + '@opentui/core-linux-arm64-musl': 0.8.0 + '@opentui/core-linux-x64': 0.8.0 + '@opentui/core-linux-x64-musl': 0.8.0 + '@opentui/core-win32-arm64': 0.8.0 + '@opentui/core-win32-x64': 0.8.0 transitivePeerDependencies: - typescript - '@opentui/solid@0.7.0(solid-js@1.9.12)(typescript@5.9.3)(web-tree-sitter@0.25.10)': + '@opentui/core@0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10)': + dependencies: + bun-ffi-structs: 0.3.1(typescript@5.9.3) + diff: 9.0.0 + marked: 17.0.1 + string-width: 7.2.0 + web-tree-sitter: 0.25.10 + optionalDependencies: + '@opentui/core-darwin-arm64': 0.8.2 + '@opentui/core-darwin-x64': 0.8.2 + '@opentui/core-linux-arm64': 0.8.2 + '@opentui/core-linux-arm64-musl': 0.8.2 + '@opentui/core-linux-x64': 0.8.2 + '@opentui/core-linux-x64-musl': 0.8.2 + '@opentui/core-win32-arm64': 0.8.2 + '@opentui/core-win32-x64': 0.8.2 + transitivePeerDependencies: + - typescript + + '@opentui/solid@0.8.2(solid-js@1.9.18)(typescript@5.9.3)(web-tree-sitter@0.25.10)': dependencies: '@babel/core': 7.29.7 '@babel/preset-typescript': 7.27.1(@babel/core@7.29.7) - '@opentui/core': 0.7.0(typescript@5.9.3)(web-tree-sitter@0.25.10) + '@opentui/core': 0.8.2(typescript@5.9.3)(web-tree-sitter@0.25.10) babel-plugin-module-resolver: 5.0.2 - babel-preset-solid: 1.9.12(@babel/core@7.29.7)(solid-js@1.9.12) + babel-preset-solid: 1.9.16(@babel/core@7.29.7)(solid-js@1.9.18) entities: 7.0.1 s-js: 0.4.9 - solid-js: 1.9.12 + solid-js: 1.9.18 transitivePeerDependencies: - supports-color - typescript @@ -7725,24 +7706,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.11 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.11 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.11 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.11 '@types/better-sqlite3@7.6.13': dependencies: @@ -7935,7 +7916,7 @@ snapshots: dependencies: '@types/node': 24.10.4 - '@types/ws@8.18.1': + '@types/ws@8.18.2': dependencies: '@types/node': 24.10.4 @@ -8228,12 +8209,12 @@ snapshots: b4a@1.7.3: {} - babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7): + babel-plugin-jsx-dom-expressions@0.40.11(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.18.6 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 html-entities: 2.3.3 parse5: 7.3.0 @@ -8245,12 +8226,12 @@ snapshots: reselect: 4.1.8 resolve: 1.22.12 - babel-preset-solid@1.9.12(@babel/core@7.29.7)(solid-js@1.9.12): + babel-preset-solid@1.9.16(@babel/core@7.29.7)(solid-js@1.9.18): dependencies: '@babel/core': 7.29.7 - babel-plugin-jsx-dom-expressions: 0.40.7(@babel/core@7.29.7) + babel-plugin-jsx-dom-expressions: 0.40.11(@babel/core@7.29.7) optionalDependencies: - solid-js: 1.9.12 + solid-js: 1.9.18 bail@2.0.2: {} @@ -8264,7 +8245,7 @@ snapshots: baseline-browser-mapping@2.9.11: {} - better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.15)(vitest@3.2.4): + better-auth@1.7.12(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(solid-js@1.9.18)(vitest@3.2.4): dependencies: '@better-auth/core': 1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5) '@better-auth/drizzle-adapter': 1.7.12(@better-auth/core@1.7.12(@better-auth/utils@0.5.0)(@better-fetch/fetch@1.3.2)(@opentelemetry/api@1.9.0)(better-call@1.4.4(zod@4.3.2))(jose@6.2.13)(kysely@0.29.7)(nanostores@1.5.5))(@better-auth/utils@0.5.0) @@ -8286,7 +8267,7 @@ snapshots: optionalDependencies: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - solid-js: 1.9.15 + solid-js: 1.9.18 vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.30.2)(terser@5.46.0)(yaml@2.9.0) transitivePeerDependencies: - '@opentelemetry/api' @@ -8826,7 +8807,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - effect@4.0.1: {} + effect@4.0.6: {} electron-to-chromium@1.5.267: {} @@ -9614,7 +9595,7 @@ snapshots: magicast@0.3.5: dependencies: '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 + '@babel/types': 7.29.11 source-map-js: 1.2.1 make-dir@4.0.0: @@ -10659,19 +10640,11 @@ snapshots: semver@7.7.3: {} - seroval-plugins@1.5.4(seroval@1.5.4): - dependencies: - seroval: 1.5.4 - - seroval-plugins@1.5.6(seroval@1.5.6): + seroval-plugins@1.6.12(seroval@1.6.12): dependencies: - seroval: 1.5.6 - optional: true + seroval: 1.6.12 - seroval@1.5.4: {} - - seroval@1.5.6: - optional: true + seroval@1.6.12: {} set-cookie-parser@2.7.2: {} @@ -10729,18 +10702,11 @@ snapshots: ip-address: 10.7.2 smart-buffer: 4.2.0 - solid-js@1.9.12: + solid-js@1.9.18: dependencies: csstype: 3.2.3 - seroval: 1.5.4 - seroval-plugins: 1.5.4(seroval@1.5.4) - - solid-js@1.9.15: - dependencies: - csstype: 3.2.3 - seroval: 1.5.6 - seroval-plugins: 1.5.6(seroval@1.5.6) - optional: true + seroval: 1.6.12 + seroval-plugins: 1.6.12(seroval@1.6.12) sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3a3dcf14e..20b813a96 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,16 @@ packages: - ocm-cli - '!workspace/**' +catalog: + '@opencode/client': 2.2.5 + '@opencode/plugin': 2.2.5 + '@opencode/schema': 2.2.5 + '@opencode/theme': 2.2.5 + '@opentui/core': 0.8.2 + '@opentui/solid': 0.8.2 + effect: 4.0.6 + solid-js: 1.9.18 + onlyBuiltDependencies: - better-sqlite3 - esbuild diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh index ef8976f20..b1ff39349 100644 --- a/scripts/docker-entrypoint.sh +++ b/scripts/docker-entrypoint.sh @@ -60,7 +60,7 @@ ensure_auth_secret() { export AUTH_SECRET } -OPENCODE_SUPPORTED_FLOOR="${OPENCODE_BUNDLED_VERSION:-}" +OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-}" read_opencode_version() { local binary output @@ -93,7 +93,7 @@ reconcile_persisted_opencode() { return 0 fi if ! is_supported_opencode_version "$persisted_version"; then - echo "Persisted OpenCode $persisted_version is outside the supported range $(supported_opencode_range); removing it so the bundled OpenCode $OPENCODE_SUPPORTED_FLOOR binary is used" + echo "Persisted OpenCode $persisted_version is outside the supported range $(supported_opencode_range); removing it so the bundled OpenCode ${OPENCODE_BUNDLED_VERSION:-} binary is used" rm -f "$persisted_path" return 0 fi diff --git a/scripts/setup-dev.sh b/scripts/setup-dev.sh index 0a1c9fd0a..c71a45225 100644 --- a/scripts/setup-dev.sh +++ b/scripts/setup-dev.sh @@ -38,7 +38,7 @@ fi REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" source "$REPO_ROOT/scripts/lib/opencode-release.sh" -OPENCODE_SUPPORTED_FLOOR="$(sed -n 's/^ARG OPENCODE_VERSION=//p' "$REPO_ROOT/Dockerfile" | head -1)" +OPENCODE_SUPPORTED_FLOOR="$(sed -n 's/^ARG OPENCODE_MIN_VERSION=//p' "$REPO_ROOT/Dockerfile" | head -1)" OPENCODE_VERSION_OUTPUT="$(opencode --version 2>&1)" || OPENCODE_VERSION_OUTPUT="" OPENCODE_VERSION="$(parse_opencode_version_output "$OPENCODE_VERSION_OUTPUT")" diff --git a/shared/package.json b/shared/package.json index fbf2b8130..23e1b9a47 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-manager/shared", - "version": "1.1.0", + "version": "1.2.0", "description": "Shared schemas, types, OpenCode helpers and Manager API contracts for OpenCode Manager clients.", "license": "MIT", "repository": { @@ -34,9 +34,9 @@ "./themes": "./src/themes/index.ts" }, "dependencies": { - "@opencode/client": "2.2.1", - "@opencode/schema": "2.2.1", - "effect": "4.0.1", + "@opencode/client": "catalog:", + "@opencode/schema": "catalog:", + "effect": "catalog:", "jsonc-parser": "^3.3.1", "zod": "^4.1.12" }, diff --git a/shared/src/opencode/index.ts b/shared/src/opencode/index.ts index 8298fb758..1893d31ee 100644 --- a/shared/src/opencode/index.ts +++ b/shared/src/opencode/index.ts @@ -136,6 +136,7 @@ export type { } from './pairing' export { + OPENCODE_MIN_VERSION, OPENCODE_PINNED_VERSION, OPENCODE_SUPPORTED_VERSION_RANGE, buildOpenCodeReleaseAsset, diff --git a/shared/src/opencode/release.ts b/shared/src/opencode/release.ts index 97aefb6fc..0be9431d4 100644 --- a/shared/src/opencode/release.ts +++ b/shared/src/opencode/release.ts @@ -1,6 +1,10 @@ import { compareParsedSemver, compareSemver, normalizeSemver, parseSemver, type Semver } from '../utils/semver' -export const OPENCODE_PINNED_VERSION = '2.2.1' +/** The OpenCode release Manager is built against and bundles; matches the `@opencode/*` pnpm catalog entries. */ +export const OPENCODE_PINNED_VERSION = '2.2.5' + +/** The oldest OpenCode release Manager supports; any later release of the same major is accepted. */ +export const OPENCODE_MIN_VERSION = '2.2.5' const OPENCODE_RELEASE_BASE_URL = 'https://opencode.ai/files/bin' @@ -47,20 +51,20 @@ export function compareOpenCodeVersions(left: string, right: string): number { return compareSemver(left, right) } -function parsePinnedOpenCodeVersion(): OpenCodeVersion { - const pinned = parseOpenCodeVersion(OPENCODE_PINNED_VERSION) - if (!pinned) throw new Error(`OPENCODE_PINNED_VERSION is not a valid version: ${OPENCODE_PINNED_VERSION}`) - return pinned +function parseMinOpenCodeVersion(): OpenCodeVersion { + const min = parseOpenCodeVersion(OPENCODE_MIN_VERSION) + if (!min) throw new Error(`OPENCODE_MIN_VERSION is not a valid version: ${OPENCODE_MIN_VERSION}`) + return min } -const OPENCODE_PINNED = parsePinnedOpenCodeVersion() +const OPENCODE_MIN = parseMinOpenCodeVersion() -export const OPENCODE_SUPPORTED_VERSION_RANGE = `>=${OPENCODE_PINNED_VERSION} <${OPENCODE_PINNED.major + 1}.0.0` +export const OPENCODE_SUPPORTED_VERSION_RANGE = `>=${OPENCODE_MIN_VERSION} <${OPENCODE_MIN.major + 1}.0.0` export function isSupportedOpenCodeVersion(version: string): boolean { const parsed = parseOpenCodeVersion(version) if (!parsed) return false - return parsed.major === OPENCODE_PINNED.major && compareParsedSemver(parsed, OPENCODE_PINNED) >= 0 + return parsed.major === OPENCODE_MIN.major && compareParsedSemver(parsed, OPENCODE_MIN) >= 0 } export function describeUnsupportedOpenCodeVersion(version: string): string { From bf7c6cdbac7eba27ec98dfff6123ad4e14eba291 Mon Sep 17 00:00:00 2001 From: Chris Scott <99081550+chriswritescode-dev@users.noreply.github.com> Date: Fri, 6 Nov 2026 09:43:35 -0500 Subject: [PATCH 2/2] fix(opencode): address review feedback on the 2.2.5 pin Document ocm 0.6.x as requiring OpenCode 2.2.5, matching its @opencode/theme peer, and make the package compatibility test check bounded peer ranges instead of treating them as one version. Linked servers on another 2.x release keep the neutral version notice, while a different major version is shown as unsupported again through a shared version badge. The Docker build now fails when the bundled OpenCode is below the supported minimum, and the entrypoint falls back to the bundled version as the floor when OPENCODE_MIN_VERSION is unset instead of discarding every persisted binary. --- Dockerfile | 2 ++ .../test/scripts/docker-entrypoint.test.ts | 14 +++++++++++++- backend/test/shared/opencode-contract.test.ts | 12 +++++++++++- .../settings/OpenCodeServerDialog.tsx | 16 +++++++++++----- .../settings/OpenCodeServersSettings.tsx | 8 ++------ ocm-cli/README.md | 8 ++++---- ocm-cli/test/package-compat.test.ts | 19 ++++++++++++++----- scripts/docker-entrypoint.sh | 2 +- shared/src/opencode/index.ts | 1 + shared/src/opencode/release.ts | 5 +++++ 10 files changed, 64 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index ab217a4cb..d1e5aae10 100644 --- a/Dockerfile +++ b/Dockerfile @@ -80,6 +80,8 @@ RUN echo "Installing uv=${UV_VERSION} opencode=${OPENCODE_VERSION} (cachebust=${ chmod +x /usr/local/bin/uv /usr/local/bin/uvx && \ test "$(uv --version | cut -d' ' -f2)" = "${UV_VERSION}" && \ . /usr/local/lib/ocm/opencode-release.sh && \ + OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION}" && \ + { is_supported_opencode_version "${OPENCODE_VERSION}" || { echo "OPENCODE_VERSION ${OPENCODE_VERSION} is outside the supported range $(supported_opencode_range)" >&2; exit 1; }; } && \ echo "Downloading opencode ${OPENCODE_VERSION}..." && \ download_opencode_to "${OPENCODE_VERSION}" /opt/opencode/bin/opencode && \ ln -s /opt/opencode/bin/opencode /usr/local/bin/opencode && \ diff --git a/backend/test/scripts/docker-entrypoint.test.ts b/backend/test/scripts/docker-entrypoint.test.ts index cad3ae0f1..9c778dd73 100644 --- a/backend/test/scripts/docker-entrypoint.test.ts +++ b/backend/test/scripts/docker-entrypoint.test.ts @@ -460,16 +460,28 @@ exit 1`) expect(curlLog()).toHaveLength(0) }) - it('treats every persisted binary as unsupported when the minimum version is unknown', () => { + it('treats every persisted binary as unsupported when neither the minimum nor the bundled version is known', () => { mkdirSync(join(stubDir, 'home/.opencode/bin'), { recursive: true }) writeBinary(join(stubDir, 'home/.opencode/bin'), '2.0.15') const res = runOpenCodeSection(`${installPrelude()}\nreconcile_persisted_opencode`, { OPENCODE_MIN_VERSION: '', + OPENCODE_BUNDLED_VERSION: '', }) expect(res.status).toBe(0) expect(existsSync(homeBinPath())).toBe(false) }) + it('falls back to the bundled version as the floor when the minimum is unset', () => { + mkdirSync(join(stubDir, 'home/.opencode/bin'), { recursive: true }) + writeBinary(join(stubDir, 'home/.opencode/bin'), '2.0.15') + const res = runOpenCodeSection(`${installPrelude()}\nreconcile_persisted_opencode`, { + OPENCODE_MIN_VERSION: '', + }) + expect(res.status).toBe(0) + expect(res.stdout).toContain('Persisted OpenCode 2.0.15 is usable; retaining it') + expect(existsSync(homeBinPath())).toBe(true) + }) + it('removes a malformed persisted home binary and selects the bundled binary without downloading', () => { mkdirSync(join(stubDir, 'home/.opencode/bin'), { recursive: true }) writeBinary(join(stubDir, 'home/.opencode/bin'), 'garbage') diff --git a/backend/test/shared/opencode-contract.test.ts b/backend/test/shared/opencode-contract.test.ts index 0b00dd666..9e6130e09 100644 --- a/backend/test/shared/opencode-contract.test.ts +++ b/backend/test/shared/opencode-contract.test.ts @@ -11,6 +11,7 @@ import { compareOpenCodeVersions, createOpenCodeApi, describeUnsupportedOpenCodeVersion, + isSameOpenCodeMajor, isStableOpenCodeVersion, isSupportedOpenCodeVersion, normalizeOpenCodeVersion, @@ -104,6 +105,15 @@ describe('OpenCode v2 contract', () => { expect(isSupportedOpenCodeVersion('')).toBe(false) }) + it('treats any release of the same major as reachable, regardless of the floor', () => { + expect(isSameOpenCodeMajor('2.0.15')).toBe(true) + expect(isSameOpenCodeMajor('2.2.4')).toBe(true) + expect(isSameOpenCodeMajor('2.9.0')).toBe(true) + expect(isSameOpenCodeMajor('1.18.32')).toBe(false) + expect(isSameOpenCodeMajor('3.0.0')).toBe(false) + expect(isSameOpenCodeMajor('not-a-version')).toBe(false) + }) + it('describes the supported range for unsupported versions', () => { expect(describeUnsupportedOpenCodeVersion('3.0.0')).toBe( 'OpenCode 3.0.0 is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0', @@ -174,7 +184,7 @@ describe('OpenCode v2 contract', () => { const entrypoint = readFileSync(join(REPO_ROOT, 'scripts', 'docker-entrypoint.sh'), 'utf8') const setupDev = readFileSync(join(REPO_ROOT, 'scripts', 'setup-dev.sh'), 'utf8') const releaseHelper = readFileSync(join(REPO_ROOT, 'scripts', 'lib', 'opencode-release.sh'), 'utf8') - expect(entrypoint).toContain('OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-}"') + expect(entrypoint).toContain('OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-${OPENCODE_BUNDLED_VERSION:-}}"') expect(setupDev).toContain("sed -n 's/^ARG OPENCODE_MIN_VERSION=//p' \"$REPO_ROOT/Dockerfile\"") for (const script of [entrypoint, setupDev, releaseHelper]) { expect(script).not.toMatch(/(?Supported + if (result.version && isSameOpenCodeMajor(result.version)) { + return Different version: some features may not work + } + return Unsupported version +} + function TestResultSummary({ result }: { result: OpenCodeServerTestResult }) { if (!result.ok) { return ( @@ -112,11 +122,7 @@ function TestResultSummary({ result }: { result: OpenCodeServerTestResult }) {
Connected — OpenCode {result.version ?? 'unknown version'} - {result.versionSupported ? ( - Supported - ) : ( - Different version: some features may not work - )} +
) } diff --git a/frontend/src/components/settings/OpenCodeServersSettings.tsx b/frontend/src/components/settings/OpenCodeServersSettings.tsx index 3b6e081fd..ca95f0437 100644 --- a/frontend/src/components/settings/OpenCodeServersSettings.tsx +++ b/frontend/src/components/settings/OpenCodeServersSettings.tsx @@ -12,7 +12,7 @@ import { Switch } from '@/components/ui/switch' import { DeleteDialog } from '@/components/ui/delete-dialog' import { showToast } from '@/lib/toast' import { useOpenCodeServers } from '@/hooks/useOpenCodeServers' -import { OpenCodeServerDialog, type OpenCodeServerFormValues } from './OpenCodeServerDialog' +import { OpenCodeServerDialog, OpenCodeVersionBadge, type OpenCodeServerFormValues } from './OpenCodeServerDialog' function errorMessage(error: unknown, fallback: string): string { return error instanceof Error && error.message ? error.message : fallback @@ -31,11 +31,7 @@ function ServerTestResult({ result }: { result: OpenCodeServerTestResult }) {
OpenCode {result.version ?? 'unknown version'} - {result.versionSupported ? ( - Supported - ) : ( - Different version: some features may not work - )} +
) } diff --git a/ocm-cli/README.md b/ocm-cli/README.md index a9d75128f..acb560265 100644 --- a/ocm-cli/README.md +++ b/ocm-cli/README.md @@ -10,7 +10,7 @@ to the local working tree. | ocm | OpenCode Manager | Local OpenCode | |---|---|---| -| 0.6.x | >= 0.19.0 | >= 2.2.3, < 3 | +| 0.6.x | >= 0.19.0 | >= 2.2.5, < 3 | | 0.5.x | >= 0.19.0 | >= 2.2.0, < 3 | | 0.4.x | >= 0.19.0 | >= 2.0.15, < 3 | | 0.3.x | >= 0.19.0 | >= 2.0.15, < 3 | @@ -26,8 +26,8 @@ Use ocm 0.2.x with OpenCode Manager < 0.19.0 and OpenCode 1.x, installed pinned release. From ocm 0.6.0, the TUI plugin is built against `@opencode/plugin` 2.2.5 and -OpenTUI 0.8, so it requires a local OpenCode >= 2.2.3. OpenCode 2.2.0 through -2.2.2 ship OpenTUI 0.7; stay on ocm 0.5.x with those builds. ocm 0.5.0 itself +OpenTUI 0.8 and declares an `@opencode/theme` peer of `>=2.2.5`, so it requires +a local OpenCode >= 2.2.5. Stay on ocm 0.5.x with an older 2.2.x. ocm 0.5.0 itself is built against `@opencode/plugin` 2.2.1 and OpenTUI 0.7 and requires a local OpenCode >= 2.2.0: OpenCode 2.2.0 refuses plugins built for OpenTUI 0.6, and older OpenCode builds may refuse the 0.7 peers. Stay on ocm 0.4.x with a local @@ -324,7 +324,7 @@ global installs); the plugin surface is TUI-only. ## Requirements - macOS or Linux (Windows is unsupported) -- OpenCode >= 2.2.3 (same major, 2.x) available on `PATH` for the TUI plugin +- OpenCode >= 2.2.5 (same major, 2.x) available on `PATH` for the TUI plugin - Node available on `PATH` (runs the `ocm` binary) - OpenCode Manager >= 0.19.0 - `git` and `tar` (with gzip support, i.e. the `-z` flag) available on `PATH` diff --git a/ocm-cli/test/package-compat.test.ts b/ocm-cli/test/package-compat.test.ts index e45c82eb3..e02d1771b 100644 --- a/ocm-cli/test/package-compat.test.ts +++ b/ocm-cli/test/package-compat.test.ts @@ -45,15 +45,24 @@ function deriveMinorLineRange(version: string): string { } /** - * Asserts that an installed version satisfies a plugin peer declaration, - * accepting both an exact peer pin and an open `>=` floor. + * Asserts that an installed version satisfies a plugin peer declaration: an exact + * pin, or space-separated `>=`, `<=` and `<` bounds such as `>=0.8.0 <0.9.0`. */ function expectVersionSatisfiesPeer(version: string, peer: string): void { - if (peer.startsWith('>=')) { - expect(compareSemver(version, peer.slice(2).trim())).toBeGreaterThanOrEqual(0) + const comparators = peer.trim().split(/\s+/) + if (!comparators.every((comparator) => comparator.startsWith('>=') || comparator.startsWith('<'))) { + expect(version).toBe(peer) return } - expect(version).toBe(peer) + for (const comparator of comparators) { + if (comparator.startsWith('>=')) { + expect(compareSemver(version, comparator.slice(2))).toBeGreaterThanOrEqual(0) + } else if (comparator.startsWith('<=')) { + expect(compareSemver(version, comparator.slice(2))).toBeLessThanOrEqual(0) + } else { + expect(compareSemver(version, comparator.slice(1))).toBeLessThan(0) + } + } } const ocmManifest = readManifest(join(__dirname, '..', 'package.json')) diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh index b1ff39349..3d20d77e1 100644 --- a/scripts/docker-entrypoint.sh +++ b/scripts/docker-entrypoint.sh @@ -60,7 +60,7 @@ ensure_auth_secret() { export AUTH_SECRET } -OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-}" +OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-${OPENCODE_BUNDLED_VERSION:-}}" read_opencode_version() { local binary output diff --git a/shared/src/opencode/index.ts b/shared/src/opencode/index.ts index 1893d31ee..95b6a7bbc 100644 --- a/shared/src/opencode/index.ts +++ b/shared/src/opencode/index.ts @@ -143,6 +143,7 @@ export { compareOpenCodeVersions, describeUnsupportedOpenCodeVersion, isStableOpenCodeVersion, + isSameOpenCodeMajor, isSupportedOpenCodeVersion, normalizeOpenCodeVersion, parseOpenCodeVersion, diff --git a/shared/src/opencode/release.ts b/shared/src/opencode/release.ts index 0be9431d4..994668db9 100644 --- a/shared/src/opencode/release.ts +++ b/shared/src/opencode/release.ts @@ -67,6 +67,11 @@ export function isSupportedOpenCodeVersion(version: string): boolean { return parsed.major === OPENCODE_MIN.major && compareParsedSemver(parsed, OPENCODE_MIN) >= 0 } +/** Reports whether a server version shares Manager's OpenCode major, so the V2 client can still talk to it. */ +export function isSameOpenCodeMajor(version: string): boolean { + return parseOpenCodeVersion(version)?.major === OPENCODE_MIN.major +} + export function describeUnsupportedOpenCodeVersion(version: string): string { return `OpenCode ${version} is not supported; OpenCode Manager requires OpenCode ${OPENCODE_SUPPORTED_VERSION_RANGE}` }