Compare commits
1 Commits
beta
...
typed-erro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b5dff1779 |
2
.github/workflows/deploy.yml
vendored
@@ -36,8 +36,6 @@ jobs:
|
||||
PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
|
||||
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
|
||||
STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
|
||||
HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }}
|
||||
INCIDENT_API_KEY: ${{ secrets.INCIDENT_API_KEY }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_ORG: ${{ vars.SENTRY_ORG }}
|
||||
SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }}
|
||||
|
||||
20
.github/workflows/publish.yml
vendored
@@ -304,7 +304,7 @@ jobs:
|
||||
|
||||
- name: Prepare
|
||||
run: bun ./scripts/prepare.ts
|
||||
working-directory: packages/desktop
|
||||
working-directory: packages/desktop-electron
|
||||
env:
|
||||
OPENCODE_VERSION: ${{ needs.version.outputs.version }}
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
@@ -315,7 +315,7 @@ jobs:
|
||||
|
||||
- name: Build
|
||||
run: bun run build
|
||||
working-directory: packages/desktop
|
||||
working-directory: packages/desktop-electron
|
||||
env:
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
@@ -329,7 +329,7 @@ jobs:
|
||||
- name: Package and publish
|
||||
if: needs.version.outputs.release
|
||||
run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish always --config electron-builder.config.ts
|
||||
working-directory: packages/desktop
|
||||
working-directory: packages/desktop-electron
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
@@ -343,14 +343,14 @@ jobs:
|
||||
- name: Package (no publish)
|
||||
if: ${{ !needs.version.outputs.release }}
|
||||
run: npx electron-builder ${{ matrix.settings.platform_flag }} --publish never --config electron-builder.config.ts
|
||||
working-directory: packages/desktop
|
||||
working-directory: packages/desktop-electron
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||
|
||||
- name: Create and upload macOS .app.tar.gz
|
||||
if: runner.os == 'macOS' && needs.version.outputs.release
|
||||
working-directory: packages/desktop/dist
|
||||
working-directory: packages/desktop-electron/dist
|
||||
env:
|
||||
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
||||
run: |
|
||||
@@ -377,9 +377,9 @@ jobs:
|
||||
shell: pwsh
|
||||
run: |
|
||||
$files = @()
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*.exe" | Select-Object -ExpandProperty FullName
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*.exe" | Select-Object -ExpandProperty FullName
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*unpacked\*.exe" | Select-Object -ExpandProperty FullName
|
||||
$files += Get-ChildItem "${{ github.workspace }}\packages\desktop-electron\dist\*unpacked\resources\opencode-cli.exe" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
|
||||
|
||||
foreach ($file in $files | Select-Object -Unique) {
|
||||
$sig = Get-AuthenticodeSignature $file
|
||||
@@ -391,13 +391,13 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: opencode-desktop-${{ matrix.settings.target }}
|
||||
path: packages/desktop/dist/*
|
||||
path: packages/desktop-electron/dist/*
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: needs.version.outputs.release
|
||||
with:
|
||||
name: latest-yml-${{ matrix.settings.target }}
|
||||
path: packages/desktop/dist/latest*.yml
|
||||
path: packages/desktop-electron/dist/latest*.yml
|
||||
|
||||
publish:
|
||||
needs:
|
||||
|
||||
308
bun.lock
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -85,7 +85,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -107,7 +107,6 @@
|
||||
"solid-js": "catalog:",
|
||||
"solid-list": "0.3.0",
|
||||
"solid-stripe": "0.8.1",
|
||||
"svix": "1.92.2",
|
||||
"vite": "catalog:",
|
||||
"zod": "catalog:",
|
||||
},
|
||||
@@ -120,7 +119,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -147,7 +146,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.64",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -171,7 +170,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -195,7 +194,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -229,7 +228,42 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@opencode-ai/app": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
"@sentry/solid": "catalog:",
|
||||
"@solid-primitives/i18n": "2.2.1",
|
||||
"@solid-primitives/storage": "catalog:",
|
||||
"@solidjs/meta": "catalog:",
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-clipboard-manager": "~2",
|
||||
"@tauri-apps/plugin-deep-link": "~2",
|
||||
"@tauri-apps/plugin-dialog": "~2",
|
||||
"@tauri-apps/plugin-http": "~2",
|
||||
"@tauri-apps/plugin-notification": "~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",
|
||||
"@sentry/vite-plugin": "catalog:",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"@types/bun": "catalog:",
|
||||
"@typescript/native-preview": "catalog:",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "catalog:",
|
||||
},
|
||||
},
|
||||
"packages/desktop-electron": {
|
||||
"name": "@opencode-ai/desktop-electron",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -275,7 +309,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/ui": "workspace:*",
|
||||
@@ -304,7 +338,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -320,7 +354,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -462,7 +496,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"effect": "catalog:",
|
||||
@@ -497,7 +531,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -512,7 +546,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -547,7 +581,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -596,7 +630,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -672,7 +706,7 @@
|
||||
"@types/bun": "1.3.12",
|
||||
"@types/cross-spawn": "6.0.6",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "24.12.2",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/semver": "7.7.1",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
||||
"ai": "6.0.168",
|
||||
@@ -1536,6 +1570,8 @@
|
||||
|
||||
"@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
|
||||
|
||||
"@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"],
|
||||
|
||||
"@opencode-ai/enterprise": ["@opencode-ai/enterprise@workspace:packages/enterprise"],
|
||||
|
||||
"@opencode-ai/function": ["@opencode-ai/function@workspace:packages/function"],
|
||||
@@ -2160,8 +2196,6 @@
|
||||
|
||||
"@speed-highlight/core": ["@speed-highlight/core@1.2.15", "", {}, "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw=="],
|
||||
|
||||
"@stablelib/base64": ["@stablelib/base64@1.0.1", "", {}, "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ=="],
|
||||
|
||||
"@standard-community/standard-json": ["@standard-community/standard-json@0.3.5", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "quansync": "^0.2.11", "sury": "^10.0.0", "typebox": "^1.0.17", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-to-json-schema"] }, "sha512-4+ZPorwDRt47i+O7RjyuaxHRK/37QY/LmgxlGrRrSTLYoFatEOzvqIc85GTlM18SFZ5E91C+v0o/M37wZPpUHA=="],
|
||||
|
||||
"@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.9", "", { "peerDependencies": { "@standard-community/standard-json": "^0.3.5", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "effect": "^3.17.14", "openapi-types": "^12.1.3", "sury": "^10.0.0", "typebox": "^1.0.0", "valibot": "^1.1.0", "zod": "^3.25.0 || ^4.0.0", "zod-openapi": "^4" }, "optionalPeers": ["arktype", "effect", "sury", "typebox", "valibot", "zod", "zod-openapi"] }, "sha512-htj+yldvN1XncyZi4rehbf9kLbu8os2Ke/rfqoZHCMHuw34kiF3LP/yQPdA0tQ940y8nDq3Iou8R3wG+AGGyvg=="],
|
||||
@@ -2236,8 +2270,54 @@
|
||||
|
||||
"@tauri-apps/api": ["@tauri-apps/api@2.10.1", "", {}, "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw=="],
|
||||
|
||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.10.1", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.10.1", "@tauri-apps/cli-darwin-x64": "2.10.1", "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", "@tauri-apps/cli-linux-arm64-gnu": "2.10.1", "@tauri-apps/cli-linux-arm64-musl": "2.10.1", "@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-musl": "2.10.1", "@tauri-apps/cli-win32-arm64-msvc": "2.10.1", "@tauri-apps/cli-win32-ia32-msvc": "2.10.1", "@tauri-apps/cli-win32-x64-msvc": "2.10.1" }, "bin": { "tauri": "tauri.js" } }, "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.10.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.10.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.10.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.10.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.10.1", "", { "os": "linux", "cpu": "none" }, "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.10.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ=="],
|
||||
|
||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.10.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg=="],
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.10.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw=="],
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.10.1", "", { "os": "win32", "cpu": "x64" }, "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg=="],
|
||||
|
||||
"@tauri-apps/plugin-clipboard-manager": ["@tauri-apps/plugin-clipboard-manager@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ=="],
|
||||
|
||||
"@tauri-apps/plugin-deep-link": ["@tauri-apps/plugin-deep-link@2.4.8", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-Cd2Cs960MGuGONeIwxOPx9wqwedetAHOGlwK5boJ/SMTfAtAyfErpfVPEn+EJzgXsJun8EKzsEumHjr+64V4fw=="],
|
||||
|
||||
"@tauri-apps/plugin-dialog": ["@tauri-apps/plugin-dialog@2.7.0", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-4nS/hfGMGCXiAS3LtVjH9AgsSAPJeG/7R+q8agTFqytjnMa4Zq95Bq8WzVDkckpanX+yyRHXnRtrKXkANKDHvw=="],
|
||||
|
||||
"@tauri-apps/plugin-http": ["@tauri-apps/plugin-http@2.5.8", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-oxd7oypzQeu8kAfFCrw534Kq7Cw+NzozcnCY21O4rz3A+veJiIiuSCMIprgGcZOcLAXFP9GmDhKUbhuKWcunRw=="],
|
||||
|
||||
"@tauri-apps/plugin-notification": ["@tauri-apps/plugin-notification@2.3.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-Zw+ZH18RJb41G4NrfHgIuofJiymusqN+q8fGUIIV7vyCH+5sSn5coqRv/MWB9qETsUs97vmU045q7OyseCV3Qg=="],
|
||||
|
||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.3", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-CCcUltXMOfUEArbf3db3kCE7Ggy1ExBEBl51Ko2ODJ6GDYHRp1nSNlQm5uNCFY5k7/ufaK5Ib3Du/Zir19IYQQ=="],
|
||||
|
||||
"@tauri-apps/plugin-os": ["@tauri-apps/plugin-os@2.3.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A=="],
|
||||
|
||||
"@tauri-apps/plugin-process": ["@tauri-apps/plugin-process@2.3.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA=="],
|
||||
|
||||
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.3.5", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg=="],
|
||||
|
||||
"@tauri-apps/plugin-store": ["@tauri-apps/plugin-store@2.4.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-0ClHS50Oq9HEvLPhNzTNFxbWVOqoAp3dRvtewQBeqfIQ0z5m3JRnOISIn2ZVPCrQC0MyGyhTS9DWhHjpigQE7A=="],
|
||||
|
||||
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.10.1", "", { "dependencies": { "@tauri-apps/api": "^2.10.1" } }, "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA=="],
|
||||
|
||||
"@tauri-apps/plugin-window-state": ["@tauri-apps/plugin-window-state@2.4.1", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-OuvdrzyY8Q5Dbzpj+GcrnV1iCeoZbcFdzMjanZMMcAEUNy/6PH5pxZPXpaZLOR7whlzXiuzx0L9EKZbH7zpdRw=="],
|
||||
|
||||
"@tediousjs/connection-string": ["@tediousjs/connection-string@0.5.0", "", {}, "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ=="],
|
||||
|
||||
"@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
|
||||
@@ -2338,7 +2418,7 @@
|
||||
|
||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="],
|
||||
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
|
||||
|
||||
@@ -3172,8 +3252,6 @@
|
||||
|
||||
"fast-querystring": ["fast-querystring@1.1.2", "", { "dependencies": { "fast-decode-uri-component": "^1.0.1" } }, "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg=="],
|
||||
|
||||
"fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="],
|
||||
|
||||
"fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="],
|
||||
|
||||
"fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="],
|
||||
@@ -4648,8 +4726,6 @@
|
||||
|
||||
"standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="],
|
||||
|
||||
"standardwebhooks": ["standardwebhooks@1.0.0", "", { "dependencies": { "@stablelib/base64": "^1.0.0", "fast-sha256": "^1.3.0" } }, "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg=="],
|
||||
|
||||
"stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="],
|
||||
|
||||
"statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="],
|
||||
@@ -4718,8 +4794,6 @@
|
||||
|
||||
"sury": ["sury@11.0.0-alpha.4", "", { "peerDependencies": { "rescript": "12.x" }, "optionalPeers": ["rescript"] }, "sha512-oeG/GJWZvQCKtGPpLbu0yCZudfr5LxycDo5kh7SJmKHDPCsEPJssIZL2Eb4Tl7g9aPEvIDuRrkS+L0pybsMEMA=="],
|
||||
|
||||
"svix": ["svix@1.92.2", "", { "dependencies": { "standardwebhooks": "1.0.0" } }, "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ=="],
|
||||
|
||||
"system-architecture": ["system-architecture@0.1.0", "", {}, "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.11", "", {}, "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA=="],
|
||||
@@ -4876,7 +4950,7 @@
|
||||
|
||||
"undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="],
|
||||
|
||||
@@ -5426,8 +5500,6 @@
|
||||
|
||||
"@gitlab/opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||
|
||||
"@happy-dom/global-registrator/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@hey-api/openapi-ts/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
|
||||
|
||||
"@hey-api/openapi-ts/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||
@@ -5556,10 +5628,14 @@
|
||||
|
||||
"@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/desktop/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"@opencode-ai/desktop/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||
|
||||
"@opencode-ai/desktop-electron/@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-electron/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
|
||||
|
||||
"@opencode-ai/desktop-electron/typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||
|
||||
"@opencode-ai/ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="],
|
||||
|
||||
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
|
||||
@@ -5602,24 +5678,16 @@
|
||||
|
||||
"@slack/bolt/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
||||
|
||||
"@slack/logger/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@slack/oauth/@slack/logger": ["@slack/logger@3.0.0", "", { "dependencies": { "@types/node": ">=12.0.0" } }, "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA=="],
|
||||
|
||||
"@slack/oauth/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@slack/socket-mode/@slack/logger": ["@slack/logger@3.0.0", "", { "dependencies": { "@types/node": ">=12.0.0" } }, "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA=="],
|
||||
|
||||
"@slack/socket-mode/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@slack/socket-mode/@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="],
|
||||
|
||||
"@slack/socket-mode/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="],
|
||||
|
||||
"@slack/web-api/@slack/logger": ["@slack/logger@3.0.0", "", { "dependencies": { "@types/node": ">=12.0.0" } }, "sha512-DTuBFbqu4gGfajREEMrkq5jBhcnskinhr4+AnfJEk48zhVeEv3XnUKGIX98B74kxhYsIMfApGGySTn7V3b5yBA=="],
|
||||
|
||||
"@slack/web-api/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@slack/web-api/eventemitter3": ["eventemitter3@3.1.2", "", {}, "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="],
|
||||
|
||||
"@slack/web-api/form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
|
||||
@@ -5680,62 +5748,8 @@
|
||||
|
||||
"@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
|
||||
|
||||
"@types/body-parser/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/cacache/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/cacheable-request/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/connect/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/cross-spawn/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/express-serve-static-core/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/fontkit/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/fs-extra/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/is-stream/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/jsonwebtoken/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/keyv/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/mssql/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/node-fetch/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/npm-registry-fetch/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/npmcli__arborist/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/npmlog/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/pacote/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/plist/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="],
|
||||
|
||||
"@types/readable-stream/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/responselike/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/sax/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/send/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/serve-static/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/ssri/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/tunnel/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/ws/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@types/yauzl/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||
|
||||
"@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
|
||||
@@ -5812,8 +5826,6 @@
|
||||
|
||||
"builder-util-runtime/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||
|
||||
"bun-types/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"bun-webgpu/@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="],
|
||||
|
||||
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||
@@ -5822,8 +5834,6 @@
|
||||
|
||||
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
|
||||
|
||||
"cloudflare/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
|
||||
|
||||
"condense-newlines/kind-of": ["kind-of@3.2.2", "", { "dependencies": { "is-buffer": "^1.1.5" } }, "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ=="],
|
||||
@@ -5858,8 +5868,6 @@
|
||||
|
||||
"effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"electron/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"electron-builder/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||
@@ -5914,8 +5922,6 @@
|
||||
|
||||
"gray-matter/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="],
|
||||
|
||||
"happy-dom/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"happy-dom/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
|
||||
|
||||
"html-minifier-terser/commander": ["commander@10.0.1", "", {}, "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug=="],
|
||||
@@ -5928,8 +5934,6 @@
|
||||
|
||||
"iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="],
|
||||
|
||||
"image-q/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"js-beautify/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||
|
||||
"js-beautify/nopt": ["nopt@7.2.1", "", { "dependencies": { "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w=="],
|
||||
@@ -6050,8 +6054,6 @@
|
||||
|
||||
"proper-lockfile/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
|
||||
|
||||
"protobufjs/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||
|
||||
"readable-stream/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||
@@ -6082,8 +6084,6 @@
|
||||
|
||||
"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/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"sitemap/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||
|
||||
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
@@ -6106,14 +6106,10 @@
|
||||
|
||||
"strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"stripe/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="],
|
||||
|
||||
"tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||
|
||||
"tedious/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
|
||||
|
||||
"tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="],
|
||||
@@ -6452,8 +6448,6 @@
|
||||
|
||||
"@gitlab/opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"@happy-dom/global-registrator/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@jsx-email/cli/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.19.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA=="],
|
||||
|
||||
"@jsx-email/cli/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.12", "", { "os": "android", "cpu": "arm" }, "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w=="],
|
||||
@@ -6624,6 +6618,8 @@
|
||||
|
||||
"@octokit/rest/@octokit/core/before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="],
|
||||
|
||||
"@opencode-ai/desktop-electron/@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.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=="],
|
||||
@@ -6646,14 +6642,6 @@
|
||||
|
||||
"@sentry/cli/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"@slack/logger/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@slack/oauth/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@slack/socket-mode/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@slack/web-api/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@slack/web-api/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
|
||||
@@ -6680,60 +6668,6 @@
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||
|
||||
"@types/body-parser/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/cacache/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/cacheable-request/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/connect/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/cross-spawn/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/express-serve-static-core/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/fontkit/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/fs-extra/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/is-stream/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/jsonwebtoken/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/keyv/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/mssql/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/node-fetch/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/npm-registry-fetch/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/npmcli__arborist/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/npmlog/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/pacote/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/plist/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/readable-stream/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/responselike/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/sax/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/send/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/serve-static/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/ssri/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/tunnel/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/ws/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@types/yauzl/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
|
||||
|
||||
"accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
@@ -6782,12 +6716,8 @@
|
||||
|
||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"bun-types/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||
|
||||
"cloudflare/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"crc/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
@@ -6806,8 +6736,6 @@
|
||||
|
||||
"electron-winstaller/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="],
|
||||
|
||||
"electron/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"esbuild-plugin-copy/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
|
||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
@@ -6820,14 +6748,10 @@
|
||||
|
||||
"gray-matter/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="],
|
||||
|
||||
"happy-dom/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"iconv-corefoundation/cli-truncate/slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="],
|
||||
|
||||
"iconv-corefoundation/cli-truncate/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
|
||||
|
||||
"image-q/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"js-beautify/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="],
|
||||
|
||||
"js-beautify/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||
@@ -6846,8 +6770,6 @@
|
||||
|
||||
"motion/framer-motion/motion-utils": ["motion-utils@12.36.0", "", {}, "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg=="],
|
||||
|
||||
"mssql/tedious/@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
|
||||
|
||||
"mssql/tedious/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"opencode-gitlab-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
@@ -6898,8 +6820,6 @@
|
||||
|
||||
"pkg-up/find-up/locate-path": ["locate-path@3.0.0", "", { "dependencies": { "p-locate": "^3.0.0", "path-exists": "^3.0.0" } }, "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="],
|
||||
|
||||
"protobufjs/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"readable-stream/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
@@ -6910,16 +6830,10 @@
|
||||
|
||||
"send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"sitemap/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"storybook/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
|
||||
|
||||
"stripe/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"tedious/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"tw-to-css/tailwindcss/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=="],
|
||||
|
||||
"tw-to-css/tailwindcss/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
@@ -7154,6 +7068,8 @@
|
||||
|
||||
"@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@opencode-ai/desktop-electron/@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=="],
|
||||
|
||||
"@sentry/bundler-plugin-core/glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||
@@ -7222,8 +7138,6 @@
|
||||
|
||||
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"mssql/tedious/@types/node/undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
|
||||
"opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="],
|
||||
|
||||
@@ -221,9 +221,6 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
|
||||
properties: { value: stripeWebhook.secret },
|
||||
})
|
||||
const INCIDENT_WEBHOOK_SIGNING_SECRET = new sst.Secret("INCIDENT_WEBHOOK_SIGNING_SECRET")
|
||||
const DISCORD_INCIDENT_WEBHOOK_URL = new sst.Secret("DISCORD_INCIDENT_WEBHOOK_URL")
|
||||
|
||||
const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
|
||||
|
||||
////////////////
|
||||
@@ -254,8 +251,6 @@ new sst.cloudflare.x.SolidStart("Console", {
|
||||
database,
|
||||
AUTH_API_URL,
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
INCIDENT_WEBHOOK_SIGNING_SECRET,
|
||||
DISCORD_INCIDENT_WEBHOOK_URL,
|
||||
STRIPE_SECRET_KEY,
|
||||
EMAILOCTOPUS_API_KEY,
|
||||
AWS_SES_ACCESS_KEY_ID,
|
||||
|
||||
@@ -1,320 +0,0 @@
|
||||
const displayName = (s: string) =>
|
||||
s
|
||||
.split("-")
|
||||
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
||||
.join(" ")
|
||||
.replace(/(?<=\d) (?=\d)/g, ".")
|
||||
|
||||
const resourceName = (s: string) => displayName(s).replace(/[^a-zA-Z0-9]/g, "")
|
||||
|
||||
const varSpec = (label: string, name: string) =>
|
||||
$jsonStringify({
|
||||
content: [
|
||||
{
|
||||
content: [
|
||||
{
|
||||
attrs: {
|
||||
name,
|
||||
label,
|
||||
missing: false,
|
||||
},
|
||||
type: "varSpec",
|
||||
},
|
||||
],
|
||||
type: "paragraph",
|
||||
},
|
||||
],
|
||||
type: "doc",
|
||||
})
|
||||
|
||||
const fields = {
|
||||
model: incident.getAlertAttributeOutput({ name: "Model" }),
|
||||
product: incident.getAlertAttributeOutput({ name: "Product" }),
|
||||
}
|
||||
|
||||
const alertSource = new incident.AlertSource("HoneycombAlertSource", {
|
||||
name: $app.stage === "production" ? "Honeycomb" : `Honeycomb (${$app.stage})`,
|
||||
sourceType: "honeycomb",
|
||||
template: {
|
||||
title: {
|
||||
literal: varSpec("Payload -> Title", "title"),
|
||||
},
|
||||
description: {
|
||||
literal: varSpec("Payload -> Description", "description"),
|
||||
},
|
||||
attributes: [
|
||||
{
|
||||
alertAttributeId: fields.model.id,
|
||||
binding: {
|
||||
value: {
|
||||
reference: 'expressions["model"]',
|
||||
},
|
||||
mergeStrategy: "first_wins",
|
||||
},
|
||||
},
|
||||
{
|
||||
alertAttributeId: fields.product.id,
|
||||
binding: {
|
||||
value: {
|
||||
reference: 'expressions["product"]',
|
||||
},
|
||||
mergeStrategy: "first_wins",
|
||||
},
|
||||
},
|
||||
],
|
||||
expressions: [
|
||||
{
|
||||
label: "Model",
|
||||
operations: [
|
||||
{
|
||||
operationType: "parse",
|
||||
parse: {
|
||||
returns: {
|
||||
array: false,
|
||||
type: fields.model.type,
|
||||
},
|
||||
source: "$['model']",
|
||||
},
|
||||
},
|
||||
],
|
||||
reference: "model",
|
||||
rootReference: "payload",
|
||||
},
|
||||
{
|
||||
label: "Product",
|
||||
operations: [
|
||||
{
|
||||
operationType: "parse",
|
||||
parse: {
|
||||
returns: {
|
||||
array: false,
|
||||
type: fields.product.type,
|
||||
},
|
||||
source: "$['product']",
|
||||
},
|
||||
},
|
||||
],
|
||||
reference: "product",
|
||||
rootReference: "payload",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
const webhookRecipient = new honeycomb.WebhookRecipient(`IncidentWebhook`, {
|
||||
name: $app.stage === "production" ? "Incident.io" : `Incident.io (${$app.stage})`,
|
||||
url: alertSource.alertEventsUrl,
|
||||
secret: alertSource.secretToken,
|
||||
templates: [
|
||||
{
|
||||
type: "trigger",
|
||||
body: $jsonStringify({
|
||||
title: "{{ .Name }}",
|
||||
description: "{{ .Description }}",
|
||||
status: "{{ .Alert.Status }}",
|
||||
deduplication_key: "{{ .Alert.InstanceID }}",
|
||||
source_url: "{{ .Result.URL }}",
|
||||
model: "{{ .Vars.model }}",
|
||||
product: "{{ .Vars.product }}",
|
||||
}),
|
||||
},
|
||||
],
|
||||
variables: [
|
||||
{
|
||||
name: "model",
|
||||
},
|
||||
{
|
||||
name: "product",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
new incident.AlertRoute("HoneycombAlertRoute", {
|
||||
name: $app.stage === "production" ? "Honeycomb" : `Honeycomb (${$app.stage})`,
|
||||
enabled: true,
|
||||
isPrivate: false,
|
||||
alertSources: [
|
||||
{
|
||||
alertSourceId: alertSource.id,
|
||||
conditionGroups: [
|
||||
{
|
||||
conditions: [
|
||||
{
|
||||
subject: "alert.title",
|
||||
operation: "is_set",
|
||||
paramBindings: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
conditionGroups: [
|
||||
{
|
||||
conditions: [
|
||||
{
|
||||
subject: "alert.title",
|
||||
operation: "is_set",
|
||||
paramBindings: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
expressions: [],
|
||||
escalationConfig: {
|
||||
autoCancelEscalations: true,
|
||||
escalationTargets: [],
|
||||
},
|
||||
incidentConfig: {
|
||||
autoDeclineEnabled: true,
|
||||
enabled: true,
|
||||
conditionGroups: [],
|
||||
deferTimeSeconds: 0,
|
||||
groupingKeys: [
|
||||
{
|
||||
reference: $interpolate`alert.attributes.${fields.model.id}`,
|
||||
},
|
||||
{
|
||||
reference: $interpolate`alert.attributes.${fields.product.id}`,
|
||||
},
|
||||
],
|
||||
groupingWindowSeconds: 900,
|
||||
},
|
||||
incidentTemplate: {
|
||||
name: {
|
||||
value: {
|
||||
literal: varSpec("Alert -> Title", "alert.title"),
|
||||
},
|
||||
},
|
||||
summary: {
|
||||
value: {
|
||||
literal: varSpec("Alert -> Description", "alert.description"),
|
||||
},
|
||||
},
|
||||
startInTriage: {
|
||||
value: {
|
||||
literal: "true",
|
||||
},
|
||||
},
|
||||
severity: {
|
||||
mergeStrategy: "first-wins",
|
||||
},
|
||||
incidentMode: {
|
||||
value: {
|
||||
literal: $app.stage === "production" ? "standard" : "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
type Product = "go" | "zen"
|
||||
|
||||
type Trigger = (opts: { model: string; product: Product }) => {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
json: honeycomb.GetQuerySpecificationOutputArgs
|
||||
threshold: { op: ">=" | "<="; value: number }
|
||||
baseline: 3600 | 86400
|
||||
}
|
||||
|
||||
type Model = { id: string; products: Product[]; triggers: Trigger[] }
|
||||
|
||||
const httpErrors: Trigger = ({ model, product }) => ({
|
||||
id: "increased-http-errors",
|
||||
title: `Increased HTTP Errors for ${displayName(model)} on ${displayName(product)}`,
|
||||
description: `Detected increased rate of HTTP errors for ${displayName(model)} on OpenCode ${displayName(product)}`,
|
||||
json: {
|
||||
calculations: [
|
||||
{
|
||||
op: "COUNT",
|
||||
name: "TOTAL",
|
||||
filterCombination: "AND",
|
||||
filters: [
|
||||
{ column: "model", op: "=", value: model },
|
||||
{ column: "isGoTier", op: "=", value: product === "go" ? "true" : "false" },
|
||||
],
|
||||
},
|
||||
{
|
||||
op: "COUNT",
|
||||
name: "FAILED",
|
||||
filterCombination: "AND",
|
||||
filters: [
|
||||
{ column: "model", op: "=", value: model },
|
||||
{ column: "isGoTier", op: "=", value: product === "go" ? "true" : "false" },
|
||||
{ column: "status", op: ">=", value: "400" },
|
||||
{ column: "status", op: "!=", value: "401" },
|
||||
],
|
||||
},
|
||||
],
|
||||
formulas: [{ name: "ERROR", expression: "$FAILED / $TOTAL" }],
|
||||
timeRange: 900,
|
||||
},
|
||||
// Alert when errors surge 50% compared to the previous period
|
||||
threshold: { op: ">=", value: 50 },
|
||||
// What previous time period to evaluate against
|
||||
baseline: 3600,
|
||||
})
|
||||
|
||||
const models: Model[] = [
|
||||
{ id: "kimi-k2.6", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "kimi-k2.5", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "deepseek-v4-flash", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "deepseek-v4-pro", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "glm-5.1", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
// { id: "glm-5", products: ["go"], triggers: [httpErrors] },
|
||||
{ id: "qwen3.6-plus", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "qwen3.5-plus", products: ["go"], triggers: [httpErrors] },
|
||||
{ id: "minimax-m2.7", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
// { id: "minimax-m2.5", products: ["go", "zen"], triggers: [httpErrors] },
|
||||
{ id: "mimo-v2.5-pro", products: ["go"], triggers: [httpErrors] },
|
||||
// { id: "mimo-v2.5", products: ["go"], triggers: [httpErrors] },
|
||||
// { id: "mimo-v2-omni", products: ["go"], triggers: [httpErrors] },
|
||||
// { id: "mimo-v2-pro", products: ["go"], triggers: [httpErrors] },
|
||||
{ id: "claude-opus-4-7", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "claude-opus-4-6", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "claude-sonnet-4-6", products: ["zen"], triggers: [httpErrors] },
|
||||
{ id: "gpt-5.5", products: ["zen"], triggers: [httpErrors] },
|
||||
{ id: "big-pickle", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "minimax-m2.5-free", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "hy3-preview-free", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "nemotron-3-super-free", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "trinity-large-preview-free", products: ["zen"], triggers: [httpErrors] },
|
||||
// { id: "ling-2.6-flash-free", products: ["zen"], triggers: [httpErrors] },
|
||||
]
|
||||
|
||||
if ($app.stage !== "production") {
|
||||
models.splice(1)
|
||||
}
|
||||
|
||||
for (const model of models) {
|
||||
for (const product of model.products) {
|
||||
for (const trigger of model.triggers) {
|
||||
const spec = trigger({ model: model.id, product })
|
||||
|
||||
new honeycomb.Trigger(resourceName(`${spec.id}-${product}-${model.id}`), {
|
||||
name: spec.title,
|
||||
description: spec.description,
|
||||
queryJson: honeycomb.getQuerySpecificationOutput(spec.json).json,
|
||||
alertType: "on_change",
|
||||
// This is the minimum when using % change detection
|
||||
frequency: 900,
|
||||
baselineDetails: [{ type: "percentage", offsetMinutes: spec.baseline / 60 }],
|
||||
thresholds: [{ ...spec.threshold, exceededLimit: 1 }],
|
||||
recipients: [
|
||||
{
|
||||
id: webhookRecipient.id,
|
||||
notificationDetails: [
|
||||
{
|
||||
variables: [
|
||||
{ name: "model", value: model.id },
|
||||
{ name: "product", value: product },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-Oo27Xkoo5HOzLaRs7FmSobzb1SNyidKIqk1+/BWtcqg=",
|
||||
"aarch64-linux": "sha256-/d3ukZERWvV7egmc2Rtxg5vroZaXkCs7yVcIjIa4CUE=",
|
||||
"aarch64-darwin": "sha256-1CX6n+9Wo2vAuPLekGsdjByReHQBbpKHwuK3L7Pfous=",
|
||||
"x86_64-darwin": "sha256-Jqx3LDSoLSy8em7c/455xLEy9Pn4DmoYLHDemA1i+9w="
|
||||
"x86_64-linux": "sha256-9wTDLZsuGjkWyVOb6AG2VRYPiaSj/lnXwVkSwNeDcns=",
|
||||
"aarch64-linux": "sha256-gmKlL2fQxY8bo+//8m9e1TNYJK3RXa4i8xsgtd046bc=",
|
||||
"aarch64-darwin": "sha256-ENSJK+7rZi3m342mjtGg9N0P6zWEypXMpI7QdFMydbc=",
|
||||
"x86_64-darwin": "sha256-gkxCxGh5dlwj03vZdz20pbiAwFEDpAlu/5iU8cwZOGI="
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"packageManager": "bun@1.3.13",
|
||||
"scripts": {
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"dev:desktop": "bun --cwd packages/desktop dev",
|
||||
"dev:desktop": "bun --cwd packages/desktop-electron dev",
|
||||
"dev:web": "bun --cwd packages/app dev",
|
||||
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
|
||||
"dev:storybook": "bun --cwd packages/storybook storybook",
|
||||
@@ -39,7 +39,7 @@
|
||||
"ulid": "3.0.1",
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "24.12.2",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/semver": "7.7.1",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -42,7 +42,6 @@ import { PromptProvider } from "@/context/prompt"
|
||||
import { ServerConnection, ServerProvider, serverName, useServer } from "@/context/server"
|
||||
import { SettingsProvider } from "@/context/settings"
|
||||
import { TerminalProvider } from "@/context/terminal"
|
||||
import { WslServersProvider } from "@/context/wsl-servers"
|
||||
import DirectoryLayout from "@/pages/directory-layout"
|
||||
import Layout from "@/pages/layout"
|
||||
import { ErrorPage } from "./pages/error"
|
||||
@@ -75,7 +74,7 @@ declare global {
|
||||
__OPENCODE__?: {
|
||||
updaterEnabled?: boolean
|
||||
deepLinks?: string[]
|
||||
activeServer?: string
|
||||
wsl?: boolean
|
||||
}
|
||||
api?: {
|
||||
setTitlebar?: (theme: { mode: "light" | "dark" }) => Promise<void>
|
||||
@@ -157,13 +156,11 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
|
||||
}}
|
||||
>
|
||||
<QueryProvider>
|
||||
<WslServersProvider>
|
||||
<DialogProvider>
|
||||
<MarkedProvider>
|
||||
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
|
||||
</MarkedProvider>
|
||||
</DialogProvider>
|
||||
</WslServersProvider>
|
||||
<DialogProvider>
|
||||
<MarkedProvider>
|
||||
<FileComponentProvider component={File}>{props.children}</FileComponentProvider>
|
||||
</MarkedProvider>
|
||||
</DialogProvider>
|
||||
</QueryProvider>
|
||||
</ErrorBoundary>
|
||||
</UiI18nBridge>
|
||||
@@ -286,11 +283,11 @@ function ConnectionError(props: { onRetry?: () => void; onServerSelected?: (key:
|
||||
)
|
||||
}
|
||||
|
||||
function ServerKey(props: { children: (key: ServerConnection.Key) => JSX.Element }) {
|
||||
function ServerKey(props: ParentProps) {
|
||||
const server = useServer()
|
||||
return (
|
||||
<Show when={server.key} keyed>
|
||||
{(key) => props.children(key)}
|
||||
{props.children}
|
||||
</Show>
|
||||
)
|
||||
}
|
||||
@@ -310,24 +307,22 @@ export function AppInterface(props: {
|
||||
>
|
||||
<ConnectionGate disableHealthCheck={props.disableHealthCheck}>
|
||||
<ServerKey>
|
||||
{() => (
|
||||
<QueryProvider>
|
||||
<GlobalSDKProvider>
|
||||
<GlobalSyncProvider>
|
||||
<Dynamic
|
||||
component={props.router ?? Router}
|
||||
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
|
||||
>
|
||||
<Route path="/" component={HomeRoute} />
|
||||
<Route path="/:dir" component={DirectoryLayout}>
|
||||
<Route path="/" component={SessionIndexRoute} />
|
||||
<Route path="/session/:id?" component={SessionRoute} />
|
||||
</Route>
|
||||
</Dynamic>
|
||||
</GlobalSyncProvider>
|
||||
</GlobalSDKProvider>
|
||||
</QueryProvider>
|
||||
)}
|
||||
<QueryProvider>
|
||||
<GlobalSDKProvider>
|
||||
<GlobalSyncProvider>
|
||||
<Dynamic
|
||||
component={props.router ?? Router}
|
||||
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
|
||||
>
|
||||
<Route path="/" component={HomeRoute} />
|
||||
<Route path="/:dir" component={DirectoryLayout}>
|
||||
<Route path="/" component={SessionIndexRoute} />
|
||||
<Route path="/session/:id?" component={SessionRoute} />
|
||||
</Route>
|
||||
</Dynamic>
|
||||
</GlobalSyncProvider>
|
||||
</GlobalSDKProvider>
|
||||
</QueryProvider>
|
||||
</ServerKey>
|
||||
</ConnectionGate>
|
||||
</ServerProvider>
|
||||
|
||||
@@ -5,26 +5,20 @@ import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||
import { Icon } from "@opencode-ai/ui/icon"
|
||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||
import { List } from "@opencode-ai/ui/list"
|
||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||
import { TextField } from "@opencode-ai/ui/text-field"
|
||||
import { useMutation } from "@tanstack/solid-query"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { batch, createEffect, createMemo, createResource, For, onCleanup, Show, untrack } from "solid-js"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { createEffect, createMemo, createResource, onCleanup, Show } from "solid-js"
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
import { DialogWslServer } from "@/components/dialog-wsl-server"
|
||||
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
|
||||
import { useWslServers } from "@/context/wsl-servers"
|
||||
import { type ServerHealth, useCheckServerHealth } from "@/utils/server-health"
|
||||
|
||||
const DEFAULT_USERNAME = "opencode"
|
||||
|
||||
interface DialogSelectServerProps {
|
||||
onNavigateHome?: () => void
|
||||
}
|
||||
|
||||
interface ServerFormProps {
|
||||
value: string
|
||||
name: string
|
||||
@@ -33,6 +27,7 @@ interface ServerFormProps {
|
||||
placeholder: string
|
||||
busy: boolean
|
||||
error: string
|
||||
status: boolean | undefined
|
||||
onChange: (value: string) => void
|
||||
onNameChange: (value: string) => void
|
||||
onUsernameChange: (value: string) => void
|
||||
@@ -49,17 +44,15 @@ function showRequestError(language: ReturnType<typeof useLanguage>, err: unknown
|
||||
})
|
||||
}
|
||||
|
||||
function isWslSidecar(conn: ServerConnection.Any): conn is ServerConnection.Sidecar & { variant: "wsl" } {
|
||||
return conn.type === "sidecar" && conn.variant === "wsl"
|
||||
}
|
||||
|
||||
function useDefaultServer() {
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const [defaultKey, defaultActions] = createResource(
|
||||
const [defaultKey, defaultUrlActions] = createResource(
|
||||
async () => {
|
||||
try {
|
||||
return (await platform.getDefaultServer?.()) ?? null
|
||||
const key = await platform.getDefaultServer?.()
|
||||
if (!key) return null
|
||||
return key
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
return null
|
||||
@@ -67,18 +60,52 @@ function useDefaultServer() {
|
||||
},
|
||||
{ initialValue: null },
|
||||
)
|
||||
|
||||
const canDefault = createMemo(() => !!platform.getDefaultServer && !!platform.setDefaultServer)
|
||||
const setDefault = async (key: ServerConnection.Key | null) => {
|
||||
try {
|
||||
await platform.setDefaultServer?.(key)
|
||||
defaultActions.mutate(key)
|
||||
defaultUrlActions.mutate(key)
|
||||
} catch (err) {
|
||||
showRequestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
return { defaultKey, canDefault, setDefault }
|
||||
}
|
||||
|
||||
function useServerPreview() {
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
|
||||
const looksComplete = (value: string) => {
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return false
|
||||
const host = normalized.replace(/^https?:\/\//, "").split("/")[0]
|
||||
if (!host) return false
|
||||
if (host.includes("localhost") || host.startsWith("127.0.0.1")) return true
|
||||
return host.includes(".") || host.includes(":")
|
||||
}
|
||||
|
||||
const previewStatus = async (
|
||||
value: string,
|
||||
username: string,
|
||||
password: string,
|
||||
setStatus: (value: boolean | undefined) => void,
|
||||
) => {
|
||||
setStatus(undefined)
|
||||
if (!looksComplete(value)) return
|
||||
const normalized = normalizeServerUrl(value)
|
||||
if (!normalized) return
|
||||
const http: ServerConnection.HttpBase = { url: normalized }
|
||||
if (username) http.username = username
|
||||
if (password) http.password = password
|
||||
const result = await checkServerHealth(http)
|
||||
setStatus(result.healthy)
|
||||
}
|
||||
|
||||
return { previewStatus }
|
||||
}
|
||||
|
||||
function ServerForm(props: ServerFormProps) {
|
||||
const language = useLanguage()
|
||||
const keyDown = (event: KeyboardEvent) => {
|
||||
@@ -144,18 +171,15 @@ function ServerForm(props: ServerFormProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
export function DialogSelectServer() {
|
||||
const navigate = useNavigate()
|
||||
const dialog = useDialog()
|
||||
const server = useServer()
|
||||
const platform = usePlatform()
|
||||
const language = useLanguage()
|
||||
const wslServers = useWslServers()
|
||||
const defaultServer = useDefaultServer()
|
||||
const { defaultKey, canDefault, setDefault } = useDefaultServer()
|
||||
const { previewStatus } = useServerPreview()
|
||||
const checkServerHealth = useCheckServerHealth()
|
||||
let disposed = false
|
||||
onCleanup(() => {
|
||||
disposed = true
|
||||
})
|
||||
const [store, setStore] = createStore({
|
||||
status: {} as Record<ServerConnection.Key, ServerHealth | undefined>,
|
||||
addServer: {
|
||||
@@ -165,9 +189,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
},
|
||||
addWsl: {
|
||||
showWizard: false,
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
editServer: {
|
||||
id: undefined as string | undefined,
|
||||
@@ -176,6 +198,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined as boolean | undefined,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -187,6 +210,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
password: "",
|
||||
error: "",
|
||||
showForm: false,
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
const resetEdit = () => {
|
||||
@@ -197,6 +221,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: "",
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -269,31 +294,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
},
|
||||
}))
|
||||
|
||||
const removeWslMutation = useMutation(() => ({
|
||||
mutationFn: async (key: ServerConnection.Key) => {
|
||||
await platform.wslServers?.removeServer(key)
|
||||
return key
|
||||
},
|
||||
onSuccess: async (key) => {
|
||||
server.remove(key)
|
||||
},
|
||||
onError: (err) => showRequestError(language, err),
|
||||
}))
|
||||
|
||||
const retryWslMutation = useMutation(() => ({
|
||||
mutationFn: async (key: ServerConnection.Key) => {
|
||||
await platform.wslServers?.startServer(key)
|
||||
},
|
||||
onError: (err) => showRequestError(language, err),
|
||||
}))
|
||||
|
||||
const updateWslMutation = useMutation(() => ({
|
||||
mutationFn: async (distro: string) => {
|
||||
await platform.wslServers?.installOpencode(distro)
|
||||
},
|
||||
onError: (err) => showRequestError(language, err),
|
||||
}))
|
||||
|
||||
const replaceServer = (original: ServerConnection.Http, next: ServerConnection.Http) => {
|
||||
const active = server.key
|
||||
const newConn = server.add(next)
|
||||
@@ -312,32 +312,6 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
})
|
||||
|
||||
const current = createMemo(() => items().find((x) => ServerConnection.key(x) === server.key) ?? items()[0])
|
||||
const wslState = () => wslServers.data
|
||||
const healthPollKey = createMemo(() =>
|
||||
items()
|
||||
.map((conn) =>
|
||||
[ServerConnection.key(conn), conn.http.url, conn.http.username ?? "", conn.http.password ?? ""].join("\n"),
|
||||
)
|
||||
.join("\n\n"),
|
||||
)
|
||||
const health = (key: ServerConnection.Key) => store.status[key]
|
||||
const wslRuntime = (conn: ServerConnection.Any) => {
|
||||
if (!isWslSidecar(conn)) return
|
||||
return wslState()?.servers.find((item) => item.config.id === ServerConnection.key(conn))?.runtime
|
||||
}
|
||||
const nonReadyWslServers = createMemo(() =>
|
||||
(wslState()?.servers ?? []).filter((item) => item.runtime.kind !== "ready"),
|
||||
)
|
||||
const canRetryWsl = (conn: ServerConnection.Any) => {
|
||||
const runtime = wslRuntime(conn)
|
||||
return runtime?.kind === "failed" || runtime?.kind === "stopped"
|
||||
}
|
||||
const canRetryWslRuntime = (kind: string) => kind === "failed" || kind === "stopped"
|
||||
const wslRuntimeLabel = (kind: string) => {
|
||||
if (kind === "starting") return "Starting"
|
||||
if (kind === "failed") return "Failed"
|
||||
return "Stopped"
|
||||
}
|
||||
|
||||
const sortedItems = createMemo(() => {
|
||||
const list = items()
|
||||
@@ -352,7 +326,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
return list.slice().sort((a, b) => {
|
||||
if (a === active) return -1
|
||||
if (b === active) return 1
|
||||
const diff = rank(health(ServerConnection.key(a))) - rank(health(ServerConnection.key(b)))
|
||||
const diff = rank(store.status[ServerConnection.key(a)]) - rank(store.status[ServerConnection.key(b)])
|
||||
if (diff !== 0) return diff
|
||||
return (order.get(a) ?? 0) - (order.get(b) ?? 0)
|
||||
})
|
||||
@@ -360,60 +334,39 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
|
||||
async function refreshHealth() {
|
||||
const results: Record<ServerConnection.Key, ServerHealth> = {}
|
||||
const list = untrack(items)
|
||||
await Promise.all(
|
||||
list.map(async (conn) => {
|
||||
items().map(async (conn) => {
|
||||
results[ServerConnection.key(conn)] = await checkServerHealth(conn.http)
|
||||
}),
|
||||
)
|
||||
if (disposed) return
|
||||
setStore("status", reconcile(results))
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
healthPollKey()
|
||||
items()
|
||||
void refreshHealth()
|
||||
const interval = setInterval(refreshHealth, 10_000)
|
||||
onCleanup(() => clearInterval(interval))
|
||||
})
|
||||
|
||||
const wslCheck = (conn: ServerConnection.Any) => {
|
||||
if (!isWslSidecar(conn)) return null
|
||||
return wslState()?.opencodeChecks[conn.distro] ?? null
|
||||
}
|
||||
|
||||
async function select(conn: ServerConnection.Any, persist?: boolean) {
|
||||
if (!persist && health(ServerConnection.key(conn))?.healthy === false) return
|
||||
const nextKey = ServerConnection.key(conn)
|
||||
const changed = server.key !== nextKey
|
||||
|
||||
const navigateHome = () => props.onNavigateHome?.()
|
||||
|
||||
const apply = () => {
|
||||
dialog.close()
|
||||
if (persist && conn.type === "http") {
|
||||
server.add(conn)
|
||||
navigateHome()
|
||||
return
|
||||
}
|
||||
|
||||
batch(() => {
|
||||
navigateHome()
|
||||
server.setActive(nextKey)
|
||||
})
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
await apply()
|
||||
if (!persist && store.status[ServerConnection.key(conn)]?.healthy === false) return
|
||||
dialog.close()
|
||||
if (persist && conn.type === "http") {
|
||||
server.add(conn)
|
||||
navigate("/")
|
||||
return
|
||||
}
|
||||
|
||||
apply()
|
||||
navigate("/")
|
||||
queueMicrotask(() => server.setActive(ServerConnection.key(conn)))
|
||||
}
|
||||
|
||||
const handleAddChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { url: value, error: "" })
|
||||
void previewStatus(value, store.addServer.username, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddNameChange = (value: string) => {
|
||||
@@ -424,16 +377,25 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const handleAddUsernameChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { username: value, error: "" })
|
||||
void previewStatus(store.addServer.url, value, store.addServer.password, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleAddPasswordChange = (value: string) => {
|
||||
if (addMutation.isPending) return
|
||||
setStore("addServer", { password: value, error: "" })
|
||||
void previewStatus(store.addServer.url, store.addServer.username, value, (next) =>
|
||||
setStore("addServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { value, error: "" })
|
||||
void previewStatus(value, store.editServer.username, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditNameChange = (value: string) => {
|
||||
@@ -444,15 +406,20 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const handleEditUsernameChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { username: value, error: "" })
|
||||
void previewStatus(store.editServer.value, value, store.editServer.password, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const handleEditPasswordChange = (value: string) => {
|
||||
if (editMutation.isPending) return
|
||||
setStore("editServer", { password: value, error: "" })
|
||||
void previewStatus(store.editServer.value, store.editServer.username, value, (next) =>
|
||||
setStore("editServer", { status: next }),
|
||||
)
|
||||
}
|
||||
|
||||
const mode = createMemo<"list" | "add-wsl" | "add" | "edit">(() => {
|
||||
if (store.addWsl.showWizard) return "add-wsl"
|
||||
const mode = createMemo<"list" | "add" | "edit">(() => {
|
||||
if (store.editServer.id) return "edit"
|
||||
if (store.addServer.showForm) return "add"
|
||||
return "list"
|
||||
@@ -466,11 +433,9 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
const resetForm = () => {
|
||||
resetAdd()
|
||||
resetEdit()
|
||||
setStore("addWsl", "showWizard", false)
|
||||
}
|
||||
|
||||
const startAdd = () => {
|
||||
setStore("addWsl", "showWizard", false)
|
||||
resetEdit()
|
||||
setStore("addServer", {
|
||||
showForm: true,
|
||||
@@ -479,11 +444,11 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: DEFAULT_USERNAME,
|
||||
password: "",
|
||||
error: "",
|
||||
status: undefined,
|
||||
})
|
||||
}
|
||||
|
||||
const startEdit = (conn: ServerConnection.Http) => {
|
||||
setStore("addWsl", "showWizard", false)
|
||||
resetAdd()
|
||||
setStore("editServer", {
|
||||
id: conn.http.url,
|
||||
@@ -492,22 +457,10 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
username: conn.http.username ?? "",
|
||||
password: conn.http.password ?? "",
|
||||
error: "",
|
||||
status: store.status[ServerConnection.key(conn)]?.healthy,
|
||||
})
|
||||
}
|
||||
|
||||
const startAddWsl = () => {
|
||||
resetAdd()
|
||||
resetEdit()
|
||||
setStore("addWsl", "showWizard", true)
|
||||
}
|
||||
|
||||
const handleAddedWsl = async (distro: string) => {
|
||||
const key = ServerConnection.Key.make(`wsl:${distro}`)
|
||||
setStore("addWsl", "showWizard", false)
|
||||
const conn = items().find((item) => ServerConnection.key(item) === key)
|
||||
if (conn) await select(conn)
|
||||
}
|
||||
|
||||
const submitForm = () => {
|
||||
if (mode() === "add") {
|
||||
if (addMutation.isPending) return
|
||||
@@ -524,22 +477,14 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
|
||||
const isFormMode = createMemo(() => mode() !== "list")
|
||||
const isAddMode = createMemo(() => mode() === "add")
|
||||
const isAddWslMode = createMemo(() => mode() === "add-wsl")
|
||||
const formBusy = createMemo(() => (isAddMode() ? addMutation.isPending : editMutation.isPending))
|
||||
const canAddWsl = createMemo(() => !!platform.wslServers && platform.os === "windows")
|
||||
|
||||
const formTitle = createMemo(() => {
|
||||
if (!isFormMode()) return language.t("dialog.server.title")
|
||||
return (
|
||||
<div class="flex items-center gap-2 -ml-2">
|
||||
<IconButton icon="arrow-left" variant="ghost" onClick={resetForm} aria-label={language.t("common.goBack")} />
|
||||
<span>
|
||||
{isAddWslMode()
|
||||
? "Add WSL server"
|
||||
: isAddMode()
|
||||
? language.t("dialog.server.add.title")
|
||||
: language.t("dialog.server.edit.title")}
|
||||
</span>
|
||||
<span>{isAddMode() ? language.t("dialog.server.add.title") : language.t("dialog.server.edit.title")}</span>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
@@ -550,107 +495,37 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
resetEdit()
|
||||
})
|
||||
|
||||
async function handleRemove(key: ServerConnection.Key) {
|
||||
server.remove(key)
|
||||
if (defaultServer.defaultKey() === key) await defaultServer.setDefault(null)
|
||||
async function handleRemove(url: ServerConnection.Key) {
|
||||
server.remove(url)
|
||||
if ((await platform.getDefaultServer?.()) === url) {
|
||||
void platform.setDefaultServer?.(null)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
title={formTitle()}
|
||||
fit={isAddWslMode()}
|
||||
class={isAddWslMode() ? "[&_[data-slot=dialog-body]]:flex-none [&_[data-slot=dialog-body]]:overflow-visible" : undefined}
|
||||
>
|
||||
<div class={isAddWslMode() ? "flex flex-col gap-2" : "flex flex-1 min-h-0 flex-col gap-2"}>
|
||||
<Dialog title={formTitle()}>
|
||||
<div class="flex flex-1 min-h-0 flex-col gap-2">
|
||||
<Show
|
||||
when={!isFormMode()}
|
||||
fallback={
|
||||
<Show
|
||||
when={isAddWslMode()}
|
||||
fallback={
|
||||
<ServerForm
|
||||
value={isAddMode() ? store.addServer.url : store.editServer.value}
|
||||
name={isAddMode() ? store.addServer.name : store.editServer.name}
|
||||
username={isAddMode() ? store.addServer.username : store.editServer.username}
|
||||
password={isAddMode() ? store.addServer.password : store.editServer.password}
|
||||
placeholder={language.t("dialog.server.add.placeholder")}
|
||||
busy={formBusy()}
|
||||
error={isAddMode() ? store.addServer.error : store.editServer.error}
|
||||
onChange={isAddMode() ? handleAddChange : handleEditChange}
|
||||
onNameChange={isAddMode() ? handleAddNameChange : handleEditNameChange}
|
||||
onUsernameChange={isAddMode() ? handleAddUsernameChange : handleEditUsernameChange}
|
||||
onPasswordChange={isAddMode() ? handleAddPasswordChange : handleEditPasswordChange}
|
||||
onSubmit={submitForm}
|
||||
onBack={resetForm}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<DialogWslServer onAdded={handleAddedWsl} />
|
||||
</Show>
|
||||
<ServerForm
|
||||
value={isAddMode() ? store.addServer.url : store.editServer.value}
|
||||
name={isAddMode() ? store.addServer.name : store.editServer.name}
|
||||
username={isAddMode() ? store.addServer.username : store.editServer.username}
|
||||
password={isAddMode() ? store.addServer.password : store.editServer.password}
|
||||
placeholder={language.t("dialog.server.add.placeholder")}
|
||||
busy={formBusy()}
|
||||
error={isAddMode() ? store.addServer.error : store.editServer.error}
|
||||
status={isAddMode() ? store.addServer.status : store.editServer.status}
|
||||
onChange={isAddMode() ? handleAddChange : handleEditChange}
|
||||
onNameChange={isAddMode() ? handleAddNameChange : handleEditNameChange}
|
||||
onUsernameChange={isAddMode() ? handleAddUsernameChange : handleEditUsernameChange}
|
||||
onPasswordChange={isAddMode() ? handleAddPasswordChange : handleEditPasswordChange}
|
||||
onSubmit={submitForm}
|
||||
onBack={resetForm}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Show when={nonReadyWslServers().length > 0}>
|
||||
<div class="px-5">
|
||||
<div class="bg-surface-base rounded-md overflow-hidden">
|
||||
<For each={nonReadyWslServers()}>
|
||||
{(item) => {
|
||||
const key = ServerConnection.Key.make(item.config.id)
|
||||
const retryable = () => canRetryWslRuntime(item.runtime.kind)
|
||||
return (
|
||||
<div class="min-h-14 p-3 flex items-center gap-3 border-b border-border-weak-base last:border-b-0">
|
||||
<div
|
||||
classList={{
|
||||
"size-1.5 rounded-full shrink-0": true,
|
||||
"bg-icon-critical-base": item.runtime.kind === "failed",
|
||||
"bg-border-weak-base": item.runtime.kind !== "failed",
|
||||
}}
|
||||
/>
|
||||
<div class="flex items-center gap-2 min-w-0 flex-1">
|
||||
<span class="text-14-medium text-text-base truncate">{item.config.distro}</span>
|
||||
<span class="text-11-regular text-text-weak border border-border-weak-base bg-surface-base px-1.5 py-0.5 rounded-md shrink-0">
|
||||
WSL
|
||||
</span>
|
||||
<span class="text-12-regular text-text-weak truncate">
|
||||
{wslRuntimeLabel(item.runtime.kind)}
|
||||
</span>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu.Trigger
|
||||
as={IconButton}
|
||||
icon="dot-grid"
|
||||
variant="ghost"
|
||||
class="shrink-0 size-8 hover:bg-surface-base-hover data-[expanded]:bg-surface-base-active"
|
||||
onClick={(e: MouseEvent) => e.stopPropagation()}
|
||||
onPointerDown={(e: PointerEvent) => e.stopPropagation()}
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="mt-1">
|
||||
<Show when={retryable()}>
|
||||
<DropdownMenu.Item onSelect={() => retryWslMutation.mutate(key)}>
|
||||
<DropdownMenu.ItemLabel>Retry start</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={retryable()}>
|
||||
<DropdownMenu.Separator />
|
||||
</Show>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => removeWslMutation.mutate(key)}
|
||||
class="text-text-on-critical-base hover:bg-surface-critical-weak"
|
||||
>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{language.t("dialog.server.menu.delete")}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<List
|
||||
search={{
|
||||
placeholder: language.t("dialog.server.search.placeholder"),
|
||||
@@ -659,7 +534,7 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
noInitialSelection
|
||||
emptyMessage={language.t("dialog.server.empty")}
|
||||
items={sortedItems}
|
||||
key={(x) => ServerConnection.key(x)}
|
||||
key={(x) => x.http.url}
|
||||
onSelect={(x) => {
|
||||
if (x) void select(x)
|
||||
}}
|
||||
@@ -668,35 +543,18 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
>
|
||||
{(i) => {
|
||||
const key = ServerConnection.key(i)
|
||||
const wsl = isWslSidecar(i)
|
||||
const wslDistro = wsl ? i.distro : undefined
|
||||
const blocked = () => health(key)?.healthy === false
|
||||
const canChangeDefault = () => defaultServer.canDefault() && i.type === "http"
|
||||
const canRemove = () => i.type === "http" || wsl
|
||||
const opencodeAction = () => {
|
||||
const check = wslCheck(i)
|
||||
if (!check) return null
|
||||
if (!check.resolvedPath) return "Install OpenCode"
|
||||
if (check.matchesDesktop === false) return "Update OpenCode"
|
||||
return null
|
||||
}
|
||||
const updating = () => {
|
||||
const job = wslState()?.job
|
||||
return job?.kind === "install-opencode" && job.distro === wslDistro
|
||||
}
|
||||
return (
|
||||
<div class="flex items-center gap-3 min-w-0 flex-1 w-full group/item">
|
||||
<div class="flex flex-col h-full items-start w-5">
|
||||
<ServerHealthIndicator health={health(key)} />
|
||||
<ServerHealthIndicator health={store.status[key]} />
|
||||
</div>
|
||||
<ServerRow
|
||||
conn={i}
|
||||
dimmed={blocked()}
|
||||
status={health(key)}
|
||||
version={wslCheck(i)?.version ?? undefined}
|
||||
dimmed={store.status[key]?.healthy === false}
|
||||
status={store.status[key]}
|
||||
class="flex items-center gap-3 min-w-0 flex-1"
|
||||
badge={
|
||||
<Show when={defaultServer.defaultKey() === ServerConnection.key(i)}>
|
||||
<Show when={defaultKey() === ServerConnection.key(i)}>
|
||||
<span class="text-text-base bg-surface-base text-14-regular px-1.5 rounded-xs">
|
||||
{language.t("dialog.server.status.default")}
|
||||
</span>
|
||||
@@ -704,32 +562,12 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
}
|
||||
showCredentials
|
||||
/>
|
||||
<div class="flex items-center justify-center gap-3 pl-4">
|
||||
<Show when={wsl && opencodeAction()}>
|
||||
{(label) => (
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={!!wslState()?.job}
|
||||
class="shrink-0"
|
||||
onPointerDown={(e: PointerEvent) => e.stopPropagation()}
|
||||
onClick={(e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
if (wslDistro) updateWslMutation.mutate(wslDistro)
|
||||
}}
|
||||
>
|
||||
<Show when={updating()}>
|
||||
<Spinner class="size-3.5 shrink-0" />
|
||||
</Show>
|
||||
{label()}
|
||||
</Button>
|
||||
)}
|
||||
</Show>
|
||||
<div class="flex items-center justify-center gap-4 pl-4">
|
||||
<Show when={ServerConnection.key(current()) === key}>
|
||||
<Icon name="check" class="h-6" />
|
||||
</Show>
|
||||
|
||||
<Show when={i.type === "http" || i.type === "sidecar"}>
|
||||
<Show when={i.type === "http"}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu.Trigger
|
||||
as={IconButton}
|
||||
@@ -741,54 +579,35 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
/>
|
||||
<DropdownMenu.Portal>
|
||||
<DropdownMenu.Content class="mt-1">
|
||||
<Show when={i.type === "http"}>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => {
|
||||
if (i.type !== "http") return
|
||||
startEdit(i)
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={wsl && canRetryWsl(i)}>
|
||||
<DropdownMenu.Item onSelect={() => retryWslMutation.mutate(key)}>
|
||||
<DropdownMenu.ItemLabel>Retry start</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={canChangeDefault() && defaultServer.defaultKey() !== key}>
|
||||
<DropdownMenu.Item onSelect={() => void defaultServer.setDefault(key)}>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => {
|
||||
if (i.type !== "http") return
|
||||
startEdit(i)
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.edit")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
<Show when={canDefault() && defaultKey() !== key}>
|
||||
<DropdownMenu.Item onSelect={() => setDefault(key)}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{language.t("dialog.server.menu.default")}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={canChangeDefault() && defaultServer.defaultKey() === key}>
|
||||
<DropdownMenu.Item onSelect={() => void defaultServer.setDefault(null)}>
|
||||
<Show when={canDefault() && defaultKey() === key}>
|
||||
<DropdownMenu.Item onSelect={() => setDefault(null)}>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{language.t("dialog.server.menu.defaultRemove")}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<Show when={canRemove() && (i.type === "http" || canChangeDefault() || canRetryWsl(i))}>
|
||||
<DropdownMenu.Separator />
|
||||
</Show>
|
||||
<Show when={canRemove()}>
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => {
|
||||
if (wsl) {
|
||||
removeWslMutation.mutate(key)
|
||||
return
|
||||
}
|
||||
void handleRemove(key)
|
||||
}}
|
||||
class="text-text-on-critical-base hover:bg-surface-critical-weak"
|
||||
>
|
||||
<DropdownMenu.ItemLabel>
|
||||
{language.t("dialog.server.menu.delete")}
|
||||
</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</Show>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item
|
||||
onSelect={() => handleRemove(ServerConnection.key(i))}
|
||||
class="text-text-on-critical-base hover:bg-surface-critical-weak"
|
||||
>
|
||||
<DropdownMenu.ItemLabel>{language.t("dialog.server.menu.delete")}</DropdownMenu.ItemLabel>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Portal>
|
||||
</DropdownMenu>
|
||||
@@ -802,32 +621,17 @@ export function DialogSelectServer(props: DialogSelectServerProps = {}) {
|
||||
|
||||
<div class="shrink-0 px-5 pb-5">
|
||||
<Show
|
||||
when={!isAddWslMode() && isFormMode()}
|
||||
when={isFormMode()}
|
||||
fallback={
|
||||
<Show when={!isAddWslMode()}>
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="plus-small"
|
||||
size="large"
|
||||
onClick={startAdd}
|
||||
class="py-1.5 pl-1.5 pr-3 flex items-center gap-1.5"
|
||||
>
|
||||
{language.t("dialog.server.add.button")}
|
||||
</Button>
|
||||
<Show when={canAddWsl()}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="plus-small"
|
||||
size="large"
|
||||
onClick={startAddWsl}
|
||||
class="py-1.5 pl-1.5 pr-3 flex items-center gap-1.5"
|
||||
>
|
||||
Add WSL
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon="plus-small"
|
||||
size="large"
|
||||
onClick={startAdd}
|
||||
class="py-1.5 pl-1.5 pr-3 flex items-center gap-1.5"
|
||||
>
|
||||
{language.t("dialog.server.add.button")}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Button variant="primary" size="large" onClick={submitForm} disabled={formBusy()} class="px-3 py-1.5">
|
||||
|
||||
@@ -1,582 +0,0 @@
|
||||
import { Button } from "@opencode-ai/ui/button"
|
||||
import { useDialog } from "@opencode-ai/ui/context/dialog"
|
||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { createEffect, createMemo, For, Match, onCleanup, Show, Switch } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { useLanguage } from "@/context/language"
|
||||
import { usePlatform } from "@/context/platform"
|
||||
import { useWslServers } from "@/context/wsl-servers"
|
||||
|
||||
type WslServerStep = "wsl" | "distro" | "opencode"
|
||||
|
||||
const STEPS: WslServerStep[] = ["wsl", "distro", "opencode"]
|
||||
|
||||
function isHiddenDistro(name: string) {
|
||||
return /^docker-desktop(?:-data)?$/i.test(name)
|
||||
}
|
||||
|
||||
interface DialogWslServerProps {
|
||||
onAdded?: (distro: string) => void | Promise<void>
|
||||
}
|
||||
|
||||
export function DialogWslServer(props: DialogWslServerProps = {}) {
|
||||
const language = useLanguage()
|
||||
const platform = usePlatform()
|
||||
const dialog = useDialog()
|
||||
const wslServers = useWslServers()
|
||||
const api = platform.wslServers!
|
||||
const [store, setStore] = createStore({
|
||||
step: undefined as WslServerStep | undefined,
|
||||
selectedDistro: null as string | null,
|
||||
installTarget: undefined as string | undefined,
|
||||
adding: false,
|
||||
})
|
||||
const current = () => wslServers.data
|
||||
let disposed = false
|
||||
onCleanup(() => {
|
||||
disposed = true
|
||||
})
|
||||
const busy = createMemo(() => !!current()?.job || store.adding)
|
||||
const selectedProbe = createMemo(() => {
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return current()?.distroProbes[distro] ?? null
|
||||
})
|
||||
const selectedInstalled = createMemo(() => {
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return (current()?.installed ?? []).find((item) => item.name === distro) ?? null
|
||||
})
|
||||
const visibleInstalledDistros = createMemo(() =>
|
||||
(current()?.installed ?? []).filter((item) => !isHiddenDistro(item.name)),
|
||||
)
|
||||
const visibleOnlineDistros = createMemo(() => (current()?.online ?? []).filter((item) => !isHiddenDistro(item.name)))
|
||||
const defaultInstalledDistro = createMemo(() => visibleInstalledDistros().find((item) => item.isDefault) ?? null)
|
||||
const opencodeCheck = createMemo(() => {
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return null
|
||||
return current()?.opencodeChecks[distro] ?? null
|
||||
})
|
||||
const distroWarningProbe = createMemo(() => {
|
||||
const probe = selectedProbe()
|
||||
if (!probe) return null
|
||||
if (distroReady()) return null
|
||||
return probe
|
||||
})
|
||||
const distroUnavailableMessage = createMemo(() => {
|
||||
const probe = distroWarningProbe()
|
||||
const distro = store.selectedDistro
|
||||
if (!probe || probe.canExecute || !distro) return null
|
||||
if (!selectedInstalled()) return `${distro} is not installed yet.`
|
||||
return `Open ${distro} once to finish setup.`
|
||||
})
|
||||
const distroMissingTools = createMemo(() => {
|
||||
const probe = distroWarningProbe()
|
||||
if (!probe?.canExecute) return null
|
||||
if (probe.hasBash && probe.hasCurl) return null
|
||||
return probe
|
||||
})
|
||||
const existingServerDistros = createMemo(() => new Set((current()?.servers ?? []).map((item) => item.config.distro)))
|
||||
const addableInstalledDistros = createMemo(() => {
|
||||
return visibleInstalledDistros().filter((item) => !existingServerDistros().has(item.name))
|
||||
})
|
||||
const installableDistros = createMemo(() => {
|
||||
const online = visibleOnlineDistros()
|
||||
const installed = new Set(visibleInstalledDistros().map((item) => item.name))
|
||||
const hasVersionedUbuntu = online.some((item) => /^Ubuntu-\d/.test(item.name))
|
||||
return online
|
||||
.filter((item) => !installed.has(item.name))
|
||||
.filter((item) => !(item.name === "Ubuntu" && hasVersionedUbuntu))
|
||||
})
|
||||
const installTarget = createMemo(() => installableDistros().find((item) => item.name === store.installTarget) ?? null)
|
||||
const installingDistro = createMemo(() => current()?.job?.kind === "install-distro")
|
||||
const installingOpencode = createMemo(() => {
|
||||
const job = current()?.job
|
||||
return job?.kind === "install-opencode" && job.distro === store.selectedDistro
|
||||
})
|
||||
const wslReady = createMemo(() => !!current()?.runtime?.available && !current()?.pendingRestart)
|
||||
const distroReady = createMemo(() => {
|
||||
const probe = selectedProbe()
|
||||
if (!probe || !store.selectedDistro) return false
|
||||
if (selectedInstalled()?.version === 1) return false
|
||||
return probe.canExecute && probe.hasBash && probe.hasCurl
|
||||
})
|
||||
const opencodeReady = createMemo(() => {
|
||||
const check = opencodeCheck()
|
||||
return !!check?.resolvedPath && !check.error
|
||||
})
|
||||
const allReady = createMemo(() => wslReady() && distroReady() && opencodeReady())
|
||||
const addDisabled = createMemo(() => {
|
||||
const job = current()?.job
|
||||
if (!job) return store.adding
|
||||
return store.adding || job.kind !== "probe-opencode"
|
||||
})
|
||||
const recommendedStep = createMemo<WslServerStep>(() => {
|
||||
if (!wslReady()) return "wsl"
|
||||
if (!distroReady()) return "distro"
|
||||
return "opencode"
|
||||
})
|
||||
// activeStep falls back to recommendedStep when the user hasn't picked one.
|
||||
// Once the user clicks a step tab we respect their choice rather than snapping
|
||||
// them back when a probe result updates recommendedStep.
|
||||
const activeStep = createMemo(() => store.step ?? recommendedStep())
|
||||
|
||||
const autoProbe = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state || busy()) return null
|
||||
if (state.pendingRestart) return null
|
||||
if (!state.runtime) return { key: "runtime", run: () => api.probeRuntime() }
|
||||
if (!wslReady()) return null
|
||||
if (!state.installed.length && !state.online.length) {
|
||||
return { key: "distros", run: () => api.refreshDistros() }
|
||||
}
|
||||
const distro = store.selectedDistro
|
||||
if (distro && !state.distroProbes[distro]) {
|
||||
return { key: `probe-distro:${distro}`, run: () => api.probeDistro(distro) }
|
||||
}
|
||||
if (!distro || !distroReady()) return null
|
||||
if (!state.opencodeChecks[distro]) {
|
||||
return { key: `probe-opencode:${distro}`, run: () => api.probeOpencode(distro) }
|
||||
}
|
||||
return null
|
||||
})
|
||||
|
||||
let lastAutoProbe: string | null = null
|
||||
createEffect(() => {
|
||||
const probe = autoProbe()
|
||||
if (!probe || probe.key === lastAutoProbe) return
|
||||
const key = probe.key
|
||||
lastAutoProbe = key
|
||||
void (async () => {
|
||||
try {
|
||||
await probe.run()
|
||||
} catch (err) {
|
||||
if (disposed) return
|
||||
// Allow the same probe to run again when reactive inputs next change
|
||||
// (e.g. user reselects a distro). Without this the user would be stuck
|
||||
// on a transient wsl.exe failure until they pick a different distro.
|
||||
if (lastAutoProbe === key) lastAutoProbe = null
|
||||
requestError(language, err)
|
||||
}
|
||||
})()
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const state = current()
|
||||
const distro = defaultInstalledDistro()
|
||||
if (!state || !distro || busy()) return
|
||||
if (store.selectedDistro) return
|
||||
if (existingServerDistros().has(distro.name)) return
|
||||
setStore("selectedDistro", distro.name)
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const distros = installableDistros()
|
||||
if (!distros.length) {
|
||||
if (store.installTarget) setStore("installTarget", undefined)
|
||||
return
|
||||
}
|
||||
if (store.installTarget && distros.some((item) => item.name === store.installTarget)) return
|
||||
setStore("installTarget", distros[0]!.name)
|
||||
})
|
||||
|
||||
const wslMessage = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state || state.job?.kind === "runtime") return "Checking WSL..."
|
||||
if (state.pendingRestart) return "Windows needs a restart to finish installing WSL."
|
||||
if (state.runtime?.available) return state.runtime.version ?? "WSL is ready."
|
||||
return state.runtime?.error ?? "WSL is required to continue."
|
||||
})
|
||||
|
||||
const distroMessage = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state) return "Checking distros..."
|
||||
const distro = store.selectedDistro
|
||||
if (state.job?.kind === "install-distro") return `Installing ${state.job.distro}...`
|
||||
if (state.job?.kind === "probe-distro") return `Checking ${state.job.distro}...`
|
||||
if (state.job?.kind === "distros") return "Listing distros..."
|
||||
if (distroUnavailableMessage()) return distroUnavailableMessage()!
|
||||
if (selectedProbe() && distroReady()) return `${selectedProbe()!.name} is ready.`
|
||||
if (distro) return `Finishing setup for ${distro}.`
|
||||
return "Pick a distro or install one below."
|
||||
})
|
||||
|
||||
const opencodeMessage = createMemo(() => {
|
||||
const state = current()
|
||||
if (!state) return "Checking OpenCode..."
|
||||
const distro = store.selectedDistro
|
||||
if (state.job?.kind === "probe-opencode" || state.job?.kind === "install-opencode") {
|
||||
return distro ? `Checking OpenCode in ${distro}...` : "Checking OpenCode..."
|
||||
}
|
||||
if (opencodeCheck()?.error) return opencodeCheck()!.error
|
||||
if (opencodeCheck()?.matchesDesktop === false) {
|
||||
return distro ? `Update OpenCode in ${distro}.` : "Update OpenCode."
|
||||
}
|
||||
if (opencodeReady()) return distro ? `OpenCode is ready in ${distro}.` : "OpenCode is ready."
|
||||
return distro ? `Install OpenCode in ${distro}.` : "Choose a distro first."
|
||||
})
|
||||
|
||||
const run = async (action: () => Promise<unknown>) => {
|
||||
try {
|
||||
await action()
|
||||
} catch (err) {
|
||||
requestError(language, err)
|
||||
}
|
||||
}
|
||||
|
||||
const runSelectedDistro = (action: (distro: string) => Promise<unknown>) => {
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return
|
||||
void run(() => action(distro))
|
||||
}
|
||||
|
||||
const selectDistro = (name: string) => {
|
||||
setStore("selectedDistro", name)
|
||||
setStore("step", undefined)
|
||||
}
|
||||
|
||||
const finish = async () => {
|
||||
const distro = store.selectedDistro
|
||||
if (!distro) return
|
||||
setStore("adding", true)
|
||||
try {
|
||||
await api.addServer(distro)
|
||||
if (props.onAdded) {
|
||||
await props.onAdded(distro)
|
||||
} else {
|
||||
dialog.close()
|
||||
}
|
||||
} catch (err) {
|
||||
requestError(language, err)
|
||||
} finally {
|
||||
setStore("adding", false)
|
||||
}
|
||||
}
|
||||
|
||||
const steps = createMemo(() => {
|
||||
const active = activeStep()
|
||||
const activeIndex = STEPS.indexOf(active)
|
||||
const recommendedIndex = STEPS.indexOf(recommendedStep())
|
||||
return STEPS.map((step) => {
|
||||
const index = STEPS.indexOf(step)
|
||||
return {
|
||||
step,
|
||||
title: step === "wsl" ? "WSL" : step === "distro" ? "Choose distro" : "OpenCode",
|
||||
state:
|
||||
active === step
|
||||
? "current"
|
||||
: step === "wsl"
|
||||
? wslReady()
|
||||
? "done"
|
||||
: "warning"
|
||||
: step === "distro"
|
||||
? distroReady()
|
||||
? "done"
|
||||
: index > activeIndex
|
||||
? "locked"
|
||||
: "warning"
|
||||
: opencodeCheck()?.matchesDesktop === false
|
||||
? "warning"
|
||||
: opencodeReady()
|
||||
? "done"
|
||||
: index > activeIndex
|
||||
? "locked"
|
||||
: "warning",
|
||||
locked: index > recommendedIndex,
|
||||
}
|
||||
})
|
||||
})
|
||||
const loadError = createMemo(() => {
|
||||
const error = wslServers.error
|
||||
if (!error) return "Failed to load WSL state."
|
||||
return error instanceof Error ? error.message : String(error)
|
||||
})
|
||||
|
||||
return (
|
||||
<div class="px-5 pb-5 flex flex-col gap-4">
|
||||
<Show when={!wslServers.isPending} fallback={<div class="px-1 py-6 text-14-regular text-text-weak">Loading...</div>}>
|
||||
<Show when={!wslServers.isError} fallback={<div class="px-1 py-6 text-14-regular text-text-weak">{loadError()}</div>}>
|
||||
<div class="flex gap-2 pb-1">
|
||||
<For each={steps()}>
|
||||
{(item) => (
|
||||
<button
|
||||
type="button"
|
||||
class="basis-0 flex-1 min-w-0 rounded-md border px-3 py-2 text-left transition-colors"
|
||||
classList={{
|
||||
"border-border-strong-base bg-surface-base-hover": item.state === "current",
|
||||
"border-icon-success-base/40 bg-surface-base": item.state === "done",
|
||||
"border-border-weak-base bg-background-base opacity-60": item.state === "locked",
|
||||
"border-icon-warning-base/40 bg-surface-base": item.state === "warning",
|
||||
}}
|
||||
disabled={item.locked}
|
||||
onClick={() => setStore("step", item.step)}
|
||||
>
|
||||
<div class="text-13-medium text-text-strong">{item.title}</div>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Match when={activeStep() === "wsl"}>
|
||||
<div class="rounded-md bg-surface-base p-4 flex flex-col gap-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-14-medium text-text-strong">WSL</div>
|
||||
<Show when={current()?.runtime && !wslReady() && !current()?.pendingRestart}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
disabled={busy()}
|
||||
onClick={() => void run(() => api.installWsl())}
|
||||
>
|
||||
Install WSL
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-weak whitespace-pre-wrap break-words">{wslMessage()}</div>
|
||||
<Show when={current()?.pendingRestart}>
|
||||
<div class="rounded-md border border-border-weak-base px-3 py-3 flex items-center justify-between gap-3">
|
||||
<div class="text-12-regular text-text-warning-base">Windows restart required.</div>
|
||||
<Button variant="secondary" size="large" onClick={() => void platform.restart()}>
|
||||
Relaunch OpenCode
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
<div class="flex items-center justify-end">
|
||||
<Button variant="secondary" size="large" disabled={busy() || !wslReady()} onClick={() => setStore("step", "distro")}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Match>
|
||||
|
||||
<Match when={activeStep() === "distro"}>
|
||||
<div class="rounded-md bg-surface-base p-4 flex flex-col gap-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-14-medium text-text-strong">Choose a distro</div>
|
||||
<Show when={store.selectedDistro}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="small"
|
||||
disabled={busy()}
|
||||
onClick={() => runSelectedDistro((distro) => api.probeDistro(distro))}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-weak whitespace-pre-wrap break-words">{distroMessage()}</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Show
|
||||
when={addableInstalledDistros().length > 0}
|
||||
fallback={
|
||||
<div class="text-12-regular text-text-weak">
|
||||
{visibleInstalledDistros().length
|
||||
? "All installed distros are already added."
|
||||
: current()?.runtime?.available
|
||||
? "No distros detected yet."
|
||||
: "Checking distros..."}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<For each={addableInstalledDistros()}>
|
||||
{(item) => (
|
||||
<button
|
||||
type="button"
|
||||
class="rounded-md border border-border-weak-base px-3 py-2 text-left transition-colors"
|
||||
classList={{ "bg-surface-raised-base": store.selectedDistro === item.name }}
|
||||
onClick={() => selectDistro(item.name)}
|
||||
>
|
||||
<div class="text-13-medium text-text-strong">{item.name}</div>
|
||||
<Show when={item.isDefault}>
|
||||
<div class="text-12-regular text-text-weak">Default</div>
|
||||
</Show>
|
||||
</button>
|
||||
)}
|
||||
</For>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<Show when={installableDistros().length > 0}>
|
||||
<div class="rounded-md border border-border-weak-base p-2 flex flex-col gap-2">
|
||||
<div class="px-1 flex items-center justify-between gap-3">
|
||||
<div class="text-12-medium text-text-weak">Install</div>
|
||||
<div class="flex items-center gap-2 shrink-0">
|
||||
<Show when={installingDistro()}>
|
||||
<Spinner class="h-4 w-4 text-icon-info-base shrink-0" />
|
||||
</Show>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
disabled={busy() || !installTarget()}
|
||||
onClick={() => void run(() => api.installDistro(installTarget()!.name))}
|
||||
>
|
||||
{installingDistro() ? "Installing..." : "Install"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
role="radiogroup"
|
||||
aria-label="Install distro"
|
||||
class="max-h-52 overflow-y-auto rounded-md bg-background-base"
|
||||
>
|
||||
<For each={installableDistros()}>
|
||||
{(item) => {
|
||||
const selected = () => store.installTarget === item.name
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={selected()}
|
||||
disabled={busy()}
|
||||
class="w-full px-3 py-2 flex items-center gap-3 text-left border-b border-border-weak-base last:border-b-0 transition-colors"
|
||||
classList={{
|
||||
"bg-surface-raised-base": selected(),
|
||||
"hover:bg-surface-base": !selected(),
|
||||
}}
|
||||
onClick={() => setStore("installTarget", item.name)}
|
||||
>
|
||||
<div
|
||||
class="mt-0.5 h-4 w-4 rounded-full border border-border-strong-base flex items-center justify-center shrink-0"
|
||||
classList={{ "border-text-strong": selected() }}
|
||||
>
|
||||
<div class="h-2 w-2 rounded-full bg-text-strong" classList={{ hidden: !selected() }} />
|
||||
</div>
|
||||
<div class="min-w-0 flex-1 text-13-medium text-text-strong truncate">{item.label}</div>
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
when={
|
||||
selectedInstalled()?.version === 1 ||
|
||||
distroUnavailableMessage() ||
|
||||
distroMissingTools()
|
||||
}
|
||||
>
|
||||
<div class="rounded-md border border-border-weak-base px-3 py-3 flex flex-col gap-1">
|
||||
<Show when={selectedInstalled()?.version === 1}>
|
||||
<div class="text-12-regular text-text-warning-base">WSL 2 is required.</div>
|
||||
</Show>
|
||||
<Show when={distroUnavailableMessage()}>
|
||||
{(message) => <div class="text-12-regular text-text-warning-base">{message()}</div>}
|
||||
</Show>
|
||||
<Show when={distroMissingTools()}>
|
||||
<div class="text-12-regular text-text-warning-base">This distro needs bash and curl.</div>
|
||||
</Show>
|
||||
</div>
|
||||
</Show>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
disabled={busy() || !selectedInstalled()}
|
||||
onClick={() => runSelectedDistro((distro) => api.openTerminal(distro))}
|
||||
>
|
||||
Open terminal
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="large"
|
||||
disabled={busy() || !store.selectedDistro}
|
||||
onClick={() => runSelectedDistro((distro) => api.probeDistro(distro))}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
disabled={busy() || !store.selectedDistro || !distroReady()}
|
||||
onClick={() => setStore("step", "opencode")}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Match>
|
||||
|
||||
<Match when={activeStep() === "opencode"}>
|
||||
<div class="rounded-md bg-surface-base p-4 flex flex-col gap-3">
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
<div class="text-14-medium text-text-strong">OpenCode</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Show when={store.selectedDistro}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="large"
|
||||
disabled={busy()}
|
||||
onClick={() => runSelectedDistro((distro) => api.probeOpencode(distro))}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</Show>
|
||||
<Show when={!opencodeReady() || opencodeCheck()?.matchesDesktop === false}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="large"
|
||||
disabled={busy()}
|
||||
onClick={() => runSelectedDistro((distro) => api.installOpencode(distro))}
|
||||
>
|
||||
<Show when={installingOpencode()}>
|
||||
<Spinner class="size-4 shrink-0" />
|
||||
</Show>
|
||||
{opencodeCheck()?.resolvedPath ? "Update OpenCode" : "Install OpenCode"}
|
||||
</Button>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-weak whitespace-pre-wrap break-words">{opencodeMessage()}</div>
|
||||
<Show when={opencodeCheck()?.matchesDesktop === false ? opencodeCheck() : null}>
|
||||
{(check) => (
|
||||
<div class="rounded-md border border-border-weak-base px-3 py-3 flex flex-col gap-1">
|
||||
<div class="text-12-regular text-text-weak">Path: {check().resolvedPath ?? "not found"}</div>
|
||||
<div class="text-12-regular text-text-weak">
|
||||
Version: {check().version ?? "unknown"}
|
||||
<Show when={check().expectedVersion}>
|
||||
{(expected) => <span>{` · desktop ${expected()}`}</span>}
|
||||
</Show>
|
||||
</div>
|
||||
<div class="text-12-regular text-text-warning-base">
|
||||
Installed version does not match the desktop app version.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
</div>
|
||||
</Match>
|
||||
</Switch>
|
||||
|
||||
<Show when={activeStep() === "opencode" && allReady() && store.selectedDistro}>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<Button variant="ghost" size="large" disabled={store.adding} onClick={() => dialog.close()}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button variant="primary" size="large" disabled={addDisabled()} onClick={() => void finish()}>
|
||||
{store.adding ? "Adding..." : "Add WSL server"}
|
||||
</Button>
|
||||
</div>
|
||||
</Show>
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function requestError(language: ReturnType<typeof useLanguage>, err: unknown) {
|
||||
console.error("WSL servers request failed", err instanceof Error ? (err.stack ?? err.message) : String(err))
|
||||
showToast({
|
||||
variant: "error",
|
||||
title: language.t("common.requestFailed"),
|
||||
description: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import type { ServerHealth } from "@/utils/server-health"
|
||||
interface ServerRowProps extends ParentProps {
|
||||
conn: ServerConnection.Any
|
||||
status?: ServerHealth
|
||||
version?: string
|
||||
class?: string
|
||||
nameClass?: string
|
||||
versionClass?: string
|
||||
@@ -32,8 +31,6 @@ export function ServerRow(props: ServerRowProps) {
|
||||
let nameRef: HTMLSpanElement | undefined
|
||||
let versionRef: HTMLSpanElement | undefined
|
||||
const name = createMemo(() => serverName(props.conn))
|
||||
const isWsl = createMemo(() => props.conn.type === "sidecar" && props.conn.variant === "wsl")
|
||||
const version = createMemo(() => props.version ?? props.status?.version)
|
||||
|
||||
const check = () => {
|
||||
const nameTruncated = nameRef ? nameRef.scrollWidth > nameRef.clientWidth : false
|
||||
@@ -44,7 +41,7 @@ export function ServerRow(props: ServerRowProps) {
|
||||
createEffect(() => {
|
||||
name()
|
||||
props.conn.http.url
|
||||
version()
|
||||
props.status?.version
|
||||
queueMicrotask(check)
|
||||
})
|
||||
|
||||
@@ -57,11 +54,8 @@ export function ServerRow(props: ServerRowProps) {
|
||||
const tooltipValue = () => (
|
||||
<span class="flex items-center gap-2">
|
||||
<span>{serverName(props.conn, true)}</span>
|
||||
<Show when={isWsl()}>
|
||||
<span class="text-text-invert-weak">WSL</span>
|
||||
</Show>
|
||||
<Show when={version()}>
|
||||
<span class="text-text-invert-weak">v{version()}</span>
|
||||
<Show when={props.status?.version}>
|
||||
<span class="text-text-invert-weak">v{props.status?.version}</span>
|
||||
</Show>
|
||||
</span>
|
||||
)
|
||||
@@ -82,20 +76,15 @@ export function ServerRow(props: ServerRowProps) {
|
||||
<span ref={nameRef} class={`${props.nameClass ?? "truncate"} min-w-0`}>
|
||||
{name()}
|
||||
</span>
|
||||
<Show when={isWsl()}>
|
||||
<span class="text-11-regular text-text-weak border border-border-weak-base bg-surface-base px-1.5 py-0.5 rounded-md shrink-0">
|
||||
WSL
|
||||
</span>
|
||||
</Show>
|
||||
<Show
|
||||
when={badge()}
|
||||
fallback={
|
||||
<Show when={version()}>
|
||||
<Show when={props.status?.version}>
|
||||
<span
|
||||
ref={versionRef}
|
||||
class={`${props.versionClass ?? "text-text-weak text-14-regular truncate"} min-w-0`}
|
||||
>
|
||||
v{version()}
|
||||
v{props.status?.version}
|
||||
</span>
|
||||
</Show>
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Switch } from "@opencode-ai/ui/switch"
|
||||
import { Tabs } from "@opencode-ai/ui/tabs"
|
||||
import { useMutation, useQueryClient } from "@tanstack/solid-query"
|
||||
import { showToast } from "@opencode-ai/ui/toast"
|
||||
import { useLocation, useNavigate } from "@solidjs/router"
|
||||
import { useNavigate } from "@solidjs/router"
|
||||
import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
|
||||
import { createStore, reconcile } from "solid-js/store"
|
||||
import { ServerHealthIndicator, ServerRow } from "@/components/server/server-row"
|
||||
@@ -156,14 +156,13 @@ const useMcpToggleMutation = () => {
|
||||
}))
|
||||
}
|
||||
|
||||
export function StatusPopoverBody(props: { shown: Accessor<boolean>; close?: () => void }) {
|
||||
export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
|
||||
const sync = useSync()
|
||||
const server = useServer()
|
||||
const platform = usePlatform()
|
||||
const dialog = useDialog()
|
||||
const language = useLanguage()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
|
||||
const fail = (err: unknown) => {
|
||||
showToast({
|
||||
@@ -252,16 +251,8 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean>; close?: ()
|
||||
aria-disabled={blocked()}
|
||||
onClick={() => {
|
||||
if (blocked()) return
|
||||
props.close?.()
|
||||
navigate("/")
|
||||
const activate = () => {
|
||||
if (location.pathname !== "/") {
|
||||
setTimeout(activate, 16)
|
||||
return
|
||||
}
|
||||
setTimeout(() => server.setActive(key), 0)
|
||||
}
|
||||
setTimeout(activate, 0)
|
||||
queueMicrotask(() => server.setActive(key))
|
||||
}}
|
||||
>
|
||||
<ServerHealthIndicator health={health[key]} />
|
||||
|
||||
@@ -58,7 +58,7 @@ export function StatusPopover() {
|
||||
<div class="w-[360px] h-14 rounded-xl bg-background-strong shadow-[var(--shadow-lg-border-base)]" />
|
||||
}
|
||||
>
|
||||
<Body shown={shown} close={() => setShown(false)} />
|
||||
<Body shown={shown} />
|
||||
</Suspense>
|
||||
</Show>
|
||||
</Popover>
|
||||
|
||||
@@ -35,9 +35,6 @@ type TauriApi = {
|
||||
const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
|
||||
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
|
||||
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
|
||||
const titlebarHeight = 40
|
||||
const minTitlebarZoom = 0.25
|
||||
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
|
||||
|
||||
export function Titlebar() {
|
||||
const layout = useLayout()
|
||||
@@ -54,14 +51,7 @@ export function Titlebar() {
|
||||
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
|
||||
const web = createMemo(() => platform.platform === "web")
|
||||
const zoom = () => platform.webviewZoom?.() ?? 1
|
||||
const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom())
|
||||
const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1)
|
||||
const minHeight = () => {
|
||||
if (mac()) return `${titlebarHeight / zoom()}px`
|
||||
if (windows()) return `${titlebarHeight / Math.min(titlebarZoom(), 1)}px`
|
||||
return undefined
|
||||
}
|
||||
const windowsControlsWidth = () => `${windowsControlsBaseWidth / Math.max(titlebarZoom(), 1)}px`
|
||||
const minHeight = () => (mac() ? `${40 / zoom()}px` : undefined)
|
||||
|
||||
const [history, setHistory] = createStore({
|
||||
stack: [] as string[],
|
||||
@@ -175,161 +165,156 @@ export function Titlebar() {
|
||||
|
||||
return (
|
||||
<header
|
||||
class="h-10 shrink-0 bg-background-base relative overflow-hidden"
|
||||
class="h-10 shrink-0 bg-background-base relative grid grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center"
|
||||
style={{ "min-height": minHeight() }}
|
||||
data-tauri-drag-region
|
||||
onMouseDown={drag}
|
||||
onDblClick={maximize}
|
||||
>
|
||||
<div
|
||||
class="grid h-full min-h-full w-full grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center"
|
||||
style={{ zoom: counterZoom() }}
|
||||
classList={{
|
||||
"flex items-center min-w-0": true,
|
||||
"pl-2": !mac(),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
"flex items-center min-w-0": true,
|
||||
"pl-2": !mac(),
|
||||
}}
|
||||
>
|
||||
<Show when={mac()}>
|
||||
<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />
|
||||
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md"
|
||||
onClick={layout.mobileSidebar.toggle}
|
||||
aria-label={language.t("sidebar.menu.toggle")}
|
||||
aria-expanded={layout.mobileSidebar.opened()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={!mac()}>
|
||||
<div class="xl:hidden w-[48px] shrink-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md"
|
||||
onClick={layout.mobileSidebar.toggle}
|
||||
aria-label={language.t("sidebar.menu.toggle")}
|
||||
aria-expanded={layout.mobileSidebar.opened()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<div class="flex items-center gap-1 shrink-0">
|
||||
<TooltipKeybind
|
||||
class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"}
|
||||
placement="bottom"
|
||||
title={language.t("command.sidebar.toggle")}
|
||||
keybind={command.keybind("sidebar.toggle")}
|
||||
<Show when={mac()}>
|
||||
<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />
|
||||
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md"
|
||||
onClick={layout.mobileSidebar.toggle}
|
||||
aria-label={language.t("sidebar.menu.toggle")}
|
||||
aria-expanded={layout.mobileSidebar.opened()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<Show when={!mac()}>
|
||||
<div class="xl:hidden w-[48px] shrink-0 flex items-center justify-center">
|
||||
<IconButton
|
||||
icon="menu"
|
||||
variant="ghost"
|
||||
class="titlebar-icon rounded-md"
|
||||
onClick={layout.mobileSidebar.toggle}
|
||||
aria-label={language.t("sidebar.menu.toggle")}
|
||||
aria-expanded={layout.mobileSidebar.opened()}
|
||||
/>
|
||||
</div>
|
||||
</Show>
|
||||
<div class="flex items-center gap-1 shrink-0">
|
||||
<TooltipKeybind
|
||||
class={web() ? "hidden xl:flex shrink-0 ml-14" : "hidden xl:flex shrink-0 ml-2"}
|
||||
placement="bottom"
|
||||
title={language.t("command.sidebar.toggle")}
|
||||
keybind={command.keybind("sidebar.toggle")}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="group/sidebar-toggle titlebar-icon w-8 h-6 p-0 box-border"
|
||||
onClick={layout.sidebar.toggle}
|
||||
aria-label={language.t("command.sidebar.toggle")}
|
||||
aria-expanded={layout.sidebar.opened()}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
class="group/sidebar-toggle titlebar-icon w-8 h-6 p-0 box-border"
|
||||
onClick={layout.sidebar.toggle}
|
||||
aria-label={language.t("command.sidebar.toggle")}
|
||||
aria-expanded={layout.sidebar.opened()}
|
||||
<Icon size="small" name={layout.sidebar.opened() ? "sidebar-active" : "sidebar"} />
|
||||
</Button>
|
||||
</TooltipKeybind>
|
||||
<div class="hidden xl:flex items-center shrink-0">
|
||||
<Show when={params.dir}>
|
||||
<div
|
||||
class="flex items-center shrink-0 w-8 mr-1"
|
||||
aria-hidden={layout.sidebar.opened() ? "true" : undefined}
|
||||
>
|
||||
<Icon size="small" name={layout.sidebar.opened() ? "sidebar-active" : "sidebar"} />
|
||||
</Button>
|
||||
</TooltipKeybind>
|
||||
<div class="hidden xl:flex items-center shrink-0">
|
||||
<Show when={params.dir}>
|
||||
<div
|
||||
class="flex items-center shrink-0 w-8 mr-1"
|
||||
aria-hidden={layout.sidebar.opened() ? "true" : undefined}
|
||||
class="transition-opacity"
|
||||
classList={{
|
||||
"opacity-100 duration-120 ease-out": !layout.sidebar.opened(),
|
||||
"opacity-0 duration-120 ease-in delay-0 pointer-events-none": layout.sidebar.opened(),
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="transition-opacity"
|
||||
classList={{
|
||||
"opacity-100 duration-120 ease-out": !layout.sidebar.opened(),
|
||||
"opacity-0 duration-120 ease-in delay-0 pointer-events-none": layout.sidebar.opened(),
|
||||
}}
|
||||
<TooltipKeybind
|
||||
placement="bottom"
|
||||
title={language.t("command.session.new")}
|
||||
keybind={command.keybind("session.new")}
|
||||
openDelay={2000}
|
||||
>
|
||||
<TooltipKeybind
|
||||
placement="bottom"
|
||||
title={language.t("command.session.new")}
|
||||
keybind={command.keybind("session.new")}
|
||||
openDelay={2000}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon={creating() ? "new-session-active" : "new-session"}
|
||||
class="titlebar-icon w-8 h-6 p-0 box-border"
|
||||
disabled={layout.sidebar.opened()}
|
||||
tabIndex={layout.sidebar.opened() ? -1 : undefined}
|
||||
onClick={() => {
|
||||
if (!params.dir) return
|
||||
navigate(`/${params.dir}/session`)
|
||||
}}
|
||||
aria-label={language.t("command.session.new")}
|
||||
aria-current={creating() ? "page" : undefined}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon={creating() ? "new-session-active" : "new-session"}
|
||||
class="titlebar-icon w-8 h-6 p-0 box-border"
|
||||
disabled={layout.sidebar.opened()}
|
||||
tabIndex={layout.sidebar.opened() ? -1 : undefined}
|
||||
onClick={() => {
|
||||
if (!params.dir) return
|
||||
navigate(`/${params.dir}/session`)
|
||||
}}
|
||||
aria-label={language.t("command.session.new")}
|
||||
aria-current={creating() ? "page" : undefined}
|
||||
/>
|
||||
</TooltipKeybind>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
<div
|
||||
class="flex items-center shrink-0"
|
||||
classList={{
|
||||
"-translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
|
||||
"duration-180 ease-out": !layout.sidebar.opened(),
|
||||
"duration-180 ease-in": layout.sidebar.opened(),
|
||||
}}
|
||||
>
|
||||
<Show when={hasProjects() && nav()}>
|
||||
<div class="flex items-center gap-0 transition-transform">
|
||||
<Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon="chevron-left"
|
||||
class="titlebar-icon w-6 h-6 p-0 box-border"
|
||||
disabled={!canBack()}
|
||||
onClick={back}
|
||||
aria-label={language.t("common.goBack")}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon="chevron-right"
|
||||
class="titlebar-icon w-6 h-6 p-0 box-border"
|
||||
disabled={!canForward()}
|
||||
onClick={forward}
|
||||
aria-label={language.t("common.goForward")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Show>
|
||||
<div
|
||||
class="flex items-center shrink-0"
|
||||
classList={{
|
||||
"-translate-x-[36px]": layout.sidebar.opened() && !!params.dir,
|
||||
"duration-180 ease-out": !layout.sidebar.opened(),
|
||||
"duration-180 ease-in": layout.sidebar.opened(),
|
||||
}}
|
||||
>
|
||||
<Show when={hasProjects() && nav()}>
|
||||
<div class="flex items-center gap-0 transition-transform">
|
||||
<Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon="chevron-left"
|
||||
class="titlebar-icon w-6 h-6 p-0 box-border"
|
||||
disabled={!canBack()}
|
||||
onClick={back}
|
||||
aria-label={language.t("common.goBack")}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
icon="chevron-right"
|
||||
class="titlebar-icon w-6 h-6 p-0 box-border"
|
||||
disabled={!canForward()}
|
||||
onClick={forward}
|
||||
aria-label={language.t("common.goForward")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Show>
|
||||
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" />
|
||||
{["beta", "dev"].includes(import.meta.env.VITE_OPENCODE_CHANNEL) && (
|
||||
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono">
|
||||
{import.meta.env.VITE_OPENCODE_CHANNEL.toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" />
|
||||
{["beta", "dev"].includes(import.meta.env.VITE_OPENCODE_CHANNEL) && (
|
||||
<div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono">
|
||||
{import.meta.env.VITE_OPENCODE_CHANNEL.toUpperCase()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="min-w-0 flex items-center justify-center pointer-events-none">
|
||||
<div id="opencode-titlebar-center" class="pointer-events-auto min-w-0 flex justify-center w-fit max-w-full" />
|
||||
</div>
|
||||
<div class="min-w-0 flex items-center justify-center pointer-events-none">
|
||||
<div id="opencode-titlebar-center" class="pointer-events-auto min-w-0 flex justify-center w-fit max-w-full" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
classList={{
|
||||
"flex items-center min-w-0 justify-end": true,
|
||||
"pr-2": !windows(),
|
||||
}}
|
||||
data-tauri-drag-region
|
||||
onMouseDown={drag}
|
||||
>
|
||||
<div id="opencode-titlebar-right" class="flex items-center gap-1 shrink-0 justify-end" />
|
||||
<Show when={windows()}>
|
||||
{!tauriApi() && <div class="shrink-0" style={{ width: windowsControlsWidth() }} />}
|
||||
<div data-tauri-decorum-tb class="flex flex-row" />
|
||||
</Show>
|
||||
</div>
|
||||
<div
|
||||
classList={{
|
||||
"flex items-center min-w-0 justify-end": true,
|
||||
"pr-2": !windows(),
|
||||
}}
|
||||
data-tauri-drag-region
|
||||
onMouseDown={drag}
|
||||
>
|
||||
<div id="opencode-titlebar-right" class="flex items-center gap-1 shrink-0 justify-end" />
|
||||
<Show when={windows()}>
|
||||
{!tauriApi() && <div class="w-36 shrink-0" />}
|
||||
<div data-tauri-decorum-tb class="flex flex-row" />
|
||||
</Show>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
|
||||
@@ -371,7 +371,11 @@ function createGlobalSync() {
|
||||
onCleanup(() => {
|
||||
queue.dispose()
|
||||
})
|
||||
onCleanup(children.disposeAll)
|
||||
onCleanup(() => {
|
||||
for (const directory of Object.keys(children.children)) {
|
||||
children.disposeDirectory(directoryKey(directory))
|
||||
}
|
||||
})
|
||||
|
||||
onMount(() => {
|
||||
if (typeof requestAnimationFrame === "function") {
|
||||
|
||||
@@ -92,22 +92,6 @@ export function createChildStoreManager(input: {
|
||||
})
|
||||
}
|
||||
|
||||
function disposeChild(key: DirectoryKey) {
|
||||
const dispose = disposers.get(key)
|
||||
if (!key || !children[key]) return false
|
||||
vcsCache.delete(key)
|
||||
metaCache.delete(key)
|
||||
iconCache.delete(key)
|
||||
lifecycle.delete(key)
|
||||
disposers.delete(key)
|
||||
delete children[key]
|
||||
input.onDispose(key)
|
||||
if (dispose) {
|
||||
dispose()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function disposeDirectory(directory: DirectoryKey) {
|
||||
const key = directory
|
||||
if (
|
||||
@@ -122,13 +106,18 @@ export function createChildStoreManager(input: {
|
||||
return false
|
||||
}
|
||||
|
||||
return disposeChild(key)
|
||||
}
|
||||
|
||||
function disposeAll() {
|
||||
for (const directory of Object.keys(children)) {
|
||||
disposeChild(directoryKey(directory))
|
||||
vcsCache.delete(key)
|
||||
metaCache.delete(key)
|
||||
iconCache.delete(key)
|
||||
lifecycle.delete(key)
|
||||
const dispose = disposers.get(key)
|
||||
if (dispose) {
|
||||
dispose()
|
||||
disposers.delete(key)
|
||||
}
|
||||
delete children[key]
|
||||
input.onDispose(key)
|
||||
return true
|
||||
}
|
||||
|
||||
function runEviction(skip?: string) {
|
||||
@@ -340,7 +329,6 @@ export function createChildStoreManager(input: {
|
||||
unpin,
|
||||
pinned,
|
||||
disposeDirectory,
|
||||
disposeAll,
|
||||
runEviction,
|
||||
vcsCache,
|
||||
metaCache,
|
||||
|
||||
@@ -9,88 +9,6 @@ type OpenFilePickerOptions = { title?: string; multiple?: boolean; accept?: stri
|
||||
type SaveFilePickerOptions = { title?: string; defaultPath?: string }
|
||||
type UpdateInfo = { updateAvailable: boolean; version?: string }
|
||||
|
||||
export type WslRuntimeCheck = {
|
||||
available: boolean
|
||||
version: string | null
|
||||
error: string | null
|
||||
}
|
||||
export type WslInstalledDistro = {
|
||||
name: string
|
||||
version: number | null
|
||||
isDefault: boolean
|
||||
}
|
||||
export type WslOnlineDistro = {
|
||||
name: string
|
||||
label: string
|
||||
}
|
||||
export type WslDistroProbe = {
|
||||
name: string
|
||||
canExecute: boolean
|
||||
hasBash: boolean
|
||||
hasCurl: boolean
|
||||
error: string | null
|
||||
}
|
||||
export type WslOpencodeCheck = {
|
||||
distro: string
|
||||
resolvedPath: string | null
|
||||
version: string | null
|
||||
expectedVersion: string | null
|
||||
matchesDesktop: boolean | null
|
||||
error: string | null
|
||||
}
|
||||
export type WslServerConfig = {
|
||||
id: string
|
||||
distro: string
|
||||
}
|
||||
|
||||
export type WslServerRuntime =
|
||||
| { kind: "starting" }
|
||||
| { kind: "ready"; url: string; username: string | null; password: string | null }
|
||||
| { kind: "failed"; message: string }
|
||||
| { kind: "stopped" }
|
||||
|
||||
export type WslServerItem = {
|
||||
config: WslServerConfig
|
||||
runtime: WslServerRuntime
|
||||
}
|
||||
|
||||
export type WslJob =
|
||||
| { kind: "runtime"; startedAt: number }
|
||||
| { kind: "distros"; startedAt: number }
|
||||
| { kind: "install-wsl"; startedAt: number }
|
||||
| { kind: "install-distro"; distro: string; startedAt: number }
|
||||
| { kind: "probe-distro"; distro: string; startedAt: number }
|
||||
| { kind: "probe-opencode"; distro: string; startedAt: number }
|
||||
| { kind: "install-opencode"; distro: string; startedAt: number }
|
||||
|
||||
export type WslServersState = {
|
||||
runtime: WslRuntimeCheck | null
|
||||
installed: WslInstalledDistro[]
|
||||
online: WslOnlineDistro[]
|
||||
distroProbes: Record<string, WslDistroProbe>
|
||||
opencodeChecks: Record<string, WslOpencodeCheck>
|
||||
pendingRestart: boolean
|
||||
servers: WslServerItem[]
|
||||
job: WslJob | null
|
||||
}
|
||||
export type WslServersEvent = { type: "state"; state: WslServersState }
|
||||
|
||||
export type WslServersPlatform = {
|
||||
getState(): Promise<WslServersState>
|
||||
subscribe(cb: (event: WslServersEvent) => void): () => void
|
||||
probeRuntime(): Promise<void>
|
||||
refreshDistros(): Promise<void>
|
||||
installWsl(): Promise<void>
|
||||
installDistro(name: string): Promise<void>
|
||||
probeDistro(name: string): Promise<void>
|
||||
probeOpencode(name: string): Promise<void>
|
||||
installOpencode(name: string): Promise<void>
|
||||
openTerminal(name: string): Promise<void>
|
||||
addServer(distro: string): Promise<WslServerConfig>
|
||||
removeServer(id: string): Promise<void>
|
||||
startServer(id: string): Promise<void>
|
||||
}
|
||||
|
||||
export type Platform = {
|
||||
/** Platform discriminator */
|
||||
platform: "web" | "desktop"
|
||||
@@ -146,8 +64,11 @@ export type Platform = {
|
||||
/** Set the default server URL to use on app startup (platform-specific) */
|
||||
setDefaultServer?(url: ServerConnection.Key | null): Promise<void> | void
|
||||
|
||||
/** Manage WSL sidecar servers (Electron on Windows only) */
|
||||
wslServers?: WslServersPlatform
|
||||
/** Get the configured WSL integration (desktop only) */
|
||||
getWslEnabled?(): Promise<boolean>
|
||||
|
||||
/** Set the configured WSL integration (desktop only) */
|
||||
setWslEnabled?(config: boolean): Promise<void> | void
|
||||
|
||||
/** Get the preferred display backend (desktop only) */
|
||||
getDisplayBackend?(): Promise<DisplayBackend | null> | DisplayBackend | null
|
||||
|
||||
@@ -180,12 +180,6 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
||||
if (state.active !== input) setState("active", input)
|
||||
}
|
||||
|
||||
createEffect(() => {
|
||||
if (typeof window === "undefined") return
|
||||
window.__OPENCODE__ ??= {}
|
||||
window.__OPENCODE__.activeServer = state.active
|
||||
})
|
||||
|
||||
function add(input: ServerConnection.Http) {
|
||||
const url_ = normalizeServerUrl(input.http.url)
|
||||
if (!url_) return
|
||||
@@ -236,7 +230,7 @@ export const { use: useServer, provider: ServerProvider } = createSimpleContext(
|
||||
)
|
||||
const isLocal = createMemo(() => {
|
||||
const c = current()
|
||||
return c?.type === "sidecar" || (c?.type === "http" && isLocalHost(c.http.url))
|
||||
return (c?.type === "sidecar" && c.variant === "base") || (c?.type === "http" && isLocalHost(c.http.url))
|
||||
})
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||
import { queryOptions, skipToken, useQuery, useQueryClient } from "@tanstack/solid-query"
|
||||
import { createEffect, onCleanup } from "solid-js"
|
||||
import type { WslServersPlatform, WslServersState } from "./platform"
|
||||
import { usePlatform } from "./platform"
|
||||
|
||||
const wslServersQueryKey = ["platform", "wslServers"] as const
|
||||
|
||||
export const { use: useWslServers, provider: WslServersProvider } = createSimpleContext({
|
||||
name: "WslServers",
|
||||
init: () => {
|
||||
const platform = usePlatform()
|
||||
const queryClient = useQueryClient()
|
||||
const query = useQuery(() => {
|
||||
const api = platform.wslServers
|
||||
return queryOptions<WslServersState>({
|
||||
queryKey: wslServersQueryKey,
|
||||
queryFn: api ? () => api.getState() : skipToken,
|
||||
staleTime: Number.POSITIVE_INFINITY,
|
||||
gcTime: Number.POSITIVE_INFINITY,
|
||||
})
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const api = platform.wslServers
|
||||
if (!api) return
|
||||
const off = api.subscribe((event) => {
|
||||
queryClient.setQueryData(wslServersQueryKey, event.state)
|
||||
})
|
||||
onCleanup(off)
|
||||
})
|
||||
|
||||
return query
|
||||
},
|
||||
})
|
||||
@@ -2,19 +2,6 @@ export { AppBaseProviders, AppInterface } from "./app"
|
||||
export { ACCEPTED_FILE_EXTENSIONS, ACCEPTED_FILE_TYPES, filePickerFilters } from "./constants/file-picker"
|
||||
export { useCommand } from "./context/command"
|
||||
export { loadLocaleDict, normalizeLocale, type Locale } from "./context/language"
|
||||
export { useWslServers } from "./context/wsl-servers"
|
||||
export {
|
||||
type DisplayBackend,
|
||||
type Platform,
|
||||
PlatformProvider,
|
||||
type WslInstalledDistro,
|
||||
type WslOnlineDistro,
|
||||
type WslOpencodeCheck,
|
||||
type WslServerConfig,
|
||||
type WslServerItem,
|
||||
type WslServersEvent,
|
||||
type WslServersPlatform,
|
||||
type WslServersState,
|
||||
} from "./context/platform"
|
||||
export { type DisplayBackend, type Platform, PlatformProvider } from "./context/platform"
|
||||
export { ServerConnection } from "./context/server"
|
||||
export { handleNotificationClick } from "./utils/notification-click"
|
||||
|
||||
@@ -36,7 +36,6 @@ export default function Home() {
|
||||
if (healthy === false) return "bg-icon-critical-base"
|
||||
return "bg-border-weak-base"
|
||||
})
|
||||
const useWebDirectoryPicker = createMemo(() => server.current?.type === "sidecar" && server.current.variant === "wsl")
|
||||
|
||||
function openProject(directory: string) {
|
||||
layout.projects.open(directory)
|
||||
@@ -55,7 +54,7 @@ export default function Home() {
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal() && !useWebDirectoryPicker()) {
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal()) {
|
||||
const result = await platform.openDirectoryPickerDialog?.({
|
||||
title: language.t("command.project.open"),
|
||||
multiple: true,
|
||||
@@ -76,7 +75,7 @@ export default function Home() {
|
||||
size="large"
|
||||
variant="ghost"
|
||||
class="mt-4 mx-auto text-14-regular text-text-weak"
|
||||
onClick={() => dialog.show(() => <DialogSelectServer onNavigateHome={() => navigate("/")} />)}
|
||||
onClick={() => dialog.show(() => <DialogSelectServer />)}
|
||||
>
|
||||
<div
|
||||
classList={{
|
||||
|
||||
@@ -149,7 +149,6 @@ export default function Layout(props: ParentProps) {
|
||||
}
|
||||
const colorSchemeLabel = (scheme: ColorScheme) => language.t(colorSchemeKey[scheme])
|
||||
const currentDir = createMemo(() => route().dir)
|
||||
const useWebDirectoryPicker = createMemo(() => server.current?.type === "sidecar" && server.current.variant === "wsl")
|
||||
|
||||
const [state, setState] = createStore({
|
||||
autoselect: !initialDirectory,
|
||||
@@ -1208,7 +1207,7 @@ export default function Layout(props: ParentProps) {
|
||||
const run = ++dialogRun
|
||||
void import("@/components/dialog-select-server").then((x) => {
|
||||
if (dialogDead || dialogRun !== run) return
|
||||
dialog.show(() => <x.DialogSelectServer onNavigateHome={() => navigate("/")} />)
|
||||
dialog.show(() => <x.DialogSelectServer />)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1460,7 +1459,7 @@ export default function Layout(props: ParentProps) {
|
||||
}
|
||||
}
|
||||
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal() && !useWebDirectoryPicker()) {
|
||||
if (platform.openDirectoryPickerDialog && server.isLocal()) {
|
||||
const result = await platform.openDirectoryPickerDialog?.({
|
||||
title: language.t("command.project.open"),
|
||||
multiple: true,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useDragDropContext } from "@thisbeyond/solid-dnd"
|
||||
import type { Transformer } from "@thisbeyond/solid-dnd"
|
||||
import type { JSXElement } from "solid-js"
|
||||
import { createRoot, onCleanup, type JSXElement } from "solid-js"
|
||||
|
||||
type DragEvent = { draggable?: { id?: unknown } }
|
||||
|
||||
@@ -27,16 +27,20 @@ const createAxisConstraint = (axis: "x" | "y", transformerId: string) => (): JSX
|
||||
if (!context) return null
|
||||
const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context
|
||||
const transformer = createTransformer(transformerId, axis)
|
||||
onDragStart((event) => {
|
||||
const id = getDraggableId(event)
|
||||
if (!id) return
|
||||
addTransformer("draggables", id, transformer)
|
||||
})
|
||||
onDragEnd((event) => {
|
||||
const id = getDraggableId(event)
|
||||
if (!id) return
|
||||
removeTransformer("draggables", id, transformer.id)
|
||||
const dispose = createRoot((dispose) => {
|
||||
onDragStart((event) => {
|
||||
const id = getDraggableId(event)
|
||||
if (!id) return
|
||||
addTransformer("draggables", id, transformer)
|
||||
})
|
||||
onDragEnd((event) => {
|
||||
const id = getDraggableId(event)
|
||||
if (!id) return
|
||||
removeTransformer("draggables", id, transformer.id)
|
||||
})
|
||||
return dispose
|
||||
})
|
||||
onCleanup(dispose)
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
@@ -31,7 +31,6 @@
|
||||
"solid-js": "catalog:",
|
||||
"solid-list": "0.3.0",
|
||||
"solid-stripe": "0.8.1",
|
||||
"svix": "1.92.2",
|
||||
"vite": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { Webhook } from "svix"
|
||||
|
||||
type Incident = {
|
||||
mode?: "test" | "standard"
|
||||
name?: string
|
||||
permalink?: string
|
||||
summary?: string
|
||||
}
|
||||
|
||||
type IncidentWebhookPayload = {
|
||||
event_type?: string
|
||||
"public_incident.incident_created_v2"?: Incident
|
||||
}
|
||||
|
||||
const verifyWebhook = async (request: Request) => {
|
||||
const body = await request.text()
|
||||
try {
|
||||
return new Webhook(Resource.INCIDENT_WEBHOOK_SIGNING_SECRET.value).verify(
|
||||
body,
|
||||
Object.fromEntries(request.headers.entries()),
|
||||
) as IncidentWebhookPayload
|
||||
} catch {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
const postDiscordMessage = async (incident: Incident) => {
|
||||
return fetch(Resource.DISCORD_INCIDENT_WEBHOOK_URL.value, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
content: [
|
||||
`**${incident.mode === "test" ? "[TEST] " : ""}${incident.name ?? "Incident has been created"}**`,
|
||||
incident.summary,
|
||||
"",
|
||||
"@everyone",
|
||||
"",
|
||||
incident.permalink,
|
||||
]
|
||||
.filter((line) => line !== undefined)
|
||||
.join("\n"),
|
||||
allowed_mentions: {
|
||||
parse: ["everyone"],
|
||||
},
|
||||
flags: 4,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
export async function POST(input: APIEvent) {
|
||||
const payload = await verifyWebhook(input.request)
|
||||
if (!payload) {
|
||||
return Response.json({ message: "invalid signature" }, { status: 401 })
|
||||
}
|
||||
|
||||
if (payload.event_type !== "public_incident.incident_created_v2") {
|
||||
return Response.json({ message: "ignored event" }, { status: 200 })
|
||||
}
|
||||
|
||||
const incident = payload["public_incident.incident_created_v2"]
|
||||
if (!incident) {
|
||||
return Response.json({ message: "missing incident" }, { status: 400 })
|
||||
}
|
||||
|
||||
const response = await postDiscordMessage(incident)
|
||||
if (!response.ok) {
|
||||
return Response.json({ message: "discord webhook failed" }, { status: 502 })
|
||||
}
|
||||
|
||||
return Response.json({ message: "sent" }, { status: 200 })
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
8
packages/console/core/sst-env.d.ts
vendored
@@ -35,10 +35,6 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DISCORD_INCIDENT_WEBHOOK_URL": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DISCORD_SUPPORT_BOT_TOKEN": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -91,10 +87,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"INCIDENT_WEBHOOK_SIGNING_SECRET": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"R2AccessKey": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
8
packages/console/function/sst-env.d.ts
vendored
@@ -35,10 +35,6 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DISCORD_INCIDENT_WEBHOOK_URL": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DISCORD_SUPPORT_BOT_TOKEN": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -91,10 +87,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"INCIDENT_WEBHOOK_SIGNING_SECRET": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"R2AccessKey": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
8
packages/console/resource/sst-env.d.ts
vendored
@@ -35,10 +35,6 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DISCORD_INCIDENT_WEBHOOK_URL": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DISCORD_SUPPORT_BOT_TOKEN": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -91,10 +87,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"INCIDENT_WEBHOOK_SIGNING_SECRET": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"R2AccessKey": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.14.39",
|
||||
"version": "1.14.35",
|
||||
"name": "@opencode-ai/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
28
packages/desktop-electron/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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?
|
||||
out/
|
||||
|
||||
resources/opencode-cli*
|
||||
resources/icons
|
||||
4
packages/desktop-electron/AGENTS.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# Desktop package notes
|
||||
|
||||
- Renderer process should only call `window.api` from `src/preload`.
|
||||
- Main process should register IPC handlers in `src/main/ipc.ts`.
|
||||
32
packages/desktop-electron/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# OpenCode Desktop
|
||||
|
||||
Native OpenCode desktop app, built with Tauri v2.
|
||||
|
||||
## Development
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
bun install
|
||||
bun run --cwd packages/desktop tauri dev
|
||||
```
|
||||
|
||||
This starts the Vite dev server on http://localhost:1420 and opens the native window.
|
||||
|
||||
If you only want the web dev server (no native shell):
|
||||
|
||||
```bash
|
||||
bun run --cwd packages/desktop dev
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
To create a production `dist/` and build the native app bundle:
|
||||
|
||||
```bash
|
||||
bun run --cwd packages/desktop tauri build
|
||||
```
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Running the desktop app requires additional Tauri dependencies (Rust toolchain, platform-specific libraries). See the [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for setup instructions.
|
||||
|
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 687 B After Width: | Height: | Size: 687 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 582 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |