diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml new file mode 100644 index 000000000..11d6a9c82 --- /dev/null +++ b/.github/workflows/docs-update.yml @@ -0,0 +1,69 @@ +name: Docs Update + +on: + schedule: + - cron: "0 */12 * * *" + workflow_dispatch: + +jobs: + update-docs: + if: github.repository == 'sst/opencode' + runs-on: blacksmith-4vcpu-ubuntu-2404 + permissions: + id-token: write + contents: write + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history to access commits + + - name: Setup Bun + uses: ./.github/actions/setup-bun + + - name: Get recent commits + id: commits + run: | + COMMITS=$(git log --since="4 hours ago" --pretty=format:"- %h %s" 2>/dev/null || echo "") + if [ -z "$COMMITS" ]; then + echo "No commits in the last 4 hours" + echo "has_commits=false" >> $GITHUB_OUTPUT + else + echo "has_commits=true" >> $GITHUB_OUTPUT + { + echo "list<> $GITHUB_OUTPUT + fi + + - name: Run opencode + if: steps.commits.outputs.has_commits == 'true' + uses: sst/opencode/github@latest + env: + OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + with: + model: opencode/gpt-5.2 + agent: docs + prompt: | + Review the following commits from the last 4 hours and identify any new features that may need documentation. + + + ${{ steps.commits.outputs.list }} + + + Steps: + 1. For each commit that looks like a new feature or significant change: + - Read the changed files to understand what was added + - Check if the feature is already documented in packages/web/src/content/docs/* + 2. If you find undocumented features: + - Update the relevant documentation files in packages/web/src/content/docs/* + - Follow the existing documentation style and structure + - Make sure to document the feature clearly with examples where appropriate + 3. If all new features are already documented, report that no updates are needed + 4. If you are creating a new documentation file be sure to update packages/web/astro.config.mjs too. + + Focus on user-facing features and API changes. Skip internal refactors, bug fixes, and test updates unless they affect user-facing behavior. + Don't feel the need to document every little thing. It is perfectly okay to make 0 changes at all. + Try to keep documentation only for large features or changes that already have a good spot to be documented. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc9181bc5..ec98d7061 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,18 +41,6 @@ jobs: - uses: ./.github/actions/setup-bun - - name: Setup SSH for AUR - if: inputs.bump || inputs.version - run: | - sudo apt-get update - sudo apt-get install -y pacman-package-manager - mkdir -p ~/.ssh - echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - git config --global user.email "opencode@sst.dev" - git config --global user.name "opencode" - ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true - - name: Install OpenCode if: inputs.bump || inputs.version run: bun i -g opencode-ai@1.0.169 @@ -75,9 +63,15 @@ jobs: node-version: "24" registry-url: "https://registry.npmjs.org" + - name: Setup Git Identity + run: | + git config --global user.email "opencode@sst.dev" + git config --global user.name "opencode" + git remote set-url origin https://x-access-token:${{ secrets.SST_GITHUB_TOKEN }}@github.com/${{ github.repository }} + - name: Publish id: publish - run: ./script/publish.ts + run: ./script/publish-start.ts env: OPENCODE_BUMP: ${{ inputs.bump }} OPENCODE_VERSION: ${{ inputs.version }} @@ -85,9 +79,16 @@ jobs: AUR_KEY: ${{ secrets.AUR_KEY }} GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} NPM_CONFIG_PROVENANCE: false + + - uses: actions/upload-artifact@v4 + with: + name: opencode-cli + path: packages/opencode/dist + outputs: - releaseId: ${{ steps.publish.outputs.releaseId }} - tagName: ${{ steps.publish.outputs.tagName }} + release: ${{ steps.publish.outputs.release }} + tag: ${{ steps.publish.outputs.tag }} + version: ${{ steps.publish.outputs.version }} publish-tauri: needs: publish @@ -104,12 +105,14 @@ jobs: target: x86_64-pc-windows-msvc - host: blacksmith-4vcpu-ubuntu-2404 target: x86_64-unknown-linux-gnu + - host: blacksmith-4vcpu-ubuntu-2404-arm + target: aarch64-unknown-linux-gnu runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - ref: ${{ needs.publish.outputs.tagName }} + ref: ${{ needs.publish.outputs.tag }} - uses: apple-actions/import-codesign-certs@v2 if: ${{ runner.os == 'macOS' }} @@ -148,24 +151,22 @@ jobs: - uses: Swatinem/rust-cache@v2 with: - workspaces: packages/tauri/src-tauri + workspaces: packages/desktop/src-tauri shared-key: ${{ matrix.settings.target }} - name: Prepare run: | - cd packages/tauri + cd packages/desktop bun ./scripts/prepare.ts env: - OPENCODE_BUMP: ${{ inputs.bump }} - OPENCODE_VERSION: ${{ inputs.version }} - OPENCODE_CHANNEL: latest + OPENCODE_VERSION: ${{ needs.publish.outputs.version }} NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }} GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} AUR_KEY: ${{ secrets.AUR_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} RUST_TARGET: ${{ matrix.settings.target }} GH_TOKEN: ${{ github.token }} - OPENCODE_RELEASE_TAG: ${{ needs.publish.outputs.tagName }} + GITHUB_RUN_ID: ${{ github.run_id }} # Fixes AppImage build issues, can be removed when https://github.com/tauri-apps/tauri/pull/12491 is released - name: Install tauri-cli from portable appimage branch @@ -190,13 +191,13 @@ jobs: APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} APPLE_API_KEY_PATH: ${{ runner.temp }}/apple-api-key.p8 with: - projectPath: packages/tauri + projectPath: packages/desktop uploadWorkflowArtifacts: true tauriScript: ${{ (contains(matrix.settings.host, 'ubuntu') && 'cargo tauri') || '' }} - args: --target ${{ matrix.settings.target }} --config src-tauri/tauri.prod.conf.json + args: --target ${{ matrix.settings.target }} --config ./src-tauri/tauri.prod.conf.json --verbose updaterJsonPreferNsis: true - releaseId: ${{ needs.publish.outputs.releaseId }} - tagName: ${{ needs.publish.outputs.tagName }} + releaseId: ${{ needs.publish.outputs.release }} + tagName: ${{ needs.publish.outputs.tag }} releaseAssetNamePattern: opencode-desktop-[platform]-[arch][ext] releaseDraft: true @@ -204,14 +205,29 @@ jobs: needs: - publish - publish-tauri - if: needs.publish.outputs.tagName + if: needs.publish.outputs.tag runs-on: blacksmith-4vcpu-ubuntu-2404 steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - ref: ${{ needs.publish.outputs.tagName }} + ref: ${{ needs.publish.outputs.tag }} - - run: gh release edit ${{ needs.publish.outputs.tagName }} --draft=false + - uses: ./.github/actions/setup-bun + + - name: Setup SSH for AUR + run: | + sudo apt-get update + sudo apt-get install -y pacman-package-manager + mkdir -p ~/.ssh + echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa + chmod 600 ~/.ssh/id_rsa + git config --global user.email "opencode@sst.dev" + git config --global user.name "opencode" + ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true + + - run: ./script/publish-complete.ts env: - GH_TOKEN: ${{ github.token }} + OPENCODE_VERSION: ${{ needs.publish.outputs.version }} + AUR_KEY: ${{ secrets.AUR_KEY }} + GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }} diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml index 36f6df54f..c0e3a5deb 100644 --- a/.github/workflows/review.yml +++ b/.github/workflows/review.yml @@ -67,6 +67,8 @@ jobs: When critiquing code style don't be a zealot, we don't like "let" statements but sometimes they are the simpliest option, if someone does a bunch of nesting with let, they should consider using iife (see packages/opencode/src/util.iife.ts) Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block. + If you are writing suggested fixes, BE SURE THAT the change you are recommending is actually valid typescript, often I have seen missing closing "}" or other syntax errors. + Generally, write a comment instead of writing suggested change if you can help it. Command MUST be like this. \`\`\` diff --git a/.github/workflows/stats.yml b/.github/workflows/stats.yml index 97e924517..57e93642b 100644 --- a/.github/workflows/stats.yml +++ b/.github/workflows/stats.yml @@ -5,8 +5,11 @@ on: - cron: "0 12 * * *" # Run daily at 12:00 UTC workflow_dispatch: # Allow manual trigger +concurrency: ${{ github.workflow }}-${{ github.ref }} + jobs: stats: + if: github.repository == 'sst/opencode' runs-on: blacksmith-4vcpu-ubuntu-2404 permissions: contents: write diff --git a/.opencode/agent/triage.md b/.opencode/agent/triage.md index 36cec9bc3..b2db100e9 100644 --- a/.opencode/agent/triage.md +++ b/.opencode/agent/triage.md @@ -63,8 +63,6 @@ TUI issues potentially caused by our underlying TUI library: **Do not** add for general TUI bugs. ---- - When assigning to people here are the following rules: adamdotdev: diff --git a/.opencode/skill/test-skill/SKILL.md b/.opencode/skill/test-skill/SKILL.md new file mode 100644 index 000000000..3fef059f2 --- /dev/null +++ b/.opencode/skill/test-skill/SKILL.md @@ -0,0 +1,6 @@ +--- +name: test-skill +description: use this when asked to test skill +--- + +woah this is a test skill diff --git a/.opencode/tool/github-triage.ts b/.opencode/tool/github-triage.ts index 739135169..a5e6c811d 100644 --- a/.opencode/tool/github-triage.ts +++ b/.opencode/tool/github-triage.ts @@ -1,5 +1,5 @@ /// -import { Octokit } from "@octokit/rest" +// import { Octokit } from "@octokit/rest" import { tool } from "@opencode-ai/plugin" import DESCRIPTION from "./github-triage.txt" @@ -9,6 +9,22 @@ function getIssueNumber(): number { return issue } +async function githubFetch(endpoint: string, options: RequestInit = {}) { + const response = await fetch(`https://api.github.com${endpoint}`, { + ...options, + headers: { + Authorization: `Bearer ${process.env.GITHUB_TOKEN}`, + Accept: "application/vnd.github+json", + "Content-Type": "application/json", + ...options.headers, + }, + }) + if (!response.ok) { + throw new Error(`GitHub API error: ${response.status} ${response.statusText}`) + } + return response.json() +} + export default tool({ description: DESCRIPTION, args: { @@ -23,7 +39,7 @@ export default tool({ }, async execute(args) { const issue = getIssueNumber() - const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }) + // const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN }) const owner = "sst" const repo = "opencode" @@ -41,22 +57,30 @@ export default tool({ throw new Error("Only opentui issues should be assigned to kommander") } - await octokit.rest.issues.addAssignees({ - owner, - repo, - issue_number: issue, - assignees: [args.assignee], + // await octokit.rest.issues.addAssignees({ + // owner, + // repo, + // issue_number: issue, + // assignees: [args.assignee], + // }) + await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/assignees`, { + method: "POST", + body: JSON.stringify({ assignees: [args.assignee] }), }) results.push(`Assigned @${args.assignee} to issue #${issue}`) const labels: string[] = args.labels.map((label) => (label === "desktop" ? "web" : label)) if (labels.length > 0) { - await octokit.rest.issues.addLabels({ - owner, - repo, - issue_number: issue, - labels, + // await octokit.rest.issues.addLabels({ + // owner, + // repo, + // issue_number: issue, + // labels, + // }) + await githubFetch(`/repos/${owner}/${repo}/issues/${issue}/labels`, { + method: "POST", + body: JSON.stringify({ labels }), }) results.push(`Added labels: ${args.labels.join(", ")}`) } diff --git a/AGENTS.md b/AGENTS.md index 5a95fc509..bbb2a96f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,31 +4,4 @@ ## Tool Calling -- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. Here is an example illustrating how to execute 3 parallel file reads in this chat environment: - -json -{ -"recipient_name": "multi_tool_use.parallel", -"parameters": { -"tool_uses": [ -{ -"recipient_name": "functions.read", -"parameters": { -"filePath": "path/to/file.tsx" -} -}, -{ -"recipient_name": "functions.read", -"parameters": { -"filePath": "path/to/file.ts" -} -}, -{ -"recipient_name": "functions.read", -"parameters": { -"filePath": "path/to/file.md" -} -} -] -} -} +- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. diff --git a/README.md b/README.md index 5829c6705..5295810b6 100644 --- a/README.md +++ b/README.md @@ -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 -g ubi:sst/opencode # Any OS +mise use -g github:sst/opencode # Any OS nix run nixpkgs#opencode # or github:sst/opencode for latest dev branch ``` diff --git a/README.zh-TW.md b/README.zh-TW.md new file mode 100644 index 000000000..d3cbb263c --- /dev/null +++ b/README.zh-TW.md @@ -0,0 +1,115 @@ +

+ + + + + OpenCode logo + + +

+

開源的 AI Coding Agent。

+

+ Discord + npm + Build status +

+ +[![OpenCode Terminal UI](packages/web/src/assets/lander/screenshot.png)](https://opencode.ai) + +--- + +### 安裝 + +```bash +# 直接安裝 (YOLO) +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 +paru -S opencode-bin # Arch Linux +mise use -g github:sst/opencode # 任何作業系統 +nix run nixpkgs#opencode # 或使用 github:sst/opencode 以取得最新開發分支 +``` + +> [!TIP] +> 安裝前請先移除 0.1.x 以前的舊版本。 + +### 桌面應用程式 (BETA) + +OpenCode 也提供桌面版應用程式。您可以直接從 [發佈頁面 (releases page)](https://github.com/sst/opencode/releases) 或 [opencode.ai/download](https://opencode.ai/download) 下載。 + +| 平台 | 下載連結 | +| --------------------- | ------------------------------------- | +| macOS (Apple Silicon) | `opencode-desktop-darwin-aarch64.dmg` | +| macOS (Intel) | `opencode-desktop-darwin-x64.dmg` | +| Windows | `opencode-desktop-windows-x64.exe` | +| Linux | `.deb`, `.rpm`, 或 AppImage | + +```bash +# macOS (Homebrew Cask) +brew install --cask opencode-desktop +``` + +#### 安裝目錄 + +安裝腳本會依據以下優先順序決定安裝路徑: + +1. `$OPENCODE_INSTALL_DIR` - 自定義安裝目錄 +2. `$XDG_BIN_DIR` - 符合 XDG 基礎目錄規範的路徑 +3. `$HOME/bin` - 標準使用者執行檔目錄 (若存在或可建立) +4. `$HOME/.opencode/bin` - 預設備用路徑 + +```bash +# 範例 +OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash +XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash +``` + +### Agents + +OpenCode 內建了兩種 Agent,您可以使用 `Tab` 鍵快速切換。 + +- **build** - 預設模式,具備完整權限的 Agent,適用於開發工作。 +- **plan** - 唯讀模式,適用於程式碼分析與探索。 + - 預設禁止修改檔案。 + - 執行 bash 指令前會詢問權限。 + - 非常適合用來探索陌生的程式碼庫或規劃變更。 + +此外,OpenCode 還包含一個 **general** 子 Agent,用於處理複雜搜尋與多步驟任務。此 Agent 供系統內部使用,亦可透過在訊息中輸入 `@general` 來呼叫。 + +了解更多關於 [Agents](https://opencode.ai/docs/agents) 的資訊。 + +### 線上文件 + +關於如何設定 OpenCode 的詳細資訊,請參閱我們的 [**官方文件**](https://opencode.ai/docs)。 + +### 參與貢獻 + +如果您有興趣參與 OpenCode 的開發,請在提交 Pull Request 前先閱讀我們的 [貢獻指南 (Contributing Docs)](./CONTRIBUTING.md)。 + +### 基於 OpenCode 進行開發 + +如果您正在開發與 OpenCode 相關的專案,並在名稱中使用了 "opencode"(例如 "opencode-dashboard" 或 "opencode-mobile"),請在您的 README 中加入聲明,說明該專案並非由 OpenCode 團隊開發,且與我們沒有任何隸屬關係。 + +### 常見問題 (FAQ) + +#### 這跟 Claude Code 有什麼不同? + +在功能面上與 Claude Code 非常相似。以下是關鍵差異: + +- 100% 開源。 +- 不綁定特定的服務提供商。雖然我們推薦使用透過 [OpenCode Zen](https://opencode.ai/zen) 提供的模型,但 OpenCode 也可搭配 Claude, OpenAI, Google 甚至本地模型使用。隨著模型不斷演進,彼此間的差距會縮小且價格會下降,因此具備「不限廠商 (provider-agnostic)」的特性至關重要。 +- 內建 LSP (語言伺服器協定) 支援。 +- 專注於終端機介面 (TUI)。OpenCode 由 Neovim 愛好者與 [terminal.shop](https://terminal.shop) 的創作者打造;我們將不斷挑戰終端機介面的極限。 +- 客戶端/伺服器架構 (Client/Server Architecture)。這讓 OpenCode 能夠在您的電腦上運行的同時,由行動裝置進行遠端操控。這意味著 TUI 前端只是眾多可能的客戶端之一。 + +#### 另一個同名的 Repo 是什麼? + +另一個名稱相近的儲存庫與本專案無關。您可以點此[閱讀背後的故事](https://x.com/thdxr/status/1933561254481666466)。 + +--- + +**加入我們的社群** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode) diff --git a/STATS.md b/STATS.md index 2bf0d0116..7f59be1aa 100644 --- a/STATS.md +++ b/STATS.md @@ -175,3 +175,6 @@ | 2025-12-17 | 1,151,067 (+30,590) | 1,097,661 (+19,639) | 2,248,728 (+50,229) | | 2025-12-18 | 1,178,658 (+27,591) | 1,113,418 (+15,757) | 2,292,076 (+43,348) | | 2025-12-19 | 1,203,485 (+24,827) | 1,129,698 (+16,280) | 2,333,183 (+41,107) | +| 2025-12-20 | 1,223,000 (+19,515) | 1,146,258 (+16,560) | 2,369,258 (+36,075) | +| 2025-12-21 | 1,242,675 (+19,675) | 1,158,909 (+12,651) | 2,401,584 (+32,326) | +| 2025-12-22 | 1,262,522 (+19,847) | 1,169,121 (+10,212) | 2,431,643 (+30,059) | diff --git a/bun.lock b/bun.lock index df1cf20d4..11099e1ed 100644 --- a/bun.lock +++ b/bun.lock @@ -5,13 +5,21 @@ "": { "name": "opencode", "dependencies": { + "@ai-sdk/cerebras": "1.0.33", + "@ai-sdk/cohere": "2.0.21", + "@ai-sdk/deepinfra": "1.0.30", + "@ai-sdk/gateway": "2.0.23", + "@ai-sdk/groq": "2.0.33", + "@ai-sdk/perplexity": "2.0.22", + "@ai-sdk/togetherai": "1.0.30", "@aws-sdk/client-s3": "3.933.0", - "@octokit/rest": "22.0.1", + "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", "typescript": "catalog:", }, "devDependencies": { + "@actions/artifact": "5.0.1", "@tsconfig/bun": "catalog:", "husky": "9.1.7", "prettier": "3.6.2", @@ -19,9 +27,57 @@ "turbo": "2.5.6", }, }, + "packages/app": { + "name": "@opencode-ai/app", + "version": "1.0.191", + "dependencies": { + "@kobalte/core": "catalog:", + "@opencode-ai/sdk": "workspace:*", + "@opencode-ai/ui": "workspace:*", + "@opencode-ai/util": "workspace:*", + "@shikijs/transformers": "3.9.2", + "@solid-primitives/active-element": "2.1.3", + "@solid-primitives/audio": "1.4.2", + "@solid-primitives/event-bus": "1.1.2", + "@solid-primitives/media": "2.3.3", + "@solid-primitives/resize-observer": "2.1.3", + "@solid-primitives/scroll": "2.1.3", + "@solid-primitives/storage": "catalog:", + "@solid-primitives/websocket": "1.3.1", + "@solidjs/meta": "catalog:", + "@solidjs/router": "catalog:", + "@thisbeyond/solid-dnd": "0.7.5", + "diff": "catalog:", + "fuzzysort": "catalog:", + "ghostty-web": "0.3.0", + "luxon": "catalog:", + "marked": "16.2.0", + "marked-shiki": "1.2.1", + "remeda": "catalog:", + "shiki": "catalog:", + "solid-js": "catalog:", + "solid-list": "catalog:", + "tailwindcss": "catalog:", + "virtua": "catalog:", + "zod": "catalog:", + }, + "devDependencies": { + "@happy-dom/global-registrator": "20.0.11", + "@tailwindcss/vite": "catalog:", + "@tsconfig/bun": "1.0.9", + "@types/bun": "catalog:", + "@types/luxon": "catalog:", + "@types/node": "catalog:", + "@typescript/native-preview": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vite-plugin-icons-spritesheet": "3.0.1", + "vite-plugin-solid": "catalog:", + }, + }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -49,7 +105,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -76,7 +132,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -100,7 +156,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -124,55 +180,34 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { - "@kobalte/core": "catalog:", - "@opencode-ai/sdk": "workspace:*", - "@opencode-ai/ui": "workspace:*", - "@opencode-ai/util": "workspace:*", - "@shikijs/transformers": "3.9.2", - "@solid-primitives/active-element": "2.1.3", - "@solid-primitives/audio": "1.4.2", - "@solid-primitives/event-bus": "1.1.2", - "@solid-primitives/media": "2.3.3", - "@solid-primitives/resize-observer": "2.1.3", - "@solid-primitives/scroll": "2.1.3", + "@opencode-ai/app": "workspace:*", "@solid-primitives/storage": "catalog:", - "@solid-primitives/websocket": "1.3.1", - "@solidjs/meta": "catalog:", - "@solidjs/router": "catalog:", - "@thisbeyond/solid-dnd": "0.7.5", - "diff": "catalog:", - "fuzzysort": "catalog:", - "ghostty-web": "0.3.0", - "luxon": "catalog:", - "marked": "16.2.0", - "marked-shiki": "1.2.1", - "remeda": "catalog:", - "shiki": "3.9.2", + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "~2", + "@tauri-apps/plugin-http": "~2", + "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", + "@tauri-apps/plugin-process": "~2", + "@tauri-apps/plugin-shell": "~2", + "@tauri-apps/plugin-store": "~2", + "@tauri-apps/plugin-updater": "~2", + "@tauri-apps/plugin-window-state": "~2", "solid-js": "catalog:", - "solid-list": "catalog:", - "tailwindcss": "catalog:", - "virtua": "catalog:", - "zod": "catalog:", }, "devDependencies": { - "@happy-dom/global-registrator": "20.0.11", - "@tailwindcss/vite": "catalog:", - "@tsconfig/bun": "1.0.9", + "@actions/artifact": "4.0.0", + "@tauri-apps/cli": "^2", "@types/bun": "catalog:", - "@types/luxon": "catalog:", - "@types/node": "catalog:", "@typescript/native-preview": "catalog:", - "typescript": "catalog:", + "typescript": "~5.6.2", "vite": "catalog:", - "vite-plugin-icons-spritesheet": "3.0.1", - "vite-plugin-solid": "catalog:", }, }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -201,7 +236,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -217,7 +252,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.170", + "version": "1.0.191", "bin": { "opencode": "./bin/opencode", }, @@ -231,10 +266,12 @@ "@ai-sdk/google": "2.0.44", "@ai-sdk/google-vertex": "3.0.81", "@ai-sdk/mcp": "0.0.8", + "@ai-sdk/mistral": "2.0.26", "@ai-sdk/openai": "2.0.71", "@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", + "@ai-sdk/xai": "2.0.42", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", @@ -247,8 +284,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.2", - "@opentui/core": "0.1.61", - "@opentui/solid": "0.1.61", + "@opentui/core": "0.1.63", + "@opentui/solid": "0.1.63", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", @@ -309,7 +346,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -329,7 +366,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.170", + "version": "1.0.191", "devDependencies": { "@hey-api/openapi-ts": "0.88.1", "@tsconfig/node22": "catalog:", @@ -340,7 +377,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -351,36 +388,9 @@ "typescript": "catalog:", }, }, - "packages/tauri": { - "name": "@opencode-ai/tauri", - "version": "1.0.170", - "dependencies": { - "@opencode-ai/desktop": "workspace:*", - "@solid-primitives/storage": "catalog:", - "@tauri-apps/api": "^2", - "@tauri-apps/plugin-dialog": "~2", - "@tauri-apps/plugin-http": "~2", - "@tauri-apps/plugin-opener": "^2", - "@tauri-apps/plugin-os": "~2", - "@tauri-apps/plugin-process": "~2", - "@tauri-apps/plugin-shell": "~2", - "@tauri-apps/plugin-store": "~2", - "@tauri-apps/plugin-updater": "~2", - "@tauri-apps/plugin-window-state": "~2", - "solid-js": "catalog:", - }, - "devDependencies": { - "@actions/artifact": "4.0.0", - "@tauri-apps/cli": "^2", - "@types/bun": "catalog:", - "@typescript/native-preview": "catalog:", - "typescript": "~5.6.2", - "vite": "catalog:", - }, - }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -396,7 +406,7 @@ "marked": "16.2.0", "marked-shiki": "1.2.1", "remeda": "catalog:", - "shiki": "3.9.2", + "shiki": "catalog:", "solid-js": "catalog:", "solid-list": "catalog:", "virtua": "catalog:", @@ -415,7 +425,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "zod": "catalog:", }, @@ -426,7 +436,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", @@ -445,8 +455,7 @@ "marked-shiki": "1.2.1", "rehype-autolink-headings": "7.1.0", "remeda": "catalog:", - "sharp": "0.32.5", - "shiki": "3.4.2", + "shiki": "catalog:", "solid-js": "catalog:", "toolbeam-docs-theme": "0.4.8", }, @@ -458,7 +467,6 @@ }, }, "trustedDependencies": [ - "sharp", "esbuild", "web-tree-sitter", "tree-sitter-bash", @@ -476,11 +484,11 @@ "@kobalte/core": "0.13.11", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/diffs": "1.0.0-beta.3", + "@pierre/diffs": "1.0.2", "@solid-primitives/storage": "4.3.3", "@solidjs/meta": "0.29.4", "@solidjs/router": "0.15.4", - "@solidjs/start": "https://pkg.pr.new/@solidjs/start@57aeb22", + "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", "@tailwindcss/vite": "4.1.11", "@tsconfig/bun": "1.0.9", "@tsconfig/node22": "22.0.2", @@ -495,6 +503,7 @@ "hono-openapi": "1.1.2", "luxon": "3.6.1", "remeda": "2.26.0", + "shiki": "3.20.0", "solid-js": "1.9.10", "solid-list": "0.3.0", "tailwindcss": "4.1.11", @@ -506,7 +515,7 @@ "zod": "4.1.8", }, "packages": { - "@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="], + "@actions/artifact": ["@actions/artifact@5.0.1", "", { "dependencies": { "@actions/core": "^2.0.0", "@actions/github": "^6.0.1", "@actions/http-client": "^3.0.0", "@azure/storage-blob": "^12.29.1", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-dHJ5rHduhCKUikKTT9eXeWoUvfKia3IjR1sO/VTAV3DVAL4yMTRnl2iO5mcfiBjySHLwPNezwENAVskKYU5ymw=="], "@actions/core": ["@actions/core@1.11.1", "", { "dependencies": { "@actions/exec": "^1.1.1", "@actions/http-client": "^2.0.1" } }, "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A=="], @@ -514,7 +523,7 @@ "@actions/github": ["@actions/github@6.0.1", "", { "dependencies": { "@actions/http-client": "^2.2.0", "@octokit/core": "^5.0.1", "@octokit/plugin-paginate-rest": "^9.2.2", "@octokit/plugin-rest-endpoint-methods": "^10.4.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "undici": "^5.28.5" } }, "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw=="], - "@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], + "@actions/http-client": ["@actions/http-client@3.0.0", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.28.5" } }, "sha512-1s3tXAfVMSz9a4ZEBkXXRQD4QhY3+GAsWSbaYpeknPOKEeyRiU3lH+bHiLMZdo2x/fIeQ/hscL1wCkDLVM2DZQ=="], "@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="], @@ -528,22 +537,38 @@ "@ai-sdk/azure": ["@ai-sdk/azure@2.0.73", "", { "dependencies": { "@ai-sdk/openai": "2.0.71", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LpAg3Ak/V3WOemBu35Qbx9jfQfApsHNXX9p3bXVsnRu3XXi1QQUt5gMOCIb4znPonz+XnHenIDZMBwdsb1TfRQ=="], - "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], + "@ai-sdk/cerebras": ["@ai-sdk/cerebras@1.0.33", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2gSSS/7kunIwMdC4td5oWsUAzoLw84ccGpz6wQbxVnrb1iWnrEnKa5tRBduaP6IXpzLWsu8wME3+dQhZy+gT7w=="], + + "@ai-sdk/cohere": ["@ai-sdk/cohere@2.0.21", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZjaZFvJlc5XOPi3QwTLEFZbHIgTJc6YGvxz+8zIMGVZi/hdynR8/f/C1A9x6mhzmBtAqi/dZ2h11oouAQH5z4g=="], + + "@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XK8oRZFApzo6xnS5C+FhWUUkB2itA5Nfon3pU9dJVM0goViq8GwdleZTBRqhu4DE4KJURo5DGWpJr2hfV54cEg=="], + + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-qmX7afPRszUqG5hryHF3UN8ITPIRSGmDW6VYCmByzjoUkgm3MekzSx2hMV1wr0P+llDeuXb378SjqUfpvWJulg=="], "@ai-sdk/google": ["@ai-sdk/google@2.0.44", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-c5dck36FjqiVoeeMJQLTEmUheoURcGTU/nBT6iJu8/nZiKFT/y8pD85KMDRB7RerRYaaQOtslR2d6/5PditiRw=="], "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@3.0.81", "", { "dependencies": { "@ai-sdk/anthropic": "2.0.50", "@ai-sdk/google": "2.0.44", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", "google-auth-library": "^9.15.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yrl5Ug0Mqwo9ya45oxczgy2RWgpEA/XQQCSFYP+3NZMQ4yA3Iim1vkOjVCsGaZZ8rjVk395abi1ZMZV0/6rqVA=="], + "@ai-sdk/groq": ["@ai-sdk/groq@2.0.33", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-FWGl7xNr88NBveao3y9EcVWYUt9ABPrwLFY7pIutSNgaTf32vgvyhREobaMrLU4Scr5G/2tlNqOPZ5wkYMaZig=="], + "@ai-sdk/mcp": ["@ai-sdk/mcp@0.0.8", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "pkce-challenge": "^5.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9y9GuGcZ9/+pMIHfpOCJgZVp+AZMv6TkjX2NVT17SQZvTF2N8LXuCXyoUPyi1PxIxzxl0n463LxxaB2O6olC+Q=="], + "@ai-sdk/mistral": ["@ai-sdk/mistral@2.0.26", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-jxDB++4WI1wEx5ONNBI+VbkmYJOYIuS8UQY13/83UGRaiW7oB/WHiH4ETe6KzbKpQPB3XruwTJQjUMsMfKyTXA=="], + "@ai-sdk/openai": ["@ai-sdk/openai@2.0.2", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-D4zYz2uR90aooKQvX1XnS00Z7PkbrcY+snUvPfm5bCabTG7bzLrVtD56nJ5bSaZG8lmuOMfXpyiEEArYLyWPpw=="], "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.1", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-luHVcU+yKzwv3ekKgbP3v+elUVxb2Rt+8c6w9qi7g2NYG2/pEL21oIrnaEnc6UtTZLLZX9EFBcpq2N1FQKDIMw=="], + "@ai-sdk/perplexity": ["@ai-sdk/perplexity@2.0.22", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-zwzcnk08R2J3mZcQPn4Ifl4wYGrvANR7jsBB0hCTUSbb+Rx3ybpikSWiGuXQXxdiRc1I5MWXgj70m+bZaLPvHw=="], + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.18", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ypv1xXMsgGcNKUP+hglKqtdDuMg68nWHucPPAhIENrbFAI+xCHiqPVN8Zllxyv1TNZwGWUghPxJXU+Mqps0YRQ=="], + "@ai-sdk/togetherai": ["@ai-sdk/togetherai@1.0.30", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9bxQbIXnWSN4bNismrza3NvIo+ui/Y3pj3UN6e9vCszCWFCN45RgISi4oDe10RqmzaJ/X8cfO/Tem+K8MT3wGQ=="], + + "@ai-sdk/xai": ["@ai-sdk/xai@2.0.42", "", { "dependencies": { "@ai-sdk/openai-compatible": "1.0.29", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-wlwO4yRoZ/d+ca29vN8SDzxus7POdnL7GBTyRdSrt6icUF0hooLesauC8qRUC4aLxtqvMEc1YHtJOU7ZnLWbTQ=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], @@ -650,7 +675,7 @@ "@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.1", "", {}, "sha512-sIyFcoPZkTtNu9xFeEoynMef3bPJIAbOfUh+ueYcfhVl6xm2VRtMcMclSxmZCMnHHd4hlYKJeq/aggmBEWynww=="], - "@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="], + "@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], "@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="], @@ -666,7 +691,7 @@ "@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.22.2", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg=="], - "@azure/core-tracing": ["@azure/core-tracing@1.0.0-preview.13", "", { "dependencies": { "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" } }, "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ=="], + "@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="], "@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="], @@ -1088,11 +1113,11 @@ "@octokit/auth-oauth-user": ["@octokit/auth-oauth-user@6.0.2", "", { "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A=="], - "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], + "@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="], - "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], + "@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="], - "@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + "@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="], "@octokit/graphql": ["@octokit/graphql@9.0.2", "", { "dependencies": { "@octokit/request": "^10.0.4", "@octokit/types": "^15.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw=="], @@ -1102,19 +1127,19 @@ "@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], - "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@14.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw=="], + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="], - "@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="], + "@octokit/plugin-request-log": ["@octokit/plugin-request-log@1.0.4", "", { "peerDependencies": { "@octokit/core": ">=3" } }, "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA=="], - "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@17.0.0", "", { "dependencies": { "@octokit/types": "^16.0.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw=="], + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="], "@octokit/plugin-retry": ["@octokit/plugin-retry@3.0.9", "", { "dependencies": { "@octokit/types": "^6.0.3", "bottleneck": "^2.15.3" } }, "sha512-r+fArdP5+TG6l1Rv/C9hVoty6tldw6cE2pRHNGmFPdyfrc696R6JjrQ3d7HdVqGwuzfyrcaLAKD7K8TX8aehUQ=="], - "@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=="], + "@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="], - "@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + "@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="], - "@octokit/rest": ["@octokit/rest@22.0.1", "", { "dependencies": { "@octokit/core": "^7.0.6", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0" } }, "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw=="], + "@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="], "@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="], @@ -1124,6 +1149,8 @@ "@openauthjs/openauth": ["@openauthjs/openauth@0.0.0-20250322224806", "", { "dependencies": { "@standard-schema/spec": "1.0.0-beta.3", "aws4fetch": "1.0.20", "jose": "5.9.6" }, "peerDependencies": { "arctic": "^2.2.2", "hono": "^4.0.0" } }, "sha512-p5IWSRXvABcwocH2dNI0w8c1QJelIOFulwhKk+aLLFfUbs8u1pr7kQbYe8yCSM2+bcLHiwbogpUQc2ovrGwCuw=="], + "@opencode-ai/app": ["@opencode-ai/app@workspace:packages/app"], + "@opencode-ai/console-app": ["@opencode-ai/console-app@workspace:packages/console/app"], "@opencode-ai/console-core": ["@opencode-ai/console-core@workspace:packages/console/core"], @@ -1148,8 +1175,6 @@ "@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"], - "@opencode-ai/tauri": ["@opencode-ai/tauri@workspace:packages/tauri"], - "@opencode-ai/ui": ["@opencode-ai/ui@workspace:packages/ui"], "@opencode-ai/util": ["@opencode-ai/util@workspace:packages/util"], @@ -1162,21 +1187,21 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentui/core": ["@opentui/core@0.1.61", "", { "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.61", "@opentui/core-darwin-x64": "0.1.61", "@opentui/core-linux-arm64": "0.1.61", "@opentui/core-linux-x64": "0.1.61", "@opentui/core-win32-arm64": "0.1.61", "@opentui/core-win32-x64": "0.1.61", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-WrVbdki0tnsgmWCB3Iix6n8eXGXUheTqr/tcnBN7gLA/TqT9udcX+DW3/qRdgtTNJS1sVBVeuwSTYU3eqDSUJQ=="], + "@opentui/core": ["@opentui/core@0.1.63", "", { "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.63", "@opentui/core-darwin-x64": "0.1.63", "@opentui/core-linux-arm64": "0.1.63", "@opentui/core-linux-x64": "0.1.63", "@opentui/core-win32-arm64": "0.1.63", "@opentui/core-win32-x64": "0.1.63", "bun-webgpu": "0.1.4", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-m4xZQTNCnHXWUWCnGvacJ3Gts1H2aMwP5V/puAG77SDb51jm4W/QOyqAAdgeSakkb9II+8FfUpApX7sfwRXPUg=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.61", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zX7EK8PwBJFwsZ2tDnScLFD0GbBfHE7sqpzGDXP2luMnBZJ0OOO95a4Hzu9dQWqxEr4RgfGDT8uIRhgimKNQEg=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.63", "", { "os": "darwin", "cpu": "arm64" }, "sha512-jKCThZGiiublKkP/hMtDtl1MLCw5NU0hMNJdEYvz1WLT9bzliWf6Kb7MIDAmk32XlbQW8/RHdp+hGyGDXK62OQ=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.61", "", { "os": "darwin", "cpu": "x64" }, "sha512-xfvl8EnyN0XwlYpyTskVhHOpbMdgt++ntcuTh7M7IEFYQGzJux19NBwJl17mOxB1McG+KTa7kNx5/zu0VB9eVQ=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.63", "", { "os": "darwin", "cpu": "x64" }, "sha512-rfNxynHzJpxN9i+SAMnn1NToEc8rYj64BsOxY78JNsm4Gg1Js1uyMaawwh2WbdGknFy4cDXS9QwkUMdMcfnjiw=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.61", "", { "os": "linux", "cpu": "arm64" }, "sha512-Ghg7j4H6bz7CLxhgDcWx3Ann3AblDIjKFUu4vFrVysuiwfmDHwdKm8awLj8tnmC/0y8juG4ODUQbR0BXBIkE+Q=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.63", "", { "os": "linux", "cpu": "arm64" }, "sha512-wG9d6mHWWKZGrzxYS4c+BrcEGXBv/MYBUPSyjP/lD0CxT+X3h6CYhI317JkRyMNfh3vI9CpAKGFTOFvrTTHimQ=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.61", "", { "os": "linux", "cpu": "x64" }, "sha512-Xs9czMEOuHtnX4tigC4fNb1MU7+Gaohbk+k4teraulIgYZf19nRHIKNvXissDjOfqvOGygCkxMQIG0zeUFsPEA=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.63", "", { "os": "linux", "cpu": "x64" }, "sha512-TKSzFv4BgWW3RB/iZmq5qxTR4/tRaXo8IZNnVR+LFzShbPOqhUi466AByy9SUmCxD8uYjmMDFYfKtkCy0AnAwA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.61", "", { "os": "win32", "cpu": "arm64" }, "sha512-2CYAEPqArJqE36LkSRAs0csRzWwVJY99S/7EuY7abBm58BIL6RUw5kSw1r75oDo4I3W6v6WwW0u8B5Ik98m0Kg=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.63", "", { "os": "win32", "cpu": "arm64" }, "sha512-CBWPyPognERP0Mq4eC1q01Ado2C2WU+BLTgMdhyt+E2P4w8rPhJ2kCt2MNxO66vQUiynspmZkgjQr0II/VjxWA=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.61", "", { "os": "win32", "cpu": "x64" }, "sha512-c0OK5YwcKH51Qj6wPmwTZP3X8LHA0I0dKz4fO4mOh4f+OqgU9WOG4hpbf7lv0bVlHoTvgP4zDUsjmtIVA8l6Lg=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.63", "", { "os": "win32", "cpu": "x64" }, "sha512-qEp6h//FrT+TQiiHm87wZWUwqTPTqIy1ZD+8R+VCUK+usoQiOAD2SqrYnM7W8JkCMGn5/TKm/GaKLyx/qlK4VA=="], - "@opentui/solid": ["@opentui/solid@0.1.61", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.61", "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-CiZHduIoeABoS0ev+eGeHA/LiRl/SpdL6io4jrwiwFi/rToKtc7YgJ8MWxIgeHScHUbpQnIr1v7jzsGI3DAYvw=="], + "@opentui/solid": ["@opentui/solid@0.1.63", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.63", "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-Gccln4qRucAoaoQEZ4NPAHvGmVYzU/8aKCLG8EPgwCKTcpUzlqYt4357cDHq4cnCNOcXOC06hTz/0pK9r0dqXA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -1292,7 +1317,7 @@ "@petamoriken/float16": ["@petamoriken/float16@3.9.3", "", {}, "sha512-8awtpHXCx/bNpFt4mt2xdkgtgVvKqty8VbjHI/WWWQuEw+KLzFot3f4+LkQY9YmOtq7A5GdOnqoIC8Pdygjk2g=="], - "@pierre/diffs": ["@pierre/diffs@1.0.0-beta.3", "", { "dependencies": { "@shikijs/core": "3.19.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/transformers": "3.19.0", "diff": "8.0.2", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "3.19.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-W3dFWdFOBZ9OskGSOgN16aci8dsUyAavCxz3ZvbbVLTb2qRzMZ7H90qdfON13/N2l1HTyh84lkrCs1/sDvnRjQ=="], + "@pierre/diffs": ["@pierre/diffs@1.0.2", "", { "dependencies": { "@shikijs/core": "^3.0.0", "@shikijs/engine-javascript": "3.19.0", "@shikijs/transformers": "3.19.0", "diff": "8.0.2", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "3.19.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-RkFSDD5X/U+8QjyilPViYGJfmJNWXR17zTL8zw48+DcVC1Ujbh6I1edyuRnFfgRzpft05x2DSCkz2cjoIAxPvQ=="], "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], @@ -1428,13 +1453,13 @@ "@shikijs/core": ["@shikijs/core@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-3q/mzmw09B2B6PgFNeiaN8pkNOixWS726IHmJEpjDAcneDPMQmUg2cweT9cWXY4XcyQS3i6mOOUgQz9RRUP6HA=="], - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-kUTRVKPsB/28H5Ko6qEsyudBiWEDLst+Sfi+hwr59E0GLHV0h8RfgbQU7fdN5Lt9A8R1ulRiZyTvAizkROjwDA=="], + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-OFx8fHAZuk7I42Z9YAdZ95To6jDePQ9Rnfbw9uSRTSbBhYBp1kEOKv/3jOimcj3VRUKusDYM6DswLauwfhboLg=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-Vn/w5oyQ6TUgTVDIC/BrpXwIlfK6V6kGWDVVz2eRkF2v13YoENUvaNwxMsQU/t6oCuZKzqp9vqtEtEzKl9VegA=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-Yx3gy7xLzM0ZOjqoxciHjA7dAt5tyzJE3L4uQoM83agahy+PlW244XJSrmJRSBvGYELDhYXPacD4R/cauV5bzQ=="], - "@shikijs/langs": ["@shikijs/langs@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2" } }, "sha512-X1Q6wRRQXY7HqAuX3I8WjMscjeGjqXCg/Sve7J2GWFORXkSrXud23UECqTBIdCSNKJioFtmUGJQNKtlMMZMn0w=="], + "@shikijs/langs": ["@shikijs/langs@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-le+bssCxcSHrygCWuOrYJHvjus6zhQ2K7q/0mgjiffRbkhM4o1EWu2m+29l0yEsHDbWaWPNnDUTRVVBvBBeKaA=="], - "@shikijs/themes": ["@shikijs/themes@3.9.2", "", { "dependencies": { "@shikijs/types": "3.9.2" } }, "sha512-6z5lBPBMRfLyyEsgf6uJDHPa6NAGVzFJqH4EAZ+03+7sedYir2yJBRu2uPZOKmj43GyhVHWHvyduLDAwJQfDjA=="], + "@shikijs/themes": ["@shikijs/themes@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0" } }, "sha512-U1NSU7Sl26Q7ErRvJUouArxfM2euWqq1xaSrbqMu2iqa+tSp0D1Yah8216sDYbdDHw4C8b75UpE65eWorm2erQ=="], "@shikijs/transformers": ["@shikijs/transformers@3.9.2", "", { "dependencies": { "@shikijs/core": "3.9.2", "@shikijs/types": "3.9.2" } }, "sha512-MW5hT4TyUp6bNAgTExRYLk1NNasVQMTCw1kgbxHcEC0O5cbepPWaB+1k+JzW9r3SP2/R8kiens8/3E6hGKfgsA=="], @@ -1598,7 +1623,7 @@ "@solidjs/router": ["@solidjs/router@0.15.4", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ=="], - "@solidjs/start": ["@solidjs/start@https://pkg.pr.new/@solidjs/start@57aeb22", { "dependencies": { "@babel/core": "^7.28.3", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.5", "@solidjs/meta": "^0.29.4", "@tanstack/server-functions-plugin": "1.134.5", "@types/babel__traverse": "^7.28.0", "@types/micromatch": "^4.0.9", "cookie-es": "^2.0.0", "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.3", "fast-glob": "^3.3.3", "h3": "npm:h3@2.0.1-rc.4", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "path-to-regexp": "^8.2.0", "pathe": "^2.0.3", "radix3": "^1.1.2", "seroval": "^1.3.2", "seroval-plugins": "^1.2.1", "shiki": "^1.26.1", "solid-js": "^1.9.9", "source-map-js": "^1.2.1", "srvx": "^0.9.1", "terracotta": "^1.0.6", "vite-plugin-solid": "^2.11.9" }, "peerDependencies": { "vite": "^7" } }], + "@solidjs/start": ["@solidjs/start@https://pkg.pr.new/@solidjs/start@dfb2020", { "dependencies": { "@babel/core": "^7.28.3", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.5", "@solidjs/meta": "^0.29.4", "@tanstack/server-functions-plugin": "1.134.5", "@types/babel__traverse": "^7.28.0", "@types/micromatch": "^4.0.9", "cookie-es": "^2.0.0", "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.3", "fast-glob": "^3.3.3", "h3": "npm:h3@2.0.1-rc.4", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "path-to-regexp": "^8.2.0", "pathe": "^2.0.3", "radix3": "^1.1.2", "seroval": "^1.3.2", "seroval-plugins": "^1.2.1", "shiki": "^1.26.1", "solid-js": "^1.9.9", "source-map-js": "^1.2.1", "srvx": "^0.9.1", "terracotta": "^1.0.6", "vite": "7.1.10", "vite-plugin-solid": "^2.11.9", "vitest": "^4.0.10" } }], "@speed-highlight/core": ["@speed-highlight/core@1.2.12", "", {}, "sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA=="], @@ -1714,10 +1739,14 @@ "@types/bun": ["@types/bun@1.3.4", "", { "dependencies": { "bun-types": "1.3.4" } }, "sha512-EEPTKXHP+zKGPkhRLv+HI0UEX8/o+65hqARxLy8Ov5rIxMBPNTjeZww00CIihrIQGEQBYg+0roO5qOnS/7boGA=="], + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], @@ -1820,6 +1849,20 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@vitest/expect": ["@vitest/expect@4.0.16", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.16", "@vitest/utils": "4.0.16", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA=="], + + "@vitest/mocker": ["@vitest/mocker@4.0.16", "", { "dependencies": { "@vitest/spy": "4.0.16", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@4.0.16", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA=="], + + "@vitest/runner": ["@vitest/runner@4.0.16", "", { "dependencies": { "@vitest/utils": "4.0.16", "pathe": "^2.0.3" } }, "sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q=="], + + "@vitest/snapshot": ["@vitest/snapshot@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA=="], + + "@vitest/spy": ["@vitest/spy@4.0.16", "", {}, "sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw=="], + + "@vitest/utils": ["@vitest/utils@4.0.16", "", { "dependencies": { "@vitest/pretty-format": "4.0.16", "tinyrainbow": "^3.0.3" } }, "sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA=="], + "@webgpu/types": ["@webgpu/types@0.1.66", "", {}, "sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA=="], "@zip.js/zip.js": ["@zip.js/zip.js@2.7.62", "", {}, "sha512-OaLvZ8j4gCkLn048ypkZu29KX30r8/OfFF2w4Jo5WXFr+J04J+lzJ5TKZBVgFXhlvSkqNFQdfnY1Q8TMTCyBVA=="], @@ -1886,6 +1929,8 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="], "astro": ["astro@5.7.13", "", { "dependencies": { "@astrojs/compiler": "^2.11.0", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.1", "@astrojs/telemetry": "3.2.1", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-cRGq2llKOhV3XMcYwQpfBIUcssN6HEK5CRbcMxAfd9OcFhvWE7KUy50zLioAZVVl3AqgUTJoNTlmZfD2eG0G1w=="], @@ -1930,16 +1975,6 @@ "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], - "bare-fs": ["bare-fs@4.5.1", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg=="], - - "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], - - "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], - - "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], - - "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], - "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], @@ -1950,7 +1985,7 @@ "bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="], - "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + "before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="], "bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="], @@ -1958,8 +1993,6 @@ "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], "blob-to-buffer": ["blob-to-buffer@1.2.9", "", {}, "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA=="], @@ -2032,9 +2065,11 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@6.2.1", "", {}, "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg=="], + "chainsaw": ["chainsaw@0.1.0", "", { "dependencies": { "traverse": ">=0.3.0 <0.4" } }, "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ=="], - "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -2160,10 +2195,6 @@ "decode-named-character-reference": ["decode-named-character-reference@1.2.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q=="], - "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], - - "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], - "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], "default-browser": ["default-browser@5.4.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg=="], @@ -2192,7 +2223,7 @@ "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], @@ -2252,8 +2283,6 @@ "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], - "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="], "entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], @@ -2320,7 +2349,7 @@ "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="], + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], @@ -2334,7 +2363,7 @@ "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], - "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], @@ -2402,8 +2431,6 @@ "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], - "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -2454,8 +2481,6 @@ "giget": ["giget@2.0.0", "", { "dependencies": { "citty": "^0.1.6", "consola": "^3.4.0", "defu": "^6.1.4", "node-fetch-native": "^1.6.6", "nypm": "^0.6.0", "pathe": "^2.0.3" }, "bin": { "giget": "dist/cli.mjs" } }, "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA=="], - "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], - "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], "glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="], @@ -2980,8 +3005,6 @@ "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="], - "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], - "miniflare": ["miniflare@4.20251118.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "7.14.0", "workerd": "1.20251118.0", "ws": "8.18.0", "youch": "4.1.0-beta.10", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-uLSAE/DvOm392fiaig4LOaatxLjM7xzIniFRG5Y3yF9IduOYLLK/pkCPQNCgKQH3ou0YJRHnTN+09LPfqYNTQQ=="], "minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], @@ -2994,8 +3017,6 @@ "mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], - "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], - "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], @@ -3010,8 +3031,6 @@ "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], - "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], @@ -3024,9 +3043,7 @@ "no-case": ["no-case@3.0.4", "", { "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg=="], - "node-abi": ["node-abi@3.85.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg=="], - - "node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], @@ -3064,6 +3081,8 @@ "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="], + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + "ofetch": ["ofetch@2.0.0-alpha.3", "", {}, "sha512-zpYTCs2byOuft65vI3z43Dd6iSdFbOZZLb9/d21aCpx2rGastVU9dOCv0lu4ykc1Ur1anAYjDi3SUvR0vq50JA=="], "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], @@ -3216,8 +3235,6 @@ "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], - "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "pretty": ["pretty@2.0.0", "", { "dependencies": { "condense-newlines": "^0.2.1", "extend-shallow": "^2.0.1", "js-beautify": "^1.6.12" } }, "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w=="], @@ -3240,8 +3257,6 @@ "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], - "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], - "punycode": ["punycode@1.3.2", "", {}, "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="], "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], @@ -3258,8 +3273,6 @@ "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "rc9": ["rc9@2.1.2", "", { "dependencies": { "defu": "^6.1.4", "destr": "^2.0.3" } }, "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg=="], "react": ["react@18.2.0", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="], @@ -3418,7 +3431,7 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "sharp": ["sharp@0.32.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.2", "node-addon-api": "^6.1.0", "prebuild-install": "^7.1.1", "semver": "^7.5.4", "simple-get": "^4.0.1", "tar-fs": "^3.0.4", "tunnel-agent": "^0.6.0" } }, "sha512-0dap3iysgDkNaPOaOL4X/0akdu0ma62GcdC2NBQ+93eqpePdDdr2/LM0sFdDSMmN7yS+odyZtPsb7tx/cYBKnQ=="], + "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], @@ -3426,7 +3439,7 @@ "shell-quote": ["shell-quote@1.8.3", "", {}, "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw=="], - "shiki": ["shiki@3.9.2", "", { "dependencies": { "@shikijs/core": "3.9.2", "@shikijs/engine-javascript": "3.9.2", "@shikijs/engine-oniguruma": "3.9.2", "@shikijs/langs": "3.9.2", "@shikijs/themes": "3.9.2", "@shikijs/types": "3.9.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-t6NKl5e/zGTvw/IyftLcumolgOczhuroqwXngDeMqJ3h3EQiTY/7wmfgPlsmloD8oYfqkEDqxiaH37Pjm1zUhQ=="], + "shiki": ["shiki@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/engine-javascript": "3.20.0", "@shikijs/engine-oniguruma": "3.20.0", "@shikijs/langs": "3.20.0", "@shikijs/themes": "3.20.0", "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kgCOlsnyWb+p0WU+01RjkCH+eBVsjL1jOwUYWv0YDWkM2/A46+LDKVs5yZCUXjJG6bj4ndFoAg5iLIIue6dulg=="], "shikiji": ["shikiji@0.6.13", "", { "dependencies": { "hast-util-to-html": "^9.0.0" } }, "sha512-4T7X39csvhT0p7GDnq9vysWddf2b6BeioiN3Ymhnt3xcy9tXmDcnsEFVxX18Z4YcQgEE/w48dLJ4pPPUcG9KkA=="], @@ -3438,12 +3451,10 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], - - "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], "simple-xml-to-json": ["simple-xml-to-json@1.2.3", "", {}, "sha512-kWJDCr9EWtZ+/EYYM5MareWj2cRnZGF93YDNpH4jQiHB+hBIZnfPFSQiVMzZOdk+zXWqTZ/9fTeQNu2DqeiudA=="], @@ -3500,6 +3511,8 @@ "sst-win32-x86": ["sst-win32-x86@3.17.23", "", { "os": "win32", "cpu": "none" }, "sha512-DIp3s54IpNAfdYjSRt6McvkbEPQDMxUu6RUeRAd2C+FcTJgTloon/ghAPQBaDgu2VoVgymjcJARO/XyfKcCLOQ=="], + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + "stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="], "stage-js": ["stage-js@1.0.0-alpha.17", "", {}, "sha512-AzlMO+t51v6cFvKZ+Oe9DJnL1OXEH5s9bEy6di5aOrUpcP7PCzI/wIeXF0u3zg0L89gwnceoKxrLId0ZpYnNXw=="], @@ -3508,6 +3521,8 @@ "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], @@ -3538,8 +3553,6 @@ "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - "stripe": ["stripe@18.0.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-3Fs33IzKUby//9kCkCa1uRpinAoTvj6rJgQ2jrBEysoxEvfsclvXdna1amyEYbA2EKkjynuB4+L/kleCCaWTpA=="], "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], @@ -3566,8 +3579,6 @@ "tar": ["tar@7.5.2", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg=="], - "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], - "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], "terracotta": ["terracotta@1.0.6", "", { "dependencies": { "solid-use": "^0.9.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-yVrmT/Lg6a3tEbeYEJH8ksb1PYkR5FA9k5gr1TchaSNIiA2ZWs5a+koEbePXwlBP0poaV7xViZ/v50bQFcMgqw=="], @@ -3586,12 +3597,16 @@ "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], + "titleize": ["titleize@4.0.0", "", {}, "sha512-ZgUJ1K83rhdu7uh7EHAC2BgY5DzoX8V5rTvoWI4vFysggi6YjLe5gUXABPWAU7VkvGP7P/0YiWq+dcPeYDsf1g=="], "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], @@ -3624,8 +3639,6 @@ "tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="], - "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - "turbo": ["turbo@2.5.6", "", { "optionalDependencies": { "turbo-darwin-64": "2.5.6", "turbo-darwin-arm64": "2.5.6", "turbo-linux-64": "2.5.6", "turbo-linux-arm64": "2.5.6", "turbo-windows-64": "2.5.6", "turbo-windows-arm64": "2.5.6" }, "bin": { "turbo": "bin/turbo" } }, "sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w=="], "turbo-darwin-64": ["turbo-darwin-64@2.5.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A=="], @@ -3754,6 +3767,8 @@ "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + "vitest": ["vitest@4.0.16", "", { "dependencies": { "@vitest/expect": "4.0.16", "@vitest/mocker": "4.0.16", "@vitest/pretty-format": "4.0.16", "@vitest/runner": "4.0.16", "@vitest/snapshot": "4.0.16", "@vitest/spy": "4.0.16", "@vitest/utils": "4.0.16", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.16", "@vitest/browser-preview": "4.0.16", "@vitest/browser-webdriverio": "4.0.16", "@vitest/ui": "4.0.16", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q=="], + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="], "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], @@ -3842,24 +3857,16 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@actions/artifact/@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="], + "@actions/artifact/@actions/core": ["@actions/core@2.0.1", "", { "dependencies": { "@actions/exec": "^2.0.0", "@actions/http-client": "^3.0.0" } }, "sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg=="], - "@actions/artifact/@octokit/plugin-request-log": ["@octokit/plugin-request-log@1.0.4", "", { "peerDependencies": { "@octokit/core": ">=3" } }, "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA=="], + "@actions/core/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], - "@actions/artifact/@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="], - - "@actions/artifact/@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="], - - "@actions/github/@octokit/core": ["@octokit/core@5.2.2", "", { "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.0.0", "before-after-hook": "^2.2.0", "universal-user-agent": "^6.0.0" } }, "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg=="], + "@actions/github/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], "@actions/github/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@9.2.2", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ=="], "@actions/github/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@10.4.1", "", { "dependencies": { "@octokit/types": "^12.6.0" }, "peerDependencies": { "@octokit/core": "5" } }, "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg=="], - "@actions/github/@octokit/request": ["@octokit/request@8.4.1", "", { "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw=="], - - "@actions/github/@octokit/request-error": ["@octokit/request-error@5.1.1", "", { "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g=="], - "@actions/github/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], "@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], @@ -3876,16 +3883,40 @@ "@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], + + "@ai-sdk/cerebras/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + + "@ai-sdk/cohere/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + + "@ai-sdk/deepinfra/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], + + "@ai-sdk/deepinfra/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + + "@ai-sdk/gateway/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-21PaHfoLmouOXXNINTsZJsMw+wE5oLR2He/1kq/sKokTVKyq7ObGT1LDk6ahwxaz/GoaNaGankMh+EgVcdv2Cw=="], + "@ai-sdk/groq/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/mcp/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "@ai-sdk/mistral/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="], "@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.0", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.3", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw=="], + "@ai-sdk/perplexity/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + + "@ai-sdk/togetherai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], + + "@ai-sdk/togetherai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + + "@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.29", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cZUppWzxjfpNaH1oVZ6U8yDLKKsdGbC9X0Pex8cG9CXhKWSoVLLnW1rKr6tu9jDISK5okjBIW/O1ZzfnbUrtEw=="], + + "@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.19", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA=="], + "@astrojs/cloudflare/vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], "@astrojs/markdown-remark/@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.6.1", "", {}, "sha512-l5Pqf6uZu31aG+3Lv8nl/3s4DbUzdlxTWDof4pEpto6GUJNhhCbelVi9dEyurOVyqaelwmS9oSyOWOENSfgo9A=="], @@ -3930,40 +3961,16 @@ "@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@azure/core-auth/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], + "@azure/core-http/@azure/abort-controller": ["@azure/abort-controller@1.1.0", "", { "dependencies": { "tslib": "^2.2.0" } }, "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw=="], - "@azure/core-client/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/core-client/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="], + "@azure/core-http/@azure/core-tracing": ["@azure/core-tracing@1.0.0-preview.13", "", { "dependencies": { "@opentelemetry/api": "^1.0.1", "tslib": "^2.2.0" } }, "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ=="], "@azure/core-http/uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], "@azure/core-http/xml2js": ["xml2js@0.5.0", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA=="], - "@azure/core-http-compat/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/core-lro/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/core-rest-pipeline/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/core-rest-pipeline/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="], - - "@azure/core-util/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - "@azure/core-xml/fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="], - "@azure/storage-blob/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/storage-blob/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="], - - "@azure/storage-blob/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - - "@azure/storage-common/@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="], - - "@azure/storage-common/@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="], - - "@azure/storage-common/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="], @@ -3976,6 +3983,8 @@ "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + "@dot/log/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "@expressive-code/plugin-shiki/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="], @@ -4022,6 +4031,8 @@ "@jimp/types/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@jsx-email/cli/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@jsx-email/cli/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="], "@jsx-email/cli/tailwindcss": ["tailwindcss@3.3.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.2.12", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.18.2", "lilconfig": "^2.1.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.0.0", "postcss": "^8.4.23", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.1", "postcss-nested": "^6.0.1", "postcss-selector-parser": "^6.0.11", "resolve": "^1.22.2", "sucrase": "^3.32.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w=="], @@ -4036,57 +4047,81 @@ "@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@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=="], + + "@octokit/auth-app/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + + "@octokit/auth-oauth-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=="], + "@octokit/auth-oauth-app/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/auth-oauth-device/@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=="], + "@octokit/auth-oauth-device/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/auth-oauth-user/@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=="], + "@octokit/auth-oauth-user/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], - "@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], + "@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="], - "@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - "@octokit/endpoint/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], + + "@octokit/endpoint/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + + "@octokit/endpoint/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], + + "@octokit/graphql/@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=="], "@octokit/graphql/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], + "@octokit/oauth-methods/@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=="], + + "@octokit/oauth-methods/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + "@octokit/oauth-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], - "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/plugin-paginate-rest/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], - "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], "@octokit/plugin-retry/@octokit/types": ["@octokit/types@6.41.0", "", { "dependencies": { "@octokit/openapi-types": "^12.11.0" } }, "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg=="], - "@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - "@octokit/request-error/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + "@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], + + "@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + + "@octokit/rest/@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], + + "@octokit/rest/@octokit/plugin-request-log": ["@octokit/plugin-request-log@6.0.0", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q=="], "@openauthjs/openauth/@standard-schema/spec": ["@standard-schema/spec@1.0.0-beta.3", "", {}, "sha512-0ifF3BjA1E8SY9C+nUew8RefNOIq0cDlYALPty4rhUm8Rrl6tCM8hBT4bhGhx7I7iXD0uAgt50lgo8dD73ACMw=="], "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], - "@opencode-ai/function/@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="], + "@opencode-ai/desktop/@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="], - "@opencode-ai/tauri/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], + "@opencode-ai/desktop/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="], "@opencode-ai/web/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], - "@opencode-ai/web/shiki": ["shiki@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/engine-javascript": "3.4.2", "@shikijs/engine-oniguruma": "3.4.2", "@shikijs/langs": "3.4.2", "@shikijs/themes": "3.4.2", "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-wuxzZzQG8kvZndD7nustrNFIKYJ1jJoWIPaBpVe2+KHSvtzMi4SBjOxrigs8qeqce/l3U0cwiC+VAkLKSunHQQ=="], - "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], "@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.9", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.1" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.8" }, "optionalPeers": ["solid-js"] }, "sha512-pCnxWrciluXCeli/dj5PIEHgbNzim3evtTn12snjqqg8QZWJNMjH1AWIp4iG/tbVjqQ72aBEymMSagvmgxubXw=="], "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], - "@parcel/watcher/detect-libc": ["detect-libc@1.0.3", "", { "bin": { "detect-libc": "./bin/detect-libc.js" } }, "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg=="], - - "@parcel/watcher/node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], - - "@pierre/diffs/@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + "@pierre/diffs/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="], "@pierre/diffs/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-ZfWJNm2VMhKkQIKT9qXbs76RRcT0SF/CAvEz0+RkpUDAoDaCx0uFdCGzSRiD9gSlhm6AHkjdieOBJMaO2eC1rQ=="], @@ -4100,6 +4135,14 @@ "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + "@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + + "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + + "@shikijs/langs/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + + "@shikijs/themes/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + "@slack/bolt/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], "@slack/oauth/@slack/logger": ["@slack/logger@3.0.0", "", { "dependencies": { "@types/node": ">=12.0.0" } }, "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA=="], @@ -4122,6 +4165,10 @@ "@solidjs/start/shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="], + "@solidjs/start/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], + + "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.7.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.7.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA=="], @@ -4136,6 +4183,8 @@ "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "ai/@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.12", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-W+cB1sOWvPcz9qiIsNtD+HxUrBUva2vWv2K1EFukuImX+HA0uZx3EyyOjhYQ9gtf/teqEG80M6OvJ7xx/VLV2A=="], + "ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -4150,8 +4199,6 @@ "astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], - "astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="], "astro/unstorage": ["unstorage@1.17.3", "", { "dependencies": { "anymatch": "^3.1.3", "chokidar": "^4.0.3", "destr": "^2.0.5", "h3": "^1.15.4", "lru-cache": "^10.4.3", "node-fetch-native": "^1.6.7", "ofetch": "^1.5.1", "ufo": "^1.6.1" }, "peerDependencies": { "@azure/app-configuration": "^1.8.0", "@azure/cosmos": "^4.2.0", "@azure/data-tables": "^13.3.0", "@azure/identity": "^4.6.0", "@azure/keyvault-secrets": "^4.9.0", "@azure/storage-blob": "^12.26.0", "@capacitor/preferences": "^6.0.3 || ^7.0.0", "@deno/kv": ">=0.9.0", "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", "@planetscale/database": "^1.19.0", "@upstash/redis": "^1.34.3", "@vercel/blob": ">=0.27.1", "@vercel/functions": "^2.2.12 || ^3.0.0", "@vercel/kv": "^1.0.1", "aws4fetch": "^1.0.20", "db0": ">=0.2.1", "idb-keyval": "^6.2.1", "ioredis": "^5.4.2", "uploadthing": "^7.4.4" }, "optionalPeers": ["@azure/app-configuration", "@azure/cosmos", "@azure/data-tables", "@azure/identity", "@azure/keyvault-secrets", "@azure/storage-blob", "@capacitor/preferences", "@deno/kv", "@netlify/blobs", "@planetscale/database", "@upstash/redis", "@vercel/blob", "@vercel/functions", "@vercel/kv", "aws4fetch", "db0", "idb-keyval", "ioredis", "uploadthing"] }, "sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q=="], @@ -4160,22 +4207,18 @@ "astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "aws-sdk/events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="], + "babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="], "babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="], - "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - - "bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "body-parser/qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], - "boxen/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "clean-css/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -4194,6 +4237,8 @@ "es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "execa/is-stream": ["is-stream@3.0.0", "", {}, "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA=="], @@ -4230,6 +4275,8 @@ "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "md-to-react-email/marked": ["marked@7.0.4", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-t8eP0dXRJMtMvBojtkcsA7n48BkauktUKzfkPSCq85ZMTJ0v76Rke4DYz01omYpPTUh4p/f7HePgRo3ebG8+QQ=="], "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], @@ -4238,8 +4285,6 @@ "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], - "miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "miniflare/undici": ["undici@7.14.0", "", {}, "sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ=="], "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], @@ -4258,8 +4303,6 @@ "opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@1.0.27", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bpYruxVLhrTbVH6CCq48zMJNeHu6FmHtEedl9FXckEgcIEAi036idFhJlcRwC1jNCwlacbzb8dPD7OAH1EKJaQ=="], - "opencode/@octokit/rest": ["@octokit/rest@22.0.0", "", { "dependencies": { "@octokit/core": "^7.0.2", "@octokit/plugin-paginate-rest": "^13.0.1", "@octokit/plugin-request-log": "^6.0.0", "@octokit/plugin-rest-endpoint-methods": "^16.0.0" } }, "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA=="], - "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="], "opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="], @@ -4288,16 +4331,12 @@ "postcss-load-config/lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], - "prebuild-install/tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], - "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], "readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="], - "readable-stream/events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "readdir-glob/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], "router/path-to-regexp": ["path-to-regexp@8.3.0", "", {}, "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA=="], @@ -4312,6 +4351,12 @@ "send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + "sharp/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "shiki/@shikijs/core": ["@shikijs/core@3.20.0", "", { "dependencies": { "@shikijs/types": "3.20.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-f2ED7HYV4JEk827mtMDwe/yQ25pRiXZmtHjWF8uzZKuKiEsJR7Ce1nuQ+HhV9FzDcbIo4ObBCD9GPTzNuy9S1g=="], + + "shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], + "sitemap/sax": ["sax@1.4.3", "", {}, "sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -4348,7 +4393,11 @@ "utif2/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], - "vite-plugin-icons-spritesheet/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "vitest/tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], + + "vitest/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], + + "vitest/why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], "which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], @@ -4366,46 +4415,14 @@ "zod-to-ts/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "@actions/artifact/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="], + "@actions/artifact/@actions/core/@actions/exec": ["@actions/exec@2.0.0", "", { "dependencies": { "@actions/io": "^2.0.0" } }, "sha512-k8ngrX2voJ/RIN6r9xB82NVqKpnMRtxDoiO+g3olkIUpQNqjArXrCQceduQZCQj3P3xm32pChRLqRrtXTlqhIw=="], - "@actions/artifact/@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="], - - "@actions/artifact/@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - - "@actions/artifact/@octokit/core/before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="], - - "@actions/artifact/@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], - - "@actions/artifact/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="], - - "@actions/artifact/@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - - "@actions/artifact/@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], - - "@actions/artifact/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - - "@actions/github/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@4.0.0", "", {}, "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA=="], - - "@actions/github/@octokit/core/@octokit/graphql": ["@octokit/graphql@7.1.1", "", { "dependencies": { "@octokit/request": "^8.4.1", "@octokit/types": "^13.0.0", "universal-user-agent": "^6.0.0" } }, "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g=="], - - "@actions/github/@octokit/core/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - - "@actions/github/@octokit/core/before-after-hook": ["before-after-hook@2.2.3", "", {}, "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="], - - "@actions/github/@octokit/core/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], + "@actions/core/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], "@actions/github/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], "@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="], - "@actions/github/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@9.0.6", "", { "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" } }, "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw=="], - - "@actions/github/@octokit/request/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - - "@actions/github/@octokit/request/universal-user-agent": ["universal-user-agent@6.0.1", "", {}, "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ=="], - - "@actions/github/@octokit/request-error/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], - "@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="], "@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="], @@ -4596,58 +4613,110 @@ "@modelcontextprotocol/sdk/raw-body/http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "@octokit/auth-app/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/auth-app/@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/auth-app/@octokit/request-error/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/auth-oauth-app/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/auth-oauth-app/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + "@octokit/auth-oauth-app/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/auth-oauth-device/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/auth-oauth-device/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + "@octokit/auth-oauth-device/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/auth-oauth-user/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/auth-oauth-user/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + "@octokit/auth-oauth-user/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], - "@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - "@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/endpoint/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + + "@octokit/graphql/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/graphql/@octokit/request/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + + "@octokit/graphql/@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], "@octokit/graphql/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], + "@octokit/oauth-methods/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + "@octokit/oauth-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], - "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], - "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], + + "@octokit/plugin-paginate-rest/@octokit/core/@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=="], + + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/plugin-paginate-rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + + "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@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=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], "@octokit/plugin-retry/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@12.11.0", "", {}, "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="], - "@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - "@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + "@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - "@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="], + "@octokit/rest/@octokit/core/@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], - "@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="], + "@octokit/rest/@octokit/core/@octokit/graphql": ["@octokit/graphql@9.0.3", "", { "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA=="], + + "@octokit/rest/@octokit/core/@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=="], + + "@octokit/rest/@octokit/core/@octokit/request-error": ["@octokit/request-error@7.1.0", "", { "dependencies": { "@octokit/types": "^16.0.0" } }, "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw=="], + + "@octokit/rest/@octokit/core/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], + + "@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + + "@opencode-ai/desktop/@actions/artifact/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], "@opencode-ai/web/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="], "@opencode-ai/web/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="], - "@opencode-ai/web/shiki/@shikijs/core": ["@shikijs/core@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="], - - "@opencode-ai/web/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-1/adJbSMBOkpScCE/SB6XkjJU17ANln3Wky7lOmrnpl+zBdQ1qXUJg2GXTYVHRq+2j3hd1DesmElTXYDgtfSOQ=="], - - "@opencode-ai/web/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-zcZKMnNndgRa3ORja6Iemsr3DrLtkX3cAF7lTJkdMB6v9alhlBsX9uNiCpqofNrXOvpA3h6lHcLJxgCIhVOU5Q=="], - - "@opencode-ai/web/shiki/@shikijs/langs": ["@shikijs/langs@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2" } }, "sha512-H6azIAM+OXD98yztIfs/KH5H4PU39t+SREhmM8LaNXyUrqj2mx+zVkr8MWYqjceSjDw9I1jawm1WdFqU806rMA=="], - - "@opencode-ai/web/shiki/@shikijs/themes": ["@shikijs/themes@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2" } }, "sha512-qAEuAQh+brd8Jyej2UDDf+b4V2g1Rm8aBIdvt32XhDPrHvDkEnpb7Kzc9hSuHUxz0Iuflmq7elaDuQAP9bHIhg=="], - - "@opencode-ai/web/shiki/@shikijs/types": ["@shikijs/types@3.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="], - "@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "@pierre/diffs/@shikijs/core/@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + "@pierre/diffs/@shikijs/core/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="], "@pierre/diffs/@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + "@pierre/diffs/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + "@pierre/diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.19.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-Z2hdeEQlzuntf/BZpFG8a+Fsw9UVXdML7w0o3TgSXV3yNESGon+bs9ITkQb3Ki7zxoXOOu5oJWqZ2uto06V9iQ=="], + "@pierre/diffs/shiki/@shikijs/core": ["@shikijs/core@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-L7SrRibU7ZoYi1/TrZsJOFAnnHyLTE1SwHG1yNWjZIVCqjOEmCSuK2ZO9thnRbJG6TOkPp+Z963JmpCNw5nzvA=="], + "@pierre/diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1hRxtYIJfJSZeM5ivbUXv9hcJP3PWRo5prG/V2sWwiubUKTa+7P62d2qxCW8jiVFX4pgRHhnHNp+qeR7Xl+6kg=="], "@pierre/diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.19.0", "", { "dependencies": { "@shikijs/types": "3.19.0" } }, "sha512-dBMFzzg1QiXqCVQ5ONc0z2ebyoi5BKz+MtfByLm0o5/nbUu3Iz8uaTCa5uzGiscQKm7lVShfZHU1+OG3t5hgwg=="], @@ -4838,10 +4907,6 @@ "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], - "opencode/@octokit/rest/@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@13.2.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw=="], - - "opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@16.1.1", "", { "dependencies": { "@octokit/types": "^15.0.1" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg=="], - "opencontrol/@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=="], "opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="], @@ -4856,10 +4921,6 @@ "pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="], - "prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - - "prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -4878,26 +4939,18 @@ "type-is/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "vitest/vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "@actions/artifact/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - - "@actions/artifact/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - - "@actions/artifact/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - - "@actions/github/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + "@actions/artifact/@actions/core/@actions/exec/@actions/io": ["@actions/io@2.0.0", "", {}, "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg=="], "@actions/github/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], "@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@20.0.0", "", {}, "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA=="], - "@actions/github/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - - "@actions/github/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], - "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="], "@astrojs/mdx/@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="], @@ -4980,9 +5033,25 @@ "@modelcontextprotocol/sdk/raw-body/http-errors/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], + "@octokit/auth-app/@octokit/request-error/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], - "@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], + "@octokit/auth-app/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/graphql/@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/plugin-paginate-rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@octokit/rest/@octokit/core/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.2", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ=="], + + "@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], + + "@opencode-ai/desktop/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], "@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -5004,10 +5073,6 @@ "js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - "opencode/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], - - "opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@15.0.2", "", { "dependencies": { "@octokit/openapi-types": "^26.0.0" } }, "sha512-rR+5VRjhYSer7sC51krfCctQhVTmjyUMAaShfPB8mscVa8tSoLyon3coxQmXu0ahJoLVWl8dSGD/3OGZlFV44Q=="], - "opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], @@ -5040,28 +5105,68 @@ "pkg-up/find-up/locate-path/path-exists": ["path-exists@3.0.0", "", {}, "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="], - "prebuild-install/tar-fs/tar-stream/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], - "tw-to-css/tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "tw-to-css/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "vitest/vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "vitest/vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "vitest/vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "vitest/vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "vitest/vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "vitest/vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "vitest/vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "vitest/vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "vitest/vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "vitest/vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "vitest/vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "vitest/vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "vitest/vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "vitest/vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "vitest/vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "vitest/vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "vitest/vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "vitest/vite/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "vitest/vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "vitest/vite/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "vitest/vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "vitest/vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "vitest/vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "vitest/vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "vitest/vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="], "@jsx-email/cli/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "@opencode-ai/function/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], - - "@opencode-ai/function/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], - "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex": ["regex@5.1.1", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-dN5I359AVGPnwzJm2jN1k0W9LPZ+ePvoOeVMMfqIMFz53sSwXkxaJoxr50ptnsC771lK95BnTrVSZxq0b9yCGw=="], "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="], - "opencode/@octokit/rest/@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], - - "opencode/@octokit/rest/@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], - "opencontrol/@modelcontextprotocol/sdk/express/accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "opencontrol/@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], diff --git a/flake.lock b/flake.lock index 127262668..6beb162c7 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1766025857, - "narHash": "sha256-Lav5jJazCW4mdg1iHcROpuXqmM94BWJvabLFWaJVJp0=", + "lastModified": 1766314097, + "narHash": "sha256-laJftWbghBehazn/zxVJ8NdENVgjccsWAdAqKXhErrM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "def3da69945bbe338c373fddad5a1bb49cf199ce", + "rev": "306ea70f9eb0fb4e040f8540e2deab32ed7e2055", "type": "github" }, "original": { diff --git a/github/action.yml b/github/action.yml index cf276b51c..57e26d856 100644 --- a/github/action.yml +++ b/github/action.yml @@ -9,6 +9,10 @@ inputs: description: "Model to use" required: true + agent: + description: "Agent to use. Must be a primary agent. Falls back to default_agent from config or 'build' if not found." + required: false + share: description: "Share the opencode session (defaults to true for public repos)" required: false @@ -62,6 +66,7 @@ runs: run: opencode github run env: MODEL: ${{ inputs.model }} + AGENT: ${{ inputs.agent }} SHARE: ${{ inputs.share }} PROMPT: ${{ inputs.prompt }} USE_GITHUB_TOKEN: ${{ inputs.use_github_token }} diff --git a/github/index.ts b/github/index.ts index 6d826326e..2dcf6e754 100644 --- a/github/index.ts +++ b/github/index.ts @@ -318,6 +318,10 @@ function useEnvRunUrl() { return `/${repo.owner}/${repo.repo}/actions/runs/${runId}` } +function useEnvAgent() { + return process.env["AGENT"] || undefined +} + function useEnvShare() { const value = process.env["SHARE"] if (!value) return undefined @@ -570,24 +574,49 @@ async function subscribeSessionEvents() { } async function summarize(response: string) { - const payload = useContext().payload as IssueCommentEvent try { return await chat(`Summarize the following in less than 40 characters:\n\n${response}`) } catch (e) { + if (isScheduleEvent()) { + return "Scheduled task changes" + } + const payload = useContext().payload as IssueCommentEvent return `Fix issue: ${payload.issue.title}` } } +async function resolveAgent(): Promise { + const envAgent = useEnvAgent() + if (!envAgent) return undefined + + // Validate the agent exists and is a primary agent + const agents = await client.agent.list() + const agent = agents.data?.find((a) => a.name === envAgent) + + if (!agent) { + console.warn(`agent "${envAgent}" not found. Falling back to default agent`) + return undefined + } + + if (agent.mode === "subagent") { + console.warn(`agent "${envAgent}" is a subagent, not a primary agent. Falling back to default agent`) + return undefined + } + + return envAgent +} + async function chat(text: string, files: PromptFiles = []) { console.log("Sending message to opencode...") const { providerID, modelID } = useEnvModel() + const agent = await resolveAgent() const chat = await client.session.chat({ path: session, body: { providerID, modelID, - agent: "build", + agent, parts: [ { type: "text", diff --git a/infra/app.ts b/infra/app.ts index 7215995ba..da4ac45b8 100644 --- a/infra/app.ts +++ b/infra/app.ts @@ -44,3 +44,12 @@ new sst.cloudflare.x.Astro("Web", { VITE_API_URL: api.url.apply((url) => url!), }, }) + +new sst.cloudflare.StaticSite("App", { + domain: "app." + domain, + path: "packages/app", + build: { + command: "bun turbo build", + output: "./dist", + }, +}) diff --git a/infra/console.ts b/infra/console.ts index 8f54823f8..0cc6a404b 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -118,6 +118,7 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv") //////////////// const bucket = new sst.cloudflare.Bucket("ZenData") +const bucketNew = new sst.cloudflare.Bucket("ZenDataNew") const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID") const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY") @@ -136,6 +137,7 @@ new sst.cloudflare.x.SolidStart("Console", { path: "packages/console/app", link: [ bucket, + bucketNew, database, AUTH_API_URL, STRIPE_WEBHOOK_SECRET, diff --git a/infra/desktop.ts b/infra/desktop.ts index d4e32c65d..5c4155cc9 100644 --- a/infra/desktop.ts +++ b/infra/desktop.ts @@ -2,7 +2,7 @@ import { domain } from "./stage" new sst.cloudflare.StaticSite("Desktop", { domain: "desktop." + domain, - path: "packages/desktop", + path: "packages/app", build: { command: "bun turbo build", output: "./dist", diff --git a/nix/hashes.json b/nix/hashes.json index 40e6fd5ee..dbf753171 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-oT1WPPR1sHBhQcJaFL+mod5l3+V8O3uPKJUdrcfTst0=" + "nodeModules": "sha256-QlQblkUq49DOdvNNMNAzHHAfHxR6cZNmJtyzc4rD168=" } diff --git a/package.json b/package.json index e9ad0fb20..23ef2253a 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@tsconfig/bun": "1.0.9", "@cloudflare/workers-types": "4.20251008.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/diffs": "1.0.0-beta.3", + "@pierre/diffs": "1.0.2", "@solid-primitives/storage": "4.3.3", "@tailwindcss/vite": "4.1.11", "diff": "8.0.2", @@ -44,18 +44,20 @@ "@typescript/native-preview": "7.0.0-dev.20251207.1", "zod": "4.1.8", "remeda": "2.26.0", + "shiki": "3.20.0", "solid-list": "0.3.0", "tailwindcss": "4.1.11", "virtua": "0.42.3", "vite": "7.1.4", "@solidjs/meta": "0.29.4", "@solidjs/router": "0.15.4", - "@solidjs/start": "https://pkg.pr.new/@solidjs/start@57aeb22", + "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", "solid-js": "1.9.10", "vite-plugin-solid": "2.11.10" } }, "devDependencies": { + "@actions/artifact": "5.0.1", "@tsconfig/bun": "catalog:", "husky": "9.1.7", "prettier": "3.6.2", @@ -63,8 +65,15 @@ "turbo": "2.5.6" }, "dependencies": { + "@ai-sdk/cerebras": "1.0.33", + "@ai-sdk/cohere": "2.0.21", + "@ai-sdk/deepinfra": "1.0.30", + "@ai-sdk/gateway": "2.0.23", + "@ai-sdk/groq": "2.0.33", + "@ai-sdk/perplexity": "2.0.22", + "@ai-sdk/togetherai": "1.0.30", "@aws-sdk/client-s3": "3.933.0", - "@octokit/rest": "22.0.1", + "@opencode-ai/plugin": "workspace:*", "@opencode-ai/script": "workspace:*", "@opencode-ai/sdk": "workspace:*", "typescript": "catalog:" @@ -81,7 +90,6 @@ "trustedDependencies": [ "esbuild", "protobufjs", - "sharp", "tree-sitter", "tree-sitter-bash", "web-tree-sitter" diff --git a/packages/app/.gitignore b/packages/app/.gitignore new file mode 100644 index 000000000..4a20d55a7 --- /dev/null +++ b/packages/app/.gitignore @@ -0,0 +1 @@ +src/assets/theme.css diff --git a/packages/desktop/AGENTS.md b/packages/app/AGENTS.md similarity index 100% rename from packages/desktop/AGENTS.md rename to packages/app/AGENTS.md diff --git a/packages/app/README.md b/packages/app/README.md new file mode 100644 index 000000000..6a1764536 --- /dev/null +++ b/packages/app/README.md @@ -0,0 +1,34 @@ +## Usage + +Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`. + +This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template. + +```bash +$ npm install # or pnpm install or yarn install +``` + +### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) + +## Available Scripts + +In the project directory, you can run: + +### `npm run dev` or `npm start` + +Runs the app in the development mode.
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. + +The page will reload if you make edits.
+ +### `npm run build` + +Builds the app for production to the `dist` folder.
+It correctly bundles Solid in production mode and optimizes the build for the best performance. + +The build is minified and the filenames include the hashes.
+Your app is ready to be deployed! + +## Deployment + +You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) diff --git a/packages/desktop/bunfig.toml b/packages/app/bunfig.toml similarity index 100% rename from packages/desktop/bunfig.toml rename to packages/app/bunfig.toml diff --git a/packages/desktop/happydom.ts b/packages/app/happydom.ts similarity index 100% rename from packages/desktop/happydom.ts rename to packages/app/happydom.ts diff --git a/packages/tauri/index.html b/packages/app/index.html similarity index 95% rename from packages/tauri/index.html rename to packages/app/index.html index 8de2d21d0..9803517a0 100644 --- a/packages/tauri/index.html +++ b/packages/app/index.html @@ -23,6 +23,6 @@
- + diff --git a/packages/app/package.json b/packages/app/package.json new file mode 100644 index 000000000..9280bec2b --- /dev/null +++ b/packages/app/package.json @@ -0,0 +1,62 @@ +{ + "name": "@opencode-ai/app", + "version": "1.0.191", + "description": "", + "type": "module", + "exports": { + ".": "./src/index.ts", + "./vite": "./vite.js" + }, + "scripts": { + "typecheck": "tsgo -b", + "start": "vite", + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "license": "MIT", + "devDependencies": { + "@happy-dom/global-registrator": "20.0.11", + "@tailwindcss/vite": "catalog:", + "@tsconfig/bun": "1.0.9", + "@types/bun": "catalog:", + "@types/luxon": "catalog:", + "@types/node": "catalog:", + "@typescript/native-preview": "catalog:", + "typescript": "catalog:", + "vite": "catalog:", + "vite-plugin-icons-spritesheet": "3.0.1", + "vite-plugin-solid": "catalog:" + }, + "dependencies": { + "@kobalte/core": "catalog:", + "@opencode-ai/sdk": "workspace:*", + "@opencode-ai/ui": "workspace:*", + "@opencode-ai/util": "workspace:*", + "@shikijs/transformers": "3.9.2", + "@solid-primitives/active-element": "2.1.3", + "@solid-primitives/audio": "1.4.2", + "@solid-primitives/event-bus": "1.1.2", + "@solid-primitives/media": "2.3.3", + "@solid-primitives/resize-observer": "2.1.3", + "@solid-primitives/scroll": "2.1.3", + "@solid-primitives/storage": "catalog:", + "@solid-primitives/websocket": "1.3.1", + "@solidjs/meta": "catalog:", + "@solidjs/router": "catalog:", + "@thisbeyond/solid-dnd": "0.7.5", + "diff": "catalog:", + "fuzzysort": "catalog:", + "ghostty-web": "0.3.0", + "luxon": "catalog:", + "marked": "16.2.0", + "marked-shiki": "1.2.1", + "remeda": "catalog:", + "shiki": "catalog:", + "solid-js": "catalog:", + "solid-list": "catalog:", + "tailwindcss": "catalog:", + "virtua": "catalog:", + "zod": "catalog:" + } +} diff --git a/packages/desktop/public/apple-touch-icon.png b/packages/app/public/apple-touch-icon.png similarity index 100% rename from packages/desktop/public/apple-touch-icon.png rename to packages/app/public/apple-touch-icon.png diff --git a/packages/desktop/public/favicon-96x96.png b/packages/app/public/favicon-96x96.png similarity index 100% rename from packages/desktop/public/favicon-96x96.png rename to packages/app/public/favicon-96x96.png diff --git a/packages/desktop/public/favicon.ico b/packages/app/public/favicon.ico similarity index 100% rename from packages/desktop/public/favicon.ico rename to packages/app/public/favicon.ico diff --git a/packages/desktop/public/favicon.svg b/packages/app/public/favicon.svg similarity index 100% rename from packages/desktop/public/favicon.svg rename to packages/app/public/favicon.svg diff --git a/packages/desktop/public/site.webmanifest b/packages/app/public/site.webmanifest similarity index 100% rename from packages/desktop/public/site.webmanifest rename to packages/app/public/site.webmanifest diff --git a/packages/desktop/public/social-share-zen.png b/packages/app/public/social-share-zen.png similarity index 100% rename from packages/desktop/public/social-share-zen.png rename to packages/app/public/social-share-zen.png diff --git a/packages/desktop/public/social-share.png b/packages/app/public/social-share.png similarity index 100% rename from packages/desktop/public/social-share.png rename to packages/app/public/social-share.png diff --git a/packages/desktop/public/web-app-manifest-192x192.png b/packages/app/public/web-app-manifest-192x192.png similarity index 100% rename from packages/desktop/public/web-app-manifest-192x192.png rename to packages/app/public/web-app-manifest-192x192.png diff --git a/packages/desktop/public/web-app-manifest-512x512.png b/packages/app/public/web-app-manifest-512x512.png similarity index 100% rename from packages/desktop/public/web-app-manifest-512x512.png rename to packages/app/public/web-app-manifest-512x512.png diff --git a/packages/desktop/src/addons/serialize.test.ts b/packages/app/src/addons/serialize.test.ts similarity index 100% rename from packages/desktop/src/addons/serialize.test.ts rename to packages/app/src/addons/serialize.test.ts diff --git a/packages/desktop/src/addons/serialize.ts b/packages/app/src/addons/serialize.ts similarity index 100% rename from packages/desktop/src/addons/serialize.ts rename to packages/app/src/addons/serialize.ts diff --git a/packages/desktop/src/app.tsx b/packages/app/src/app.tsx similarity index 85% rename from packages/desktop/src/app.tsx rename to packages/app/src/app.tsx index 2ed529bbc..11216643e 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/app/src/app.tsx @@ -21,6 +21,7 @@ import Home from "@/pages/home" import DirectoryLayout from "@/pages/directory-layout" import Session from "@/pages/session" import { ErrorPage } from "./pages/error" +import { iife } from "@opencode-ai/util/iife" declare global { interface Window { @@ -28,14 +29,17 @@ declare global { } } -const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" -const port = window.__OPENCODE__?.port ?? import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" +const url = iife(() => { + const param = new URLSearchParams(document.location.search).get("url") + if (param) return param -const url = - new URLSearchParams(document.location.search).get("url") || - (location.hostname.includes("opencode.ai") || location.hostname.includes("localhost") - ? `http://${host}:${port}` - : "/") + if (location.hostname.includes("opencode.ai")) return "http://localhost:4096" + if (window.__OPENCODE__) return `http://127.0.0.1:${window.__OPENCODE__.port}` + if (import.meta.env.DEV) + return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` + + return "http://localhost:4096" +}) export function App() { return ( diff --git a/packages/desktop/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx similarity index 96% rename from packages/desktop/src/components/dialog-connect-provider.tsx rename to packages/app/src/components/dialog-connect-provider.tsx index 0d6737815..789a5d3b7 100644 --- a/packages/desktop/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -1,24 +1,24 @@ +import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client" +import { Button } from "@opencode-ai/ui/button" +import { useDialog } from "@opencode-ai/ui/context/dialog" +import { Dialog } from "@opencode-ai/ui/dialog" +import { Icon } from "@opencode-ai/ui/icon" +import { IconButton } from "@opencode-ai/ui/icon-button" +import type { IconName } from "@opencode-ai/ui/icons/provider" +import { List, type ListRef } from "@opencode-ai/ui/list" +import { ProviderIcon } from "@opencode-ai/ui/provider-icon" +import { Spinner } from "@opencode-ai/ui/spinner" +import { TextField } from "@opencode-ai/ui/text-field" +import { showToast } from "@opencode-ai/ui/toast" +import { iife } from "@opencode-ai/util/iife" import { createMemo, Match, onCleanup, onMount, Switch } from "solid-js" import { createStore, produce } from "solid-js/store" -import { useDialog } from "@opencode-ai/ui/context/dialog" -import { useGlobalSync } from "@/context/global-sync" -import { useGlobalSDK } from "@/context/global-sdk" -import { usePlatform } from "@/context/platform" -import { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2/client" -import { Dialog } from "@opencode-ai/ui/dialog" -import { List, ListRef } from "@opencode-ai/ui/list" -import { Button } from "@opencode-ai/ui/button" -import { IconButton } from "@opencode-ai/ui/icon-button" -import { TextField } from "@opencode-ai/ui/text-field" -import { Spinner } from "@opencode-ai/ui/spinner" -import { Icon } from "@opencode-ai/ui/icon" -import { showToast } from "@opencode-ai/ui/toast" -import { ProviderIcon } from "@opencode-ai/ui/provider-icon" -import { IconName } from "@opencode-ai/ui/icons/provider" -import { iife } from "@opencode-ai/util/iife" import { Link } from "@/components/link" -import { DialogSelectProvider } from "./dialog-select-provider" +import { useGlobalSDK } from "@/context/global-sdk" +import { useGlobalSync } from "@/context/global-sync" +import { usePlatform } from "@/context/platform" import { DialogSelectModel } from "./dialog-select-model" +import { DialogSelectProvider } from "./dialog-select-provider" export function DialogConnectProvider(props: { provider: string }) { const dialog = useDialog() @@ -154,7 +154,9 @@ export function DialogConnectProvider(props: { provider: string }) {
Select login method for {provider().name}.
(listRef = ref)} + ref={(ref) => { + listRef = ref + }} items={methods} key={(m) => m?.label} onSelect={async (method, index) => { @@ -163,7 +165,7 @@ export function DialogConnectProvider(props: { provider: string }) { }} > {(i) => ( -
+
@@ -175,7 +177,7 @@ export function DialogConnectProvider(props: { provider: string }) {
-
+
Authorization in progress...
@@ -183,7 +185,7 @@ export function DialogConnectProvider(props: { provider: string }) {
-
+
Authorization failed: {store.error}
diff --git a/packages/desktop/src/components/dialog-manage-models.tsx b/packages/app/src/components/dialog-manage-models.tsx similarity index 83% rename from packages/desktop/src/components/dialog-manage-models.tsx rename to packages/app/src/components/dialog-manage-models.tsx index 5765a8e1a..66d125288 100644 --- a/packages/desktop/src/components/dialog-manage-models.tsx +++ b/packages/app/src/components/dialog-manage-models.tsx @@ -1,16 +1,15 @@ -import { Component } from "solid-js" -import { useLocal } from "@/context/local" -import { popularProviders } from "@/hooks/use-providers" import { Dialog } from "@opencode-ai/ui/dialog" import { List } from "@opencode-ai/ui/list" import { Switch } from "@opencode-ai/ui/switch" +import type { Component } from "solid-js" +import { useLocal } from "@/context/local" +import { popularProviders } from "@/hooks/use-providers" export const DialogManageModels: Component = () => { const local = useLocal() return ( `${x?.provider?.id}:${x?.id}`} @@ -27,16 +26,24 @@ export const DialogManageModels: Component = () => { }} onSelect={(x) => { if (!x) return - const visible = local.model.visible({ modelID: x.id, providerID: x.provider.id }) + const visible = local.model.visible({ + modelID: x.id, + providerID: x.provider.id, + }) local.model.setVisibility({ modelID: x.id, providerID: x.provider.id }, !visible) }} > {(i) => ( -
+
{i.name}
e.stopPropagation()}> { local.model.setVisibility({ modelID: i.id, providerID: i.provider.id }, checked) }} diff --git a/packages/desktop/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx similarity index 95% rename from packages/desktop/src/components/dialog-select-file.tsx rename to packages/app/src/components/dialog-select-file.tsx index 61c518719..b27afdc8b 100644 --- a/packages/desktop/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -1,12 +1,12 @@ -import { useLocal } from "@/context/local" -import { Dialog } from "@opencode-ai/ui/dialog" -import { List } from "@opencode-ai/ui/list" -import { FileIcon } from "@opencode-ai/ui/file-icon" -import { getDirectory, getFilename } from "@opencode-ai/util/path" -import { useLayout } from "@/context/layout" import { useDialog } from "@opencode-ai/ui/context/dialog" +import { Dialog } from "@opencode-ai/ui/dialog" +import { FileIcon } from "@opencode-ai/ui/file-icon" +import { List } from "@opencode-ai/ui/list" +import { getDirectory, getFilename } from "@opencode-ai/util/path" import { useParams } from "@solidjs/router" import { createMemo } from "solid-js" +import { useLayout } from "@/context/layout" +import { useLocal } from "@/context/local" export function DialogSelectFile() { const layout = useLayout() @@ -18,7 +18,6 @@ export function DialogSelectFile() { return ( {(i) => (
-
+
diff --git a/packages/desktop/src/components/dialog-select-model-unpaid.tsx b/packages/app/src/components/dialog-select-model-unpaid.tsx similarity index 83% rename from packages/desktop/src/components/dialog-select-model-unpaid.tsx rename to packages/app/src/components/dialog-select-model-unpaid.tsx index 77e493d3c..24ec8092d 100644 --- a/packages/desktop/src/components/dialog-select-model-unpaid.tsx +++ b/packages/app/src/components/dialog-select-model-unpaid.tsx @@ -1,15 +1,15 @@ -import { Component, onCleanup, onMount, Show } from "solid-js" -import { useLocal } from "@/context/local" -import { useDialog } from "@opencode-ai/ui/context/dialog" -import { popularProviders, useProviders } from "@/hooks/use-providers" import { Button } from "@opencode-ai/ui/button" -import { Tag } from "@opencode-ai/ui/tag" +import { useDialog } from "@opencode-ai/ui/context/dialog" import { Dialog } from "@opencode-ai/ui/dialog" -import { List, ListRef } from "@opencode-ai/ui/list" +import type { IconName } from "@opencode-ai/ui/icons/provider" +import { List, type ListRef } from "@opencode-ai/ui/list" import { ProviderIcon } from "@opencode-ai/ui/provider-icon" -import { IconName } from "@opencode-ai/ui/icons/provider" -import { DialogSelectProvider } from "./dialog-select-provider" +import { Tag } from "@opencode-ai/ui/tag" +import { type Component, onCleanup, onMount, Show } from "solid-js" +import { useLocal } from "@/context/local" +import { popularProviders, useProviders } from "@/hooks/use-providers" import { DialogConnectProvider } from "./dialog-connect-provider" +import { DialogSelectProvider } from "./dialog-select-provider" export const DialogSelectModelUnpaid: Component = () => { const local = useLocal() @@ -64,7 +64,7 @@ export const DialogSelectModelUnpaid: Component = () => {
Add more models from popular providers
x?.id} items={providers.popular} activeIcon="plus-small" @@ -79,17 +79,8 @@ export const DialogSelectModelUnpaid: Component = () => { }} > {(i) => ( -
- +
+ {i.name} Recommended diff --git a/packages/desktop/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx similarity index 97% rename from packages/desktop/src/components/dialog-select-model.tsx rename to packages/app/src/components/dialog-select-model.tsx index 622ab15fb..54783386a 100644 --- a/packages/desktop/src/components/dialog-select-model.tsx +++ b/packages/app/src/components/dialog-select-model.tsx @@ -35,7 +35,6 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { } > `${x.provider.id}:${x.id}`} @@ -61,7 +60,7 @@ export const DialogSelectModel: Component<{ provider?: string }> = (props) => { }} > {(i) => ( -
+
{i.name} Free diff --git a/packages/desktop/src/components/dialog-select-provider.tsx b/packages/app/src/components/dialog-select-provider.tsx similarity index 80% rename from packages/desktop/src/components/dialog-select-provider.tsx rename to packages/app/src/components/dialog-select-provider.tsx index 52fac7073..5bbde5d41 100644 --- a/packages/desktop/src/components/dialog-select-provider.tsx +++ b/packages/app/src/components/dialog-select-provider.tsx @@ -15,7 +15,6 @@ export const DialogSelectProvider: Component = () => { return ( x?.id} @@ -38,17 +37,8 @@ export const DialogSelectProvider: Component = () => { }} > {(i) => ( -
- +
+ {i.name} Recommended diff --git a/packages/desktop/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx similarity index 100% rename from packages/desktop/src/components/file-tree.tsx rename to packages/app/src/components/file-tree.tsx diff --git a/packages/desktop/src/components/header.tsx b/packages/app/src/components/header.tsx similarity index 60% rename from packages/desktop/src/components/header.tsx rename to packages/app/src/components/header.tsx index fd4b2c439..ec7cdfa25 100644 --- a/packages/desktop/src/components/header.tsx +++ b/packages/app/src/components/header.tsx @@ -10,6 +10,7 @@ import { Select } from "@opencode-ai/ui/select" import { TextField } from "@opencode-ai/ui/text-field" import { Tooltip } from "@opencode-ai/ui/tooltip" import { base64Decode } from "@opencode-ai/util/encode" +import { useCommand } from "@/context/command" import { getFilename } from "@opencode-ai/util/path" import { A, useParams } from "@solidjs/router" import { createMemo, createResource, Show } from "solid-js" @@ -19,19 +20,29 @@ import { iife } from "@opencode-ai/util/iife" export function Header(props: { navigateToProject: (directory: string) => void navigateToSession: (session: Session | undefined) => void + onMobileMenuToggle?: () => void }) { const globalSync = useGlobalSync() const globalSDK = useGlobalSDK() const layout = useLayout() const params = useParams() + const command = useCommand() return (
+ { const currentDirectory = createMemo(() => base64Decode(directory())) const store = createMemo(() => globalSync.child(currentDirectory())[0]) - const sessions = createMemo(() => store().session ?? []) + const sessions = createMemo(() => (store().session ?? []).filter((s) => !s.parentID)) const currentSession = createMemo(() => sessions().find((s) => s.id === params.id)) const shareEnabled = createMemo(() => store().config.share !== "disabled") return ( <> -
-
- -
/
+
+
+ agent.name)} - current={local.agent.current().name} - onSelect={local.agent.set} - class="capitalize" - variant="ghost" - /> -
} > - {local.model.current()?.name ?? "Select model"} - {local.model.current()?.provider.name} - - + (params.id ? (sync.data.message[params.id] ?? []) : [])) + + const cost = createMemo(() => { + const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(total) + }) + + const context = createMemo(() => { + const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage + if (!last) return + const total = + last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write + const model = sync.data.provider.all.find((x) => x.id === last.providerID)?.models[last.modelID] + return { + tokens: total.toLocaleString(), + percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null, + } + }) + + return ( + + {(ctx) => ( + +
+ Tokens + {ctx().tokens} +
+
+ Usage + {ctx().percentage ?? 0}% +
+
+ Cost + {cost()} +
+
+ } + placement="top" + > +
+ {`${ctx().percentage ?? 0}%`} + +
+ + )} + + ) +} diff --git a/packages/desktop/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx similarity index 100% rename from packages/desktop/src/components/terminal.tsx rename to packages/app/src/components/terminal.tsx diff --git a/packages/desktop/src/context/command.tsx b/packages/app/src/context/command.tsx similarity index 96% rename from packages/desktop/src/context/command.tsx rename to packages/app/src/context/command.tsx index 8fd76ee21..f91a1cf05 100644 --- a/packages/desktop/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -119,7 +119,6 @@ function DialogCommand(props: { options: CommandOption[] }) { return ( props.options.filter((x) => !x.id.startsWith("suggested.") || !x.disabled)} @@ -226,6 +225,11 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex } } }, + keybind(id: string) { + const option = options().find((x) => x.id === id || x.id === "suggested." + id) + if (!option?.keybind) return "" + return formatKeybind(option.keybind) + }, show: showPalette, keybinds(enabled: boolean) { setSuspendCount((count) => count + (enabled ? -1 : 1)) diff --git a/packages/desktop/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx similarity index 83% rename from packages/desktop/src/context/global-sdk.tsx rename to packages/app/src/context/global-sdk.tsx index ac6697093..3732ca085 100644 --- a/packages/desktop/src/context/global-sdk.tsx +++ b/packages/app/src/context/global-sdk.tsx @@ -6,8 +6,22 @@ import { usePlatform } from "./platform" export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleContext({ name: "GlobalSDK", init: (props: { url: string }) => { - const platform = usePlatform() + const eventSdk = createOpencodeClient({ + baseUrl: props.url, + // signal: AbortSignal.timeout(1000 * 60 * 10), + }) + const emitter = createGlobalEmitter<{ + [key: string]: Event + }>() + eventSdk.global.event().then(async (events) => { + for await (const event of events.stream) { + // console.log("event", event) + emitter.emit(event.directory ?? "global", event.payload) + } + }) + + const platform = usePlatform() const sdk = createOpencodeClient({ baseUrl: props.url, signal: AbortSignal.timeout(1000 * 60 * 10), @@ -15,17 +29,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo throwOnError: true, }) - const emitter = createGlobalEmitter<{ - [key: string]: Event - }>() - - sdk.global.event().then(async (events) => { - for await (const event of events.stream) { - // console.log("event", event) - emitter.emit(event.directory ?? "global", event.payload) - } - }) - return { url: props.url, client: sdk, event: emitter } }, }) diff --git a/packages/desktop/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx similarity index 89% rename from packages/desktop/src/context/global-sync.tsx rename to packages/app/src/context/global-sync.tsx index fffef5b5f..ae40555d6 100644 --- a/packages/desktop/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -18,6 +18,7 @@ import { } from "@opencode-ai/sdk/v2/client" import { createStore, produce, reconcile } from "solid-js/store" import { Binary } from "@opencode-ai/util/binary" +import { retry } from "@opencode-ai/util/retry" import { useGlobalSDK } from "./global-sdk" import { ErrorPage, type InitError } from "../pages/error" import { createContext, useContext, onMount, type ParentProps, Switch, Match } from "solid-js" @@ -145,7 +146,7 @@ function createGlobalSync() { changes: () => sdk.file.status().then((x) => setStore("changes", x.data!)), node: () => sdk.file.list({ path: "/" }).then((x) => setStore("node", x.data!)), } - await Promise.all(Object.values(load).map((p) => p().catch((e) => setGlobalStore("error", e)))) + await Promise.all(Object.values(load).map((p) => retry(p).catch((e) => setGlobalStore("error", e)))) .then(() => setStore("ready", true)) .catch((e) => setGlobalStore("error", e)) } @@ -294,22 +295,39 @@ function createGlobalSync() { }) async function bootstrap() { + const health = await globalSDK.client.global.health().then((x) => x.data) + if (!health?.healthy) { + setGlobalStore( + "error", + new Error(`Could not connect to server. Is there a server running at \`${globalSDK.url}\`?`), + ) + return + } + return Promise.all([ - globalSDK.client.path.get().then((x) => { - setGlobalStore("path", x.data!) - }), - globalSDK.client.project.list().then(async (x) => { - setGlobalStore( - "project", - x.data!.filter((p) => !p.worktree.includes("opencode-test")).sort((a, b) => a.id.localeCompare(b.id)), - ) - }), - globalSDK.client.provider.list().then((x) => { - setGlobalStore("provider", x.data ?? {}) - }), - globalSDK.client.provider.auth().then((x) => { - setGlobalStore("provider_auth", x.data ?? {}) - }), + retry(() => + globalSDK.client.path.get().then((x) => { + setGlobalStore("path", x.data!) + }), + ), + retry(() => + globalSDK.client.project.list().then(async (x) => { + setGlobalStore( + "project", + x.data!.filter((p) => !p.worktree.includes("opencode-test")).sort((a, b) => a.id.localeCompare(b.id)), + ) + }), + ), + retry(() => + globalSDK.client.provider.list().then((x) => { + setGlobalStore("provider", x.data ?? {}) + }), + ), + retry(() => + globalSDK.client.provider.auth().then((x) => { + setGlobalStore("provider_auth", x.data ?? {}) + }), + ), ]) .then(() => setGlobalStore("ready", true)) .catch((e) => setGlobalStore("error", e)) diff --git a/packages/desktop/src/context/layout.tsx b/packages/app/src/context/layout.tsx similarity index 85% rename from packages/desktop/src/context/layout.tsx rename to packages/app/src/context/layout.tsx index 01e0bdf52..c6ba5fef5 100644 --- a/packages/desktop/src/context/layout.tsx +++ b/packages/app/src/context/layout.tsx @@ -27,6 +27,8 @@ type SessionTabs = { all: string[] } +export type LocalProject = Partial & { worktree: string; expanded: boolean } + export const { use: useLayout, provider: LayoutProvider } = createSimpleContext({ name: "Layout", init: () => { @@ -45,7 +47,10 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( height: 280, }, review: { - state: "pane" as "pane" | "tab", + opened: true, + }, + session: { + width: 600, }, sessionTabs: {} as Record, }), @@ -61,21 +66,22 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( function enrich(project: { worktree: string; expanded: boolean }) { const metadata = globalSync.data.project.find((x) => x.worktree === project.worktree) - if (!metadata) return [] return [ { ...project, - ...metadata, + ...(metadata ?? {}), }, ] } - function colorize(project: Project & { expanded: boolean }) { + function colorize(project: LocalProject) { if (project.icon?.color) return project const color = pickAvailableColor() usedColors.add(color) project.icon = { ...project.icon, color } - globalSdk.client.project.update({ projectID: project.id, icon: { color } }) + if (project.id) { + globalSdk.client.project.update({ projectID: project.id, icon: { color } }) + } return project } @@ -95,7 +101,9 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( projects: { list, open(directory: string) { - if (store.projects.find((x) => x.worktree === directory)) return + if (store.projects.find((x) => x.worktree === directory)) { + return + } globalSync.project.loadSessions(directory) setStore("projects", (x) => [{ worktree: directory, expanded: true }, ...x]) }, @@ -103,10 +111,12 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( setStore("projects", (x) => x.filter((x) => x.worktree !== directory)) }, expand(directory: string) { - setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: true } : x))) + const index = store.projects.findIndex((x) => x.worktree === directory) + if (index !== -1) setStore("projects", index, "expanded", true) }, collapse(directory: string) { - setStore("projects", (x) => x.map((x) => (x.worktree === directory ? { ...x, expanded: false } : x))) + const index = store.projects.findIndex((x) => x.worktree === directory) + if (index !== -1) setStore("projects", index, "expanded", false) }, move(directory: string, toIndex: number) { setStore("projects", (projects) => { @@ -152,12 +162,25 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( }, }, review: { - state: createMemo(() => store.review?.state ?? "closed"), - pane() { - setStore("review", "state", "pane") + opened: createMemo(() => store.review?.opened ?? true), + open() { + setStore("review", "opened", true) }, - tab() { - setStore("review", "state", "tab") + close() { + setStore("review", "opened", false) + }, + toggle() { + setStore("review", "opened", (x) => !x) + }, + }, + session: { + width: createMemo(() => store.session?.width ?? 600), + resize(width: number) { + if (!store.session) { + setStore("session", { width }) + } else { + setStore("session", "width", width) + } }, }, tabs(sessionKey: string) { @@ -181,14 +204,6 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext( } }, async open(tab: string) { - if (tab === "chat") { - if (!store.sessionTabs[sessionKey]) { - setStore("sessionTabs", sessionKey, { all: [], active: undefined }) - } else { - setStore("sessionTabs", sessionKey, "active", undefined) - } - return - } const current = store.sessionTabs[sessionKey] ?? { all: [] } if (tab !== "review") { if (!current.all.includes(tab)) { diff --git a/packages/desktop/src/context/local.tsx b/packages/app/src/context/local.tsx similarity index 98% rename from packages/desktop/src/context/local.tsx rename to packages/app/src/context/local.tsx index 2ea4de524..69807a2f4 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/app/src/context/local.tsx @@ -337,6 +337,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const load = async (path: string) => { const relativePath = relative(path) await sdk.client.file.read({ path: relativePath }).then((x) => { + if (!store.node[relativePath]) return setStore( "node", relativePath, @@ -359,7 +360,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const init = async (path: string) => { const relativePath = relative(path) if (!store.node[relativePath]) await fetch(path) - if (store.node[relativePath].loaded) return + if (store.node[relativePath]?.loaded) return return load(relativePath) } @@ -379,7 +380,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ context.addActive() if (options?.pinned) setStore("node", path, "pinned", true) if (options?.view && store.node[relativePath].view === undefined) setStore("node", path, "view", options.view) - if (store.node[relativePath].loaded) return + if (store.node[relativePath]?.loaded) return return load(relativePath) } @@ -425,7 +426,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ init, expand(path: string) { setStore("node", path, "expanded", true) - if (store.node[path].loaded) return + if (store.node[path]?.loaded) return setStore("node", path, "loaded", true) list(path) }, diff --git a/packages/desktop/src/context/notification.tsx b/packages/app/src/context/notification.tsx similarity index 100% rename from packages/desktop/src/context/notification.tsx rename to packages/app/src/context/notification.tsx diff --git a/packages/desktop/src/context/platform.tsx b/packages/app/src/context/platform.tsx similarity index 100% rename from packages/desktop/src/context/platform.tsx rename to packages/app/src/context/platform.tsx diff --git a/packages/desktop/src/context/prompt.tsx b/packages/app/src/context/prompt.tsx similarity index 100% rename from packages/desktop/src/context/prompt.tsx rename to packages/app/src/context/prompt.tsx diff --git a/packages/desktop/src/context/sdk.tsx b/packages/app/src/context/sdk.tsx similarity index 100% rename from packages/desktop/src/context/sdk.tsx rename to packages/app/src/context/sdk.tsx diff --git a/packages/desktop/src/context/sync.tsx b/packages/app/src/context/sync.tsx similarity index 92% rename from packages/desktop/src/context/sync.tsx rename to packages/app/src/context/sync.tsx index ca25cae98..941b8b629 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/app/src/context/sync.tsx @@ -1,6 +1,7 @@ import { produce } from "solid-js/store" import { createMemo } from "solid-js" import { Binary } from "@opencode-ai/util/binary" +import { retry } from "@opencode-ai/util/retry" import { createSimpleContext } from "@opencode-ai/ui/context" import { useGlobalSync } from "./global-sync" import { useSDK } from "./sdk" @@ -61,10 +62,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string, _isRetry = false) { const [session, messages, todo, diff] = await Promise.all([ - sdk.client.session.get({ sessionID }, { throwOnError: true }), - sdk.client.session.messages({ sessionID, limit: 100 }), - sdk.client.session.todo({ sessionID }), - sdk.client.session.diff({ sessionID }), + retry(() => sdk.client.session.get({ sessionID })), + retry(() => sdk.client.session.messages({ sessionID, limit: 100 })), + retry(() => sdk.client.session.todo({ sessionID })), + retry(() => sdk.client.session.diff({ sessionID })), ]) setStore( produce((draft) => { diff --git a/packages/desktop/src/context/terminal.tsx b/packages/app/src/context/terminal.tsx similarity index 100% rename from packages/desktop/src/context/terminal.tsx rename to packages/app/src/context/terminal.tsx diff --git a/packages/desktop/src/custom-elements.d.ts b/packages/app/src/custom-elements.d.ts similarity index 100% rename from packages/desktop/src/custom-elements.d.ts rename to packages/app/src/custom-elements.d.ts diff --git a/packages/desktop/src/entry.tsx b/packages/app/src/entry.tsx similarity index 100% rename from packages/desktop/src/entry.tsx rename to packages/app/src/entry.tsx diff --git a/packages/desktop/src/env.d.ts b/packages/app/src/env.d.ts similarity index 100% rename from packages/desktop/src/env.d.ts rename to packages/app/src/env.d.ts diff --git a/packages/desktop/src/hooks/use-providers.ts b/packages/app/src/hooks/use-providers.ts similarity index 100% rename from packages/desktop/src/hooks/use-providers.ts rename to packages/app/src/hooks/use-providers.ts diff --git a/packages/desktop/src/index.css b/packages/app/src/index.css similarity index 100% rename from packages/desktop/src/index.css rename to packages/app/src/index.css diff --git a/packages/desktop/src/index.ts b/packages/app/src/index.ts similarity index 100% rename from packages/desktop/src/index.ts rename to packages/app/src/index.ts diff --git a/packages/desktop/src/pages/directory-layout.tsx b/packages/app/src/pages/directory-layout.tsx similarity index 100% rename from packages/desktop/src/pages/directory-layout.tsx rename to packages/app/src/pages/directory-layout.tsx diff --git a/packages/desktop/src/pages/error.tsx b/packages/app/src/pages/error.tsx similarity index 72% rename from packages/desktop/src/pages/error.tsx rename to packages/app/src/pages/error.tsx index 352b9f3e8..9914279ad 100644 --- a/packages/desktop/src/pages/error.tsx +++ b/packages/app/src/pages/error.tsx @@ -62,12 +62,54 @@ function formatInitError(error: InitError): string { } } -function formatError(error: unknown): string { +function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): string { if (!error) return "Unknown error" - if (isInitError(error)) return formatInitError(error) - if (error instanceof Error) return `${error.name}: ${error.message}\n\n${error.stack}` - if (typeof error === "string") return error - return JSON.stringify(error, null, 2) + + if (isInitError(error)) { + const message = formatInitError(error) + if (depth > 0 && parentMessage === message) return "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + return indent + message + } + + if (error instanceof Error) { + const isDuplicate = depth > 0 && parentMessage === error.message + const parts: string[] = [] + const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + + if (!isDuplicate) { + // Stack already includes error name and message, so prefer it + parts.push(indent + (error.stack ?? `${error.name}: ${error.message}`)) + } else if (error.stack) { + // Duplicate message - only show the stack trace lines (skip message) + const trace = error.stack.split("\n").slice(1).join("\n").trim() + if (trace) { + parts.push(trace) + } + } + + if (error.cause) { + const causeResult = formatErrorChain(error.cause, depth + 1, error.message) + if (causeResult) { + parts.push(causeResult) + } + } + + return parts.join("\n\n") + } + + if (typeof error === "string") { + if (depth > 0 && parentMessage === error) return "" + const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + return indent + error + } + + const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" + return indent + JSON.stringify(error, null, 2) +} + +function formatError(error: unknown): string { + return formatErrorChain(error, 0) } interface ErrorPageProps { diff --git a/packages/desktop/src/pages/home.tsx b/packages/app/src/pages/home.tsx similarity index 100% rename from packages/desktop/src/pages/home.tsx rename to packages/app/src/pages/home.tsx diff --git a/packages/desktop/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx similarity index 77% rename from packages/desktop/src/pages/layout.tsx rename to packages/app/src/pages/layout.tsx index 626bceb22..489899f88 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -4,6 +4,7 @@ import { createSignal, For, Match, + onCleanup, onMount, ParentProps, Show, @@ -12,7 +13,7 @@ import { } from "solid-js" import { DateTime } from "luxon" import { A, useNavigate, useParams } from "@solidjs/router" -import { useLayout, getAvatarColors } from "@/context/layout" +import { useLayout, getAvatarColors, LocalProject } from "@/context/layout" import { useGlobalSync } from "@/context/global-sync" import { base64Decode, base64Encode } from "@opencode-ai/util/encode" import { Avatar } from "@opencode-ai/ui/avatar" @@ -26,7 +27,7 @@ import { DiffChanges } from "@opencode-ai/ui/diff-changes" import { Spinner } from "@opencode-ai/ui/spinner" import { getFilename } from "@opencode-ai/util/path" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" -import { Session, Project } from "@opencode-ai/sdk/v2/client" +import { Session } from "@opencode-ai/sdk/v2/client" import { usePlatform } from "@/context/platform" import { createStore, produce } from "solid-js/store" import { @@ -53,9 +54,29 @@ export default function Layout(props: ParentProps) { const [store, setStore] = createStore({ lastSession: {} as { [directory: string]: string }, activeDraggable: undefined as string | undefined, + mobileSidebarOpen: false, + mobileProjectsExpanded: {} as Record, }) + const mobileSidebar = { + open: () => store.mobileSidebarOpen, + show: () => setStore("mobileSidebarOpen", true), + hide: () => setStore("mobileSidebarOpen", false), + toggle: () => setStore("mobileSidebarOpen", (x) => !x), + } + + const mobileProjects = { + expanded: (directory: string) => store.mobileProjectsExpanded[directory] ?? true, + expand: (directory: string) => setStore("mobileProjectsExpanded", directory, true), + collapse: (directory: string) => setStore("mobileProjectsExpanded", directory, false), + } + let scrollContainerRef: HTMLDivElement | undefined + const xlQuery = window.matchMedia("(min-width: 1280px)") + const [isLargeViewport, setIsLargeViewport] = createSignal(xlQuery.matches) + const handleViewportChange = (e: MediaQueryListEvent) => setIsLargeViewport(e.matches) + xlQuery.addEventListener("change", handleViewportChange) + onCleanup(() => xlQuery.removeEventListener("change", handleViewportChange)) const params = useParams() const globalSDK = useGlobalSDK() @@ -270,11 +291,13 @@ export default function Layout(props: ParentProps) { if (!directory) return const lastSession = store.lastSession[directory] navigate(`/${base64Encode(directory)}${lastSession ? `/session/${lastSession}` : ""}`) + mobileSidebar.hide() } function navigateToSession(session: Session | undefined) { if (!session) return navigate(`/${params.dir}/session/${session?.id}`) + mobileSidebar.hide() } function openProject(directory: string, navigate = true) { @@ -313,8 +336,12 @@ export default function Layout(props: ParentProps) { }) createEffect(() => { - const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48 - document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`) + if (isLargeViewport()) { + const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48 + document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`) + } else { + document.documentElement.style.setProperty("--dialog-left-margin", "0px") + } }) function getDraggableId(event: unknown): string | undefined { @@ -348,7 +375,7 @@ export default function Layout(props: ParentProps) { } const ProjectAvatar = (props: { - project: Project + project: LocalProject class?: string expandable?: boolean notify?: boolean @@ -391,7 +418,7 @@ export default function Layout(props: ParentProps) { ) } - const ProjectVisual = (props: { project: Project & { expanded: boolean }; class?: string }): JSX.Element => { + const ProjectVisual = (props: { project: LocalProject; class?: string }): JSX.Element => { const name = createMemo(() => getFilename(props.project.worktree)) const current = createMemo(() => base64Decode(params.dir ?? "")) return ( @@ -427,9 +454,10 @@ export default function Layout(props: ParentProps) { const SessionItem = (props: { session: Session slug: string - project: Project + project: LocalProject depth?: number childrenMap: Map + mobile?: boolean }): JSX.Element => { const notification = useNotification() const depth = props.depth ?? 0 @@ -450,7 +478,7 @@ export default function Layout(props: ParentProps) { hover:bg-surface-raised-base-hover focus-within:bg-surface-raised-base-hover has-[.active]:bg-surface-raised-base-hover" style={{ "padding-left": `${16 + depth * 12}px` }} > - +
@@ -510,6 +538,7 @@ export default function Layout(props: ParentProps) { project={props.project} depth={depth + 1} childrenMap={props.childrenMap} + mobile={props.mobile} /> )} @@ -517,8 +546,9 @@ export default function Layout(props: ParentProps) { ) } - const SortableProject = (props: { project: Project & { expanded: boolean } }): JSX.Element => { + const SortableProject = (props: { project: LocalProject; mobile?: boolean }): JSX.Element => { const sortable = createSortable(props.project.worktree) + const showExpanded = createMemo(() => props.mobile || layout.sidebar.opened()) const slug = createMemo(() => base64Encode(props.project.worktree)) const name = createMemo(() => getFilename(props.project.worktree)) const [store, setProjectStore] = globalSync.child(props.project.worktree) @@ -542,13 +572,24 @@ export default function Layout(props: ParentProps) { setProjectStore("limit", (limit) => limit + 5) await globalSync.project.loadSessions(props.project.worktree) } - const [expanded, setExpanded] = createSignal(true) + const isExpanded = createMemo(() => + props.mobile ? mobileProjects.expanded(props.project.worktree) : props.project.expanded, + ) + const handleOpenChange = (open: boolean) => { + if (props.mobile) { + if (open) mobileProjects.expand(props.project.worktree) + else mobileProjects.collapse(props.project.worktree) + } else { + if (open) layout.projects.expand(props.project.worktree) + else layout.projects.collapse(props.project.worktree) + } + } return ( // @ts-ignore
- - + + - + + + +
- - -
- p.worktree)}> - {(project) => } - -
- - - - -
-
- - -
-
-
Getting started
-
OpenCode includes free models so you can start immediately.
-
Connect any provider to use models, inc. Claude, GPT, Gemini etc.
-
- - - + p.worktree)}> + + {(project) => } + + +
+ + + + +
+
+ + +
+
+
Getting started
+
OpenCode includes free models so you can start immediately.
+
Connect any provider to use models, inc. Claude, GPT, Gemini etc.
- - - + - - - - +
+
+ + - - {/* */} - {/* */} - {/* */} - + +
+ + + Open project + + {command.keybind("project.open")} + +
+ } + inactive={expanded()} + > + + + + +
+ + ) + } + + return ( +
+
+
+
+ + + + +
+
+
{ + if (e.target === e.currentTarget) mobileSidebar.hide() + }} + /> +
e.stopPropagation()} + > +
+
{props.children}
diff --git a/packages/desktop/src/pages/session.tsx b/packages/app/src/pages/session.tsx similarity index 62% rename from packages/desktop/src/pages/session.tsx rename to packages/app/src/pages/session.tsx index 6e993ff8f..42e43232a 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/app/src/pages/session.tsx @@ -12,6 +12,7 @@ import { createRenderEffect, batch, } from "solid-js" + import { Dynamic } from "solid-js/web" import { useLocal, type LocalFile } from "@/context/local" import { createStore } from "solid-js/store" @@ -22,11 +23,11 @@ import { IconButton } from "@opencode-ai/ui/icon-button" import { Icon } from "@opencode-ai/ui/icon" import { Tooltip } from "@opencode-ai/ui/tooltip" import { DiffChanges } from "@opencode-ai/ui/diff-changes" -import { ProgressCircle } from "@opencode-ai/ui/progress-circle" import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { Tabs } from "@opencode-ai/ui/tabs" import { useCodeComponent } from "@opencode-ai/ui/context/code" import { SessionTurn } from "@opencode-ai/ui/session-turn" +import { createAutoScroll } from "@opencode-ai/ui/hooks" import { SessionMessageRail } from "@opencode-ai/ui/session-message-rail" import { SessionReview } from "@opencode-ai/ui/session-review" import { @@ -50,7 +51,7 @@ import { DialogSelectFile } from "@/components/dialog-select-file" import { DialogSelectModel } from "@/components/dialog-select-model" import { useCommand } from "@/context/command" import { useNavigate, useParams } from "@solidjs/router" -import { AssistantMessage, UserMessage } from "@opencode-ai/sdk/v2" +import { UserMessage } from "@opencode-ai/sdk/v2" import { useSDK } from "@/context/sdk" import { usePrompt } from "@/context/prompt" import { extractPromptFromParts } from "@/utils/prompt" @@ -71,7 +72,6 @@ export default function Page() { const sessionKey = createMemo(() => `${params.dir}${params.id ? "/" + params.id : ""}`) const tabs = createMemo(() => layout.tabs(sessionKey())) - const info = createMemo(() => (params.id ? sync.session.get(params.id) : undefined)) const revertMessageID = createMemo(() => info()?.revert?.messageID) const messages = createMemo(() => (params.id ? (sync.data.message[params.id] ?? []) : [])) @@ -80,7 +80,6 @@ export default function Page() { .filter((m) => m.role === "user") .sort((a, b) => a.id.localeCompare(b.id)), ) - // Visible user messages excludes reverted messages (those >= revertMessageID) const visibleUserMessages = createMemo(() => { const revert = revertMessageID() if (!revert) return userMessages() @@ -88,15 +87,24 @@ export default function Page() { }) const lastUserMessage = createMemo(() => visibleUserMessages()?.at(-1)) - const [messageStore, setMessageStore] = createStore<{ messageId?: string }>({}) + const [store, setStore] = createStore({ + clickTimer: undefined as number | undefined, + activeDraggable: undefined as string | undefined, + activeTerminalDraggable: undefined as string | undefined, + userInteracted: false, + stepsExpanded: true, + mobileStepsExpanded: {} as Record, + messageId: undefined as string | undefined, + }) + const activeMessage = createMemo(() => { - if (!messageStore.messageId) return lastUserMessage() + if (!store.messageId) return lastUserMessage() // If the stored message is no longer visible (e.g., was reverted), fall back to last visible - const found = visibleUserMessages()?.find((m) => m.id === messageStore.messageId) + const found = visibleUserMessages()?.find((m) => m.id === store.messageId) return found ?? lastUserMessage() }) const setActiveMessage = (message: UserMessage | undefined) => { - setMessageStore("messageId", message?.id) + setStore("messageId", message?.id) } function navigateMessageByOffset(offset: number) { @@ -118,34 +126,8 @@ export default function Page() { setActiveMessage(msgs[targetIndex]) } - const last = createMemo( - () => messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage, - ) - const model = createMemo(() => - last() ? sync.data.provider.all.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined, - ) const diffs = createMemo(() => (params.id ? (sync.data.session_diff[params.id] ?? []) : [])) - const tokens = createMemo(() => { - if (!last()) return - const t = last().tokens - return t.input + t.output + t.reasoning + t.cache.read + t.cache.write - }) - - const context = createMemo(() => { - const total = tokens() - const limit = model()?.limit.context - if (!total || !limit) return 0 - return Math.round((total / limit) * 100) - }) - - const [store, setStore] = createStore({ - clickTimer: undefined as number | undefined, - activeDraggable: undefined as string | undefined, - activeTerminalDraggable: undefined as string | undefined, - userInteracted: false, - stepsExpanded: true, - }) let inputRef!: HTMLDivElement createEffect(() => { @@ -166,7 +148,7 @@ export default function Page() { () => visibleUserMessages().at(-1)?.id, (lastId, prevLastId) => { if (lastId && prevLastId && lastId > prevLastId) { - setMessageStore("messageId", undefined) + setStore("messageId", undefined) } }, { defer: true }, @@ -239,6 +221,15 @@ export default function Page() { slash: "terminal", onSelect: () => layout.terminal.toggle(), }, + { + id: "review.toggle", + title: "Toggle review", + description: "Show or hide the review panel", + category: "View", + keybind: "mod+b", + slash: "review", + onSelect: () => layout.review.toggle(), + }, { id: "terminal.new", title: "New terminal", @@ -551,279 +542,321 @@ export default function Page() { ) } - const wide = createMemo(() => layout.review.state() === "tab" || !diffs().length) + const showTabs = createMemo(() => layout.review.opened() && (diffs().length > 0 || tabs().all().length > 0)) + + const mobileWorking = createMemo(() => status().type !== "idle") + const mobileAutoScroll = createAutoScroll({ + working: mobileWorking, + onUserInteracted: () => setStore("userInteracted", true), + }) + + const MobileTurns = () => ( +
+
+ + {(message) => ( + setStore("mobileStepsExpanded", message.id, (x) => !x)} + onUserInteracted={() => setStore("userInteracted", true)} + classes={{ + root: "min-w-0 w-full relative", + content: + "flex flex-col justify-between !overflow-visible [&_[data-slot=session-turn-message-header]]:top-[-32px]", + container: "px-4", + }} + /> + )} + +
+
+ ) + + const NewSessionView = () => ( +
+
New session
+
+ +
+ {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)} +
+
+ + {(project) => ( +
+ +
+ Last modified  + + {DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()} + +
+
+ )} +
+
+ ) + + const DesktopSessionContent = () => ( + + +
+ + + setStore("stepsExpanded", (x) => !x)} + onUserInteracted={() => setStore("userInteracted", true)} + classes={{ + root: "pb-20 flex-1 min-w-0", + content: "pb-20", + container: + "w-full " + + (!showTabs() ? "max-w-200 mx-auto px-6" : visibleUserMessages().length > 1 ? "pr-6 pl-18" : "px-6"), + }} + /> + +
+
+ + + +
+ ) return ( -
-
- - - - -
- - -
-
Session
- - -
{context() ?? 0}%
-
-
-
- - - - - } - > -
- - - -
-
Review
- -
- {info()?.summary?.files ?? 0} -
-
-
-
-
-
- - - {(tab) => } - - -
- - dialog.show(() => )} - /> - -
-
+
+
+ + +
+
- -
-
- - -
- - - setStore("stepsExpanded", (x) => !x)} - onUserInteracted={() => setStore("userInteracted", true)} - classes={{ - root: "pb-20 flex-1 min-w-0", - content: "pb-20", - container: - "w-full " + - (wide() - ? "max-w-200 mx-auto px-6" - : visibleUserMessages().length > 1 - ? "pr-6 pl-18" - : "px-6"), - }} - /> - -
-
- -
-
New session
-
- -
- {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
-
- - {(project) => ( -
- -
- Last modified  - - {DateTime.fromMillis(project().time.updated ?? project().time.created).toRelative()} - -
-
- )} -
-
-
-
-
-
- { - inputRef = el - }} - /> -
-
-
- -
- - { - layout.review.tab() - tabs().setActive("review") - }} - /> - - } - /> -
-
-
-
- - -
+ + 0}> + + + + Session + + + {diffs().length} Files Changed + + + + + + - - - {(tab) => { - const [file] = createResource( - () => tab, - async (tab) => { - if (tab.startsWith("file://")) { - return local.file.node(tab.replace("file://", "")) - } - return undefined - }, - ) - return ( - - - - {(f) => ( - - )} - - - - ) - }} - - - - - {(draggedFile) => { - const [file] = createResource( - () => draggedFile(), - async (tab) => { - if (tab.startsWith("file://")) { - return local.file.node(tab.replace("file://", "")) - } - return undefined - }, - ) - return ( -
- {(f) => } -
- ) - }} -
-
- - -
+ + + +
+ +
+
+ +
+
{ inputRef = el }} />
+
+
+ +
+
{(pty) => }
- + + New terminal + {command.keybind("terminal.new")} +
+ } + class="flex items-center" + >
diff --git a/packages/desktop/src/sst-env.d.ts b/packages/app/src/sst-env.d.ts similarity index 100% rename from packages/desktop/src/sst-env.d.ts rename to packages/app/src/sst-env.d.ts diff --git a/packages/desktop/src/utils/dom.ts b/packages/app/src/utils/dom.ts similarity index 100% rename from packages/desktop/src/utils/dom.ts rename to packages/app/src/utils/dom.ts diff --git a/packages/desktop/src/utils/id.ts b/packages/app/src/utils/id.ts similarity index 100% rename from packages/desktop/src/utils/id.ts rename to packages/app/src/utils/id.ts diff --git a/packages/desktop/src/utils/index.ts b/packages/app/src/utils/index.ts similarity index 100% rename from packages/desktop/src/utils/index.ts rename to packages/app/src/utils/index.ts diff --git a/packages/desktop/src/utils/persist.ts b/packages/app/src/utils/persist.ts similarity index 100% rename from packages/desktop/src/utils/persist.ts rename to packages/app/src/utils/persist.ts diff --git a/packages/desktop/src/utils/prompt.ts b/packages/app/src/utils/prompt.ts similarity index 100% rename from packages/desktop/src/utils/prompt.ts rename to packages/app/src/utils/prompt.ts diff --git a/packages/desktop/src/utils/solid-dnd.tsx b/packages/app/src/utils/solid-dnd.tsx similarity index 100% rename from packages/desktop/src/utils/solid-dnd.tsx rename to packages/app/src/utils/solid-dnd.tsx diff --git a/packages/desktop/src/utils/speech.ts b/packages/app/src/utils/speech.ts similarity index 100% rename from packages/desktop/src/utils/speech.ts rename to packages/app/src/utils/speech.ts diff --git a/packages/tauri/sst-env.d.ts b/packages/app/sst-env.d.ts similarity index 100% rename from packages/tauri/sst-env.d.ts rename to packages/app/sst-env.d.ts diff --git a/packages/tauri/tsconfig.json b/packages/app/tsconfig.json similarity index 57% rename from packages/tauri/tsconfig.json rename to packages/app/tsconfig.json index e7f5c5c27..db04f79ca 100644 --- a/packages/tauri/tsconfig.json +++ b/packages/app/tsconfig.json @@ -1,5 +1,7 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { + "composite": true, "target": "ESNext", "module": "ESNext", "skipLibCheck": true, @@ -10,11 +12,13 @@ "jsxImportSource": "solid-js", "allowJs": true, "strict": true, + "noEmit": false, + "emitDeclarationOnly": true, + "outDir": "node_modules/.ts-dist", "isolatedModules": true, - "noEmit": true, - "emitDeclarationOnly": false, - "outDir": "node_modules/.ts-dist" + "paths": { + "@/*": ["./src/*"] + } }, - "references": [{ "path": "../desktop" }], - "include": ["src"] + "exclude": ["dist", "ts-dist"] } diff --git a/packages/app/vite.config.ts b/packages/app/vite.config.ts new file mode 100644 index 000000000..57071a894 --- /dev/null +++ b/packages/app/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from "vite" +import desktopPlugin from "./vite" + +export default defineConfig({ + plugins: [desktopPlugin] as any, + server: { + host: "0.0.0.0", + allowedHosts: true, + port: 3000, + }, + build: { + target: "esnext", + sourcemap: true, + }, +}) diff --git a/packages/desktop/vite.js b/packages/app/vite.js similarity index 100% rename from packages/desktop/vite.js rename to packages/app/vite.js diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 129ef4e94..f22d54b8a 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.0.170", + "version": "1.0.191", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/console/app/src/component/icon.tsx b/packages/console/app/src/component/icon.tsx index a28fc51a3..1225aeb10 100644 --- a/packages/console/app/src/component/icon.tsx +++ b/packages/console/app/src/component/icon.tsx @@ -202,6 +202,14 @@ export function IconZai(props: JSX.SvgSVGAttributes) { ) } +export function IconMiniMax(props: JSX.SvgSVGAttributes) { + return ( + + + + ) +} + export function IconGemini(props: JSX.SvgSVGAttributes) { return ( diff --git a/packages/console/app/src/config.ts b/packages/console/app/src/config.ts index 29df86cbd..bf20681ae 100644 --- a/packages/console/app/src/config.ts +++ b/packages/console/app/src/config.ts @@ -9,8 +9,8 @@ export const config = { github: { repoUrl: "https://github.com/sst/opencode", starsFormatted: { - compact: "38K", - full: "38,000", + compact: "41K", + full: "41,000", }, }, @@ -22,8 +22,8 @@ export const config = { // Static stats (used on landing page) stats: { - contributors: "400", - commits: "5,000", + contributors: "450", + commits: "6,000", monthlyUsers: "400,000", }, } as const diff --git a/packages/console/app/src/routes/download/[platform].ts b/packages/console/app/src/routes/download/[platform].ts index 486b6bf6c..427fb132b 100644 --- a/packages/console/app/src/routes/download/[platform].ts +++ b/packages/console/app/src/routes/download/[platform].ts @@ -6,6 +6,7 @@ const assetNames: Record = { "darwin-x64-dmg": "opencode-desktop-darwin-x64.dmg", "windows-x64-nsis": "opencode-desktop-windows-x64.exe", "linux-x64-deb": "opencode-desktop-linux-amd64.deb", + "linux-x64-appimage": "opencode-desktop-linux-amd64.AppImage", "linux-x64-rpm": "opencode-desktop-linux-x86_64.rpm", } satisfies Record diff --git a/packages/console/app/src/routes/download/index.tsx b/packages/console/app/src/routes/download/index.tsx index 4f4258755..d4de97c68 100644 --- a/packages/console/app/src/routes/download/index.tsx +++ b/packages/console/app/src/routes/download/index.tsx @@ -244,6 +244,22 @@ export default function Download() { Download
+
diff --git a/packages/console/app/src/routes/download/types.ts b/packages/console/app/src/routes/download/types.ts index adf71880d..916f97022 100644 --- a/packages/console/app/src/routes/download/types.ts +++ b/packages/console/app/src/routes/download/types.ts @@ -1 +1,4 @@ -export type DownloadPlatform = `darwin-${"x64" | "aarch64"}-dmg` | "windows-x64-nsis" | `linux-x64-${"deb" | "rpm"}` +export type DownloadPlatform = + | `darwin-${"x64" | "aarch64"}-dmg` + | "windows-x64-nsis" + | `linux-x64-${"deb" | "rpm" | "appimage"}` diff --git a/packages/console/app/src/routes/index.tsx b/packages/console/app/src/routes/index.tsx index dbea5b319..227021b89 100644 --- a/packages/console/app/src/routes/index.tsx +++ b/packages/console/app/src/routes/index.tsx @@ -1,6 +1,6 @@ import "./index.css" import { Title, Meta, Link } from "@solidjs/meta" -import { HttpHeader } from "@solidjs/start" +//import { HttpHeader } from "@solidjs/start" import video from "../asset/lander/opencode-min.mp4" import videoPoster from "../asset/lander/opencode-poster.png" import { IconCopy, IconCheck } from "../component/icon" @@ -42,7 +42,7 @@ export default function Home() { return (
- + {/**/} OpenCode | The open source AI coding agent diff --git a/packages/console/app/src/routes/workspace/[id]/model-section.tsx b/packages/console/app/src/routes/workspace/[id]/model-section.tsx index e760ccea2..38813d276 100644 --- a/packages/console/app/src/routes/workspace/[id]/model-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/model-section.tsx @@ -9,6 +9,7 @@ import { IconAlibaba, IconAnthropic, IconGemini, + IconMiniMax, IconMoonshotAI, IconOpenAI, IconStealth, @@ -23,6 +24,7 @@ const getModelLab = (modelId: string) => { if (modelId.startsWith("kimi")) return "Moonshot AI" if (modelId.startsWith("glm")) return "Z.ai" if (modelId.startsWith("qwen")) return "Alibaba" + if (modelId.startsWith("minimax")) return "MiniMax" if (modelId.startsWith("grok")) return "xAI" return "Stealth" } @@ -35,7 +37,7 @@ const getModelsInfo = query(async (workspaceID: string) => { .filter(([id, _model]) => !["claude-3-5-haiku"].includes(id)) .filter(([id, _model]) => !id.startsWith("alpha-")) .sort(([idA, modelA], [idB, modelB]) => { - const priority = ["big-pickle", "grok", "claude", "gpt", "gemini"] + const priority = ["big-pickle", "minimax", "grok", "claude", "gpt", "gemini"] const getPriority = (id: string) => { const index = priority.findIndex((p) => id.startsWith(p)) return index === -1 ? Infinity : index @@ -129,6 +131,8 @@ export function ModelSection() { return case "xAI": return + case "MiniMax": + return default: return } diff --git a/packages/console/app/src/routes/zen/index.tsx b/packages/console/app/src/routes/zen/index.tsx index f1750ceca..5708c238c 100644 --- a/packages/console/app/src/routes/zen/index.tsx +++ b/packages/console/app/src/routes/zen/index.tsx @@ -1,7 +1,7 @@ import "./index.css" import { createAsync, query, redirect } from "@solidjs/router" import { Title, Meta, Link } from "@solidjs/meta" -import { HttpHeader } from "@solidjs/start" +//import { HttpHeader } from "@solidjs/start" import zenLogoLight from "../../asset/zen-ornate-light.svg" import { config } from "~/config" import zenLogoDark from "../../asset/zen-ornate-dark.svg" @@ -30,7 +30,7 @@ export default function Home() { const loggedin = createAsync(() => checkLoggedIn()) return (
- + {/**/} OpenCode Zen | A curated set of reliable optimized models for coding agents diff --git a/packages/console/app/src/routes/zen/util/dataDumper.ts b/packages/console/app/src/routes/zen/util/dataDumper.ts index 155cc6c58..b852ca0b5 100644 --- a/packages/console/app/src/routes/zen/util/dataDumper.ts +++ b/packages/console/app/src/routes/zen/util/dataDumper.ts @@ -19,17 +19,23 @@ export function createDataDumper(sessionId: string, requestId: string, projectId if (!data.modelName) return const timestamp = new Date().toISOString().replace(/[^0-9]/g, "") + const year = timestamp.substring(0, 4) + const month = timestamp.substring(4, 6) + const day = timestamp.substring(6, 8) + const hour = timestamp.substring(8, 10) + const minute = timestamp.substring(10, 12) + const second = timestamp.substring(12, 14) waitUntil( - Resource.ZenData.put( - `data/${data.modelName}/${sessionId}/${requestId}.json`, + Resource.ZenDataNew.put( + `data/${data.modelName}/${year}/${month}/${day}/${hour}/${minute}/${second}/${requestId}.json`, JSON.stringify({ timestamp, ...data }), ), ) waitUntil( - Resource.ZenData.put( - `meta/${data.modelName}/${timestamp}/${requestId}.json`, + Resource.ZenDataNew.put( + `meta/${data.modelName}/${sessionId}/${requestId}.json`, JSON.stringify({ timestamp, ...metadata }), ), ) diff --git a/packages/console/core/package.json b/packages/console/core/package.json index ef03aeee1..6ffedab0a 100644 --- a/packages/console/core/package.json +++ b/packages/console/core/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/console-core", - "version": "1.0.170", + "version": "1.0.191", "private": true, "type": "module", "dependencies": { diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 632ea3fbe..ffa17f276 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -132,6 +132,7 @@ declare module "sst" { "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service "ZenData": cloudflare.R2Bucket + "ZenDataNew": cloudflare.R2Bucket } } diff --git a/packages/console/function/package.json b/packages/console/function/package.json index 1171b9361..95fc0e474 100644 --- a/packages/console/function/package.json +++ b/packages/console/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-function", - "version": "1.0.170", + "version": "1.0.191", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 632ea3fbe..ffa17f276 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -132,6 +132,7 @@ declare module "sst" { "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service "ZenData": cloudflare.R2Bucket + "ZenDataNew": cloudflare.R2Bucket } } diff --git a/packages/console/mail/package.json b/packages/console/mail/package.json index 51d37c786..ff6b3f102 100644 --- a/packages/console/mail/package.json +++ b/packages/console/mail/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-mail", - "version": "1.0.170", + "version": "1.0.191", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 632ea3fbe..ffa17f276 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -132,6 +132,7 @@ declare module "sst" { "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service "ZenData": cloudflare.R2Bucket + "ZenDataNew": cloudflare.R2Bucket } } diff --git a/packages/desktop/.gitignore b/packages/desktop/.gitignore index 4a20d55a7..a547bf36d 100644 --- a/packages/desktop/.gitignore +++ b/packages/desktop/.gitignore @@ -1 +1,24 @@ -src/assets/theme.css +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/packages/desktop/README.md b/packages/desktop/README.md index 6a1764536..b381dcf5b 100644 --- a/packages/desktop/README.md +++ b/packages/desktop/README.md @@ -1,34 +1,7 @@ -## Usage +# Tauri + Vanilla TS -Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`. +This template should help get you started developing with Tauri in vanilla HTML, CSS and Typescript. -This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template. +## Recommended IDE Setup -```bash -$ npm install # or pnpm install or yarn install -``` - -### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) - -## Available Scripts - -In the project directory, you can run: - -### `npm run dev` or `npm start` - -Runs the app in the development mode.
-Open [http://localhost:3000](http://localhost:3000) to view it in the browser. - -The page will reload if you make edits.
- -### `npm run build` - -Builds the app for production to the `dist` folder.
-It correctly bundles Solid in production mode and optimizes the build for the best performance. - -The build is minified and the filenames include the hashes.
-Your app is ready to be deployed! - -## Deployment - -You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) +- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) diff --git a/packages/desktop/index.html b/packages/desktop/index.html index 9803517a0..faeb1a1fd 100644 --- a/packages/desktop/index.html +++ b/packages/desktop/index.html @@ -14,7 +14,7 @@ - +
- + diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 5c821a79c..23eab7f4d 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,62 +1,37 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.170", - "description": "", + "private": true, + "version": "1.0.191", "type": "module", - "exports": { - ".": "./src/index.ts", - "./vite": "./vite.js" - }, "scripts": { "typecheck": "tsgo -b", - "start": "vite", + "predev": "bun ./scripts/predev.ts", "dev": "vite", - "build": "vite build", - "serve": "vite preview" - }, - "license": "MIT", - "devDependencies": { - "@happy-dom/global-registrator": "20.0.11", - "@tailwindcss/vite": "catalog:", - "@tsconfig/bun": "1.0.9", - "@types/bun": "catalog:", - "@types/luxon": "catalog:", - "@types/node": "catalog:", - "@typescript/native-preview": "catalog:", - "typescript": "catalog:", - "vite": "catalog:", - "vite-plugin-icons-spritesheet": "3.0.1", - "vite-plugin-solid": "catalog:" + "build": "bun run typecheck && vite build", + "preview": "vite preview", + "tauri": "tauri" }, "dependencies": { - "@kobalte/core": "catalog:", - "@opencode-ai/sdk": "workspace:*", - "@opencode-ai/ui": "workspace:*", - "@opencode-ai/util": "workspace:*", - "@shikijs/transformers": "3.9.2", - "@solid-primitives/active-element": "2.1.3", - "@solid-primitives/audio": "1.4.2", - "@solid-primitives/event-bus": "1.1.2", - "@solid-primitives/media": "2.3.3", - "@solid-primitives/resize-observer": "2.1.3", - "@solid-primitives/scroll": "2.1.3", + "@opencode-ai/app": "workspace:*", "@solid-primitives/storage": "catalog:", - "@solid-primitives/websocket": "1.3.1", - "@solidjs/meta": "catalog:", - "@solidjs/router": "catalog:", - "@thisbeyond/solid-dnd": "0.7.5", - "diff": "catalog:", - "fuzzysort": "catalog:", - "ghostty-web": "0.3.0", - "luxon": "catalog:", - "marked": "16.2.0", - "marked-shiki": "1.2.1", - "remeda": "catalog:", - "shiki": "3.9.2", - "solid-js": "catalog:", - "solid-list": "catalog:", - "tailwindcss": "catalog:", - "virtua": "catalog:", - "zod": "catalog:" + "@tauri-apps/api": "^2", + "@tauri-apps/plugin-dialog": "~2", + "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-os": "~2", + "@tauri-apps/plugin-process": "~2", + "@tauri-apps/plugin-shell": "~2", + "@tauri-apps/plugin-store": "~2", + "@tauri-apps/plugin-updater": "~2", + "@tauri-apps/plugin-http": "~2", + "@tauri-apps/plugin-window-state": "~2", + "solid-js": "catalog:" + }, + "devDependencies": { + "@actions/artifact": "4.0.0", + "@tauri-apps/cli": "^2", + "@types/bun": "catalog:", + "@typescript/native-preview": "catalog:", + "typescript": "~5.6.2", + "vite": "catalog:" } } diff --git a/packages/tauri/scripts/copy-bundles.ts b/packages/desktop/scripts/copy-bundles.ts similarity index 100% rename from packages/tauri/scripts/copy-bundles.ts rename to packages/desktop/scripts/copy-bundles.ts diff --git a/packages/tauri/scripts/predev.ts b/packages/desktop/scripts/predev.ts similarity index 100% rename from packages/tauri/scripts/predev.ts rename to packages/desktop/scripts/predev.ts diff --git a/packages/desktop/scripts/prepare.ts b/packages/desktop/scripts/prepare.ts new file mode 100755 index 000000000..495a0baea --- /dev/null +++ b/packages/desktop/scripts/prepare.ts @@ -0,0 +1,15 @@ +#!/usr/bin/env bun +import { $ } from "bun" + +import { copyBinaryToSidecarFolder, getCurrentSidecar } from "./utils" + +const sidecarConfig = getCurrentSidecar() + +const dir = "src-tauri/target/opencode-binaries" + +await $`mkdir -p ${dir}` +await $`gh run download ${Bun.env.GITHUB_RUN_ID} -n opencode-cli`.cwd(dir) + +await copyBinaryToSidecarFolder( + `${dir}/${sidecarConfig.ocBinary}/bin/opencode${process.platform === "win32" ? ".exe" : ""}`, +) diff --git a/packages/tauri/scripts/utils.ts b/packages/desktop/scripts/utils.ts similarity index 91% rename from packages/tauri/scripts/utils.ts rename to packages/desktop/scripts/utils.ts index 3e74346c8..885d0afce 100644 --- a/packages/tauri/scripts/utils.ts +++ b/packages/desktop/scripts/utils.ts @@ -21,6 +21,11 @@ export const SIDECAR_BINARIES: Array<{ rustTarget: string; ocBinary: string; ass ocBinary: "opencode-linux-x64", assetExt: "tar.gz", }, + { + rustTarget: "aarch64-unknown-linux-gnu", + ocBinary: "opencode-linux-arm64", + assetExt: "tar.gz", + }, ] export const RUST_TARGET = Bun.env.RUST_TARGET diff --git a/packages/tauri/src-tauri/.gitignore b/packages/desktop/src-tauri/.gitignore similarity index 100% rename from packages/tauri/src-tauri/.gitignore rename to packages/desktop/src-tauri/.gitignore diff --git a/packages/tauri/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock similarity index 100% rename from packages/tauri/src-tauri/Cargo.lock rename to packages/desktop/src-tauri/Cargo.lock diff --git a/packages/tauri/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml similarity index 92% rename from packages/tauri/src-tauri/Cargo.toml rename to packages/desktop/src-tauri/Cargo.toml index c6208190b..0463966c0 100644 --- a/packages/tauri/src-tauri/Cargo.toml +++ b/packages/desktop/src-tauri/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "opencode-desktop" version = "0.0.0" -description = "A Tauri App" -authors = ["you"] +description = "The open source AI coding agent" +authors = ["Anomaly Innovations"] edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/packages/tauri/src-tauri/assets/nsis-header.bmp b/packages/desktop/src-tauri/assets/nsis-header.bmp similarity index 100% rename from packages/tauri/src-tauri/assets/nsis-header.bmp rename to packages/desktop/src-tauri/assets/nsis-header.bmp diff --git a/packages/tauri/src-tauri/assets/nsis-sidebar.bmp b/packages/desktop/src-tauri/assets/nsis-sidebar.bmp similarity index 100% rename from packages/tauri/src-tauri/assets/nsis-sidebar.bmp rename to packages/desktop/src-tauri/assets/nsis-sidebar.bmp diff --git a/packages/tauri/src-tauri/build.rs b/packages/desktop/src-tauri/build.rs similarity index 100% rename from packages/tauri/src-tauri/build.rs rename to packages/desktop/src-tauri/build.rs diff --git a/packages/tauri/src-tauri/capabilities/default.json b/packages/desktop/src-tauri/capabilities/default.json similarity index 100% rename from packages/tauri/src-tauri/capabilities/default.json rename to packages/desktop/src-tauri/capabilities/default.json diff --git a/packages/tauri/src-tauri/entitlements.plist b/packages/desktop/src-tauri/entitlements.plist similarity index 100% rename from packages/tauri/src-tauri/entitlements.plist rename to packages/desktop/src-tauri/entitlements.plist diff --git a/packages/tauri/src-tauri/icons/README.md b/packages/desktop/src-tauri/icons/README.md similarity index 88% rename from packages/tauri/src-tauri/icons/README.md rename to packages/desktop/src-tauri/icons/README.md index d4a4e687d..db86593cc 100644 --- a/packages/tauri/src-tauri/icons/README.md +++ b/packages/desktop/src-tauri/icons/README.md @@ -2,7 +2,7 @@ Here's the process I've been using to create icons: -- Save source image as `app-icon.png` in `packages/tauri` +- Save source image as `app-icon.png` in `packages/desktop` - `cd` to `src-tauri` - Run `bun tauri icons -o icons/{environment}` - Use [Image2Icon](https://img2icnsapp.com/)'s 'Big Sur Icon' preset to generate an `icon.icns` file and place it in the appropriate icons folder diff --git a/packages/tauri/src-tauri/icons/dev/128x128.png b/packages/desktop/src-tauri/icons/dev/128x128.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/128x128.png rename to packages/desktop/src-tauri/icons/dev/128x128.png diff --git a/packages/tauri/src-tauri/icons/dev/128x128@2x.png b/packages/desktop/src-tauri/icons/dev/128x128@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/128x128@2x.png rename to packages/desktop/src-tauri/icons/dev/128x128@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/32x32.png b/packages/desktop/src-tauri/icons/dev/32x32.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/32x32.png rename to packages/desktop/src-tauri/icons/dev/32x32.png diff --git a/packages/tauri/src-tauri/icons/dev/64x64.png b/packages/desktop/src-tauri/icons/dev/64x64.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/64x64.png rename to packages/desktop/src-tauri/icons/dev/64x64.png diff --git a/packages/tauri/src-tauri/icons/dev/Square107x107Logo.png b/packages/desktop/src-tauri/icons/dev/Square107x107Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square107x107Logo.png rename to packages/desktop/src-tauri/icons/dev/Square107x107Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square142x142Logo.png b/packages/desktop/src-tauri/icons/dev/Square142x142Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square142x142Logo.png rename to packages/desktop/src-tauri/icons/dev/Square142x142Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square150x150Logo.png b/packages/desktop/src-tauri/icons/dev/Square150x150Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square150x150Logo.png rename to packages/desktop/src-tauri/icons/dev/Square150x150Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square284x284Logo.png b/packages/desktop/src-tauri/icons/dev/Square284x284Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square284x284Logo.png rename to packages/desktop/src-tauri/icons/dev/Square284x284Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square30x30Logo.png b/packages/desktop/src-tauri/icons/dev/Square30x30Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square30x30Logo.png rename to packages/desktop/src-tauri/icons/dev/Square30x30Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square310x310Logo.png b/packages/desktop/src-tauri/icons/dev/Square310x310Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square310x310Logo.png rename to packages/desktop/src-tauri/icons/dev/Square310x310Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square44x44Logo.png b/packages/desktop/src-tauri/icons/dev/Square44x44Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square44x44Logo.png rename to packages/desktop/src-tauri/icons/dev/Square44x44Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square71x71Logo.png b/packages/desktop/src-tauri/icons/dev/Square71x71Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square71x71Logo.png rename to packages/desktop/src-tauri/icons/dev/Square71x71Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/Square89x89Logo.png b/packages/desktop/src-tauri/icons/dev/Square89x89Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/Square89x89Logo.png rename to packages/desktop/src-tauri/icons/dev/Square89x89Logo.png diff --git a/packages/tauri/src-tauri/icons/dev/StoreLogo.png b/packages/desktop/src-tauri/icons/dev/StoreLogo.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/StoreLogo.png rename to packages/desktop/src-tauri/icons/dev/StoreLogo.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-anydpi-v26/ic_launcher.xml b/packages/desktop/src-tauri/icons/dev/android/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-anydpi-v26/ic_launcher.xml rename to packages/desktop/src-tauri/icons/dev/android/mipmap-anydpi-v26/ic_launcher.xml diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-hdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-mdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/dev/android/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/dev/android/values/ic_launcher_background.xml b/packages/desktop/src-tauri/icons/dev/android/values/ic_launcher_background.xml similarity index 100% rename from packages/tauri/src-tauri/icons/dev/android/values/ic_launcher_background.xml rename to packages/desktop/src-tauri/icons/dev/android/values/ic_launcher_background.xml diff --git a/packages/tauri/src-tauri/icons/dev/icon.icns b/packages/desktop/src-tauri/icons/dev/icon.icns similarity index 100% rename from packages/tauri/src-tauri/icons/dev/icon.icns rename to packages/desktop/src-tauri/icons/dev/icon.icns diff --git a/packages/tauri/src-tauri/icons/dev/icon.ico b/packages/desktop/src-tauri/icons/dev/icon.ico similarity index 100% rename from packages/tauri/src-tauri/icons/dev/icon.ico rename to packages/desktop/src-tauri/icons/dev/icon.ico diff --git a/packages/tauri/src-tauri/icons/dev/icon.png b/packages/desktop/src-tauri/icons/dev/icon.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/icon.png rename to packages/desktop/src-tauri/icons/dev/icon.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@1x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@1x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@1x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@2x-1.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@2x-1.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@2x-1.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@3x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-20x20@3x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-20x20@3x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@1x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@1x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@1x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@2x-1.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@2x-1.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@2x-1.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@3x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-29x29@3x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-29x29@3x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@1x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@1x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@1x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@2x-1.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@2x-1.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@2x-1.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@3x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-40x40@3x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-40x40@3x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-512@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-512@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-512@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-512@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-60x60@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-60x60@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-60x60@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-60x60@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-60x60@3x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-60x60@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-60x60@3x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-60x60@3x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-76x76@1x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-76x76@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-76x76@1x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-76x76@1x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-76x76@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-76x76@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-76x76@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-76x76@2x.png diff --git a/packages/tauri/src-tauri/icons/dev/ios/AppIcon-83.5x83.5@2x.png b/packages/desktop/src-tauri/icons/dev/ios/AppIcon-83.5x83.5@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/dev/ios/AppIcon-83.5x83.5@2x.png rename to packages/desktop/src-tauri/icons/dev/ios/AppIcon-83.5x83.5@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/128x128.png b/packages/desktop/src-tauri/icons/prod/128x128.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/128x128.png rename to packages/desktop/src-tauri/icons/prod/128x128.png diff --git a/packages/tauri/src-tauri/icons/prod/128x128@2x.png b/packages/desktop/src-tauri/icons/prod/128x128@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/128x128@2x.png rename to packages/desktop/src-tauri/icons/prod/128x128@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/32x32.png b/packages/desktop/src-tauri/icons/prod/32x32.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/32x32.png rename to packages/desktop/src-tauri/icons/prod/32x32.png diff --git a/packages/tauri/src-tauri/icons/prod/64x64.png b/packages/desktop/src-tauri/icons/prod/64x64.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/64x64.png rename to packages/desktop/src-tauri/icons/prod/64x64.png diff --git a/packages/tauri/src-tauri/icons/prod/Square107x107Logo.png b/packages/desktop/src-tauri/icons/prod/Square107x107Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square107x107Logo.png rename to packages/desktop/src-tauri/icons/prod/Square107x107Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square142x142Logo.png b/packages/desktop/src-tauri/icons/prod/Square142x142Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square142x142Logo.png rename to packages/desktop/src-tauri/icons/prod/Square142x142Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square150x150Logo.png b/packages/desktop/src-tauri/icons/prod/Square150x150Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square150x150Logo.png rename to packages/desktop/src-tauri/icons/prod/Square150x150Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square284x284Logo.png b/packages/desktop/src-tauri/icons/prod/Square284x284Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square284x284Logo.png rename to packages/desktop/src-tauri/icons/prod/Square284x284Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square30x30Logo.png b/packages/desktop/src-tauri/icons/prod/Square30x30Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square30x30Logo.png rename to packages/desktop/src-tauri/icons/prod/Square30x30Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square310x310Logo.png b/packages/desktop/src-tauri/icons/prod/Square310x310Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square310x310Logo.png rename to packages/desktop/src-tauri/icons/prod/Square310x310Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square44x44Logo.png b/packages/desktop/src-tauri/icons/prod/Square44x44Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square44x44Logo.png rename to packages/desktop/src-tauri/icons/prod/Square44x44Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square71x71Logo.png b/packages/desktop/src-tauri/icons/prod/Square71x71Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square71x71Logo.png rename to packages/desktop/src-tauri/icons/prod/Square71x71Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/Square89x89Logo.png b/packages/desktop/src-tauri/icons/prod/Square89x89Logo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/Square89x89Logo.png rename to packages/desktop/src-tauri/icons/prod/Square89x89Logo.png diff --git a/packages/tauri/src-tauri/icons/prod/StoreLogo.png b/packages/desktop/src-tauri/icons/prod/StoreLogo.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/StoreLogo.png rename to packages/desktop/src-tauri/icons/prod/StoreLogo.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-anydpi-v26/ic_launcher.xml b/packages/desktop/src-tauri/icons/prod/android/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-anydpi-v26/ic_launcher.xml rename to packages/desktop/src-tauri/icons/prod/android/mipmap-anydpi-v26/ic_launcher.xml diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-hdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-mdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_foreground.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_round.png b/packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_round.png rename to packages/desktop/src-tauri/icons/prod/android/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/packages/tauri/src-tauri/icons/prod/android/values/ic_launcher_background.xml b/packages/desktop/src-tauri/icons/prod/android/values/ic_launcher_background.xml similarity index 100% rename from packages/tauri/src-tauri/icons/prod/android/values/ic_launcher_background.xml rename to packages/desktop/src-tauri/icons/prod/android/values/ic_launcher_background.xml diff --git a/packages/tauri/src-tauri/icons/prod/icon.icns b/packages/desktop/src-tauri/icons/prod/icon.icns similarity index 100% rename from packages/tauri/src-tauri/icons/prod/icon.icns rename to packages/desktop/src-tauri/icons/prod/icon.icns diff --git a/packages/tauri/src-tauri/icons/prod/icon.ico b/packages/desktop/src-tauri/icons/prod/icon.ico similarity index 100% rename from packages/tauri/src-tauri/icons/prod/icon.ico rename to packages/desktop/src-tauri/icons/prod/icon.ico diff --git a/packages/tauri/src-tauri/icons/prod/icon.png b/packages/desktop/src-tauri/icons/prod/icon.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/icon.png rename to packages/desktop/src-tauri/icons/prod/icon.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@1x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@1x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@1x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@2x-1.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@2x-1.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@2x-1.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@3x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-20x20@3x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-20x20@3x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@1x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@1x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@1x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@2x-1.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@2x-1.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@2x-1.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@3x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-29x29@3x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-29x29@3x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@1x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@1x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@1x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@2x-1.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@2x-1.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@2x-1.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@2x-1.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@3x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-40x40@3x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-40x40@3x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-512@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-512@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-512@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-512@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-60x60@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-60x60@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-60x60@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-60x60@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-60x60@3x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-60x60@3x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-60x60@3x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-60x60@3x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-76x76@1x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-76x76@1x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-76x76@1x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-76x76@1x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-76x76@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-76x76@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-76x76@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-76x76@2x.png diff --git a/packages/tauri/src-tauri/icons/prod/ios/AppIcon-83.5x83.5@2x.png b/packages/desktop/src-tauri/icons/prod/ios/AppIcon-83.5x83.5@2x.png similarity index 100% rename from packages/tauri/src-tauri/icons/prod/ios/AppIcon-83.5x83.5@2x.png rename to packages/desktop/src-tauri/icons/prod/ios/AppIcon-83.5x83.5@2x.png diff --git a/packages/tauri/src-tauri/src/lib.rs b/packages/desktop/src-tauri/src/lib.rs similarity index 97% rename from packages/tauri/src-tauri/src/lib.rs rename to packages/desktop/src-tauri/src/lib.rs index ffefbabf6..3c08841ab 100644 --- a/packages/tauri/src-tauri/src/lib.rs +++ b/packages/desktop/src-tauri/src/lib.rs @@ -6,7 +6,7 @@ use std::{ sync::{Arc, Mutex}, time::{Duration, Instant}, }; -use tauri::{AppHandle, LogicalSize, Manager, RunEvent, WebviewUrl, WebviewWindow}; +use tauri::{AppHandle, LogicalSize, Manager, RunEvent, WebviewUrl, WebviewWindow, path::BaseDirectory}; use tauri_plugin_clipboard_manager::ClipboardExt; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogResult}; use tauri_plugin_shell::process::{CommandChild, CommandEvent}; @@ -97,6 +97,11 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { let log_state = app.state::(); let log_state_clone = log_state.inner().clone(); + let state_dir = app + .path() + .resolve("", BaseDirectory::AppLocalData) + .expect("Failed to resolve app local data dir"); + #[cfg(target_os = "windows")] let (mut rx, child) = app .shell() @@ -104,6 +109,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { .unwrap() .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") .env("OPENCODE_CLIENT", "desktop") + .env("XDG_STATE_HOME", &state_dir) .args(["serve", &format!("--port={port}")]) .spawn() .expect("Failed to spawn opencode"); @@ -120,6 +126,7 @@ fn spawn_sidecar(app: &AppHandle, port: u32) -> CommandChild { .command(&shell) .env("OPENCODE_EXPERIMENTAL_ICON_DISCOVERY", "true") .env("OPENCODE_CLIENT", "desktop") + .env("XDG_STATE_HOME", &state_dir) .args([ "-il", "-c", diff --git a/packages/tauri/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs similarity index 100% rename from packages/tauri/src-tauri/src/main.rs rename to packages/desktop/src-tauri/src/main.rs diff --git a/packages/tauri/src-tauri/src/window_customizer.rs b/packages/desktop/src-tauri/src/window_customizer.rs similarity index 100% rename from packages/tauri/src-tauri/src/window_customizer.rs rename to packages/desktop/src-tauri/src/window_customizer.rs diff --git a/packages/tauri/src-tauri/tauri.conf.json b/packages/desktop/src-tauri/tauri.conf.json similarity index 93% rename from packages/tauri/src-tauri/tauri.conf.json rename to packages/desktop/src-tauri/tauri.conf.json index b6737d383..bcb067a32 100644 --- a/packages/tauri/src-tauri/tauri.conf.json +++ b/packages/desktop/src-tauri/tauri.conf.json @@ -26,7 +26,7 @@ "icons/dev/icon.ico" ], "active": true, - "targets": ["deb", "rpm", "dmg", "nsis", "app"], + "targets": ["deb", "rpm", "dmg", "nsis", "app", "appimage"], "externalBin": ["sidecars/opencode-cli"], "macOS": { "entitlements": "./entitlements.plist" diff --git a/packages/tauri/src-tauri/tauri.prod.conf.json b/packages/desktop/src-tauri/tauri.prod.conf.json similarity index 100% rename from packages/tauri/src-tauri/tauri.prod.conf.json rename to packages/desktop/src-tauri/tauri.prod.conf.json diff --git a/packages/tauri/src/index.tsx b/packages/desktop/src/index.tsx similarity index 97% rename from packages/tauri/src/index.tsx rename to packages/desktop/src/index.tsx index dc2c4047d..57c1fbe55 100644 --- a/packages/tauri/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -1,6 +1,6 @@ // @refresh reload import { render } from "solid-js/web" -import { App, PlatformProvider, Platform } from "@opencode-ai/desktop" +import { App, PlatformProvider, Platform } from "@opencode-ai/app" import { open, save } from "@tauri-apps/plugin-dialog" import { open as shellOpen } from "@tauri-apps/plugin-shell" import { type as ostype } from "@tauri-apps/plugin-os" diff --git a/packages/tauri/src/menu.ts b/packages/desktop/src/menu.ts similarity index 100% rename from packages/tauri/src/menu.ts rename to packages/desktop/src/menu.ts diff --git a/packages/tauri/src/updater.ts b/packages/desktop/src/updater.ts similarity index 100% rename from packages/tauri/src/updater.ts rename to packages/desktop/src/updater.ts diff --git a/packages/desktop/tsconfig.json b/packages/desktop/tsconfig.json index db04f79ca..64a6bc357 100644 --- a/packages/desktop/tsconfig.json +++ b/packages/desktop/tsconfig.json @@ -1,7 +1,5 @@ { - "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "composite": true, "target": "ESNext", "module": "ESNext", "skipLibCheck": true, @@ -12,13 +10,11 @@ "jsxImportSource": "solid-js", "allowJs": true, "strict": true, - "noEmit": false, - "emitDeclarationOnly": true, - "outDir": "node_modules/.ts-dist", "isolatedModules": true, - "paths": { - "@/*": ["./src/*"] - } + "noEmit": true, + "emitDeclarationOnly": false, + "outDir": "node_modules/.ts-dist" }, - "exclude": ["dist", "ts-dist"] + "references": [{ "path": "../app" }], + "include": ["src"] } diff --git a/packages/desktop/vite.config.ts b/packages/desktop/vite.config.ts index a388884cd..123a2028c 100644 --- a/packages/desktop/vite.config.ts +++ b/packages/desktop/vite.config.ts @@ -1,14 +1,30 @@ import { defineConfig } from "vite" -import desktopPlugin from "./vite" +import appPlugin from "@opencode-ai/app/vite" +const host = process.env.TAURI_DEV_HOST + +// https://vite.dev/config/ export default defineConfig({ - plugins: [desktopPlugin] as any, + plugins: [appPlugin], + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent Vite from obscuring rust errors + clearScreen: false, + // 2. tauri expects a fixed port, fail if that port is not available server: { - host: "0.0.0.0", - allowedHosts: true, - port: 3000, - }, - build: { - target: "esnext", + port: 1420, + strictPort: true, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, + watch: { + // 3. tell Vite to ignore watching `src-tauri` + ignored: ["**/src-tauri/**"], + }, }, }) diff --git a/packages/enterprise/package.json b/packages/enterprise/package.json index 025bff972..3df50aff2 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.170", + "version": "1.0.191", "private": true, "type": "module", "scripts": { diff --git a/packages/enterprise/src/routes/share/[shareID].tsx b/packages/enterprise/src/routes/share/[shareID].tsx index a8b2c7f24..471104d79 100644 --- a/packages/enterprise/src/routes/share/[shareID].tsx +++ b/packages/enterprise/src/routes/share/[shareID].tsx @@ -212,6 +212,7 @@ export default function () { {iife(() => { const [store, setStore] = createStore({ messageId: undefined as string | undefined, + expandedSteps: {} as Record, }) const messages = createMemo(() => data().sessionID @@ -253,20 +254,22 @@ export default function () { const title = () => (
-
-
+
+
v{info().version}
-
- -
{model()?.name ?? modelID()}
-
-
- {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
+
+ +
{model()?.name ?? modelID()}
+
+
+ {DateTime.fromMillis(info().time.created).toFormat("dd MMM yyyy, HH:mm")} +
{info().title}
@@ -282,6 +285,8 @@ export default function () { setStore("expandedSteps", message.id, (v) => !v)} classes={{ root: "min-w-0 w-full relative", content: @@ -359,6 +364,13 @@ export default function () { { + const id = store.messageId ?? firstUserMessage()!.id! + setStore("expandedSteps", id, (v) => !v) + }} classes={{ root: "grow", content: "flex flex-col justify-between", diff --git a/packages/enterprise/sst-env.d.ts b/packages/enterprise/sst-env.d.ts index 632ea3fbe..ffa17f276 100644 --- a/packages/enterprise/sst-env.d.ts +++ b/packages/enterprise/sst-env.d.ts @@ -132,6 +132,7 @@ declare module "sst" { "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service "ZenData": cloudflare.R2Bucket + "ZenDataNew": cloudflare.R2Bucket } } diff --git a/packages/extensions/zed/extension.toml b/packages/extensions/zed/extension.toml index 97008b6c3..7e415b51f 100644 --- a/packages/extensions/zed/extension.toml +++ b/packages/extensions/zed/extension.toml @@ -1,7 +1,7 @@ id = "opencode" name = "OpenCode" description = "The open source coding agent." -version = "1.0.170" +version = "1.0.191" 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.170/opencode-darwin-arm64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.191/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.170/opencode-darwin-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.191/opencode-darwin-x64.zip" cmd = "./opencode" args = ["acp"] [agent_servers.opencode.targets.linux-aarch64] -archive = "https://github.com/sst/opencode/releases/download/v1.0.170/opencode-linux-arm64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.191/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.170/opencode-linux-x64.tar.gz" +archive = "https://github.com/sst/opencode/releases/download/v1.0.191/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.170/opencode-windows-x64.zip" +archive = "https://github.com/sst/opencode/releases/download/v1.0.191/opencode-windows-x64.zip" cmd = "./opencode.exe" args = ["acp"] diff --git a/packages/function/package.json b/packages/function/package.json index b547e6492..83de02f9d 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.170", + "version": "1.0.191", "$schema": "https://json.schemastore.org/package.json", "private": true, "type": "module", diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 632ea3fbe..ffa17f276 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -132,6 +132,7 @@ declare module "sst" { "GatewayKv": cloudflare.KVNamespace "LogProcessor": cloudflare.Service "ZenData": cloudflare.R2Bucket + "ZenDataNew": cloudflare.R2Bucket } } diff --git a/packages/opencode/package.json b/packages/opencode/package.json index 02a2530b4..59307256c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,13 +1,13 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.170", + "version": "1.0.191", "name": "opencode", "type": "module", "private": true, "scripts": { "typecheck": "tsgo --noEmit", "test": "bun test", - "build": "./script/build.ts", + "build": "bun run script/build.ts", "dev": "bun run --conditions=browser ./src/index.ts", "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", @@ -55,10 +55,12 @@ "@ai-sdk/google": "2.0.44", "@ai-sdk/google-vertex": "3.0.81", "@ai-sdk/mcp": "0.0.8", + "@ai-sdk/mistral": "2.0.26", "@ai-sdk/openai": "2.0.71", "@ai-sdk/openai-compatible": "1.0.27", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.18", + "@ai-sdk/xai": "2.0.42", "@clack/prompts": "1.0.0-alpha.1", "@hono/standard-validator": "0.1.5", "@hono/zod-validator": "catalog:", @@ -71,8 +73,8 @@ "@opencode-ai/sdk": "workspace:*", "@opencode-ai/util": "workspace:*", "@openrouter/ai-sdk-provider": "1.5.2", - "@opentui/core": "0.1.61", - "@opentui/solid": "0.1.61", + "@opentui/core": "0.1.63", + "@opentui/solid": "0.1.63", "@parcel/watcher": "2.5.1", "@pierre/diffs": "catalog:", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/opencode/script/build.ts b/packages/opencode/script/build.ts index a85fde9e2..f51cb2924 100755 --- a/packages/opencode/script/build.ts +++ b/packages/opencode/script/build.ts @@ -16,6 +16,7 @@ import pkg from "../package.json" import { Script } from "@opencode-ai/script" const singleFlag = process.argv.includes("--single") +const baselineFlag = process.argv.includes("--baseline") const skipInstall = process.argv.includes("--skip-install") const allTargets: { @@ -78,7 +79,19 @@ const allTargets: { ] const targets = singleFlag - ? allTargets.filter((item) => item.os === process.platform && item.arch === process.arch) + ? allTargets.filter((item) => { + if (item.os !== process.platform || item.arch !== process.arch) { + return false + } + + // When building for the current platform, prefer a single native binary by default. + // Baseline binaries require additional Bun artifacts and can be flaky to download. + if (item.avx2 === false) { + return baselineFlag + } + + return true + }) : allTargets await $`rm -rf dist` diff --git a/packages/opencode/script/publish-registries.ts b/packages/opencode/script/publish-registries.ts new file mode 100644 index 000000000..85d87bd68 --- /dev/null +++ b/packages/opencode/script/publish-registries.ts @@ -0,0 +1,187 @@ +#!/usr/bin/env bun +import { $ } from "bun" +import { Script } from "@opencode-ai/script" + +if (!Script.preview) { + // Calculate SHA values + const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) + const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) + const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) + + const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) + + // arch + const binaryPkgbuild = [ + "# Maintainer: dax", + "# Maintainer: adam", + "", + "pkgname='opencode-bin'", + `pkgver=${pkgver}`, + `_subver=${_subver}`, + "options=('!debug' '!strip')", + "pkgrel=1", + "pkgdesc='The AI coding agent built for the terminal.'", + "url='https://github.com/sst/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode')", + "depends=('ripgrep')", + "", + `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`, + `sha256sums_aarch64=('${arm64Sha}')`, + + `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`, + `sha256sums_x86_64=('${x64Sha}')`, + "", + "package() {", + ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', + "}", + "", + ].join("\n") + + // Source-based PKGBUILD for opencode + const sourcePkgbuild = [ + "# Maintainer: dax", + "# Maintainer: adam", + "", + "pkgname='opencode'", + `pkgver=${pkgver}`, + `_subver=${_subver}`, + "options=('!debug' '!strip')", + "pkgrel=1", + "pkgdesc='The AI coding agent built for the terminal.'", + "url='https://github.com/sst/opencode'", + "arch=('aarch64' 'x86_64')", + "license=('MIT')", + "provides=('opencode')", + "conflicts=('opencode-bin')", + "depends=('ripgrep')", + "makedepends=('git' 'bun-bin' 'go')", + "", + `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`, + `sha256sums=('SKIP')`, + "", + "build() {", + ` cd "opencode-\${pkgver}"`, + ` bun install`, + " cd ./packages/opencode", + ` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`, + "}", + "", + "package() {", + ` cd "opencode-\${pkgver}/packages/opencode"`, + ' mkdir -p "${pkgdir}/usr/bin"', + ' target_arch="x64"', + ' case "$CARCH" in', + ' x86_64) target_arch="x64" ;;', + ' aarch64) target_arch="arm64" ;;', + ' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;', + " esac", + ' libc=""', + " if command -v ldd >/dev/null 2>&1; then", + " if ldd --version 2>&1 | grep -qi musl; then", + ' libc="-musl"', + " fi", + " fi", + ' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then', + ' libc="-musl"', + " fi", + ' base=""', + ' if [ "$target_arch" = "x64" ]; then', + " if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then", + ' base="-baseline"', + " fi", + " fi", + ' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"', + ' if [ ! -f "$bin" ]; then', + ' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2', + " return 1", + " fi", + ' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"', + "}", + "", + ].join("\n") + + for (const [pkg, pkgbuild] of [ + ["opencode-bin", binaryPkgbuild], + ["opencode", sourcePkgbuild], + ]) { + for (let i = 0; i < 30; i++) { + try { + await $`rm -rf ./dist/aur-${pkg}` + await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` + await $`cd ./dist/aur-${pkg} && git checkout master` + await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild) + await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` + await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` + await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"` + await $`cd ./dist/aur-${pkg} && git push` + break + } catch (e) { + continue + } + } + } + + // Homebrew formula + const homebrewFormula = [ + "# typed: false", + "# frozen_string_literal: true", + "", + "# This file was generated by GoReleaser. DO NOT EDIT.", + "class Opencode < Formula", + ` desc "The AI coding agent built for the terminal."`, + ` homepage "https://github.com/sst/opencode"`, + ` version "${Script.version.split("-")[0]}"`, + "", + ` depends_on "ripgrep"`, + "", + " on_macos do", + " if Hardware::CPU.intel?", + ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`, + ` sha256 "${macX64Sha}"`, + "", + " def install", + ' bin.install "opencode"', + " end", + " end", + " if Hardware::CPU.arm?", + ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`, + ` sha256 "${macArm64Sha}"`, + "", + " def install", + ' bin.install "opencode"', + " end", + " end", + " end", + "", + " on_linux do", + " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", + ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`, + ` sha256 "${x64Sha}"`, + " def install", + ' bin.install "opencode"', + " end", + " end", + " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", + ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`, + ` sha256 "${arm64Sha}"`, + " def install", + ' bin.install "opencode"', + " end", + " end", + " end", + "end", + "", + "", + ].join("\n") + + await $`rm -rf ./dist/homebrew-tap` + await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap` + await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula) + await $`cd ./dist/homebrew-tap && git add opencode.rb` + await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"` + await $`cd ./dist/homebrew-tap && git push` +} diff --git a/packages/opencode/script/publish.ts b/packages/opencode/script/publish.ts index 72632992f..f4c4c2db9 100755 --- a/packages/opencode/script/publish.ts +++ b/packages/opencode/script/publish.ts @@ -40,7 +40,7 @@ const tags = [Script.channel] const tasks = Object.entries(binaries).map(async ([name]) => { if (process.platform !== "win32") { - await $`chmod 755 -R .`.cwd(`./dist/${name}`) + await $`chmod -R 755 .`.cwd(`./dist/${name}`) } await $`bun pm pack`.cwd(`./dist/${name}`) for (const tag of tags) { @@ -53,196 +53,15 @@ for (const tag of tags) { } if (!Script.preview) { + // Create archives for GitHub release for (const key of Object.keys(binaries)) { if (key.includes("linux")) { - await $`cd dist/${key}/bin && tar -czf ../../${key}.tar.gz *` + await $`tar -czf ../../${key}.tar.gz *`.cwd(`dist/${key}/bin`) } else { - await $`cd dist/${key}/bin && zip -r ../../${key}.zip *` + await $`zip -r ../../${key}.zip *`.cwd(`dist/${key}/bin`) } } - // Calculate SHA values - const arm64Sha = await $`sha256sum ./dist/opencode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) - const x64Sha = await $`sha256sum ./dist/opencode-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim()) - const macX64Sha = await $`sha256sum ./dist/opencode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - const macArm64Sha = await $`sha256sum ./dist/opencode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim()) - - const [pkgver, _subver = ""] = Script.version.split(/(-.*)/, 2) - - // arch - const binaryPkgbuild = [ - "# Maintainer: dax", - "# Maintainer: adam", - "", - "pkgname='opencode-bin'", - `pkgver=${pkgver}`, - `_subver=${_subver}`, - "options=('!debug' '!strip')", - "pkgrel=1", - "pkgdesc='The AI coding agent built for the terminal.'", - "url='https://github.com/sst/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode')", - "depends=('ripgrep')", - "", - `source_aarch64=("\${pkgname}_\${pkgver}_aarch64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-arm64.tar.gz")`, - `sha256sums_aarch64=('${arm64Sha}')`, - - `source_x86_64=("\${pkgname}_\${pkgver}_x86_64.tar.gz::https://github.com/sst/opencode/releases/download/v\${pkgver}\${_subver}/opencode-linux-x64.tar.gz")`, - `sha256sums_x86_64=('${x64Sha}')`, - "", - "package() {", - ' install -Dm755 ./opencode "${pkgdir}/usr/bin/opencode"', - "}", - "", - ].join("\n") - - // Source-based PKGBUILD for opencode - const sourcePkgbuild = [ - "# Maintainer: dax", - "# Maintainer: adam", - "", - "pkgname='opencode'", - `pkgver=${pkgver}`, - `_subver=${_subver}`, - "options=('!debug' '!strip')", - "pkgrel=1", - "pkgdesc='The AI coding agent built for the terminal.'", - "url='https://github.com/sst/opencode'", - "arch=('aarch64' 'x86_64')", - "license=('MIT')", - "provides=('opencode')", - "conflicts=('opencode-bin')", - "depends=('ripgrep')", - "makedepends=('git' 'bun-bin' 'go')", - "", - `source=("opencode-\${pkgver}.tar.gz::https://github.com/sst/opencode/archive/v\${pkgver}\${_subver}.tar.gz")`, - `sha256sums=('SKIP')`, - "", - "build() {", - ` cd "opencode-\${pkgver}"`, - ` bun install`, - " cd ./packages/opencode", - ` OPENCODE_CHANNEL=latest OPENCODE_VERSION=${pkgver} bun run ./script/build.ts --single`, - "}", - "", - "package() {", - ` cd "opencode-\${pkgver}/packages/opencode"`, - ' mkdir -p "${pkgdir}/usr/bin"', - ' target_arch="x64"', - ' case "$CARCH" in', - ' x86_64) target_arch="x64" ;;', - ' aarch64) target_arch="arm64" ;;', - ' *) printf "unsupported architecture: %s\\n" "$CARCH" >&2 ; return 1 ;;', - " esac", - ' libc=""', - " if command -v ldd >/dev/null 2>&1; then", - " if ldd --version 2>&1 | grep -qi musl; then", - ' libc="-musl"', - " fi", - " fi", - ' if [ -z "$libc" ] && ls /lib/ld-musl-* >/dev/null 2>&1; then', - ' libc="-musl"', - " fi", - ' base=""', - ' if [ "$target_arch" = "x64" ]; then', - " if ! grep -qi avx2 /proc/cpuinfo 2>/dev/null; then", - ' base="-baseline"', - " fi", - " fi", - ' bin="dist/opencode-linux-${target_arch}${base}${libc}/bin/opencode"', - ' if [ ! -f "$bin" ]; then', - ' printf "unable to find binary for %s%s%s\\n" "$target_arch" "$base" "$libc" >&2', - " return 1", - " fi", - ' install -Dm755 "$bin" "${pkgdir}/usr/bin/opencode"', - "}", - "", - ].join("\n") - - for (const [pkg, pkgbuild] of [ - ["opencode-bin", binaryPkgbuild], - ["opencode", sourcePkgbuild], - ]) { - for (let i = 0; i < 30; i++) { - try { - await $`rm -rf ./dist/aur-${pkg}` - await $`git clone ssh://aur@aur.archlinux.org/${pkg}.git ./dist/aur-${pkg}` - await $`cd ./dist/aur-${pkg} && git checkout master` - await Bun.file(`./dist/aur-${pkg}/PKGBUILD`).write(pkgbuild) - await $`cd ./dist/aur-${pkg} && makepkg --printsrcinfo > .SRCINFO` - await $`cd ./dist/aur-${pkg} && git add PKGBUILD .SRCINFO` - await $`cd ./dist/aur-${pkg} && git commit -m "Update to v${Script.version}"` - await $`cd ./dist/aur-${pkg} && git push` - break - } catch (e) { - continue - } - } - } - - // Homebrew formula - const homebrewFormula = [ - "# typed: false", - "# frozen_string_literal: true", - "", - "# This file was generated by GoReleaser. DO NOT EDIT.", - "class Opencode < Formula", - ` desc "The AI coding agent built for the terminal."`, - ` homepage "https://github.com/sst/opencode"`, - ` version "${Script.version.split("-")[0]}"`, - "", - ` depends_on "ripgrep"`, - "", - " on_macos do", - " if Hardware::CPU.intel?", - ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-x64.zip"`, - ` sha256 "${macX64Sha}"`, - "", - " def install", - ' bin.install "opencode"', - " end", - " end", - " if Hardware::CPU.arm?", - ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-darwin-arm64.zip"`, - ` sha256 "${macArm64Sha}"`, - "", - " def install", - ' bin.install "opencode"', - " end", - " end", - " end", - "", - " on_linux do", - " if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?", - ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-x64.tar.gz"`, - ` sha256 "${x64Sha}"`, - " def install", - ' bin.install "opencode"', - " end", - " end", - " if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?", - ` url "https://github.com/sst/opencode/releases/download/v${Script.version}/opencode-linux-arm64.tar.gz"`, - ` sha256 "${arm64Sha}"`, - " def install", - ' bin.install "opencode"', - " end", - " end", - " end", - "end", - "", - "", - ].join("\n") - - await $`rm -rf ./dist/homebrew-tap` - await $`git clone https://${process.env["GITHUB_TOKEN"]}@github.com/sst/homebrew-tap.git ./dist/homebrew-tap` - await Bun.file("./dist/homebrew-tap/opencode.rb").write(homebrewFormula) - await $`cd ./dist/homebrew-tap && git add opencode.rb` - await $`cd ./dist/homebrew-tap && git commit -m "Update to v${Script.version}"` - await $`cd ./dist/homebrew-tap && git push` - const image = "ghcr.io/sst/opencode" const platforms = "linux/amd64,linux/arm64" const tags = [`${image}:${Script.version}`, `${image}:latest`] diff --git a/packages/opencode/src/acp/agent.ts b/packages/opencode/src/acp/agent.ts index d20c971eb..e6419dd76 100644 --- a/packages/opencode/src/acp/agent.ts +++ b/packages/opencode/src/acp/agent.ts @@ -22,6 +22,7 @@ import { Log } from "../util/log" import { ACPSessionManager } from "./session" import type { ACPConfig, ACPSessionState } from "./types" import { Provider } from "../provider/provider" +import { Agent as AgentModule } from "../agent/agent" import { Installation } from "@/installation" import { MessageV2 } from "@/session/message-v2" import { Config } from "@/config/config" @@ -698,14 +699,15 @@ export namespace ACP { }) const availableModes = agents - .filter((agent) => agent.mode !== "subagent") + .filter((agent) => agent.mode !== "subagent" && !agent.hidden) .map((agent) => ({ id: agent.name, name: agent.name, description: agent.description, })) - const currentModeId = availableModes.find((m) => m.name === "build")?.id ?? availableModes[0].id + const defaultAgentName = await AgentModule.defaultAgent() + const currentModeId = availableModes.find((m) => m.name === defaultAgentName)?.id ?? availableModes[0].id const mcpServers: Record = {} for (const server of params.mcpServers) { @@ -807,7 +809,7 @@ export namespace ACP { if (!current) { this.sessionManager.setModel(session.id, model) } - const agent = session.modeId ?? "build" + const agent = session.modeId ?? (await AgentModule.defaultAgent()) const parts: Array< { type: "text"; text: string } | { type: "file"; url: string; filename: string; mime: string } diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index add120f91..ad665e5d6 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -5,6 +5,9 @@ import { generateObject, type ModelMessage } from "ai" import { SystemPrompt } from "../session/system" import { Instance } from "../project/instance" import { mergeDeep } from "remeda" +import { Log } from "../util/log" + +const log = Log.create({ service: "agent" }) import PROMPT_GENERATE from "./generate.txt" import PROMPT_COMPACTION from "./prompt/compaction.txt" @@ -20,12 +23,14 @@ export namespace Agent { mode: z.enum(["subagent", "primary", "all"]), native: z.boolean().optional(), hidden: z.boolean().optional(), + default: z.boolean().optional(), topP: z.number().optional(), temperature: z.number().optional(), color: z.string().optional(), permission: z.object({ edit: Config.Permission, bash: z.record(z.string(), Config.Permission), + skill: z.record(z.string(), Config.Permission), webfetch: Config.Permission.optional(), doom_loop: Config.Permission.optional(), external_directory: Config.Permission.optional(), @@ -54,6 +59,9 @@ export namespace Agent { bash: { "*": "allow", }, + skill: { + "*": "allow", + }, webfetch: "allow", doom_loop: "ask", external_directory: "ask", @@ -245,6 +253,27 @@ export namespace Agent { item.permission = mergeAgentPermissions(cfg.permission ?? {}, permission ?? {}) } } + + // Mark the default agent + const defaultName = cfg.default_agent ?? "build" + const defaultCandidate = result[defaultName] + if (defaultCandidate && defaultCandidate.mode !== "subagent") { + defaultCandidate.default = true + } else { + // Fall back to "build" if configured default is invalid + if (result["build"]) { + result["build"].default = true + } + } + + const hasPrimaryAgents = Object.values(result).filter((a) => a.mode !== "subagent" && !a.hidden).length > 0 + if (!hasPrimaryAgents) { + throw new Config.InvalidError({ + path: "config", + message: "No primary agents are available. Please configure at least one agent with mode 'primary' or 'all'.", + }) + } + return result }) @@ -256,6 +285,12 @@ export namespace Agent { return state().then((x) => Object.values(x)) } + export async function defaultAgent(): Promise { + const agents = await state() + const defaultCandidate = Object.values(agents).find((a) => a.default) + return defaultCandidate?.name ?? "build" + } + export async function generate(input: { description: string; model?: { providerID: string; modelID: string } }) { const cfg = await Config.get() const defaultModel = input.model ?? (await Provider.defaultModel()) @@ -306,6 +341,17 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag "*": overridePermission.bash, } } + + if (typeof basePermission.skill === "string") { + basePermission.skill = { + "*": basePermission.skill, + } + } + if (typeof overridePermission.skill === "string") { + overridePermission.skill = { + "*": overridePermission.skill, + } + } const merged = mergeDeep(basePermission ?? {}, overridePermission ?? {}) as any let mergedBash if (merged.bash) { @@ -323,10 +369,27 @@ function mergeAgentPermissions(basePermission: any, overridePermission: any): Ag } } + let mergedSkill + if (merged.skill) { + if (typeof merged.skill === "string") { + mergedSkill = { + "*": merged.skill, + } + } else if (typeof merged.skill === "object") { + mergedSkill = mergeDeep( + { + "*": "allow", + }, + merged.skill, + ) + } + } + const result: Agent.Info["permission"] = { edit: merged.edit ?? "allow", webfetch: merged.webfetch ?? "allow", bash: mergedBash ?? { "*": "allow" }, + skill: mergedSkill ?? { "*": "allow" }, doom_loop: merged.doom_loop, external_directory: merged.external_directory, } diff --git a/packages/opencode/src/agent/prompt/summary.txt b/packages/opencode/src/agent/prompt/summary.txt index 6c11638db..c9264db18 100644 --- a/packages/opencode/src/agent/prompt/summary.txt +++ b/packages/opencode/src/agent/prompt/summary.txt @@ -1,4 +1,10 @@ -Summarize the following conversation into 2 sentences MAX explaining what the -assistant did and why -Do not explain the user's input. -Do not speak in the third person about the assistant. +Summarize what was done in this conversation. Write like a pull request description. + +Rules: +- 2-3 sentences max +- Describe the changes made, not the process +- Do not mention running tests, builds, or other validation steps +- Do not explain what the user asked for +- Write in first person (I added..., I fixed...) +- Never ask questions or add new questions +- Only exception: if the conversation ends with an unanswered question to the user, preserve that exact question diff --git a/packages/opencode/src/cli/cmd/debug/index.ts b/packages/opencode/src/cli/cmd/debug/index.ts index 172987875..3b0aefa28 100644 --- a/packages/opencode/src/cli/cmd/debug/index.ts +++ b/packages/opencode/src/cli/cmd/debug/index.ts @@ -6,6 +6,7 @@ import { FileCommand } from "./file" import { LSPCommand } from "./lsp" import { RipgrepCommand } from "./ripgrep" import { ScrapCommand } from "./scrap" +import { SkillCommand } from "./skill" import { SnapshotCommand } from "./snapshot" export const DebugCommand = cmd({ @@ -17,6 +18,7 @@ export const DebugCommand = cmd({ .command(RipgrepCommand) .command(FileCommand) .command(ScrapCommand) + .command(SkillCommand) .command(SnapshotCommand) .command(PathsCommand) .command({ diff --git a/packages/opencode/src/cli/cmd/debug/skill.ts b/packages/opencode/src/cli/cmd/debug/skill.ts new file mode 100644 index 000000000..8079b688e --- /dev/null +++ b/packages/opencode/src/cli/cmd/debug/skill.ts @@ -0,0 +1,15 @@ +import { EOL } from "os" +import { Skill } from "../../../skill" +import { bootstrap } from "../../bootstrap" +import { cmd } from "../cmd" + +export const SkillCommand = cmd({ + command: "skill", + builder: (yargs) => yargs, + async handler() { + await bootstrap(process.cwd(), async () => { + const skills = await Skill.all() + process.stdout.write(JSON.stringify(skills, null, 2) + EOL) + }) + }, +}) diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index f4f026d4c..607fc7caf 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -7,7 +7,7 @@ import { graphql } from "@octokit/graphql" import * as core from "@actions/core" import * as github from "@actions/github" import type { Context } from "@actions/github/lib/context" -import type { IssueCommentEvent, PullRequestReviewCommentEvent } from "@octokit/webhooks-types" +import type { IssueCommentEvent, PullRequestReviewCommentEvent, PullRequestEvent } from "@octokit/webhooks-types" import { UI } from "../ui" import { cmd } from "./cmd" import { ModelsDev } from "../../provider/models" @@ -127,6 +127,7 @@ type IssueQueryResponse = { const AGENT_USERNAME = "opencode-agent[bot]" const AGENT_REACTION = "eyes" const WORKFLOW_FILE = ".github/workflows/opencode.yml" +const SUPPORTED_EVENTS = ["issue_comment", "pull_request_review_comment", "schedule", "pull_request"] as const // Parses GitHub remote URLs in various formats: // - https://github.com/owner/repo.git @@ -387,24 +388,30 @@ export const GithubRunCommand = cmd({ const isMock = args.token || args.event const context = isMock ? (JSON.parse(args.event!) as Context) : github.context - if (context.eventName !== "issue_comment" && context.eventName !== "pull_request_review_comment") { + if (!SUPPORTED_EVENTS.includes(context.eventName as (typeof SUPPORTED_EVENTS)[number])) { core.setFailed(`Unsupported event type: ${context.eventName}`) process.exit(1) } + const isCommentEvent = ["issue_comment", "pull_request_review_comment"].includes(context.eventName) + const isScheduleEvent = context.eventName === "schedule" const { providerID, modelID } = normalizeModel() const runId = normalizeRunId() const share = normalizeShare() const oidcBaseUrl = normalizeOidcBaseUrl() const { owner, repo } = context.repo - const payload = context.payload as IssueCommentEvent | PullRequestReviewCommentEvent - const issueEvent = isIssueCommentEvent(payload) ? payload : undefined - const actor = context.actor + // For schedule events, payload has no issue/comment data + const payload = isCommentEvent + ? (context.payload as IssueCommentEvent | PullRequestReviewCommentEvent) + : undefined + const issueEvent = payload && isIssueCommentEvent(payload) ? payload : undefined + const actor = isScheduleEvent ? undefined : context.actor - const issueId = - context.eventName === "pull_request_review_comment" - ? (payload as PullRequestReviewCommentEvent).pull_request.number - : (payload as IssueCommentEvent).issue.number + const issueId = isScheduleEvent + ? undefined + : context.eventName === "issue_comment" + ? (payload as IssueCommentEvent).issue.number + : (payload as PullRequestEvent | PullRequestReviewCommentEvent).pull_request.number const runUrl = `/${owner}/${repo}/actions/runs/${runId}` const shareBaseUrl = isMock ? "https://dev.opencode.ai" : "https://opencode.ai" @@ -416,9 +423,13 @@ export const GithubRunCommand = cmd({ let shareId: string | undefined let exitCode = 0 type PromptFiles = Awaited>["promptFiles"] - const triggerCommentId = payload.comment.id + const triggerCommentId = payload?.comment.id const useGithubToken = normalizeUseGithubToken() - const commentType = context.eventName === "pull_request_review_comment" ? "pr_review" : "issue" + const commentType = isCommentEvent + ? context.eventName === "pull_request_review_comment" + ? "pr_review" + : "issue" + : undefined try { if (useGithubToken) { @@ -442,9 +453,11 @@ export const GithubRunCommand = cmd({ if (!useGithubToken) { await configureGit(appToken) } - await assertPermissions() - - await addReaction(commentType) + // Skip permission check for schedule events (no actor to check) + if (!isScheduleEvent) { + await assertPermissions() + await addReaction(commentType) + } // Setup opencode session const repoData = await fetchRepo() @@ -458,11 +471,34 @@ export const GithubRunCommand = cmd({ })() console.log("opencode session", session.id) - // Handle 3 cases - // 1. Issue - // 2. Local PR - // 3. Fork PR - if (context.eventName === "pull_request_review_comment" || issueEvent?.issue.pull_request) { + // Handle 4 cases + // 1. Schedule (no issue/PR context) + // 2. Issue + // 3. Local PR + // 4. Fork PR + if (isScheduleEvent) { + // Schedule event - no issue/PR context, output goes to logs + const branch = await checkoutNewBranch("schedule") + const head = (await $`git rev-parse HEAD`).stdout.toString().trim() + const response = await chat(userPrompt, promptFiles) + const { dirty, uncommittedChanges } = await branchIsDirty(head) + if (dirty) { + const summary = await summarize(response) + await pushToNewBranch(summary, branch, uncommittedChanges, true) + const pr = await createPR( + repoData.data.default_branch, + branch, + summary, + `${response}\n\nTriggered by scheduled workflow${footer({ image: true })}`, + ) + console.log(`Created PR #${pr}`) + } else { + console.log("Response:", response) + } + } else if ( + ["pull_request", "pull_request_review_comment"].includes(context.eventName) || + issueEvent?.issue.pull_request + ) { const prData = await fetchPR() // Local PR if (prData.headRepository.nameWithOwner === prData.baseRepository.nameWithOwner) { @@ -497,7 +533,7 @@ export const GithubRunCommand = cmd({ } // Issue else { - const branch = await checkoutNewBranch() + const branch = await checkoutNewBranch("issue") const head = (await $`git rev-parse HEAD`).stdout.toString().trim() const issueData = await fetchIssue() const dataPrompt = buildPromptDataForIssue(issueData) @@ -505,7 +541,7 @@ export const GithubRunCommand = cmd({ const { dirty, uncommittedChanges } = await branchIsDirty(head) if (dirty) { const summary = await summarize(response) - await pushToNewBranch(summary, branch, uncommittedChanges) + await pushToNewBranch(summary, branch, uncommittedChanges, false) const pr = await createPR( repoData.data.default_branch, branch, @@ -528,8 +564,10 @@ export const GithubRunCommand = cmd({ } else if (e instanceof Error) { msg = e.message } - await createComment(`${msg}${footer()}`) - await removeReaction(commentType) + if (!isScheduleEvent) { + await createComment(`${msg}${footer()}`) + await removeReaction(commentType) + } core.setFailed(msg) // Also output the clean error message for the action to capture //core.setOutput("prepare_error", e.message); @@ -605,6 +643,14 @@ export const GithubRunCommand = cmd({ async function getUserPrompt() { const customPrompt = process.env["PROMPT"] + // For schedule events, PROMPT is required since there's no comment to extract from + if (isScheduleEvent) { + if (!customPrompt) { + throw new Error("PROMPT input is required for scheduled events") + } + return { userPrompt: customPrompt, promptFiles: [] } + } + if (customPrompt) { return { userPrompt: customPrompt, promptFiles: [] } } @@ -615,7 +661,10 @@ export const GithubRunCommand = cmd({ .map((m) => m.trim().toLowerCase()) .filter(Boolean) let prompt = (() => { - const body = payload.comment.body.trim() + if (!isCommentEvent) { + return "Review this pull request" + } + const body = payload!.comment.body.trim() const bodyLower = body.toLowerCase() if (mentions.some((m) => bodyLower === m)) { if (reviewContext) { @@ -762,7 +811,7 @@ export const GithubRunCommand = cmd({ providerID, modelID, }, - agent: "build", + // agent is omitted - server will use default_agent from config or fall back to "build" parts: [ { id: Identifier.ascending("part"), @@ -865,9 +914,9 @@ export const GithubRunCommand = cmd({ await $`git config --local ${config} "${gitConfig}"` } - async function checkoutNewBranch() { + async function checkoutNewBranch(type: "issue" | "schedule") { console.log("Checking out new branch...") - const branch = generateBranchName("issue") + const branch = generateBranchName(type) await $`git checkout -b ${branch}` return branch } @@ -894,23 +943,32 @@ export const GithubRunCommand = cmd({ await $`git checkout -b ${localBranch} fork/${remoteBranch}` } - function generateBranchName(type: "issue" | "pr") { + function generateBranchName(type: "issue" | "pr" | "schedule") { const timestamp = new Date() .toISOString() .replace(/[:-]/g, "") .replace(/\.\d{3}Z/, "") .split("T") .join("") + if (type === "schedule") { + const hex = crypto.randomUUID().slice(0, 6) + return `opencode/scheduled-${hex}-${timestamp}` + } return `opencode/${type}${issueId}-${timestamp}` } - async function pushToNewBranch(summary: string, branch: string, commit: boolean) { + async function pushToNewBranch(summary: string, branch: string, commit: boolean, isSchedule: boolean) { console.log("Pushing to new branch...") if (commit) { await $`git add .` - await $`git commit -m "${summary} + if (isSchedule) { + // No co-author for scheduled events - the schedule is operating as the repo + await $`git commit -m "${summary}"` + } else { + await $`git commit -m "${summary} Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` + } } await $`git push -u origin ${branch}` } @@ -958,6 +1016,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` } async function assertPermissions() { + // Only called for non-schedule events, so actor is defined console.log(`Asserting permissions for user ${actor}...`) let permission @@ -965,7 +1024,7 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` const response = await octoRest.repos.getCollaboratorPermissionLevel({ owner, repo, - username: actor, + username: actor!, }) permission = response.data.permission @@ -978,70 +1037,99 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`) } - async function addReaction(commentType: "issue" | "pr_review") { + async function addReaction(commentType?: "issue" | "pr_review") { + // Only called for non-schedule events, so triggerCommentId is defined console.log("Adding reaction...") - if (commentType === "pr_review") { - return await octoRest.rest.reactions.createForPullRequestReviewComment({ + if (triggerCommentId) { + if (commentType === "pr_review") { + return await octoRest.rest.reactions.createForPullRequestReviewComment({ + owner, + repo, + comment_id: triggerCommentId!, + content: AGENT_REACTION, + }) + } + return await octoRest.rest.reactions.createForIssueComment({ owner, repo, - comment_id: triggerCommentId, + comment_id: triggerCommentId!, content: AGENT_REACTION, }) } - return await octoRest.rest.reactions.createForIssueComment({ + return await octoRest.rest.reactions.createForIssue({ owner, repo, - comment_id: triggerCommentId, + issue_number: issueId!, content: AGENT_REACTION, }) } - async function removeReaction(commentType: "issue" | "pr_review") { + async function removeReaction(commentType?: "issue" | "pr_review") { + // Only called for non-schedule events, so triggerCommentId is defined console.log("Removing reaction...") - if (commentType === "pr_review") { - const reactions = await octoRest.rest.reactions.listForPullRequestReviewComment({ + if (triggerCommentId) { + if (commentType === "pr_review") { + const reactions = await octoRest.rest.reactions.listForPullRequestReviewComment({ + owner, + repo, + comment_id: triggerCommentId!, + content: AGENT_REACTION, + }) + + const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) + if (!eyesReaction) return + + return await octoRest.rest.reactions.deleteForPullRequestComment({ + owner, + repo, + comment_id: triggerCommentId!, + reaction_id: eyesReaction.id, + }) + } + + const reactions = await octoRest.rest.reactions.listForIssueComment({ owner, repo, - comment_id: triggerCommentId, + comment_id: triggerCommentId!, content: AGENT_REACTION, }) const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) if (!eyesReaction) return - await octoRest.rest.reactions.deleteForPullRequestComment({ + return await octoRest.rest.reactions.deleteForIssueComment({ owner, repo, - comment_id: triggerCommentId, + comment_id: triggerCommentId!, reaction_id: eyesReaction.id, }) - return } - const reactions = await octoRest.rest.reactions.listForIssueComment({ + const reactions = await octoRest.rest.reactions.listForIssue({ owner, repo, - comment_id: triggerCommentId, + issue_number: issueId!, content: AGENT_REACTION, }) const eyesReaction = reactions.data.find((r) => r.user?.login === AGENT_USERNAME) if (!eyesReaction) return - await octoRest.rest.reactions.deleteForIssueComment({ + await octoRest.rest.reactions.deleteForIssue({ owner, repo, - comment_id: triggerCommentId, + issue_number: issueId!, reaction_id: eyesReaction.id, }) } async function createComment(body: string) { + // Only called for non-schedule events, so issueId is defined console.log("Creating comment...") return await octoRest.rest.issues.createComment({ owner, repo, - issue_number: issueId, + issue_number: issueId!, body, }) } @@ -1119,10 +1207,11 @@ query($owner: String!, $repo: String!, $number: Int!) { } function buildPromptDataForIssue(issue: GitHubIssue) { + // Only called for non-schedule events, so payload is defined const comments = (issue.comments?.nodes || []) .filter((c) => { const id = parseInt(c.databaseId) - return id !== payload.comment.id + return id !== triggerCommentId }) .map((c) => ` - ${c.author.login} at ${c.createdAt}: ${c.body}`) @@ -1246,10 +1335,11 @@ query($owner: String!, $repo: String!, $number: Int!) { } function buildPromptDataForPR(pr: GitHubPullRequest) { + // Only called for non-schedule events, so payload is defined const comments = (pr.comments?.nodes || []) .filter((c) => { const id = parseInt(c.databaseId) - return id !== payload.comment.id + return id !== triggerCommentId }) .map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`) diff --git a/packages/opencode/src/cli/cmd/mcp.ts b/packages/opencode/src/cli/cmd/mcp.ts index 9ca4b3bff..b4ae8a37f 100644 --- a/packages/opencode/src/cli/cmd/mcp.ts +++ b/packages/opencode/src/cli/cmd/mcp.ts @@ -1,16 +1,41 @@ import { cmd } from "./cmd" import { Client } from "@modelcontextprotocol/sdk/client/index.js" import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js" +import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" +import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" import * as prompts from "@clack/prompts" import { UI } from "../ui" import { MCP } from "../../mcp" import { McpAuth } from "../../mcp/auth" +import { McpOAuthProvider } from "../../mcp/oauth-provider" import { Config } from "../../config/config" import { Instance } from "../../project/instance" +import { Installation } from "../../installation" import path from "path" -import os from "os" import { Global } from "../../global" +function getAuthStatusIcon(status: MCP.AuthStatus): string { + switch (status) { + case "authenticated": + return "✓" + case "expired": + return "⚠" + case "not_authenticated": + return "○" + } +} + +function getAuthStatusText(status: MCP.AuthStatus): string { + switch (status) { + case "authenticated": + return "authenticated" + case "expired": + return "expired" + case "not_authenticated": + return "not authenticated" + } +} + export const McpCommand = cmd({ command: "mcp", builder: (yargs) => @@ -19,6 +44,7 @@ export const McpCommand = cmd({ .command(McpListCommand) .command(McpAuthCommand) .command(McpLogoutCommand) + .command(McpDebugCommand) .demandCommand(), async handler() {}, }) @@ -94,10 +120,12 @@ export const McpAuthCommand = cmd({ command: "auth [name]", describe: "authenticate with an OAuth-enabled MCP server", builder: (yargs) => - yargs.positional("name", { - describe: "name of the MCP server", - type: "string", - }), + yargs + .positional("name", { + describe: "name of the MCP server", + type: "string", + }) + .command(McpAuthListCommand), async handler(args) { await Instance.provide({ directory: process.cwd(), @@ -108,20 +136,19 @@ export const McpAuthCommand = cmd({ const config = await Config.get() const mcpServers = config.mcp ?? {} - // Get OAuth-enabled servers - const oauthServers = Object.entries(mcpServers).filter(([_, cfg]) => cfg.type === "remote" && !!cfg.oauth) + // Get OAuth-capable servers (remote servers with oauth not explicitly disabled) + const oauthServers = Object.entries(mcpServers).filter( + ([_, cfg]) => cfg.type === "remote" && cfg.oauth !== false, + ) if (oauthServers.length === 0) { - prompts.log.warn("No OAuth-enabled MCP servers configured") - prompts.log.info("Add OAuth config to a remote MCP server in opencode.json:") + prompts.log.warn("No OAuth-capable MCP servers configured") + prompts.log.info("Remote MCP servers support OAuth by default. Add a remote server in opencode.json:") prompts.log.info(` "mcp": { "my-server": { "type": "remote", - "url": "https://example.com/mcp", - "oauth": { - "scope": "tools:read" - } + "url": "https://example.com/mcp" } }`) prompts.outro("Done") @@ -130,13 +157,24 @@ export const McpAuthCommand = cmd({ let serverName = args.name if (!serverName) { + // Build options with auth status + const options = await Promise.all( + oauthServers.map(async ([name, cfg]) => { + const authStatus = await MCP.getAuthStatus(name) + const icon = getAuthStatusIcon(authStatus) + const statusText = getAuthStatusText(authStatus) + const url = cfg.type === "remote" ? cfg.url : "" + return { + label: `${icon} ${name} (${statusText})`, + value: name, + hint: url, + } + }), + ) + const selected = await prompts.select({ message: "Select MCP server to authenticate", - options: oauthServers.map(([name, cfg]) => ({ - label: name, - value: name, - hint: cfg.type === "remote" ? cfg.url : undefined, - })), + options, }) if (prompts.isCancel(selected)) throw new UI.CancelledError() serverName = selected @@ -149,22 +187,24 @@ export const McpAuthCommand = cmd({ return } - if (serverConfig.type !== "remote" || !serverConfig.oauth) { - prompts.log.error(`MCP server ${serverName} does not have OAuth configured`) + if (serverConfig.type !== "remote" || serverConfig.oauth === false) { + prompts.log.error(`MCP server ${serverName} does not support OAuth (oauth is disabled)`) prompts.outro("Done") return } // Check if already authenticated - const hasTokens = await MCP.hasStoredTokens(serverName) - if (hasTokens) { + const authStatus = await MCP.getAuthStatus(serverName) + if (authStatus === "authenticated") { const confirm = await prompts.confirm({ - message: `${serverName} already has stored credentials. Re-authenticate?`, + message: `${serverName} already has valid credentials. Re-authenticate?`, }) if (prompts.isCancel(confirm) || !confirm) { prompts.outro("Cancelled") return } + } else if (authStatus === "expired") { + prompts.log.warn(`${serverName} has expired credentials. Re-authenticating...`) } const spinner = prompts.spinner() @@ -207,6 +247,46 @@ export const McpAuthCommand = cmd({ }, }) +export const McpAuthListCommand = cmd({ + command: "list", + aliases: ["ls"], + describe: "list OAuth-capable MCP servers and their auth status", + async handler() { + await Instance.provide({ + directory: process.cwd(), + async fn() { + UI.empty() + prompts.intro("MCP OAuth Status") + + const config = await Config.get() + const mcpServers = config.mcp ?? {} + + // Get OAuth-capable servers + const oauthServers = Object.entries(mcpServers).filter( + ([_, cfg]) => cfg.type === "remote" && cfg.oauth !== false, + ) + + if (oauthServers.length === 0) { + prompts.log.warn("No OAuth-capable MCP servers configured") + prompts.outro("Done") + return + } + + for (const [name, serverConfig] of oauthServers) { + const authStatus = await MCP.getAuthStatus(name) + const icon = getAuthStatusIcon(authStatus) + const statusText = getAuthStatusText(authStatus) + const url = serverConfig.type === "remote" ? serverConfig.url : "" + + prompts.log.info(`${icon} ${name} ${UI.Style.TEXT_DIM}${statusText}\n ${UI.Style.TEXT_DIM}${url}`) + } + + prompts.outro(`${oauthServers.length} OAuth-capable server(s)`) + }, + }) + }, +}) + export const McpLogoutCommand = cmd({ command: "logout [name]", describe: "remove OAuth credentials for an MCP server", @@ -398,3 +478,177 @@ export const McpAddCommand = cmd({ prompts.outro("MCP server added successfully") }, }) + +export const McpDebugCommand = cmd({ + command: "debug ", + describe: "debug OAuth connection for an MCP server", + builder: (yargs) => + yargs.positional("name", { + describe: "name of the MCP server", + type: "string", + demandOption: true, + }), + async handler(args) { + await Instance.provide({ + directory: process.cwd(), + async fn() { + UI.empty() + prompts.intro("MCP OAuth Debug") + + const config = await Config.get() + const mcpServers = config.mcp ?? {} + const serverName = args.name + + const serverConfig = mcpServers[serverName] + if (!serverConfig) { + prompts.log.error(`MCP server not found: ${serverName}`) + prompts.outro("Done") + return + } + + if (serverConfig.type !== "remote") { + prompts.log.error(`MCP server ${serverName} is not a remote server`) + prompts.outro("Done") + return + } + + if (serverConfig.oauth === false) { + prompts.log.warn(`MCP server ${serverName} has OAuth explicitly disabled`) + prompts.outro("Done") + return + } + + prompts.log.info(`Server: ${serverName}`) + prompts.log.info(`URL: ${serverConfig.url}`) + + // Check stored auth status + const authStatus = await MCP.getAuthStatus(serverName) + prompts.log.info(`Auth status: ${getAuthStatusIcon(authStatus)} ${getAuthStatusText(authStatus)}`) + + const entry = await McpAuth.get(serverName) + if (entry?.tokens) { + prompts.log.info(` Access token: ${entry.tokens.accessToken.substring(0, 20)}...`) + if (entry.tokens.expiresAt) { + const expiresDate = new Date(entry.tokens.expiresAt * 1000) + const isExpired = entry.tokens.expiresAt < Date.now() / 1000 + prompts.log.info(` Expires: ${expiresDate.toISOString()} ${isExpired ? "(EXPIRED)" : ""}`) + } + if (entry.tokens.refreshToken) { + prompts.log.info(` Refresh token: present`) + } + } + if (entry?.clientInfo) { + prompts.log.info(` Client ID: ${entry.clientInfo.clientId}`) + if (entry.clientInfo.clientSecretExpiresAt) { + const expiresDate = new Date(entry.clientInfo.clientSecretExpiresAt * 1000) + prompts.log.info(` Client secret expires: ${expiresDate.toISOString()}`) + } + } + + const spinner = prompts.spinner() + spinner.start("Testing connection...") + + // Test basic HTTP connectivity first + try { + const response = await fetch(serverConfig.url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json, text/event-stream", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + method: "initialize", + params: { + protocolVersion: "2024-11-05", + capabilities: {}, + clientInfo: { name: "opencode-debug", version: Installation.VERSION }, + }, + id: 1, + }), + }) + + spinner.stop(`HTTP response: ${response.status} ${response.statusText}`) + + // Check for WWW-Authenticate header + const wwwAuth = response.headers.get("www-authenticate") + if (wwwAuth) { + prompts.log.info(`WWW-Authenticate: ${wwwAuth}`) + } + + if (response.status === 401) { + prompts.log.warn("Server returned 401 Unauthorized") + + // Try to discover OAuth metadata + const oauthConfig = typeof serverConfig.oauth === "object" ? serverConfig.oauth : undefined + const authProvider = new McpOAuthProvider( + serverName, + serverConfig.url, + { + clientId: oauthConfig?.clientId, + clientSecret: oauthConfig?.clientSecret, + scope: oauthConfig?.scope, + }, + { + onRedirect: async () => {}, + }, + ) + + prompts.log.info("Testing OAuth flow (without completing authorization)...") + + // Try creating transport with auth provider to trigger discovery + const transport = new StreamableHTTPClientTransport(new URL(serverConfig.url), { + authProvider, + }) + + try { + const client = new Client({ + name: "opencode-debug", + version: Installation.VERSION, + }) + await client.connect(transport) + prompts.log.success("Connection successful (already authenticated)") + await client.close() + } catch (error) { + if (error instanceof UnauthorizedError) { + prompts.log.info(`OAuth flow triggered: ${error.message}`) + + // Check if dynamic registration would be attempted + const clientInfo = await authProvider.clientInformation() + if (clientInfo) { + prompts.log.info(`Client ID available: ${clientInfo.client_id}`) + } else { + prompts.log.info("No client ID - dynamic registration will be attempted") + } + } else { + prompts.log.error(`Connection error: ${error instanceof Error ? error.message : String(error)}`) + } + } + } else if (response.status >= 200 && response.status < 300) { + prompts.log.success("Server responded successfully (no auth required or already authenticated)") + const body = await response.text() + try { + const json = JSON.parse(body) + if (json.result?.serverInfo) { + prompts.log.info(`Server info: ${JSON.stringify(json.result.serverInfo)}`) + } + } catch { + // Not JSON, ignore + } + } else { + prompts.log.warn(`Unexpected status: ${response.status}`) + const body = await response.text().catch(() => "") + if (body) { + prompts.log.info(`Response body: ${body.substring(0, 500)}`) + } + } + } catch (error) { + spinner.stop("Connection failed", 1) + prompts.log.error(`Error: ${error instanceof Error ? error.message : String(error)}`) + } + + prompts.outro("Debug complete") + }, + }) + }, +}) diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 3a0b2f23f..0c371b864 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -10,6 +10,7 @@ import { select } from "@clack/prompts" import { createOpencodeClient, type OpencodeClient } from "@opencode-ai/sdk/v2" import { Server } from "../../server/server" import { Provider } from "../../provider/provider" +import { Agent } from "../../agent/agent" const TOOL: Record = { todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD], @@ -223,10 +224,33 @@ export const RunCommand = cmd({ } })() + // Validate agent if specified + const resolvedAgent = await (async () => { + if (!args.agent) return undefined + const agent = await Agent.get(args.agent) + if (!agent) { + UI.println( + UI.Style.TEXT_WARNING_BOLD + "!", + UI.Style.TEXT_NORMAL, + `agent "${args.agent}" not found. Falling back to default agent`, + ) + return undefined + } + if (agent.mode === "subagent") { + UI.println( + UI.Style.TEXT_WARNING_BOLD + "!", + UI.Style.TEXT_NORMAL, + `agent "${args.agent}" is a subagent, not a primary agent. Falling back to default agent`, + ) + return undefined + } + return args.agent + })() + if (args.command) { await sdk.session.command({ sessionID, - agent: args.agent || "build", + agent: resolvedAgent, model: args.model, command: args.command, arguments: message, @@ -235,7 +259,7 @@ export const RunCommand = cmd({ const modelParam = args.model ? Provider.parseModel(args.model) : undefined await sdk.session.prompt({ sessionID, - agent: args.agent || "build", + agent: resolvedAgent, model: modelParam, parts: [...fileParts, { type: "text", text: message }], }) diff --git a/packages/opencode/src/cli/cmd/tui/app.tsx b/packages/opencode/src/cli/cmd/tui/app.tsx index 028905fc3..f63f6cb1a 100644 --- a/packages/opencode/src/cli/cmd/tui/app.tsx +++ b/packages/opencode/src/cli/cmd/tui/app.tsx @@ -169,7 +169,7 @@ function App() { const local = useLocal() const kv = useKV() const command = useCommandDialog() - const { event } = useSDK() + const sdk = useSDK() const toast = useToast() const { theme, mode, setMode } = useTheme() const sync = useSync() @@ -229,7 +229,8 @@ function App() { let continued = false createEffect(() => { - if (continued || sync.status !== "complete" || !args.continue) return + // When using -c, session list is loaded in blocking phase, so we can navigate at "partial" + if (continued || sync.status === "loading" || !args.continue) return const match = sync.data.session .toSorted((a, b) => b.time.updated - a.time.updated) .find((x) => x.parentID === undefined)?.id @@ -416,6 +417,15 @@ function App() { }, category: "System", }, + { + title: "Open WebUI", + value: "webui.open", + onSelect: () => { + open(sdk.url).catch(() => {}) + dialog.clear() + }, + category: "System", + }, { title: "Exit the app", value: "app.exit", @@ -486,11 +496,11 @@ function App() { } }) - event.on(TuiEvent.CommandExecute.type, (evt) => { + sdk.event.on(TuiEvent.CommandExecute.type, (evt) => { command.trigger(evt.properties.command) }) - event.on(TuiEvent.ToastShow.type, (evt) => { + sdk.event.on(TuiEvent.ToastShow.type, (evt) => { toast.show({ title: evt.properties.title, message: evt.properties.message, @@ -499,7 +509,7 @@ function App() { }) }) - event.on(SessionApi.Event.Deleted.type, (evt) => { + sdk.event.on(SessionApi.Event.Deleted.type, (evt) => { if (route.data.type === "session" && route.data.sessionID === evt.properties.info.id) { route.navigate({ type: "home" }) toast.show({ @@ -509,7 +519,7 @@ function App() { } }) - event.on(SessionApi.Event.Error.type, (evt) => { + sdk.event.on(SessionApi.Event.Error.type, (evt) => { const error = evt.properties.error const message = (() => { if (!error) return "An error occured" @@ -530,7 +540,7 @@ function App() { }) }) - event.on(Installation.Event.Updated.type, (evt) => { + sdk.event.on(Installation.Event.Updated.type, (evt) => { toast.show({ variant: "success", title: "Update Complete", @@ -539,7 +549,7 @@ function App() { }) }) - event.on(Installation.Event.UpdateAvailable.type, (evt) => { + sdk.event.on(Installation.Event.UpdateAvailable.type, (evt) => { toast.show({ variant: "info", title: "Update Available", diff --git a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 99a90ab46..47940d0e2 100644 --- a/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx @@ -14,7 +14,7 @@ import { Keybind } from "@/util/keybind" import { usePromptHistory, type PromptInfo } from "./history" import { type AutocompleteRef, Autocomplete } from "./autocomplete" import { useCommandDialog } from "../dialog-command" -import { useRenderer } from "@opentui/solid" +import { useRenderer, useTerminalDimensions } from "@opentui/solid" import { Editor } from "@tui/util/editor" import { useExit } from "../../context/exit" import { Clipboard } from "../../util/clipboard" @@ -120,6 +120,9 @@ export function Prompt(props: PromptProps) { const history = usePromptHistory() const command = useCommandDialog() const renderer = useRenderer() + const dimensions = useTerminalDimensions() + const tall = createMemo(() => dimensions().height > 40) + const wide = createMemo(() => dimensions().width > 120) const { theme, syntax } = useTheme() function promptModelWarning() { @@ -310,6 +313,11 @@ export function Prompt(props: PromptProps) { sdk.event.on(TuiEvent.PromptAppend.type, (evt) => { input.insertText(evt.properties.text) + setTimeout(() => { + input.getLayoutNode().markDirty() + input.gotoBufferEnd() + renderer.requestRender() + }, 0) }) createEffect(() => { @@ -731,6 +739,23 @@ export function Prompt(props: PromptProps) { e.preventDefault() return } + // Handle clipboard paste (Ctrl+V) - check for images first on Windows + // This is needed because Windows terminal doesn't properly send image data + // through bracketed paste, so we need to intercept the keypress and + // directly read from clipboard before the terminal handles it + if (keybind.match("input_paste", e)) { + const content = await Clipboard.read() + if (content?.mime.startsWith("image/")) { + e.preventDefault() + await pasteImage({ + filename: "clipboard", + mime: content.mime, + content: content.data, + }) + return + } + // If no image, let the default paste behavior continue + } if (keybind.match("input_clear", e) && store.prompt.input !== "") { input.clear() input.extmarks.clear() @@ -859,19 +884,21 @@ export function Prompt(props: PromptProps) { cursorColor={theme.text} syntaxStyle={syntax()} /> - - - {store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "} - - - - - {local.model.parsed().model} - - {local.model.parsed().provider} - - - + + + + {store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "} + + + + + {local.model.parsed().model} + + {local.model.parsed().provider} + + + + - }> - - - {/* @ts-ignore // SpinnerOptions doesn't support marginLeft */} - - - {(() => { - const retry = createMemo(() => { - const s = status() - if (s.type !== "retry") return - return s - }) - const message = createMemo(() => { - const r = retry() - if (!r) return - if (r.message.includes("exceeded your current quota") && r.message.includes("gemini")) - return "gemini is way too hot right now" - if (r.message.length > 80) return r.message.slice(0, 80) + "..." - return r.message - }) - const isTruncated = createMemo(() => { - const r = retry() - if (!r) return false - return r.message.length > 120 - }) - const [seconds, setSeconds] = createSignal(0) - onMount(() => { - const timer = setInterval(() => { - const next = retry()?.next - if (next) setSeconds(Math.round((next - Date.now()) / 1000)) - }, 1000) - - onCleanup(() => { - clearInterval(timer) + + + + + {/* @ts-ignore // SpinnerOptions doesn't support marginLeft */} + + + {(() => { + const retry = createMemo(() => { + const s = status() + if (s.type !== "retry") return + return s }) - }) - const handleMessageClick = () => { - const r = retry() - if (!r) return - if (isTruncated()) { - DialogAlert.show(dialog, "Retry Error", r.message) + const message = createMemo(() => { + const r = retry() + if (!r) return + if (r.message.includes("exceeded your current quota") && r.message.includes("gemini")) + return "gemini is way too hot right now" + if (r.message.length > 80) return r.message.slice(0, 80) + "..." + return r.message + }) + const isTruncated = createMemo(() => { + const r = retry() + if (!r) return false + return r.message.length > 120 + }) + const [seconds, setSeconds] = createSignal(0) + onMount(() => { + const timer = setInterval(() => { + const next = retry()?.next + if (next) setSeconds(Math.round((next - Date.now()) / 1000)) + }, 1000) + + onCleanup(() => { + clearTimeout(timer) + }) + }) + const handleMessageClick = () => { + const r = retry() + if (!r) return + if (isTruncated()) { + DialogAlert.show(dialog, "Retry Error", r.message) + } } - } - const retryText = () => { - const r = retry() - if (!r) return "" - const baseMessage = message() - const truncatedHint = isTruncated() ? " (click to expand)" : "" - const retryInfo = ` [retrying ${seconds() > 0 ? `in ${seconds()}s ` : ""}attempt #${r.attempt}]` - return baseMessage + truncatedHint + retryInfo - } + const retryText = () => { + const r = retry() + if (!r) return "" + const baseMessage = message() + const truncatedHint = isTruncated() ? " (click to expand)" : "" + const retryInfo = ` [retrying ${seconds() > 0 ? `in ${seconds()}s ` : ""}attempt #${r.attempt}]` + return baseMessage + truncatedHint + retryInfo + } - return ( - - - {retryText()} - - - ) - })()} + return ( + + + {retryText()} + + + ) + })()} + + 0 ? theme.primary : theme.text}> + esc{" "} + 0 ? theme.primary : theme.textMuted }}> + {store.interrupt > 0 ? "again to interrupt" : "interrupt"} + + - 0 ? theme.primary : theme.text}> - esc{" "} - 0 ? theme.primary : theme.textMuted }}> - {store.interrupt > 0 ? "again to interrupt" : "interrupt"} - - - - - - - - + + + + + {store.mode === "shell" ? "Shell" : Locale.titlecase(local.agent.current().name)}{" "} + + + + + {local.model.parsed().model} + + {local.model.parsed().provider} + + + + + + + + + {keybind.print("agent_cycle")} switch agent + + - {keybind.print("command_list")} commands + {keybind.print("sidebar_toggle")} sidebar - - - - esc exit shell mode - - - - - + + + {keybind.print("command_list")} commands + + + + + esc exit shell mode + + + + diff --git a/packages/opencode/src/cli/cmd/tui/context/local.tsx b/packages/opencode/src/cli/cmd/tui/context/local.tsx index f04b79685..55c04621e 100644 --- a/packages/opencode/src/cli/cmd/tui/context/local.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/local.tsx @@ -56,7 +56,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const [agentStore, setAgentStore] = createStore<{ current: string }>({ - current: agents()[0].name, + current: agents().find((x) => x.default)?.name ?? agents()[0].name, }) const { theme } = useTheme() const colors = createMemo(() => [ diff --git a/packages/opencode/src/cli/cmd/tui/context/route.tsx b/packages/opencode/src/cli/cmd/tui/context/route.tsx index 22333a058..358461921 100644 --- a/packages/opencode/src/cli/cmd/tui/context/route.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/route.tsx @@ -10,6 +10,7 @@ export type HomeRoute = { export type SessionRoute = { type: "session" sessionID: string + initialPrompt?: PromptInfo } export type Route = HomeRoute | SessionRoute diff --git a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx index b283f672f..3ea7c90b7 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sdk.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sdk.tsx @@ -2,7 +2,6 @@ import { createOpencodeClient, type Event } from "@opencode-ai/sdk/v2" import { createSimpleContext } from "./helper" import { createGlobalEmitter } from "@solid-primitives/event-bus" import { batch, onCleanup, onMount } from "solid-js" -import { iife } from "@/util/iife" export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ name: "SDK", @@ -70,6 +69,6 @@ export const { use: useSDK, provider: SDKProvider } = createSimpleContext({ abort.abort() }) - return { client: sdk, event: emitter } + return { client: sdk, event: emitter, url: props.url } }, }) diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index f74f787db..2528a4998 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -22,6 +22,7 @@ import { Binary } from "@opencode-ai/util/binary" import { createSimpleContext } from "./helper" import type { Snapshot } from "@/snapshot" import { useExit } from "./exit" +import { useArgs } from "./args" import { batch, onMount } from "solid-js" import { Log } from "@/util/log" import type { Path } from "@opencode-ai/sdk" @@ -254,10 +255,18 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }) const exit = useExit() + const args = useArgs() async function bootstrap() { - // blocking - await Promise.all([ + const sessionListPromise = sdk.client.session.list().then((x) => + setStore( + "session", + (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)), + ), + ) + + // blocking - include session.list when continuing a session + const blockingRequests: Promise[] = [ sdk.client.config.providers({}, { throwOnError: true }).then((x) => { batch(() => { setStore("provider", x.data!.providers) @@ -271,17 +280,15 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }), sdk.client.app.agents({}, { throwOnError: true }).then((x) => setStore("agent", x.data ?? [])), sdk.client.config.get({}, { throwOnError: true }).then((x) => setStore("config", x.data!)), - ]) + ...(args.continue ? [sessionListPromise] : []), + ] + + await Promise.all(blockingRequests) .then(() => { if (store.status !== "complete") setStore("status", "partial") // non-blocking Promise.all([ - sdk.client.session.list().then((x) => - setStore( - "session", - (x.data ?? []).toSorted((a, b) => a.id.localeCompare(b.id)), - ), - ), + ...(args.continue ? [] : [sessionListPromise]), sdk.client.command.list().then((x) => setStore("command", x.data ?? [])), sdk.client.lsp.status().then((x) => setStore("lsp", x.data!)), sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)), diff --git a/packages/opencode/src/cli/cmd/tui/context/theme.tsx b/packages/opencode/src/cli/cmd/tui/context/theme.tsx index 595d3025d..a17b13533 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/theme.tsx @@ -6,8 +6,10 @@ import { createSimpleContext } from "./helper" import aura from "./theme/aura.json" with { type: "json" } import ayu from "./theme/ayu.json" with { type: "json" } import catppuccin from "./theme/catppuccin.json" with { type: "json" } +import catppuccinFrappe from "./theme/catppuccin-frappe.json" with { type: "json" } import catppuccinMacchiato from "./theme/catppuccin-macchiato.json" with { type: "json" } import cobalt2 from "./theme/cobalt2.json" with { type: "json" } +import cursor from "./theme/cursor.json" with { type: "json" } import dracula from "./theme/dracula.json" with { type: "json" } import everforest from "./theme/everforest.json" with { type: "json" } import flexoki from "./theme/flexoki.json" with { type: "json" } @@ -136,8 +138,10 @@ export const DEFAULT_THEMES: Record = { aura, ayu, catppuccin, + ["catppuccin-frappe"]: catppuccinFrappe, ["catppuccin-macchiato"]: catppuccinMacchiato, cobalt2, + cursor, dracula, everforest, flexoki, @@ -279,14 +283,23 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ ready: false, }) - createEffect(async () => { - const custom = await getCustomThemes() - setStore( - produce((draft) => { - Object.assign(draft.themes, custom) - draft.ready = true - }), - ) + createEffect(() => { + getCustomThemes() + .then((custom) => { + setStore( + produce((draft) => { + Object.assign(draft.themes, custom) + }), + ) + }) + .catch(() => { + setStore("active", "opencode") + }) + .finally(() => { + if (store.active !== "system") { + setStore("ready", true) + } + }) }) const renderer = useRenderer() @@ -295,8 +308,25 @@ export const { use: useTheme, provider: ThemeProvider } = createSimpleContext({ size: 16, }) .then((colors) => { - if (!colors.palette[0]) return - setStore("themes", "system", generateSystem(colors, store.mode)) + if (!colors.palette[0]) { + if (store.active === "system") { + setStore( + produce((draft) => { + draft.active = "opencode" + draft.ready = true + }), + ) + } + return + } + setStore( + produce((draft) => { + draft.themes.system = generateSystem(colors, store.mode) + if (store.active === "system") { + draft.ready = true + } + }), + ) }) const values = createMemo(() => { diff --git a/packages/opencode/src/cli/cmd/tui/context/theme/catppuccin-frappe.json b/packages/opencode/src/cli/cmd/tui/context/theme/catppuccin-frappe.json new file mode 100644 index 000000000..79e56ee9a --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/context/theme/catppuccin-frappe.json @@ -0,0 +1,233 @@ +{ + "$schema": "https://opencode.ai/theme.json", + "defs": { + "frappeRosewater": "#f2d5cf", + "frappeFlamingo": "#eebebe", + "frappePink": "#f4b8e4", + "frappeMauve": "#ca9ee6", + "frappeRed": "#e78284", + "frappeMaroon": "#ea999c", + "frappePeach": "#ef9f76", + "frappeYellow": "#e5c890", + "frappeGreen": "#a6d189", + "frappeTeal": "#81c8be", + "frappeSky": "#99d1db", + "frappeSapphire": "#85c1dc", + "frappeBlue": "#8da4e2", + "frappeLavender": "#babbf1", + "frappeText": "#c6d0f5", + "frappeSubtext1": "#b5bfe2", + "frappeSubtext0": "#a5adce", + "frappeOverlay2": "#949cb8", + "frappeOverlay1": "#838ba7", + "frappeOverlay0": "#737994", + "frappeSurface2": "#626880", + "frappeSurface1": "#51576d", + "frappeSurface0": "#414559", + "frappeBase": "#303446", + "frappeMantle": "#292c3c", + "frappeCrust": "#232634" + }, + "theme": { + "primary": { + "dark": "frappeBlue", + "light": "frappeBlue" + }, + "secondary": { + "dark": "frappeMauve", + "light": "frappeMauve" + }, + "accent": { + "dark": "frappePink", + "light": "frappePink" + }, + "error": { + "dark": "frappeRed", + "light": "frappeRed" + }, + "warning": { + "dark": "frappeYellow", + "light": "frappeYellow" + }, + "success": { + "dark": "frappeGreen", + "light": "frappeGreen" + }, + "info": { + "dark": "frappeTeal", + "light": "frappeTeal" + }, + "text": { + "dark": "frappeText", + "light": "frappeText" + }, + "textMuted": { + "dark": "frappeSubtext1", + "light": "frappeSubtext1" + }, + "background": { + "dark": "frappeBase", + "light": "frappeBase" + }, + "backgroundPanel": { + "dark": "frappeMantle", + "light": "frappeMantle" + }, + "backgroundElement": { + "dark": "frappeCrust", + "light": "frappeCrust" + }, + "border": { + "dark": "frappeSurface0", + "light": "frappeSurface0" + }, + "borderActive": { + "dark": "frappeSurface1", + "light": "frappeSurface1" + }, + "borderSubtle": { + "dark": "frappeSurface2", + "light": "frappeSurface2" + }, + "diffAdded": { + "dark": "frappeGreen", + "light": "frappeGreen" + }, + "diffRemoved": { + "dark": "frappeRed", + "light": "frappeRed" + }, + "diffContext": { + "dark": "frappeOverlay2", + "light": "frappeOverlay2" + }, + "diffHunkHeader": { + "dark": "frappePeach", + "light": "frappePeach" + }, + "diffHighlightAdded": { + "dark": "frappeGreen", + "light": "frappeGreen" + }, + "diffHighlightRemoved": { + "dark": "frappeRed", + "light": "frappeRed" + }, + "diffAddedBg": { + "dark": "#29342b", + "light": "#29342b" + }, + "diffRemovedBg": { + "dark": "#3a2a31", + "light": "#3a2a31" + }, + "diffContextBg": { + "dark": "frappeMantle", + "light": "frappeMantle" + }, + "diffLineNumber": { + "dark": "frappeSurface1", + "light": "frappeSurface1" + }, + "diffAddedLineNumberBg": { + "dark": "#223025", + "light": "#223025" + }, + "diffRemovedLineNumberBg": { + "dark": "#2f242b", + "light": "#2f242b" + }, + "markdownText": { + "dark": "frappeText", + "light": "frappeText" + }, + "markdownHeading": { + "dark": "frappeMauve", + "light": "frappeMauve" + }, + "markdownLink": { + "dark": "frappeBlue", + "light": "frappeBlue" + }, + "markdownLinkText": { + "dark": "frappeSky", + "light": "frappeSky" + }, + "markdownCode": { + "dark": "frappeGreen", + "light": "frappeGreen" + }, + "markdownBlockQuote": { + "dark": "frappeYellow", + "light": "frappeYellow" + }, + "markdownEmph": { + "dark": "frappeYellow", + "light": "frappeYellow" + }, + "markdownStrong": { + "dark": "frappePeach", + "light": "frappePeach" + }, + "markdownHorizontalRule": { + "dark": "frappeSubtext0", + "light": "frappeSubtext0" + }, + "markdownListItem": { + "dark": "frappeBlue", + "light": "frappeBlue" + }, + "markdownListEnumeration": { + "dark": "frappeSky", + "light": "frappeSky" + }, + "markdownImage": { + "dark": "frappeBlue", + "light": "frappeBlue" + }, + "markdownImageText": { + "dark": "frappeSky", + "light": "frappeSky" + }, + "markdownCodeBlock": { + "dark": "frappeText", + "light": "frappeText" + }, + "syntaxComment": { + "dark": "frappeOverlay2", + "light": "frappeOverlay2" + }, + "syntaxKeyword": { + "dark": "frappeMauve", + "light": "frappeMauve" + }, + "syntaxFunction": { + "dark": "frappeBlue", + "light": "frappeBlue" + }, + "syntaxVariable": { + "dark": "frappeRed", + "light": "frappeRed" + }, + "syntaxString": { + "dark": "frappeGreen", + "light": "frappeGreen" + }, + "syntaxNumber": { + "dark": "frappePeach", + "light": "frappePeach" + }, + "syntaxType": { + "dark": "frappeYellow", + "light": "frappeYellow" + }, + "syntaxOperator": { + "dark": "frappeSky", + "light": "frappeSky" + }, + "syntaxPunctuation": { + "dark": "frappeText", + "light": "frappeText" + } + } +} diff --git a/packages/opencode/src/cli/cmd/tui/context/theme/cursor.json b/packages/opencode/src/cli/cmd/tui/context/theme/cursor.json new file mode 100644 index 000000000..ab518dbe7 --- /dev/null +++ b/packages/opencode/src/cli/cmd/tui/context/theme/cursor.json @@ -0,0 +1,249 @@ +{ + "$schema": "https://opencode.ai/theme.json", + "defs": { + "darkBg": "#181818", + "darkPanel": "#141414", + "darkElement": "#262626", + "darkFg": "#e4e4e4", + "darkMuted": "#e4e4e45e", + "darkBorder": "#e4e4e413", + "darkBorderActive": "#e4e4e426", + "darkCyan": "#88c0d0", + "darkBlue": "#81a1c1", + "darkGreen": "#3fa266", + "darkGreenBright": "#70b489", + "darkRed": "#e34671", + "darkRedBright": "#fc6b83", + "darkYellow": "#f1b467", + "darkOrange": "#d2943e", + "darkPink": "#E394DC", + "darkPurple": "#AAA0FA", + "darkTeal": "#82D2CE", + "darkSyntaxYellow": "#F8C762", + "darkSyntaxOrange": "#EFB080", + "darkSyntaxGreen": "#A8CC7C", + "darkSyntaxBlue": "#87C3FF", + "lightBg": "#fcfcfc", + "lightPanel": "#f3f3f3", + "lightElement": "#ededed", + "lightFg": "#141414", + "lightMuted": "#141414ad", + "lightBorder": "#14141413", + "lightBorderActive": "#14141426", + "lightTeal": "#6f9ba6", + "lightBlue": "#3c7cab", + "lightBlueDark": "#206595", + "lightGreen": "#1f8a65", + "lightGreenBright": "#55a583", + "lightRed": "#cf2d56", + "lightRedBright": "#e75e78", + "lightOrange": "#db704b", + "lightYellow": "#c08532", + "lightPurple": "#9e94d5", + "lightPurpleDark": "#6049b3", + "lightPink": "#b8448b", + "lightMagenta": "#b3003f" + }, + "theme": { + "primary": { + "dark": "darkCyan", + "light": "lightTeal" + }, + "secondary": { + "dark": "darkBlue", + "light": "lightBlue" + }, + "accent": { + "dark": "darkCyan", + "light": "lightTeal" + }, + "error": { + "dark": "darkRed", + "light": "lightRed" + }, + "warning": { + "dark": "darkYellow", + "light": "lightOrange" + }, + "success": { + "dark": "darkGreen", + "light": "lightGreen" + }, + "info": { + "dark": "darkBlue", + "light": "lightBlue" + }, + "text": { + "dark": "darkFg", + "light": "lightFg" + }, + "textMuted": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "background": { + "dark": "darkBg", + "light": "lightBg" + }, + "backgroundPanel": { + "dark": "darkPanel", + "light": "lightPanel" + }, + "backgroundElement": { + "dark": "darkElement", + "light": "lightElement" + }, + "border": { + "dark": "darkBorder", + "light": "lightBorder" + }, + "borderActive": { + "dark": "darkCyan", + "light": "lightTeal" + }, + "borderSubtle": { + "dark": "#0f0f0f", + "light": "#e0e0e0" + }, + "diffAdded": { + "dark": "darkGreen", + "light": "lightGreen" + }, + "diffRemoved": { + "dark": "darkRed", + "light": "lightRed" + }, + "diffContext": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "diffHunkHeader": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "diffHighlightAdded": { + "dark": "darkGreenBright", + "light": "lightGreenBright" + }, + "diffHighlightRemoved": { + "dark": "darkRedBright", + "light": "lightRedBright" + }, + "diffAddedBg": { + "dark": "#3fa26633", + "light": "#1f8a651f" + }, + "diffRemovedBg": { + "dark": "#b8004933", + "light": "#cf2d5614" + }, + "diffContextBg": { + "dark": "darkPanel", + "light": "lightPanel" + }, + "diffLineNumber": { + "dark": "#e4e4e442", + "light": "#1414147a" + }, + "diffAddedLineNumberBg": { + "dark": "#3fa26633", + "light": "#1f8a651f" + }, + "diffRemovedLineNumberBg": { + "dark": "#b8004933", + "light": "#cf2d5614" + }, + "markdownText": { + "dark": "darkFg", + "light": "lightFg" + }, + "markdownHeading": { + "dark": "darkPurple", + "light": "lightBlueDark" + }, + "markdownLink": { + "dark": "darkTeal", + "light": "lightBlueDark" + }, + "markdownLinkText": { + "dark": "darkBlue", + "light": "lightMuted" + }, + "markdownCode": { + "dark": "darkPink", + "light": "lightGreen" + }, + "markdownBlockQuote": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "markdownEmph": { + "dark": "darkTeal", + "light": "lightFg" + }, + "markdownStrong": { + "dark": "darkSyntaxYellow", + "light": "lightFg" + }, + "markdownHorizontalRule": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "markdownListItem": { + "dark": "darkFg", + "light": "lightFg" + }, + "markdownListEnumeration": { + "dark": "darkCyan", + "light": "lightMuted" + }, + "markdownImage": { + "dark": "darkCyan", + "light": "lightBlueDark" + }, + "markdownImageText": { + "dark": "darkBlue", + "light": "lightMuted" + }, + "markdownCodeBlock": { + "dark": "darkFg", + "light": "lightFg" + }, + "syntaxComment": { + "dark": "darkMuted", + "light": "lightMuted" + }, + "syntaxKeyword": { + "dark": "darkTeal", + "light": "lightMagenta" + }, + "syntaxFunction": { + "dark": "darkSyntaxOrange", + "light": "lightOrange" + }, + "syntaxVariable": { + "dark": "darkFg", + "light": "lightFg" + }, + "syntaxString": { + "dark": "darkPink", + "light": "lightPurple" + }, + "syntaxNumber": { + "dark": "darkSyntaxYellow", + "light": "lightPink" + }, + "syntaxType": { + "dark": "darkSyntaxOrange", + "light": "lightBlueDark" + }, + "syntaxOperator": { + "dark": "darkFg", + "light": "lightFg" + }, + "syntaxPunctuation": { + "dark": "darkFg", + "light": "lightFg" + } + } +} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx index d47d1df3b..62154cce5 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx @@ -6,6 +6,7 @@ import { Locale } from "@/util/locale" import { useSDK } from "@tui/context/sdk" import { useRoute } from "@tui/context/route" import { useDialog } from "../../ui/dialog" +import type { PromptInfo } from "@tui/component/prompt/history" export function DialogForkFromTimeline(props: { sessionID: string; onMove: (messageID: string) => void }) { const sync = useSync() @@ -35,9 +36,21 @@ export function DialogForkFromTimeline(props: { sessionID: string; onMove: (mess sessionID: props.sessionID, messageID: message.id, }) + const parts = sync.data.part[message.id] ?? [] + const initialPrompt = parts.reduce( + (agg, part) => { + if (part.type === "text") { + if (!part.synthetic) agg.input += part.text + } + if (part.type === "file") agg.parts.push(part) + return agg + }, + { input: "", parts: [] as PromptInfo["parts"] }, + ) route.navigate({ sessionID: forked.data!.id, type: "session", + initialPrompt, }) dialog.clear() }, diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index b9e6632ac..ff17b5567 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -54,7 +54,7 @@ export function DialogMessage(props: { { title: "Copy", value: "message.copy", - description: "copy message text to clipboard", + description: "message text to clipboard", onSelect: async (dialog) => { const msg = message() if (!msg) return @@ -80,9 +80,25 @@ export function DialogMessage(props: { sessionID: props.sessionID, messageID: props.messageID, }) + const initialPrompt = (() => { + const msg = message() + if (!msg) return undefined + const parts = sync.data.part[msg.id] + return parts.reduce( + (agg, part) => { + if (part.type === "text") { + if (!part.synthetic) agg.input += part.text + } + if (part.type === "file") agg.parts.push(part) + return agg + }, + { input: "", parts: [] as PromptInfo["parts"] }, + ) + })() route.navigate({ sessionID: result.data!.id, type: "session", + initialPrompt, }) dialog.clear() }, diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx index a9446b20d..c5ef70ef0 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-subagent.tsx @@ -11,7 +11,7 @@ export function DialogSubagent(props: { sessionID: string }) { { title: "Open", value: "subagent.view", - description: "open the subagent's session", + description: "the subagent's session", onSelect: (dialog) => { route.navigate({ type: "session", diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx index bfdbfa51b..cf6abef47 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx @@ -1,121 +1,140 @@ import { type Accessor, createMemo, Match, Show, Switch } from "solid-js" import { useRouteData } from "@tui/context/route" import { useSync } from "@tui/context/sync" -import { pipe, sumBy } from "remeda" import { useTheme } from "@tui/context/theme" -import { SplitBorder, EmptyBorder } from "@tui/component/border" -import type { AssistantMessage, Session } from "@opencode-ai/sdk/v2" -import { useDirectory } from "../../context/directory" +import { EmptyBorder } from "@tui/component/border" +import type { Session } from "@opencode-ai/sdk/v2" import { useKeybind } from "../../context/keybind" +import { useTerminalDimensions } from "@opentui/solid" -const Title = (props: { session: Accessor }) => { +const Title = (props: { session: Accessor; truncate?: boolean }) => { const { theme } = useTheme() return ( - + # {props.session().title} ) } -const ContextInfo = (props: { context: Accessor; cost: Accessor }) => { - const { theme } = useTheme() - return ( - - - {props.context()} ({props.cost()}) - - - ) -} - export function Header() { const route = useRouteData("session") const sync = useSync() const session = createMemo(() => sync.session.get(route.sessionID)!) - const messages = createMemo(() => sync.data.message[route.sessionID] ?? []) const shareEnabled = createMemo(() => sync.data.config.share !== "disabled") - - const cost = createMemo(() => { - const total = pipe( - messages(), - sumBy((x) => (x.role === "assistant" ? x.cost : 0)), - ) - return new Intl.NumberFormat("en-US", { - style: "currency", - currency: "USD", - }).format(total) - }) - - const context = createMemo(() => { - const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage - if (!last) return - const total = - last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write - const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID] - let result = total.toLocaleString() - if (model?.limit.context) { - result += " " + Math.round((total / model.limit.context) * 100) + "%" - } - return result - }) + const showShare = createMemo(() => shareEnabled() && !session()?.share?.url) const { theme } = useTheme() const keybind = useKeybind() + const dimensions = useTerminalDimensions() + const tall = createMemo(() => dimensions().height > 40) return ( - - - - - Subagent session - - - Prev {keybind.print("session_child_cycle_reverse")} - - - Next {keybind.print("session_child_cycle")} - - - - - - - - - <ContextInfo context={context} cost={cost} /> - </box> - <Show when={shareEnabled()}> - <box flexDirection="row" justifyContent="space-between" gap={1}> - <box flexGrow={1} flexShrink={1}> - <Switch> - <Match when={session().share?.url}> - <text fg={theme.textMuted} wrapMode="word"> - {session().share!.url} - </text> - </Match> - <Match when={true}> - <text fg={theme.text} wrapMode="word"> - /share <span style={{ fg: theme.textMuted }}>copy link</span> - </text> - </Match> - </Switch> - </box> + <box + height={1} + border={["top"]} + borderColor={theme.backgroundPanel} + customBorderChars={ + theme.backgroundPanel.a !== 0 + ? { + ...EmptyBorder, + horizontal: "▄", + } + : { + ...EmptyBorder, + horizontal: " ", + } + } + /> + </box> + <box + border={["left"]} + borderColor={theme.border} + customBorderChars={{ + ...EmptyBorder, + vertical: "┃", + bottomLeft: "╹", + }} + > + <box + paddingTop={tall() ? 1 : 0} + paddingBottom={tall() ? 1 : 0} + paddingLeft={2} + paddingRight={1} + flexShrink={0} + flexGrow={1} + backgroundColor={theme.backgroundPanel} + > + <Switch> + <Match when={session()?.parentID}> + <box flexDirection="row" gap={2}> + <text fg={theme.text}> + <b>Subagent session</b> + </text> + <text fg={theme.text}> + Parent <span style={{ fg: theme.textMuted }}>{keybind.print("session_parent")}</span> + </text> + <text fg={theme.text}> + Prev <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle_reverse")}</span> + </text> + <text fg={theme.text}> + Next <span style={{ fg: theme.textMuted }}>{keybind.print("session_child_cycle")}</span> + </text> + <box flexGrow={1} flexShrink={1} /> + <Show when={showShare()}> + <text fg={theme.textMuted} wrapMode="none" flexShrink={0}> + /share{" "} + </text> + </Show> </box> - </Show> - </Match> - </Switch> + </Match> + <Match when={true}> + <box flexDirection="row" justifyContent="space-between" gap={1}> + <Title session={session} truncate={!tall()} /> + <Show when={showShare()}> + <text fg={theme.textMuted} wrapMode="none" flexShrink={0}> + /share{" "} + </text> + </Show> + </box> + </Match> + </Switch> + </box> + </box> + <box + height={1} + border={["left"]} + borderColor={theme.border} + customBorderChars={{ + ...EmptyBorder, + vertical: theme.backgroundPanel.a !== 0 ? "╹" : " ", + }} + > + <box + height={1} + border={["bottom"]} + borderColor={theme.backgroundPanel} + customBorderChars={ + theme.backgroundPanel.a !== 0 + ? { + ...EmptyBorder, + horizontal: "▀", + } + : { + ...EmptyBorder, + horizontal: " ", + } + } + /> </box> </box> ) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 288504a72..826fa2acf 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -22,6 +22,7 @@ import { ScrollBoxRenderable, addDefaultParsers, MacOSScrollAccel, + RGBA, type ScrollAcceleration, } from "@opentui/core" import { Prompt, type PromptRef } from "@tui/component/prompt" @@ -129,13 +130,15 @@ export function Session() { const [diffWrapMode, setDiffWrapMode] = createSignal<"word" | "none">("word") const wide = createMemo(() => dimensions().width > 120) + const tall = createMemo(() => dimensions().height > 40) const sidebarVisible = createMemo(() => { if (session()?.parentID) return false if (sidebar() === "show") return true if (sidebar() === "auto" && wide()) return true return false }) - const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4) + const sidebarOverlay = createMemo(() => sidebarVisible() && !wide()) + const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() && !sidebarOverlay() ? 42 : 0) - 4) const scrollAcceleration = createMemo(() => { const tui = sync.data.config.tui @@ -168,6 +171,13 @@ export function Session() { const toast = useToast() const sdk = useSDK() + // Handle initial prompt from fork + createEffect(() => { + if (route.initialPrompt && prompt) { + prompt.set(route.initialPrompt) + } + }) + // Auto-navigate to whichever session currently needs permission input createEffect(() => { const currentSession = session() @@ -194,6 +204,52 @@ export function Session() { let prompt: PromptRef const keybind = useKeybind() + // Helper: Find next visible message boundary in direction + const findNextVisibleMessage = (direction: "next" | "prev"): string | null => { + const children = scroll.getChildren() + const messagesList = messages() + const scrollTop = scroll.y + + // Get visible messages sorted by position, filtering for valid non-synthetic, non-ignored content + const visibleMessages = children + .filter((c) => { + if (!c.id) return false + const message = messagesList.find((m) => m.id === c.id) + if (!message) return false + + // Check if message has valid non-synthetic, non-ignored text parts + const parts = sync.data.part[message.id] + if (!parts || !Array.isArray(parts)) return false + + return parts.some((part) => part && part.type === "text" && !part.synthetic && !part.ignored) + }) + .sort((a, b) => a.y - b.y) + + if (visibleMessages.length === 0) return null + + if (direction === "next") { + // Find first message below current position + return visibleMessages.find((c) => c.y > scrollTop + 10)?.id ?? null + } + // Find last message above current position + return [...visibleMessages].reverse().find((c) => c.y < scrollTop - 10)?.id ?? null + } + + // Helper: Scroll to message in direction or fallback to page scroll + const scrollToMessage = (direction: "next" | "prev", dialog: ReturnType<typeof useDialog>) => { + const targetID = findNextVisibleMessage(direction) + + if (!targetID) { + scroll.scrollBy(direction === "next" ? scroll.height : -scroll.height) + dialog.clear() + return + } + + const child = scroll.getChildren().find((c) => c.id === targetID) + if (child) scroll.scrollBy(child.y - scroll.y - 1) + dialog.clear() + } + useKeyboard((evt) => { if (dialog.stack.length > 0) return @@ -627,6 +683,22 @@ export function Session() { } }, }, + { + title: "Next message", + value: "session.message.next", + keybind: "messages_next", + category: "Session", + disabled: true, + onSelect: (dialog) => scrollToMessage("next", dialog), + }, + { + title: "Previous message", + value: "session.message.previous", + keybind: "messages_previous", + category: "Session", + disabled: true, + onSelect: (dialog) => scrollToMessage("prev", dialog), + }, { title: "Copy last assistant message", value: "messages.copy", @@ -801,6 +873,23 @@ export function Session() { dialog.clear() }, }, + { + title: "Go to parent session", + value: "session.parent", + keybind: "session_parent", + category: "Session", + disabled: true, + onSelect: (dialog) => { + const parentID = session()?.parentID + if (parentID) { + navigate({ + type: "session", + sessionID: parentID, + }) + } + dialog.clear() + }, + }, ]) const revertInfo = createMemo(() => session()?.revert) @@ -875,7 +964,7 @@ export function Session() { <box flexDirection="row"> <box flexGrow={1} paddingBottom={1} paddingTop={1} paddingLeft={2} paddingRight={2} gap={1}> <Show when={session()}> - <Show when={!sidebarVisible()}> + <Show when={!sidebarVisible() || sidebarOverlay()}> <Header /> </Show> <scrollbox @@ -1005,15 +1094,33 @@ export function Session() { sessionID={route.sessionID} /> </box> - <Show when={!sidebarVisible()}> + <Show when={(!sidebarVisible() || sidebarOverlay()) && tall()}> <Footer /> </Show> </Show> <Toast /> </box> - <Show when={sidebarVisible()}> + <Show when={sidebarVisible() && !sidebarOverlay()}> <Sidebar sessionID={route.sessionID} /> </Show> + <Show when={sidebarOverlay()}> + <box + position="absolute" + left={0} + top={0} + width={dimensions().width} + height={dimensions().height} + backgroundColor={RGBA.fromInts(0, 0, 0, 150)} + zIndex={100} + flexDirection="row" + justifyContent="flex-end" + onMouseUp={() => setSidebar("hide")} + > + <box onMouseUp={(e) => e.stopPropagation()}> + <Sidebar sessionID={route.sessionID} /> + </box> + </box> + </Show> </box> </context.Provider> ) @@ -1560,33 +1667,15 @@ ToolRegistry.register<typeof ListTool>({ ToolRegistry.register<typeof TaskTool>({ name: "task", - container: "inline", + container: "block", render(props) { const { theme } = useTheme() const keybind = useKeybind() const dialog = useDialog() const renderer = useRenderer() - const [hover, setHover] = createSignal(false) return ( - <box - border={["left"]} - customBorderChars={SplitBorder.customBorderChars} - borderColor={theme.background} - paddingTop={1} - paddingBottom={1} - paddingLeft={2} - marginTop={1} - gap={1} - backgroundColor={hover() ? theme.backgroundElement : theme.backgroundPanel} - onMouseOver={() => setHover(true)} - onMouseOut={() => setHover(false)} - onMouseUp={() => { - const id = props.metadata.sessionId - if (renderer.getSelection()?.getSelectedText() || !id) return - dialog.replace(() => <DialogSubagent sessionID={id} />) - }} - > + <> <ToolTitle icon="◉" fallback="Delegating..." when={props.input.subagent_type ?? props.input.description}> {Locale.titlecase(props.input.subagent_type ?? "unknown")} Task "{props.input.description}" </ToolTitle> @@ -1609,7 +1698,7 @@ ToolRegistry.register<typeof TaskTool>({ {keybind.print("session_child_cycle")}, {keybind.print("session_child_cycle_reverse")} <span style={{ fg: theme.textMuted }}> to navigate between subagent sessions</span> </text> - </box> + </> ) }, }) diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx index b64a18ae2..0bc4e860f 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/sidebar.tsx @@ -29,6 +29,16 @@ export function Sidebar(props: { sessionID: string }) { // Sort MCP servers alphabetically for consistent display order const mcpEntries = createMemo(() => Object.entries(sync.data.mcp).sort(([a], [b]) => a.localeCompare(b))) + // Count connected and error MCP servers for collapsed header display + const connectedMcpCount = createMemo(() => mcpEntries().filter(([_, item]) => item.status === "connected").length) + const errorMcpCount = createMemo( + () => + mcpEntries().filter( + ([_, item]) => + item.status === "failed" || item.status === "needs_auth" || item.status === "needs_client_registration", + ).length, + ) + const cost = createMemo(() => { const total = messages().reduce((sum, x) => sum + (x.role === "assistant" ? x.cost : 0), 0) return new Intl.NumberFormat("en-US", { @@ -62,6 +72,7 @@ export function Sidebar(props: { sessionID: string }) { <box backgroundColor={theme.backgroundPanel} width={42} + height="100%" paddingTop={1} paddingBottom={1} paddingLeft={2} @@ -97,6 +108,13 @@ export function Sidebar(props: { sessionID: string }) { </Show> <text fg={theme.text}> <b>MCP</b> + <Show when={!expanded.mcp}> + <span style={{ fg: theme.textMuted }}> + {" "} + ({connectedMcpCount()} active + {errorMcpCount() > 0 ? `, ${errorMcpCount()} error${errorMcpCount() > 1 ? "s" : ""}` : ""}) + </span> + </Show> </text> </box> <Show when={mcpEntries().length <= 2 || expanded.mcp}> @@ -154,7 +172,11 @@ export function Sidebar(props: { sessionID: string }) { </box> <Show when={sync.data.lsp.length <= 2 || expanded.lsp}> <Show when={sync.data.lsp.length === 0}> - <text fg={theme.textMuted}>LSPs will activate as files are read</text> + <text fg={theme.textMuted}> + {sync.data.config.lsp === false + ? "LSPs have been disabled in settings" + : "LSPs will activate as files are read"} + </text> </Show> <For each={sync.data.lsp}> {(item) => ( diff --git a/packages/opencode/src/cli/error.ts b/packages/opencode/src/cli/error.ts index 07a53d293..54ced0d7a 100644 --- a/packages/opencode/src/cli/error.ts +++ b/packages/opencode/src/cli/error.ts @@ -32,7 +32,8 @@ export function FormatError(input: unknown) { } if (Config.InvalidError.isInstance(input)) return [ - `Config file at ${input.data.path} is invalid` + (input.data.message ? `: ${input.data.message}` : ""), + `Configuration is invalid${input.data.path && input.data.path !== "config" ? ` at ${input.data.path}` : ""}` + + (input.data.message ? `: ${input.data.message}` : ""), ...(input.data.issues?.map((issue) => "↳ " + issue.message + " " + issue.path.join(".")) ?? []), ].join("\n") diff --git a/packages/opencode/src/command/template/review.txt b/packages/opencode/src/command/template/review.txt index 6ae94ce64..1ffa0fca0 100644 --- a/packages/opencode/src/command/template/review.txt +++ b/packages/opencode/src/command/template/review.txt @@ -28,31 +28,53 @@ Use best judgement when processing input. --- +## Gathering Context + +**Diffs alone are not enough.** After getting the diff, read the entire file(s) being modified to understand the full context. Code that looks wrong in isolation may be correct given surrounding logic—and vice versa. + +- Use the diff to identify which files changed +- Read the full file to understand existing patterns, control flow, and error handling +- Check for existing style guide or conventions files (CONVENTIONS.md, AGENTS.md, .editorconfig, etc.) + +--- + ## What to Look For **Bugs** - Your primary focus. - Logic errors, off-by-one mistakes, incorrect conditionals -- Edge cases: null/empty inputs, error conditions, race conditions +- If-else guards: missing guards, incorrect branching, unreachable code paths +- Edge cases: null/empty/undefined inputs, error conditions, race conditions - Security issues: injection, auth bypass, data exposure -- Broken error handling that swallows failures +- Broken error handling that swallows failures, throws unexpectedly or returns error types that are not caught. **Structure** - Does the code fit the codebase? - Does it follow existing patterns and conventions? - Are there established abstractions it should use but doesn't? +- Excessive nesting that could be flattened with early returns or extraction **Performance** - Only flag if obviously problematic. - O(n²) on unbounded data, N+1 queries, blocking I/O on hot paths +--- + ## Before You Flag Something -Be certain. If you're going to call something a bug, you need to be confident it actually is one. +**Be certain.** If you're going to call something a bug, you need to be confident it actually is one. - Only review the changes - do not review pre-existing code that wasn't modified - Don't flag something as a bug if you're unsure - investigate first -- Don't flag style preferences as issues - Don't invent hypothetical problems - if an edge case matters, explain the realistic scenario where it breaks - If you need more context to be sure, use the tools below to get it +**Don't be a zealot about style.** When checking code against conventions: + +- Verify the code is *actually* in violation. Don't complain about else statements if early returns are already being used correctly. +- Some "violations" are acceptable when they're the simplest option. A `let` statement is fine if the alternative is convoluted. +- Excessive nesting is a legitimate concern regardless of other style choices. +- Don't flag style preferences as issues unless they clearly violate established project conventions. + +--- + ## Tools Use these to inform your review: @@ -63,11 +85,13 @@ Use these to inform your review: If you're uncertain about something and can't verify it with these tools, say "I'm not sure about X" rather than flagging it as a definite issue. -## Tone and Approach +--- + +## Output 1. If there is a bug, be direct and clear about why it is a bug. -2. You should clearly communicate severity of issues, do not claim issues are more severe than they actually are. +2. Clearly communicate severity of issues. Do not overstate severity. 3. Critiques should clearly and explicitly communicate the scenarios, environments, or inputs that are necessary for the bug to arise. The comment should immediately indicate that the issue's severity depends on these factors. 4. Your tone should be matter-of-fact and not accusatory or overly positive. It should read as a helpful AI assistant suggestion without sounding too much like a human reviewer. -5. Write in a manner that allows reader to quickly understand issue without reading too closely. +5. Write so the reader can quickly understand the issue without reading too closely. 6. AVOID flattery, do not give any comments that are not helpful to the reader. Avoid phrasing like "Great job ...", "Thanks for ...". diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index a01cc832a..cb93c0ebf 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -414,6 +414,7 @@ export namespace Config { .object({ edit: Permission.optional(), bash: z.union([Permission, z.record(z.string(), Permission)]).optional(), + skill: z.union([Permission, z.record(z.string(), Permission)]).optional(), webfetch: Permission.optional(), doom_loop: Permission.optional(), external_directory: Permission.optional(), @@ -456,6 +457,8 @@ export namespace Config { .describe("Scroll messages down by half page"), messages_first: z.string().optional().default("ctrl+g,home").describe("Navigate to first message"), messages_last: z.string().optional().default("ctrl+alt+g,end").describe("Navigate to last message"), + messages_next: z.string().optional().default("none").describe("Navigate to next message"), + messages_previous: z.string().optional().default("none").describe("Navigate to previous message"), messages_last_user: z.string().optional().default("none").describe("Navigate to last user message"), messages_copy: z.string().optional().default("<leader>y").describe("Copy message"), messages_undo: z.string().optional().default("<leader>u").describe("Undo message"), @@ -560,6 +563,7 @@ export namespace Config { history_next: z.string().optional().default("down").describe("Next history item"), session_child_cycle: z.string().optional().default("<leader>right").describe("Next child session"), session_child_cycle_reverse: z.string().optional().default("<leader>left").describe("Previous child session"), + session_parent: z.string().optional().default("<leader>up").describe("Go to parent session"), terminal_suspend: z.string().optional().default("ctrl+z").describe("Suspend terminal"), terminal_title_toggle: z.string().optional().default("none").describe("Toggle terminal title"), }) @@ -666,6 +670,12 @@ export namespace Config { .string() .describe("Small model to use for tasks like title generation in the format of provider/model") .optional(), + default_agent: z + .string() + .optional() + .describe( + "Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.", + ), username: z .string() .optional() @@ -755,6 +765,7 @@ export namespace Config { .object({ edit: Permission.optional(), bash: z.union([Permission, z.record(z.string(), Permission)]).optional(), + skill: z.union([Permission, z.record(z.string(), Permission)]).optional(), webfetch: Permission.optional(), doom_loop: Permission.optional(), external_directory: Permission.optional(), diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index 61630ff1c..148ab45cb 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -1,5 +1,4 @@ import { BusEvent } from "@/bus/bus-event" -import { Bus } from "@/bus" import z from "zod" import { $ } from "bun" import type { BunFile } from "bun" @@ -74,6 +73,7 @@ export namespace File { async function shouldEncode(file: BunFile): Promise<boolean> { const type = file.type?.toLowerCase() + log.info("shouldEncode", { type }) if (!type) return false if (type.startsWith("text/")) return false @@ -87,15 +87,12 @@ export namespace File { const tops = ["image", "audio", "video", "font", "model", "multipart"] if (tops.includes(top)) return true - if (type === "application/octet-stream") return true - const bins = [ "zip", "gzip", "bzip", "compressed", "binary", - "stream", "pdf", "msword", "powerpoint", @@ -125,6 +122,8 @@ export namespace File { let cache: Entry = { files: [], dirs: [] } let fetching = false const fn = async (result: Entry) => { + // Disable scanning if in root of file system + if (Instance.directory === path.parse(Instance.directory).root) return fetching = true const set = new Set<string>() for await (const file of Ripgrep.files({ cwd: Instance.directory })) { @@ -290,9 +289,11 @@ export namespace File { } const resolved = dir ? path.join(Instance.directory, dir) : Instance.directory const nodes: Node[] = [] - for (const entry of await fs.promises.readdir(resolved, { - withFileTypes: true, - })) { + for (const entry of await fs.promises + .readdir(resolved, { + withFileTypes: true, + }) + .catch(() => [])) { if (exclude.includes(entry.name)) continue const fullPath = path.join(resolved, entry.name) const relativePath = path.relative(Instance.directory, fullPath) diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index 00d9e8c38..22b714b85 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -240,7 +240,8 @@ export namespace Ripgrep { if (done) break buffer += decoder.decode(value, { stream: true }) - const lines = buffer.split("\n") + // Handle both Unix (\n) and Windows (\r\n) line endings + const lines = buffer.split(/\r?\n/) buffer = lines.pop() || "" for (const line of lines) { @@ -379,7 +380,8 @@ export namespace Ripgrep { return [] } - const lines = result.text().trim().split("\n").filter(Boolean) + // Handle both Unix (\n) and Windows (\r\n) line endings + const lines = result.text().trim().split(/\r?\n/).filter(Boolean) // Parse JSON lines from ripgrep output return lines diff --git a/packages/opencode/src/flag/flag.ts b/packages/opencode/src/flag/flag.ts index 412377693..805da33cc 100644 --- a/packages/opencode/src/flag/flag.ts +++ b/packages/opencode/src/flag/flag.ts @@ -30,6 +30,7 @@ export namespace Flag { export const OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX = number("OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX") export const OPENCODE_EXPERIMENTAL_OXFMT = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_OXFMT") export const OPENCODE_EXPERIMENTAL_LSP_TY = truthy("OPENCODE_EXPERIMENTAL_LSP_TY") + export const OPENCODE_EXPERIMENTAL_LSP_TOOL = OPENCODE_EXPERIMENTAL || truthy("OPENCODE_EXPERIMENTAL_LSP_TOOL") function truthy(key: string) { const value = process.env[key]?.toLowerCase() diff --git a/packages/opencode/src/installation/index.ts b/packages/opencode/src/installation/index.ts index 0359c16fe..2c99e21a8 100644 --- a/packages/opencode/src/installation/index.ts +++ b/packages/opencode/src/installation/index.ts @@ -1,5 +1,4 @@ import { BusEvent } from "@/bus/bus-event" -import { Bus } from "@/bus" import path from "path" import { $ } from "bun" import z from "zod" @@ -66,23 +65,23 @@ export namespace Installation { const checks = [ { name: "npm" as const, - command: () => $`npm list -g --depth=0`.throws(false).text(), + command: () => $`npm list -g --depth=0`.throws(false).quiet().text(), }, { name: "yarn" as const, - command: () => $`yarn global list`.throws(false).text(), + command: () => $`yarn global list`.throws(false).quiet().text(), }, { name: "pnpm" as const, - command: () => $`pnpm list -g --depth=0`.throws(false).text(), + command: () => $`pnpm list -g --depth=0`.throws(false).quiet().text(), }, { name: "bun" as const, - command: () => $`bun pm ls -g`.throws(false).text(), + command: () => $`bun pm ls -g`.throws(false).quiet().text(), }, { name: "brew" as const, - command: () => $`brew list --formula opencode`.throws(false).text(), + command: () => $`brew list --formula opencode`.throws(false).quiet().text(), }, ] @@ -112,9 +111,9 @@ export namespace Installation { ) async function getBrewFormula() { - const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).text() + const tapFormula = await $`brew list --formula sst/tap/opencode`.throws(false).quiet().text() if (tapFormula.includes("opencode")) return "sst/tap/opencode" - const coreFormula = await $`brew list --formula opencode`.throws(false).text() + const coreFormula = await $`brew list --formula opencode`.throws(false).quiet().text() if (coreFormula.includes("opencode")) return "opencode" return "opencode" } diff --git a/packages/opencode/src/lsp/index.ts b/packages/opencode/src/lsp/index.ts index 1d52aefcb..0fd3b69df 100644 --- a/packages/opencode/src/lsp/index.ts +++ b/packages/opencode/src/lsp/index.ts @@ -261,23 +261,36 @@ export namespace LSP { return result } + export async function hasClients(file: string) { + const s = await state() + const extension = path.parse(file).ext || file + for (const server of Object.values(s.servers)) { + if (server.extensions.length && !server.extensions.includes(extension)) continue + const root = await server.root(file) + if (!root) continue + if (s.broken.has(root + server.id)) continue + return true + } + return false + } + export async function touchFile(input: string, waitForDiagnostics?: boolean) { log.info("touching file", { file: input }) const clients = await getClients(input) - await run(async (client) => { - if (!clients.includes(client)) return - const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: input }) : Promise.resolve() - await client.notify.open({ path: input }) - - return wait - }).catch((err) => { + await Promise.all( + clients.map(async (client) => { + const wait = waitForDiagnostics ? client.waitForDiagnostics({ path: input }) : Promise.resolve() + await client.notify.open({ path: input }) + return wait + }), + ).catch((err) => { log.error("failed to touch file", { err, file: input }) }) } export async function diagnostics() { const results: Record<string, LSPClient.Diagnostic[]> = {} - for (const result of await run(async (client) => client.diagnostics)) { + for (const result of await runAll(async (client) => client.diagnostics)) { for (const [path, diagnostics] of result.entries()) { const arr = results[path] || [] arr.push(...diagnostics) @@ -288,16 +301,18 @@ export namespace LSP { } export async function hover(input: { file: string; line: number; character: number }) { - return run((client) => { - return client.connection.sendRequest("textDocument/hover", { - textDocument: { - uri: pathToFileURL(input.file).href, - }, - position: { - line: input.line, - character: input.character, - }, - }) + return run(input.file, (client) => { + return client.connection + .sendRequest("textDocument/hover", { + textDocument: { + uri: pathToFileURL(input.file).href, + }, + position: { + line: input.line, + character: input.character, + }, + }) + .catch(() => null) }) } @@ -342,7 +357,7 @@ export namespace LSP { ] export async function workspaceSymbol(query: string) { - return run((client) => + return runAll((client) => client.connection .sendRequest("workspace/symbol", { query, @@ -354,7 +369,8 @@ export namespace LSP { } export async function documentSymbol(uri: string) { - return run((client) => + const file = new URL(uri).pathname + return run(file, (client) => client.connection .sendRequest("textDocument/documentSymbol", { textDocument: { @@ -367,12 +383,89 @@ export namespace LSP { .then((result) => result.filter(Boolean)) } - async function run<T>(input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> { + export async function definition(input: { file: string; line: number; character: number }) { + return run(input.file, (client) => + client.connection + .sendRequest("textDocument/definition", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + }) + .catch(() => null), + ).then((result) => result.flat().filter(Boolean)) + } + + export async function references(input: { file: string; line: number; character: number }) { + return run(input.file, (client) => + client.connection + .sendRequest("textDocument/references", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + context: { includeDeclaration: true }, + }) + .catch(() => []), + ).then((result) => result.flat().filter(Boolean)) + } + + export async function implementation(input: { file: string; line: number; character: number }) { + return run(input.file, (client) => + client.connection + .sendRequest("textDocument/implementation", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + }) + .catch(() => null), + ).then((result) => result.flat().filter(Boolean)) + } + + export async function prepareCallHierarchy(input: { file: string; line: number; character: number }) { + return run(input.file, (client) => + client.connection + .sendRequest("textDocument/prepareCallHierarchy", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + }) + .catch(() => []), + ).then((result) => result.flat().filter(Boolean)) + } + + export async function incomingCalls(input: { file: string; line: number; character: number }) { + return run(input.file, async (client) => { + const items = (await client.connection + .sendRequest("textDocument/prepareCallHierarchy", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + }) + .catch(() => [])) as any[] + if (!items?.length) return [] + return client.connection.sendRequest("callHierarchy/incomingCalls", { item: items[0] }).catch(() => []) + }).then((result) => result.flat().filter(Boolean)) + } + + export async function outgoingCalls(input: { file: string; line: number; character: number }) { + return run(input.file, async (client) => { + const items = (await client.connection + .sendRequest("textDocument/prepareCallHierarchy", { + textDocument: { uri: pathToFileURL(input.file).href }, + position: { line: input.line, character: input.character }, + }) + .catch(() => [])) as any[] + if (!items?.length) return [] + return client.connection.sendRequest("callHierarchy/outgoingCalls", { item: items[0] }).catch(() => []) + }).then((result) => result.flat().filter(Boolean)) + } + + async function runAll<T>(input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> { const clients = await state().then((x) => x.clients) const tasks = clients.map((x) => input(x)) return Promise.all(tasks) } + async function run<T>(file: string, input: (client: LSPClient.Info) => Promise<T>): Promise<T[]> { + const clients = await getClients(file) + const tasks = clients.map((x) => input(x)) + return Promise.all(tasks) + } + export namespace Diagnostic { export function pretty(diagnostic: LSPClient.Diagnostic) { const severityMap = { diff --git a/packages/opencode/src/lsp/language.ts b/packages/opencode/src/lsp/language.ts index 5261873f6..620944a8e 100644 --- a/packages/opencode/src/lsp/language.ts +++ b/packages/opencode/src/lsp/language.ts @@ -25,6 +25,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = { ".ex": "elixir", ".exs": "elixir", ".erl": "erlang", + ".ets": "typescript", ".hrl": "erlang", ".fs": "fsharp", ".fsi": "fsharp", @@ -109,4 +110,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = { ".tf": "terraform", ".tfvars": "terraform-vars", ".hcl": "hcl", + ".nix": "nix", + ".typ": "typst", + ".typc": "typst", } as const diff --git a/packages/opencode/src/lsp/server.ts b/packages/opencode/src/lsp/server.ts index 4db0e7237..b432e5a5d 100644 --- a/packages/opencode/src/lsp/server.ts +++ b/packages/opencode/src/lsp/server.ts @@ -697,7 +697,7 @@ export namespace LSPServer { }) if (!ok) return } else { - await $`tar -xf ${tempPath}`.cwd(Global.Path.bin).nothrow() + await $`tar -xf ${tempPath}`.cwd(Global.Path.bin).quiet().nothrow() } await fs.rm(tempPath, { force: true }) @@ -710,7 +710,7 @@ export namespace LSPServer { } if (platform !== "win32") { - await $`chmod +x ${bin}`.nothrow() + await $`chmod +x ${bin}`.quiet().nothrow() } log.info(`installed zls`, { bin }) @@ -1003,7 +1003,7 @@ export namespace LSPServer { if (!ok) return } if (tar) { - await $`tar -xf ${archive}`.cwd(Global.Path.bin).nothrow() + await $`tar -xf ${archive}`.cwd(Global.Path.bin).quiet().nothrow() } await fs.rm(archive, { force: true }) @@ -1014,7 +1014,7 @@ export namespace LSPServer { } if (platform !== "win32") { - await $`chmod +x ${bin}`.nothrow() + await $`chmod +x ${bin}`.quiet().nothrow() } await fs.unlink(path.join(Global.Path.bin, "clangd")).catch(() => {}) @@ -1580,7 +1580,7 @@ export namespace LSPServer { } if (platform !== "win32") { - await $`chmod +x ${bin}`.nothrow() + await $`chmod +x ${bin}`.quiet().nothrow() } log.info(`installed terraform-ls`, { bin }) @@ -1663,7 +1663,7 @@ export namespace LSPServer { if (!ok) return } if (ext === "tar.gz") { - await $`tar -xzf ${tempPath}`.cwd(Global.Path.bin).nothrow() + await $`tar -xzf ${tempPath}`.cwd(Global.Path.bin).quiet().nothrow() } await fs.rm(tempPath, { force: true }) @@ -1676,7 +1676,7 @@ export namespace LSPServer { } if (platform !== "win32") { - await $`chmod +x ${bin}`.nothrow() + await $`chmod +x ${bin}`.quiet().nothrow() } log.info("installed texlab", { bin }) @@ -1746,4 +1746,150 @@ export namespace LSPServer { } }, } + + export const Clojure: Info = { + id: "clojure-lsp", + extensions: [".clj", ".cljs", ".cljc", ".edn"], + root: NearestRoot(["deps.edn", "project.clj", "shadow-cljs.edn", "bb.edn", "build.boot"]), + async spawn(root) { + let bin = Bun.which("clojure-lsp") + if (!bin && process.platform === "win32") { + bin = Bun.which("clojure-lsp.exe") + } + if (!bin) { + log.info("clojure-lsp not found, please install clojure-lsp first") + return + } + return { + process: spawn(bin, ["listen"], { + cwd: root, + }), + } + }, + } + + export const Nixd: Info = { + id: "nixd", + extensions: [".nix"], + root: async (file) => { + // First, look for flake.nix - the most reliable Nix project root indicator + const flakeRoot = await NearestRoot(["flake.nix"])(file) + if (flakeRoot && flakeRoot !== Instance.directory) return flakeRoot + + // If no flake.nix, fall back to git repository root + if (Instance.worktree && Instance.worktree !== Instance.directory) return Instance.worktree + + // Finally, use the instance directory as fallback + return Instance.directory + }, + async spawn(root) { + const nixd = Bun.which("nixd") + if (!nixd) { + log.info("nixd not found, please install nixd first") + return + } + return { + process: spawn(nixd, [], { + cwd: root, + env: { + ...process.env, + }, + }), + } + }, + } + + export const Tinymist: Info = { + id: "tinymist", + extensions: [".typ", ".typc"], + root: NearestRoot(["typst.toml"]), + async spawn(root) { + let bin = Bun.which("tinymist", { + PATH: process.env["PATH"] + path.delimiter + Global.Path.bin, + }) + + if (!bin) { + if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return + log.info("downloading tinymist from GitHub releases") + + const response = await fetch("https://api.github.com/repos/Myriad-Dreamin/tinymist/releases/latest") + if (!response.ok) { + log.error("Failed to fetch tinymist release info") + return + } + + const release = (await response.json()) as { + tag_name?: string + assets?: { name?: string; browser_download_url?: string }[] + } + + const platform = process.platform + const arch = process.arch + + const tinymistArch = arch === "arm64" ? "aarch64" : "x86_64" + let tinymistPlatform: string + let ext: string + + if (platform === "darwin") { + tinymistPlatform = "apple-darwin" + ext = "tar.gz" + } else if (platform === "win32") { + tinymistPlatform = "pc-windows-msvc" + ext = "zip" + } else { + tinymistPlatform = "unknown-linux-gnu" + ext = "tar.gz" + } + + const assetName = `tinymist-${tinymistArch}-${tinymistPlatform}.${ext}` + + const assets = release.assets ?? [] + const asset = assets.find((a) => a.name === assetName) + if (!asset?.browser_download_url) { + log.error(`Could not find asset ${assetName} in tinymist release`) + return + } + + const downloadResponse = await fetch(asset.browser_download_url) + if (!downloadResponse.ok) { + log.error("Failed to download tinymist") + return + } + + const tempPath = path.join(Global.Path.bin, assetName) + await Bun.file(tempPath).write(downloadResponse) + + if (ext === "zip") { + const ok = await Archive.extractZip(tempPath, Global.Path.bin) + .then(() => true) + .catch((error) => { + log.error("Failed to extract tinymist archive", { error }) + return false + }) + if (!ok) return + } else { + await $`tar -xzf ${tempPath} --strip-components=1`.cwd(Global.Path.bin).quiet().nothrow() + } + + await fs.rm(tempPath, { force: true }) + + bin = path.join(Global.Path.bin, "tinymist" + (platform === "win32" ? ".exe" : "")) + + if (!(await Bun.file(bin).exists())) { + log.error("Failed to extract tinymist binary") + return + } + + if (platform !== "win32") { + await $`chmod +x ${bin}`.quiet().nothrow() + } + + log.info("installed tinymist", { bin }) + } + + return { + process: spawn(bin, { cwd: root }), + } + }, + } } diff --git a/packages/opencode/src/mcp/auth.ts b/packages/opencode/src/mcp/auth.ts index 6ebb95698..7f7dbd156 100644 --- a/packages/opencode/src/mcp/auth.ts +++ b/packages/opencode/src/mcp/auth.ts @@ -121,4 +121,15 @@ export namespace McpAuth { await set(mcpName, entry) } } + + /** + * Check if stored tokens are expired. + * Returns null if no tokens exist, false if no expiry or not expired, true if expired. + */ + export async function isTokenExpired(mcpName: string): Promise<boolean | null> { + const entry = await get(mcpName) + if (!entry?.tokens) return null + if (!entry.tokens.expiresAt) return false + return entry.tokens.expiresAt < Date.now() / 1000 + } } diff --git a/packages/opencode/src/mcp/index.ts b/packages/opencode/src/mcp/index.ts index 625809af9..40ee25565 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -1,18 +1,22 @@ -import { type Tool } from "ai" -import { experimental_createMCPClient } from "@ai-sdk/mcp" +import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai" +import { Client } from "@modelcontextprotocol/sdk/client/index.js" import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js" import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js" import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js" +import type { Tool as MCPToolDef } from "@modelcontextprotocol/sdk/types.js" import { Config } from "../config/config" import { Log } from "../util/log" import { NamedError } from "@opencode-ai/util/error" import z from "zod/v4" import { Instance } from "../project/instance" +import { Installation } from "../installation" import { withTimeout } from "@/util/timeout" import { McpOAuthProvider } from "./oauth-provider" import { McpOAuthCallback } from "./oauth-callback" import { McpAuth } from "./auth" +import { Bus } from "@/bus" +import { TuiEvent } from "@/cli/cmd/tui/event" import open from "open" export namespace MCP { @@ -25,7 +29,7 @@ export namespace MCP { }), ) - type Client = Awaited<ReturnType<typeof experimental_createMCPClient>> + type MCPClient = Client export const Status = z .discriminatedUnion("status", [ @@ -71,7 +75,30 @@ export namespace MCP { ref: "MCPStatus", }) export type Status = z.infer<typeof Status> - type MCPClient = Awaited<ReturnType<typeof experimental_createMCPClient>> + + // Convert MCP tool definition to AI SDK Tool type + function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient): Tool { + const inputSchema = mcpTool.inputSchema + + // Spread first, then override type to ensure it's always "object" + const schema: JSONSchema7 = { + ...(inputSchema as JSONSchema7), + type: "object", + properties: (inputSchema.properties ?? {}) as JSONSchema7["properties"], + additionalProperties: false, + } + + return dynamicTool({ + description: mcpTool.description ?? "", + inputSchema: jsonSchema(schema), + execute: async (args: unknown) => { + return client.callTool({ + name: mcpTool.name, + arguments: args as Record<string, unknown>, + }) + }, + }) + } // Store transports for OAuth servers to allow finishing auth type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport @@ -81,7 +108,7 @@ export namespace MCP { async () => { const cfg = await Config.get() const config = cfg.mcp ?? {} - const clients: Record<string, Client> = {} + const clients: Record<string, MCPClient> = {} const status: Record<string, Status> = {} await Promise.all( @@ -204,10 +231,12 @@ export namespace MCP { let lastError: Error | undefined for (const { name, transport } of transports) { try { - mcpClient = await experimental_createMCPClient({ + const client = new Client({ name: "opencode", - transport, + version: Installation.VERSION, }) + await client.connect(transport) + mcpClient = client log.info("connected", { key, transport: name }) status = { status: "connected" } break @@ -224,10 +253,24 @@ export namespace MCP { status: "needs_client_registration" as const, error: "Server does not support dynamic client registration. Please provide clientId in config.", } + // Show toast for needs_client_registration + Bus.publish(TuiEvent.ToastShow, { + title: "MCP Authentication Required", + message: `Server "${key}" requires a pre-registered client ID. Add clientId to your config.`, + variant: "warning", + duration: 8000, + }).catch((e) => log.debug("failed to show toast", { error: e })) } else { // Store transport for later finishAuth call pendingOAuthTransports.set(key, transport) status = { status: "needs_auth" as const } + // Show toast for needs_auth + Bus.publish(TuiEvent.ToastShow, { + title: "MCP Authentication Required", + message: `Server "${key}" requires authentication. Run: opencode mcp auth ${key}`, + variant: "warning", + duration: 8000, + }).catch((e) => log.debug("failed to show toast", { error: e })) } break } @@ -248,36 +291,38 @@ export namespace MCP { if (mcp.type === "local") { const [cmd, ...args] = mcp.command - await experimental_createMCPClient({ - name: "opencode", - transport: new StdioClientTransport({ - stderr: "ignore", - command: cmd, - args, - env: { - ...process.env, - ...(cmd === "opencode" ? { BUN_BE_BUN: "1" } : {}), - ...mcp.environment, - }, - }), + const transport = new StdioClientTransport({ + stderr: "ignore", + command: cmd, + args, + env: { + ...process.env, + ...(cmd === "opencode" ? { BUN_BE_BUN: "1" } : {}), + ...mcp.environment, + }, }) - .then((client) => { - mcpClient = client - status = { - status: "connected", - } + + try { + const client = new Client({ + name: "opencode", + version: Installation.VERSION, }) - .catch((error) => { - log.error("local mcp startup failed", { - key, - command: mcp.command, - error: error instanceof Error ? error.message : String(error), - }) - status = { - status: "failed" as const, - error: error instanceof Error ? error.message : String(error), - } + await client.connect(transport) + mcpClient = client + status = { + status: "connected", + } + } catch (error) { + log.error("local mcp startup failed", { + key, + command: mcp.command, + error: error instanceof Error ? error.message : String(error), }) + status = { + status: "failed" as const, + error: error instanceof Error ? error.message : String(error), + } + } } if (!status) { @@ -294,7 +339,7 @@ export namespace MCP { } } - const result = await withTimeout(mcpClient.tools(), mcp.timeout ?? 5000).catch((err) => { + const result = await withTimeout(mcpClient.listTools(), mcp.timeout ?? 5000).catch((err) => { log.error("failed to get tools from client", { key, error: err }) return undefined }) @@ -317,7 +362,7 @@ export namespace MCP { } } - log.info("create() successfully created client", { key, toolCount: Object.keys(result).length }) + log.info("create() successfully created client", { key, toolCount: result.tools.length }) return { mcpClient, status, @@ -392,7 +437,7 @@ export namespace MCP { continue } - const tools = await client.tools().catch((e) => { + const toolsResult = await client.listTools().catch((e) => { log.error("failed to get tools", { clientName, error: e.message }) const failedStatus = { status: "failed" as const, @@ -400,14 +445,15 @@ export namespace MCP { } s.status[clientName] = failedStatus delete s.clients[clientName] + return undefined }) - if (!tools) { + if (!toolsResult) { continue } - for (const [toolName, tool] of Object.entries(tools)) { + for (const mcpTool of toolsResult.tools) { const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_") - const sanitizedToolName = toolName.replace(/[^a-zA-Z0-9_-]/g, "_") - result[sanitizedClientName + "_" + sanitizedToolName] = tool + const sanitizedToolName = mcpTool.name.replace(/[^a-zA-Z0-9_-]/g, "_") + result[sanitizedClientName + "_" + sanitizedToolName] = convertMcpTool(mcpTool, client) } } return result @@ -469,10 +515,11 @@ export namespace MCP { // Try to connect - this will trigger the OAuth flow try { - await experimental_createMCPClient({ + const client = new Client({ name: "opencode", - transport, + version: Installation.VERSION, }) + await client.connect(transport) // If we get here, we're already authenticated return { authorizationUrl: "" } } catch (error) { @@ -592,4 +639,16 @@ export namespace MCP { const entry = await McpAuth.get(mcpName) return !!entry?.tokens } + + export type AuthStatus = "authenticated" | "expired" | "not_authenticated" + + /** + * Get the authentication status for an MCP server. + */ + export async function getAuthStatus(mcpName: string): Promise<AuthStatus> { + const hasTokens = await hasStoredTokens(mcpName) + if (!hasTokens) return "not_authenticated" + const expired = await McpAuth.isTokenExpired(mcpName) + return expired ? "expired" : "authenticated" + } } diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index b8d4dadbd..b11ca9368 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -25,6 +25,15 @@ import { createOpenAI } from "@ai-sdk/openai" import { createOpenAICompatible } from "@ai-sdk/openai-compatible" import { createOpenRouter, type LanguageModelV2 } from "@openrouter/ai-sdk-provider" import { createOpenaiCompatible as createGitHubCopilotOpenAICompatible } from "./sdk/openai-compatible/src" +import { createXai } from "@ai-sdk/xai" +import { createMistral } from "@ai-sdk/mistral" +import { createGroq } from "@ai-sdk/groq" +import { createDeepInfra } from "@ai-sdk/deepinfra" +import { createCerebras } from "@ai-sdk/cerebras" +import { createCohere } from "@ai-sdk/cohere" +import { createGateway } from "@ai-sdk/gateway" +import { createTogetherAI } from "@ai-sdk/togetherai" +import { createPerplexity } from "@ai-sdk/perplexity" export namespace Provider { const log = Log.create({ service: "provider" }) @@ -39,6 +48,15 @@ export namespace Provider { "@ai-sdk/openai": createOpenAI, "@ai-sdk/openai-compatible": createOpenAICompatible, "@openrouter/ai-sdk-provider": createOpenRouter, + "@ai-sdk/xai": createXai, + "@ai-sdk/mistral": createMistral, + "@ai-sdk/groq": createGroq, + "@ai-sdk/deepinfra": createDeepInfra, + "@ai-sdk/cerebras": createCerebras, + "@ai-sdk/cohere": createCohere, + "@ai-sdk/gateway": createGateway, + "@ai-sdk/togetherai": createTogetherAI, + "@ai-sdk/perplexity": createPerplexity, // @ts-ignore (TODO: kill this code so we dont have to maintain it) "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible, } @@ -67,6 +85,8 @@ export namespace Provider { const env = Env.all() if (input.env.some((item) => env[item])) return true if (await Auth.get(input.id)) return true + const config = await Config.get() + if (config.provider?.["opencode"]?.options?.apiKey) return true return false })() diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 606cf2d43..d86fe9022 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -75,11 +75,9 @@ export namespace ProviderTransform { } if ( - model.providerID === "deepseek" || - model.api.id.toLowerCase().includes("deepseek") || - (model.capabilities.interleaved && - typeof model.capabilities.interleaved === "object" && - model.capabilities.interleaved.field === "reasoning_content") + model.capabilities.interleaved && + typeof model.capabilities.interleaved === "object" && + model.capabilities.interleaved.field === "reasoning_content" ) { return msgs.map((msg) => { if (msg.role === "assistant" && Array.isArray(msg.content)) { @@ -218,24 +216,28 @@ export namespace ProviderTransform { if (id.includes("claude")) return undefined if (id.includes("gemini-3-pro")) return 1.0 if (id.includes("glm-4.6")) return 1.0 + if (id.includes("glm-4.7")) return 1.0 if (id.includes("minimax-m2")) return 1.0 - // if (id.includes("kimi-k2")) { - // if (id.includes("thinking")) return 1.0 - // return 0.6 - // } + if (id.includes("kimi-k2")) { + if (id.includes("thinking")) return 1.0 + return 0.6 + } return undefined } export function topP(model: Provider.Model) { const id = model.id.toLowerCase() if (id.includes("qwen")) return 1 - if (id.includes("minimax-m2")) return 0.95 + if (id.includes("minimax-m2")) { + if (id.includes("m2.1")) return 0.9 + return 0.95 + } return undefined } export function topK(model: Provider.Model) { const id = model.id.toLowerCase() - if (id.includes("minimax-m2")) return 40 + if (id.includes("minimax-m2")) return 20 return undefined } @@ -426,6 +428,10 @@ export namespace ProviderTransform { result.required = result.required.filter((field: any) => field in result.properties) } + if (result.type === "array" && result.items == null) { + result.items = {} + } + return result } diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 34e5c0623..c74dbbb41 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -47,6 +47,7 @@ import { SessionStatus } from "@/session/status" import { upgradeWebSocket, websocket } from "hono/bun" import { errors } from "./error" import { Pty } from "@/pty" +import { Installation } from "@/installation" // @ts-ignore This global is needed to prevent ai-sdk from logging warnings to stdout https://github.com/vercel/ai/blob/2dc67e0ef538307f21368db32d5a12345d98831b/packages/ai/src/logger/log-warnings.ts#L85 globalThis.AI_SDK_LOG_WARNINGS = false @@ -96,6 +97,27 @@ export namespace Server { } }) .use(cors()) + .get( + "/global/health", + describeRoute({ + summary: "Get health", + description: "Get health information about the OpenCode server.", + operationId: "global.health", + responses: { + 200: { + description: "Health information", + content: { + "application/json": { + schema: resolver(z.object({ healthy: z.literal(true), version: z.string() })), + }, + }, + }, + }, + }), + async (c) => { + return c.json({ healthy: true, version: Installation.VERSION }) + }, + ) .get( "/global/event", describeRoute({ @@ -1054,17 +1076,18 @@ export namespace Server { z.object({ providerID: z.string(), modelID: z.string(), + auto: z.boolean().optional().default(false), }), ), async (c) => { const sessionID = c.req.valid("param").sessionID const body = c.req.valid("json") const msgs = await Session.messages({ sessionID }) - let currentAgent = "build" + let currentAgent = await Agent.defaultAgent() for (let i = msgs.length - 1; i >= 0; i--) { const info = msgs[i].info if (info.role === "user") { - currentAgent = info.agent || "build" + currentAgent = info.agent || (await Agent.defaultAgent()) break } } @@ -1075,7 +1098,7 @@ export namespace Server { providerID: body.providerID, modelID: body.modelID, }, - auto: false, + auto: body.auto, }) await SessionPrompt.loop(sessionID) return c.json(true) @@ -1188,6 +1211,79 @@ export namespace Server { return c.json(message) }, ) + .delete( + "/session/:sessionID/message/:messageID/part/:partID", + describeRoute({ + description: "Delete a part from a message", + operationId: "part.delete", + responses: { + 200: { + description: "Successfully deleted part", + content: { + "application/json": { + schema: resolver(z.boolean()), + }, + }, + }, + ...errors(400, 404), + }, + }), + validator( + "param", + z.object({ + sessionID: z.string().meta({ description: "Session ID" }), + messageID: z.string().meta({ description: "Message ID" }), + partID: z.string().meta({ description: "Part ID" }), + }), + ), + async (c) => { + const params = c.req.valid("param") + await Session.removePart({ + sessionID: params.sessionID, + messageID: params.messageID, + partID: params.partID, + }) + return c.json(true) + }, + ) + .patch( + "/session/:sessionID/message/:messageID/part/:partID", + describeRoute({ + description: "Update a part in a message", + operationId: "part.update", + responses: { + 200: { + description: "Successfully updated part", + content: { + "application/json": { + schema: resolver(MessageV2.Part), + }, + }, + }, + ...errors(400, 404), + }, + }), + validator( + "param", + z.object({ + sessionID: z.string().meta({ description: "Session ID" }), + messageID: z.string().meta({ description: "Message ID" }), + partID: z.string().meta({ description: "Part ID" }), + }), + ), + validator("json", MessageV2.Part), + async (c) => { + const params = c.req.valid("param") + const body = c.req.valid("json") + if (body.id !== params.partID || body.messageID !== params.messageID || body.sessionID !== params.sessionID) { + throw new Error( + `Part mismatch: body.id='${body.id}' vs partID='${params.partID}', body.messageID='${body.messageID}' vs messageID='${params.messageID}', body.sessionID='${body.sessionID}' vs sessionID='${params.sessionID}'`, + ) + } + const part = await Session.updatePart(body) + return c.json(part) + }, + ) .post( "/session/:sessionID/message", describeRoute({ @@ -2504,10 +2600,10 @@ export namespace Server { }, ) .all("/*", async (c) => { - return proxy(`https://desktop.opencode.ai${c.req.path}`, { + return proxy(`https://app.opencode.ai${c.req.path}`, { ...c.req, headers: { - host: "desktop.opencode.ai", + host: "app.opencode.ai", }, }) }), @@ -2528,13 +2624,19 @@ export namespace Server { } export function listen(opts: { port: number; hostname: string }) { - const server = Bun.serve({ - port: opts.port, + const args = { hostname: opts.hostname, idleTimeout: 0, fetch: App().fetch, websocket: websocket, - }) - return server + } as const + if (opts.port === 0) { + try { + return Bun.serve({ ...args, port: 4096 }) + } catch { + // port 4096 not available, fall through to use port 0 + } + } + return Bun.serve({ ...args, port: opts.port }) } } diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index 3e4c8020d..339ba2f42 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -40,6 +40,8 @@ export namespace SessionCompaction { export const PRUNE_MINIMUM = 20_000 export const PRUNE_PROTECT = 40_000 + const PRUNE_PROTECTED_TOOLS = ["skill"] + // goes backwards through parts until there are 40_000 tokens worth of tool // calls. then erases output of previous tool calls. idea is to throw away old // tool calls that are no longer relevant. @@ -61,6 +63,8 @@ export namespace SessionCompaction { const part = msg.parts[partIndex] if (part.type === "tool") if (part.state.status === "completed") { + if (PRUNE_PROTECTED_TOOLS.includes(part.tool)) continue + if (part.state.time.compacted) break loop const estimate = Token.estimate(part.state.output) total += estimate @@ -126,12 +130,15 @@ export namespace SessionCompaction { model, abort: input.abort, }) - // Allow plugins to inject context for compaction + // Allow plugins to inject context or replace compaction prompt const compacting = await Plugin.trigger( "experimental.session.compacting", { sessionID: input.sessionID }, - { context: [] }, + { context: [], prompt: undefined }, ) + const defaultPrompt = + "Provide a detailed prompt for continuing our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next considering new session will not have access to our conversation." + const promptText = compacting.prompt ?? [defaultPrompt, ...compacting.context].join("\n\n") const result = await processor.process({ user: userMessage, agent, @@ -146,10 +153,7 @@ export namespace SessionCompaction { content: [ { type: "text", - text: [ - "Provide a detailed prompt for continuing our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next considering new session will not have access to our conversation.", - ...compacting.context, - ].join("\n\n"), + text: promptText, }, ], }, diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts index b1a193904..4285223bc 100644 --- a/packages/opencode/src/session/index.ts +++ b/packages/opencode/src/session/index.ts @@ -339,6 +339,23 @@ export namespace Session { }, ) + export const removePart = fn( + z.object({ + sessionID: Identifier.schema("session"), + messageID: Identifier.schema("message"), + partID: Identifier.schema("part"), + }), + async (input) => { + await Storage.remove(["part", input.messageID, input.partID]) + Bus.publish(MessageV2.Event.PartRemoved, { + sessionID: input.sessionID, + messageID: input.messageID, + partID: input.partID, + }) + return input.partID + }, + ) + const UpdatePartInput = z.union([ MessageV2.Part, z.object({ diff --git a/packages/opencode/src/session/processor.ts b/packages/opencode/src/session/processor.ts index 1d4d24303..78871630c 100644 --- a/packages/opencode/src/session/processor.ts +++ b/packages/opencode/src/session/processor.ts @@ -365,6 +365,20 @@ export namespace SessionProcessor { error: input.assistantMessage.error, }) } + if (snapshot) { + const patch = await Snapshot.patch(snapshot) + if (patch.files.length) { + await Session.updatePart({ + id: Identifier.ascending("part"), + messageID: input.assistantMessage.id, + sessionID: input.sessionID, + type: "patch", + hash: patch.hash, + files: patch.files, + }) + } + snapshot = undefined + } const p = await MessageV2.parts(input.assistantMessage.id) for (const part of p) { if (part.type === "tool" && part.state.status !== "completed" && part.state.status !== "error") { diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index cbb3eedf3..fabe3fa51 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -583,7 +583,7 @@ export namespace SessionPrompt { mergeDeep(await ToolRegistry.enabled(input.agent)), mergeDeep(input.tools ?? {}), ) - for (const item of await ToolRegistry.tools(input.model.providerID)) { + for (const item of await ToolRegistry.tools(input.model.providerID, input.agent)) { if (Wildcard.all(item.id, enabledTools) === false) continue const schema = ProviderTransform.schema(input.model, z.toJSONSchema(item.parameters)) tools[item.id] = tool({ @@ -715,7 +715,7 @@ export namespace SessionPrompt { } async function createUserMessage(input: PromptInput) { - const agent = await Agent.get(input.agent ?? "build") + const agent = await Agent.get(input.agent ?? (await Agent.defaultAgent())) const info: MessageV2.Info = { id: input.messageID ?? Identifier.ascending("message"), role: "user", @@ -1282,7 +1282,7 @@ export namespace SessionPrompt { export async function command(input: CommandInput) { log.info("command", input) const command = await Command.get(input.command) - const agentName = command.agent ?? input.agent ?? "build" + const agentName = command.agent ?? input.agent ?? (await Agent.defaultAgent()) const raw = input.arguments.match(argsRegex) ?? [] const args = raw.map((arg) => arg.replace(quoteTrimRegex, "")) @@ -1309,7 +1309,7 @@ export namespace SessionPrompt { const results = await Promise.all( shell.map(async ([, cmd]) => { try { - return await $`${{ raw: cmd }}`.nothrow().text() + return await $`${{ raw: cmd }}`.quiet().nothrow().text() } catch (error) { return `Error executing command: ${error instanceof Error ? error.message : String(error)}` } @@ -1425,7 +1425,7 @@ export namespace SessionPrompt { time: { created: Date.now(), }, - agent: input.message.info.role === "user" ? input.message.info.agent : "build", + agent: input.message.info.role === "user" ? input.message.info.agent : await Agent.defaultAgent(), model: { providerID: input.providerID, modelID: input.modelID, diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index e15185b38..300943881 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -2,6 +2,7 @@ import { Ripgrep } from "../file/ripgrep" import { Global } from "../global" import { Filesystem } from "../util/filesystem" import { Config } from "../config/config" +import { Skill } from "../skill" import { Instance } from "../project/instance" import path from "path" @@ -13,7 +14,6 @@ import PROMPT_POLARIS from "./prompt/polaris.txt" import PROMPT_BEAST from "./prompt/beast.txt" import PROMPT_GEMINI from "./prompt/gemini.txt" import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt" -import PROMPT_COMPACTION from "./prompt/compaction.txt" import PROMPT_CODEX from "./prompt/codex.txt" import type { Provider } from "@/provider/provider" diff --git a/packages/opencode/src/skill/index.ts b/packages/opencode/src/skill/index.ts new file mode 100644 index 000000000..67bef3bd3 --- /dev/null +++ b/packages/opencode/src/skill/index.ts @@ -0,0 +1 @@ +export * from "./skill" diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts new file mode 100644 index 000000000..41df88f8b --- /dev/null +++ b/packages/opencode/src/skill/skill.ts @@ -0,0 +1,83 @@ +import z from "zod" +import { Config } from "../config/config" +import { Instance } from "../project/instance" +import { NamedError } from "@opencode-ai/util/error" +import { ConfigMarkdown } from "../config/markdown" +import { Log } from "../util/log" + +export namespace Skill { + const log = Log.create({ service: "skill" }) + export const Info = z.object({ + name: z.string(), + description: z.string(), + location: z.string(), + }) + export type Info = z.infer<typeof Info> + + export const InvalidError = NamedError.create( + "SkillInvalidError", + z.object({ + path: z.string(), + message: z.string().optional(), + issues: z.custom<z.core.$ZodIssue[]>().optional(), + }), + ) + + export const NameMismatchError = NamedError.create( + "SkillNameMismatchError", + z.object({ + path: z.string(), + expected: z.string(), + actual: z.string(), + }), + ) + + const SKILL_GLOB = new Bun.Glob("skill/**/SKILL.md") + + export const state = Instance.state(async () => { + const directories = await Config.directories() + const skills: Record<string, Info> = {} + + for (const dir of directories) { + for await (const match of SKILL_GLOB.scan({ + cwd: dir, + absolute: true, + onlyFiles: true, + followSymlinks: true, + })) { + const md = await ConfigMarkdown.parse(match) + if (!md) { + continue + } + + const parsed = Info.pick({ name: true, description: true }).safeParse(md.data) + if (!parsed.success) continue + + // Warn on duplicate skill names + if (skills[parsed.data.name]) { + log.warn("duplicate skill name", { + name: parsed.data.name, + existing: skills[parsed.data.name].location, + duplicate: match, + }) + } + + skills[parsed.data.name] = { + name: parsed.data.name, + description: parsed.data.description, + location: match, + } + } + } + + return skills + }) + + export async function get(name: string) { + return state().then((x) => x[name]) + } + + export async function all() { + return state().then((x) => Object.values(x)) + } +} diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 99af448ba..d73bc1616 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -49,7 +49,8 @@ export const GrepTool = Tool.define("grep", { throw new Error(`ripgrep failed: ${errorOutput}`) } - const lines = output.trim().split("\n") + // Handle both Unix (\n) and Windows (\r\n) line endings + const lines = output.trim().split(/\r?\n/) const matches = [] for (const line of lines) { diff --git a/packages/opencode/src/tool/lsp.ts b/packages/opencode/src/tool/lsp.ts new file mode 100644 index 000000000..2a15ed7e3 --- /dev/null +++ b/packages/opencode/src/tool/lsp.ts @@ -0,0 +1,87 @@ +import z from "zod" +import { Tool } from "./tool" +import path from "path" +import { LSP } from "../lsp" +import DESCRIPTION from "./lsp.txt" +import { Instance } from "../project/instance" +import { pathToFileURL } from "url" + +const operations = [ + "goToDefinition", + "findReferences", + "hover", + "documentSymbol", + "workspaceSymbol", + "goToImplementation", + "prepareCallHierarchy", + "incomingCalls", + "outgoingCalls", +] as const + +export const LspTool = Tool.define("lsp", { + description: DESCRIPTION, + parameters: z.object({ + operation: z.enum(operations).describe("The LSP operation to perform"), + filePath: z.string().describe("The absolute or relative path to the file"), + line: z.number().int().min(1).describe("The line number (1-based, as shown in editors)"), + character: z.number().int().min(1).describe("The character offset (1-based, as shown in editors)"), + }), + execute: async (args) => { + const file = path.isAbsolute(args.filePath) ? args.filePath : path.join(Instance.directory, args.filePath) + const uri = pathToFileURL(file).href + const position = { + file, + line: args.line - 1, + character: args.character - 1, + } + + const relPath = path.relative(Instance.worktree, file) + const title = `${args.operation} ${relPath}:${args.line}:${args.character}` + + const exists = await Bun.file(file).exists() + if (!exists) { + throw new Error(`File not found: ${file}`) + } + + const available = await LSP.hasClients(file) + if (!available) { + throw new Error("No LSP server available for this file type.") + } + + await LSP.touchFile(file, true) + + const result: unknown[] = await (async () => { + switch (args.operation) { + case "goToDefinition": + return LSP.definition(position) + case "findReferences": + return LSP.references(position) + case "hover": + return LSP.hover(position) + case "documentSymbol": + return LSP.documentSymbol(uri) + case "workspaceSymbol": + return LSP.workspaceSymbol("") + case "goToImplementation": + return LSP.implementation(position) + case "prepareCallHierarchy": + return LSP.prepareCallHierarchy(position) + case "incomingCalls": + return LSP.incomingCalls(position) + case "outgoingCalls": + return LSP.outgoingCalls(position) + } + })() + + const output = (() => { + if (result.length === 0) return `No results found for ${args.operation}` + return JSON.stringify(result, null, 2) + })() + + return { + title, + metadata: { result }, + output, + } + }, +}) diff --git a/packages/opencode/src/tool/lsp.txt b/packages/opencode/src/tool/lsp.txt new file mode 100644 index 000000000..5a50a571b --- /dev/null +++ b/packages/opencode/src/tool/lsp.txt @@ -0,0 +1,19 @@ +Interact with Language Server Protocol (LSP) servers to get code intelligence features. + +Supported operations: +- goToDefinition: Find where a symbol is defined +- findReferences: Find all references to a symbol +- hover: Get hover information (documentation, type info) for a symbol +- documentSymbol: Get all symbols (functions, classes, variables) in a document +- workspaceSymbol: Search for symbols across the entire workspace +- goToImplementation: Find implementations of an interface or abstract method +- prepareCallHierarchy: Get call hierarchy item at a position (functions/methods) +- incomingCalls: Find all functions/methods that call the function at a position +- outgoingCalls: Find all functions/methods called by the function at a position + +All operations require: +- filePath: The file to operate on +- line: The line number (1-based, as shown in editors) +- character: The character offset (1-based, as shown in editors) + +Note: LSP servers must be configured for the file type. If no server is available, an error will be returned. diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 27426ad24..fd81c4864 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -60,10 +60,12 @@ export const ReadTool = Tool.define("read", { } const block = iife(() => { - const whitelist = [".env.sample", ".example"] + const basename = path.basename(filepath) + const whitelist = [".env.sample", ".env.example", ".example", ".env.template"] - if (whitelist.some((w) => filepath.endsWith(w))) return false - if (filepath.includes(".env")) return true + if (whitelist.some((w) => basename.endsWith(w))) return false + // Block .env, .env.local, .env.production, etc. but not .envrc + if (/^\.env(\.|$)/.test(basename)) return true return false }) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index 647c74267..69a45432d 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -10,6 +10,7 @@ import { TodoWriteTool, TodoReadTool } from "./todo" import { WebFetchTool } from "./webfetch" import { WriteTool } from "./write" import { InvalidTool } from "./invalid" +import { SkillTool } from "./skill" import type { Agent } from "../agent/agent" import { Tool } from "./tool" import { Instance } from "../project/instance" @@ -22,6 +23,7 @@ import { WebSearchTool } from "./websearch" import { CodeSearchTool } from "./codesearch" import { Flag } from "@/flag/flag" import { Log } from "@/util/log" +import { LspTool } from "./lsp" export namespace ToolRegistry { const log = Log.create({ service: "tool.registry" }) @@ -102,6 +104,8 @@ export namespace ToolRegistry { TodoReadTool, WebSearchTool, CodeSearchTool, + SkillTool, + ...(Flag.OPENCODE_EXPERIMENTAL_LSP_TOOL ? [LspTool] : []), ...(config.experimental?.batch_tool === true ? [BatchTool] : []), ...custom, ] @@ -111,7 +115,7 @@ export namespace ToolRegistry { return all().then((x) => x.map((t) => t.id)) } - export async function tools(providerID: string) { + export async function tools(providerID: string, agent?: Agent.Info) { const tools = await all() const result = await Promise.all( tools @@ -126,7 +130,7 @@ export namespace ToolRegistry { using _ = log.time(t.id) return { id: t.id, - ...(await t.init()), + ...(await t.init({ agent })), } }), ) @@ -148,6 +152,10 @@ export namespace ToolRegistry { result["codesearch"] = false result["websearch"] = false } + // Disable skill tool if all skills are denied + if (agent.permission.skill["*"] === "deny" && Object.keys(agent.permission.skill).length === 1) { + result["skill"] = false + } return result } diff --git a/packages/opencode/src/tool/skill.ts b/packages/opencode/src/tool/skill.ts new file mode 100644 index 000000000..2503f7639 --- /dev/null +++ b/packages/opencode/src/tool/skill.ts @@ -0,0 +1,100 @@ +import path from "path" +import z from "zod" +import { Tool } from "./tool" +import { Skill } from "../skill" +import { Agent } from "../agent/agent" +import { Permission } from "../permission" +import { Wildcard } from "../util/wildcard" +import { ConfigMarkdown } from "../config/markdown" + +const parameters = z.object({ + name: z.string().describe("The skill identifier from available_skills (e.g., 'code-review')"), +}) + +export const SkillTool: Tool.Info<typeof parameters> = { + id: "skill", + async init(ctx) { + const skills = await Skill.all() + + // Filter skills by agent permissions if agent provided + let accessibleSkills = skills + if (ctx?.agent) { + const permissions = ctx.agent.permission.skill + accessibleSkills = skills.filter((skill) => { + const action = Wildcard.all(skill.name, permissions) + return action !== "deny" + }) + } + + return { + description: [ + "Load a skill to get detailed instructions for a specific task.", + "Skills provide specialized knowledge and step-by-step guidance.", + "Use this when a task matches an available skill's description.", + "<available_skills>", + ...accessibleSkills.flatMap((skill) => [ + ` <skill>`, + ` <name>${skill.name}</name>`, + ` <description>${skill.description}</description>`, + ` </skill>`, + ]), + "</available_skills>", + ].join(" "), + parameters, + async execute(params, ctx) { + const agent = await Agent.get(ctx.agent) + + const skill = await Skill.get(params.name) + + if (!skill) { + const available = await Skill.all().then((x) => x.map((s) => s.name).join(", ")) + throw new Error(`Skill "${params.name}" not found. Available skills: ${available || "none"}`) + } + + // Check permission using Wildcard.all on the skill name + const permissions = agent.permission.skill + const action = Wildcard.all(params.name, permissions) + + if (action === "deny") { + throw new Permission.RejectedError( + ctx.sessionID, + "skill", + ctx.callID, + { skill: params.name }, + `Access to skill "${params.name}" is denied for agent "${agent.name}".`, + ) + } + + if (action === "ask") { + await Permission.ask({ + type: "skill", + pattern: params.name, + sessionID: ctx.sessionID, + messageID: ctx.messageID, + callID: ctx.callID, + title: `Load skill: ${skill.name}`, + metadata: { name: skill.name, description: skill.description }, + }) + } + + // Load and parse skill content + const parsed = await ConfigMarkdown.parse(skill.location) + const dir = path.dirname(skill.location) + + // Format output similar to plugin pattern + const output = [`## Skill: ${skill.name}`, "", `**Base directory**: ${dir}`, "", parsed.content.trim()].join( + "\n", + ) + + return { + title: `Loaded skill: ${skill.name}`, + output, + metadata: { + name: skill.name, + dir, + }, + } + }, + } + }, +} diff --git a/packages/opencode/src/tool/tool.ts b/packages/opencode/src/tool/tool.ts index 80b6abe8c..acee24902 100644 --- a/packages/opencode/src/tool/tool.ts +++ b/packages/opencode/src/tool/tool.ts @@ -1,11 +1,16 @@ import z from "zod" import type { MessageV2 } from "../session/message-v2" +import type { Agent } from "../agent/agent" export namespace Tool { interface Metadata { [key: string]: any } + export interface InitContext { + agent?: Agent.Info + } + export type Context<M extends Metadata = Metadata> = { sessionID: string messageID: string @@ -17,7 +22,7 @@ export namespace Tool { } export interface Info<Parameters extends z.ZodType = z.ZodType, M extends Metadata = Metadata> { id: string - init: () => Promise<{ + init: (ctx?: InitContext) => Promise<{ description: string parameters: Parameters execute( @@ -42,8 +47,8 @@ export namespace Tool { ): Info<Parameters, Result> { return { id, - init: async () => { - const toolInfo = init instanceof Function ? await init() : init + init: async (ctx) => { + const toolInfo = init instanceof Function ? await init(ctx) : init const execute = toolInfo.execute toolInfo.execute = (args, ctx) => { try { diff --git a/packages/opencode/test/agent/agent.test.ts b/packages/opencode/test/agent/agent.test.ts new file mode 100644 index 000000000..222bf8367 --- /dev/null +++ b/packages/opencode/test/agent/agent.test.ts @@ -0,0 +1,146 @@ +import { test, expect } from "bun:test" +import path from "path" +import fs from "fs/promises" +import { tmpdir } from "../fixture/fixture" +import { Instance } from "../../src/project/instance" +import { Agent } from "../../src/agent/agent" + +test("loads built-in agents when no custom agents configured", async () => { + await using tmp = await tmpdir() + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const agents = await Agent.list() + const names = agents.map((a) => a.name) + expect(names).toContain("build") + expect(names).toContain("plan") + }, + }) +}) + +test("custom subagent works alongside built-in primary agents", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + const opencodeDir = path.join(dir, ".opencode") + await fs.mkdir(opencodeDir, { recursive: true }) + const agentDir = path.join(opencodeDir, "agent") + await fs.mkdir(agentDir, { recursive: true }) + + await Bun.write( + path.join(agentDir, "helper.md"), + `--- +model: test/model +mode: subagent +--- +Helper subagent prompt`, + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const agents = await Agent.list() + const helper = agents.find((a) => a.name === "helper") + expect(helper).toBeDefined() + expect(helper?.mode).toBe("subagent") + + // Built-in primary agents should still exist + const build = agents.find((a) => a.name === "build") + expect(build).toBeDefined() + expect(build?.mode).toBe("primary") + }, + }) +}) + +test("throws error when all primary agents are disabled", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + agent: { + build: { disable: true }, + plan: { disable: true }, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + try { + await Agent.list() + expect(true).toBe(false) // should not reach here + } catch (e: any) { + expect(e.data?.message).toContain("No primary agents are available") + } + }, + }) +}) + +test("does not throw when at least one primary agent remains", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + agent: { + build: { disable: true }, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const agents = await Agent.list() + const plan = agents.find((a) => a.name === "plan") + expect(plan).toBeDefined() + expect(plan?.mode).toBe("primary") + }, + }) +}) + +test("custom primary agent satisfies requirement when built-ins disabled", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + const opencodeDir = path.join(dir, ".opencode") + await fs.mkdir(opencodeDir, { recursive: true }) + const agentDir = path.join(opencodeDir, "agent") + await fs.mkdir(agentDir, { recursive: true }) + + await Bun.write( + path.join(agentDir, "custom.md"), + `--- +model: test/model +mode: primary +--- +Custom primary agent`, + ) + + await Bun.write( + path.join(dir, "opencode.json"), + JSON.stringify({ + $schema: "https://opencode.ai/config.json", + agent: { + build: { disable: true }, + plan: { disable: true }, + }, + }), + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const agents = await Agent.list() + const custom = agents.find((a) => a.name === "custom") + expect(custom).toBeDefined() + expect(custom?.mode).toBe("primary") + }, + }) +}) diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 2ff8c01cd..6f43cab61 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -450,6 +450,38 @@ test("merges plugin arrays from global and local configs", async () => { }) }) +test("does not error when only custom agent is a subagent", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + const opencodeDir = path.join(dir, ".opencode") + await fs.mkdir(opencodeDir, { recursive: true }) + const agentDir = path.join(opencodeDir, "agent") + await fs.mkdir(agentDir, { recursive: true }) + + await Bun.write( + path.join(agentDir, "helper.md"), + `--- +model: test/model +mode: subagent +--- +Helper subagent prompt`, + ) + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const config = await Config.get() + expect(config.agent?.["helper"]).toEqual({ + name: "helper", + model: "test/model", + mode: "subagent", + prompt: "Helper subagent prompt", + }) + }, + }) +}) + test("deduplicates duplicate plugins from global and local configs", async () => { await using tmp = await tmpdir({ init: async (dir) => { diff --git a/packages/opencode/test/preload.ts b/packages/opencode/test/preload.ts index 08316a23f..b6b6a66cf 100644 --- a/packages/opencode/test/preload.ts +++ b/packages/opencode/test/preload.ts @@ -3,9 +3,14 @@ import os from "os" import path from "path" import fs from "fs/promises" +import fsSync from "fs" +import { afterAll } from "bun:test" const dir = path.join(os.tmpdir(), "opencode-test-data-" + process.pid) await fs.mkdir(dir, { recursive: true }) +afterAll(() => { + fsSync.rmSync(dir, { recursive: true, force: 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") diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 2d8dc5506..78bd296c9 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -167,6 +167,30 @@ describe("ProviderTransform.maxOutputTokens", () => { }) }) +describe("ProviderTransform.schema - gemini array items", () => { + test("adds missing items for array properties", () => { + const geminiModel = { + providerID: "google", + api: { + id: "gemini-3-pro", + }, + } as any + + const schema = { + type: "object", + properties: { + nodes: { type: "array" }, + edges: { type: "array", items: { type: "string" } }, + }, + } as any + + const result = ProviderTransform.schema(geminiModel, schema) as any + + expect(result.properties.nodes.items).toBeDefined() + expect(result.properties.edges.items.type).toBe("string") + }) +}) + describe("ProviderTransform.message - DeepSeek reasoning content", () => { test("DeepSeek with tool calls includes reasoning_content in providerOptions", () => { const msgs = [ @@ -200,7 +224,9 @@ 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, + interleaved: { + field: "reasoning_content", + }, }, cost: { input: 0.001, @@ -229,58 +255,6 @@ describe("ProviderTransform.message - DeepSeek reasoning content", () => { expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBe("Let me think about this...") }) - test("DeepSeek model ID containing 'deepseek' matches (case insensitive)", () => { - const msgs = [ - { - role: "assistant", - content: [ - { type: "reasoning", text: "Thinking..." }, - { - type: "tool-call", - toolCallId: "test", - toolName: "get_weather", - input: { location: "Hangzhou" }, - }, - ], - }, - ] as any[] - - const result = ProviderTransform.message(msgs, { - id: "someprovider/deepseek-reasoner", - providerID: "someprovider", - api: { - id: "deepseek-reasoner", - url: "https://api.someprovider.com", - npm: "@ai-sdk/openai-compatible", - }, - name: "SomeProvider DeepSeek Reasoner", - capabilities: { - temperature: true, - reasoning: true, - attachment: false, - 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, - output: 0.002, - cache: { read: 0.0001, write: 0.0002 }, - }, - limit: { - context: 128000, - output: 8192, - }, - status: "active", - options: {}, - headers: {}, - release_date: "2023-04-01", - }) - - expect(result[0].providerOptions?.openaiCompatible?.reasoning_content).toBe("Thinking...") - }) - test("Non-DeepSeek providers leave reasoning content unchanged", () => { const msgs = [ { diff --git a/packages/opencode/test/skill/skill.test.ts b/packages/opencode/test/skill/skill.test.ts new file mode 100644 index 000000000..4a1d75f9f --- /dev/null +++ b/packages/opencode/test/skill/skill.test.ts @@ -0,0 +1,131 @@ +import { test, expect } from "bun:test" +import { Skill } from "../../src/skill" +import { SystemPrompt } from "../../src/session/system" +import { Instance } from "../../src/project/instance" +import { tmpdir } from "../fixture/fixture" +import path from "path" + +test("discovers skills from .opencode/skill/ directory", async () => { + await using tmp = await tmpdir({ + git: true, + init: async (dir) => { + const skillDir = path.join(dir, ".opencode", "skill", "test-skill") + await Bun.write( + path.join(skillDir, "SKILL.md"), + `--- +name: test-skill +description: A test skill for verification. +--- + +# Test Skill + +Instructions here. +`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const skills = await Skill.all() + expect(skills.length).toBe(1) + expect(skills[0].name).toBe("test-skill") + expect(skills[0].description).toBe("A test skill for verification.") + expect(skills[0].location).toContain("skill/test-skill/SKILL.md") + }, + }) +}) + +test("discovers multiple skills from .opencode/skill/ directory", async () => { + await using tmp = await tmpdir({ + git: true, + init: async (dir) => { + const skillDir = path.join(dir, ".opencode", "skill", "my-skill") + await Bun.write( + path.join(skillDir, "SKILL.md"), + `--- +name: my-skill +description: Another test skill. +--- + +# My Skill +`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const skills = await Skill.all() + expect(skills.length).toBe(1) + expect(skills[0].name).toBe("my-skill") + }, + }) +}) + +test("skips skills with missing frontmatter", async () => { + await using tmp = await tmpdir({ + git: true, + init: async (dir) => { + const skillDir = path.join(dir, ".opencode", "skill", "no-frontmatter") + await Bun.write( + path.join(skillDir, "SKILL.md"), + `# No Frontmatter + +Just some content without YAML frontmatter. +`, + ) + }, + }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const skills = await Skill.all() + expect(skills).toEqual([]) + }, + }) +}) + +test("returns empty array when no skills exist", async () => { + await using tmp = await tmpdir({ git: true }) + + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const skills = await Skill.all() + expect(skills).toEqual([]) + }, + }) +}) + +// test("discovers skills from .claude/skills/ directory", async () => { +// await using tmp = await tmpdir({ +// git: true, +// init: async (dir) => { +// const skillDir = path.join(dir, ".claude", "skills", "claude-skill") +// await Bun.write( +// path.join(skillDir, "SKILL.md"), +// `--- +// name: claude-skill +// description: A skill in the .claude/skills directory. +// --- + +// # Claude Skill +// `, +// ) +// }, +// }) + +// await Instance.provide({ +// directory: tmp.path, +// fn: async () => { +// const skills = await Skill.all() +// expect(skills.length).toBe(1) +// expect(skills[0].name).toBe("claude-skill") +// expect(skills[0].location).toContain(".claude/skills/claude-skill/SKILL.md") +// }, +// }) +// }) diff --git a/packages/opencode/test/tool/grep.test.ts b/packages/opencode/test/tool/grep.test.ts new file mode 100644 index 000000000..f3da666a0 --- /dev/null +++ b/packages/opencode/test/tool/grep.test.ts @@ -0,0 +1,108 @@ +import { describe, expect, test } from "bun:test" +import path from "path" +import { GrepTool } from "../../src/tool/grep" +import { Instance } from "../../src/project/instance" +import { tmpdir } from "../fixture/fixture" + +const ctx = { + sessionID: "test", + messageID: "", + callID: "", + agent: "build", + abort: AbortSignal.any([]), + metadata: () => {}, +} + +const projectRoot = path.join(__dirname, "../..") + +describe("tool.grep", () => { + test("basic search", async () => { + await Instance.provide({ + directory: projectRoot, + fn: async () => { + const grep = await GrepTool.init() + const result = await grep.execute( + { + pattern: "export", + path: path.join(projectRoot, "src/tool"), + include: "*.ts", + }, + ctx, + ) + expect(result.metadata.matches).toBeGreaterThan(0) + expect(result.output).toContain("Found") + }, + }) + }) + + test("no matches returns correct output", async () => { + await using tmp = await tmpdir({ + init: async (dir) => { + await Bun.write(path.join(dir, "test.txt"), "hello world") + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const grep = await GrepTool.init() + const result = await grep.execute( + { + pattern: "xyznonexistentpatternxyz123", + path: tmp.path, + }, + ctx, + ) + expect(result.metadata.matches).toBe(0) + expect(result.output).toBe("No files found") + }, + }) + }) + + test("handles CRLF line endings in output", async () => { + // This test verifies the regex split handles both \n and \r\n + await using tmp = await tmpdir({ + init: async (dir) => { + // Create a test file with content + await Bun.write(path.join(dir, "test.txt"), "line1\nline2\nline3") + }, + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const grep = await GrepTool.init() + const result = await grep.execute( + { + pattern: "line", + path: tmp.path, + }, + ctx, + ) + expect(result.metadata.matches).toBeGreaterThan(0) + }, + }) + }) +}) + +describe("CRLF regex handling", () => { + test("regex correctly splits Unix line endings", () => { + const unixOutput = "file1.txt|1|content1\nfile2.txt|2|content2\nfile3.txt|3|content3" + const lines = unixOutput.trim().split(/\r?\n/) + expect(lines.length).toBe(3) + expect(lines[0]).toBe("file1.txt|1|content1") + expect(lines[2]).toBe("file3.txt|3|content3") + }) + + test("regex correctly splits Windows CRLF line endings", () => { + const windowsOutput = "file1.txt|1|content1\r\nfile2.txt|2|content2\r\nfile3.txt|3|content3" + const lines = windowsOutput.trim().split(/\r?\n/) + expect(lines.length).toBe(3) + expect(lines[0]).toBe("file1.txt|1|content1") + expect(lines[2]).toBe("file3.txt|3|content3") + }) + + test("regex handles mixed line endings", () => { + const mixedOutput = "file1.txt|1|content1\nfile2.txt|2|content2\r\nfile3.txt|3|content3" + const lines = mixedOutput.trim().split(/\r?\n/) + expect(lines.length).toBe(3) + }) +}) diff --git a/packages/opencode/test/tool/read.test.ts b/packages/opencode/test/tool/read.test.ts new file mode 100644 index 000000000..47a7aee2a --- /dev/null +++ b/packages/opencode/test/tool/read.test.ts @@ -0,0 +1,42 @@ +import { describe, expect, test } from "bun:test" +import path from "path" +import { ReadTool } from "../../src/tool/read" +import { Instance } from "../../src/project/instance" +import { tmpdir } from "../fixture/fixture" + +const ctx = { + sessionID: "test", + messageID: "", + callID: "", + agent: "build", + abort: AbortSignal.any([]), + metadata: () => {}, +} + +describe("tool.read env file blocking", () => { + test.each([ + [".env", true], + [".env.local", true], + [".env.production", true], + [".env.sample", false], + [".env.example", false], + [".envrc", false], + ["environment.ts", false], + ])("%s blocked=%s", async (filename, blocked) => { + await using tmp = await tmpdir({ + init: (dir) => Bun.write(path.join(dir, filename), "content"), + }) + await Instance.provide({ + directory: tmp.path, + fn: async () => { + const read = await ReadTool.init() + const promise = read.execute({ filePath: path.join(tmp.path, filename) }, ctx) + if (blocked) { + await expect(promise).rejects.toThrow("blocked") + } else { + expect((await promise).output).toContain("content") + } + }, + }) + }) +}) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index f8976c177..babe21271 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/plugin", - "version": "1.0.170", + "version": "1.0.191", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index 487e6ed3e..fbc0e710c 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -192,10 +192,16 @@ export interface Hooks { }, ) => Promise<void> /** - * Called before session compaction starts. Allows plugins to append - * additional context to the compaction prompt. + * Called before session compaction starts. Allows plugins to customize + * the compaction prompt. + * + * - `context`: Additional context strings appended to the default prompt + * - `prompt`: If set, replaces the default compaction prompt entirely */ - "experimental.session.compacting"?: (input: { sessionID: string }, output: { context: string[] }) => Promise<void> + "experimental.session.compacting"?: ( + input: { sessionID: string }, + output: { context: string[]; prompt?: string }, + ) => Promise<void> "experimental.text.complete"?: ( input: { sessionID: string; messageID: string; partID: string }, output: { text: string }, diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 0500ac84a..6278ebaef 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/sdk", - "version": "1.0.170", + "version": "1.0.191", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts index 964112d81..06993d3f9 100644 --- a/packages/sdk/js/src/gen/types.gen.ts +++ b/packages/sdk/js/src/gen/types.gen.ts @@ -858,6 +858,14 @@ export type KeybindsConfig = { * Navigate to last message */ messages_last?: string + /** + * Navigate to next message + */ + messages_next?: string + /** + * Navigate to previous message + */ + messages_previous?: string /** * Navigate to last user message */ diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index 16fe07ae4..97bc92b86 100644 --- a/packages/sdk/js/src/v2/gen/sdk.gen.ts +++ b/packages/sdk/js/src/v2/gen/sdk.gen.ts @@ -30,6 +30,7 @@ import type { FormatterStatusResponses, GlobalDisposeResponses, GlobalEventResponses, + GlobalHealthResponses, InstanceDisposeResponses, LspStatusResponses, McpAddErrors, @@ -47,6 +48,11 @@ import type { McpLocalConfig, McpRemoteConfig, McpStatusResponses, + Part as Part2, + PartDeleteErrors, + PartDeleteResponses, + PartUpdateErrors, + PartUpdateResponses, PathGetResponses, PermissionRespondErrors, PermissionRespondResponses, @@ -183,6 +189,18 @@ class HeyApiRegistry<T> { } export class Global extends HeyApiClient { + /** + * Get health + * + * Get health information about the OpenCode server. + */ + public health<ThrowOnError extends boolean = false>(options?: Options<never, ThrowOnError>) { + return (options?.client ?? this.client).get<GlobalHealthResponses, unknown, ThrowOnError>({ + url: "/global/health", + ...options, + }) + } + /** * Get global events * @@ -1127,6 +1145,7 @@ export class Session extends HeyApiClient { directory?: string providerID?: string modelID?: string + auto?: boolean }, options?: Options<never, ThrowOnError>, ) { @@ -1139,6 +1158,7 @@ export class Session extends HeyApiClient { { in: "query", key: "directory" }, { in: "body", key: "providerID" }, { in: "body", key: "modelID" }, + { in: "body", key: "auto" }, ], }, ], @@ -1486,6 +1506,79 @@ export class Session extends HeyApiClient { } } +export class Part extends HeyApiClient { + /** + * Delete a part from a message + */ + public delete<ThrowOnError extends boolean = false>( + parameters: { + sessionID: string + messageID: string + partID: string + directory?: string + }, + options?: Options<never, ThrowOnError>, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "path", key: "messageID" }, + { in: "path", key: "partID" }, + { in: "query", key: "directory" }, + ], + }, + ], + ) + return (options?.client ?? this.client).delete<PartDeleteResponses, PartDeleteErrors, ThrowOnError>({ + url: "/session/{sessionID}/message/{messageID}/part/{partID}", + ...options, + ...params, + }) + } + + /** + * Update a part in a message + */ + public update<ThrowOnError extends boolean = false>( + parameters: { + sessionID: string + messageID: string + partID: string + directory?: string + part?: Part2 + }, + options?: Options<never, ThrowOnError>, + ) { + const params = buildClientParams( + [parameters], + [ + { + args: [ + { in: "path", key: "sessionID" }, + { in: "path", key: "messageID" }, + { in: "path", key: "partID" }, + { in: "query", key: "directory" }, + { key: "part", map: "body" }, + ], + }, + ], + ) + return (options?.client ?? this.client).patch<PartUpdateResponses, PartUpdateErrors, ThrowOnError>({ + url: "/session/{sessionID}/message/{messageID}/part/{partID}", + ...options, + ...params, + headers: { + "Content-Type": "application/json", + ...options?.headers, + ...params.headers, + }, + }) + } +} + export class Permission extends HeyApiClient { /** * Respond to permission @@ -2588,6 +2681,8 @@ export class OpencodeClient extends HeyApiClient { session = new Session({ client: this.client }) + part = new Part({ client: this.client }) + permission = new Permission({ client: this.client }) command = new Command({ client: this.client }) diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index c96530737..1372765e3 100644 --- a/packages/sdk/js/src/v2/gen/types.gen.ts +++ b/packages/sdk/js/src/v2/gen/types.gen.ts @@ -547,6 +547,48 @@ export type EventSessionCompacted = { } } +export type EventTuiPromptAppend = { + type: "tui.prompt.append" + properties: { + text: string + } +} + +export type EventTuiCommandExecute = { + type: "tui.command.execute" + properties: { + command: + | "session.list" + | "session.new" + | "session.share" + | "session.interrupt" + | "session.compact" + | "session.page.up" + | "session.page.down" + | "session.half.page.up" + | "session.half.page.down" + | "session.first" + | "session.last" + | "prompt.clear" + | "prompt.submit" + | "agent.cycle" + | string + } +} + +export type EventTuiToastShow = { + type: "tui.toast.show" + properties: { + title?: string + message: string + variant: "info" | "success" | "warning" | "error" + /** + * Duration in milliseconds + */ + duration?: number + } +} + export type EventCommandExecuted = { type: "command.executed" properties: { @@ -639,48 +681,6 @@ export type EventVcsBranchUpdated = { } } -export type EventTuiPromptAppend = { - type: "tui.prompt.append" - properties: { - text: string - } -} - -export type EventTuiCommandExecute = { - type: "tui.command.execute" - properties: { - command: - | "session.list" - | "session.new" - | "session.share" - | "session.interrupt" - | "session.compact" - | "session.page.up" - | "session.page.down" - | "session.half.page.up" - | "session.half.page.down" - | "session.first" - | "session.last" - | "prompt.clear" - | "prompt.submit" - | "agent.cycle" - | string - } -} - -export type EventTuiToastShow = { - type: "tui.toast.show" - properties: { - title?: string - message: string - variant: "info" | "success" | "warning" | "error" - /** - * Duration in milliseconds - */ - duration?: number - } -} - export type Pty = { id: string title: string @@ -752,6 +752,9 @@ export type Event = | EventSessionStatus | EventSessionIdle | EventSessionCompacted + | EventTuiPromptAppend + | EventTuiCommandExecute + | EventTuiToastShow | EventCommandExecuted | EventSessionCreated | EventSessionUpdated @@ -760,9 +763,6 @@ export type Event = | EventSessionError | EventFileWatcherUpdated | EventVcsBranchUpdated - | EventTuiPromptAppend - | EventTuiCommandExecute - | EventTuiToastShow | EventPtyCreated | EventPtyUpdated | EventPtyExited @@ -890,6 +890,14 @@ export type KeybindsConfig = { * Navigate to last message */ messages_last?: string + /** + * Navigate to next message + */ + messages_next?: string + /** + * Navigate to previous message + */ + messages_previous?: string /** * Navigate to last user message */ @@ -1114,6 +1122,10 @@ export type KeybindsConfig = { * Previous child session */ session_child_cycle_reverse?: string + /** + * Go to parent session + */ + session_parent?: string /** * Suspend terminal */ @@ -1155,6 +1167,13 @@ export type AgentConfig = { | { [key: string]: "ask" | "allow" | "deny" } + skill?: + | "ask" + | "allow" + | "deny" + | { + [key: string]: "ask" | "allow" | "deny" + } webfetch?: "ask" | "allow" | "deny" doom_loop?: "ask" | "allow" | "deny" external_directory?: "ask" | "allow" | "deny" @@ -1181,6 +1200,13 @@ export type AgentConfig = { | { [key: string]: "ask" | "allow" | "deny" } + skill?: + | "ask" + | "allow" + | "deny" + | { + [key: string]: "ask" | "allow" | "deny" + } webfetch?: "ask" | "allow" | "deny" doom_loop?: "ask" | "allow" | "deny" external_directory?: "ask" | "allow" | "deny" @@ -1414,6 +1440,10 @@ export type Config = { * Small model to use for tasks like title generation in the format of provider/model */ small_model?: string + /** + * Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid. + */ + default_agent?: string /** * Custom username to display in conversations instead of system username */ @@ -1496,6 +1526,13 @@ export type Config = { | { [key: string]: "ask" | "allow" | "deny" } + skill?: + | "ask" + | "allow" + | "deny" + | { + [key: string]: "ask" | "allow" | "deny" + } webfetch?: "ask" | "allow" | "deny" doom_loop?: "ask" | "allow" | "deny" external_directory?: "ask" | "allow" | "deny" @@ -1767,6 +1804,7 @@ export type Agent = { mode: "subagent" | "primary" | "all" native?: boolean hidden?: boolean + default?: boolean topP?: number temperature?: number color?: string @@ -1775,6 +1813,9 @@ export type Agent = { bash: { [key: string]: "ask" | "allow" | "deny" } + skill: { + [key: string]: "ask" | "allow" | "deny" + } webfetch?: "ask" | "allow" | "deny" doom_loop?: "ask" | "allow" | "deny" external_directory?: "ask" | "allow" | "deny" @@ -1856,6 +1897,25 @@ export type WellKnownAuth = { export type Auth = OAuth | ApiAuth | WellKnownAuth +export type GlobalHealthData = { + body?: never + path?: never + query?: never + url: "/global/health" +} + +export type GlobalHealthResponses = { + /** + * Health information + */ + 200: { + healthy: true + version: string + } +} + +export type GlobalHealthResponse = GlobalHealthResponses[keyof GlobalHealthResponses] + export type GlobalEventData = { body?: never path?: never @@ -2745,6 +2805,7 @@ export type SessionSummarizeData = { body?: { providerID: string modelID: string + auto?: boolean } path: { /** @@ -2915,6 +2976,94 @@ export type SessionMessageResponses = { export type SessionMessageResponse = SessionMessageResponses[keyof SessionMessageResponses] +export type PartDeleteData = { + body?: never + path: { + /** + * Session ID + */ + sessionID: string + /** + * Message ID + */ + messageID: string + /** + * Part ID + */ + partID: string + } + query?: { + directory?: string + } + url: "/session/{sessionID}/message/{messageID}/part/{partID}" +} + +export type PartDeleteErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type PartDeleteError = PartDeleteErrors[keyof PartDeleteErrors] + +export type PartDeleteResponses = { + /** + * Successfully deleted part + */ + 200: boolean +} + +export type PartDeleteResponse = PartDeleteResponses[keyof PartDeleteResponses] + +export type PartUpdateData = { + body?: Part + path: { + /** + * Session ID + */ + sessionID: string + /** + * Message ID + */ + messageID: string + /** + * Part ID + */ + partID: string + } + query?: { + directory?: string + } + url: "/session/{sessionID}/message/{messageID}/part/{partID}" +} + +export type PartUpdateErrors = { + /** + * Bad request + */ + 400: BadRequestError + /** + * Not found + */ + 404: NotFoundError +} + +export type PartUpdateError = PartUpdateErrors[keyof PartUpdateErrors] + +export type PartUpdateResponses = { + /** + * Successfully updated part + */ + 200: Part +} + +export type PartUpdateResponse = PartUpdateResponses[keyof PartUpdateResponses] + export type SessionPromptAsyncData = { body?: { messageID?: string diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 09c7ea8e9..588b130b9 100644 --- a/packages/sdk/openapi.json +++ b/packages/sdk/openapi.json @@ -6,6 +6,41 @@ "version": "1.0.0" }, "paths": { + "/global/health": { + "get": { + "operationId": "global.health", + "summary": "Get health", + "description": "Get health information about the OpenCode server.", + "responses": { + "200": { + "description": "Health information", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "healthy": { + "type": "boolean", + "const": true + }, + "version": { + "type": "string" + } + }, + "required": ["healthy", "version"] + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.global.health({\n ...\n})" + } + ] + } + }, "/global/event": { "get": { "operationId": "global.event", @@ -1804,6 +1839,10 @@ }, "modelID": { "type": "string" + }, + "auto": { + "default": false, + "type": "boolean" } }, "required": ["providerID", "modelID"] @@ -2126,6 +2165,173 @@ ] } }, + "/session/{sessionID}/message/{messageID}/part/{partID}": { + "delete": { + "operationId": "part.delete", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "sessionID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Session ID" + }, + { + "in": "path", + "name": "messageID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Message ID" + }, + { + "in": "path", + "name": "partID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Part ID" + } + ], + "description": "Delete a part from a message", + "responses": { + "200": { + "description": "Successfully deleted part", + "content": { + "application/json": { + "schema": { + "type": "boolean" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestError" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.part.delete({\n ...\n})" + } + ] + }, + "patch": { + "operationId": "part.update", + "parameters": [ + { + "in": "query", + "name": "directory", + "schema": { + "type": "string" + } + }, + { + "in": "path", + "name": "sessionID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Session ID" + }, + { + "in": "path", + "name": "messageID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Message ID" + }, + { + "in": "path", + "name": "partID", + "schema": { + "type": "string" + }, + "required": true, + "description": "Part ID" + } + ], + "description": "Update a part in a message", + "responses": { + "200": { + "description": "Successfully updated part", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Part" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BadRequestError" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/NotFoundError" + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Part" + } + } + } + }, + "x-codeSamples": [ + { + "lang": "js", + "source": "import { createOpencodeClient } from \"@opencode-ai/sdk\n\nconst client = createOpencodeClient()\nawait client.part.update({\n ...\n})" + } + ] + } + }, "/session/{sessionID}/prompt_async": { "post": { "operationId": "session.prompt_async", @@ -6328,6 +6534,98 @@ }, "required": ["type", "properties"] }, + "Event.tui.prompt.append": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.prompt.append" + }, + "properties": { + "type": "object", + "properties": { + "text": { + "type": "string" + } + }, + "required": ["text"] + } + }, + "required": ["type", "properties"] + }, + "Event.tui.command.execute": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.command.execute" + }, + "properties": { + "type": "object", + "properties": { + "command": { + "anyOf": [ + { + "type": "string", + "enum": [ + "session.list", + "session.new", + "session.share", + "session.interrupt", + "session.compact", + "session.page.up", + "session.page.down", + "session.half.page.up", + "session.half.page.down", + "session.first", + "session.last", + "prompt.clear", + "prompt.submit", + "agent.cycle" + ] + }, + { + "type": "string" + } + ] + } + }, + "required": ["command"] + } + }, + "required": ["type", "properties"] + }, + "Event.tui.toast.show": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "tui.toast.show" + }, + "properties": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "message": { + "type": "string" + }, + "variant": { + "type": "string", + "enum": ["info", "success", "warning", "error"] + }, + "duration": { + "description": "Duration in milliseconds", + "default": 5000, + "type": "number" + } + }, + "required": ["message", "variant"] + } + }, + "required": ["type", "properties"] + }, "Event.command.executed": { "type": "object", "properties": { @@ -6622,98 +6920,6 @@ }, "required": ["type", "properties"] }, - "Event.tui.prompt.append": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "tui.prompt.append" - }, - "properties": { - "type": "object", - "properties": { - "text": { - "type": "string" - } - }, - "required": ["text"] - } - }, - "required": ["type", "properties"] - }, - "Event.tui.command.execute": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "tui.command.execute" - }, - "properties": { - "type": "object", - "properties": { - "command": { - "anyOf": [ - { - "type": "string", - "enum": [ - "session.list", - "session.new", - "session.share", - "session.interrupt", - "session.compact", - "session.page.up", - "session.page.down", - "session.half.page.up", - "session.half.page.down", - "session.first", - "session.last", - "prompt.clear", - "prompt.submit", - "agent.cycle" - ] - }, - { - "type": "string" - } - ] - } - }, - "required": ["command"] - } - }, - "required": ["type", "properties"] - }, - "Event.tui.toast.show": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "tui.toast.show" - }, - "properties": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "message": { - "type": "string" - }, - "variant": { - "type": "string", - "enum": ["info", "success", "warning", "error"] - }, - "duration": { - "description": "Duration in milliseconds", - "default": 5000, - "type": "number" - } - }, - "required": ["message", "variant"] - } - }, - "required": ["type", "properties"] - }, "Pty": { "type": "object", "properties": { @@ -6908,6 +7114,15 @@ { "$ref": "#/components/schemas/Event.session.compacted" }, + { + "$ref": "#/components/schemas/Event.tui.prompt.append" + }, + { + "$ref": "#/components/schemas/Event.tui.command.execute" + }, + { + "$ref": "#/components/schemas/Event.tui.toast.show" + }, { "$ref": "#/components/schemas/Event.command.executed" }, @@ -6932,15 +7147,6 @@ { "$ref": "#/components/schemas/Event.vcs.branch.updated" }, - { - "$ref": "#/components/schemas/Event.tui.prompt.append" - }, - { - "$ref": "#/components/schemas/Event.tui.command.execute" - }, - { - "$ref": "#/components/schemas/Event.tui.toast.show" - }, { "$ref": "#/components/schemas/Event.pty.created" }, @@ -7137,6 +7343,16 @@ "default": "ctrl+alt+g,end", "type": "string" }, + "messages_next": { + "description": "Navigate to next message", + "default": "none", + "type": "string" + }, + "messages_previous": { + "description": "Navigate to previous message", + "default": "none", + "type": "string" + }, "messages_last_user": { "description": "Navigate to last user message", "default": "none", @@ -7417,6 +7633,11 @@ "default": "<leader>left", "type": "string" }, + "session_parent": { + "description": "Go to parent session", + "default": "<leader>up", + "type": "string" + }, "terminal_suspend": { "description": "Suspend terminal", "default": "ctrl+z", @@ -7501,6 +7722,24 @@ } ] }, + "skill": { + "anyOf": [ + { + "type": "string", + "enum": ["ask", "allow", "deny"] + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string", + "enum": ["ask", "allow", "deny"] + } + } + ] + }, "webfetch": { "type": "string", "enum": ["ask", "allow", "deny"] @@ -7986,6 +8225,10 @@ "description": "Small model to use for tasks like title generation in the format of provider/model", "type": "string" }, + "default_agent": { + "description": "Default agent to use when none is specified. Must be a primary agent. Falls back to 'build' if not set or if the specified agent is invalid.", + "type": "string" + }, "username": { "description": "Custom username to display in conversations instead of system username", "type": "string" @@ -8205,6 +8448,24 @@ } ] }, + "skill": { + "anyOf": [ + { + "type": "string", + "enum": ["ask", "allow", "deny"] + }, + { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string", + "enum": ["ask", "allow", "deny"] + } + } + ] + }, "webfetch": { "type": "string", "enum": ["ask", "allow", "deny"] @@ -8985,6 +9246,9 @@ "hidden": { "type": "boolean" }, + "default": { + "type": "boolean" + }, "topP": { "type": "number" }, @@ -9011,6 +9275,16 @@ "enum": ["ask", "allow", "deny"] } }, + "skill": { + "type": "object", + "propertyNames": { + "type": "string" + }, + "additionalProperties": { + "type": "string", + "enum": ["ask", "allow", "deny"] + } + }, "webfetch": { "type": "string", "enum": ["ask", "allow", "deny"] @@ -9024,7 +9298,7 @@ "enum": ["ask", "allow", "deny"] } }, - "required": ["edit", "bash"] + "required": ["edit", "bash", "skill"] }, "model": { "type": "object", diff --git a/packages/slack/package.json b/packages/slack/package.json index ba2b5b7fa..4be3b2ef1 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.170", + "version": "1.0.191", "type": "module", "scripts": { "dev": "bun run src/index.ts", diff --git a/packages/tauri/.gitignore b/packages/tauri/.gitignore deleted file mode 100644 index a547bf36d..000000000 --- a/packages/tauri/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? diff --git a/packages/tauri/README.md b/packages/tauri/README.md deleted file mode 100644 index b381dcf5b..000000000 --- a/packages/tauri/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Tauri + Vanilla TS - -This template should help get you started developing with Tauri in vanilla HTML, CSS and Typescript. - -## Recommended IDE Setup - -- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) diff --git a/packages/tauri/package.json b/packages/tauri/package.json deleted file mode 100644 index 18fb561e9..000000000 --- a/packages/tauri/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@opencode-ai/tauri", - "private": true, - "version": "1.0.170", - "type": "module", - "scripts": { - "typecheck": "tsgo -b", - "predev": "bun ./scripts/predev.ts", - "dev": "vite", - "build": "bun run typecheck && vite build", - "preview": "vite preview", - "tauri": "tauri" - }, - "dependencies": { - "@opencode-ai/desktop": "workspace:*", - "@solid-primitives/storage": "catalog:", - "@tauri-apps/api": "^2", - "@tauri-apps/plugin-dialog": "~2", - "@tauri-apps/plugin-opener": "^2", - "@tauri-apps/plugin-os": "~2", - "@tauri-apps/plugin-process": "~2", - "@tauri-apps/plugin-shell": "~2", - "@tauri-apps/plugin-store": "~2", - "@tauri-apps/plugin-updater": "~2", - "@tauri-apps/plugin-http": "~2", - "@tauri-apps/plugin-window-state": "~2", - "solid-js": "catalog:" - }, - "devDependencies": { - "@actions/artifact": "4.0.0", - "@tauri-apps/cli": "^2", - "@types/bun": "catalog:", - "@typescript/native-preview": "catalog:", - "typescript": "~5.6.2", - "vite": "catalog:" - } -} diff --git a/packages/tauri/scripts/prepare.ts b/packages/tauri/scripts/prepare.ts deleted file mode 100755 index 23cf3dc5d..000000000 --- a/packages/tauri/scripts/prepare.ts +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bun - -import { $ } from "bun" - -import { copyBinaryToSidecarFolder, getCurrentSidecar } from "./utils" - -const sidecarConfig = getCurrentSidecar() - -const dir = "src-tauri/target/opencode-binaries" - -await $`mkdir -p ${dir}` -await $`gh release download ${Bun.env.OPENCODE_RELEASE_TAG} --pattern ${sidecarConfig.ocBinary}.${sidecarConfig.assetExt} --repo sst/opencode --skip-existing --dir ${dir}` - -if (sidecarConfig.assetExt === "tar.gz") { - await $`tar -xvzf ${dir}/${sidecarConfig.ocBinary}.${sidecarConfig.assetExt} -C ${dir}` -} else { - await $`unzip -o ${dir}/${sidecarConfig.ocBinary}.${sidecarConfig.assetExt} -d ${dir}` -} - -await copyBinaryToSidecarFolder(`${dir}/opencode${process.platform === "win32" ? ".exe" : ""}`) diff --git a/packages/tauri/vite.config.ts b/packages/tauri/vite.config.ts deleted file mode 100644 index ead3d8a8d..000000000 --- a/packages/tauri/vite.config.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defineConfig } from "vite" -import desktopPlugin from "@opencode-ai/desktop/vite" - -const host = process.env.TAURI_DEV_HOST - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [desktopPlugin], - // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` - // - // 1. prevent Vite from obscuring rust errors - clearScreen: false, - // 2. tauri expects a fixed port, fail if that port is not available - server: { - port: 1420, - strictPort: true, - host: host || false, - hmr: host - ? { - protocol: "ws", - host, - port: 1421, - } - : undefined, - watch: { - // 3. tell Vite to ignore watching `src-tauri` - ignored: ["**/src-tauri/**"], - }, - }, -}) diff --git a/packages/ui/package.json b/packages/ui/package.json index 1f083a6e6..214d0b3a6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.170", + "version": "1.0.191", "type": "module", "exports": { "./*": "./src/components/*.tsx", @@ -47,7 +47,7 @@ "marked": "16.2.0", "marked-shiki": "1.2.1", "remeda": "catalog:", - "shiki": "3.9.2", + "shiki": "catalog:", "solid-js": "catalog:", "solid-list": "catalog:", "virtua": "catalog:" diff --git a/packages/ui/src/assets/icons/provider/aihubmix.svg b/packages/ui/src/assets/icons/provider/aihubmix.svg index 39d6a7dc5..33164b78b 100644 --- a/packages/ui/src/assets/icons/provider/aihubmix.svg +++ b/packages/ui/src/assets/icons/provider/aihubmix.svg @@ -1,3 +1,3 @@ -<svg viewBox="0 0 128 128" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M59.9499 44.7625C60.4499 41.3292 61.5582 36.9208 63.2749 31.5375L63.9999 29.3125L64.7249 31.5375C66.4416 36.9208 67.5499 41.3292 68.0499 44.7625C68.4332 47.2792 68.4332 50.4792 68.0499 54.3625C67.6666 58.2625 67.6666 61.4875 68.0499 64.0375C68.6166 67.7708 70.3249 70.8708 73.1749 73.3375C76.0749 75.8542 79.4332 77.1125 83.2499 77.1125C87.4832 77.1125 91.0999 75.6208 94.0999 72.6375C97.0832 69.6542 98.5916 66.0542 98.6249 61.8375C98.6416 58.1042 99.0249 53.9875 99.7749 49.4875C100.142 47.2375 100.517 45.3542 100.9 43.8375L101.4 41.7875L102.3 43.6875C104.883 49.1542 106.233 54.9208 106.35 60.9875V61.8375C106.35 67.5875 105.242 73.0792 103.025 78.3125C100.875 83.3958 97.8499 87.8875 93.9499 91.7875C90.0332 95.6875 85.5416 98.7125 80.4749 100.863C75.2416 103.079 69.7499 104.188 63.9999 104.188C58.2499 104.188 52.7582 103.079 47.5249 100.863C42.4582 98.7125 37.9666 95.6875 34.0499 91.7875C30.1499 87.8875 27.1249 83.3958 24.9749 78.3125C22.7582 73.0792 21.6499 67.5875 21.6499 61.8375V60.9875C21.7666 54.9208 23.1166 49.1542 25.6999 43.6875L26.5999 41.7875L27.0999 43.8375C27.4832 45.3542 27.8582 47.2375 28.2249 49.4875C28.9749 53.9875 29.3582 58.1042 29.3749 61.8375C29.4082 66.0542 30.9166 69.6542 33.8999 72.6375C36.8999 75.6208 40.5166 77.1125 44.7499 77.1125C48.5666 77.1125 51.9249 75.8542 54.8249 73.3375C57.6749 70.8708 59.3832 67.7708 59.9499 64.0375C60.3332 61.4875 60.3332 58.2625 59.9499 54.3625C59.5666 50.4792 59.5666 47.2792 59.9499 44.7625ZM63.9999 96.4625C69.8499 96.4625 75.3332 95.0792 80.4499 92.3125C84.076 90.3611 87.2249 87.8753 89.8967 84.8551C90.2301 84.4783 89.8372 83.9047 89.3515 84.0364C87.3832 84.5705 85.3493 84.8375 83.2499 84.8375C79.0832 84.8375 75.2082 83.7875 71.6249 81.6875C68.7295 79.9998 66.3303 77.8087 64.4274 75.1141C64.2203 74.8208 63.7794 74.8208 63.5724 75.1141C61.6695 77.8087 59.2703 79.9998 56.3749 81.6875C52.7916 83.7875 48.9166 84.8375 44.7499 84.8375C42.6505 84.8375 40.6166 84.5705 38.6483 84.0364C38.1626 83.9047 37.7697 84.4783 38.1031 84.8551C40.7749 87.8753 43.9238 90.3611 47.5499 92.3125C52.6666 95.0792 58.1499 96.4625 63.9999 96.4625Z"/> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M18.7344 13.9883C18.8906 12.9154 19.237 11.5378 19.7734 9.85547L20 9.16016L20.2266 9.85547C20.763 11.5378 21.1094 12.9154 21.2656 13.9883C21.3854 14.7748 21.3854 15.7747 21.2656 16.9883C21.1458 18.207 21.1458 19.2148 21.2656 20.0117C21.4427 21.1784 21.9766 22.1471 22.8672 22.918C23.7734 23.7044 24.8229 24.0977 26.0156 24.0977C27.3385 24.0977 28.4688 23.6315 29.4062 22.6992C30.3385 21.7669 30.8099 20.6419 30.8203 19.3242C30.8255 18.1576 30.9453 16.8711 31.1797 15.4648C31.2944 14.7617 31.4116 14.1732 31.5313 13.6992L31.6875 13.0586L31.9688 13.6523C32.776 15.3607 33.1978 17.1628 33.2344 19.0586V19.3242C33.2344 21.1211 32.8882 22.8372 32.1953 24.4727C31.5235 26.0612 30.5781 27.4648 29.3594 28.6836C28.1354 29.9023 26.7318 30.8477 25.1484 31.5197C23.513 32.2122 21.7969 32.5588 20 32.5588C18.2031 32.5588 16.487 32.2122 14.8516 31.5197C13.2682 30.8477 11.8646 29.9023 10.6406 28.6836C9.42187 27.4648 8.47656 26.0612 7.80469 24.4727C7.11197 22.8372 6.76562 21.1211 6.76562 19.3242V19.0586C6.80209 17.1628 7.22397 15.3607 8.03125 13.6523L8.3125 13.0586L8.46875 13.6992C8.58853 14.1732 8.70572 14.7617 8.82031 15.4648C9.05469 16.8711 9.17447 18.1576 9.17969 19.3242C9.19009 20.6419 9.66147 21.7669 10.5937 22.6992C11.5312 23.6315 12.6615 24.0977 13.9844 24.0977C15.1771 24.0977 16.2266 23.7044 17.1328 22.918C18.0234 22.1471 18.5573 21.1784 18.7344 20.0117C18.8542 19.2148 18.8542 18.207 18.7344 16.9883C18.6146 15.7747 18.6146 14.7748 18.7344 13.9883ZM20 30.1445C21.8281 30.1445 23.5417 29.7122 25.1406 28.8477C26.2738 28.2378 27.2578 27.461 28.0927 26.5172C28.1969 26.3995 28.0742 26.2202 27.9224 26.2614C27.3073 26.4283 26.6717 26.5117 26.0156 26.5117C24.7135 26.5117 23.5026 26.1836 22.3828 25.5273C21.478 24.9999 20.7282 24.3152 20.1336 23.4732C20.0689 23.3815 19.9311 23.3815 19.8664 23.4732C19.2718 24.3152 18.522 24.9999 17.6172 25.5273C16.4974 26.1836 15.2865 26.5117 13.9844 26.5117C13.3283 26.5117 12.6927 26.4283 12.0776 26.2614C11.9258 26.2202 11.8031 26.3995 11.9072 26.5172C12.7422 27.461 13.7262 28.2378 14.8594 28.8477C16.4583 29.7122 18.1719 30.1445 20 30.1445Z" fill="currentColor"/> </svg> diff --git a/packages/ui/src/assets/icons/provider/alibaba-cn.svg b/packages/ui/src/assets/icons/provider/alibaba-cn.svg index 6172e8813..5d8355c18 100644 --- a/packages/ui/src/assets/icons/provider/alibaba-cn.svg +++ b/packages/ui/src/assets/icons/provider/alibaba-cn.svg @@ -1,3 +1,3 @@ -<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M24 14.014c-2.8 1.512-5.62 2.896-8.759 3.524-.7.139-1.476.139-2.187.043-.678-.085-1.017-.682-.776-1.31.23-.585.536-1.181.93-1.671.852-1.065 1.814-2.034 2.678-3.088a15.75 15.75 0 001.422-2.054c.306-.511.164-1.129-.372-1.384-.897-.437-1.859-.745-2.81-1.075-.11-.043-.274.074-.492.149.273.244.47.425.743.67-2.821.48-5.49 1.16-8.08 2.098-.012.053-.033.095-.023.117.383.585.208 1.032-.35 1.394a2.365 2.365 0 00-.568.522c1.706.5 3.226.213 4.68-.735-.087-.127-.175-.244-.262-.372.546.096.874.394.918.862.011.107-.054.213-.087.32-.077-.086-.175-.17-.24-.267-.045-.064-.056-.138-.088-.245-1.728 1.15-3.587 1.438-5.632.842 0 .404-.022.745.011 1.075.022.287-.098.415-.36.564-.591.362-1.204.735-1.696 1.214-.59.585-.371 1.299.427 1.597.907.34 1.859.35 2.81.234 1.126-.139 2.23-.32 3.456-.49-1.433.67-2.844 1.14-4.33 1.33-1.04.14-2.078.214-3.106-.084-1.476-.415-2.133-1.501-1.75-2.96.361-1.363 1.236-2.449 2.176-3.45 3.139-3.332 7.108-5.024 11.7-5.365 1.072-.074 2.155.064 3.16.511 1.411.639 2.002 1.99 1.313 3.354-.448.905-1.072 1.735-1.695 2.555-.612.809-1.301 1.554-1.946 2.331-.186.234-.361.48-.503.745-.274.5-.088.83.492.778 1.213-.118 2.45-.213 3.62-.511 1.716-.437 3.389-1.054 5.084-1.597.175-.043.339-.107.492-.17z"></path> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path d="M38 23.021C33.8 25.289 29.5701 27.365 24.8616 28.307C23.8116 28.5154 22.6476 28.5154 21.5811 28.3715C20.5641 28.244 20.0557 27.3485 20.4172 26.4065C20.7621 25.529 21.2211 24.635 21.8121 23.9C23.0901 22.3025 24.5331 20.849 25.8291 19.268C26.6206 18.2991 27.3338 17.2689 27.9621 16.1871C28.4211 15.4206 28.2081 14.4936 27.4041 14.1111C26.0586 13.4556 24.6156 12.9936 23.1891 12.4986C23.0241 12.4341 22.7781 12.6096 22.4511 12.7221C22.8606 13.0881 23.1561 13.3596 23.5656 13.7271C19.3342 14.4471 15.3307 15.4671 11.4457 16.8741C11.4277 16.9536 11.3962 17.0166 11.4112 17.0496C11.9857 17.927 11.7232 18.5975 10.8862 19.1405C10.5613 19.3531 10.2735 19.6177 10.0342 19.9235C12.5932 20.6735 14.8732 20.243 17.0542 18.821C16.9237 18.6305 16.7917 18.455 16.6612 18.263C17.4802 18.407 17.9722 18.854 18.0382 19.556C18.0547 19.7165 17.9572 19.8755 17.9077 20.036C17.7922 19.907 17.6452 19.781 17.5477 19.6355C17.4802 19.5395 17.4637 19.4285 17.4157 19.268C14.8237 20.993 12.0352 21.425 8.96775 20.531C8.96775 21.137 8.93475 21.6485 8.98425 22.1435C9.01725 22.574 8.83725 22.766 8.44426 22.9895C7.55776 23.5325 6.63827 24.092 5.90028 24.8105C5.01528 25.688 5.34378 26.759 6.54077 27.206C7.90126 27.716 9.32925 27.731 10.7557 27.557C12.4447 27.3485 14.1007 27.077 15.9397 26.822C13.7902 27.827 11.6737 28.5319 9.44475 28.8169C7.88476 29.0269 6.32777 29.1379 4.78579 28.6909C2.57181 28.0685 1.58631 26.4395 2.16081 24.251C2.7023 22.2065 4.01479 20.5775 5.42478 19.076C10.1332 14.0781 16.0867 11.5401 22.9746 11.0286C24.5826 10.9176 26.2071 11.1246 27.7146 11.7951C29.8311 12.7536 30.7176 14.7801 29.6841 16.8261C29.0121 18.1835 28.0761 19.4285 27.1416 20.6585C26.2236 21.872 25.1901 22.9895 24.2226 24.155C23.9436 24.506 23.6811 24.875 23.4681 25.2725C23.0571 26.0225 23.3361 26.5175 24.2061 26.4395C26.0256 26.2625 27.8811 26.12 29.6361 25.673C32.2101 25.0175 34.7195 24.092 37.262 23.2775C37.5245 23.213 37.7705 23.117 38 23.0225V23.021Z" fill="currentColor"/> </svg> diff --git a/packages/ui/src/assets/icons/provider/alibaba.svg b/packages/ui/src/assets/icons/provider/alibaba.svg index 6172e8813..b3a2edc3c 100644 --- a/packages/ui/src/assets/icons/provider/alibaba.svg +++ b/packages/ui/src/assets/icons/provider/alibaba.svg @@ -1,3 +1,3 @@ -<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M24 14.014c-2.8 1.512-5.62 2.896-8.759 3.524-.7.139-1.476.139-2.187.043-.678-.085-1.017-.682-.776-1.31.23-.585.536-1.181.93-1.671.852-1.065 1.814-2.034 2.678-3.088a15.75 15.75 0 001.422-2.054c.306-.511.164-1.129-.372-1.384-.897-.437-1.859-.745-2.81-1.075-.11-.043-.274.074-.492.149.273.244.47.425.743.67-2.821.48-5.49 1.16-8.08 2.098-.012.053-.033.095-.023.117.383.585.208 1.032-.35 1.394a2.365 2.365 0 00-.568.522c1.706.5 3.226.213 4.68-.735-.087-.127-.175-.244-.262-.372.546.096.874.394.918.862.011.107-.054.213-.087.32-.077-.086-.175-.17-.24-.267-.045-.064-.056-.138-.088-.245-1.728 1.15-3.587 1.438-5.632.842 0 .404-.022.745.011 1.075.022.287-.098.415-.36.564-.591.362-1.204.735-1.696 1.214-.59.585-.371 1.299.427 1.597.907.34 1.859.35 2.81.234 1.126-.139 2.23-.32 3.456-.49-1.433.67-2.844 1.14-4.33 1.33-1.04.14-2.078.214-3.106-.084-1.476-.415-2.133-1.501-1.75-2.96.361-1.363 1.236-2.449 2.176-3.45 3.139-3.332 7.108-5.024 11.7-5.365 1.072-.074 2.155.064 3.16.511 1.411.639 2.002 1.99 1.313 3.354-.448.905-1.072 1.735-1.695 2.555-.612.809-1.301 1.554-1.946 2.331-.186.234-.361.48-.503.745-.274.5-.088.83.492.778 1.213-.118 2.45-.213 3.62-.511 1.716-.437 3.389-1.054 5.084-1.597.175-.043.339-.107.492-.17z"></path> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path d="M37.9998 23.021C33.7998 25.2889 29.5698 27.3649 24.8614 28.3069C23.8114 28.5154 22.6474 28.5154 21.5809 28.3714C20.5639 28.2439 20.0554 27.3484 20.4169 26.4064C20.7619 25.5289 21.2209 24.635 21.8119 23.9C23.0899 22.3025 24.5329 20.849 25.8289 19.268C26.6203 18.2991 27.3335 17.2689 27.9618 16.187C28.4208 15.4205 28.2078 14.4935 27.4038 14.111C26.0584 13.4556 24.6154 12.9936 23.1889 12.4986C23.0239 12.4341 22.7779 12.6096 22.4509 12.7221C22.8604 13.0881 23.1559 13.3596 23.5654 13.727C19.3339 14.447 15.3305 15.467 11.4455 16.874C11.4275 16.9535 11.396 17.0165 11.411 17.0495C11.9855 17.927 11.723 18.5975 10.886 19.1405C10.5611 19.3531 10.2732 19.6176 10.034 19.9235C12.593 20.6735 14.873 20.243 17.0539 18.821C16.9234 18.6305 16.7914 18.455 16.6609 18.263C17.4799 18.407 17.9719 18.854 18.0379 19.556C18.0544 19.7165 17.9569 19.8755 17.9074 20.036C17.7919 19.907 17.6449 19.781 17.5474 19.6355C17.4799 19.5395 17.4634 19.4285 17.4154 19.268C14.8235 20.993 12.035 21.425 8.96751 20.531C8.96751 21.137 8.93451 21.6485 8.98401 22.1435C9.01701 22.574 8.83701 22.766 8.44401 22.9895C7.55752 23.5325 6.63803 24.092 5.90003 24.8105C5.01504 25.6879 5.34354 26.7589 6.54053 27.2059C7.90102 27.7159 9.329 27.7309 10.7555 27.5569C12.4445 27.3484 14.1005 27.0769 15.9394 26.8219C13.79 27.8269 11.6735 28.5319 9.4445 28.8169C7.88452 29.0269 6.32753 29.1379 4.78554 28.6909C2.57156 28.0684 1.58607 26.4394 2.16057 24.251C2.70206 22.2065 4.01455 20.5775 5.42454 19.076C10.133 14.078 16.0864 11.5401 22.9744 11.0286C24.5824 10.9176 26.2069 11.1246 27.7143 11.7951C29.8308 12.7536 30.7173 14.78 29.6838 16.826C29.0118 18.1835 28.0758 19.4285 27.1413 20.6585C26.2234 21.872 25.1899 22.9895 24.2224 24.155C23.9434 24.506 23.6809 24.875 23.4679 25.2724C23.0569 26.0224 23.3359 26.5174 24.2059 26.4394C26.0254 26.2624 27.8808 26.1199 29.6358 25.6729C32.2098 25.0174 34.7193 24.092 37.2618 23.2775C37.5243 23.213 37.7703 23.117 37.9998 23.0225V23.021Z" fill="currentColor"/> </svg> diff --git a/packages/ui/src/assets/icons/provider/amazon-bedrock.svg b/packages/ui/src/assets/icons/provider/amazon-bedrock.svg index b69b6eed9..1f185ef53 100644 --- a/packages/ui/src/assets/icons/provider/amazon-bedrock.svg +++ b/packages/ui/src/assets/icons/provider/amazon-bedrock.svg @@ -1,3 +1,3 @@ -<svg fill="currentColor" fill-rule="evenodd" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path d="M13.05 15.513h3.08c.214 0 .389.177.389.394v1.82a1.704 1.704 0 011.296 1.661c0 .943-.755 1.708-1.685 1.708-.931 0-1.686-.765-1.686-1.708 0-.807.554-1.484 1.297-1.662v-1.425h-2.69v4.663a.395.395 0 01-.188.338l-2.69 1.641a.385.385 0 01-.405-.002l-4.926-3.086a.395.395 0 01-.185-.336V16.3L2.196 14.87A.395.395 0 012 14.555L2 14.528V9.406c0-.14.073-.27.192-.34l2.465-1.462V4.448c0-.129.062-.249.165-.322l.021-.014L9.77 1.058a.385.385 0 01.407 0l2.69 1.675a.395.395 0 01.185.336V7.6h3.856V5.683a1.704 1.704 0 01-1.296-1.662c0-.943.755-1.708 1.685-1.708.931 0 1.685.765 1.685 1.708 0 .807-.553 1.484-1.296 1.662v2.311a.391.391 0 01-.389.394h-4.245v1.806h6.624a1.69 1.69 0 011.64-1.313c.93 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708a1.69 1.69 0 01-1.64-1.314H13.05v1.937h4.953l.915 1.18a1.66 1.66 0 01.84-.227c.931 0 1.685.764 1.685 1.707 0 .943-.754 1.708-1.685 1.708-.93 0-1.685-.765-1.685-1.708 0-.346.102-.668.276-.937l-.724-.935H13.05v1.806zM9.973 1.856L7.93 3.122V6.09h-.778V3.604L5.435 4.669v2.945l2.11 1.36L9.712 7.61V5.334h.778V7.83c0 .136-.07.263-.184.335L7.963 9.638v2.081l1.422 1.009-.446.646-1.406-.998-1.53 1.005-.423-.66 1.605-1.055v-1.99L5.038 8.29l-2.26 1.34v1.676l1.972-1.189.398.677-2.37 1.429V14.3l2.166 1.258 2.27-1.368.397.677-2.176 1.311V19.3l1.876 1.175 2.365-1.426.398.678-2.017 1.216 1.918 1.201 2.298-1.403v-5.78l-4.758 2.893-.4-.675 5.158-3.136V3.289L9.972 1.856zM16.13 18.47a.913.913 0 00-.908.92c0 .507.406.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zm3.63-3.81a.913.913 0 00-.908.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92zm1.555-4.99a.913.913 0 00-.908.92c0 .507.407.918.908.918a.913.913 0 00.907-.919.913.913 0 00-.907-.92zM17.296 3.1a.913.913 0 00-.907.92c0 .508.406.92.907.92a.913.913 0 00.908-.92.913.913 0 00-.908-.92z"></path> - </svg> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path d="M21.5835 24.7904H25.7835C26.0754 24.7904 26.314 25.0318 26.314 25.3277V27.8096C26.8199 27.9343 27.2691 28.2255 27.5896 28.6362C27.9102 29.047 28.0833 29.5536 28.0813 30.0746C28.0813 31.3605 27.0517 32.4037 25.7835 32.4037C24.514 32.4037 23.4844 31.3605 23.4844 30.0746C23.4844 28.9741 24.2399 28.0509 25.2531 27.8082V25.865H21.5849V32.2237C21.5852 32.3161 21.5618 32.407 21.5169 32.4877C21.4721 32.5684 21.4072 32.6362 21.3285 32.6846L17.6603 34.9224C17.577 34.9733 17.4813 35 17.3837 34.9995C17.2862 34.999 17.1907 34.9714 17.108 34.9197L10.3906 30.7114C10.3131 30.6628 10.2493 30.5952 10.2052 30.515C10.161 30.4348 10.138 30.3447 10.1384 30.2532V25.8636L6.78241 23.9136C6.70609 23.8694 6.64181 23.8071 6.59521 23.7322C6.5486 23.6573 6.52111 23.5721 6.51514 23.4841V23.4472V16.4626C6.51514 16.2717 6.61468 16.0944 6.77696 15.999L10.1384 14.0053V9.70163C10.1384 9.52572 10.2229 9.36208 10.3634 9.26253L10.392 9.24344L17.1107 5.07885C17.194 5.027 17.2901 4.99951 17.3882 4.99951C17.4863 4.99951 17.5824 5.027 17.6657 5.07885L21.3339 7.36296C21.4115 7.41162 21.4753 7.47921 21.5194 7.55938C21.5636 7.63955 21.5866 7.72964 21.5862 7.82115V13.9999H26.8445V11.3857C26.3384 11.2609 25.8889 10.9696 25.5684 10.5585C25.2479 10.1475 25.0749 9.6406 25.0772 9.11935C25.0772 7.83342 26.1067 6.79023 27.3749 6.79023C28.6445 6.79023 29.6727 7.83342 29.6727 9.11935C29.6727 10.2198 28.9186 11.143 27.9054 11.3857V14.5371C27.9059 14.6073 27.8926 14.6768 27.8662 14.7418C27.8399 14.8068 27.8009 14.8659 27.7516 14.9158C27.7024 14.9658 27.6437 15.0055 27.5791 15.0327C27.5144 15.0599 27.4451 15.0741 27.3749 15.0744H21.5862V17.5372H30.6191C30.7351 17.0302 31.0192 16.5773 31.4252 16.2522C31.8313 15.9271 32.3353 15.749 32.8554 15.7467C34.1236 15.7467 35.1532 16.7885 35.1532 18.0745C35.1532 19.3604 34.125 20.4036 32.8554 20.4036C32.3351 20.4012 31.8309 20.2229 31.4249 19.8975C31.0188 19.5722 30.7348 19.119 30.6191 18.6117H21.5835V21.2531H28.3377L29.5854 22.8622C29.9329 22.659 30.3283 22.5522 30.7309 22.5527C32.0004 22.5527 33.0286 23.5945 33.0286 24.8804C33.0286 26.1664 32.0004 27.2096 30.7309 27.2096C29.4627 27.2096 28.4331 26.1664 28.4331 24.8804C28.4331 24.4086 28.5722 23.9695 28.8095 23.6027L27.8222 22.3277H21.5835V24.7904ZM17.3875 6.16704L14.6016 7.89342V11.9407H13.5407V8.55071L11.1993 10.003V14.019L14.0766 15.8735L17.0316 14.0135V10.9098H18.0925V14.3135C18.0925 14.499 17.9971 14.6721 17.8416 14.7703L14.6466 16.779V19.6167L16.5857 20.9927L15.9775 21.8736L14.0602 20.5127L11.9738 21.8831L11.397 20.9831L13.5857 19.5445V16.8308L10.6579 14.9408L7.57606 16.7681V19.0536L10.2652 17.4322L10.8079 18.3554L7.57606 20.304V23.1363L10.5297 24.8518L13.6252 22.9863L14.1666 23.9095L11.1993 25.6973V29.9546L13.7575 31.5569L16.9825 29.6123L17.5253 30.5369L14.7748 32.1951L17.3903 33.8328L20.5239 31.9196V24.0377L14.0357 27.9828L13.4902 27.0623L20.5239 22.7859V8.12115L17.3875 6.16704ZM25.7835 28.8228C25.6198 28.8237 25.4579 28.8568 25.307 28.9204C25.1561 28.984 25.0192 29.0767 24.9042 29.1932C24.7892 29.3097 24.6983 29.4478 24.6367 29.5995C24.5751 29.7512 24.5441 29.9136 24.5453 30.0773C24.5453 30.7687 25.099 31.3292 25.7835 31.3292C25.947 31.3281 26.1087 31.2948 26.2594 31.2313C26.41 31.1677 26.5467 31.0751 26.6615 30.9587C26.7764 30.8424 26.8672 30.7045 26.9287 30.553C26.9903 30.4016 27.0215 30.2395 27.0204 30.076C27.0216 29.9123 26.9906 29.7501 26.9291 29.5985C26.8676 29.4468 26.7769 29.3088 26.662 29.1923C26.5471 29.0758 26.4104 28.9831 26.2597 28.9194C26.109 28.8558 25.9472 28.8238 25.7835 28.8228ZM30.7336 23.6272C30.5699 23.6281 30.4079 23.6613 30.257 23.7249C30.1062 23.7884 29.9693 23.8811 29.8543 23.9977C29.7393 24.1142 29.6484 24.2523 29.5868 24.404C29.5252 24.5557 29.4942 24.7181 29.4954 24.8818C29.4954 25.5745 30.0491 26.1364 30.7322 26.1364C30.896 26.1355 31.0579 26.1023 31.2088 26.0387C31.3597 25.9752 31.4966 25.8825 31.6116 25.7659C31.7266 25.6494 31.8175 25.5113 31.8791 25.3596C31.9406 25.2079 31.9717 25.0455 31.9704 24.8818C31.9717 24.7181 31.9406 24.5557 31.8791 24.404C31.8175 24.2523 31.7266 24.1142 31.6116 23.9977C31.4966 23.8811 31.3597 23.7884 31.2088 23.7249C31.0579 23.6613 30.8973 23.6281 30.7336 23.6272ZM32.8541 16.8226C32.6904 16.8235 32.5284 16.8567 32.3775 16.9203C32.2266 16.9838 32.0898 17.0765 31.9747 17.1931C31.8597 17.3096 31.7688 17.4477 31.7073 17.5994C31.6457 17.7511 31.6146 17.9135 31.6159 18.0772C31.6159 18.7686 32.1709 19.329 32.8541 19.329C33.0176 19.3279 33.1793 19.2947 33.3299 19.2311C33.4806 19.1676 33.6172 19.075 33.7321 18.9586C33.8469 18.8422 33.9377 18.7044 33.9993 18.5529C34.0609 18.4014 34.092 18.2393 34.0909 18.0758C34.0922 17.9122 34.0612 17.7499 33.9997 17.5983C33.9382 17.4467 33.8474 17.3087 33.7325 17.1922C33.6177 17.0757 33.481 16.9829 33.3302 16.9193C33.1795 16.8556 33.0177 16.8223 32.8541 16.8213V16.8226ZM27.3736 7.86342C27.2099 7.86449 27.0482 7.8978 26.8974 7.96145C26.7467 8.02509 26.61 8.11783 26.4951 8.23434C26.3802 8.35086 26.2895 8.48887 26.228 8.64049C26.1665 8.79212 26.1355 8.95437 26.1367 9.11798C26.1367 9.81072 26.6904 10.3725 27.3736 10.3725C27.5373 10.3717 27.6992 10.3385 27.8501 10.2749C28.001 10.2113 28.1379 10.1186 28.2529 10.0021C28.3679 9.88558 28.4588 9.7475 28.5204 9.59579C28.582 9.44408 28.613 9.28171 28.6118 9.11798C28.613 8.95426 28.582 8.79189 28.5204 8.64018C28.4588 8.48847 28.3679 8.35039 28.2529 8.23386C28.1379 8.11733 28.001 8.02462 27.8501 7.96106C27.6992 7.89749 27.5373 7.86432 27.3736 7.86342Z" fill="currentColor"/> +</svg> diff --git a/packages/ui/src/assets/icons/provider/anthropic.svg b/packages/ui/src/assets/icons/provider/anthropic.svg index 59f6d7787..aaa01fcdb 100644 --- a/packages/ui/src/assets/icons/provider/anthropic.svg +++ b/packages/ui/src/assets/icons/provider/anthropic.svg @@ -1,7 +1,3 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <!-- https://icones.js.org/collection/ri?s=anthropic&icon=ri:anthropic-fill --> - <path - fill="currentColor" - d="M16.765 5h-3.308l5.923 15h3.23zM7.226 5L1.38 20h3.308l1.307-3.154h6.154l1.23 3.077h3.309L10.688 5zm-.308 9.077l2-5.308l2.077 5.308z" - /> -</svg> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path d="M26.9568 9.88184H22.1265L30.7753 31.7848H35.4917L26.9568 9.88184ZM13.028 9.88184L4.4917 31.7848H9.32203L11.2305 27.1793H20.2166L22.0126 31.6724H26.8444L18.0832 9.88184H13.028ZM12.5783 23.1361L15.4987 15.3853L18.5315 23.1361H12.5783Z" fill="currentColor"/> +</svg> \ No newline at end of file diff --git a/packages/ui/src/assets/icons/provider/azure.svg b/packages/ui/src/assets/icons/provider/azure.svg index ecd0f9cbd..07c6519ba 100644 --- a/packages/ui/src/assets/icons/provider/azure.svg +++ b/packages/ui/src/assets/icons/provider/azure.svg @@ -1,4 +1,3 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> - <!-- https://api.iconify.design/mdi:microsoft-azure.svg --> - <path fill="currentColor" d="M13.05 4.24L6.56 18.05L2 18l5.09-8.76zm.7 1.09L22 19.76H6.74l9.3-1.66l-4.87-5.79z"/> +<svg width="24" height="24" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"> +<path d="M21.68 7.58398L11.296 29.68L4 29.6L12.144 15.584L21.68 7.58398ZM22.8 9.32798L36 32.416H11.584L26.464 29.76L18.672 20.496L22.8 9.32798Z" fill="currentColor"/> </svg> diff --git a/packages/ui/src/assets/icons/provider/bailing.svg b/packages/ui/src/assets/icons/provider/bailing.svg index 3d933dde5..b8ed486a8 100644 --- a/packages/ui/src/assets/icons/provider/bailing.svg +++ b/packages/ui/src/assets/icons/provider/bailing.svg @@ -1,3 +1,3 @@ -<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> - <path fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M7.73028253,0.0727948466 C7.89515204,-0.0256727629 8.1027655,-0.0271646964 8.25847559,0.0862222479 L11.1711702,1.45283331 C11.3055084,1.55130092 11.2841364,1.74823614 11.1375857,1.82730862 C7.16545181,3.95629769 4.76568451,6.03306909 3.50626465,8.04867122 C2.69260309,9.35262108 1.19198524,12.9108824 2.54910555,16.1215233 C3.95354952,19.4425672 7.13950012,20.6420817 9.49805005,20.6420817 C11.2795567,20.6420817 12.4901263,20.1169211 13.7205414,19.2993416 C17.0545692,17.0808365 17.4392647,12.7527375 14.9570627,9.77782212 C14.9570627,9.77483825 16.3184295,10.917554 17.1899953,12.6560084 C17.0258098,12.0295295 16.7691185,11.4289444 16.4225694,10.8714094 L16.2609389,10.620553 C15.5431485,9.5466181 14.596138,8.64533804 13.2976816,8.00391321 C10.1865329,6.46871366 3.69097956,7.20871267 2.66817797,13.8209618 C2.7719847,9.23475833 5.86481457,6.35681865 8.66759622,5.4676263 C10.2231706,4.97230439 11.8703391,4.87831258 13.4259134,5.1513364 C15.3345528,3.63463683 17.4279608,2.88570293 18.3902229,2.59899428 L18.6147233,2.53448508 C18.8055445,2.48077548 19.0101048,2.53448508 19.1490226,2.67174296 C19.3841143,2.90448459 19.7947615,3.30730663 20.3168483,3.81456401 C20.4756115,3.96524929 20.3931768,4.22633765 20.1779305,4.26661985 C18.1598056,4.64706289 16.7828398,5.02601399 15.3249659,5.68694052 C17.0912069,6.38964119 18.6040373,7.62048631 19.5260853,9.31681467 C19.6344717,9.51673376 19.541351,9.76439472 19.3230515,9.84197526 L18.0880568,10.2806037 C19.1734477,12.0918109 19.4512834,14.311808 18.9200372,16.5870065 C18.016308,20.4526061 14.9891206,23.1903041 11.12232,23.8407871 C11.0835772,23.8472889 11.0448469,23.8536499 11.0061316,23.8598585 C10.6838522,23.9178464 10.3607257,23.9576805 10.039799,23.9793698 L9.68201999,23.9962346 L9.32707426,23.998932 C9.09503569,23.9944562 8.91948019,23.7870775 8.96069757,23.5632875 L9.19578928,22.2578457 C8.11650462,22.1847409 7.10591559,22.0086928 6.09227343,21.6058707 C0.0791160545,19.2157933 -0.551357161,13.3823334 0.311154623,9.84645106 C1.43013009,5.26173948 4.8710178,1.79001028 7.73028253,0.0727948466 Z" id="形状结合"></path> -</svg> \ No newline at end of file +<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M8.28591 2.75588C8.41369 2.67957 8.5746 2.67841 8.69528 2.76629L10.9527 3.82546C11.0568 3.90177 11.0403 4.05441 10.9267 4.11569C7.84815 5.76573 5.98825 7.3753 5.01215 8.93746C4.38154 9.94807 3.21851 12.7058 4.27032 15.1942C5.35882 17.7681 7.82804 18.6978 9.65599 18.6978C11.0367 18.6978 11.975 18.2908 12.9286 17.6571C15.5126 15.9377 15.8107 12.5833 13.8869 10.2776C13.8869 10.2753 14.942 11.1609 15.6175 12.5083C15.4903 12.0228 15.2913 11.5573 15.0227 11.1252L14.8975 10.9308C14.3412 10.0984 13.6072 9.3999 12.6008 8.90277C10.1896 7.71294 5.15531 8.28646 4.36261 13.4112C4.44306 9.85672 6.84011 7.62622 9.01236 6.93706C10.218 6.55317 11.4946 6.48032 12.7002 6.69193C14.1795 5.51643 15.8019 4.93598 16.5477 4.71377L16.7217 4.66378C16.8696 4.62215 17.0282 4.66378 17.1358 4.77016C17.318 4.95054 17.6363 5.26274 18.0409 5.65588C18.164 5.77267 18.1001 5.97502 17.9333 6.00624C16.3692 6.3011 15.302 6.5948 14.1721 7.10704C15.541 7.65166 16.7134 8.6056 17.4281 9.92032C17.5121 10.0753 17.4399 10.2672 17.2707 10.3273L16.3135 10.6673C17.1548 12.071 17.3701 13.7916 16.9584 15.555C16.2579 18.5509 13.9118 20.6727 10.9149 21.1769C10.8848 21.1819 10.8548 21.1869 10.8248 21.1917C10.575 21.2366 10.3246 21.2675 10.0759 21.2843L9.79858 21.2974L9.52348 21.2995C9.34364 21.296 9.20758 21.1353 9.23953 20.9618L9.42173 19.9501C8.58525 19.8934 7.80201 19.757 7.0164 19.4448C2.356 17.5924 1.86736 13.0712 2.53583 10.3308C3.40308 6.77749 6.06988 4.08678 8.28591 2.75588Z" fill="currentColor"/> +</svg> diff --git a/packages/ui/src/assets/icons/provider/baseten.svg b/packages/ui/src/assets/icons/provider/baseten.svg index 401c9b3dc..ffd4fbd8b 100644 --- a/packages/ui/src/assets/icons/provider/baseten.svg +++ b/packages/ui/src/assets/icons/provider/baseten.svg @@ -1 +1,3 @@ -<svg fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Baseten + + + diff --git a/packages/ui/src/assets/icons/provider/cerebras.svg b/packages/ui/src/assets/icons/provider/cerebras.svg index 00d2ceb15..b16759672 100644 --- a/packages/ui/src/assets/icons/provider/cerebras.svg +++ b/packages/ui/src/assets/icons/provider/cerebras.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/packages/ui/src/assets/icons/provider/cloudflare-ai-gateway.svg b/packages/ui/src/assets/icons/provider/cloudflare-ai-gateway.svg index f04d0959f..02c7e51d3 100644 --- a/packages/ui/src/assets/icons/provider/cloudflare-ai-gateway.svg +++ b/packages/ui/src/assets/icons/provider/cloudflare-ai-gateway.svg @@ -1,6 +1,4 @@ - - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/cloudflare-workers-ai.svg b/packages/ui/src/assets/icons/provider/cloudflare-workers-ai.svg index f04d0959f..02c7e51d3 100644 --- a/packages/ui/src/assets/icons/provider/cloudflare-workers-ai.svg +++ b/packages/ui/src/assets/icons/provider/cloudflare-workers-ai.svg @@ -1,6 +1,4 @@ - - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/cohere.svg b/packages/ui/src/assets/icons/provider/cohere.svg index 0752883f8..cfeaa6002 100644 --- a/packages/ui/src/assets/icons/provider/cohere.svg +++ b/packages/ui/src/assets/icons/provider/cohere.svg @@ -1,19 +1,5 @@ - - - - - - - - - - - - - + + + + diff --git a/packages/ui/src/assets/icons/provider/deepinfra.svg b/packages/ui/src/assets/icons/provider/deepinfra.svg index bf908d1cf..c35ab7183 100644 --- a/packages/ui/src/assets/icons/provider/deepinfra.svg +++ b/packages/ui/src/assets/icons/provider/deepinfra.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/packages/ui/src/assets/icons/provider/deepseek.svg b/packages/ui/src/assets/icons/provider/deepseek.svg index 7eaa6a83c..5d6efa991 100644 --- a/packages/ui/src/assets/icons/provider/deepseek.svg +++ b/packages/ui/src/assets/icons/provider/deepseek.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/fastrouter.svg b/packages/ui/src/assets/icons/provider/fastrouter.svg index da8521c11..ec73dc49c 100644 --- a/packages/ui/src/assets/icons/provider/fastrouter.svg +++ b/packages/ui/src/assets/icons/provider/fastrouter.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/fireworks-ai.svg b/packages/ui/src/assets/icons/provider/fireworks-ai.svg index 834b2b0d0..72cc91f09 100644 --- a/packages/ui/src/assets/icons/provider/fireworks-ai.svg +++ b/packages/ui/src/assets/icons/provider/fireworks-ai.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/github-copilot.svg b/packages/ui/src/assets/icons/provider/github-copilot.svg index 423f676cc..2d426f265 100644 --- a/packages/ui/src/assets/icons/provider/github-copilot.svg +++ b/packages/ui/src/assets/icons/provider/github-copilot.svg @@ -1,4 +1,3 @@ - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/github-models.svg b/packages/ui/src/assets/icons/provider/github-models.svg index 177a15242..39689d95c 100644 --- a/packages/ui/src/assets/icons/provider/github-models.svg +++ b/packages/ui/src/assets/icons/provider/github-models.svg @@ -1,4 +1,3 @@ - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/google-vertex.svg b/packages/ui/src/assets/icons/provider/google-vertex.svg index 47d238715..fda56b447 100644 --- a/packages/ui/src/assets/icons/provider/google-vertex.svg +++ b/packages/ui/src/assets/icons/provider/google-vertex.svg @@ -1,3 +1,10 @@ - - + + + + + + + + + diff --git a/packages/ui/src/assets/icons/provider/google.svg b/packages/ui/src/assets/icons/provider/google.svg index 2b574c1dd..4ebfcfd2b 100644 --- a/packages/ui/src/assets/icons/provider/google.svg +++ b/packages/ui/src/assets/icons/provider/google.svg @@ -1,7 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/groq.svg b/packages/ui/src/assets/icons/provider/groq.svg index e58ff6963..fdd22ed7d 100644 --- a/packages/ui/src/assets/icons/provider/groq.svg +++ b/packages/ui/src/assets/icons/provider/groq.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/helicone.svg b/packages/ui/src/assets/icons/provider/helicone.svg index 68dcd520b..8a4bd43ad 100644 --- a/packages/ui/src/assets/icons/provider/helicone.svg +++ b/packages/ui/src/assets/icons/provider/helicone.svg @@ -1,3 +1,6 @@ - - + + + + + diff --git a/packages/ui/src/assets/icons/provider/huggingface.svg b/packages/ui/src/assets/icons/provider/huggingface.svg index 255d07593..4241ff94e 100644 --- a/packages/ui/src/assets/icons/provider/huggingface.svg +++ b/packages/ui/src/assets/icons/provider/huggingface.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/iflowcn.svg b/packages/ui/src/assets/icons/provider/iflowcn.svg index f90d80069..6f35a7d59 100644 --- a/packages/ui/src/assets/icons/provider/iflowcn.svg +++ b/packages/ui/src/assets/icons/provider/iflowcn.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/inception.svg b/packages/ui/src/assets/icons/provider/inception.svg index bde688837..f70ffbc78 100644 --- a/packages/ui/src/assets/icons/provider/inception.svg +++ b/packages/ui/src/assets/icons/provider/inception.svg @@ -1,9 +1,4 @@ - - - - - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/inference.svg b/packages/ui/src/assets/icons/provider/inference.svg index 1966aeb81..c17f66574 100644 --- a/packages/ui/src/assets/icons/provider/inference.svg +++ b/packages/ui/src/assets/icons/provider/inference.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/io-net.svg b/packages/ui/src/assets/icons/provider/io-net.svg index 23671a21b..651e57df8 100644 --- a/packages/ui/src/assets/icons/provider/io-net.svg +++ b/packages/ui/src/assets/icons/provider/io-net.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/packages/ui/src/assets/icons/provider/kimi-for-coding.svg b/packages/ui/src/assets/icons/provider/kimi-for-coding.svg index 77eb5eb66..8f2af02e6 100644 --- a/packages/ui/src/assets/icons/provider/kimi-for-coding.svg +++ b/packages/ui/src/assets/icons/provider/kimi-for-coding.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/llama.svg b/packages/ui/src/assets/icons/provider/llama.svg index 7ce9f56bf..3053b251f 100644 --- a/packages/ui/src/assets/icons/provider/llama.svg +++ b/packages/ui/src/assets/icons/provider/llama.svg @@ -1,7 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/lucidquery.svg b/packages/ui/src/assets/icons/provider/lucidquery.svg index 1d40cf5bb..6420a042e 100644 --- a/packages/ui/src/assets/icons/provider/lucidquery.svg +++ b/packages/ui/src/assets/icons/provider/lucidquery.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/minimax-cn.svg b/packages/ui/src/assets/icons/provider/minimax-cn.svg index 086f4e9e6..44c5eec21 100644 --- a/packages/ui/src/assets/icons/provider/minimax-cn.svg +++ b/packages/ui/src/assets/icons/provider/minimax-cn.svg @@ -1,5 +1,3 @@ - - - - - \ No newline at end of file + + + diff --git a/packages/ui/src/assets/icons/provider/minimax.svg b/packages/ui/src/assets/icons/provider/minimax.svg index 086f4e9e6..44c5eec21 100644 --- a/packages/ui/src/assets/icons/provider/minimax.svg +++ b/packages/ui/src/assets/icons/provider/minimax.svg @@ -1,5 +1,3 @@ - - - - - \ No newline at end of file + + + diff --git a/packages/ui/src/assets/icons/provider/mistral.svg b/packages/ui/src/assets/icons/provider/mistral.svg index b3cd0b5cf..966e474bc 100644 --- a/packages/ui/src/assets/icons/provider/mistral.svg +++ b/packages/ui/src/assets/icons/provider/mistral.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/modelscope.svg b/packages/ui/src/assets/icons/provider/modelscope.svg index b54f8ee9a..94a894a55 100644 --- a/packages/ui/src/assets/icons/provider/modelscope.svg +++ b/packages/ui/src/assets/icons/provider/modelscope.svg @@ -1,3 +1,5 @@ - - + + + + diff --git a/packages/ui/src/assets/icons/provider/moonshotai-cn.svg b/packages/ui/src/assets/icons/provider/moonshotai-cn.svg index 77eb5eb66..3cdf7c868 100644 --- a/packages/ui/src/assets/icons/provider/moonshotai-cn.svg +++ b/packages/ui/src/assets/icons/provider/moonshotai-cn.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/moonshotai.svg b/packages/ui/src/assets/icons/provider/moonshotai.svg index 77eb5eb66..3cdf7c868 100644 --- a/packages/ui/src/assets/icons/provider/moonshotai.svg +++ b/packages/ui/src/assets/icons/provider/moonshotai.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/nebius.svg b/packages/ui/src/assets/icons/provider/nebius.svg index eb7a4f5e3..aeba5890d 100644 --- a/packages/ui/src/assets/icons/provider/nebius.svg +++ b/packages/ui/src/assets/icons/provider/nebius.svg @@ -1 +1,4 @@ -Nebius \ No newline at end of file + + + + diff --git a/packages/ui/src/assets/icons/provider/nvidia.svg b/packages/ui/src/assets/icons/provider/nvidia.svg index b0d47f227..1f53eefca 100644 --- a/packages/ui/src/assets/icons/provider/nvidia.svg +++ b/packages/ui/src/assets/icons/provider/nvidia.svg @@ -1,4 +1,3 @@ - - - - \ No newline at end of file + + + diff --git a/packages/ui/src/assets/icons/provider/ollama-cloud.svg b/packages/ui/src/assets/icons/provider/ollama-cloud.svg index 4c77f4296..08c05cf28 100644 --- a/packages/ui/src/assets/icons/provider/ollama-cloud.svg +++ b/packages/ui/src/assets/icons/provider/ollama-cloud.svg @@ -1,3 +1,7 @@ - - + + + + + + diff --git a/packages/ui/src/assets/icons/provider/openai.svg b/packages/ui/src/assets/icons/provider/openai.svg index cd4c28c52..000f65c34 100644 --- a/packages/ui/src/assets/icons/provider/openai.svg +++ b/packages/ui/src/assets/icons/provider/openai.svg @@ -1,7 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/opencode.svg b/packages/ui/src/assets/icons/provider/opencode.svg index 9adb6f2d8..9e336ef2d 100644 --- a/packages/ui/src/assets/icons/provider/opencode.svg +++ b/packages/ui/src/assets/icons/provider/opencode.svg @@ -1,5 +1,4 @@ - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/openrouter.svg b/packages/ui/src/assets/icons/provider/openrouter.svg index 2dabb4556..7e8abc81d 100644 --- a/packages/ui/src/assets/icons/provider/openrouter.svg +++ b/packages/ui/src/assets/icons/provider/openrouter.svg @@ -1,19 +1,8 @@ - - - - - - - + + + + + + + diff --git a/packages/ui/src/assets/icons/provider/ovhcloud.svg b/packages/ui/src/assets/icons/provider/ovhcloud.svg index 7b53f446f..064b0835b 100644 --- a/packages/ui/src/assets/icons/provider/ovhcloud.svg +++ b/packages/ui/src/assets/icons/provider/ovhcloud.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/perplexity.svg b/packages/ui/src/assets/icons/provider/perplexity.svg index 70b382523..a0f38862a 100644 --- a/packages/ui/src/assets/icons/provider/perplexity.svg +++ b/packages/ui/src/assets/icons/provider/perplexity.svg @@ -1,3 +1,3 @@ - - - \ No newline at end of file + + + diff --git a/packages/ui/src/assets/icons/provider/poe.svg b/packages/ui/src/assets/icons/provider/poe.svg index e03a44227..a5ab62d72 100644 --- a/packages/ui/src/assets/icons/provider/poe.svg +++ b/packages/ui/src/assets/icons/provider/poe.svg @@ -1,7 +1,7 @@ - - - - - - - \ No newline at end of file + + + + + + + diff --git a/packages/ui/src/assets/icons/provider/scaleway.svg b/packages/ui/src/assets/icons/provider/scaleway.svg index 64f6a4cca..7574f72f3 100644 --- a/packages/ui/src/assets/icons/provider/scaleway.svg +++ b/packages/ui/src/assets/icons/provider/scaleway.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/packages/ui/src/assets/icons/provider/siliconflow-cn.svg b/packages/ui/src/assets/icons/provider/siliconflow-cn.svg new file mode 100644 index 000000000..13cac22b9 --- /dev/null +++ b/packages/ui/src/assets/icons/provider/siliconflow-cn.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/provider/siliconflow.svg b/packages/ui/src/assets/icons/provider/siliconflow.svg index 808f52a7e..13cac22b9 100644 --- a/packages/ui/src/assets/icons/provider/siliconflow.svg +++ b/packages/ui/src/assets/icons/provider/siliconflow.svg @@ -1,6 +1,3 @@ - - SiliconFlow - - - - \ No newline at end of file + + + diff --git a/packages/ui/src/assets/icons/provider/submodel.svg b/packages/ui/src/assets/icons/provider/submodel.svg index 5dd4fcfc7..5bef03c64 100644 --- a/packages/ui/src/assets/icons/provider/submodel.svg +++ b/packages/ui/src/assets/icons/provider/submodel.svg @@ -1,5 +1,3 @@ - - - - + + diff --git a/packages/ui/src/assets/icons/provider/togetherai.svg b/packages/ui/src/assets/icons/provider/togetherai.svg index b31a60fe2..68413386c 100644 --- a/packages/ui/src/assets/icons/provider/togetherai.svg +++ b/packages/ui/src/assets/icons/provider/togetherai.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/packages/ui/src/assets/icons/provider/v0.svg b/packages/ui/src/assets/icons/provider/v0.svg index d4e9a436c..09f3b411e 100644 --- a/packages/ui/src/assets/icons/provider/v0.svg +++ b/packages/ui/src/assets/icons/provider/v0.svg @@ -1,4 +1,3 @@ - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/venice.svg b/packages/ui/src/assets/icons/provider/venice.svg index 086e9aa1f..3d5809e3b 100644 --- a/packages/ui/src/assets/icons/provider/venice.svg +++ b/packages/ui/src/assets/icons/provider/venice.svg @@ -1,24 +1,4 @@ - - - - + + + diff --git a/packages/ui/src/assets/icons/provider/vercel.svg b/packages/ui/src/assets/icons/provider/vercel.svg index 4d2395bbf..a99425f2a 100644 --- a/packages/ui/src/assets/icons/provider/vercel.svg +++ b/packages/ui/src/assets/icons/provider/vercel.svg @@ -1,4 +1,3 @@ - - - + + diff --git a/packages/ui/src/assets/icons/provider/vultr.svg b/packages/ui/src/assets/icons/provider/vultr.svg index e24a1dda2..9205d4484 100644 --- a/packages/ui/src/assets/icons/provider/vultr.svg +++ b/packages/ui/src/assets/icons/provider/vultr.svg @@ -1,18 +1,6 @@ - - - - - + + + + + diff --git a/packages/ui/src/assets/icons/provider/xai.svg b/packages/ui/src/assets/icons/provider/xai.svg index 9a6263086..ccd22443c 100644 --- a/packages/ui/src/assets/icons/provider/xai.svg +++ b/packages/ui/src/assets/icons/provider/xai.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/xiaomi.svg b/packages/ui/src/assets/icons/provider/xiaomi.svg new file mode 100644 index 000000000..4a893919e --- /dev/null +++ b/packages/ui/src/assets/icons/provider/xiaomi.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/ui/src/assets/icons/provider/zai-coding-plan.svg b/packages/ui/src/assets/icons/provider/zai-coding-plan.svg index 121abb371..d7da9b7c5 100644 --- a/packages/ui/src/assets/icons/provider/zai-coding-plan.svg +++ b/packages/ui/src/assets/icons/provider/zai-coding-plan.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/zai.svg b/packages/ui/src/assets/icons/provider/zai.svg index 121abb371..d7da9b7c5 100644 --- a/packages/ui/src/assets/icons/provider/zai.svg +++ b/packages/ui/src/assets/icons/provider/zai.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/zenmux.svg b/packages/ui/src/assets/icons/provider/zenmux.svg index dfb56b1d0..d8d9ef665 100644 --- a/packages/ui/src/assets/icons/provider/zenmux.svg +++ b/packages/ui/src/assets/icons/provider/zenmux.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/zhipuai-coding-plan.svg b/packages/ui/src/assets/icons/provider/zhipuai-coding-plan.svg index 121abb371..3d0d0c455 100644 --- a/packages/ui/src/assets/icons/provider/zhipuai-coding-plan.svg +++ b/packages/ui/src/assets/icons/provider/zhipuai-coding-plan.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/assets/icons/provider/zhipuai.svg b/packages/ui/src/assets/icons/provider/zhipuai.svg index 121abb371..d7da9b7c5 100644 --- a/packages/ui/src/assets/icons/provider/zhipuai.svg +++ b/packages/ui/src/assets/icons/provider/zhipuai.svg @@ -1,3 +1,3 @@ - - + + diff --git a/packages/ui/src/components/basic-tool.css b/packages/ui/src/components/basic-tool.css index 45778cf6f..1fe1b079a 100644 --- a/packages/ui/src/components/basic-tool.css +++ b/packages/ui/src/components/basic-tool.css @@ -1,4 +1,5 @@ [data-component="tool-trigger"] { + content-visibility: auto; width: 100%; display: flex; align-items: center; diff --git a/packages/ui/src/components/basic-tool.tsx b/packages/ui/src/components/basic-tool.tsx index 8fae33694..28320eeb3 100644 --- a/packages/ui/src/components/basic-tool.tsx +++ b/packages/ui/src/components/basic-tool.tsx @@ -1,4 +1,4 @@ -import { children, For, Match, Show, Switch, type JSX } from "solid-js" +import { For, Match, Show, Switch, type JSX } from "solid-js" import { Collapsible } from "./collapsible" import { Icon, IconProps } from "./icon" @@ -27,7 +27,6 @@ export interface BasicToolProps { } export function BasicTool(props: BasicToolProps) { - const resolved = children(() => props.children) return ( @@ -81,13 +80,13 @@ export function BasicTool(props: BasicToolProps) {
- +
- - {resolved()} + + {props.children} ) diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx index b98639758..7187e4ac3 100644 --- a/packages/ui/src/components/checkbox.tsx +++ b/packages/ui/src/components/checkbox.tsx @@ -1,5 +1,5 @@ import { Checkbox as Kobalte } from "@kobalte/core/checkbox" -import { children, Show, splitProps } from "solid-js" +import { Show, splitProps } from "solid-js" import type { ComponentProps, JSX, ParentProps } from "solid-js" export interface CheckboxProps extends ParentProps> { @@ -10,7 +10,6 @@ export interface CheckboxProps extends ParentProps local.children) return ( @@ -29,9 +28,9 @@ export function Checkbox(props: CheckboxProps) {
- + - {resolved()} + {props.children} diff --git a/packages/ui/src/components/code.css b/packages/ui/src/components/code.css index 1c890deaa..671b40512 100644 --- a/packages/ui/src/components/code.css +++ b/packages/ui/src/components/code.css @@ -1,3 +1,4 @@ [data-component="code"] { + content-visibility: auto; overflow: hidden; } diff --git a/packages/ui/src/components/dialog.css b/packages/ui/src/components/dialog.css index 6fa71c64c..782f2570c 100644 --- a/packages/ui/src/components/dialog.css +++ b/packages/ui/src/components/dialog.css @@ -4,7 +4,7 @@ position: fixed; inset: 0; z-index: 50; - background-color: transparent; + background-color: hsl(from var(--background-base) h s l / 0.2); /* animation: overlayHide 250ms ease 100ms forwards; */ /**/ @@ -43,13 +43,14 @@ /* padding: 8px; */ /* padding: 8px 8px 0 8px; */ - border: 1px solid var(--border-base); + border: 1px solid hsl(from var(--border-base) h s l / 0.2); border-radius: var(--radius-xl); background: var(--surface-raised-stronger-non-alpha); + background-clip: padding-box; box-shadow: - 0 15px 45px 0 rgba(19, 16, 16, 0.22), - 0 3.35px 10.051px 0 rgba(19, 16, 16, 0.13), - 0 0.998px 2.993px 0 rgba(19, 16, 16, 0.09); + 0 15px 45px 0 rgba(19, 16, 16, 0.35), + 0 3.35px 10.051px 0 rgba(19, 16, 16, 0.25), + 0 0.998px 2.993px 0 rgba(19, 16, 16, 0.2); /* animation: contentHide 300ms ease-in forwards; */ /**/ @@ -59,8 +60,7 @@ [data-slot="dialog-header"] { display: flex; - padding: 16px; - padding-left: 20px; + padding: 16px 16px 16px 24px; justify-content: space-between; align-items: center; flex-shrink: 0; diff --git a/packages/ui/src/components/diff.css b/packages/ui/src/components/diff.css index 3251eb4c6..1d94e417a 100644 --- a/packages/ui/src/components/diff.css +++ b/packages/ui/src/components/diff.css @@ -1,5 +1,5 @@ [data-component="diff"] { - contain: content; + content-visibility: auto; [data-slot="diff-hunk-separator-line-number"] { position: sticky; diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 8642be0f8..75a737d88 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -54,6 +54,7 @@ const icons = { photo: ``, share: ``, download: ``, + menu: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/list.css b/packages/ui/src/components/list.css index 368065e53..1714b3513 100644 --- a/packages/ui/src/components/list.css +++ b/packages/ui/src/components/list.css @@ -1,17 +1,36 @@ +@property --bottom-fade { + syntax: ""; + inherits: false; + initial-value: 0px; +} + +@keyframes scroll { + 0% { + --bottom-fade: 20px; + } + 90% { + --bottom-fade: 20px; + } + 100% { + --bottom-fade: 0; + } +} + [data-component="list"] { display: flex; flex-direction: column; - gap: 20px; + gap: 12px; overflow: hidden; + padding: 0 12px; [data-slot="list-search"] { display: flex; - height: 40px; flex-shrink: 0; - padding: 4px 10px 4px 16px; + padding: 8px; align-items: center; gap: 12px; align-self: stretch; + margin-bottom: 4px; border-radius: var(--radius-md); background: var(--surface-base); @@ -19,11 +38,17 @@ [data-slot="list-search-container"] { display: flex; align-items: center; - gap: 16px; + gap: 8px; flex: 1 0 0; + max-height: 20px; [data-slot="list-search-input"] { width: 100%; + + &[data-slot="input-input"] { + line-height: 20px; + max-height: 20px; + } } } } @@ -31,88 +56,67 @@ [data-slot="list-scroll"] { display: flex; flex-direction: column; - gap: 20px; + gap: 12px; overflow-y: auto; + overscroll-behavior: contain; + mask: linear-gradient(to bottom, #ffff calc(100% - var(--bottom-fade)), #0000); + animation: scroll; + animation-timeline: --scroll; + scroll-timeline: --scroll y; scrollbar-width: none; -ms-overflow-style: none; &::-webkit-scrollbar { display: none; } - } - [data-slot="list-empty-state"] { - display: flex; - padding: 32px 0px; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 8px; - align-self: stretch; - - [data-slot="list-message"] { + [data-slot="list-empty-state"] { display: flex; + padding: 32px 0px; + flex-direction: column; justify-content: center; align-items: center; - gap: 2px; - color: var(--text-weak); - text-align: center; - - /* text-14-regular */ - font-family: var(--font-family-sans); - font-size: 14px; - font-style: normal; - font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 142.857% */ - letter-spacing: var(--letter-spacing-normal); - } - - [data-slot="list-filter"] { - color: var(--text-strong); - } - } - - [data-slot="list-group"] { - position: relative; - display: flex; - flex-direction: column; - - [data-slot="list-header"] { - display: flex; - z-index: 10; - height: 28px; - padding: 0 10px; - justify-content: space-between; - align-items: center; + gap: 8px; align-self: stretch; - background: var(--surface-raised-stronger-non-alpha); - position: sticky; - top: 0; - color: var(--text-base); + [data-slot="list-message"] { + display: flex; + justify-content: center; + align-items: center; + gap: 2px; + color: var(--text-weak); + text-align: center; - /* text-14-medium */ - font-family: var(--font-family-sans); - font-size: 14px; - font-style: normal; - font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 142.857% */ - letter-spacing: var(--letter-spacing-normal); + /* text-14-regular */ + font-family: var(--font-family-sans); + font-size: 14px; + font-style: normal; + font-weight: var(--font-weight-regular); + line-height: var(--line-height-large); /* 142.857% */ + letter-spacing: var(--letter-spacing-normal); + } + + [data-slot="list-filter"] { + color: var(--text-strong); + } } - [data-slot="list-items"] { + [data-slot="list-group"] { + position: relative; display: flex; flex-direction: column; - align-items: flex-start; - align-self: stretch; - [data-slot="list-item"] { + [data-slot="list-header"] { display: flex; - width: 100%; - height: 28px; - padding: 4px 10px; + z-index: 10; + padding: 0 12px 8px 8px; + justify-content: space-between; align-items: center; - color: var(--text-strong); - scroll-margin-top: 28px; + align-self: stretch; + background: var(--surface-raised-stronger-non-alpha); + position: sticky; + top: 0; + + color: var(--text-base); /* text-14-medium */ font-family: var(--font-family-sans); @@ -122,30 +126,76 @@ line-height: var(--line-height-large); /* 142.857% */ letter-spacing: var(--letter-spacing-normal); - [data-slot="list-item-selected-icon"] { - color: var(--icon-strong-base); - } - [data-slot="list-item-active-icon"] { - display: none; - color: var(--icon-strong-base); + &::after { + content: ""; + position: absolute; + top: 100%; + left: 0; + right: 0; + height: 16px; + background: linear-gradient(to bottom, var(--surface-raised-stronger-non-alpha), transparent); + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; } - &[data-active="true"] { - border-radius: var(--radius-md); - background: var(--surface-raised-base-hover); + &[data-stuck="true"]::after { + opacity: 1; + } + } + + [data-slot="list-items"] { + display: flex; + flex-direction: column; + align-items: flex-start; + align-self: stretch; + + [data-slot="list-item"] { + display: flex; + width: 100%; + padding: 6px 8px 6px 8px; + align-items: center; + color: var(--text-strong); + scroll-margin-top: 28px; + + /* text-14-medium */ + font-family: var(--font-family-sans); + font-size: 14px; + font-style: normal; + font-weight: var(--font-weight-medium); + line-height: var(--line-height-large); /* 142.857% */ + letter-spacing: var(--letter-spacing-normal); + + [data-slot="list-item-selected-icon"] { + color: var(--icon-strong-base); + } [data-slot="list-item-active-icon"] { - display: block; + display: none; + color: var(--icon-strong-base); } + [data-slot="list-item-extra-icon"] { - display: block !important; - color: var(--icon-strong-base) !important; + color: var(--icon-base); + margin-left: -4px; + } + + &[data-active="true"] { + border-radius: var(--radius-md); + background: var(--surface-raised-base-hover); + [data-slot="list-item-active-icon"] { + display: block; + } + [data-slot="list-item-extra-icon"] { + display: block !important; + color: var(--icon-strong-base) !important; + } + } + &:active { + background: var(--surface-raised-base-active); + } + &:focus-visible { + outline: none; } - } - &:active { - background: var(--surface-raised-base-active); - } - &:focus-visible { - outline: none; } } } diff --git a/packages/ui/src/components/list.tsx b/packages/ui/src/components/list.tsx index 0ed745f32..4f6df0faf 100644 --- a/packages/ui/src/components/list.tsx +++ b/packages/ui/src/components/list.tsx @@ -1,7 +1,7 @@ -import { createEffect, on, Show, For, type JSX, createSignal } from "solid-js" +import { type FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks" +import { createEffect, createSignal, For, type JSX, on, Show } from "solid-js" import { createStore } from "solid-js/store" -import { FilteredListProps, useFilteredList } from "@opencode-ai/ui/hooks" -import { Icon, IconProps } from "./icon" +import { Icon, type IconProps } from "./icon" import { IconButton } from "./icon-button" import { TextField } from "./text-field" @@ -149,7 +149,31 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void }) {(group) => (
-
{group.category}
+ {(() => { + const [stuck, setStuck] = createSignal(false) + return ( +
{ + createEffect(() => { + const scroll = scrollRef() + if (!scroll) return + const handler = () => { + const rect = el.getBoundingClientRect() + const scrollRect = scroll.getBoundingClientRect() + setStuck(rect.top <= scrollRect.top + 1 && scroll.scrollTop > 0) + } + scroll.addEventListener("scroll", handler, { passive: true }) + handler() + return () => scroll.removeEventListener("scroll", handler) + }) + }} + > + {group.category} +
+ ) + })()}
@@ -160,10 +184,14 @@ export function List(props: ListProps & { ref?: (ref: ListRef) => void }) data-active={props.key(item) === active()} data-selected={item === props.current} onClick={() => handleSelect(item, i())} + type="button" onMouseMove={() => { setStore("mouseActive", true) setActive(props.key(item)) }} + onMouseLeave={() => { + setActive(null) + }} > {props.children(item)} diff --git a/packages/ui/src/components/message-part.css b/packages/ui/src/components/message-part.css index 49392d6b7..ffeb4cb28 100644 --- a/packages/ui/src/components/message-part.css +++ b/packages/ui/src/components/message-part.css @@ -1,4 +1,5 @@ [data-component="assistant-message"] { + content-visibility: auto; width: 100%; display: flex; flex-direction: column; @@ -286,6 +287,44 @@ } } +[data-component="task-tools"] { + padding: 8px 12px; + display: flex; + flex-direction: column; + gap: 6px; + + [data-slot="task-tool-item"] { + display: flex; + align-items: center; + gap: 8px; + color: var(--text-weak); + + [data-slot="icon-svg"] { + flex-shrink: 0; + color: var(--icon-weak); + } + } + + [data-slot="task-tool-title"] { + font-family: var(--font-family-sans); + font-size: var(--font-size-small); + font-weight: var(--font-weight-medium); + line-height: var(--line-height-large); + color: var(--text-weak); + } + + [data-slot="task-tool-subtitle"] { + font-family: var(--font-family-sans); + font-size: var(--font-size-small); + font-weight: var(--font-weight-regular); + line-height: var(--line-height-large); + color: var(--text-weaker); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } +} + [data-component="diagnostics"] { display: flex; flex-direction: column; @@ -319,5 +358,10 @@ [data-slot="diagnostic-message"] { color: var(--text-on-critical-base); word-break: break-word; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + line-clamp: 3; + overflow: hidden; } } diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index ef85dd9ce..973a06230 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -5,9 +5,11 @@ import { FilePart, Message as MessageType, Part as PartType, + ReasoningPart, TextPart, ToolPart, UserMessage, + Todo, } from "@opencode-ai/sdk/v2" import { useData } from "../context" import { useDiffComponent } from "../context/diff" @@ -21,6 +23,7 @@ import { DiffChanges } from "./diff-changes" import { Markdown } from "./markdown" import { getDirectory as _getDirectory, getFilename } from "@opencode-ai/util/path" import { checksum } from "@opencode-ai/util/encode" +import { createAutoScroll } from "../hooks" interface Diagnostic { range: { @@ -87,6 +90,109 @@ function getDirectory(path: string | undefined) { return relativizeProjectPaths(_getDirectory(path), data.directory) } +export function getSessionToolParts(store: ReturnType["store"], sessionId: string): ToolPart[] { + const messages = store.message[sessionId]?.filter((m) => m.role === "assistant") + if (!messages) return [] + + const parts: ToolPart[] = [] + for (const m of messages) { + const msgParts = store.part[m.id] + if (msgParts) { + for (const p of msgParts) { + if (p && p.type === "tool") parts.push(p as ToolPart) + } + } + } + return parts +} + +import type { IconProps } from "./icon" + +export type ToolInfo = { + icon: IconProps["name"] + title: string + subtitle?: string +} + +export function getToolInfo(tool: string, input: any = {}): ToolInfo { + switch (tool) { + case "read": + return { + icon: "glasses", + title: "Read", + subtitle: input.filePath ? getFilename(input.filePath) : undefined, + } + case "list": + return { + icon: "bullet-list", + title: "List", + subtitle: input.path ? getFilename(input.path) : undefined, + } + case "glob": + return { + icon: "magnifying-glass-menu", + title: "Glob", + subtitle: input.pattern, + } + case "grep": + return { + icon: "magnifying-glass-menu", + title: "Grep", + subtitle: input.pattern, + } + case "webfetch": + return { + icon: "window-cursor", + title: "Webfetch", + subtitle: input.url, + } + case "task": + return { + icon: "task", + title: `${input.subagent_type || "task"} Agent`, + subtitle: input.description, + } + case "bash": + return { + icon: "console", + title: "Shell", + subtitle: input.description, + } + case "edit": + return { + icon: "code-lines", + title: "Edit", + subtitle: input.filePath ? getFilename(input.filePath) : undefined, + } + case "write": + return { + icon: "code-lines", + title: "Write", + subtitle: input.filePath ? getFilename(input.filePath) : undefined, + } + case "todowrite": + return { + icon: "checklist", + title: "To-dos", + } + case "todoread": + return { + icon: "checklist", + title: "Read to-dos", + } + default: + return { + icon: "mcp", + title: tool, + } + } +} + +function getToolPartInfo(part: ToolPart): ToolInfo { + const input = part.state.input || {} + return getToolInfo(part.tool, input) +} + export function registerPartComponent(type: string, component: PartComponent) { PART_MAPPING[type] = component } @@ -225,6 +331,7 @@ export interface ToolProps { metadata: Record tool: string output?: string + status?: string hideDetails?: boolean defaultOpen?: boolean } @@ -293,6 +400,7 @@ PART_MAPPING["tool"] = function ToolPartDisplay(props) { tool={part.tool} metadata={metadata} output={part.state.status === "completed" ? part.state.output : undefined} + status={part.state.status} hideDetails={props.hideDetails} defaultOpen={props.defaultOpen} /> @@ -320,7 +428,7 @@ PART_MAPPING["text"] = function TextPartDisplay(props) { } PART_MAPPING["reasoning"] = function ReasoningPartDisplay(props) { - const part = props.part as any + const part = props.part as ReasoningPart return (
@@ -453,23 +561,41 @@ ToolRegistry.register({ ToolRegistry.register({ name: "task", render(props) { + const summary = () => + (props.metadata.summary ?? []) as { id: string; tool: string; state: { status: string; title?: string } }[] + + const autoScroll = createAutoScroll({ + working: () => true, + }) + return ( - - {(output) => ( -
- -
- )} -
+
+
+ + {(item) => { + const info = getToolInfo(item.tool) + return ( +
+ + {info.title} + + {item.state.title} + +
+ ) + }} +
+
+
) }, @@ -531,12 +657,14 @@ ToolRegistry.register({
@@ -601,13 +729,15 @@ ToolRegistry.register({ icon="checklist" trigger={{ title: "To-dos", - subtitle: `${props.input.todos?.filter((t: any) => t.status === "completed").length}/${props.input.todos?.length}`, + subtitle: props.input.todos + ? `${props.input.todos.filter((t: Todo) => t.status === "completed").length}/${props.input.todos.length}` + : "", }} >
- {(todo: any) => ( + {(todo: Todo) => (
{todo.content} diff --git a/packages/ui/src/components/provider-icons/sprite.svg b/packages/ui/src/components/provider-icons/sprite.svg index 3bb4b9617..22e1223a1 100644 --- a/packages/ui/src/components/provider-icons/sprite.svg +++ b/packages/ui/src/components/provider-icons/sprite.svg @@ -1,37 +1,48 @@ - - - - - - - + + + + + + + - + - + - + + + + @@ -58,59 +69,45 @@ stroke-linejoin="round" > - + - - + + - - - - - - + + + + + @@ -137,12 +134,16 @@ stroke-linejoin="round" > - + + - - - - - - - - SiliconFlow - - - - - + + + + + + + + + + @@ -247,90 +251,135 @@ stroke-linejoin="round" > - + - + - - - - - - - - - - - - - - - - - - - - + - - - - - Nebius - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -357,53 +406,51 @@ stroke-linejoin="round" > - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -430,105 +477,123 @@ stroke-linejoin="round" > - + - - - - - - - - - + - - - - - - - - - + + + + + + + + + + + + + - + - + + + + - - - - + - + + + + @@ -555,40 +620,44 @@ stroke-linejoin="round" > - + - + - + - + - + - + @@ -615,49 +684,39 @@ stroke-linejoin="round" > - - - - - - - - - - + + + + - - - - - + + + - - - - - + + + - - - - - - Baseten - - - + + + + + + + + - + @@ -737,32 +796,36 @@ stroke-linejoin="round" > - + - + - + - + - + diff --git a/packages/ui/src/components/provider-icons/types.ts b/packages/ui/src/components/provider-icons/types.ts index b3caf7edb..81fcc3678 100644 --- a/packages/ui/src/components/provider-icons/types.ts +++ b/packages/ui/src/components/provider-icons/types.ts @@ -6,6 +6,7 @@ export const iconNames = [ "zenmux", "zai", "zai-coding-plan", + "xiaomi", "xai", "wandb", "vultr", @@ -17,6 +18,7 @@ export const iconNames = [ "synthetic", "submodel", "siliconflow", + "siliconflow-cn", "scaleway", "sap-ai-core", "requesty", diff --git a/packages/ui/src/components/session-message-rail.css b/packages/ui/src/components/session-message-rail.css index dc2352c22..5729e4920 100644 --- a/packages/ui/src/components/session-message-rail.css +++ b/packages/ui/src/components/session-message-rail.css @@ -17,7 +17,7 @@ display: none; } -@container (min-width: 72rem) { +@container (min-width: 88rem) { [data-slot="session-message-rail-compact"] { display: none; } diff --git a/packages/ui/src/components/session-review.tsx b/packages/ui/src/components/session-review.tsx index b47ab55b1..9162b5216 100644 --- a/packages/ui/src/components/session-review.tsx +++ b/packages/ui/src/components/session-review.tsx @@ -25,7 +25,7 @@ export interface SessionReviewProps { export const SessionReview = (props: SessionReviewProps) => { const diffComponent = useDiffComponent() const [store, setStore] = createStore({ - open: props.diffs.map((d) => d.file), + open: props.diffs.length > 10 ? [] : props.diffs.map((d) => d.file), }) const handleChange = (open: string[]) => { @@ -78,7 +78,7 @@ export const SessionReview = (props: SessionReviewProps) => { {(diff) => ( - +
diff --git a/packages/ui/src/components/session-turn.tsx b/packages/ui/src/components/session-turn.tsx index 12725b983..e49e70864 100644 --- a/packages/ui/src/components/session-turn.tsx +++ b/packages/ui/src/components/session-turn.tsx @@ -1,9 +1,9 @@ -import { AssistantMessage, ToolPart } from "@opencode-ai/sdk/v2/client" +import { AssistantMessage, Part as PartType, TextPart, ToolPart } from "@opencode-ai/sdk/v2/client" import { useData } from "../context" import { useDiffComponent } from "../context/diff" import { getDirectory, getFilename } from "@opencode-ai/util/path" import { checksum } from "@opencode-ai/util/encode" -import { batch, createEffect, createMemo, For, Match, onCleanup, ParentProps, Show, Switch } from "solid-js" +import { createEffect, createMemo, For, Match, onCleanup, ParentProps, Show, Switch } from "solid-js" import { createResizeObserver } from "@solid-primitives/resize-observer" import { DiffChanges } from "./diff-changes" import { Typewriter } from "./typewriter" @@ -19,6 +19,46 @@ import { Button } from "./button" import { Spinner } from "./spinner" import { createStore } from "solid-js/store" import { DateTime, DurationUnit, Interval } from "luxon" +import { createAutoScroll } from "../hooks" + +function computeStatusFromPart(part: PartType | undefined): string | undefined { + if (!part) return undefined + + if (part.type === "tool") { + switch (part.tool) { + case "task": + return "Delegating work" + case "todowrite": + case "todoread": + return "Planning next steps" + case "read": + return "Gathering context" + case "list": + case "grep": + case "glob": + return "Searching the codebase" + case "webfetch": + return "Searching the web" + case "edit": + case "write": + return "Making edits" + case "bash": + return "Running commands" + default: + return undefined + } + } + if (part.type === "reasoning") { + const text = part.text ?? "" + const match = text.trimStart().match(/^\*\*(.+?)\*\*/) + if (match) return `Thinking · ${match[1].trim()}` + return "Thinking" + } + if (part.type === "text") { + return "Gathering thoughts" + } + return undefined +} export function SessionTurn( props: ParentProps<{ @@ -36,119 +76,152 @@ export function SessionTurn( ) { const data = useData() const diffComponent = useDiffComponent() - const messages = createMemo(() => data.store.message[props.sessionID] ?? []) - const userMessages = createMemo(() => - messages() - .filter((m) => m.role === "user") - .sort((a, b) => a.id.localeCompare(b.id)), - ) - const lastUserMessage = createMemo(() => userMessages().at(-1)!) - const message = createMemo(() => userMessages().find((m) => m.id === props.messageID)!) + + const derived = createMemo(() => { + const allMessages = data.store.message[props.sessionID] ?? [] + const userMessages = allMessages.filter((m) => m.role === "user").sort((a, b) => a.id.localeCompare(b.id)) + const lastUserMessage = userMessages.at(-1) + const message = userMessages.find((m) => m.id === props.messageID) + + if (!message) { + return { + message: undefined, + parts: [] as PartType[], + assistantMessages: [] as AssistantMessage[], + assistantParts: [] as PartType[], + lastAssistantMessage: undefined as AssistantMessage | undefined, + lastTextPart: undefined as PartType | undefined, + error: undefined, + hasSteps: false, + isShellMode: false, + rawStatus: undefined as string | undefined, + isLastUserMessage: false, + } + } + + const parts = data.store.part[message.id] ?? [] + const assistantMessages = allMessages.filter( + (m) => m.role === "assistant" && m.parentID === message.id, + ) as AssistantMessage[] + + const assistantParts: PartType[] = [] + for (const m of assistantMessages) { + const msgParts = data.store.part[m.id] + if (msgParts) { + for (const p of msgParts) { + if (p) assistantParts.push(p) + } + } + } + + const lastAssistantMessage = assistantMessages.at(-1) + const error = assistantMessages.find((m) => m.error)?.error + + let lastTextPart: PartType | undefined + for (let i = assistantParts.length - 1; i >= 0; i--) { + if (assistantParts[i]?.type === "text") { + lastTextPart = assistantParts[i] + break + } + } + + const hasSteps = assistantParts.some((p) => p?.type === "tool") + + let isShellMode = false + if (parts.every((p) => p?.type === "text" && p?.synthetic) && assistantParts.length === 1) { + const assistantPart = assistantParts[0] + if (assistantPart?.type === "tool" && assistantPart?.tool === "bash") { + isShellMode = true + } + } + + let resolvedParts = assistantParts + const currentTask = assistantParts.findLast( + (p) => + p && + p.type === "tool" && + p.tool === "task" && + p.state && + "metadata" in p.state && + p.state.metadata && + p.state.metadata.sessionId && + p.state.status === "running", + ) as ToolPart | undefined + + if (currentTask?.state && "metadata" in currentTask.state && currentTask.state.metadata?.sessionId) { + const taskMessages = data.store.message[currentTask.state.metadata.sessionId as string]?.filter( + (m) => m.role === "assistant", + ) + if (taskMessages) { + const taskParts: PartType[] = [] + for (const m of taskMessages) { + const msgParts = data.store.part[m.id] + if (msgParts) { + for (const p of msgParts) { + if (p) taskParts.push(p) + } + } + } + if (taskParts.length > 0) { + resolvedParts = taskParts + } + } + } + + const lastPart = resolvedParts.at(-1) + const rawStatus = computeStatusFromPart(lastPart) + + return { + message, + parts, + assistantMessages, + assistantParts, + lastAssistantMessage, + lastTextPart, + error, + hasSteps, + isShellMode, + rawStatus, + isLastUserMessage: message.id === lastUserMessage?.id, + } + }) + + const message = () => derived().message + const parts = () => derived().parts + const assistantMessages = () => derived().assistantMessages + const assistantParts = () => derived().assistantParts + const lastAssistantMessage = () => derived().lastAssistantMessage + const lastTextPart = () => derived().lastTextPart + const error = () => derived().error + const hasSteps = () => derived().hasSteps + const isShellMode = () => derived().isShellMode + const rawStatus = () => derived().rawStatus + const status = createMemo( () => data.store.session_status[props.sessionID] ?? { type: "idle", }, ) - const working = createMemo(() => status().type !== "idle" && message().id === lastUserMessage().id) + const working = createMemo(() => status().type !== "idle" && derived().isLastUserMessage) const retry = createMemo(() => { const s = status() if (s.type !== "retry") return return s }) - const assistantMessages = createMemo(() => { - return messages().filter((m) => m.role === "assistant" && m.parentID == message().id) as AssistantMessage[] - }) - const assistantParts = createMemo(() => assistantMessages().flatMap((m) => data.store.part[m.id]) ?? []) - const lastAssistantMessage = createMemo(() => assistantMessages().at(-1)) - const error = createMemo(() => assistantMessages().find((m) => m.error)?.error) - const parts = createMemo(() => data.store.part[message().id] ?? []) - const lastTextPart = createMemo(() => - assistantParts() - .filter((p) => p?.type === "text") - .at(-1), - ) - const summary = createMemo(() => message().summary?.body) - const response = createMemo(() => lastTextPart()?.text) - const hasSteps = createMemo(() => assistantParts().some((p) => p?.type === "tool")) - - const currentTask = createMemo( - () => - assistantParts().findLast( - (p) => - p && - p.type === "tool" && - p.tool === "task" && - p.state && - "metadata" in p.state && - p.state.metadata && - p.state.metadata.sessionId && - p.state.status === "running", - ) as ToolPart, - ) - const resolvedParts = createMemo(() => { - let resolved = assistantParts() - const task = currentTask() - if (task && task.state && "metadata" in task.state && task.state.metadata?.sessionId) { - const messages = data.store.message[task.state.metadata.sessionId as string]?.filter( - (m) => m.role === "assistant", - ) - resolved = messages?.flatMap((m) => data.store.part[m.id]) ?? assistantParts() - } - return resolved - }) - const lastPart = createMemo(() => resolvedParts().slice(-1)?.at(0)) - const rawStatus = createMemo(() => { - const last = lastPart() - if (!last) return undefined - - if (last.type === "tool") { - switch (last.tool) { - case "task": - return "Delegating work" - case "todowrite": - case "todoread": - return "Planning next steps" - case "read": - return "Gathering context" - case "list": - case "grep": - case "glob": - return "Searching the codebase" - case "webfetch": - return "Searching the web" - case "edit": - case "write": - return "Making edits" - case "bash": - return "Running commands" - default: - break - } - } else if (last.type === "reasoning") { - const text = last.text ?? "" - const match = text.trimStart().match(/^\*\*(.+?)\*\*/) - if (match) return `Thinking · ${match[1].trim()}` - return "Thinking" - } else if (last.type === "text") { - return "Gathering thoughts" - } - return undefined - }) - const hasDiffs = createMemo(() => message().summary?.diffs?.length) - const isShellMode = createMemo(() => { - if (parts().some((p) => p?.type !== "text" || !p?.synthetic)) return false - if (assistantParts().length !== 1) return false - const assistantPart = assistantParts()[0] - if (assistantPart?.type !== "tool") return false - if (assistantPart?.tool !== "bash") return false - return true - }) + const summary = () => message()?.summary?.body + const response = () => { + const part = lastTextPart() + return part?.type === "text" ? (part as TextPart).text : undefined + } + const hasDiffs = () => message()?.summary?.diffs?.length function duration() { + const msg = message() + if (!msg) return "" const completed = lastAssistantMessage()?.time.completed - const from = DateTime.fromMillis(message().time.created) + const from = DateTime.fromMillis(msg.time.created) const to = completed ? DateTime.fromMillis(completed) : DateTime.now() const interval = Interval.fromDateTimes(from, to) const unit: DurationUnit[] = interval.length("seconds") > 60 ? ["minutes", "seconds"] : ["seconds"] @@ -161,14 +234,14 @@ export function SessionTurn( }) } - let scrollRef: HTMLDivElement | undefined + const autoScroll = createAutoScroll({ + working, + onUserInteracted: props.onUserInteracted, + }) + const [store, setStore] = createStore({ - contentRef: undefined as HTMLDivElement | undefined, stickyTitleRef: undefined as HTMLDivElement | undefined, stickyTriggerRef: undefined as HTMLDivElement | undefined, - lastScrollTop: 0, - autoScrolled: false, - userScrolled: false, stickyHeaderHeight: 0, retrySeconds: 0, status: rawStatus(), @@ -190,53 +263,6 @@ export function SessionTurn( onCleanup(() => clearInterval(timer)) }) - function handleScroll() { - if (!scrollRef || store.autoScrolled) return - const scrollTop = scrollRef.scrollTop - console.log("scrollTop", scrollTop) - console.log("clientHeight", store.contentRef?.clientHeight) - const reset = scrollTop <= 0 && store.lastScrollTop > 0 && working() && !store.userScrolled - if (reset) { - setStore("lastScrollTop", scrollTop) - requestAnimationFrame(scrollToBottom) - return - } - const scrolledUp = scrollTop < store.lastScrollTop - 50 - if (scrolledUp && working()) { - console.log("scrolled up") - setStore("userScrolled", true) - props.onUserInteracted?.() - } - setStore("lastScrollTop", scrollTop) - } - - function handleInteraction() { - if (working()) { - setStore("userScrolled", true) - props.onUserInteracted?.() - } - } - - function scrollToBottom() { - if (!scrollRef || store.userScrolled || !working()) return - setStore("autoScrolled", true) - requestAnimationFrame(() => { - scrollRef?.scrollTo({ top: scrollRef.scrollHeight, behavior: "smooth" }) - requestAnimationFrame(() => { - batch(() => { - setStore("lastScrollTop", scrollRef?.scrollTop ?? 0) - setStore("autoScrolled", false) - }) - }) - }) - } - - createResizeObserver(() => store.contentRef, scrollToBottom) - - createEffect(() => { - if (!working()) setStore("userScrolled", false) - }) - createResizeObserver( () => store.stickyTitleRef, ({ height }) => { @@ -286,186 +312,208 @@ export function SessionTurn( return (
-
-
-
setStore("contentRef", el)} - data-message={message().id} - data-slot="session-turn-message-container" - class={props.classes?.container} - style={{ "--sticky-header-height": `${store.stickyHeaderHeight}px` }} - > - - - - - - {/* Title (sticky) */} -
setStore("stickyTitleRef", el)} data-slot="session-turn-sticky-title"> -
-
- - - - - -

{message().summary?.title}

-
-
-
-
-
- {/* User Message */} -
- -
- {/* Trigger (sticky) */} - -
setStore("stickyTriggerRef", el)} data-slot="session-turn-response-trigger"> - -
-
- {/* Response */} - 0}> -
- - {(assistantMessage) => { - const parts = createMemo(() => data.store.part[assistantMessage.id] ?? []) - const last = createMemo(() => - parts() - .filter((p) => p?.type === "text") - .at(-1), - ) - return ( +
+
+ + {(msg) => ( +
+ + + + + + {/* Title (sticky) */} +
setStore("stickyTitleRef", el)} data-slot="session-turn-sticky-title"> +
+
- - p?.id !== last()?.id)} /> + + - +

{msg().summary?.title}

- ) - }} - - +
+
+
+ {/* User Message */} +
+ +
+ {/* Trigger (sticky) */} + +
setStore("stickyTriggerRef", el)} data-slot="session-turn-response-trigger"> + +
+
+ {/* Response */} + 0}> +
+ + {(assistantMessage) => { + const parts = createMemo(() => data.store.part[assistantMessage.id] ?? []) + const last = createMemo(() => + parts() + .filter((p) => p?.type === "text") + .at(-1), + ) + return ( + + + p?.id !== last()?.id)} + /> + + + + + + ) + }} + + + + {error()?.data?.message as string} + + +
+
+ {/* Summary */} + +
+
+ + + {(summary) => ( + <> +

Summary

+ + + )} +
+ + {(response) => ( + <> +

Response

+ + + )} +
+
+
+ + + {(diff) => ( + + + +
+
+ +
+ + + {getDirectory(diff.file)}‎ + + + {getFilename(diff.file)} +
+
+
+ + +
+
+
+
+ + + +
+ )} +
+
+
+
+ {error()?.data?.message as string} -
-
- {/* Summary */} - -
-
- - - {(summary) => ( - <> -

Summary

- - - )} -
- - {(response) => ( - <> -

Response

- - - )} -
-
-
- - - {(diff) => ( - - - -
-
- -
- - {getDirectory(diff.file)}‎ - - {getFilename(diff.file)} -
-
-
- - -
-
-
-
- - - -
- )} -
-
-
-
- - - {error()?.data?.message as string} - - - - -
+ + +
+ )} + {props.children}
diff --git a/packages/ui/src/components/switch.tsx b/packages/ui/src/components/switch.tsx index af70dfb5c..a8600aef4 100644 --- a/packages/ui/src/components/switch.tsx +++ b/packages/ui/src/components/switch.tsx @@ -1,5 +1,5 @@ import { Switch as Kobalte } from "@kobalte/core/switch" -import { children, Show, splitProps } from "solid-js" +import { Show, splitProps } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" export interface SwitchProps extends ParentProps> { @@ -9,13 +9,12 @@ export interface SwitchProps extends ParentProps> export function Switch(props: SwitchProps) { const [local, others] = splitProps(props, ["children", "class", "hideLabel", "description"]) - const resolved = children(() => local.children) return ( - + - {resolved()} + {local.children} diff --git a/packages/ui/src/context/data.tsx b/packages/ui/src/context/data.tsx index 16efe7779..f53253418 100644 --- a/packages/ui/src/context/data.tsx +++ b/packages/ui/src/context/data.tsx @@ -24,6 +24,13 @@ type Data = { export const { use: useData, provider: DataProvider } = createSimpleContext({ name: "Data", init: (props: { data: Data; directory: string }) => { - return { store: props.data, directory: props.directory } + return { + get store() { + return props.data + }, + get directory() { + return props.directory + }, + } }, }) diff --git a/packages/ui/src/hooks/create-auto-scroll.tsx b/packages/ui/src/hooks/create-auto-scroll.tsx new file mode 100644 index 000000000..d201fe36b --- /dev/null +++ b/packages/ui/src/hooks/create-auto-scroll.tsx @@ -0,0 +1,135 @@ +import { batch, createEffect } from "solid-js" +import { createStore } from "solid-js/store" +import { createResizeObserver } from "@solid-primitives/resize-observer" + +export interface AutoScrollOptions { + working: () => boolean + onUserInteracted?: () => void +} + +export function createAutoScroll(options: AutoScrollOptions) { + let scrollRef: HTMLElement | undefined + const [store, setStore] = createStore({ + contentRef: undefined as HTMLElement | undefined, + lastScrollTop: 0, + lastScrollHeight: 0, + lastContentWidth: 0, + autoScrolled: false, + userScrolled: false, + reflowing: false, + }) + + function scrollToBottom() { + if (!scrollRef || store.userScrolled || !options.working()) return + setStore("autoScrolled", true) + requestAnimationFrame(() => { + scrollRef?.scrollTo({ top: scrollRef.scrollHeight, behavior: "smooth" }) + requestAnimationFrame(() => { + batch(() => { + setStore("lastScrollTop", scrollRef?.scrollTop ?? 0) + setStore("lastScrollHeight", scrollRef?.scrollHeight ?? 0) + setStore("autoScrolled", false) + }) + }) + }) + } + + function handleScroll() { + if (!scrollRef || store.autoScrolled) return + + const scrollTop = scrollRef.scrollTop + const scrollHeight = scrollRef.scrollHeight + + if (store.reflowing) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + return + } + + const scrollHeightChanged = Math.abs(scrollHeight - store.lastScrollHeight) > 10 + const scrollTopDelta = scrollTop - store.lastScrollTop + + // Handle reflow-caused scroll position changes + if (scrollHeightChanged && scrollTopDelta < 0) { + const heightRatio = store.lastScrollHeight > 0 ? scrollHeight / store.lastScrollHeight : 1 + const expectedScrollTop = store.lastScrollTop * heightRatio + if (Math.abs(scrollTop - expectedScrollTop) < 100) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + return + } + } + + // Handle reset to top while working + const reset = scrollTop <= 0 && store.lastScrollTop > 0 && options.working() && !store.userScrolled + if (reset) { + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + requestAnimationFrame(scrollToBottom) + return + } + + // Detect intentional scroll up + const scrolledUp = scrollTop < store.lastScrollTop - 50 && !scrollHeightChanged + if (scrolledUp && options.working()) { + setStore("userScrolled", true) + options.onUserInteracted?.() + } + + batch(() => { + setStore("lastScrollTop", scrollTop) + setStore("lastScrollHeight", scrollHeight) + }) + } + + function handleInteraction() { + if (options.working()) { + setStore("userScrolled", true) + options.onUserInteracted?.() + } + } + + // Reset userScrolled when work completes + createEffect(() => { + if (!options.working()) setStore("userScrolled", false) + }) + + // Handle content resize + createResizeObserver( + () => store.contentRef, + ({ width }) => { + const widthChanged = Math.abs(width - store.lastContentWidth) > 5 + if (widthChanged && store.lastContentWidth > 0) { + setStore("reflowing", true) + requestAnimationFrame(() => { + requestAnimationFrame(() => { + setStore("reflowing", false) + if (options.working() && !store.userScrolled) { + scrollToBottom() + } + }) + }) + } else if (!store.reflowing) { + scrollToBottom() + } + setStore("lastContentWidth", width) + }, + ) + + return { + scrollRef: (el: HTMLElement | undefined) => { + scrollRef = el + }, + contentRef: (el: HTMLElement | undefined) => setStore("contentRef", el), + handleScroll, + handleInteraction, + scrollToBottom, + userScrolled: () => store.userScrolled, + } +} diff --git a/packages/ui/src/hooks/index.ts b/packages/ui/src/hooks/index.ts index 7eef78091..1c90a2e49 100644 --- a/packages/ui/src/hooks/index.ts +++ b/packages/ui/src/hooks/index.ts @@ -1 +1,2 @@ export * from "./use-filtered-list" +export * from "./create-auto-scroll" diff --git a/packages/ui/src/pierre/index.ts b/packages/ui/src/pierre/index.ts index f83fc82a2..824d96b11 100644 --- a/packages/ui/src/pierre/index.ts +++ b/packages/ui/src/pierre/index.ts @@ -10,6 +10,31 @@ export type DiffProps = FileDiffOptions & { } const unsafeCSS = ` +[data-diffs] { + --diffs-bg: light-dark(var(--diffs-light-bg), var(--diffs-dark-bg)); + --diffs-bg-buffer: var(--diffs-bg-buffer-override, light-dark( color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-mixer)))); + --diffs-bg-hover: var(--diffs-bg-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 97%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-mixer)))); + --diffs-bg-context: var(--diffs-bg-context-override, light-dark( color-mix(in lab, var(--diffs-bg) 98.5%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 92.5%, var(--diffs-mixer)))); + --diffs-bg-separator: var(--diffs-bg-separator-override, light-dark( color-mix(in lab, var(--diffs-bg) 96%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-mixer)))); + --diffs-fg: light-dark(var(--diffs-light), var(--diffs-dark)); + --diffs-fg-number: var(--diffs-fg-number-override, light-dark(color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg)), color-mix(in lab, var(--diffs-fg) 65%, var(--diffs-bg)))); + --diffs-deletion-base: var(--diffs-deletion-color-override, light-dark(var(--diffs-light-deletion-color, var(--diffs-deletion-color, rgb(255, 0, 0))), var(--diffs-dark-deletion-color, var(--diffs-deletion-color, rgb(255, 0, 0))))); + --diffs-addition-base: var(--diffs-addition-color-override, light-dark(var(--diffs-light-addition-color, var(--diffs-addition-color, rgb(0, 255, 0))), var(--diffs-dark-addition-color, var(--diffs-addition-color, rgb(0, 255, 0))))); + --diffs-modified-base: var(--diffs-modified-color-override, light-dark(var(--diffs-light-modified-color, var(--diffs-modified-color, rgb(0, 0, 255))), var(--diffs-dark-modified-color, var(--diffs-modified-color, rgb(0, 0, 255))))); + --diffs-bg-deletion: var(--diffs-bg-deletion-override, light-dark( color-mix(in lab, var(--diffs-bg) 98%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-number: var(--diffs-bg-deletion-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-hover: var(--diffs-bg-deletion-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 80%, var(--diffs-deletion-base)), color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-deletion-base)))); + --diffs-bg-deletion-emphasis: var(--diffs-bg-deletion-emphasis-override, light-dark(rgb(from var(--diffs-deletion-base) r g b / 0.7), rgb(from var(--diffs-deletion-base) r g b / 0.1))); + --diffs-bg-addition: var(--diffs-bg-addition-override, light-dark( color-mix(in lab, var(--diffs-bg) 98%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 92%, var(--diffs-addition-base)))); + --diffs-bg-addition-number: var(--diffs-bg-addition-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 91%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 85%, var(--diffs-addition-base)))); + --diffs-bg-addition-hover: var(--diffs-bg-addition-hover-override, light-dark( color-mix(in lab, var(--diffs-bg) 80%, var(--diffs-addition-base)), color-mix(in lab, var(--diffs-bg) 70%, var(--diffs-addition-base)))); + --diffs-bg-addition-emphasis: var(--diffs-bg-addition-emphasis-override, light-dark(rgb(from var(--diffs-addition-base) r g b / 0.07), rgb(from var(--diffs-addition-base) r g b / 0.1))); + --diffs-selection-base: var(--diffs-modified-base); + --diffs-selection-number-fg: light-dark( color-mix(in lab, var(--diffs-selection-base) 65%, var(--diffs-mixer)), color-mix(in lab, var(--diffs-selection-base) 75%, var(--diffs-mixer))); + --diffs-bg-selection: var(--diffs-bg-selection-override, light-dark( color-mix(in lab, var(--diffs-bg) 82%, var(--diffs-selection-base)), color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-selection-base)))); + --diffs-bg-selection-number: var(--diffs-bg-selection-number-override, light-dark( color-mix(in lab, var(--diffs-bg) 75%, var(--diffs-selection-base)), color-mix(in lab, var(--diffs-bg) 60%, var(--diffs-selection-base)))); +} + [data-diffs-header], [data-diffs] { [data-separator-wrapper] { diff --git a/packages/ui/src/styles/utilities.css b/packages/ui/src/styles/utilities.css index 66136d724..8c954f1fe 100644 --- a/packages/ui/src/styles/utilities.css +++ b/packages/ui/src/styles/utilities.css @@ -12,16 +12,16 @@ /* } */ ::-webkit-scrollbar-track { - background: var(--theme-background-panel); + background: transparent; } ::-webkit-scrollbar-thumb { - background-color: var(--theme-border-subtle); + background-color: var(--surface-float-base); border-radius: var(--radius-md); } * { - scrollbar-color: var(--theme-border-subtle) var(--theme-background-panel); + scrollbar-color: var(--surface-float-base) transparent; } } diff --git a/packages/util/package.json b/packages/util/package.json index d58a756b1..9194b1e9f 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.170", + "version": "1.0.191", "private": true, "type": "module", "exports": { diff --git a/packages/util/src/encode.ts b/packages/util/src/encode.ts index cc40fbe9d..fc1f783bf 100644 --- a/packages/util/src/encode.ts +++ b/packages/util/src/encode.ts @@ -1,9 +1,13 @@ export function base64Encode(value: string) { - return btoa(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "") + const bytes = new TextEncoder().encode(value) + const binary = Array.from(bytes, (b) => String.fromCharCode(b)).join("") + return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "") } export function base64Decode(value: string) { - return atob(value.replace(/-/g, "+").replace(/_/g, "/")) + const binary = atob(value.replace(/-/g, "+").replace(/_/g, "/")) + const bytes = Uint8Array.from(binary, (c) => c.charCodeAt(0)) + return new TextDecoder().decode(bytes) } export async function hash(content: string, algorithm = "SHA-256"): Promise { diff --git a/packages/util/src/retry.ts b/packages/util/src/retry.ts new file mode 100644 index 000000000..0014a604c --- /dev/null +++ b/packages/util/src/retry.ts @@ -0,0 +1,41 @@ +export interface RetryOptions { + attempts?: number + delay?: number + factor?: number + maxDelay?: number + retryIf?: (error: unknown) => boolean +} + +const TRANSIENT_MESSAGES = [ + "load failed", + "network connection was lost", + "network request failed", + "failed to fetch", + "econnreset", + "econnrefused", + "etimedout", + "socket hang up", +] + +function isTransientError(error: unknown): boolean { + if (!error) return false + const message = String(error instanceof Error ? error.message : error).toLowerCase() + return TRANSIENT_MESSAGES.some((m) => message.includes(m)) +} + +export async function retry(fn: () => Promise, options: RetryOptions = {}): Promise { + const { attempts = 3, delay = 500, factor = 2, maxDelay = 10000, retryIf = isTransientError } = options + + let lastError: unknown + for (let attempt = 0; attempt < attempts; attempt++) { + try { + return await fn() + } catch (error) { + lastError = error + if (attempt === attempts - 1 || !retryIf(error)) throw error + const wait = Math.min(delay * Math.pow(factor, attempt), maxDelay) + await new Promise((resolve) => setTimeout(resolve, wait)) + } + } + throw lastError +} diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs index d75686a8e..7ecf2bfd9 100644 --- a/packages/web/astro.config.mjs +++ b/packages/web/astro.config.mjs @@ -78,6 +78,7 @@ export default defineConfig({ "lsp", "mcp-servers", "acp", + "skills", "custom-tools", ], }, diff --git a/packages/web/package.json b/packages/web/package.json index bf62419ad..191e3e594 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,7 +1,7 @@ { "name": "@opencode-ai/web", "type": "module", - "version": "1.0.170", + "version": "1.0.191", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", @@ -28,8 +28,7 @@ "marked-shiki": "1.2.1", "rehype-autolink-headings": "7.1.0", "remeda": "catalog:", - "sharp": "0.32.5", - "shiki": "3.4.2", + "shiki": "catalog:", "solid-js": "catalog:", "toolbeam-docs-theme": "0.4.8" }, diff --git a/packages/web/src/components/Lander.astro b/packages/web/src/components/Lander.astro index f015fd0a6..fd3732746 100644 --- a/packages/web/src/components/Lander.astro +++ b/packages/web/src/components/Lander.astro @@ -133,9 +133,9 @@ if (image) {

Mise

-