diff --git a/.github/workflows/docs-update.yml b/.github/workflows/docs-update.yml index 559e74176..11d6a9c82 100644 --- a/.github/workflows/docs-update.yml +++ b/.github/workflows/docs-update.yml @@ -2,9 +2,8 @@ name: Docs Update on: schedule: - # Run every 4 hours - - cron: "0 */4 * * *" - workflow_dispatch: # Allow manual trigger for testing + - cron: "0 */12 * * *" + workflow_dispatch: jobs: update-docs: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9e6b49339..ec98d7061 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -151,12 +151,12 @@ 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_VERSION: ${{ needs.publish.outputs.version }} @@ -191,7 +191,7 @@ 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 --verbose diff --git a/bun.lock b/bun.lock index dee942825..11099e1ed 100644 --- a/bun.lock +++ b/bun.lock @@ -27,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.190", + "version": "1.0.191", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -57,7 +105,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -84,7 +132,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@ai-sdk/anthropic": "2.0.0", "@ai-sdk/openai": "2.0.2", @@ -108,7 +156,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -132,55 +180,34 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.0.190", + "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.190", + "version": "1.0.191", "dependencies": { "@opencode-ai/ui": "workspace:*", "@opencode-ai/util": "workspace:*", @@ -209,7 +236,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -225,7 +252,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.0.190", + "version": "1.0.191", "bin": { "opencode": "./bin/opencode", }, @@ -319,7 +346,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@opencode-ai/sdk": "workspace:*", "zod": "catalog:", @@ -339,7 +366,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.0.190", + "version": "1.0.191", "devDependencies": { "@hey-api/openapi-ts": "0.88.1", "@tsconfig/node22": "catalog:", @@ -350,7 +377,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -361,36 +388,9 @@ "typescript": "catalog:", }, }, - "packages/tauri": { - "name": "@opencode-ai/tauri", - "version": "1.0.190", - "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.190", + "version": "1.0.191", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", @@ -406,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:", @@ -425,7 +425,7 @@ }, "packages/util": { "name": "@opencode-ai/util", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "zod": "catalog:", }, @@ -436,7 +436,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.0.190", + "version": "1.0.191", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", @@ -455,7 +455,7 @@ "marked-shiki": "1.2.1", "rehype-autolink-headings": "7.1.0", "remeda": "catalog:", - "shiki": "3.4.2", + "shiki": "catalog:", "solid-js": "catalog:", "toolbeam-docs-theme": "0.4.8", }, @@ -484,7 +484,7 @@ "@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", @@ -503,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", @@ -1148,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"], @@ -1172,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"], @@ -1316,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=="], @@ -1452,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=="], @@ -2068,7 +2069,7 @@ "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=="], @@ -3438,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=="], @@ -3982,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=="], @@ -4028,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=="], @@ -4102,23 +4107,21 @@ "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], - "@opencode-ai/tauri/@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/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=="], - "@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=="], @@ -4132,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=="], @@ -4208,8 +4219,6 @@ "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=="], @@ -4228,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=="], @@ -4342,6 +4353,10 @@ "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=="], @@ -4378,8 +4393,6 @@ "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=="], @@ -4686,32 +4699,24 @@ "@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], - "@opencode-ai/tauri/@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/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=="], @@ -5046,7 +5051,7 @@ "@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="], - "@opencode-ai/tauri/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "@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=="], 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 8ca9dd5dd..dbf753171 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,3 +1,3 @@ { - "nodeModules": "sha256-brPDbHqdp4/U8AIXtp75uDRI6K3e2FH2b7V/QNb07us=" + "nodeModules": "sha256-QlQblkUq49DOdvNNMNAzHHAfHxR6cZNmJtyzc4rD168=" } diff --git a/package.json b/package.json index 3134cc976..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,6 +44,7 @@ "@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", 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 88% rename from packages/tauri/index.html rename to packages/app/index.html index faeb1a1fd..9803517a0 100644 --- a/packages/tauri/index.html +++ b/packages/app/index.html @@ -14,7 +14,7 @@ - +
- + 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 94% rename from packages/desktop/src/app.tsx rename to packages/app/src/app.tsx index 9d000b8ff..11216643e 100644 --- a/packages/desktop/src/app.tsx +++ b/packages/app/src/app.tsx @@ -35,10 +35,10 @@ const url = iife(() => { 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.VITE_OPENCODE_SERVER) - return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` + if (import.meta.env.DEV) + return `http://${import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "localhost"}:${import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096"}` - return "/" + return "http://localhost:4096" }) export function App() { diff --git a/packages/desktop/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx similarity index 100% rename from packages/desktop/src/components/dialog-connect-provider.tsx rename to packages/app/src/components/dialog-connect-provider.tsx diff --git a/packages/desktop/src/components/dialog-manage-models.tsx b/packages/app/src/components/dialog-manage-models.tsx similarity index 100% rename from packages/desktop/src/components/dialog-manage-models.tsx rename to packages/app/src/components/dialog-manage-models.tsx diff --git a/packages/desktop/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx similarity index 100% rename from packages/desktop/src/components/dialog-select-file.tsx rename to packages/app/src/components/dialog-select-file.tsx diff --git a/packages/desktop/src/components/dialog-select-model-unpaid.tsx b/packages/app/src/components/dialog-select-model-unpaid.tsx similarity index 100% rename from packages/desktop/src/components/dialog-select-model-unpaid.tsx rename to packages/app/src/components/dialog-select-model-unpaid.tsx diff --git a/packages/desktop/src/components/dialog-select-model.tsx b/packages/app/src/components/dialog-select-model.tsx similarity index 100% rename from packages/desktop/src/components/dialog-select-model.tsx rename to packages/app/src/components/dialog-select-model.tsx diff --git a/packages/desktop/src/components/dialog-select-provider.tsx b/packages/app/src/components/dialog-select-provider.tsx similarity index 100% rename from packages/desktop/src/components/dialog-select-provider.tsx rename to packages/app/src/components/dialog-select-provider.tsx 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 100% rename from packages/desktop/src/components/header.tsx rename to packages/app/src/components/header.tsx diff --git a/packages/desktop/src/components/link.tsx b/packages/app/src/components/link.tsx similarity index 100% rename from packages/desktop/src/components/link.tsx rename to packages/app/src/components/link.tsx diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx similarity index 100% rename from packages/desktop/src/components/prompt-input.tsx rename to packages/app/src/components/prompt-input.tsx diff --git a/packages/desktop/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx similarity index 100% rename from packages/desktop/src/components/session-context-usage.tsx rename to packages/app/src/components/session-context-usage.tsx 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 100% rename from packages/desktop/src/context/command.tsx rename to packages/app/src/context/command.tsx diff --git a/packages/desktop/src/context/global-sdk.tsx b/packages/app/src/context/global-sdk.tsx similarity index 100% rename from packages/desktop/src/context/global-sdk.tsx rename to packages/app/src/context/global-sdk.tsx diff --git a/packages/desktop/src/context/global-sync.tsx b/packages/app/src/context/global-sync.tsx similarity index 97% rename from packages/desktop/src/context/global-sync.tsx rename to packages/app/src/context/global-sync.tsx index 27a89e7bc..ae40555d6 100644 --- a/packages/desktop/src/context/global-sync.tsx +++ b/packages/app/src/context/global-sync.tsx @@ -295,6 +295,15 @@ 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([ retry(() => globalSDK.client.path.get().then((x) => { diff --git a/packages/desktop/src/context/layout.tsx b/packages/app/src/context/layout.tsx similarity index 100% rename from packages/desktop/src/context/layout.tsx rename to packages/app/src/context/layout.tsx diff --git a/packages/desktop/src/context/local.tsx b/packages/app/src/context/local.tsx similarity index 100% rename from packages/desktop/src/context/local.tsx rename to packages/app/src/context/local.tsx 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 100% rename from packages/desktop/src/context/sync.tsx rename to packages/app/src/context/sync.tsx 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 78% rename from packages/desktop/src/pages/error.tsx rename to packages/app/src/pages/error.tsx index c7330c298..9914279ad 100644 --- a/packages/desktop/src/pages/error.tsx +++ b/packages/app/src/pages/error.tsx @@ -62,27 +62,49 @@ function formatInitError(error: InitError): string { } } -function formatErrorChain(error: unknown, depth = 0): string { +function formatErrorChain(error: unknown, depth = 0, parentMessage?: string): string { if (!error) return "Unknown error" - const indent = depth > 0 ? `\n${"─".repeat(40)}\nCaused by:\n` : "" - if (isInitError(error)) { - return indent + formatInitError(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 parts = [indent + `${error.name}: ${error.message}`] - if (error.stack) { - parts.push(error.stack) + 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) { - parts.push(formatErrorChain(error.cause, depth + 1)) + const causeResult = formatErrorChain(error.cause, depth + 1, error.message) + if (causeResult) { + parts.push(causeResult) + } } + return parts.join("\n\n") } - if (typeof error === "string") return indent + error + 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) } 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 100% rename from packages/desktop/src/pages/layout.tsx rename to packages/app/src/pages/layout.tsx diff --git a/packages/desktop/src/pages/session.tsx b/packages/app/src/pages/session.tsx similarity index 100% rename from packages/desktop/src/pages/session.tsx rename to packages/app/src/pages/session.tsx 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 aeb5fe2b2..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.190", + "version": "1.0.191", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", 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 +
+
+ + + + + + Linux (.AppImage) +
+ + 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/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 cf2eb502a..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.190", + "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 be09677ea..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.190", + "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 3b43f7653..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.190", + "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 7665f6d0c..23eab7f4d 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,62 +1,37 @@ { "name": "@opencode-ai/desktop", - "version": "1.0.190", - "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/tauri/scripts/prepare.ts b/packages/desktop/scripts/prepare.ts similarity index 100% rename from packages/tauri/scripts/prepare.ts rename to packages/desktop/scripts/prepare.ts diff --git a/packages/tauri/scripts/utils.ts b/packages/desktop/scripts/utils.ts similarity index 100% rename from packages/tauri/scripts/utils.ts rename to packages/desktop/scripts/utils.ts 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 100% rename from packages/tauri/src-tauri/src/lib.rs rename to packages/desktop/src-tauri/src/lib.rs 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 100% rename from packages/tauri/src-tauri/tauri.conf.json rename to packages/desktop/src-tauri/tauri.conf.json 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 57071a894..123a2028c 100644 --- a/packages/desktop/vite.config.ts +++ b/packages/desktop/vite.config.ts @@ -1,15 +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", - sourcemap: true, + 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 08a9fa517..3df50aff2 100644 --- a/packages/enterprise/package.json +++ b/packages/enterprise/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/enterprise", - "version": "1.0.190", + "version": "1.0.191", "private": true, "type": "module", "scripts": { 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 2e144191a..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.190" +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.190/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.190/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.190/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.190/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.190/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 3bd83040e..83de02f9d 100644 --- a/packages/function/package.json +++ b/packages/function/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/function", - "version": "1.0.190", + "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 3fb30f37c..59307256c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -1,6 +1,6 @@ { "$schema": "https://json.schemastore.org/package.json", - "version": "1.0.190", + "version": "1.0.191", "name": "opencode", "type": "module", "private": true, 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/tui/component/prompt/index.tsx b/packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx index 71937e179..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() { @@ -881,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/routes/session/header.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/header.tsx index 098ee83cc..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,124 +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 - - - Parent {keybind.print("session_parent")} - - - 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 029a012f8..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 @@ -961,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 @@ -1091,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> ) @@ -1646,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> @@ -1695,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 6c74e04fa..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}> 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/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 2f8e4ace4..40ee25565 100644 --- a/packages/opencode/src/mcp/index.ts +++ b/packages/opencode/src/mcp/index.ts @@ -15,6 +15,8 @@ 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 { @@ -251,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 } @@ -623,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/server/server.ts b/packages/opencode/src/server/server.ts index e92c46225..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({ @@ -2578,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", }, }) }), diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index e1edfdf51..339ba2f42 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -130,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, @@ -150,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/prompt.ts b/packages/opencode/src/session/prompt.ts index 748851b4f..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({ diff --git a/packages/opencode/src/skill/skill.ts b/packages/opencode/src/skill/skill.ts index 888460e84..41df88f8b 100644 --- a/packages/opencode/src/skill/skill.ts +++ b/packages/opencode/src/skill/skill.ts @@ -3,8 +3,10 @@ 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(), @@ -50,6 +52,16 @@ export namespace Skill { 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, 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/registry.ts b/packages/opencode/src/tool/registry.ts index 52d8e2cbb..69a45432d 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -115,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 @@ -130,7 +130,7 @@ export namespace ToolRegistry { using _ = log.time(t.id) return { id: t.id, - ...(await t.init()), + ...(await t.init({ agent })), } }), ) diff --git a/packages/opencode/src/tool/skill.ts b/packages/opencode/src/tool/skill.ts index ad5001da9..2503f7639 100644 --- a/packages/opencode/src/tool/skill.ts +++ b/packages/opencode/src/tool/skill.ts @@ -7,78 +7,94 @@ import { Permission } from "../permission" import { Wildcard } from "../util/wildcard" import { ConfigMarkdown } from "../config/markdown" -export const SkillTool = Tool.define("skill", async () => { - const skills = await Skill.all() - 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>", - ...skills.flatMap((skill) => [ - ` <skill>`, - ` <name>${skill.name}</name>`, - ` <description>${skill.description}</description>`, - ` </skill>`, - ]), - "</available_skills>", - ].join(" "), - parameters: z.object({ - name: z - .string() - .describe("The skill identifier from available_skills (e.g., 'code-review' or 'category/helper')"), - }), - async execute(params, ctx) { - const agent = await Agent.get(ctx.agent) - - const skill = await Skill.get(params.name) - - if (!skill) { - const available = Skill.all().then((x) => Object.keys(x).join(", ")) - throw new Error(`Skill "${params.name}" not found. Available skills: ${available || "none"}`) - } - - // Check permission using Wildcard.all on the skill ID - 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: { id: params.name, 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, - }, - } - }, - } +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/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/plugin/package.json b/packages/plugin/package.json index 330d74b39..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.190", + "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 a85bad961..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.190", + "version": "1.0.191", "type": "module", "scripts": { "typecheck": "tsgo --noEmit", diff --git a/packages/sdk/js/src/v2/gen/sdk.gen.ts b/packages/sdk/js/src/v2/gen/sdk.gen.ts index fa7a86463..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, @@ -188,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 * diff --git a/packages/sdk/js/src/v2/gen/types.gen.ts b/packages/sdk/js/src/v2/gen/types.gen.ts index 403f1d6a0..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 @@ -1897,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 diff --git a/packages/sdk/openapi.json b/packages/sdk/openapi.json index 24aea70a8..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", @@ -6499,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": { @@ -6793,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": { @@ -7079,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" }, @@ -7103,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" }, diff --git a/packages/slack/package.json b/packages/slack/package.json index 26cefb725..4be3b2ef1 100644 --- a/packages/slack/package.json +++ b/packages/slack/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/slack", - "version": "1.0.190", + "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 1db8ec12c..000000000 --- a/packages/tauri/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "@opencode-ai/tauri", - "private": true, - "version": "1.0.190", - "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/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 d051239d0..214d0b3a6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/ui", - "version": "1.0.190", + "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/util/package.json b/packages/util/package.json index 86405908e..9194b1e9f 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/util", - "version": "1.0.190", + "version": "1.0.191", "private": true, "type": "module", "exports": { diff --git a/packages/web/package.json b/packages/web/package.json index 4ce1b359e..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.190", + "version": "1.0.191", "scripts": { "dev": "astro dev", "dev:remote": "VITE_API_URL=https://api.opencode.ai astro dev", @@ -28,7 +28,7 @@ "marked-shiki": "1.2.1", "rehype-autolink-headings": "7.1.0", "remeda": "catalog:", - "shiki": "3.4.2", + "shiki": "catalog:", "solid-js": "catalog:", "toolbeam-docs-theme": "0.4.8" }, diff --git a/packages/web/src/content/docs/plugins.mdx b/packages/web/src/content/docs/plugins.mdx index 6982b4c93..6be545482 100644 --- a/packages/web/src/content/docs/plugins.mdx +++ b/packages/web/src/content/docs/plugins.mdx @@ -233,3 +233,30 @@ Include any state that should persist across compaction: ``` The `experimental.session.compacting` hook fires before the LLM generates a continuation summary. Use it to inject domain-specific context that the default compaction prompt would miss. + +You can also replace the compaction prompt entirely by setting `output.prompt`: + +```ts title=".opencode/plugin/custom-compaction.ts" +import type { Plugin } from "@opencode-ai/plugin" + +export const CustomCompactionPlugin: Plugin = async (ctx) => { + return { + "experimental.session.compacting": async (input, output) => { + // Replace the entire compaction prompt + output.prompt = ` +You are generating a continuation prompt for a multi-agent swarm session. + +Summarize: +1. The current task and its status +2. Which files are being modified and by whom +3. Any blockers or dependencies between agents +4. The next steps to complete the work + +Format as a structured prompt that a new agent can use to resume work. +` + }, + } +} +``` + +When `output.prompt` is set, it completely replaces the default compaction prompt. The `output.context` array is ignored in this case. diff --git a/packages/web/src/content/docs/sdk.mdx b/packages/web/src/content/docs/sdk.mdx index 4f9a2959f..1ff843103 100644 --- a/packages/web/src/content/docs/sdk.mdx +++ b/packages/web/src/content/docs/sdk.mdx @@ -123,6 +123,23 @@ The SDK exposes all server APIs through a type-safe client. --- +### Global + +| Method | Description | Response | +| ----------------- | ------------------------------- | ------------------------------------ | +| `global.health()` | Check server health and version | `{ healthy: true, version: string }` | + +--- + +#### Examples + +```javascript +const health = await client.global.health() +console.log(health.data.version) +``` + +--- + ### App | Method | Description | Response | diff --git a/packages/web/src/content/docs/server.mdx b/packages/web/src/content/docs/server.mdx index 60aff2003..427d8f505 100644 --- a/packages/web/src/content/docs/server.mdx +++ b/packages/web/src/content/docs/server.mdx @@ -68,11 +68,12 @@ The opencode server exposes the following APIs. --- -### Global Events +### Global -| Method | Path | Description | Response | -| ------ | --------------- | ------------------------------ | ------------ | -| `GET` | `/global/event` | Get global events (SSE stream) | Event stream | +| Method | Path | Description | Response | +| ------ | ---------------- | ------------------------------ | ------------------------------------ | +| `GET` | `/global/health` | Get server health and version | `{ healthy: true, version: string }` | +| `GET` | `/global/event` | Get global events (SSE stream) | Event stream | --- diff --git a/packages/web/src/content/docs/skills.mdx b/packages/web/src/content/docs/skills.mdx index 217d4b3d2..c1d433a83 100644 --- a/packages/web/src/content/docs/skills.mdx +++ b/packages/web/src/content/docs/skills.mdx @@ -4,7 +4,7 @@ description: "Define reusable behavior via SKILL.md definitions" --- Agent skills let OpenCode discover reusable instructions from your repo or home directory. -When a conversation matches a skill, the agent is prompted to read its `SKILL.md`. +Skills are loaded on-demand via the native `skill` tool—agents see available skills and can load the full content when needed. --- @@ -97,24 +97,123 @@ Ask clarifying questions if the target versioning scheme is unclear. --- -## Recognize prompt injection +## Recognize tool description -OpenCode adds an `<available_skills>` XML block to the system prompt. -Each entry includes the skill name, description, and its discovered location. +OpenCode lists available skills in the `skill` tool description. +Each entry includes the skill name and description: ```xml <available_skills> <skill> <name>git-release</name> <description>Create consistent releases and changelogs</description> - <location>.opencode/skill/git-release/SKILL.md</location> </skill> </available_skills> ``` +The agent loads a skill by calling the tool: + +``` +skill({ name: "git-release" }) +``` + +--- + +## Configure permissions + +Control which skills agents can access using pattern-based permissions in `opencode.json`: + +```json +{ + "permission": { + "skill": { + "pr-review": "allow", + "internal-*": "deny", + "experimental-*": "ask", + "*": "allow" + } + } +} +``` + +| Permission | Behavior | +| ---------- | ----------------------------------------- | +| `allow` | Skill loads immediately | +| `deny` | Skill hidden from agent, access rejected | +| `ask` | User prompted for approval before loading | + +Patterns support wildcards: `internal-*` matches `internal-docs`, `internal-tools`, etc. + +--- + +## Override per agent + +Give specific agents different permissions than the global defaults. + +**For custom agents** (in agent frontmatter): + +```yaml +--- +permission: + skill: + "documents-*": "allow" +--- +``` + +**For built-in agents** (in `opencode.json`): + +```json +{ + "agent": { + "plan": { + "permission": { + "skill": { + "internal-*": "allow" + } + } + } + } +} +``` + +--- + +## Disable the skill tool + +Completely disable skills for agents that shouldn't use them: + +**For custom agents**: + +```yaml +--- +tools: + skill: false +--- +``` + +**For built-in agents**: + +```json +{ + "agent": { + "plan": { + "tools": { + "skill": false + } + } + } +} +``` + +When disabled, the `<available_skills>` section is omitted entirely. + --- ## Troubleshoot loading -If a skill does not show up, verify the folder name matches `name` exactly. -Also check that `SKILL.md` is spelled in all caps and includes frontmatter. +If a skill does not show up: + +1. Verify `SKILL.md` is spelled in all caps +2. Check that frontmatter includes `name` and `description` +3. Ensure skill names are unique across all locations +4. Check permissions—skills with `deny` are hidden from agents diff --git a/script/publish-start.ts b/script/publish-start.ts index 9213e1352..a846df14d 100755 --- a/script/publish-start.ts +++ b/script/publish-start.ts @@ -17,7 +17,7 @@ if (!Script.preview) { .then((data: any) => data.version) const log = - await $`git log v${previous}..HEAD --oneline --format="%h %s" -- packages/opencode packages/sdk packages/plugin packages/tauri packages/desktop`.text() + await $`git log v${previous}..HEAD --oneline --format="%h %s" -- packages/opencode packages/sdk packages/plugin packages/desktop packages/app`.text() const commits = log.split("\n").filter((line) => line && !line.match(/^\w+ (ignore:|test:|chore:|ci:)/i)) @@ -64,7 +64,7 @@ if (!Script.preview) { Group the changes into these categories based on the [areas: ...] tags (omit any category with no changes): - **TUI**: Changes to "opencode" area (the terminal/CLI interface) - - **Desktop**: Changes to "desktop" or "tauri" areas (the desktop application) + - **Desktop**: Changes to "app" or "tauri" areas (the desktop application) - **SDK**: Changes to "sdk" or "plugin" areas (the SDK and plugin system) - **Extensions**: Changes to "extensions/zed", "extensions/vscode", or "github" areas (editor extensions and GitHub Action) - **Other**: Any user-facing changes that don't fit the above categories diff --git a/sdks/vscode/package.json b/sdks/vscode/package.json index a80b5eb54..791533d63 100644 --- a/sdks/vscode/package.json +++ b/sdks/vscode/package.json @@ -2,7 +2,7 @@ "name": "opencode", "displayName": "opencode", "description": "opencode for VS Code", - "version": "1.0.190", + "version": "1.0.191", "publisher": "sst-dev", "repository": { "type": "git", diff --git a/sst-env.d.ts b/sst-env.d.ts index 2c182ec35..813d654a0 100644 --- a/sst-env.d.ts +++ b/sst-env.d.ts @@ -148,6 +148,10 @@ declare module "sst" { "name": string "type": "sst.cloudflare.Bucket" } + "ZenDataNew": { + "name": string + "type": "sst.cloudflare.Bucket" + } } } /// <reference path="sst-env.d.ts" />