diff --git a/Dockerfile b/Dockerfile index f326643ff..d1e5aae10 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 @@ -79,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 && \ @@ -120,6 +123,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..9c778dd73 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,16 +460,28 @@ 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 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/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..9e6130e09 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, @@ -10,6 +11,7 @@ import { compareOpenCodeVersions, createOpenCodeApi, describeUnsupportedOpenCodeVersion, + isSameOpenCodeMajor, isStableOpenCodeVersion, isSupportedOpenCodeVersion, normalizeOpenCodeVersion, @@ -49,9 +51,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 +65,58 @@ 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('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.1 <3.0.0', + 'OpenCode 3.0.0 is not supported; OpenCode Manager requires OpenCode >=2.2.5 <3.0.0', ) }) }) @@ -144,25 +158,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:-${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 - ) : ( - Unsupported version - )} +
) } diff --git a/frontend/src/components/settings/OpenCodeServersSettings.tsx b/frontend/src/components/settings/OpenCodeServersSettings.tsx index e0084ab3e..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 - ) : ( - Unsupported version - )} +
) } diff --git a/ocm-cli/README.md b/ocm-cli/README.md index 182a605c2..acb560265 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.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 | -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 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 +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.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/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..e02d1771b 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,49 @@ 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: an exact + * pin, or space-separated `>=`, `<=` and `<` bounds such as `>=0.8.0 <0.9.0`. + */ +function expectVersionSatisfiesPeer(version: string, peer: string): void { + const comparators = peer.trim().split(/\s+/) + if (!comparators.every((comparator) => comparator.startsWith('>=') || comparator.startsWith('<'))) { + expect(version).toBe(peer) + return + } + 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')) @@ -44,15 +72,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..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_BUNDLED_VERSION:-}" +OPENCODE_SUPPORTED_FLOOR="${OPENCODE_MIN_VERSION:-${OPENCODE_BUNDLED_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..95b6a7bbc 100644 --- a/shared/src/opencode/index.ts +++ b/shared/src/opencode/index.ts @@ -136,12 +136,14 @@ export type { } from './pairing' export { + OPENCODE_MIN_VERSION, OPENCODE_PINNED_VERSION, OPENCODE_SUPPORTED_VERSION_RANGE, buildOpenCodeReleaseAsset, compareOpenCodeVersions, describeUnsupportedOpenCodeVersion, isStableOpenCodeVersion, + isSameOpenCodeMajor, isSupportedOpenCodeVersion, normalizeOpenCodeVersion, parseOpenCodeVersion, diff --git a/shared/src/opencode/release.ts b/shared/src/opencode/release.ts index 97aefb6fc..994668db9 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,25 @@ 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 +} + +/** 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 {