Merge branch 'dev' into truncate-to-file
commit
ff2c1c4267
|
|
@ -172,8 +172,7 @@ jobs:
|
|||
- name: Install tauri-cli from portable appimage branch
|
||||
if: contains(matrix.settings.host, 'ubuntu')
|
||||
run: |
|
||||
# cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
|
||||
cargo install tauri-cli --git https://github.com/brendonovich/tauri --branch appimage-sidecar-binaries --force
|
||||
cargo install tauri-cli --git https://github.com/tauri-apps/tauri --branch feat/truly-portable-appimage --force
|
||||
echo "Installed tauri-cli version:"
|
||||
cargo tauri --version
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
|
|||
npm i -g opencode-ai@latest # or bun/pnpm/yarn
|
||||
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||
choco install opencode # Windows
|
||||
brew install opencode # macOS and Linux
|
||||
brew install anomalyco/tap/opencode # macOS and Linux (recommended, always up to date)
|
||||
brew install opencode # macOS and Linux (official brew formula, updated less frequently)
|
||||
paru -S opencode-bin # Arch Linux
|
||||
mise use -g opencode # Any OS
|
||||
nix run nixpkgs#opencode # or github:anomalyco/opencode for latest dev branch
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
|
|||
npm i -g opencode-ai@latest # 也可使用 bun/pnpm/yarn
|
||||
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||
choco install opencode # Windows
|
||||
brew install opencode # macOS 和 Linux
|
||||
brew install anomalyco/tap/opencode # macOS 和 Linux(推荐,始终保持最新)
|
||||
brew install opencode # macOS 和 Linux(官方 brew formula,更新频率较低)
|
||||
paru -S opencode-bin # Arch Linux
|
||||
mise use -g opencode # 任意系统
|
||||
nix run nixpkgs#opencode # 或用 github:anomalyco/opencode 获取最新 dev 分支
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@ curl -fsSL https://opencode.ai/install | bash
|
|||
npm i -g opencode-ai@latest # 也可使用 bun/pnpm/yarn
|
||||
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||
choco install opencode # Windows
|
||||
brew install opencode # macOS 與 Linux
|
||||
brew install anomalyco/tap/opencode # macOS 與 Linux(推薦,始終保持最新)
|
||||
brew install opencode # macOS 與 Linux(官方 brew formula,更新頻率較低)
|
||||
paru -S opencode-bin # Arch Linux
|
||||
mise use -g github:anomalyco/opencode # 任何作業系統
|
||||
nix run nixpkgs#opencode # 或使用 github:anomalyco/opencode 以取得最新開發分支
|
||||
|
|
|
|||
1
STATS.md
1
STATS.md
|
|
@ -193,3 +193,4 @@
|
|||
| 2026-01-04 | 1,672,656 (+39,702) | 1,339,883 (+7,969) | 3,012,539 (+62,560) |
|
||||
| 2026-01-05 | 1,738,171 (+65,515) | 1,353,043 (+13,160) | 3,091,214 (+78,675) |
|
||||
| 2026-01-06 | 1,960,988 (+222,817) | 1,377,377 (+24,334) | 3,338,365 (+247,151) |
|
||||
| 2026-01-07 | 2,123,239 (+162,251) | 1,398,648 (+21,271) | 3,521,887 (+183,522) |
|
||||
|
|
|
|||
56
bun.lock
56
bun.lock
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -70,7 +70,7 @@
|
|||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
|
@ -98,7 +98,7 @@
|
|||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
|
|
@ -149,7 +149,7 @@
|
|||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
@ -173,7 +173,7 @@
|
|||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
|
|
@ -202,7 +202,7 @@
|
|||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
|
|
@ -231,7 +231,7 @@
|
|||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
|
|
@ -247,7 +247,7 @@
|
|||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
|
|
@ -277,7 +277,7 @@
|
|||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "1.15.1",
|
||||
"@modelcontextprotocol/sdk": "1.25.2",
|
||||
"@octokit/graphql": "9.0.2",
|
||||
"@octokit/rest": "catalog:",
|
||||
"@openauthjs/openauth": "catalog:",
|
||||
|
|
@ -350,7 +350,7 @@
|
|||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
|
|
@ -370,7 +370,7 @@
|
|||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.88.1",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
|
|
@ -381,7 +381,7 @@
|
|||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
|
|
@ -394,7 +394,7 @@
|
|||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
|
|
@ -433,7 +433,7 @@
|
|||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
|
|
@ -444,7 +444,7 @@
|
|||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
|
|
@ -911,6 +911,8 @@
|
|||
|
||||
"@hey-api/openapi-ts": ["@hey-api/openapi-ts@0.88.1", "", { "dependencies": { "@hey-api/codegen-core": "^0.3.3", "@hey-api/json-schema-ref-parser": "1.2.2", "ansi-colors": "4.1.3", "c12": "3.3.2", "color-support": "1.1.3", "commander": "14.0.2", "open": "11.0.0", "semver": "7.7.2" }, "peerDependencies": { "typescript": ">=5.5.3" }, "bin": { "openapi-ts": "bin/run.js" } }, "sha512-x/nDTupOnV9VuSeNIiJpgIpc915GHduhyseJeMTnI0JMsXaObmpa0rgPr3ASVEYMLgpvqozIEG1RTOOnal6zLQ=="],
|
||||
|
||||
"@hono/node-server": ["@hono/node-server@1.19.7", "", { "peerDependencies": { "hono": "^4" } }, "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw=="],
|
||||
|
||||
"@hono/standard-validator": ["@hono/standard-validator@0.1.5", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-EIyZPPwkyLn6XKwFj5NBEWHXhXbgmnVh2ceIFo5GO7gKI9WmzTjPDKnppQB0KrqKeAkq3kpoW4SIbu5X1dgx3w=="],
|
||||
|
||||
"@hono/zod-validator": ["@hono/zod-validator@0.4.2", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.19.1" } }, "sha512-1rrlBg+EpDPhzOV4hT9pxr5+xDVmKuz6YJl+la7VCwK6ass5ldyKm5fD+umJdV2zhHD6jROoCCv8NbTwyfhT0g=="],
|
||||
|
|
@ -1095,7 +1097,7 @@
|
|||
|
||||
"@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.15.1", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w=="],
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.25.2", "", { "dependencies": { "@hono/node-server": "^1.19.7", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.0" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww=="],
|
||||
|
||||
"@motionone/animation": ["@motionone/animation@10.18.0", "", { "dependencies": { "@motionone/easing": "^10.18.0", "@motionone/types": "^10.17.1", "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw=="],
|
||||
|
||||
|
|
@ -1903,7 +1905,9 @@
|
|||
|
||||
"ai": ["ai@5.0.97", "", { "dependencies": { "@ai-sdk/gateway": "2.0.12", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8zBx0b/owis4eJI2tAlV8a1Rv0BANmLxontcAelkLNwEHhgfgXeKpDkhNB6OgV+BJSwboIUDkgd9312DdJnCOQ=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
"ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="],
|
||||
|
||||
"ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="],
|
||||
|
||||
"ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="],
|
||||
|
||||
|
|
@ -2407,7 +2411,7 @@
|
|||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="],
|
||||
|
||||
|
|
@ -2783,7 +2787,9 @@
|
|||
|
||||
"json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="],
|
||||
|
||||
"json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
|
||||
|
||||
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
|
||||
|
||||
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
|
||||
|
||||
|
|
@ -3385,6 +3391,8 @@
|
|||
|
||||
"remeda": ["remeda@2.26.0", "", { "dependencies": { "type-fest": "^4.41.0" } }, "sha512-lmNNwtaC6Co4m0WTTNoZ/JlpjEqAjPZO0+czC9YVRQUpkbS4x8Hmh+Mn9HPfJfiXqUQ5IXXgSXSOB2pBKAytdA=="],
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"reselect": ["reselect@4.1.8", "", {}, "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ=="],
|
||||
|
||||
"resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="],
|
||||
|
|
@ -3759,8 +3767,6 @@
|
|||
|
||||
"update-browserslist-db": ["update-browserslist-db@1.1.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A=="],
|
||||
|
||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||
|
||||
"url": ["url@0.10.3", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
|
||||
|
||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||
|
|
@ -4059,9 +4065,11 @@
|
|||
|
||||
"@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/jose": ["jose@6.1.3", "", {}, "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||
|
||||
"@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
"@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="],
|
||||
|
||||
"@octokit/auth-app/@octokit/request": ["@octokit/request@10.0.7", "", { "dependencies": { "@octokit/endpoint": "^11.0.2", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA=="],
|
||||
|
||||
|
|
@ -4405,8 +4413,6 @@
|
|||
|
||||
"unifont/ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="],
|
||||
|
||||
"uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"utif2/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
|
||||
|
||||
"vitest/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"nodeModules": "sha256-WHqX159BYPSHBFmxxkTrWPytBzTSTcWkoEywAxP58kI="
|
||||
"nodeModules": "sha256-rNGq0yjL5ZHYVg+zyV4nFPug4gqhKhyOnfebaufyd34="
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -248,6 +248,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
}
|
||||
}
|
||||
|
||||
const isFocused = createFocusSignal(() => editorRef)
|
||||
|
||||
createEffect(() => {
|
||||
params.id
|
||||
editorRef.focus()
|
||||
|
|
@ -258,7 +260,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
onCleanup(() => clearInterval(interval))
|
||||
})
|
||||
|
||||
const isFocused = createFocusSignal(() => editorRef)
|
||||
const [composing, setComposing] = createSignal(false)
|
||||
const isImeComposing = (event: KeyboardEvent) => event.isComposing || composing() || event.keyCode === 229
|
||||
|
||||
|
|
@ -292,12 +293,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
const clipboardData = event.clipboardData
|
||||
if (!clipboardData) return
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
const items = Array.from(clipboardData.items)
|
||||
const imageItems = items.filter((item) => ACCEPTED_FILE_TYPES.includes(item.type))
|
||||
|
||||
if (imageItems.length > 0) {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
for (const item of imageItems) {
|
||||
const file = item.getAsFile()
|
||||
if (file) await addImageAttachment(file)
|
||||
|
|
@ -305,8 +307,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
const plainText = clipboardData.getData("text/plain") ?? ""
|
||||
addPart({ type: "text", content: plainText, start: 0, end: 0 })
|
||||
}
|
||||
|
|
@ -347,13 +347,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
}
|
||||
|
||||
onMount(() => {
|
||||
editorRef.addEventListener("paste", handlePaste)
|
||||
document.addEventListener("dragover", handleGlobalDragOver)
|
||||
document.addEventListener("dragleave", handleGlobalDragLeave)
|
||||
document.addEventListener("drop", handleGlobalDrop)
|
||||
})
|
||||
onCleanup(() => {
|
||||
editorRef.removeEventListener("paste", handlePaste)
|
||||
document.removeEventListener("dragover", handleGlobalDragOver)
|
||||
document.removeEventListener("dragleave", handleGlobalDragLeave)
|
||||
document.removeEventListener("drop", handleGlobalDrop)
|
||||
|
|
@ -1508,6 +1506,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||
}}
|
||||
contenteditable="true"
|
||||
onInput={handleInput}
|
||||
onPaste={handlePaste}
|
||||
onCompositionStart={() => setComposing(true)}
|
||||
onCompositionEnd={() => setComposing(false)}
|
||||
onKeyDown={handleKeyDown}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -12,5 +12,19 @@
|
|||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
<key>com.apple.security.automation.apple-events</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.addressbook</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.calendars</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.location</key>
|
||||
<true/>
|
||||
<key>com.apple.security.personal-information.photos-library</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild {
|
|||
.args([
|
||||
"-il",
|
||||
"-c",
|
||||
&format!("{} serve --port={}", sidecar.display(), port),
|
||||
&format!("\"{}\" serve --port={}", sidecar.display(), port),
|
||||
])
|
||||
.spawn()
|
||||
.expect("Failed to spawn opencode")
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { Suspense, createResource, ParentProps } from "solid-js"
|
|||
import { UPDATER_ENABLED } from "./updater"
|
||||
import { createMenu } from "./menu"
|
||||
import pkg from "../package.json"
|
||||
import { Show } from "solid-js"
|
||||
|
||||
const root = document.getElementById("root")
|
||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
||||
|
|
@ -288,7 +289,9 @@ function ServerGate(props: ParentProps) {
|
|||
})
|
||||
|
||||
return (
|
||||
<Suspense
|
||||
// Not using suspense as not all components are compatible with it (undefined refs)
|
||||
<Show
|
||||
when={status.state !== "pending"}
|
||||
fallback={
|
||||
<div class="h-screen w-screen flex flex-col items-center justify-center bg-background-base">
|
||||
<Logo class="w-xl opacity-12 animate-pulse" />
|
||||
|
|
@ -296,9 +299,9 @@ function ServerGate(props: ParentProps) {
|
|||
</div>
|
||||
}
|
||||
>
|
||||
{/* Triggers suspense/error boundaries without rendering the returned value */}
|
||||
{/* Trigger error boundary without rendering the returned value */}
|
||||
{(status(), null)}
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</Suspense>
|
||||
{props.children}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.1.4"
|
||||
version = "1.1.6"
|
||||
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.4/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/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.4/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.4/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/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.4/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/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.4/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/anomalyco/opencode/releases/download/v1.1.6/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
@ -72,7 +72,7 @@
|
|||
"@clack/prompts": "1.0.0-alpha.1",
|
||||
"@hono/standard-validator": "0.1.5",
|
||||
"@hono/zod-validator": "catalog:",
|
||||
"@modelcontextprotocol/sdk": "1.15.1",
|
||||
"@modelcontextprotocol/sdk": "1.25.2",
|
||||
"@octokit/graphql": "9.0.2",
|
||||
"@octokit/rest": "catalog:",
|
||||
"@openauthjs/openauth": "catalog:",
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ export namespace Agent {
|
|||
options: {},
|
||||
native: true,
|
||||
hidden: true,
|
||||
temperature: 0.5,
|
||||
permission: PermissionNext.merge(
|
||||
defaults,
|
||||
PermissionNext.fromConfig({
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ Your output must be:
|
|||
</task>
|
||||
|
||||
<rules>
|
||||
- Title must be grammatically correct and read naturally - no word salad
|
||||
- Never include tool names in the title (e.g. "read tool", "bash tool", "edit tool")
|
||||
- Focus on the main topic or question the user needs to retrieve
|
||||
- Use -ing verbs for actions (Debugging, Implementing, Analyzing)
|
||||
- Vary your phrasing - avoid repetitive patterns like always starting with "Analyzing"
|
||||
- When a file is mentioned, focus on WHAT the user wants to do WITH the file, not just that they shared it
|
||||
- Keep exact: technical terms, numbers, filenames, HTTP codes
|
||||
- Remove: the, this, my, a, an
|
||||
- Never assume tech stack
|
||||
|
|
@ -29,8 +32,12 @@ Your output must be:
|
|||
<examples>
|
||||
"debug 500 errors in production" → Debugging production 500 errors
|
||||
"refactor user service" → Refactoring user service
|
||||
"why is app.js failing" → Analyzing app.js failure
|
||||
"implement rate limiting" → Implementing rate limiting
|
||||
"how do I connect postgres to my API" → Connecting Postgres to API
|
||||
"why is app.js failing" → app.js failure investigation
|
||||
"implement rate limiting" → Rate limiting implementation
|
||||
"how do I connect postgres to my API" → Postgres API connection
|
||||
"best practices for React hooks" → React hooks best practices
|
||||
"@src/auth.ts can you add refresh token support" → Auth refresh token support
|
||||
"@utils/parser.ts this is broken" → Parser bug fix
|
||||
"look at @config.json" → Config review
|
||||
"@App.tsx add dark mode toggle" → Dark mode toggle in App
|
||||
</examples>
|
||||
|
|
|
|||
|
|
@ -653,8 +653,10 @@ export function Autocomplete(props: {
|
|||
})
|
||||
|
||||
const height = createMemo(() => {
|
||||
if (options().length) return Math.min(10, options().length)
|
||||
return 1
|
||||
const count = options().length || 1
|
||||
if (!store.visible) return Math.min(10, count)
|
||||
positionTick()
|
||||
return Math.min(10, count, Math.max(1, props.anchor().y))
|
||||
})
|
||||
|
||||
let scroll: ScrollBoxRenderable
|
||||
|
|
|
|||
|
|
@ -288,11 +288,11 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
|||
|
||||
createEffect(() => {
|
||||
const theme = sync.data.config.theme
|
||||
console.log("theme", theme)
|
||||
if (theme) setStore("active", theme)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
function init() {
|
||||
resolveSystemTheme()
|
||||
getCustomThemes()
|
||||
.then((custom) => {
|
||||
setStore(
|
||||
|
|
@ -309,15 +309,18 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
|||
setStore("ready", true)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
onMount(init)
|
||||
|
||||
function resolveSystemTheme() {
|
||||
console.log("resolved system theme")
|
||||
console.log("resolveSystemTheme")
|
||||
renderer
|
||||
.getPalette({
|
||||
size: 16,
|
||||
})
|
||||
.then((colors) => {
|
||||
console.log(colors.palette)
|
||||
if (!colors.palette[0]) {
|
||||
if (store.active === "system") {
|
||||
setStore(
|
||||
|
|
@ -341,11 +344,9 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({
|
|||
}
|
||||
|
||||
const renderer = useRenderer()
|
||||
resolveSystemTheme()
|
||||
|
||||
const sdk = useSDK()
|
||||
sdk.event.on("server.instance.disposed", () => {
|
||||
resolveSystemTheme()
|
||||
process.on("SIGUSR2", async () => {
|
||||
renderer.clearPaletteCache()
|
||||
init()
|
||||
})
|
||||
|
||||
const values = createMemo(() => {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@
|
|||
"dark": "darkStep11",
|
||||
"light": "lightStep11"
|
||||
},
|
||||
"selectedListItemText": {
|
||||
"dark": "#0a0a0a",
|
||||
"light": "#ffffff"
|
||||
},
|
||||
"background": {
|
||||
"dark": "transparent",
|
||||
"light": "transparent"
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@
|
|||
"dark": "darkStep11",
|
||||
"light": "lightStep11"
|
||||
},
|
||||
"selectedListItemText": {
|
||||
"dark": "#0a0a0a",
|
||||
"light": "#ffffff"
|
||||
},
|
||||
"background": {
|
||||
"dark": "darkStep1",
|
||||
"light": "lightStep1"
|
||||
|
|
|
|||
|
|
@ -37,14 +37,40 @@ export namespace Config {
|
|||
|
||||
export const state = Instance.state(async () => {
|
||||
const auth = await Auth.all()
|
||||
let result = await global()
|
||||
|
||||
// Override with custom config if provided
|
||||
// Load remote/well-known config first as the base layer (lowest precedence)
|
||||
// This allows organizations to provide default configs that users can override
|
||||
let result: Info = {}
|
||||
for (const [key, value] of Object.entries(auth)) {
|
||||
if (value.type === "wellknown") {
|
||||
process.env[value.key] = value.token
|
||||
log.debug("fetching remote config", { url: `${key}/.well-known/opencode` })
|
||||
const response = await fetch(`${key}/.well-known/opencode`)
|
||||
if (!response.ok) {
|
||||
throw new Error(`failed to fetch remote config from ${key}: ${response.status}`)
|
||||
}
|
||||
const wellknown = (await response.json()) as any
|
||||
const remoteConfig = wellknown.config ?? {}
|
||||
// Add $schema to prevent load() from trying to write back to a non-existent file
|
||||
if (!remoteConfig.$schema) remoteConfig.$schema = "https://opencode.ai/config.json"
|
||||
result = mergeConfigConcatArrays(
|
||||
result,
|
||||
await load(JSON.stringify(remoteConfig), `${key}/.well-known/opencode`),
|
||||
)
|
||||
log.debug("loaded remote config from well-known", { url: key })
|
||||
}
|
||||
}
|
||||
|
||||
// Global user config overrides remote config
|
||||
result = mergeConfigConcatArrays(result, await global())
|
||||
|
||||
// Custom config path overrides global
|
||||
if (Flag.OPENCODE_CONFIG) {
|
||||
result = mergeConfigConcatArrays(result, await loadFile(Flag.OPENCODE_CONFIG))
|
||||
log.debug("loaded custom config", { path: Flag.OPENCODE_CONFIG })
|
||||
}
|
||||
|
||||
// Project config has highest precedence (overrides global and remote)
|
||||
for (const file of ["opencode.jsonc", "opencode.json"]) {
|
||||
const found = await Filesystem.findUp(file, Instance.directory, Instance.worktree)
|
||||
for (const resolved of found.toReversed()) {
|
||||
|
|
@ -52,19 +78,12 @@ export namespace Config {
|
|||
}
|
||||
}
|
||||
|
||||
// Inline config content has highest precedence
|
||||
if (Flag.OPENCODE_CONFIG_CONTENT) {
|
||||
result = mergeConfigConcatArrays(result, JSON.parse(Flag.OPENCODE_CONFIG_CONTENT))
|
||||
log.debug("loaded custom config from OPENCODE_CONFIG_CONTENT")
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(auth)) {
|
||||
if (value.type === "wellknown") {
|
||||
process.env[value.key] = value.token
|
||||
const wellknown = (await fetch(`${key}/.well-known/opencode`).then((x) => x.json())) as any
|
||||
result = mergeConfigConcatArrays(result, await load(JSON.stringify(wellknown.config ?? {}), process.cwd()))
|
||||
}
|
||||
}
|
||||
|
||||
result.agent = result.agent || {}
|
||||
result.mode = result.mode || {}
|
||||
result.plugin = result.plugin || []
|
||||
|
|
|
|||
|
|
@ -111,8 +111,8 @@ export namespace Installation {
|
|||
)
|
||||
|
||||
async function getBrewFormula() {
|
||||
const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).quiet().text()
|
||||
if (tapFormula.includes("opencode")) return "sst/tap/opencode"
|
||||
const tapFormula = await $`brew list --formula anomalyco/tap/opencode`.throws(false).quiet().text()
|
||||
if (tapFormula.includes("opencode")) return "anomalyco/tap/opencode"
|
||||
const coreFormula = await $`brew list --formula opencode`.throws(false).quiet().text()
|
||||
if (coreFormula.includes("opencode")) return "opencode"
|
||||
return "opencode"
|
||||
|
|
|
|||
|
|
@ -246,7 +246,12 @@ export namespace Server {
|
|||
},
|
||||
)
|
||||
.use(async (c, next) => {
|
||||
const directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
|
||||
let directory = c.req.query("directory") || c.req.header("x-opencode-directory") || process.cwd()
|
||||
try {
|
||||
directory = decodeURIComponent(directory)
|
||||
} catch {
|
||||
// fallback to original value
|
||||
}
|
||||
return Instance.provide({
|
||||
directory,
|
||||
init: InstanceBootstrap,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { test, expect } from "bun:test"
|
||||
import { test, expect, mock, afterEach } from "bun:test"
|
||||
import { Config } from "../../src/config/config"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { Auth } from "../../src/auth"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
|
|
@ -913,3 +914,234 @@ test("permission config preserves key order", async () => {
|
|||
},
|
||||
})
|
||||
})
|
||||
|
||||
// MCP config merging tests
|
||||
|
||||
test("project config can override MCP server enabled status", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
// Simulates a base config (like from remote .well-known) with disabled MCP
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.jsonc"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
jira: {
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
enabled: false,
|
||||
},
|
||||
wiki: {
|
||||
type: "remote",
|
||||
url: "https://wiki.example.com/mcp",
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
// Project config enables just jira
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
jira: {
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await Config.get()
|
||||
// jira should be enabled (overridden by project config)
|
||||
expect(config.mcp?.jira).toEqual({
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
enabled: true,
|
||||
})
|
||||
// wiki should still be disabled (not overridden)
|
||||
expect(config.mcp?.wiki).toEqual({
|
||||
type: "remote",
|
||||
url: "https://wiki.example.com/mcp",
|
||||
enabled: false,
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("MCP config deep merges preserving base config properties", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
// Base config with full MCP definition
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.jsonc"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
myserver: {
|
||||
type: "remote",
|
||||
url: "https://myserver.example.com/mcp",
|
||||
enabled: false,
|
||||
headers: {
|
||||
"X-Custom-Header": "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
// Override just enables it, should preserve other properties
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
myserver: {
|
||||
type: "remote",
|
||||
url: "https://myserver.example.com/mcp",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await Config.get()
|
||||
expect(config.mcp?.myserver).toEqual({
|
||||
type: "remote",
|
||||
url: "https://myserver.example.com/mcp",
|
||||
enabled: true,
|
||||
headers: {
|
||||
"X-Custom-Header": "value",
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("local .opencode config can override MCP from project config", async () => {
|
||||
await using tmp = await tmpdir({
|
||||
init: async (dir) => {
|
||||
// Project config with disabled MCP
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
docs: {
|
||||
type: "remote",
|
||||
url: "https://docs.example.com/mcp",
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
// Local .opencode directory config enables it
|
||||
const opencodeDir = path.join(dir, ".opencode")
|
||||
await fs.mkdir(opencodeDir, { recursive: true })
|
||||
await Bun.write(
|
||||
path.join(opencodeDir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
docs: {
|
||||
type: "remote",
|
||||
url: "https://docs.example.com/mcp",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await Config.get()
|
||||
expect(config.mcp?.docs?.enabled).toBe(true)
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("project config overrides remote well-known config", async () => {
|
||||
const originalFetch = globalThis.fetch
|
||||
let fetchedUrl: string | undefined
|
||||
const mockFetch = mock((url: string | URL | Request) => {
|
||||
const urlStr = url.toString()
|
||||
if (urlStr.includes(".well-known/opencode")) {
|
||||
fetchedUrl = urlStr
|
||||
return Promise.resolve(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
config: {
|
||||
mcp: {
|
||||
jira: {
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
{ status: 200 },
|
||||
),
|
||||
)
|
||||
}
|
||||
return originalFetch(url)
|
||||
})
|
||||
globalThis.fetch = mockFetch as unknown as typeof fetch
|
||||
|
||||
const originalAuthAll = Auth.all
|
||||
Auth.all = mock(() =>
|
||||
Promise.resolve({
|
||||
"https://example.com": {
|
||||
type: "wellknown" as const,
|
||||
key: "TEST_TOKEN",
|
||||
token: "test-token",
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
try {
|
||||
await using tmp = await tmpdir({
|
||||
git: true,
|
||||
init: async (dir) => {
|
||||
// Project config enables jira (overriding remote default)
|
||||
await Bun.write(
|
||||
path.join(dir, "opencode.json"),
|
||||
JSON.stringify({
|
||||
$schema: "https://opencode.ai/config.json",
|
||||
mcp: {
|
||||
jira: {
|
||||
type: "remote",
|
||||
url: "https://jira.example.com/mcp",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const config = await Config.get()
|
||||
// Verify fetch was called for wellknown config
|
||||
expect(fetchedUrl).toBe("https://example.com/.well-known/opencode")
|
||||
// Project config (enabled: true) should override remote (enabled: false)
|
||||
expect(config.mcp?.jira?.enabled).toBe(true)
|
||||
},
|
||||
})
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch
|
||||
Auth.all = originalAuthAll
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ export function createOpencodeClient(config?: Config & { directory?: string }) {
|
|||
}
|
||||
|
||||
if (config?.directory) {
|
||||
const isNonASCII = /[^\x00-\x7F]/.test(config.directory)
|
||||
const encodedDirectory = isNonASCII ? encodeURIComponent(config.directory) : config.directory
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
"x-opencode-directory": config.directory,
|
||||
"x-opencode-directory": encodedDirectory,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"exports": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import fuzzysort from "fuzzysort"
|
||||
import { entries, flatMap, groupBy, map, pipe } from "remeda"
|
||||
import { createMemo, createResource } from "solid-js"
|
||||
import { createEffect, createMemo, createResource, on } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createList } from "solid-list"
|
||||
|
||||
|
|
@ -86,9 +86,14 @@ export function useFilteredList<T>(props: FilteredListProps<T>) {
|
|||
}
|
||||
}
|
||||
|
||||
createEffect(
|
||||
on(grouped, () => {
|
||||
reset()
|
||||
}),
|
||||
)
|
||||
|
||||
const onInput = (value: string) => {
|
||||
setStore("filter", value)
|
||||
reset()
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -372,3 +372,17 @@ input:where([type="button"], [type="reset"], [type="submit"]),
|
|||
[hidden]:where(:not([hidden="until-found"])) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent iOS Safari from auto-zooming on input focus.
|
||||
iOS WebKit zooms on any input with font-size < 16px as an accessibility feature.
|
||||
*/
|
||||
|
||||
@media (hover: none) and (pointer: coarse) {
|
||||
input,
|
||||
select,
|
||||
textarea,
|
||||
[contenteditable="true"] {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import catppuccinThemeJson from "./themes/catppuccin.json"
|
|||
import ayuThemeJson from "./themes/ayu.json"
|
||||
import oneDarkProThemeJson from "./themes/onedarkpro.json"
|
||||
import shadesOfPurpleThemeJson from "./themes/shadesofpurple.json"
|
||||
import nightowlThemeJson from "./themes/nightowl.json"
|
||||
|
||||
export const oc1Theme = oc1ThemeJson as DesktopTheme
|
||||
export const tokyonightTheme = tokyoThemeJson as DesktopTheme
|
||||
|
|
@ -20,6 +21,7 @@ export const catppuccinTheme = catppuccinThemeJson as DesktopTheme
|
|||
export const ayuTheme = ayuThemeJson as DesktopTheme
|
||||
export const oneDarkProTheme = oneDarkProThemeJson as DesktopTheme
|
||||
export const shadesOfPurpleTheme = shadesOfPurpleThemeJson as DesktopTheme
|
||||
export const nightowlTheme = nightowlThemeJson as DesktopTheme
|
||||
|
||||
export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
|
||||
"oc-1": oc1Theme,
|
||||
|
|
@ -32,4 +34,5 @@ export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
|
|||
ayu: ayuTheme,
|
||||
onedarkpro: oneDarkProTheme,
|
||||
shadesofpurple: shadesOfPurpleTheme,
|
||||
nightowl: nightowlTheme,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,4 +41,5 @@ export {
|
|||
ayuTheme,
|
||||
oneDarkProTheme,
|
||||
shadesOfPurpleTheme,
|
||||
nightowlTheme,
|
||||
} from "./default-themes"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
{
|
||||
"$schema": "https://opencode.ai/desktop-theme.json",
|
||||
"name": "Night Owl",
|
||||
"id": "nightowl",
|
||||
"light": {
|
||||
"seeds": {
|
||||
"neutral": "#f0f0f0",
|
||||
"primary": "#4876d6",
|
||||
"success": "#2aa298",
|
||||
"warning": "#c96765",
|
||||
"error": "#de3d3b",
|
||||
"info": "#4876d6",
|
||||
"interactive": "#4876d6",
|
||||
"diffAdd": "#2aa298",
|
||||
"diffDelete": "#de3d3b"
|
||||
},
|
||||
"overrides": {
|
||||
"background-base": "#fbfbfb",
|
||||
"background-weak": "#f0f0f0",
|
||||
"background-strong": "#ffffff",
|
||||
"background-stronger": "#ffffff",
|
||||
"border-weak-base": "#d9d9d9",
|
||||
"border-weak-hover": "#cccccc",
|
||||
"border-weak-active": "#bfbfbf",
|
||||
"border-weak-selected": "#4876d6",
|
||||
"border-weak-disabled": "#e6e6e6",
|
||||
"border-weak-focus": "#4876d6",
|
||||
"border-base": "#c0c0c0",
|
||||
"border-hover": "#b3b3b3",
|
||||
"border-active": "#a6a6a6",
|
||||
"border-selected": "#4876d6",
|
||||
"border-disabled": "#d9d9d9",
|
||||
"border-focus": "#4876d6",
|
||||
"border-strong-base": "#90a7b2",
|
||||
"border-strong-hover": "#7d9aa6",
|
||||
"border-strong-active": "#6a8d9a",
|
||||
"border-strong-selected": "#4876d6",
|
||||
"border-strong-disabled": "#c0c0c0",
|
||||
"border-strong-focus": "#4876d6",
|
||||
"surface-diff-add-base": "#eaf8f6",
|
||||
"surface-diff-delete-base": "#fbe9e9",
|
||||
"surface-diff-hidden-base": "#e8f0fc",
|
||||
"text-base": "#403f53",
|
||||
"text-weak": "#7a8181",
|
||||
"text-strong": "#1a1a1a",
|
||||
"syntax-string": "#c96765",
|
||||
"syntax-primitive": "#aa0982",
|
||||
"syntax-property": "#4876d6",
|
||||
"syntax-type": "#994cc3",
|
||||
"syntax-constant": "#2aa298",
|
||||
"syntax-info": "#4876d6",
|
||||
"markdown-heading": "#4876d6",
|
||||
"markdown-text": "#403f53",
|
||||
"markdown-link": "#4876d6",
|
||||
"markdown-link-text": "#2aa298",
|
||||
"markdown-code": "#2aa298",
|
||||
"markdown-block-quote": "#7a8181",
|
||||
"markdown-emph": "#994cc3",
|
||||
"markdown-strong": "#c96765",
|
||||
"markdown-horizontal-rule": "#90a7b2",
|
||||
"markdown-list-item": "#4876d6",
|
||||
"markdown-list-enumeration": "#2aa298",
|
||||
"markdown-image": "#4876d6",
|
||||
"markdown-image-text": "#2aa298",
|
||||
"markdown-code-block": "#403f53"
|
||||
}
|
||||
},
|
||||
"dark": {
|
||||
"seeds": {
|
||||
"neutral": "#011627",
|
||||
"primary": "#82aaff",
|
||||
"success": "#c5e478",
|
||||
"warning": "#ecc48d",
|
||||
"error": "#ef5350",
|
||||
"info": "#82aaff",
|
||||
"interactive": "#82aaff",
|
||||
"diffAdd": "#c5e478",
|
||||
"diffDelete": "#ef5350"
|
||||
},
|
||||
"overrides": {
|
||||
"background-base": "#011627",
|
||||
"background-weak": "#0b253a",
|
||||
"background-strong": "#001122",
|
||||
"background-stronger": "#000c17",
|
||||
"border-weak-base": "#1d3b53",
|
||||
"border-weak-hover": "#234561",
|
||||
"border-weak-active": "#2a506f",
|
||||
"border-weak-selected": "#82aaff",
|
||||
"border-weak-disabled": "#0f2132",
|
||||
"border-weak-focus": "#82aaff",
|
||||
"border-base": "#3a5a75",
|
||||
"border-hover": "#456785",
|
||||
"border-active": "#507494",
|
||||
"border-selected": "#82aaff",
|
||||
"border-disabled": "#1a3347",
|
||||
"border-focus": "#82aaff",
|
||||
"border-strong-base": "#5f7e97",
|
||||
"border-strong-hover": "#6e8da6",
|
||||
"border-strong-active": "#7d9cb5",
|
||||
"border-strong-selected": "#82aaff",
|
||||
"border-strong-disabled": "#2c4a63",
|
||||
"border-strong-focus": "#82aaff",
|
||||
"surface-diff-add-base": "#0a2e1a",
|
||||
"surface-diff-delete-base": "#2d1b1b",
|
||||
"surface-diff-hidden-base": "#0b253a",
|
||||
"text-base": "#d6deeb",
|
||||
"text-weak": "#5f7e97",
|
||||
"text-strong": "#ffffff",
|
||||
"syntax-string": "#ecc48d",
|
||||
"syntax-primitive": "#f78c6c",
|
||||
"syntax-property": "#82aaff",
|
||||
"syntax-type": "#c5e478",
|
||||
"syntax-constant": "#7fdbca",
|
||||
"syntax-info": "#82aaff",
|
||||
"markdown-heading": "#82aaff",
|
||||
"markdown-text": "#d6deeb",
|
||||
"markdown-link": "#82aaff",
|
||||
"markdown-link-text": "#7fdbca",
|
||||
"markdown-code": "#c5e478",
|
||||
"markdown-block-quote": "#5f7e97",
|
||||
"markdown-emph": "#c792ea",
|
||||
"markdown-strong": "#ecc48d",
|
||||
"markdown-horizontal-rule": "#5f7e97",
|
||||
"markdown-list-item": "#82aaff",
|
||||
"markdown-list-enumeration": "#7fdbca",
|
||||
"markdown-image": "#82aaff",
|
||||
"markdown-image-text": "#7fdbca",
|
||||
"markdown-code-block": "#d6deeb"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "@opencode-ai/web",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev",
|
||||
|
|
|
|||
|
|
@ -32,21 +32,74 @@ different order of precedence.
|
|||
Configuration files are **merged together**, not replaced.
|
||||
:::
|
||||
|
||||
Configuration files are merged together, not replaced. Settings from the following config locations are combined. Where later configs override earlier ones only for conflicting keys. Non-conflicting settings from all configs are preserved.
|
||||
Configuration files are merged together, not replaced. Settings from the following config locations are combined. Later configs override earlier ones only for conflicting keys. Non-conflicting settings from all configs are preserved.
|
||||
|
||||
For example, if your global config sets `theme: "opencode"` and `autoupdate: true`, and your project config sets `model: "anthropic/claude-sonnet-4-5"`, the final configuration will include all three settings.
|
||||
|
||||
---
|
||||
|
||||
### Precedence order
|
||||
|
||||
Config sources are loaded in this order (later sources override earlier ones):
|
||||
|
||||
1. **Remote config** (from `.well-known/opencode`) - organizational defaults
|
||||
2. **Global config** (`~/.config/opencode/opencode.json`) - user preferences
|
||||
3. **Custom config** (`OPENCODE_CONFIG` env var) - custom overrides
|
||||
4. **Project config** (`opencode.json` in project) - project-specific settings
|
||||
5. **`.opencode` directories** - agents, commands, plugins
|
||||
6. **Inline config** (`OPENCODE_CONFIG_CONTENT` env var) - runtime overrides
|
||||
|
||||
This means project configs can override global defaults, and global configs can override remote organizational defaults.
|
||||
|
||||
---
|
||||
|
||||
### Remote
|
||||
|
||||
Organizations can provide default configuration via the `.well-known/opencode` endpoint. This is fetched automatically when you authenticate with a provider that supports it.
|
||||
|
||||
Remote config is loaded first, serving as the base layer. All other config sources (global, project) can override these defaults.
|
||||
|
||||
For example, if your organization provides MCP servers that are disabled by default:
|
||||
|
||||
```json title="Remote config from .well-known/opencode"
|
||||
{
|
||||
"mcp": {
|
||||
"jira": {
|
||||
"type": "remote",
|
||||
"url": "https://jira.example.com/mcp",
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can enable specific servers in your local config:
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
"mcp": {
|
||||
"jira": {
|
||||
"type": "remote",
|
||||
"url": "https://jira.example.com/mcp",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Global
|
||||
|
||||
Place your global OpenCode config in `~/.config/opencode/opencode.json`. You'll want to use the global config for things like themes, providers, or keybinds.
|
||||
Place your global OpenCode config in `~/.config/opencode/opencode.json`. Use global config for user-wide preferences like themes, providers, or keybinds.
|
||||
|
||||
Global config overrides remote organizational defaults.
|
||||
|
||||
---
|
||||
|
||||
### Per project
|
||||
|
||||
You can also add a `opencode.json` in your project. Settings from this config are merged with and can override the global config. This is useful for configuring providers or modes specific to your project.
|
||||
Add `opencode.json` in your project root. Project config has the highest precedence among standard config files - it overrides both global and remote configs.
|
||||
|
||||
:::tip
|
||||
Place project specific config in the root of your project.
|
||||
|
|
@ -60,20 +113,20 @@ This is also safe to be checked into Git and uses the same schema as the global
|
|||
|
||||
### Custom path
|
||||
|
||||
You can also specify a custom config file path using the `OPENCODE_CONFIG` environment variable.
|
||||
Specify a custom config file path using the `OPENCODE_CONFIG` environment variable.
|
||||
|
||||
```bash
|
||||
export OPENCODE_CONFIG=/path/to/my/custom-config.json
|
||||
opencode run "Hello world"
|
||||
```
|
||||
|
||||
Settings from this config are merged with and **can override** the global and project configs.
|
||||
Custom config is loaded between global and project configs in the precedence order.
|
||||
|
||||
---
|
||||
|
||||
### Custom directory
|
||||
|
||||
You can specify a custom config directory using the `OPENCODE_CONFIG_DIR`
|
||||
Specify a custom config directory using the `OPENCODE_CONFIG_DIR`
|
||||
environment variable. This directory will be searched for agents, commands,
|
||||
modes, and plugins just like the standard `.opencode` directory, and should
|
||||
follow the same structure.
|
||||
|
|
|
|||
|
|
@ -76,9 +76,11 @@ You can also install it with the following commands:
|
|||
- **Using Homebrew on macOS and Linux**
|
||||
|
||||
```bash
|
||||
brew install opencode
|
||||
brew install anomalyco/tap/opencode
|
||||
```
|
||||
|
||||
> We recommend using the OpenCode tap for the most up to date releases. The official `brew install opencode` formula is maintained by the Homebrew team and is updated less frequently.
|
||||
|
||||
- **Using Paru on Arch Linux**
|
||||
|
||||
```bash
|
||||
|
|
|
|||
|
|
@ -44,6 +44,29 @@ You can also disable a server by setting `enabled` to `false`. This is useful if
|
|||
|
||||
---
|
||||
|
||||
### Overriding remote defaults
|
||||
|
||||
Organizations can provide default MCP servers via their `.well-known/opencode` endpoint. These servers may be disabled by default, allowing users to opt-in to the ones they need.
|
||||
|
||||
To enable a specific server from your organization's remote config, add it to your local config with `enabled: true`:
|
||||
|
||||
```json title="opencode.json"
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"mcp": {
|
||||
"jira": {
|
||||
"type": "remote",
|
||||
"url": "https://jira.example.com/mcp",
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your local config values override the remote defaults. See [config precedence](/docs/config#precedence-order) for more details.
|
||||
|
||||
---
|
||||
|
||||
## Local
|
||||
|
||||
Add local MCP servers using `type` to `"local"` within the MCP object.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"name": "opencode",
|
||||
"displayName": "opencode",
|
||||
"description": "opencode for VS Code",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.6",
|
||||
"publisher": "sst-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
Loading…
Reference in New Issue