Compare commits
86 Commits
interleave
...
github-v1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1835d7526f | ||
|
|
946e4f0a61 | ||
|
|
ae60f41adf | ||
|
|
6b93d23642 | ||
|
|
cfa13df346 | ||
|
|
744a7159e4 | ||
|
|
80d1c62818 | ||
|
|
83aa42f510 | ||
|
|
183a1a181c | ||
|
|
bc7e7c2c4d | ||
|
|
7b5bd89570 | ||
|
|
ba1c6122b9 | ||
|
|
baed581a7c | ||
|
|
4a23052778 | ||
|
|
ee4190aa41 | ||
|
|
de8460cb99 | ||
|
|
f7b2beaaf1 | ||
|
|
9b0933187e | ||
|
|
862141e8b2 | ||
|
|
070ced0b3f | ||
|
|
cc3b699823 | ||
|
|
301f1a191b | ||
|
|
d149c25aab | ||
|
|
18d24b8f5f | ||
|
|
cf34981e8f | ||
|
|
e2ebe560ea | ||
|
|
6db822fd92 | ||
|
|
661122bab8 | ||
|
|
4a96836d11 | ||
|
|
e072f9605c | ||
|
|
9986031481 | ||
|
|
3d95848607 | ||
|
|
221c9028af | ||
|
|
b2057791aa | ||
|
|
c1ee6d6c41 | ||
|
|
a3fbbece9a | ||
|
|
e72c974c4c | ||
|
|
a762da7cab | ||
|
|
fa6c060324 | ||
|
|
8e33ac052b | ||
|
|
0759696ec0 | ||
|
|
59dce63471 | ||
|
|
1ae28090e3 | ||
|
|
0decdf6a55 | ||
|
|
09b402a274 | ||
|
|
150baf3e96 | ||
|
|
78c51371af | ||
|
|
6dbcacf3ea | ||
|
|
4ecebc2c83 | ||
|
|
38a79fa449 | ||
|
|
bafad6b8a8 | ||
|
|
5682dddd45 | ||
|
|
a9aacdb94a | ||
|
|
e7e32c946b | ||
|
|
fc9bc26d86 | ||
|
|
ee00b4e0ce | ||
|
|
f82156f0b1 | ||
|
|
2ed6298584 | ||
|
|
52ef8dea3e | ||
|
|
ebe6015db0 | ||
|
|
56526114e4 | ||
|
|
bb1c225027 | ||
|
|
e5af0dde08 | ||
|
|
3cf17bc24f | ||
|
|
4aa1b8de0e | ||
|
|
73e9534d08 | ||
|
|
cb188f907f | ||
|
|
63d9656ad8 | ||
|
|
3512d02e9e | ||
|
|
1efdceaf10 | ||
|
|
632a0fe009 | ||
|
|
6fb32cebec | ||
|
|
8b8b17d755 | ||
|
|
2c27afaaf5 | ||
|
|
4bdc7c1426 | ||
|
|
3c1e6c2c8f | ||
|
|
b8f5809f95 | ||
|
|
552ee81455 | ||
|
|
9fdbe193cd | ||
|
|
df64612d54 | ||
|
|
0aa3e6c270 | ||
|
|
44c17c1435 | ||
|
|
132e772c26 | ||
|
|
62cbed57cc | ||
|
|
ebab7e176e | ||
|
|
9c93853e22 |
@@ -1,4 +1,4 @@
|
||||
name: format
|
||||
name: generate
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -8,14 +8,10 @@ on:
|
||||
branches-ignore:
|
||||
- production
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: ["sdk"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
format:
|
||||
generate:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
@@ -29,9 +25,14 @@ jobs:
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: run
|
||||
- name: Generate SDK
|
||||
run: |
|
||||
./script/format.ts
|
||||
bun ./packages/sdk/js/script/build.ts
|
||||
(cd packages/opencode && bun dev generate > ../sdk/openapi.json)
|
||||
bun x prettier --write packages/sdk/openapi.json
|
||||
|
||||
- name: Format
|
||||
run: ./script/format.ts
|
||||
env:
|
||||
CI: true
|
||||
PUSH_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||
36
.github/workflows/publish.yml
vendored
@@ -2,11 +2,15 @@ name: publish
|
||||
run-name: "${{ format('release {0}', inputs.bump) }}"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- snapshot-*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
bump:
|
||||
description: "Bump major, minor, or patch"
|
||||
required: true
|
||||
required: false
|
||||
type: choice
|
||||
options:
|
||||
- major
|
||||
@@ -20,6 +24,7 @@ on:
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
@@ -34,20 +39,13 @@ jobs:
|
||||
|
||||
- run: git fetch --force --tags
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ">=1.24.0"
|
||||
cache: true
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Install makepkg
|
||||
- name: Setup SSH for AUR
|
||||
if: inputs.bump || inputs.version
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pacman-package-manager
|
||||
- name: Setup SSH for AUR
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
|
||||
chmod 600 ~/.ssh/id_rsa
|
||||
@@ -56,12 +54,9 @@ jobs:
|
||||
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
|
||||
|
||||
- name: Install OpenCode
|
||||
if: inputs.bump || inputs.version
|
||||
run: curl -fsSL https://opencode.ai/install | bash
|
||||
|
||||
- name: Setup npm auth
|
||||
run: |
|
||||
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -69,19 +64,24 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "24"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
./script/publish.ts
|
||||
env:
|
||||
OPENCODE_BUMP: ${{ inputs.bump }}
|
||||
OPENCODE_VERSION: ${{ inputs.version }}
|
||||
OPENCODE_CHANNEL: latest
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
NPM_CONFIG_PROVENANCE: false
|
||||
|
||||
publish-tauri:
|
||||
if: false # inputs.bump || inputs.version
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
43
.github/workflows/sdk.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: sdk
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- production
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- production
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
format:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
|
||||
ref: ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: run
|
||||
run: |
|
||||
bun ./packages/sdk/js/script/build.ts
|
||||
(cd packages/opencode && bun dev generate > ../sdk/openapi.json)
|
||||
bun x prettier --write packages/sdk/js/src/openapi.ts
|
||||
if [ -z "$(git status --porcelain)" ]; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add -A
|
||||
git commit -m "chore: regen sdk"
|
||||
git push origin HEAD:${PUSH_BRANCH} --no-verify
|
||||
env:
|
||||
CI: true
|
||||
PUSH_BRANCH: ${{ github.event.pull_request.head.ref || github.ref_name }}
|
||||
38
.github/workflows/snapshot.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: snapshot
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- test-bedrock
|
||||
- v0
|
||||
- otui-diffs
|
||||
- snapshot-*
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: blacksmith-4vcpu-ubuntu-2404
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- run: git fetch --force --tags
|
||||
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ">=1.24.0"
|
||||
cache: true
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Publish
|
||||
run: |
|
||||
./script/publish.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
4
.github/workflows/sync-zed-extension.yml
vendored
@@ -2,8 +2,8 @@ name: "sync-zed-extension"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
# release:
|
||||
# types: [published]
|
||||
|
||||
jobs:
|
||||
zed:
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
"instructions": ["STYLE_GUIDE.md"],
|
||||
"provider": {
|
||||
"opencode": {
|
||||
"options": {
|
||||
// "baseURL": "http://localhost:8080",
|
||||
},
|
||||
"options": {},
|
||||
},
|
||||
},
|
||||
"mcp": {
|
||||
|
||||
@@ -30,7 +30,7 @@ scoop bucket add extras; scoop install extras/opencode # Windows
|
||||
choco install opencode # Windows
|
||||
brew install opencode # macOS and Linux
|
||||
paru -S opencode-bin # Arch Linux
|
||||
mise use --pin -g ubi:sst/opencode # Any OS
|
||||
mise use -g ubi:sst/opencode # Any OS
|
||||
nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch
|
||||
```
|
||||
|
||||
|
||||
1
STATS.md
@@ -165,3 +165,4 @@
|
||||
| 2025-12-07 | 994,046 (+6,162) | 951,425 (+7,652) | 1,945,471 (+13,814) |
|
||||
| 2025-12-08 | 1,000,898 (+6,852) | 957,149 (+5,724) | 1,958,047 (+12,576) |
|
||||
| 2025-12-09 | 1,011,488 (+10,590) | 973,922 (+16,773) | 1,985,410 (+27,363) |
|
||||
| 2025-12-10 | 1,025,891 (+14,403) | 991,708 (+17,786) | 2,017,599 (+32,189) |
|
||||
|
||||
50
bun.lock
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -48,7 +48,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -99,7 +99,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -123,7 +123,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -168,7 +168,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
@@ -196,7 +196,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -212,7 +212,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -242,8 +242,8 @@
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "1.2.8",
|
||||
"@opentui/core": "0.1.59",
|
||||
"@opentui/solid": "0.1.59",
|
||||
"@opentui/core": "0.1.60",
|
||||
"@opentui/solid": "0.1.60",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@pierre/precision-diffs": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
@@ -304,7 +304,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"zod": "catalog:",
|
||||
@@ -324,7 +324,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"devDependencies": {
|
||||
"@hey-api/openapi-ts": "0.88.1",
|
||||
"@tsconfig/node22": "catalog:",
|
||||
@@ -335,7 +335,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -348,7 +348,7 @@
|
||||
},
|
||||
"packages/tauri": {
|
||||
"name": "@opencode-ai/tauri",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@opencode-ai/desktop": "workspace:*",
|
||||
"@tauri-apps/api": "^2",
|
||||
@@ -370,7 +370,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -402,7 +402,7 @@
|
||||
},
|
||||
"packages/util": {
|
||||
"name": "@opencode-ai/util",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -413,7 +413,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1147,21 +1147,21 @@
|
||||
|
||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||
|
||||
"@opentui/core": ["@opentui/core@0.1.59", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.59", "@opentui/core-darwin-x64": "0.1.59", "@opentui/core-linux-arm64": "0.1.59", "@opentui/core-linux-x64": "0.1.59", "@opentui/core-win32-arm64": "0.1.59", "@opentui/core-win32-x64": "0.1.59", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vOtEvIulvfCOWJy0EfKAPzAMtDTmC+S0boGYrefjLqIp7tp+bbVJuXVh/8bz6GQTPmbQC6MIk6bv/ij3pdUVkA=="],
|
||||
"@opentui/core": ["@opentui/core@0.1.60", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.60", "@opentui/core-darwin-x64": "0.1.60", "@opentui/core-linux-arm64": "0.1.60", "@opentui/core-linux-x64": "0.1.60", "@opentui/core-win32-arm64": "0.1.60", "@opentui/core-win32-x64": "0.1.60", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-28jphd0AJo48uvEuKXcT9pJhgAu8I2rEJhPt25cc5ipJ2iw/eDk1uoxrbID80MPDqgOEzN21vXmzXwCd6ao+hg=="],
|
||||
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.59", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JQWq7W/wkmTujW/2/Ig0d7S+701rul87LSW5txQ+GM4o6EWchqHrELwo6jcZpczsyOEj4fXxI2O8l4OVYyMa9A=="],
|
||||
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.60", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N4feqnOBDA4O4yocpat5vOiV06HqJVwJGx8rEZE9DiOtl1i+1cPQ1Lx6+zWdLhbrVBJ0ENhb7Azox8sXkm/+5Q=="],
|
||||
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.59", "", { "os": "darwin", "cpu": "x64" }, "sha512-GzafWzMP9Lt4AzUwQAk02lxgITgfvvo33OLCN265LtQBO8w23u0eB7Fjs9W+nmtcvzXtB9q6HuA0PvP9a3OioA=="],
|
||||
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.60", "", { "os": "darwin", "cpu": "x64" }, "sha512-+z3q4WaoIs7ANU8+eTFlvnfCjAS81rk81TOdZm4TJ53Ti3/B+yheWtnV/mLpLLhvZDz2VUVxxRmfDrGMnJb4fQ=="],
|
||||
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.59", "", { "os": "linux", "cpu": "arm64" }, "sha512-QMMFg3dr2v43g3jICgzNFYQyU4YL3zHw733MVJINC+c882+qiQ8l0utTFoVEx/iRYeBzFvMVrKZ4f6G8fFrtrw=="],
|
||||
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.60", "", { "os": "linux", "cpu": "arm64" }, "sha512-/Q65sjqVGB9ygJ6lStI8n1X6RyfmJZC8XofRGEuFiMLiWcWC/xoBtztdL8LAIvHQy42y2+pl9zIiW0fWSQ0wjw=="],
|
||||
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.59", "", { "os": "linux", "cpu": "x64" }, "sha512-XSblVjhW/7+Xs+/o+xJHwHn74nw9j69mnPAFiNdH0d8ilP4j09nUYHZOvQ89sHZaMYeSIuJEciHnh/qP0n5QXQ=="],
|
||||
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.60", "", { "os": "linux", "cpu": "x64" }, "sha512-AegF+g7OguIpjZKN+PS55sc3ZFY6fj+fLwfETbSRGw6NqX+aiwpae0Y3gXX1s298Yq5yQEzMXnARTCJTGH4uzg=="],
|
||||
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.59", "", { "os": "win32", "cpu": "arm64" }, "sha512-GU5pPUcTpYmeOUYKpQgAPx0VKBMrfz5LNZlK8gm/jlo2CbLrIW7QLMWCoxncVZmNYqYJeG+KUZkmXYe5KLPXCQ=="],
|
||||
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.60", "", { "os": "win32", "cpu": "arm64" }, "sha512-fbkq8MOZJgT3r9q3JWqsfVxRpQ1SlbmhmvB35BzukXnZBK8eA178wbSadGH6irMDrkSIYye9WYddHI/iXjmgVQ=="],
|
||||
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.59", "", { "os": "win32", "cpu": "x64" }, "sha512-InIawEI0TOG8MBBpavMq31WBRBjJ6XPuqFcsDnjqDJcXrRbNkguRW3PNXEwlyaU4tXHfYOsdlPpRtsysS8X/bQ=="],
|
||||
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.60", "", { "os": "win32", "cpu": "x64" }, "sha512-OebCL7f9+CKodBw0G+NvKIcc74bl6/sBEHfb73cACdJDJKh+T3C3Vt9H3kQQ0m1C8wRAqX6rh706OArk1pUb2A=="],
|
||||
|
||||
"@opentui/solid": ["@opentui/solid@0.1.59", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.59", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-O88a/+YHkHlDC4IxbrfWD2ZWlpkpu4oXC2FCLTK8taaUAnLYoybxdrMpv1+o8u8KoWXOoZmEHdntdO9O4abHnQ=="],
|
||||
"@opentui/solid": ["@opentui/solid@0.1.60", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.60", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.9", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.9" } }, "sha512-pn91stzAHNGWaNL6h39q55bq3G1/DLqxKtT3wVsRAV68dHfPpwmqikX1nEJZK8OU84ZTPS9Ly9fz8po2Mot2uQ=="],
|
||||
|
||||
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
|
||||
|
||||
|
||||
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764947035,
|
||||
"narHash": "sha256-EYHSjVM4Ox4lvCXUMiKKs2vETUSL5mx+J2FfutM7T9w=",
|
||||
"lastModified": 1765270179,
|
||||
"narHash": "sha256-g2a4MhRKu4ymR4xwo+I+auTknXt/+j37Lnf0Mvfl1rE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a672be65651c80d3f592a89b3945466584a22069",
|
||||
"rev": "677fbe97984e7af3175b6c121f3c39ee5c8d62c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"nodeModules": "sha256-lM/7mkrPHz5E6YOMjWspfRhKjwav9ANrLt9kYlpPkEI="
|
||||
"nodeModules": "sha256-pwjePZClFKDhDr5xrTNCBlO0xTwxLQYiQYaIEd0FdQQ="
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -588,7 +588,7 @@ export async function handler(
|
||||
tx
|
||||
.update(KeyTable)
|
||||
.set({ timeUsed: sql`now()` })
|
||||
.where(eq(KeyTable.id, authInfo.apiKeyId)),
|
||||
.where(and(eq(KeyTable.workspaceID, authInfo.workspaceID), eq(KeyTable.id, authInfo.apiKeyId))),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
@@ -8,7 +8,7 @@
|
||||
"./vite": "./vite.js"
|
||||
},
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"typecheck": "tsgo -b",
|
||||
"start": "vite",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useFilteredList } from "@opencode-ai/ui/hooks"
|
||||
import { createEffect, on, Component, Show, For, onMount, onCleanup, Switch, Match } from "solid-js"
|
||||
import { createEffect, on, Component, Show, For, onMount, onCleanup, Switch, Match, createSignal } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createFocusSignal } from "@solid-primitives/active-element"
|
||||
import { useLocal } from "@/context/local"
|
||||
@@ -14,14 +14,44 @@ import { Button } from "@opencode-ai/ui/button"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { Tooltip } from "@opencode-ai/ui/tooltip"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||
import { Select } from "@opencode-ai/ui/select"
|
||||
import { getDirectory, getFilename } from "@opencode-ai/util/path"
|
||||
import { type IconName } from "@opencode-ai/ui/icons/provider"
|
||||
|
||||
interface PromptInputProps {
|
||||
class?: string
|
||||
ref?: (el: HTMLDivElement) => void
|
||||
}
|
||||
|
||||
const PLACEHOLDERS = [
|
||||
"Fix a TODO in the codebase",
|
||||
"What is the tech stack of this project?",
|
||||
"Fix broken tests",
|
||||
"Explain how authentication works",
|
||||
"Find and fix security vulnerabilities",
|
||||
"Add unit tests for the user service",
|
||||
"Refactor this function to be more readable",
|
||||
"What does this error mean?",
|
||||
"Help me debug this issue",
|
||||
"Generate API documentation",
|
||||
"Optimize database queries",
|
||||
"Add input validation",
|
||||
"Create a new component for...",
|
||||
"How do I deploy this project?",
|
||||
"Review my code for best practices",
|
||||
"Add error handling to this function",
|
||||
"Explain this regex pattern",
|
||||
"Convert this to TypeScript",
|
||||
"Add logging throughout the codebase",
|
||||
"What dependencies are outdated?",
|
||||
"Help me write a migration script",
|
||||
"Implement caching for this endpoint",
|
||||
"Add pagination to this list",
|
||||
"Create a CLI command for...",
|
||||
"How do environment variables work here?",
|
||||
]
|
||||
|
||||
export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
const navigate = useNavigate()
|
||||
const sdk = useSDK()
|
||||
@@ -36,6 +66,15 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
popoverIsOpen: false,
|
||||
})
|
||||
|
||||
const [placeholder, setPlaceholder] = createSignal(Math.floor(Math.random() * PLACEHOLDERS.length))
|
||||
|
||||
onMount(() => {
|
||||
const interval = setInterval(() => {
|
||||
setPlaceholder((prev) => (prev + 1) % PLACEHOLDERS.length)
|
||||
}, 6500)
|
||||
onCleanup(() => clearInterval(interval))
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
session.id
|
||||
editorRef.focus()
|
||||
@@ -68,7 +107,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
|
||||
const handleFileSelect = (path: string | undefined) => {
|
||||
if (!path) return
|
||||
addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 })
|
||||
addPart({ type: "file", path, content: "@" + path, start: 0, end: 0 })
|
||||
}
|
||||
|
||||
const { flat, active, onInput, onKeyDown, refetch } = useFilteredList<string>({
|
||||
@@ -403,7 +442,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
/>
|
||||
<Show when={!session.prompt.dirty()}>
|
||||
<div class="absolute top-0 left-0 px-5 py-3 text-14-regular text-text-weak pointer-events-none">
|
||||
Plan and build anything
|
||||
Ask anything... "{PLACEHOLDERS[placeholder()]}"
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
@@ -449,7 +488,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||
{(i) => (
|
||||
<div class="w-full flex items-center justify-between gap-x-3">
|
||||
<div class="flex items-center gap-x-2.5 text-text-muted grow min-w-0">
|
||||
<img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-6 p-0.5 shrink-0" />
|
||||
<ProviderIcon name={i.provider.id as IconName} class="size-6 p-0.5 shrink-0" />
|
||||
<div class="flex gap-x-3 items-baseline flex-[1_0_0]">
|
||||
<span class="text-14-medium text-text-strong overflow-hidden text-ellipsis">{i.name}</span>
|
||||
<Show when={false}>
|
||||
|
||||
@@ -18,6 +18,38 @@ import { Binary } from "@opencode-ai/util/binary"
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { useGlobalSDK } from "./global-sdk"
|
||||
|
||||
const PASTEL_COLORS = [
|
||||
"#FCEAFD", // pastel pink
|
||||
"#FFDFBA", // pastel peach
|
||||
"#FFFFBA", // pastel yellow
|
||||
"#BAFFC9", // pastel green
|
||||
"#EAF6FD", // pastel blue
|
||||
"#EFEAFD", // pastel lavender
|
||||
"#FEC8D8", // pastel rose
|
||||
"#D4F0F0", // pastel cyan
|
||||
"#FDF0EA", // pastel coral
|
||||
"#C1E1C1", // pastel mint
|
||||
]
|
||||
|
||||
function pickAvailableColor(usedColors: Set<string>) {
|
||||
const available = PASTEL_COLORS.filter((c) => !usedColors.has(c))
|
||||
if (available.length === 0) return PASTEL_COLORS[Math.floor(Math.random() * PASTEL_COLORS.length)]
|
||||
return available[Math.floor(Math.random() * available.length)]
|
||||
}
|
||||
|
||||
async function ensureProjectColor(
|
||||
project: Project,
|
||||
sdk: ReturnType<typeof useGlobalSDK>,
|
||||
usedColors: Set<string>,
|
||||
): Promise<Project> {
|
||||
if (project.icon?.color) return project
|
||||
const color = pickAvailableColor(usedColors)
|
||||
usedColors.add(color)
|
||||
const updated = { ...project, icon: { ...project.icon, color } }
|
||||
sdk.client.project.update({ projectID: project.id, icon: { color } })
|
||||
return updated
|
||||
}
|
||||
|
||||
type State = {
|
||||
ready: boolean
|
||||
provider: Provider[]
|
||||
@@ -92,17 +124,20 @@ export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimple
|
||||
if (directory === "global") {
|
||||
switch (event.type) {
|
||||
case "project.updated": {
|
||||
const result = Binary.search(globalStore.projects, event.properties.id, (s) => s.id)
|
||||
if (result.found) {
|
||||
setGlobalStore("projects", result.index, reconcile(event.properties))
|
||||
break
|
||||
}
|
||||
setGlobalStore(
|
||||
"projects",
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, event.properties)
|
||||
}),
|
||||
)
|
||||
const usedColors = new Set(globalStore.projects.map((p) => p.icon?.color).filter(Boolean) as string[])
|
||||
ensureProjectColor(event.properties, sdk, usedColors).then((project) => {
|
||||
const result = Binary.search(globalStore.projects, project.id, (s) => s.id)
|
||||
if (result.found) {
|
||||
setGlobalStore("projects", result.index, reconcile(project))
|
||||
return
|
||||
}
|
||||
setGlobalStore(
|
||||
"projects",
|
||||
produce((draft) => {
|
||||
draft.splice(result.index, 0, project)
|
||||
}),
|
||||
)
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -180,14 +215,15 @@ export const { use: useGlobalSync, provider: GlobalSyncProvider } = createSimple
|
||||
})
|
||||
|
||||
Promise.all([
|
||||
sdk.client.project.list().then((x) =>
|
||||
sdk.client.project.list().then(async (x) => {
|
||||
const filtered = x.data!.filter((p) => !p.worktree.includes("opencode-test") && p.vcs)
|
||||
const usedColors = new Set(filtered.map((p) => p.icon?.color).filter(Boolean) as string[])
|
||||
const projects = await Promise.all(filtered.map((p) => ensureProjectColor(p, sdk, usedColors)))
|
||||
setGlobalStore(
|
||||
"projects",
|
||||
x
|
||||
.data!.filter((x) => !x.worktree.includes("opencode-test") && x.vcs)
|
||||
.sort((a, b) => a.id.localeCompare(b.id)),
|
||||
),
|
||||
),
|
||||
projects.sort((a, b) => a.id.localeCompare(b.id)),
|
||||
)
|
||||
}),
|
||||
]).then(() => setGlobalStore("ready", true))
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { createEffect, createMemo, ErrorBoundary, For, Match, Show, Switch } fro
|
||||
import { Share } from "~/core/share"
|
||||
import { Logo, Mark } from "@opencode-ai/ui/logo"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
|
||||
import { createDefaultOptions } from "@opencode-ai/ui/pierre"
|
||||
import { iife } from "@opencode-ai/util/iife"
|
||||
import { Binary } from "@opencode-ai/util/binary"
|
||||
@@ -21,6 +22,7 @@ import { Tabs } from "@opencode-ai/ui/tabs"
|
||||
import { preloadMultiFileDiff, PreloadMultiFileDiffResult } from "@pierre/precision-diffs/ssr"
|
||||
import { Diff as SSRDiff } from "@opencode-ai/ui/diff-ssr"
|
||||
import { clientOnly } from "@solidjs/start"
|
||||
import { type IconName } from "@opencode-ai/ui/icons/provider"
|
||||
|
||||
const ClientOnlyDiff = clientOnly(() => import("@opencode-ai/ui/diff").then((m) => ({ default: m.Diff })))
|
||||
|
||||
@@ -210,10 +212,7 @@ export default function () {
|
||||
<div class="text-12-mono text-text-base">v{info().version}</div>
|
||||
</div>
|
||||
<div class="flex gap-2 items-center">
|
||||
<img
|
||||
src={`https://models.dev/logos/${provider()}.svg`}
|
||||
class="size-3.5 shrink-0 dark:invert"
|
||||
/>
|
||||
<ProviderIcon name={provider() as IconName} class="size-3.5 shrink-0 text-icon-strong-base" />
|
||||
<div class="text-12-regular text-text-base">{model()?.name ?? modelID()}</div>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-weaker">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
id = "opencode"
|
||||
name = "OpenCode"
|
||||
description = "The open source coding agent."
|
||||
version = "1.0.138"
|
||||
version = "1.0.143"
|
||||
schema_version = 1
|
||||
authors = ["Anomaly"]
|
||||
repository = "https://github.com/sst/opencode"
|
||||
@@ -11,26 +11,26 @@ name = "OpenCode"
|
||||
icon = "./icons/opencode.svg"
|
||||
|
||||
[agent_servers.opencode.targets.darwin-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.138/opencode-darwin-arm64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-darwin-arm64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.darwin-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.138/opencode-darwin-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-darwin-x64.zip"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-aarch64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.138/opencode-linux-arm64.tar.gz"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-linux-arm64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.linux-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.138/opencode-linux-x64.tar.gz"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-linux-x64.tar.gz"
|
||||
cmd = "./opencode"
|
||||
args = ["acp"]
|
||||
|
||||
[agent_servers.opencode.targets.windows-x86_64]
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.138/opencode-windows-x64.zip"
|
||||
archive = "https://github.com/sst/opencode/releases/download/v1.0.143/opencode-windows-x64.zip"
|
||||
cmd = "./opencode.exe"
|
||||
args = ["acp"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
@@ -9,7 +9,12 @@
|
||||
"test": "bun test",
|
||||
"build": "./script/build.ts",
|
||||
"dev": "bun run --conditions=browser ./src/index.ts",
|
||||
"random": "echo 'Random script updated at $(date)'"
|
||||
"random": "echo 'Random script updated at $(date)' && echo 'Change queued successfully' && echo 'Another change made' && echo 'Yet another change' && echo 'One more change' && echo 'Final change' && echo 'Another final change' && echo 'Yet another final change'",
|
||||
"clean": "echo 'Cleaning up...' && rm -rf node_modules dist",
|
||||
"lint": "echo 'Running lint checks...' && bun test --coverage",
|
||||
"format": "echo 'Formatting code...' && bun run --prettier --write src/**/*.ts",
|
||||
"docs": "echo 'Generating documentation...' && find src -name '*.ts' -exec echo 'Processing: {}' \\;",
|
||||
"deploy": "echo 'Deploying application...' && bun run build && echo 'Deployment completed successfully'"
|
||||
},
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode"
|
||||
@@ -66,8 +71,8 @@
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@opencode-ai/util": "workspace:*",
|
||||
"@openrouter/ai-sdk-provider": "1.2.8",
|
||||
"@opentui/core": "0.1.59",
|
||||
"@opentui/solid": "0.1.59",
|
||||
"@opentui/core": "0.1.60",
|
||||
"@opentui/solid": "0.1.60",
|
||||
"@parcel/watcher": "2.5.1",
|
||||
"@pierre/precision-diffs": "catalog:",
|
||||
"@solid-primitives/event-bus": "1.1.2",
|
||||
|
||||
@@ -35,26 +35,21 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
|
||||
2,
|
||||
),
|
||||
)
|
||||
for (const [name] of Object.entries(binaries)) {
|
||||
try {
|
||||
process.chdir(`./dist/${name}`)
|
||||
if (process.platform !== "win32") {
|
||||
await $`chmod 755 -R .`
|
||||
}
|
||||
await $`bun publish --access public --tag ${Script.channel}`
|
||||
} finally {
|
||||
process.chdir(dir)
|
||||
}
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && bun publish --access public --tag ${Script.channel}`
|
||||
|
||||
if (!Script.preview) {
|
||||
const major = Script.version.split(".")[0]
|
||||
const majorTag = `latest-${major}`
|
||||
for (const [name] of Object.entries(binaries)) {
|
||||
await $`cd dist/${name} && npm dist-tag add ${name}@${Script.version} ${majorTag}`
|
||||
const tags = [Script.channel]
|
||||
|
||||
const tasks = Object.entries(binaries).map(async ([name]) => {
|
||||
if (process.platform !== "win32") {
|
||||
await $`chmod 755 -R .`.cwd(`./dist/${name}`)
|
||||
}
|
||||
await $`cd ./dist/${pkg.name} && npm dist-tag add ${pkg.name}-ai@${Script.version} ${majorTag}`
|
||||
await $`bun pm pack`.cwd(`./dist/${name}`)
|
||||
for (const tag of tags) {
|
||||
await $`npm publish *.tgz --access public --tag ${tag}`.cwd(`./dist/${name}`)
|
||||
}
|
||||
})
|
||||
await Promise.all(tasks)
|
||||
for (const tag of tags) {
|
||||
await $`cd ./dist/${pkg.name} && bun pm pack && npm publish *.tgz --access public --tag ${tag}`
|
||||
}
|
||||
|
||||
if (!Script.preview) {
|
||||
|
||||
@@ -25,7 +25,6 @@ import { Provider } from "../provider/provider"
|
||||
import { Installation } from "@/installation"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
import { Config } from "@/config/config"
|
||||
import { MCP } from "@/mcp"
|
||||
import { Todo } from "@/session/todo"
|
||||
import { z } from "zod"
|
||||
import { LoadAPIKeyError } from "ai"
|
||||
|
||||
@@ -252,7 +252,7 @@ export namespace Agent {
|
||||
},
|
||||
},
|
||||
temperature: 0.3,
|
||||
prompt: [
|
||||
messages: [
|
||||
...system.map(
|
||||
(item): ModelMessage => ({
|
||||
role: "system",
|
||||
|
||||
@@ -4,131 +4,215 @@ import { UI } from "../ui"
|
||||
import { Global } from "../../global"
|
||||
import { Agent } from "../../agent/agent"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import matter from "gray-matter"
|
||||
import { Instance } from "../../project/instance"
|
||||
import { EOL } from "os"
|
||||
import type { Argv } from "yargs"
|
||||
|
||||
type AgentMode = "all" | "primary" | "subagent"
|
||||
|
||||
const AVAILABLE_TOOLS = [
|
||||
"bash",
|
||||
"read",
|
||||
"write",
|
||||
"edit",
|
||||
"list",
|
||||
"glob",
|
||||
"grep",
|
||||
"webfetch",
|
||||
"task",
|
||||
"todowrite",
|
||||
"todoread",
|
||||
]
|
||||
|
||||
const AgentCreateCommand = cmd({
|
||||
command: "create",
|
||||
describe: "create a new agent",
|
||||
async handler() {
|
||||
builder: (yargs: Argv) =>
|
||||
yargs
|
||||
.option("path", {
|
||||
type: "string",
|
||||
describe: "directory path to generate the agent file",
|
||||
})
|
||||
.option("description", {
|
||||
type: "string",
|
||||
describe: "what the agent should do",
|
||||
})
|
||||
.option("mode", {
|
||||
type: "string",
|
||||
describe: "agent mode",
|
||||
choices: ["all", "primary", "subagent"] as const,
|
||||
})
|
||||
.option("tools", {
|
||||
type: "string",
|
||||
describe: `comma-separated list of tools to enable (default: all). Available: "${AVAILABLE_TOOLS.join(", ")}"`,
|
||||
}),
|
||||
async handler(args) {
|
||||
await Instance.provide({
|
||||
directory: process.cwd(),
|
||||
async fn() {
|
||||
UI.empty()
|
||||
prompts.intro("Create agent")
|
||||
const project = Instance.project
|
||||
const cliPath = args.path
|
||||
const cliDescription = args.description
|
||||
const cliMode = args.mode as AgentMode | undefined
|
||||
const cliTools = args.tools
|
||||
|
||||
let scope: "global" | "project" = "global"
|
||||
if (project.vcs === "git") {
|
||||
const scopeResult = await prompts.select({
|
||||
message: "Location",
|
||||
options: [
|
||||
{
|
||||
label: "Current project",
|
||||
value: "project" as const,
|
||||
hint: Instance.worktree,
|
||||
},
|
||||
{
|
||||
label: "Global",
|
||||
value: "global" as const,
|
||||
hint: Global.Path.config,
|
||||
},
|
||||
],
|
||||
})
|
||||
if (prompts.isCancel(scopeResult)) throw new UI.CancelledError()
|
||||
scope = scopeResult
|
||||
const isFullyNonInteractive = cliPath && cliDescription && cliMode && cliTools !== undefined
|
||||
|
||||
if (!isFullyNonInteractive) {
|
||||
UI.empty()
|
||||
prompts.intro("Create agent")
|
||||
}
|
||||
|
||||
const query = await prompts.text({
|
||||
message: "Description",
|
||||
placeholder: "What should this agent do?",
|
||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||
})
|
||||
if (prompts.isCancel(query)) throw new UI.CancelledError()
|
||||
const project = Instance.project
|
||||
|
||||
// Determine scope/path
|
||||
let targetPath: string
|
||||
if (cliPath) {
|
||||
targetPath = path.join(cliPath, "agent")
|
||||
} else {
|
||||
let scope: "global" | "project" = "global"
|
||||
if (project.vcs === "git") {
|
||||
const scopeResult = await prompts.select({
|
||||
message: "Location",
|
||||
options: [
|
||||
{
|
||||
label: "Current project",
|
||||
value: "project" as const,
|
||||
hint: Instance.worktree,
|
||||
},
|
||||
{
|
||||
label: "Global",
|
||||
value: "global" as const,
|
||||
hint: Global.Path.config,
|
||||
},
|
||||
],
|
||||
})
|
||||
if (prompts.isCancel(scopeResult)) throw new UI.CancelledError()
|
||||
scope = scopeResult
|
||||
}
|
||||
targetPath = path.join(
|
||||
scope === "global" ? Global.Path.config : path.join(Instance.worktree, ".opencode"),
|
||||
"agent",
|
||||
)
|
||||
}
|
||||
|
||||
// Get description
|
||||
let description: string
|
||||
if (cliDescription) {
|
||||
description = cliDescription
|
||||
} else {
|
||||
const query = await prompts.text({
|
||||
message: "Description",
|
||||
placeholder: "What should this agent do?",
|
||||
validate: (x) => (x && x.length > 0 ? undefined : "Required"),
|
||||
})
|
||||
if (prompts.isCancel(query)) throw new UI.CancelledError()
|
||||
description = query
|
||||
}
|
||||
|
||||
// Generate agent
|
||||
const spinner = prompts.spinner()
|
||||
|
||||
spinner.start("Generating agent configuration...")
|
||||
const generated = await Agent.generate({ description: query }).catch((error) => {
|
||||
const generated = await Agent.generate({ description }).catch((error) => {
|
||||
spinner.stop(`LLM failed to generate agent: ${error.message}`, 1)
|
||||
if (isFullyNonInteractive) process.exit(1)
|
||||
throw new UI.CancelledError()
|
||||
})
|
||||
spinner.stop(`Agent ${generated.identifier} generated`)
|
||||
|
||||
const availableTools = [
|
||||
"bash",
|
||||
"read",
|
||||
"write",
|
||||
"edit",
|
||||
"list",
|
||||
"glob",
|
||||
"grep",
|
||||
"webfetch",
|
||||
"task",
|
||||
"todowrite",
|
||||
"todoread",
|
||||
]
|
||||
// Select tools
|
||||
let selectedTools: string[]
|
||||
if (cliTools !== undefined) {
|
||||
selectedTools = cliTools ? cliTools.split(",").map((t) => t.trim()) : AVAILABLE_TOOLS
|
||||
} else {
|
||||
const result = await prompts.multiselect({
|
||||
message: "Select tools to enable",
|
||||
options: AVAILABLE_TOOLS.map((tool) => ({
|
||||
label: tool,
|
||||
value: tool,
|
||||
})),
|
||||
initialValues: AVAILABLE_TOOLS,
|
||||
})
|
||||
if (prompts.isCancel(result)) throw new UI.CancelledError()
|
||||
selectedTools = result
|
||||
}
|
||||
|
||||
const selectedTools = await prompts.multiselect({
|
||||
message: "Select tools to enable",
|
||||
options: availableTools.map((tool) => ({
|
||||
label: tool,
|
||||
value: tool,
|
||||
})),
|
||||
initialValues: availableTools,
|
||||
})
|
||||
if (prompts.isCancel(selectedTools)) throw new UI.CancelledError()
|
||||
|
||||
const modeResult = await prompts.select({
|
||||
message: "Agent mode",
|
||||
options: [
|
||||
{
|
||||
label: "All",
|
||||
value: "all" as const,
|
||||
hint: "Can function in both primary and subagent roles",
|
||||
},
|
||||
{
|
||||
label: "Primary",
|
||||
value: "primary" as const,
|
||||
hint: "Acts as a primary/main agent",
|
||||
},
|
||||
{
|
||||
label: "Subagent",
|
||||
value: "subagent" as const,
|
||||
hint: "Can be used as a subagent by other agents",
|
||||
},
|
||||
],
|
||||
initialValue: "all",
|
||||
})
|
||||
if (prompts.isCancel(modeResult)) throw new UI.CancelledError()
|
||||
// Get mode
|
||||
let mode: AgentMode
|
||||
if (cliMode) {
|
||||
mode = cliMode
|
||||
} else {
|
||||
const modeResult = await prompts.select({
|
||||
message: "Agent mode",
|
||||
options: [
|
||||
{
|
||||
label: "All",
|
||||
value: "all" as const,
|
||||
hint: "Can function in both primary and subagent roles",
|
||||
},
|
||||
{
|
||||
label: "Primary",
|
||||
value: "primary" as const,
|
||||
hint: "Acts as a primary/main agent",
|
||||
},
|
||||
{
|
||||
label: "Subagent",
|
||||
value: "subagent" as const,
|
||||
hint: "Can be used as a subagent by other agents",
|
||||
},
|
||||
],
|
||||
initialValue: "all" as const,
|
||||
})
|
||||
if (prompts.isCancel(modeResult)) throw new UI.CancelledError()
|
||||
mode = modeResult
|
||||
}
|
||||
|
||||
// Build tools config
|
||||
const tools: Record<string, boolean> = {}
|
||||
for (const tool of availableTools) {
|
||||
for (const tool of AVAILABLE_TOOLS) {
|
||||
if (!selectedTools.includes(tool)) {
|
||||
tools[tool] = false
|
||||
}
|
||||
}
|
||||
|
||||
const frontmatter: any = {
|
||||
// Build frontmatter
|
||||
const frontmatter: {
|
||||
description: string
|
||||
mode: AgentMode
|
||||
tools?: Record<string, boolean>
|
||||
} = {
|
||||
description: generated.whenToUse,
|
||||
mode: modeResult,
|
||||
mode,
|
||||
}
|
||||
if (Object.keys(tools).length > 0) {
|
||||
frontmatter.tools = tools
|
||||
}
|
||||
|
||||
// Write file
|
||||
const content = matter.stringify(generated.systemPrompt, frontmatter)
|
||||
const filePath = path.join(
|
||||
scope === "global" ? Global.Path.config : path.join(Instance.worktree, ".opencode"),
|
||||
`agent`,
|
||||
`${generated.identifier}.md`,
|
||||
)
|
||||
const filePath = path.join(targetPath, `${generated.identifier}.md`)
|
||||
|
||||
await fs.mkdir(targetPath, { recursive: true })
|
||||
|
||||
const file = Bun.file(filePath)
|
||||
if (await file.exists()) {
|
||||
if (isFullyNonInteractive) {
|
||||
console.error(`Error: Agent file already exists: ${filePath}`)
|
||||
process.exit(1)
|
||||
}
|
||||
prompts.log.error(`Agent file already exists: ${filePath}`)
|
||||
throw new UI.CancelledError()
|
||||
}
|
||||
|
||||
await Bun.write(filePath, content)
|
||||
|
||||
prompts.log.success(`Agent created: ${filePath}`)
|
||||
prompts.outro("Done")
|
||||
if (isFullyNonInteractive) {
|
||||
console.log(filePath)
|
||||
} else {
|
||||
prompts.log.success(`Agent created: ${filePath}`)
|
||||
prompts.outro("Done")
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@@ -144,7 +144,7 @@ export function tui(input: { url: string; args: Args; onExit?: () => Promise<voi
|
||||
targetFps: 60,
|
||||
gatherStats: false,
|
||||
exitOnCtrlC: false,
|
||||
useKittyKeyboard: true,
|
||||
useKittyKeyboard: {},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
@@ -199,7 +199,7 @@ export function Prompt(props: PromptProps) {
|
||||
const content = await Editor.open({ value, renderer })
|
||||
if (!content) return
|
||||
|
||||
input.setText(content, { history: false })
|
||||
input.setText(content)
|
||||
|
||||
// Update positions for nonTextParts based on their location in new content
|
||||
// Filter out parts whose virtual text was deleted
|
||||
@@ -390,7 +390,7 @@ export function Prompt(props: PromptProps) {
|
||||
input.blur()
|
||||
},
|
||||
set(prompt) {
|
||||
input.setText(prompt.input, { history: false })
|
||||
input.setText(prompt.input)
|
||||
setStore("prompt", prompt)
|
||||
restoreExtmarksFromParts(prompt.parts)
|
||||
input.gotoBufferEnd()
|
||||
@@ -410,6 +410,11 @@ export function Prompt(props: PromptProps) {
|
||||
if (props.disabled) return
|
||||
if (autocomplete.visible) return
|
||||
if (!store.prompt.input) return
|
||||
const trimmed = store.prompt.input.trim()
|
||||
if (trimmed === "exit" || trimmed === "quit" || trimmed === ":q") {
|
||||
exit()
|
||||
return
|
||||
}
|
||||
const selectedModel = local.model.current()
|
||||
if (!selectedModel) {
|
||||
promptModelWarning()
|
||||
@@ -683,17 +688,6 @@ export function Prompt(props: PromptProps) {
|
||||
setStore("extmarkToPartIndex", new Map())
|
||||
return
|
||||
}
|
||||
if (keybind.match("input_forward_delete", e) && store.prompt.input !== "") {
|
||||
const cursorOffset = input.cursorOffset
|
||||
if (cursorOffset < input.plainText.length) {
|
||||
const text = input.plainText
|
||||
const newText = text.slice(0, cursorOffset) + text.slice(cursorOffset + 1)
|
||||
input.setText(newText)
|
||||
input.cursorOffset = cursorOffset
|
||||
}
|
||||
e.preventDefault()
|
||||
return
|
||||
}
|
||||
if (keybind.match("app_exit", e)) {
|
||||
await exit()
|
||||
return
|
||||
@@ -720,7 +714,7 @@ export function Prompt(props: PromptProps) {
|
||||
const item = history.move(direction, input.plainText)
|
||||
|
||||
if (item) {
|
||||
input.setText(item.input, { history: false })
|
||||
input.setText(item.input)
|
||||
setStore("prompt", item)
|
||||
restoreExtmarksFromParts(item.parts)
|
||||
e.preventDefault()
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"darkAccent": "#FFF7F1",
|
||||
"darkRed": "#e06c75",
|
||||
"darkOrange": "#EC5B2B",
|
||||
"darkGreen": "#7fd88f",
|
||||
"darkBlue": "#6ba1e6",
|
||||
"darkCyan": "#56b6c2",
|
||||
"darkYellow": "#e5c07b",
|
||||
"lightStep1": "#ffffff",
|
||||
@@ -36,7 +36,7 @@
|
||||
"lightAccent": "#c94d24",
|
||||
"lightRed": "#d1383d",
|
||||
"lightOrange": "#EC5B2B",
|
||||
"lightGreen": "#3d9a57",
|
||||
"lightBlue": "#0062d1",
|
||||
"lightCyan": "#318795",
|
||||
"lightYellow": "#b0851f"
|
||||
},
|
||||
@@ -62,8 +62,8 @@
|
||||
"light": "lightOrange"
|
||||
},
|
||||
"success": {
|
||||
"dark": "darkGreen",
|
||||
"light": "lightGreen"
|
||||
"dark": "darkBlue",
|
||||
"light": "lightBlue"
|
||||
},
|
||||
"info": {
|
||||
"dark": "darkCyan",
|
||||
@@ -102,8 +102,8 @@
|
||||
"light": "lightStep6"
|
||||
},
|
||||
"diffAdded": {
|
||||
"dark": "#4fd6be",
|
||||
"light": "#1e725c"
|
||||
"dark": "#6ba1e6",
|
||||
"light": "#0062d1"
|
||||
},
|
||||
"diffRemoved": {
|
||||
"dark": "#c53b53",
|
||||
@@ -118,16 +118,16 @@
|
||||
"light": "#7086b5"
|
||||
},
|
||||
"diffHighlightAdded": {
|
||||
"dark": "#b8db87",
|
||||
"light": "#4db380"
|
||||
"dark": "#6ba1e6",
|
||||
"light": "#0062d1"
|
||||
},
|
||||
"diffHighlightRemoved": {
|
||||
"dark": "#e26a75",
|
||||
"light": "#f52a65"
|
||||
},
|
||||
"diffAddedBg": {
|
||||
"dark": "#20303b",
|
||||
"light": "#d5e5d5"
|
||||
"dark": "#1a2a3d",
|
||||
"light": "#e0edfa"
|
||||
},
|
||||
"diffRemovedBg": {
|
||||
"dark": "#37222c",
|
||||
@@ -142,8 +142,8 @@
|
||||
"light": "lightStep3"
|
||||
},
|
||||
"diffAddedLineNumberBg": {
|
||||
"dark": "#1b2b34",
|
||||
"light": "#c5d5c5"
|
||||
"dark": "#162535",
|
||||
"light": "#d0e5f5"
|
||||
},
|
||||
"diffRemovedLineNumberBg": {
|
||||
"dark": "#2d1f26",
|
||||
@@ -166,8 +166,8 @@
|
||||
"light": "lightCyan"
|
||||
},
|
||||
"markdownCode": {
|
||||
"dark": "darkGreen",
|
||||
"light": "lightGreen"
|
||||
"dark": "darkBlue",
|
||||
"light": "lightBlue"
|
||||
},
|
||||
"markdownBlockQuote": {
|
||||
"dark": "#FFF7F1",
|
||||
@@ -222,8 +222,8 @@
|
||||
"light": "lightRed"
|
||||
},
|
||||
"syntaxString": {
|
||||
"dark": "darkGreen",
|
||||
"light": "lightGreen"
|
||||
"dark": "darkBlue",
|
||||
"light": "lightBlue"
|
||||
},
|
||||
"syntaxNumber": {
|
||||
"dark": "#FFF7F1",
|
||||
|
||||
@@ -10,7 +10,7 @@ export function Footer() {
|
||||
const { theme } = useTheme()
|
||||
const sync = useSync()
|
||||
const route = useRoute()
|
||||
const mcp = createMemo(() => Object.keys(sync.data.mcp))
|
||||
const mcp = createMemo(() => Object.values(sync.data.mcp).filter((x) => x.status === "connected").length)
|
||||
const mcpError = createMemo(() => Object.values(sync.data.mcp).some((x) => x.status === "failed"))
|
||||
const lsp = createMemo(() => Object.keys(sync.data.lsp))
|
||||
const permissions = createMemo(() => {
|
||||
@@ -66,7 +66,7 @@ export function Footer() {
|
||||
<text fg={theme.text}>
|
||||
<span style={{ fg: theme.success }}>•</span> {lsp().length} LSP
|
||||
</text>
|
||||
<Show when={mcp().length}>
|
||||
<Show when={mcp()}>
|
||||
<text fg={theme.text}>
|
||||
<Switch>
|
||||
<Match when={mcpError()}>
|
||||
@@ -76,7 +76,7 @@ export function Footer() {
|
||||
<span style={{ fg: theme.success }}>⊙ </span>
|
||||
</Match>
|
||||
</Switch>
|
||||
{mcp().length} MCP
|
||||
{mcp()} MCP
|
||||
</text>
|
||||
</Show>
|
||||
<text fg={theme.textMuted}>/status</text>
|
||||
|
||||
@@ -1057,6 +1057,7 @@ function UserMessage(props: {
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<span> </span>
|
||||
<span style={{ bg: theme.accent, fg: theme.backgroundPanel, bold: true }}> QUEUED </span>
|
||||
</Show>
|
||||
</text>
|
||||
|
||||
@@ -464,7 +464,6 @@ export namespace Config {
|
||||
agent_cycle: z.string().optional().default("tab").describe("Next agent"),
|
||||
agent_cycle_reverse: z.string().optional().default("shift+tab").describe("Previous agent"),
|
||||
input_clear: z.string().optional().default("ctrl+c").describe("Clear input field"),
|
||||
input_forward_delete: z.string().optional().default("ctrl+d").describe("Forward delete"),
|
||||
input_paste: z.string().optional().default("ctrl+v").describe("Paste from clipboard"),
|
||||
input_submit: z.string().optional().default("return").describe("Submit input"),
|
||||
input_newline: z.string().optional().default("shift+return,ctrl+j").describe("Insert newline in input"),
|
||||
|
||||
@@ -11,8 +11,6 @@ export namespace Flag {
|
||||
export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS")
|
||||
export const OPENCODE_DISABLE_AUTOCOMPACT = truthy("OPENCODE_DISABLE_AUTOCOMPACT")
|
||||
export const OPENCODE_FAKE_VCS = process.env["OPENCODE_FAKE_VCS"]
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH = number("OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH")
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT")
|
||||
|
||||
// Experimental
|
||||
export const OPENCODE_EXPERIMENTAL = truthy("OPENCODE_EXPERIMENTAL")
|
||||
@@ -20,6 +18,8 @@ export namespace Flag {
|
||||
export const OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT = truthy("OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT")
|
||||
export const OPENCODE_ENABLE_EXA =
|
||||
truthy("OPENCODE_ENABLE_EXA") || OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_EXA")
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH = number("OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH")
|
||||
export const OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS = number("OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS")
|
||||
|
||||
function truthy(key: string) {
|
||||
const value = process.env[key]?.toLowerCase()
|
||||
|
||||
@@ -184,7 +184,8 @@ export namespace Installation {
|
||||
return reg.endsWith("/") ? reg.slice(0, -1) : reg
|
||||
})
|
||||
const [major] = VERSION.split(".").map((x) => Number(x))
|
||||
const channel = CHANNEL === "latest" ? `latest-${major}` : CHANNEL
|
||||
// const channel = CHANNEL === "latest" ? `latest-${major}` : CHANNEL
|
||||
const channel = CHANNEL
|
||||
return fetch(`${registry}/opencode-ai/${channel}`)
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(res.statusText)
|
||||
|
||||
@@ -211,7 +211,15 @@ export namespace LSPServer {
|
||||
|
||||
export const Biome: Info = {
|
||||
id: "biome",
|
||||
root: NearestRoot(["biome.json", "biome.jsonc", "package-lock.json", "bun.lockb", "bun.lock", "pnpm-lock.yaml", "yarn.lock"]),
|
||||
root: NearestRoot([
|
||||
"biome.json",
|
||||
"biome.jsonc",
|
||||
"package-lock.json",
|
||||
"bun.lockb",
|
||||
"bun.lock",
|
||||
"pnpm-lock.yaml",
|
||||
"yarn.lock",
|
||||
]),
|
||||
extensions: [
|
||||
".ts",
|
||||
".tsx",
|
||||
|
||||
@@ -61,14 +61,10 @@ export namespace Plugin {
|
||||
for (const hook of await state().then((x) => x.hooks)) {
|
||||
const fn = hook[name]
|
||||
if (!fn) continue
|
||||
try {
|
||||
// @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
|
||||
// give up.
|
||||
// try-counter: 2
|
||||
await fn(input, output)
|
||||
} catch (e) {
|
||||
log.error("failed to trigger hook", { name, error: e })
|
||||
}
|
||||
// @ts-expect-error if you feel adventurous, please fix the typing, make sure to bump the try-counter if you
|
||||
// give up.
|
||||
// try-counter: 2
|
||||
await fn(input, output)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export namespace Project {
|
||||
await migrateFromGlobal(id, worktree)
|
||||
}
|
||||
}
|
||||
discover(existing)
|
||||
if (Flag.OPENCODE_EXPERIMENTAL) discover(existing)
|
||||
const result: Info = {
|
||||
...existing,
|
||||
worktree,
|
||||
@@ -129,7 +129,7 @@ export namespace Project {
|
||||
|
||||
export async function discover(input: Info) {
|
||||
if (input.vcs !== "git") return
|
||||
if (input.icon) return
|
||||
if (input.icon?.url) return
|
||||
const glob = new Bun.Glob("**/{favicon}.{ico,png,svg,jpg,jpeg,webp}")
|
||||
const matches = await Array.fromAsync(
|
||||
glob.scan({
|
||||
@@ -198,11 +198,24 @@ export namespace Project {
|
||||
icon: Info.shape.icon.optional(),
|
||||
}),
|
||||
async (input) => {
|
||||
return await Storage.update<Info>(["project", input.projectID], (draft) => {
|
||||
const result = await Storage.update<Info>(["project", input.projectID], (draft) => {
|
||||
if (input.name !== undefined) draft.name = input.name
|
||||
if (input.icon !== undefined) draft.icon = input.icon
|
||||
if (input.icon !== undefined) {
|
||||
draft.icon = {
|
||||
...draft.icon,
|
||||
}
|
||||
if (input.icon.url !== undefined) draft.icon.url = input.icon.url
|
||||
if (input.icon.color !== undefined) draft.icon.color = input.icon.color
|
||||
}
|
||||
draft.time.updated = Date.now()
|
||||
})
|
||||
GlobalBus.emit("event", {
|
||||
payload: {
|
||||
type: Event.Updated.type,
|
||||
properties: result,
|
||||
},
|
||||
})
|
||||
return result
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,16 @@ export namespace ModelsDev {
|
||||
reasoning: z.boolean(),
|
||||
temperature: z.boolean(),
|
||||
tool_call: z.boolean(),
|
||||
interleaved: z
|
||||
.union([
|
||||
z.literal(true),
|
||||
z
|
||||
.object({
|
||||
field: z.enum(["reasoning_content", "reasoning_details"]),
|
||||
})
|
||||
.strict(),
|
||||
])
|
||||
.optional(),
|
||||
cost: z
|
||||
.object({
|
||||
input: z.number(),
|
||||
|
||||
@@ -349,6 +349,12 @@ export namespace Provider {
|
||||
video: z.boolean(),
|
||||
pdf: z.boolean(),
|
||||
}),
|
||||
interleaved: z.union([
|
||||
z.boolean(),
|
||||
z.object({
|
||||
field: z.enum(["reasoning_content", "reasoning_details"]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
cost: z.object({
|
||||
input: z.number(),
|
||||
@@ -450,6 +456,7 @@ export namespace Provider {
|
||||
video: model.modalities?.output?.includes("video") ?? false,
|
||||
pdf: model.modalities?.output?.includes("pdf") ?? false,
|
||||
},
|
||||
interleaved: model.interleaved ?? false,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -567,6 +574,7 @@ export namespace Provider {
|
||||
video: model.modalities?.output?.includes("video") ?? existingModel?.capabilities.output.video ?? false,
|
||||
pdf: model.modalities?.output?.includes("pdf") ?? existingModel?.capabilities.output.pdf ?? false,
|
||||
},
|
||||
interleaved: model.interleaved ?? false,
|
||||
},
|
||||
cost: {
|
||||
input: model?.cost?.input ?? existingModel?.cost?.input ?? 0,
|
||||
|
||||
@@ -273,7 +273,23 @@ export namespace ProviderTransform {
|
||||
return options
|
||||
}
|
||||
|
||||
export function providerOptions(model: Provider.Model, options: { [x: string]: any }) {
|
||||
export function providerOptions(model: Provider.Model, options: { [x: string]: any }, messages: ModelMessage[]) {
|
||||
if (model.capabilities.interleaved && typeof model.capabilities.interleaved === "object") {
|
||||
const cot = []
|
||||
const assistantMessages = messages.filter((msg) => msg.role === "assistant")
|
||||
for (const msg of assistantMessages) {
|
||||
for (const part of msg.content) {
|
||||
if (typeof part === "string") {
|
||||
continue
|
||||
}
|
||||
if (part.type === "reasoning") {
|
||||
cot.push(part)
|
||||
}
|
||||
}
|
||||
}
|
||||
options[model.capabilities.interleaved.field] = cot
|
||||
}
|
||||
|
||||
switch (model.api.npm) {
|
||||
case "@ai-sdk/openai":
|
||||
case "@ai-sdk/azure":
|
||||
|
||||
@@ -143,6 +143,7 @@ export namespace SessionCompaction {
|
||||
providerOptions: ProviderTransform.providerOptions(
|
||||
model,
|
||||
pipe({}, mergeDeep(ProviderTransform.options(model, input.sessionID)), mergeDeep(model.options)),
|
||||
[],
|
||||
),
|
||||
headers: model.headers,
|
||||
abortSignal: input.abort,
|
||||
|
||||
@@ -515,6 +515,37 @@ export namespace SessionPrompt {
|
||||
})
|
||||
}
|
||||
|
||||
const messages = [
|
||||
...system.map(
|
||||
(x): ModelMessage => ({
|
||||
role: "system",
|
||||
content: x,
|
||||
}),
|
||||
),
|
||||
...MessageV2.toModelMessage(
|
||||
msgs.filter((m) => {
|
||||
if (m.info.role !== "assistant" || m.info.error === undefined) {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
MessageV2.AbortedError.isInstance(m.info.error) &&
|
||||
m.parts.some((part) => part.type !== "step-start" && part.type !== "reasoning")
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}),
|
||||
),
|
||||
...(isLastStep
|
||||
? [
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content: MAX_STEPS,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
]
|
||||
const result = await processor.process({
|
||||
onError(error) {
|
||||
log.error("stream error", {
|
||||
@@ -562,42 +593,12 @@ export namespace SessionPrompt {
|
||||
OUTPUT_TOKEN_MAX,
|
||||
),
|
||||
abortSignal: abort,
|
||||
providerOptions: ProviderTransform.providerOptions(model, params.options),
|
||||
providerOptions: ProviderTransform.providerOptions(model, params.options, messages),
|
||||
stopWhen: stepCountIs(1),
|
||||
temperature: params.temperature,
|
||||
topP: params.topP,
|
||||
toolChoice: isLastStep ? "none" : undefined,
|
||||
messages: [
|
||||
...system.map(
|
||||
(x): ModelMessage => ({
|
||||
role: "system",
|
||||
content: x,
|
||||
}),
|
||||
),
|
||||
...MessageV2.toModelMessage(
|
||||
msgs.filter((m) => {
|
||||
if (m.info.role !== "assistant" || m.info.error === undefined) {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
MessageV2.AbortedError.isInstance(m.info.error) &&
|
||||
m.parts.some((part) => part.type !== "step-start" && part.type !== "reasoning")
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}),
|
||||
),
|
||||
...(isLastStep
|
||||
? [
|
||||
{
|
||||
role: "assistant" as const,
|
||||
content: MAX_STEPS,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
messages,
|
||||
tools: model.capabilities.toolcall === false ? undefined : tools,
|
||||
model: wrapLanguageModel({
|
||||
model: language,
|
||||
@@ -1230,8 +1231,8 @@ export namespace SessionPrompt {
|
||||
},
|
||||
}
|
||||
await Session.updatePart(part)
|
||||
const shell = process.env["SHELL"] ?? "bash"
|
||||
const shellName = path.basename(shell)
|
||||
const shell = process.env["SHELL"] ?? (process.platform === "win32" ? process.env["COMSPEC"] || "cmd.exe" : "bash")
|
||||
const shellName = path.basename(shell).toLowerCase()
|
||||
|
||||
const invocations: Record<string, { args: string[] }> = {
|
||||
nu: {
|
||||
@@ -1261,6 +1262,14 @@ export namespace SessionPrompt {
|
||||
`,
|
||||
],
|
||||
},
|
||||
// Windows cmd.exe
|
||||
"cmd.exe": {
|
||||
args: ["/c", input.command],
|
||||
},
|
||||
// Windows PowerShell
|
||||
"powershell.exe": {
|
||||
args: ["-NoProfile", "-Command", input.command],
|
||||
},
|
||||
// Fallback: any shell that doesn't match those above
|
||||
"": {
|
||||
args: ["-c", "-l", `${input.command}`],
|
||||
@@ -1272,7 +1281,7 @@ export namespace SessionPrompt {
|
||||
|
||||
const proc = spawn(shell, args, {
|
||||
cwd: Instance.directory,
|
||||
detached: true,
|
||||
detached: process.platform !== "win32",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -1464,7 +1473,7 @@ export namespace SessionPrompt {
|
||||
await generateText({
|
||||
// use higher # for reasoning models since reasoning tokens eat up a lot of the budget
|
||||
maxOutputTokens: small.capabilities.reasoning ? 3000 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small, options),
|
||||
providerOptions: ProviderTransform.providerOptions(small, options, []),
|
||||
messages: [
|
||||
...SystemPrompt.title(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
|
||||
@@ -91,7 +91,7 @@ export namespace SessionSummary {
|
||||
if (textPart && !userMsg.summary?.title) {
|
||||
const result = await generateText({
|
||||
maxOutputTokens: small.capabilities.reasoning ? 1500 : 20,
|
||||
providerOptions: ProviderTransform.providerOptions(small, options),
|
||||
providerOptions: ProviderTransform.providerOptions(small, options, []),
|
||||
messages: [
|
||||
...SystemPrompt.title(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
@@ -144,7 +144,7 @@ export namespace SessionSummary {
|
||||
const result = await generateText({
|
||||
model: language,
|
||||
maxOutputTokens: 100,
|
||||
providerOptions: ProviderTransform.providerOptions(small, options),
|
||||
providerOptions: ProviderTransform.providerOptions(small, options, []),
|
||||
messages: [
|
||||
...SystemPrompt.summarize(small.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
|
||||
@@ -17,7 +17,7 @@ import path from "path"
|
||||
import { iife } from "@/util/iife"
|
||||
|
||||
const MAX_OUTPUT_LENGTH = Flag.OPENCODE_EXPERIMENTAL_BASH_MAX_OUTPUT_LENGTH || 30_000
|
||||
const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT || 2 * 60 * 1000
|
||||
const DEFAULT_TIMEOUT = Flag.OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS || 2 * 60 * 1000
|
||||
const SIGKILL_TIMEOUT_MS = 200
|
||||
|
||||
export const log = Log.create({ service: "bash-tool" })
|
||||
|
||||
@@ -17,14 +17,48 @@ Before executing the command, please follow these steps:
|
||||
- Capture the output of the command.
|
||||
|
||||
Usage notes:
|
||||
- The command argument is required.
|
||||
- You can specify an optional timeout in milliseconds. If not specified, commands will timeout after 120000ms (2 minutes). Use the `timeout` parameter to control execution time.
|
||||
- The `workdir` parameter specifies the working directory for the command. Defaults to the current working directory. Prefer setting `workdir` over using `cd` in your commands.
|
||||
- It is very helpful if you write a clear, concise description of what this command does in 5-10 words.
|
||||
- If the output exceeds 30000 characters, output will be truncated before being returned to you.
|
||||
- VERY IMPORTANT: You MUST avoid using search commands like `find` and `grep`. Instead use Grep, Glob, or Task to search. You MUST avoid read tools like `cat`, `head`, `tail`, and `ls`, and use Read and List to read files.
|
||||
- If you _still_ need to run `grep`, STOP. ALWAYS USE ripgrep at `rg` (or /usr/bin/rg) first, which all opencode users have pre-installed.
|
||||
- When issuing multiple commands, use the ';' or '&&' operator to separate them. DO NOT use newlines (newlines are ok in quoted strings).
|
||||
- The command argument is required.
|
||||
- You can specify an optional timeout in milliseconds (up to 600000ms / 10 minutes).
|
||||
If not specified, commands will timeout after 120000ms (2 minutes).
|
||||
- It is very helpful if you write a clear, concise description of what this command
|
||||
does in 5-10 words.
|
||||
- If the output exceeds 30000 characters, output will be truncated before being
|
||||
returned to you.
|
||||
- You can use the `run_in_background` parameter to run the command in the background,
|
||||
which allows you to continue working while the command runs. You can monitor the output
|
||||
using the Bash tool as it becomes available. You do not need to use '&' at the end of
|
||||
the command when using this parameter.
|
||||
|
||||
- Avoid using Bash with the `find`, `grep`, `cat`, `head`, `tail`, `sed`, `awk`, or
|
||||
`echo` commands, unless explicitly instructed or when these commands are truly necessary
|
||||
for the task. Instead, always prefer using the dedicated tools for these commands:
|
||||
- File search: Use Glob (NOT find or ls)
|
||||
- Content search: Use Grep (NOT grep or rg)
|
||||
- Read files: Use Read (NOT cat/head/tail)
|
||||
- Edit files: Use Edit (NOT sed/awk)
|
||||
- Write files: Use Write (NOT echo >/cat <<EOF)
|
||||
- Communication: Output text directly (NOT echo/printf)
|
||||
- When issuing multiple commands:
|
||||
- If the commands are independent and can run in parallel, make multiple Bash tool
|
||||
calls in a single message. For example, if you need to run "git status" and "git diff",
|
||||
send a single message with two Bash tool calls in parallel.
|
||||
- If the commands depend on each other and must run sequentially, use a single Bash
|
||||
call with '&&' to chain them together (e.g., `git add . && git commit -m "message" &&
|
||||
git push`). For instance, if one operation must complete before another starts (like
|
||||
mkdir before cp, Write before Bash for git operations, or git add before git commit),
|
||||
run these operations sequentially instead.
|
||||
- Use ';' only when you need to run commands sequentially but don't care if earlier
|
||||
commands fail
|
||||
- DO NOT use newlines to separate commands (newlines are ok in quoted strings)
|
||||
- Try to maintain your current working directory throughout the session by using
|
||||
absolute paths and avoiding usage of `cd`. You may use `cd` if the User explicitly
|
||||
requests it.
|
||||
<good-example>
|
||||
pytest /foo/bar/tests
|
||||
</good-example>
|
||||
<bad-example>
|
||||
cd /foo/bar && pytest tests
|
||||
</bad-example>
|
||||
|
||||
# Working Directory
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
- Searches file contents using regular expressions
|
||||
- Supports full regex syntax (eg. "log.*Error", "function\s+\w+", etc.)
|
||||
- Filter files by pattern with the include parameter (eg. "*.js", "*.{ts,tsx}")
|
||||
- Returns file paths with at least one match sorted by modification time
|
||||
- Returns file paths and line numbers with at least one match sorted by modification time
|
||||
- Use this tool when you need to find files containing specific patterns
|
||||
- If you need to identify/count the number of matches within files, use the Bash tool with `rg` (ripgrep) directly. Do NOT use `grep`.
|
||||
- When you are doing an open ended search that may require multiple rounds of globbing and grepping, use the Task tool instead
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
import { $ } from "bun"
|
||||
import { realpathSync } from "fs"
|
||||
import * as fs from "fs/promises"
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
|
||||
// Strip null bytes from paths (defensive fix for CI environment issues)
|
||||
function sanitizePath(p: string): string {
|
||||
return p.replace(/\0/g, "")
|
||||
}
|
||||
|
||||
type TmpDirOptions<T> = {
|
||||
git?: boolean
|
||||
init?: (dir: string) => Promise<T>
|
||||
dispose?: (dir: string) => Promise<T>
|
||||
}
|
||||
export async function tmpdir<T>(options?: TmpDirOptions<T>) {
|
||||
const dirpath = path.join(os.tmpdir(), "opencode-test-" + Math.random().toString(36).slice(2))
|
||||
await $`mkdir -p ${dirpath}`.quiet()
|
||||
const dirpath = sanitizePath(path.join(os.tmpdir(), "opencode-test-" + Math.random().toString(36).slice(2)))
|
||||
await fs.mkdir(dirpath, { recursive: true })
|
||||
if (options?.git) {
|
||||
await $`git init`.cwd(dirpath).quiet()
|
||||
await $`git commit --allow-empty -m "root commit ${dirpath}"`.cwd(dirpath).quiet()
|
||||
}
|
||||
const extra = await options?.init?.(dirpath)
|
||||
const realpath = sanitizePath(await fs.realpath(dirpath))
|
||||
const result = {
|
||||
[Symbol.asyncDispose]: async () => {
|
||||
await options?.dispose?.(dirpath)
|
||||
await $`rm -rf ${dirpath}`.quiet()
|
||||
// await fs.rm(dirpath, { recursive: true, force: true })
|
||||
},
|
||||
path: realpathSync(dirpath),
|
||||
path: realpath,
|
||||
extra: extra as T,
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
// xdg-basedir reads env vars at import time, so we must set these first
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
|
||||
const testDataDir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid)
|
||||
process.env["XDG_DATA_HOME"] = testDataDir
|
||||
process.env["XDG_CACHE_HOME"] = path.join(testDataDir, "cache")
|
||||
process.env["XDG_CONFIG_HOME"] = path.join(testDataDir, "config")
|
||||
process.env["XDG_STATE_HOME"] = path.join(testDataDir, "state")
|
||||
const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid)
|
||||
await fs.mkdir(dir, { recursive: true })
|
||||
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")
|
||||
process.env["XDG_STATE_HOME"] = path.join(dir, "state")
|
||||
|
||||
// Clear provider env vars to ensure clean test state
|
||||
delete process.env["ANTHROPIC_API_KEY"]
|
||||
|
||||
@@ -130,6 +130,7 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
cost: {
|
||||
input: 0.001,
|
||||
@@ -184,6 +185,7 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
cost: {
|
||||
input: 0.001,
|
||||
@@ -236,6 +238,7 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
cost: {
|
||||
input: 0.001,
|
||||
@@ -281,6 +284,7 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => {
|
||||
toolcall: true,
|
||||
input: { text: true, audio: false, image: true, video: false, pdf: false },
|
||||
output: { text: true, audio: false, image: false, video: false, pdf: false },
|
||||
interleaved: false,
|
||||
},
|
||||
cost: {
|
||||
input: 0.03,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -17,5 +17,5 @@ for (const [key, value] of Object.entries(pkg.exports)) {
|
||||
}
|
||||
}
|
||||
await Bun.write("package.json", JSON.stringify(pkg, null, 2))
|
||||
await $`bun publish --tag ${Script.channel} --access public`
|
||||
await $`bun pm pack && npm publish *.tgz --tag ${Script.channel} --access public`
|
||||
await Bun.write("package.json", JSON.stringify(original, null, 2))
|
||||
|
||||
@@ -13,10 +13,21 @@ if (process.versions.bun !== expectedBunVersion) {
|
||||
throw new Error(`This script requires bun@${expectedBunVersion}, but you are using bun@${process.versions.bun}`)
|
||||
}
|
||||
|
||||
const CHANNEL = process.env["OPENCODE_CHANNEL"] ?? (await $`git branch --show-current`.text().then((x) => x.trim()))
|
||||
const env = {
|
||||
OPENCODE_CHANNEL: process.env["OPENCODE_CHANNEL"],
|
||||
OPENCODE_BUMP: process.env["OPENCODE_BUMP"],
|
||||
OPENCODE_VERSION: process.env["OPENCODE_VERSION"],
|
||||
}
|
||||
const CHANNEL = await (async () => {
|
||||
if (env.OPENCODE_CHANNEL) return env.OPENCODE_CHANNEL
|
||||
if (env.OPENCODE_BUMP) return "latest"
|
||||
if (env.OPENCODE_VERSION && !env.OPENCODE_VERSION.startsWith("0.0.0-")) return "latest"
|
||||
return await $`git branch --show-current`.text().then((x) => x.trim())
|
||||
})()
|
||||
const IS_PREVIEW = CHANNEL !== "latest"
|
||||
|
||||
const VERSION = await (async () => {
|
||||
if (process.env["OPENCODE_VERSION"]) return process.env["OPENCODE_VERSION"]
|
||||
if (env.OPENCODE_VERSION) return env.OPENCODE_VERSION
|
||||
if (IS_PREVIEW) return `0.0.0-${CHANNEL}-${new Date().toISOString().slice(0, 16).replace(/[-:T]/g, "")}`
|
||||
const version = await fetch("https://registry.npmjs.org/opencode-ai/latest")
|
||||
.then((res) => {
|
||||
@@ -25,7 +36,7 @@ const VERSION = await (async () => {
|
||||
})
|
||||
.then((data: any) => data.version)
|
||||
const [major, minor, patch] = version.split(".").map((x: string) => Number(x) || 0)
|
||||
const t = process.env["OPENCODE_BUMP"]?.toLowerCase()
|
||||
const t = env.OPENCODE_BUMP?.toLowerCase()
|
||||
if (t === "major") return `${major + 1}.0.0`
|
||||
if (t === "minor") return `${major}.${minor + 1}.0`
|
||||
return `${major}.${minor}.${patch + 1}`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo --noEmit",
|
||||
|
||||
@@ -41,3 +41,4 @@ await $`bun prettier --write src/gen`
|
||||
await $`bun prettier --write src/v2`
|
||||
await $`rm -rf dist`
|
||||
await $`bun tsc`
|
||||
await $`rm openapi.json`
|
||||
|
||||
@@ -19,5 +19,6 @@ for (const [key, value] of Object.entries(pkg.exports)) {
|
||||
}
|
||||
}
|
||||
await Bun.write("package.json", JSON.stringify(pkg, null, 2))
|
||||
await $`bun publish --tag ${Script.channel} --access public`
|
||||
await $`bun pm pack`
|
||||
await $`npm publish *.tgz --tag ${Script.channel} --access public`
|
||||
await Bun.write("package.json", JSON.stringify(original, null, 2))
|
||||
|
||||
@@ -927,10 +927,6 @@ export type KeybindsConfig = {
|
||||
* Clear input field
|
||||
*/
|
||||
input_clear?: string
|
||||
/**
|
||||
* Forward delete
|
||||
*/
|
||||
input_forward_delete?: string
|
||||
/**
|
||||
* Paste from clipboard
|
||||
*/
|
||||
@@ -1044,6 +1040,11 @@ export type ProviderConfig = {
|
||||
reasoning?: boolean
|
||||
temperature?: boolean
|
||||
tool_call?: boolean
|
||||
interleaved?:
|
||||
| true
|
||||
| {
|
||||
field: "reasoning_content" | "reasoning_details"
|
||||
}
|
||||
cost?: {
|
||||
input: number
|
||||
output: number
|
||||
@@ -1479,6 +1480,11 @@ export type Model = {
|
||||
video: boolean
|
||||
pdf: boolean
|
||||
}
|
||||
interleaved:
|
||||
| boolean
|
||||
| {
|
||||
field: "reasoning_content" | "reasoning_details"
|
||||
}
|
||||
}
|
||||
cost: {
|
||||
input: number
|
||||
@@ -3026,6 +3032,11 @@ export type ProviderListResponses = {
|
||||
reasoning: boolean
|
||||
temperature: boolean
|
||||
tool_call: boolean
|
||||
interleaved?:
|
||||
| true
|
||||
| {
|
||||
field: "reasoning_content" | "reasoning_details"
|
||||
}
|
||||
cost?: {
|
||||
input: number
|
||||
output: number
|
||||
|
||||
@@ -2803,6 +2803,25 @@
|
||||
"tool_call": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"interleaved": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"const": true
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"enum": ["reasoning_content", "reasoning_details"]
|
||||
}
|
||||
},
|
||||
"required": ["field"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"cost": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -7115,11 +7134,6 @@
|
||||
"default": "ctrl+c",
|
||||
"type": "string"
|
||||
},
|
||||
"input_forward_delete": {
|
||||
"description": "Forward delete",
|
||||
"default": "ctrl+d",
|
||||
"type": "string"
|
||||
},
|
||||
"input_paste": {
|
||||
"description": "Paste from clipboard",
|
||||
"default": "ctrl+v",
|
||||
@@ -7301,6 +7315,25 @@
|
||||
"tool_call": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"interleaved": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"const": true
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"enum": ["reasoning_content", "reasoning_details"]
|
||||
}
|
||||
},
|
||||
"required": ["field"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"cost": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -8310,9 +8343,26 @@
|
||||
}
|
||||
},
|
||||
"required": ["text", "audio", "image", "video", "pdf"]
|
||||
},
|
||||
"interleaved": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"field": {
|
||||
"type": "string",
|
||||
"enum": ["reasoning_content", "reasoning_details"]
|
||||
}
|
||||
},
|
||||
"required": ["field"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["temperature", "reasoning", "attachment", "toolcall", "input", "output"]
|
||||
"required": ["temperature", "reasoning", "attachment", "toolcall", "input", "output", "interleaved"]
|
||||
},
|
||||
"cost": {
|
||||
"type": "object",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "bun run src/index.ts",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@opencode-ai/tauri",
|
||||
"private": true,
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsgo -b",
|
||||
"predev": "bun ./scripts/predev.ts",
|
||||
"dev": "vite",
|
||||
"build": "bun run typecheck && vite build",
|
||||
|
||||
@@ -108,29 +108,30 @@ pub fn run() {
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let port = get_sidecar_port();
|
||||
let socket_connected = is_server_running(port).await;
|
||||
|
||||
let should_spawn_sidecar = if socket_connected {
|
||||
let res = app
|
||||
.dialog()
|
||||
.message(
|
||||
"OpenCode Server is already running, would you like to restart it?",
|
||||
)
|
||||
.buttons(MessageDialogButtons::YesNo)
|
||||
.blocking_show_with_result();
|
||||
let should_spawn_sidecar = !is_server_running(port).await;
|
||||
|
||||
match res {
|
||||
MessageDialogResult::Yes => {
|
||||
if let Err(e) = find_and_kill_process_on_port(port) {
|
||||
eprintln!("Failed to kill process on port {}: {}", port, e);
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
true
|
||||
};
|
||||
// if server_running {
|
||||
// let res = app
|
||||
// .dialog()
|
||||
// .message(
|
||||
// "OpenCode Server is already running, would you like to restart it?",
|
||||
// )
|
||||
// .buttons(MessageDialogButtons::YesNo)
|
||||
// .blocking_show_with_result();
|
||||
|
||||
// match res {
|
||||
// MessageDialogResult::Yes => {
|
||||
// if let Err(e) = find_and_kill_process_on_port(port) {
|
||||
// eprintln!("Failed to kill process on port {}: {}", port, e);
|
||||
// }
|
||||
// true
|
||||
// }
|
||||
// _ => false,
|
||||
// }
|
||||
// } else {
|
||||
// true
|
||||
// };
|
||||
|
||||
let child = if should_spawn_sidecar {
|
||||
let child = spawn_sidecar(&app, port);
|
||||
|
||||
@@ -20,7 +20,9 @@ export async function runUpdater(onDownloadEvent?: (progress: DownloadEvent) =>
|
||||
return false
|
||||
}
|
||||
|
||||
const shouldUpdate = await ask(`Version ${update.version} of OpenCode is available, would you like to install it?`)
|
||||
const shouldUpdate = await ask(
|
||||
`Version ${update.version} of OpenCode has been downloaded, would you like to install it and relaunch?`,
|
||||
)
|
||||
if (!shouldUpdate) return
|
||||
|
||||
try {
|
||||
@@ -30,8 +32,7 @@ export async function runUpdater(onDownloadEvent?: (progress: DownloadEvent) =>
|
||||
return false
|
||||
}
|
||||
|
||||
const shouldRestart = await ask("Update installed successfully, would you like to restart OpenCode?")
|
||||
if (shouldRestart) await relaunch()
|
||||
await relaunch()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.0.138",
|
||||
"version": "1.0.143",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
"./*": "./src/components/*.tsx",
|
||||
@@ -10,6 +10,8 @@
|
||||
"./context/*": "./src/context/*.tsx",
|
||||
"./styles": "./src/styles/index.css",
|
||||
"./styles/tailwind": "./src/styles/tailwind/index.css",
|
||||
"./icons/provider": "./src/components/provider-icons/types.ts",
|
||||
"./icons/file-type": "./src/components/file-icons/types.ts",
|
||||
"./fonts/*": "./src/assets/fonts/*"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
|
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 385 B |
|
Before Width: | Height: | Size: 110 B After Width: | Height: | Size: 110 B |
|
Before Width: | Height: | Size: 746 B After Width: | Height: | Size: 746 B |
|
Before Width: | Height: | Size: 758 B After Width: | Height: | Size: 758 B |
|
Before Width: | Height: | Size: 348 B After Width: | Height: | Size: 348 B |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 511 B After Width: | Height: | Size: 511 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 297 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 775 B After Width: | Height: | Size: 775 B |
|
Before Width: | Height: | Size: 151 B After Width: | Height: | Size: 151 B |
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 509 B |
|
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 263 B |
|
Before Width: | Height: | Size: 823 B After Width: | Height: | Size: 823 B |
|
Before Width: | Height: | Size: 363 B After Width: | Height: | Size: 363 B |
|
Before Width: | Height: | Size: 431 B After Width: | Height: | Size: 431 B |
|
Before Width: | Height: | Size: 591 B After Width: | Height: | Size: 591 B |
|
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 776 B |
|
Before Width: | Height: | Size: 776 B After Width: | Height: | Size: 776 B |
|
Before Width: | Height: | Size: 579 B After Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 418 B After Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 281 B After Width: | Height: | Size: 281 B |
|
Before Width: | Height: | Size: 415 B After Width: | Height: | Size: 415 B |
|
Before Width: | Height: | Size: 517 B After Width: | Height: | Size: 517 B |
|
Before Width: | Height: | Size: 686 B After Width: | Height: | Size: 686 B |
|
Before Width: | Height: | Size: 577 B After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 185 B After Width: | Height: | Size: 185 B |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 532 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |