Compare commits

..
Author SHA1 Message Date
DaxandGitHub 23786505b2 Merge branch 'dev' into trigger-publish-on-beta 2026-01-29 16:30:26 -05:00
Dax Raad e2c6ab3a45 ci: trigger publish workflow after beta sync 2026-01-29 16:16:36 -05:00
Dax Raad 29d8cc6a1c fix: show all provider models when no providers connected
When no providers were connected, the model dialog would hide models
that were in the recent list, but the recent section wasn't being
displayed. This caused models to completely disappear.

The filter was checking recentList (which only contained non-favorite
recent items) instead of recents (all recent items), causing models
to be incorrectly filtered out of the main provider list when the
recent section wasn't shown.
2026-01-29 16:14:03 -05:00
25 changed files with 122 additions and 218 deletions
+6
View File
@@ -16,6 +16,7 @@ jobs:
permissions:
contents: write
pull-requests: read
actions: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
@@ -32,3 +33,8 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bun script/beta.ts
- name: Trigger publish workflow
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run publish.yml --ref beta
+8 -4
View File
@@ -36,6 +36,8 @@ jobs:
if: github.repository == 'anomalyco/opencode'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
- uses: ./.github/actions/setup-bun
- id: version
run: |
@@ -56,6 +58,7 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
fetch-tags: true
- uses: ./.github/actions/setup-bun
@@ -100,6 +103,7 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1
fetch-tags: true
- uses: apple-actions/import-codesign-certs@v2
@@ -188,14 +192,12 @@ jobs:
needs:
- version
- build-cli
# - build-tauri
- build-tauri
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: ./.github/actions/setup-bun
fetch-depth: 1
- name: Install OpenCode
if: inputs.bump || inputs.version
@@ -225,6 +227,8 @@ jobs:
git config --global user.name "opencode"
git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }}
- uses: ./.github/actions/setup-bun
- uses: actions/download-artifact@v4
with:
name: opencode-cli
+15 -15
View File
@@ -23,7 +23,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -73,7 +73,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -107,7 +107,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -134,7 +134,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -158,7 +158,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -182,7 +182,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -213,7 +213,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@opencode-ai/ui": "workspace:*",
"@opencode-ai/util": "workspace:*",
@@ -242,7 +242,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -258,7 +258,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"bin": {
"opencode": "./bin/opencode",
},
@@ -362,7 +362,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"zod": "catalog:",
@@ -382,7 +382,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"devDependencies": {
"@hey-api/openapi-ts": "0.90.10",
"@tsconfig/node22": "catalog:",
@@ -393,7 +393,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -406,7 +406,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*",
@@ -448,7 +448,7 @@
},
"packages/util": {
"name": "@opencode-ai/util",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"zod": "catalog:",
},
@@ -459,7 +459,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-gUWzUsk81miIrjg0fZQmsIQG4pZYmEHgzN6BaXI+lfc=",
"aarch64-linux": "sha256-gwEG75ha/ojTO2iAObTmLTtEkXIXJ7BThzfI5CqlJh8=",
"aarch64-darwin": "sha256-20RGG2GkUItCzD67gDdoSLfexttM8abS//FKO9bfjoM=",
"x86_64-darwin": "sha256-i2VawFuR1UbjPVYoybU6aJDJfFo0tcvtl1aM31Y2mTQ="
"x86_64-linux": "sha256-yAtZlh6YR78RwPt0LK/7Pk0qUm0/97+s6ghhZzuoE/0=",
"aarch64-linux": "sha256-6j81rdjQ7Wps9bvfw+mmdwW5p01qUOwX40UZltCTe3Y=",
"aarch64-darwin": "sha256-pDM8M/QMWR6Go5pz3XXsJqcJDHAlHrx2Faijjkzcngo=",
"x86_64-darwin": "sha256-eOAPtMd1n5xYupBOevCLhY1eFy3wzGqFk/EsZocl9Y8="
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"description": "",
"type": "module",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"private": true,
"type": "module",
"license": "MIT",
+6 -6
View File
@@ -1,7 +1,7 @@
id = "opencode"
name = "OpenCode"
description = "The open source coding agent."
version = "1.1.43"
version = "0.0.0-ci-202601291718"
schema_version = 1
authors = ["Anomaly"]
repository = "https://github.com/anomalyco/opencode"
@@ -11,26 +11,26 @@ name = "OpenCode"
icon = "./icons/opencode.svg"
[agent_servers.opencode.targets.darwin-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.43/opencode-darwin-arm64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-darwin-arm64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.darwin-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.43/opencode-darwin-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-darwin-x64.zip"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-aarch64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.43/opencode-linux-arm64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-linux-arm64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.linux-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.43/opencode-linux-x64.tar.gz"
archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-linux-x64.tar.gz"
cmd = "./opencode"
args = ["acp"]
[agent_servers.opencode.targets.windows-x86_64]
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.43/opencode-windows-x64.zip"
archive = "https://github.com/anomalyco/opencode/releases/download/v0.0.0-ci-202601291718/opencode-windows-x64.zip"
cmd = "./opencode.exe"
args = ["acp"]
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/function",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"name": "opencode",
"type": "module",
"license": "MIT",
+1 -26
View File
@@ -32,21 +32,6 @@ import { Event } from "../server/event"
export namespace Config {
const log = Log.create({ service: "config" })
// Managed settings directory for enterprise deployments (highest priority, admin-controlled)
// These settings override all user and project settings
function getManagedConfigDir(): string {
switch (process.platform) {
case "darwin":
return "/Library/Application Support/opencode"
case "win32":
return path.join(process.env.ProgramData || "C:\\ProgramData", "opencode")
default:
return "/etc/opencode"
}
}
const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR || getManagedConfigDir()
// Custom merge function that concatenates array fields instead of replacing them
function mergeConfigConcatArrays(target: Info, source: Info): Info {
const merged = mergeDeep(target, source)
@@ -163,18 +148,8 @@ export namespace Config {
result.plugin.push(...(await loadPlugin(dir)))
}
// Load managed config files last (highest priority) - enterprise admin-controlled
// Kept separate from directories array to avoid write operations when installing plugins
// which would fail on system directories requiring elevated permissions
// This way it only loads config file and not skills/plugins/commands
if (existsSync(managedConfigDir)) {
for (const file of ["opencode.jsonc", "opencode.json"]) {
result = mergeConfigConcatArrays(result, await loadFile(path.join(managedConfigDir, file)))
}
}
// Migrate deprecated mode field to agent field
for (const [name, mode] of Object.entries(result.mode ?? {})) {
for (const [name, mode] of Object.entries(result.mode)) {
result.agent = mergeDeep(result.agent ?? {}, {
[name]: {
...mode,
+1 -1
View File
@@ -135,7 +135,7 @@ export namespace MCP {
return client.callTool(
{
name: mcpTool.name,
arguments: (args || {}) as Record<string, unknown>,
arguments: args as Record<string, unknown>,
},
CallToolResultSchema,
{
+65 -142
View File
@@ -1,4 +1,4 @@
import { test, expect, describe, mock, afterEach } from "bun:test"
import { test, expect, describe, mock } from "bun:test"
import { Config } from "../../src/config/config"
import { Instance } from "../../src/project/instance"
import { Auth } from "../../src/auth"
@@ -6,23 +6,6 @@ import { tmpdir } from "../fixture/fixture"
import path from "path"
import fs from "fs/promises"
import { pathToFileURL } from "url"
import { Global } from "../../src/global"
// Get managed config directory from environment (set in preload.ts)
const managedConfigDir = process.env.OPENCODE_TEST_MANAGED_CONFIG_DIR!
afterEach(async () => {
await fs.rm(managedConfigDir, { force: true, recursive: true }).catch(() => {})
})
async function writeManagedSettings(settings: object, filename = "opencode.json") {
await fs.mkdir(managedConfigDir, { recursive: true })
await Bun.write(path.join(managedConfigDir, filename), JSON.stringify(settings))
}
async function writeConfig(dir: string, config: object, name = "opencode.json") {
await Bun.write(path.join(dir, name), JSON.stringify(config))
}
test("loads config with defaults when no files exist", async () => {
await using tmp = await tmpdir()
@@ -38,11 +21,14 @@ test("loads config with defaults when no files exist", async () => {
test("loads JSON config file", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
model: "test/model",
username: "testuser",
})
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
model: "test/model",
username: "testuser",
}),
)
},
})
await Instance.provide({
@@ -82,19 +68,21 @@ test("loads JSONC config file", async () => {
test("merges multiple config files with correct precedence", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(
dir,
{
await Bun.write(
path.join(dir, "opencode.jsonc"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
model: "base",
username: "base",
},
"opencode.jsonc",
}),
)
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
model: "override",
}),
)
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
model: "override",
})
},
})
await Instance.provide({
@@ -114,10 +102,13 @@ test("handles environment variable substitution", async () => {
try {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
theme: "{env:TEST_VAR}",
})
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
theme: "{env:TEST_VAR}",
}),
)
},
})
await Instance.provide({
@@ -178,10 +169,13 @@ test("handles file inclusion substitution", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(path.join(dir, "included.txt"), "test_theme")
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
theme: "{file:included.txt}",
})
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
theme: "{file:included.txt}",
}),
)
},
})
await Instance.provide({
@@ -196,10 +190,13 @@ test("handles file inclusion substitution", async () => {
test("validates config schema and throws on invalid fields", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
invalid_field: "should cause error",
})
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
invalid_field: "should cause error",
}),
)
},
})
await Instance.provide({
@@ -228,16 +225,19 @@ test("throws error for invalid JSON", async () => {
test("handles agent configuration", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
agent: {
test_agent: {
model: "test/model",
temperature: 0.7,
description: "test agent",
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
agent: {
test_agent: {
model: "test/model",
temperature: 0.7,
description: "test agent",
},
},
},
})
}),
)
},
})
await Instance.provide({
@@ -258,16 +258,19 @@ test("handles agent configuration", async () => {
test("handles command configuration", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
command: {
test_command: {
template: "test template",
description: "test command",
agent: "test_agent",
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
command: {
test_command: {
template: "test template",
description: "test command",
agent: "test_agent",
},
},
},
})
}),
)
},
})
await Instance.provide({
@@ -891,86 +894,6 @@ test("migrates legacy write tool to edit permission", async () => {
})
})
// Managed settings tests
// Note: preload.ts sets OPENCODE_TEST_MANAGED_CONFIG which Global.Path.managedConfig uses
test("managed settings override user settings", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
model: "user/model",
share: "auto",
username: "testuser",
})
},
})
await writeManagedSettings({
$schema: "https://opencode.ai/config.json",
model: "managed/model",
share: "disabled",
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.model).toBe("managed/model")
expect(config.share).toBe("disabled")
expect(config.username).toBe("testuser")
},
})
})
test("managed settings override project settings", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
autoupdate: true,
disabled_providers: [],
theme: "dark",
})
},
})
await writeManagedSettings({
$schema: "https://opencode.ai/config.json",
autoupdate: false,
disabled_providers: ["openai"],
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.autoupdate).toBe(false)
expect(config.disabled_providers).toEqual(["openai"])
expect(config.theme).toBe("dark")
},
})
})
test("missing managed settings file is not an error", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await writeConfig(dir, {
$schema: "https://opencode.ai/config.json",
model: "user/model",
})
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const config = await Config.get()
expect(config.model).toBe("user/model")
},
})
})
test("migrates legacy edit tool to edit permission", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
-4
View File
@@ -17,10 +17,6 @@ const testHome = path.join(dir, "home")
await fs.mkdir(testHome, { recursive: true })
process.env["OPENCODE_TEST_HOME"] = testHome
// Set test managed config directory to isolate tests from system managed settings
const testManagedConfigDir = path.join(dir, "managed")
process.env["OPENCODE_TEST_MANAGED_CONFIG_DIR"] = testManagedConfigDir
process.env["XDG_DATA_HOME"] = path.join(dir, "share")
process.env["XDG_CACHE_HOME"] = path.join(dir, "cache")
process.env["XDG_CONFIG_HOME"] = path.join(dir, "config")
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"type": "module",
"license": "MIT",
"exports": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/util",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "@opencode-ai/web",
"type": "module",
"license": "MIT",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"scripts": {
"dev": "astro dev",
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "opencode",
"displayName": "opencode",
"description": "opencode for VS Code",
"version": "1.1.43",
"version": "0.0.0-ci-202601291718",
"publisher": "sst-dev",
"repository": {
"type": "git",