Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ecf27f389 | ||
|
|
548446a990 |
@@ -21,6 +21,15 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
node-version: "24"
|
node-version: "24"
|
||||||
|
|
||||||
|
# Workaround for Pulumi version conflict:
|
||||||
|
# GitHub runners have Pulumi 3.212.0+ pre-installed, which removed the -root flag
|
||||||
|
# from pulumi-language-nodejs (see https://github.com/pulumi/pulumi/pull/21065).
|
||||||
|
# SST 3.17.x uses Pulumi SDK 3.210.0 which still passes -root, causing a conflict.
|
||||||
|
# Removing the system language plugin forces SST to use its bundled compatible version.
|
||||||
|
# TODO: Remove when sst supports Pulumi >3.210.0
|
||||||
|
- name: Fix Pulumi version conflict
|
||||||
|
run: sudo rm -f /usr/local/bin/pulumi-language-nodejs
|
||||||
|
|
||||||
- run: bun sst deploy --stage=${{ github.ref_name }}
|
- run: bun sst deploy --stage=${{ github.ref_name }}
|
||||||
env:
|
env:
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
|||||||
@@ -23,13 +23,13 @@
|
|||||||
"oxlint-tsgolint": "0.21.0",
|
"oxlint-tsgolint": "0.21.0",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
"sst": "4.13.1",
|
"sst": "3.18.10",
|
||||||
"turbo": "2.8.13",
|
"turbo": "2.8.13",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages/app": {
|
"packages/app": {
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/core": "workspace:*",
|
"@opencode-ai/core": "workspace:*",
|
||||||
@@ -44,7 +44,6 @@
|
|||||||
"@solid-primitives/i18n": "2.2.1",
|
"@solid-primitives/i18n": "2.2.1",
|
||||||
"@solid-primitives/media": "2.3.3",
|
"@solid-primitives/media": "2.3.3",
|
||||||
"@solid-primitives/resize-observer": "2.1.5",
|
"@solid-primitives/resize-observer": "2.1.5",
|
||||||
"@solid-primitives/scheduled": "1.5.3",
|
|
||||||
"@solid-primitives/scroll": "2.1.3",
|
"@solid-primitives/scroll": "2.1.3",
|
||||||
"@solid-primitives/storage": "catalog:",
|
"@solid-primitives/storage": "catalog:",
|
||||||
"@solid-primitives/timer": "1.4.4",
|
"@solid-primitives/timer": "1.4.4",
|
||||||
@@ -85,7 +84,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/app": {
|
"packages/console/app": {
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cloudflare/vite-plugin": "1.15.2",
|
"@cloudflare/vite-plugin": "1.15.2",
|
||||||
"@ibm/plex": "6.4.1",
|
"@ibm/plex": "6.4.1",
|
||||||
@@ -120,7 +119,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/core": {
|
"packages/console/core": {
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-sts": "3.782.0",
|
"@aws-sdk/client-sts": "3.782.0",
|
||||||
"@jsx-email/render": "1.1.1",
|
"@jsx-email/render": "1.1.1",
|
||||||
@@ -147,7 +146,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/function": {
|
"packages/console/function": {
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "3.0.64",
|
"@ai-sdk/anthropic": "3.0.64",
|
||||||
"@ai-sdk/openai": "3.0.48",
|
"@ai-sdk/openai": "3.0.48",
|
||||||
@@ -169,7 +168,7 @@
|
|||||||
},
|
},
|
||||||
"packages/console/mail": {
|
"packages/console/mail": {
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
@@ -193,7 +192,7 @@
|
|||||||
},
|
},
|
||||||
"packages/core": {
|
"packages/core": {
|
||||||
"name": "@opencode-ai/core",
|
"name": "@opencode-ai/core",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -231,7 +230,7 @@
|
|||||||
"ai-gateway-provider": "3.1.2",
|
"ai-gateway-provider": "3.1.2",
|
||||||
"cross-spawn": "catalog:",
|
"cross-spawn": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"gitlab-ai-provider": "6.7.0",
|
"gitlab-ai-provider": "6.6.0",
|
||||||
"glob": "13.0.5",
|
"glob": "13.0.5",
|
||||||
"google-auth-library": "10.5.0",
|
"google-auth-library": "10.5.0",
|
||||||
"immer": "11.1.4",
|
"immer": "11.1.4",
|
||||||
@@ -239,7 +238,7 @@
|
|||||||
"minimatch": "10.2.5",
|
"minimatch": "10.2.5",
|
||||||
"npm-package-arg": "13.0.2",
|
"npm-package-arg": "13.0.2",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"venice-ai-sdk-provider": "2.0.2",
|
"venice-ai-sdk-provider": "2.0.1",
|
||||||
"xdg-basedir": "5.1.0",
|
"xdg-basedir": "5.1.0",
|
||||||
"zod": "catalog:",
|
"zod": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -254,9 +253,8 @@
|
|||||||
},
|
},
|
||||||
"packages/desktop": {
|
"packages/desktop": {
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zip.js/zip.js": "2.7.62",
|
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"electron-context-menu": "4.1.2",
|
"electron-context-menu": "4.1.2",
|
||||||
@@ -309,7 +307,7 @@
|
|||||||
},
|
},
|
||||||
"packages/effect-drizzle-sqlite": {
|
"packages/effect-drizzle-sqlite": {
|
||||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||||
"version": "1.15.10",
|
"version": "1.15.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
@@ -323,7 +321,7 @@
|
|||||||
},
|
},
|
||||||
"packages/enterprise": {
|
"packages/enterprise": {
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/core": "workspace:*",
|
"@opencode-ai/core": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
@@ -353,7 +351,7 @@
|
|||||||
},
|
},
|
||||||
"packages/function": {
|
"packages/function": {
|
||||||
"name": "@opencode-ai/function",
|
"name": "@opencode-ai/function",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@octokit/auth-app": "8.0.1",
|
"@octokit/auth-app": "8.0.1",
|
||||||
"@octokit/rest": "catalog:",
|
"@octokit/rest": "catalog:",
|
||||||
@@ -369,7 +367,7 @@
|
|||||||
},
|
},
|
||||||
"packages/http-recorder": {
|
"packages/http-recorder": {
|
||||||
"name": "@opencode-ai/http-recorder",
|
"name": "@opencode-ai/http-recorder",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform-node": "catalog:",
|
"@effect/platform-node": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
@@ -382,7 +380,7 @@
|
|||||||
},
|
},
|
||||||
"packages/llm": {
|
"packages/llm": {
|
||||||
"name": "@opencode-ai/llm",
|
"name": "@opencode-ai/llm",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@smithy/eventstream-codec": "4.2.14",
|
"@smithy/eventstream-codec": "4.2.14",
|
||||||
"@smithy/util-utf8": "4.2.2",
|
"@smithy/util-utf8": "4.2.2",
|
||||||
@@ -400,7 +398,7 @@
|
|||||||
},
|
},
|
||||||
"packages/opencode": {
|
"packages/opencode": {
|
||||||
"name": "opencode",
|
"name": "opencode",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"bin": {
|
"bin": {
|
||||||
"opencode": "./bin/opencode",
|
"opencode": "./bin/opencode",
|
||||||
},
|
},
|
||||||
@@ -470,7 +468,7 @@
|
|||||||
"drizzle-orm": "catalog:",
|
"drizzle-orm": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"fuzzysort": "3.1.0",
|
"fuzzysort": "3.1.0",
|
||||||
"gitlab-ai-provider": "6.7.0",
|
"gitlab-ai-provider": "6.6.0",
|
||||||
"glob": "13.0.5",
|
"glob": "13.0.5",
|
||||||
"google-auth-library": "10.5.0",
|
"google-auth-library": "10.5.0",
|
||||||
"gray-matter": "4.0.3",
|
"gray-matter": "4.0.3",
|
||||||
@@ -494,7 +492,7 @@
|
|||||||
"tree-sitter-powershell": "0.25.10",
|
"tree-sitter-powershell": "0.25.10",
|
||||||
"turndown": "7.2.0",
|
"turndown": "7.2.0",
|
||||||
"ulid": "catalog:",
|
"ulid": "catalog:",
|
||||||
"venice-ai-sdk-provider": "2.0.2",
|
"venice-ai-sdk-provider": "2.0.1",
|
||||||
"vscode-jsonrpc": "8.2.1",
|
"vscode-jsonrpc": "8.2.1",
|
||||||
"web-tree-sitter": "0.25.10",
|
"web-tree-sitter": "0.25.10",
|
||||||
"which": "6.0.1",
|
"which": "6.0.1",
|
||||||
@@ -538,7 +536,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin": {
|
"packages/plugin": {
|
||||||
"name": "@opencode-ai/plugin",
|
"name": "@opencode-ai/plugin",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
@@ -576,7 +574,7 @@
|
|||||||
},
|
},
|
||||||
"packages/sdk/js": {
|
"packages/sdk/js": {
|
||||||
"name": "@opencode-ai/sdk",
|
"name": "@opencode-ai/sdk",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cross-spawn": "catalog:",
|
"cross-spawn": "catalog:",
|
||||||
},
|
},
|
||||||
@@ -591,7 +589,7 @@
|
|||||||
},
|
},
|
||||||
"packages/slack": {
|
"packages/slack": {
|
||||||
"name": "@opencode-ai/slack",
|
"name": "@opencode-ai/slack",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@slack/bolt": "^3.17.1",
|
"@slack/bolt": "^3.17.1",
|
||||||
@@ -626,7 +624,7 @@
|
|||||||
},
|
},
|
||||||
"packages/ui": {
|
"packages/ui": {
|
||||||
"name": "@opencode-ai/ui",
|
"name": "@opencode-ai/ui",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/core": "workspace:*",
|
"@opencode-ai/core": "workspace:*",
|
||||||
@@ -675,7 +673,7 @@
|
|||||||
},
|
},
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"name": "@opencode-ai/web",
|
"name": "@opencode-ai/web",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/cloudflare": "12.6.3",
|
"@astrojs/cloudflare": "12.6.3",
|
||||||
"@astrojs/markdown-remark": "6.3.1",
|
"@astrojs/markdown-remark": "6.3.1",
|
||||||
@@ -718,7 +716,6 @@
|
|||||||
],
|
],
|
||||||
"patchedDependencies": {
|
"patchedDependencies": {
|
||||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
||||||
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
|
|
||||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||||
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
||||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||||
@@ -908,8 +905,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="],
|
"@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.993.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.11", "@aws-sdk/credential-provider-node": "^3.972.10", "@aws-sdk/middleware-host-header": "^3.972.3", "@aws-sdk/middleware-logger": "^3.972.3", "@aws-sdk/middleware-recursion-detection": "^3.972.3", "@aws-sdk/middleware-user-agent": "^3.972.11", "@aws-sdk/region-config-resolver": "^3.972.3", "@aws-sdk/types": "^3.973.1", "@aws-sdk/util-endpoints": "3.993.0", "@aws-sdk/util-user-agent-browser": "^3.972.3", "@aws-sdk/util-user-agent-node": "^3.972.9", "@smithy/config-resolver": "^4.4.6", "@smithy/core": "^3.23.2", "@smithy/fetch-http-handler": "^5.3.9", "@smithy/hash-node": "^4.2.8", "@smithy/invalid-dependency": "^4.2.8", "@smithy/middleware-content-length": "^4.2.8", "@smithy/middleware-endpoint": "^4.4.16", "@smithy/middleware-retry": "^4.4.33", "@smithy/middleware-serde": "^4.2.9", "@smithy/middleware-stack": "^4.2.8", "@smithy/node-config-provider": "^4.3.8", "@smithy/node-http-handler": "^4.4.10", "@smithy/protocol-http": "^5.3.8", "@smithy/smithy-client": "^4.11.5", "@smithy/types": "^4.12.0", "@smithy/url-parser": "^4.2.8", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.32", "@smithy/util-defaults-mode-node": "^4.2.35", "@smithy/util-endpoints": "^3.2.8", "@smithy/util-middleware": "^4.2.8", "@smithy/util-retry": "^4.2.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-7Ne3Yk/bgQPVebAkv7W+RfhiwTRSbfER9BtbhOa2w/+dIr902LrJf6vrZlxiqaJbGj2ALx8M+ZK1YIHVxSwu9A=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.1048.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-node": "^3.972.42", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-ryEYNVdilyWkKsOs/7Xy/l7+qjtSz4sll8NpcWD6AtONxjG/5OMaAhxxDkQb4iBoNMKnISxsARzQAp/Wa8pXIg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.933.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-node": "3.933.0", "@aws-sdk/middleware-bucket-endpoint": "3.930.0", "@aws-sdk/middleware-expect-continue": "3.930.0", "@aws-sdk/middleware-flexible-checksums": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-location-constraint": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/middleware-ssec": "3.930.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/signature-v4-multi-region": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-KxwZvdxdCeWK6o8mpnb+kk7Kgb8V+8AjTwSXUWH1UAD85B0tjdo1cSfE5zoR5fWGol4Ml5RLez12a6LPhsoTqA=="],
|
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.933.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/credential-provider-node": "3.933.0", "@aws-sdk/middleware-bucket-endpoint": "3.930.0", "@aws-sdk/middleware-expect-continue": "3.930.0", "@aws-sdk/middleware-flexible-checksums": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-location-constraint": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-sdk-s3": "3.932.0", "@aws-sdk/middleware-ssec": "3.930.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/signature-v4-multi-region": "3.932.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/eventstream-serde-browser": "^4.2.5", "@smithy/eventstream-serde-config-resolver": "^4.3.5", "@smithy/eventstream-serde-node": "^4.2.5", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-blob-browser": "^4.2.6", "@smithy/hash-node": "^4.2.5", "@smithy/hash-stream-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/md5-js": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-stream": "^4.5.6", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.5", "tslib": "^2.6.2" } }, "sha512-KxwZvdxdCeWK6o8mpnb+kk7Kgb8V+8AjTwSXUWH1UAD85B0tjdo1cSfE5zoR5fWGol4Ml5RLez12a6LPhsoTqA=="],
|
||||||
|
|
||||||
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="],
|
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.933.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.932.0", "@aws-sdk/middleware-host-header": "3.930.0", "@aws-sdk/middleware-logger": "3.930.0", "@aws-sdk/middleware-recursion-detection": "3.933.0", "@aws-sdk/middleware-user-agent": "3.932.0", "@aws-sdk/region-config-resolver": "3.930.0", "@aws-sdk/types": "3.930.0", "@aws-sdk/util-endpoints": "3.930.0", "@aws-sdk/util-user-agent-browser": "3.930.0", "@aws-sdk/util-user-agent-node": "3.932.0", "@smithy/config-resolver": "^4.4.3", "@smithy/core": "^3.18.2", "@smithy/fetch-http-handler": "^5.3.6", "@smithy/hash-node": "^4.2.5", "@smithy/invalid-dependency": "^4.2.5", "@smithy/middleware-content-length": "^4.2.5", "@smithy/middleware-endpoint": "^4.3.9", "@smithy/middleware-retry": "^4.4.9", "@smithy/middleware-serde": "^4.2.5", "@smithy/middleware-stack": "^4.2.5", "@smithy/node-config-provider": "^4.3.5", "@smithy/node-http-handler": "^4.4.5", "@smithy/protocol-http": "^5.3.5", "@smithy/smithy-client": "^4.9.5", "@smithy/types": "^4.9.0", "@smithy/url-parser": "^4.2.5", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.8", "@smithy/util-defaults-mode-node": "^4.2.11", "@smithy/util-endpoints": "^3.2.5", "@smithy/util-middleware": "^4.2.5", "@smithy/util-retry": "^4.2.5", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zwGLSiK48z3PzKpQiDMKP85+fpIrPMF1qQOQW9OW7BGj5AuBZIisT2O4VzIgYJeh+t47MLU7VgBQL7muc+MJDg=="],
|
||||||
@@ -980,8 +975,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
|
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.930.0", "", { "dependencies": { "@smithy/types": "^4.9.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA=="],
|
||||||
|
|
||||||
"@aws/durable-execution-sdk-js": ["@aws/durable-execution-sdk-js@1.0.2", "", { "dependencies": { "@aws-sdk/client-lambda": "^3.943.0" } }, "sha512-KIYBVqV9gArkWdPEDYOjJqLlO+NecmCXOXadNBlOspJTmqztwuiyb6aZc468bYWSGuL4Me/gyTIK97ubkwFNCw=="],
|
|
||||||
|
|
||||||
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
|
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.4", "", {}, "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ=="],
|
||||||
|
|
||||||
"@azure-rest/core-client": ["@azure-rest/core-client@2.6.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-iuFKDm8XPzNxPfRjhyU5/xKZmcRDzSuEghXDHHk4MjBV/wFL34GmYVBZnn9wmuoLBeS1qAw9ceMdaeJBPcB1QQ=="],
|
"@azure-rest/core-client": ["@azure-rest/core-client@2.6.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-iuFKDm8XPzNxPfRjhyU5/xKZmcRDzSuEghXDHHk4MjBV/wFL34GmYVBZnn9wmuoLBeS1qAw9ceMdaeJBPcB1QQ=="],
|
||||||
@@ -1468,8 +1461,6 @@
|
|||||||
|
|
||||||
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
|
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
|
||||||
|
|
||||||
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
|
|
||||||
|
|
||||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||||
|
|
||||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||||
@@ -2178,7 +2169,7 @@
|
|||||||
|
|
||||||
"@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
|
"@solid-primitives/rootless": ["@solid-primitives/rootless@1.5.3", "", { "dependencies": { "@solid-primitives/utils": "^6.4.0" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-N8cIDAHbWcLahNRLr0knAAQvXyEdEMoAZvIMZKmhNb1mlx9e2UOv9BRD5YNwQUJwbNoYVhhLwFOEOcVXFx0HqA=="],
|
||||||
|
|
||||||
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.3", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-oNwLE6E6lxJAWrc8QXuwM0k2oU1BnANnkChwMw82aK1j3+mWGJkG1IFe5gCwbV+afYmjI76t9JJV3md/8tLw+g=="],
|
"@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA=="],
|
||||||
|
|
||||||
"@solid-primitives/scroll": ["@solid-primitives/scroll@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Ejq/Z7zKo/6eIEFr1bFLzXFxiGBCMLuqCM8QB8urr3YdPzjSETFLzYRWUyRiDWaBQN0F7k0SY6S7ig5nWOP7vg=="],
|
"@solid-primitives/scroll": ["@solid-primitives/scroll@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-Ejq/Z7zKo/6eIEFr1bFLzXFxiGBCMLuqCM8QB8urr3YdPzjSETFLzYRWUyRiDWaBQN0F7k0SY6S7ig5nWOP7vg=="],
|
||||||
|
|
||||||
@@ -2620,6 +2611,8 @@
|
|||||||
|
|
||||||
"avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
|
"avvio": ["avvio@9.2.0", "", { "dependencies": { "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ=="],
|
||||||
|
|
||||||
|
"aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="],
|
||||||
|
|
||||||
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
||||||
|
|
||||||
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
|
"aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="],
|
||||||
@@ -2704,7 +2697,7 @@
|
|||||||
|
|
||||||
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||||
|
|
||||||
"buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
"buffer": ["buffer@4.9.2", "", { "dependencies": { "base64-js": "^1.0.2", "ieee754": "^1.1.4", "isarray": "^1.0.0" } }, "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg=="],
|
||||||
|
|
||||||
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
"buffer-crc32": ["buffer-crc32@1.0.0", "", {}, "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w=="],
|
||||||
|
|
||||||
@@ -3300,7 +3293,7 @@
|
|||||||
|
|
||||||
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
|
||||||
|
|
||||||
"gitlab-ai-provider": ["gitlab-ai-provider@6.7.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-J7apROAmDwDJcLPcbQ9YQiPhyFN1Vvt/f3ugsePaUnzh3sedfHCHGETgG0Mm0dJCB2rC7DYwmV/sgxLQcQ/sjw=="],
|
"gitlab-ai-provider": ["gitlab-ai-provider@6.6.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-jUxYnKA4XQaPc3wxACDZ8bPDXO0Mzx7cZaBDxbT2uGgLqtGZmSi+9tVNIg7louSS+s/ioVra3SoUz3iOFVhKPA=="],
|
||||||
|
|
||||||
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
|
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
|
||||||
|
|
||||||
@@ -3442,7 +3435,7 @@
|
|||||||
|
|
||||||
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
"iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="],
|
||||||
|
|
||||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
"ieee754": ["ieee754@1.1.13", "", {}, "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="],
|
||||||
|
|
||||||
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
|
||||||
|
|
||||||
@@ -3574,7 +3567,7 @@
|
|||||||
|
|
||||||
"is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="],
|
"is64bit": ["is64bit@2.0.0", "", { "dependencies": { "system-architecture": "^0.1.0" } }, "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw=="],
|
||||||
|
|
||||||
"isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
"isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
||||||
|
|
||||||
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
|
"isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="],
|
||||||
|
|
||||||
@@ -3592,6 +3585,8 @@
|
|||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
|
|
||||||
|
"jmespath": ["jmespath@0.16.0", "", {}, "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw=="],
|
||||||
|
|
||||||
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
|
"jose": ["jose@6.0.11", "", {}, "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg=="],
|
||||||
|
|
||||||
"js-base64": ["js-base64@3.7.7", "", {}, "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="],
|
"js-base64": ["js-base64@3.7.7", "", {}, "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw=="],
|
||||||
@@ -4078,6 +4073,8 @@
|
|||||||
|
|
||||||
"opencode-poe-auth": ["opencode-poe-auth@0.0.1", "", { "dependencies": { "open": "^10.0.0", "poe-oauth": "*" }, "peerDependencies": { "@opencode-ai/plugin": "*" } }, "sha512-cXqTlS6AXHzo1oBdosnxbT47ZJEZ9WXn050X8Re6wZ1vaNnTpB/l2fMQt90evT7RBK0fB8UjXQUDMKyd7bbiqg=="],
|
"opencode-poe-auth": ["opencode-poe-auth@0.0.1", "", { "dependencies": { "open": "^10.0.0", "poe-oauth": "*" }, "peerDependencies": { "@opencode-ai/plugin": "*" } }, "sha512-cXqTlS6AXHzo1oBdosnxbT47ZJEZ9WXn050X8Re6wZ1vaNnTpB/l2fMQt90evT7RBK0fB8UjXQUDMKyd7bbiqg=="],
|
||||||
|
|
||||||
|
"opencontrol": ["opencontrol@0.0.6", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.6.1", "@tsconfig/bun": "1.0.7", "hono": "4.7.4", "zod": "3.24.2", "zod-to-json-schema": "3.24.3" }, "bin": { "opencontrol": "bin/index.mjs" } }, "sha512-QeCrpOK5D15QV8kjnGVeD/BHFLwcVr+sn4T6KKmP0WAMs2pww56e4h+eOGHb5iPOufUQXbdbBKi6WV2kk7tefQ=="],
|
||||||
|
|
||||||
"openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
|
"openid-client": ["openid-client@5.6.4", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
|
||||||
|
|
||||||
"opentui-spinner": ["opentui-spinner@0.0.6", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-xupLOeVQEAXEvVJCvHkfX6fChDWmJIPHe5jyUrVb8+n4XVTX8mBNhitFfB9v2ZbkC1H2UwPab/ElePHoW37NcA=="],
|
"opentui-spinner": ["opentui-spinner@0.0.6", "", { "dependencies": { "cli-spinners": "^3.3.0" }, "peerDependencies": { "@opentui/core": "^0.1.49", "@opentui/react": "^0.1.49", "@opentui/solid": "^0.1.49", "typescript": "^5" }, "optionalPeers": ["@opentui/react", "@opentui/solid"] }, "sha512-xupLOeVQEAXEvVJCvHkfX6fChDWmJIPHe5jyUrVb8+n4XVTX8mBNhitFfB9v2ZbkC1H2UwPab/ElePHoW37NcA=="],
|
||||||
@@ -4274,7 +4271,7 @@
|
|||||||
|
|
||||||
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
|
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
|
||||||
|
|
||||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
"punycode": ["punycode@1.3.2", "", {}, "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="],
|
||||||
|
|
||||||
"pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
|
"pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="],
|
||||||
|
|
||||||
@@ -4284,6 +4281,8 @@
|
|||||||
|
|
||||||
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||||
|
|
||||||
|
"querystring": ["querystring@0.2.0", "", {}, "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="],
|
||||||
|
|
||||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||||
|
|
||||||
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
||||||
@@ -4468,7 +4467,7 @@
|
|||||||
|
|
||||||
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
|
"sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="],
|
||||||
|
|
||||||
"sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
"sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
|
||||||
|
|
||||||
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
|
||||||
|
|
||||||
@@ -4596,23 +4595,23 @@
|
|||||||
|
|
||||||
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
"ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="],
|
||||||
|
|
||||||
"sst": ["sst@4.13.1", "", { "dependencies": { "@aws/durable-execution-sdk-js": "1.0.2", "aws4fetch": "1.0.18", "jose": "5.2.3", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "4.13.1", "sst-darwin-x64": "4.13.1", "sst-linux-arm64": "4.13.1", "sst-linux-x64": "4.13.1", "sst-linux-x86": "4.13.1", "sst-win32-arm64": "4.13.1", "sst-win32-x64": "4.13.1", "sst-win32-x86": "4.13.1" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-FKzhWuqV5NPhFWH7b+Ktf5+Mvox6DCMc8iOrjaS3mkjnnmyEcOZq7HNx2/ZlIcBwgOZE9sCPR3ot1zXtXkXzZg=="],
|
"sst": ["sst@3.18.10", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.18.10", "sst-darwin-x64": "3.18.10", "sst-linux-arm64": "3.18.10", "sst-linux-x64": "3.18.10", "sst-linux-x86": "3.18.10", "sst-win32-arm64": "3.18.10", "sst-win32-x64": "3.18.10", "sst-win32-x86": "3.18.10" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-SY+ldeJ9K5E9q+DhjXA3e2W3BEOzBwkE3IyLSD71uA3/5nRhUAST31iOWEpW36LbIvSQ9uOVDFcebztoLJ8s7w=="],
|
||||||
|
|
||||||
"sst-darwin-arm64": ["sst-darwin-arm64@4.13.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gGwfgZGOsulU6iGeZJtxP5BpSZFsRUy1dG/yTqJa++n1P1APWXZC64u2qRHm6rdwLAjEeWaCQ9k7ouKgn0zh5Q=="],
|
"sst-darwin-arm64": ["sst-darwin-arm64@3.18.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3MwIpMZhhdZKDqLp9ZQNlwkWix5+q+N0PWstuTomYwgZOxCCe6u9IIsoIszSk+GAJJN/jvGZyLiXKeV4iiQvw=="],
|
||||||
|
|
||||||
"sst-darwin-x64": ["sst-darwin-x64@4.13.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-RcKLI2oAY+Wk5fWUF0gJFKvwMSpid3qw9L3OcfgFL2ymZH652ybLpQPBxtoJ95FdyknWIMnYf6ZK9C2jnH5eaw=="],
|
"sst-darwin-x64": ["sst-darwin-x64@3.18.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-nQ0jMKkPOa+kj6Ygz8+kYhBua/vgNTLkd+4r8NSmk7v+Zs78lKnx3T//kEzS0yik6Q6QwGfokwrTcA1Jii2xSw=="],
|
||||||
|
|
||||||
"sst-linux-arm64": ["sst-linux-arm64@4.13.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Lp/8BWhH8KZnb50xPd8A6bkZ7QqCH3Dar9l16TPwuAmPxxRUlkmg42h+ACZ9n02vSJl8y/WLnV4JB6SgJpRFVg=="],
|
"sst-linux-arm64": ["sst-linux-arm64@3.18.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-mj9VNj3SvLS+HaXx2PhCX0aTA7CwJNoM6JhRc0s/zCilqchcvqDjbhpYBJO4brEPv6aOaaa7T3WvIQqtYauK4Q=="],
|
||||||
|
|
||||||
"sst-linux-x64": ["sst-linux-x64@4.13.1", "", { "os": "linux", "cpu": "x64" }, "sha512-f4eHOsApoP+CpUzLUGzfoTBYsqqr6nl/M0gjYsVNi1T+6v92lc84AhQ4DoDZx033QggyxkqRezj8jOkjkKxa7w=="],
|
"sst-linux-x64": ["sst-linux-x64@3.18.10", "", { "os": "linux", "cpu": "x64" }, "sha512-7iy1Eq2eqnT9Ag/8OVgC04vRjV7AAQyf/BvzLc+6Sz+GvRiKA8VEuPnbXNYQF+NIvEqsawfcd7MknSTtImpsvQ=="],
|
||||||
|
|
||||||
"sst-linux-x86": ["sst-linux-x86@4.13.1", "", { "os": "linux", "cpu": "none" }, "sha512-5OHdtPa0GmozDpBPdsD9Tv+SGnpjJSoTXptgK59ivQg3UYNWBBotidvEyUJs1MU9B/3+I+Fmw32zsF2O+ZtPgg=="],
|
"sst-linux-x86": ["sst-linux-x86@3.18.10", "", { "os": "linux", "cpu": "none" }, "sha512-77qZSuPZeQ5bdRCiq1pQEdY8EcGNHboKrx4P2yFid2FBDKJsXxOXtIxJdloyx+ljBn0+nxl/g040QBmXxdc9tA=="],
|
||||||
|
|
||||||
"sst-win32-arm64": ["sst-win32-arm64@4.13.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-qAUuCQj6wDdZ84a9W4FhnRTHYlEnYRHGYFqO3cmMImbnAQEoKSQR1XuWqt0Ev4RT3kU4Lz/fgvJjGkR7WUstLg=="],
|
"sst-win32-arm64": ["sst-win32-arm64@3.18.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-aY+FhMxvYs8crlrKALpLn/kKmud8YQj6LkMHsrOAAIJhfNyxhCja2vrYQaY+bcqdsS5W2LMVcS2hyaMqKXZKcg=="],
|
||||||
|
|
||||||
"sst-win32-x64": ["sst-win32-x64@4.13.1", "", { "os": "win32", "cpu": "x64" }, "sha512-r+83NMwpe4MLAvkVoOxi1KNs6nIqilFucsTr+VN1PgknsiVBY1emITjSWS2jDJJMuH8xJXL5xJzF/l5rcKWErg=="],
|
"sst-win32-x64": ["sst-win32-x64@3.18.10", "", { "os": "win32", "cpu": "x64" }, "sha512-rY+yJXOpG+P5xXnaQRpCvBK2zwwLhjzpYidGkp6F+cGgiVdh2Wre/CIQNRaVHr20ncj8lLe/RsHWa9QCNM48jg=="],
|
||||||
|
|
||||||
"sst-win32-x86": ["sst-win32-x86@4.13.1", "", { "os": "win32", "cpu": "none" }, "sha512-YPxBVdac/MsrzwlC6pF0NrrvMcmfdBLYjv7MbzHc5jNh1FQ1WPh6bdWQqgv0KD9EQTNLLEkej0beydgUvcCWJg=="],
|
"sst-win32-x86": ["sst-win32-x86@3.18.10", "", { "os": "win32", "cpu": "none" }, "sha512-pq8SmV0pIjBFMY6DraUZ4akyTxHnfjIKCRbBLdMxFUZK8TzA1NK2YdjRt1AwrgXRYGRyctrz/mt4WyO0SMOVQQ=="],
|
||||||
|
|
||||||
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
||||||
|
|
||||||
@@ -4892,6 +4891,8 @@
|
|||||||
|
|
||||||
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
|
||||||
|
|
||||||
|
"url": ["url@0.10.3", "", { "dependencies": { "punycode": "1.3.2", "querystring": "0.2.0" } }, "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ=="],
|
||||||
|
|
||||||
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
|
||||||
|
|
||||||
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
"use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="],
|
||||||
@@ -4900,6 +4901,8 @@
|
|||||||
|
|
||||||
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
|
"utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="],
|
||||||
|
|
||||||
|
"util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="],
|
||||||
|
|
||||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||||
|
|
||||||
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
|
||||||
@@ -4912,7 +4915,7 @@
|
|||||||
|
|
||||||
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
|
||||||
|
|
||||||
"venice-ai-sdk-provider": ["venice-ai-sdk-provider@2.0.2", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.47", "@ai-sdk/provider": "^3.0.10", "@ai-sdk/provider-utils": "^4.0.27" }, "peerDependencies": { "ai": "^6.0.90" } }, "sha512-aoa05nI3BTK5aGbjBflq+Gfln2AHAkwNbWuGGvCzUIsOfp5Y3iPD4O4PUGDAEiWVJWbjpPn0KfDa0H/HebwsaA=="],
|
"venice-ai-sdk-provider": ["venice-ai-sdk-provider@2.0.1", "", { "dependencies": { "@ai-sdk/openai-compatible": "^2.0.37", "@ai-sdk/provider": "^3.0.8", "@ai-sdk/provider-utils": "^4.0.21" }, "peerDependencies": { "ai": "^6.0.90" } }, "sha512-6SxA8a4MoA6Q/c+D3q7My0Hfog76enN3n0MXhwosM+tso66rXBEGeBRD/0lravRDVzL2Q1w5QJPc86rAVJtfXg=="],
|
||||||
|
|
||||||
"verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
|
"verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="],
|
||||||
|
|
||||||
@@ -5026,8 +5029,6 @@
|
|||||||
|
|
||||||
"xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
|
"xdg-basedir": ["xdg-basedir@5.1.0", "", {}, "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ=="],
|
||||||
|
|
||||||
"xml-naming": ["xml-naming@0.1.0", "", {}, "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw=="],
|
|
||||||
|
|
||||||
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
|
"xml2js": ["xml2js@0.6.2", "", { "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" } }, "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA=="],
|
||||||
|
|
||||||
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
"xmlbuilder": ["xmlbuilder@11.0.1", "", {}, "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="],
|
||||||
@@ -5190,20 +5191,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/client-cognito-identity/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/client-cognito-identity/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core": ["@aws-sdk/core@3.974.11", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@aws-sdk/xml-builder": "^3.972.24", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.972.42", "", { "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-ini": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/core": ["@smithy/core@3.24.3", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="],
|
"@aws-sdk/client-sts/@aws-sdk/core": ["@aws-sdk/core@3.775.0", "", { "dependencies": { "@aws-sdk/types": "3.775.0", "@smithy/core": "^3.2.0", "@smithy/node-config-provider": "^4.0.2", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/signature-v4": "^5.0.2", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-middleware": "^4.0.2", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-8vpW4WihVfz0DX+7WnnLGm3GuQER++b0IwQG35JlQMlgqnc44M//KbJPsIHA0aJUJVwJAEShgfr5dUbY8WUzaA=="],
|
||||||
@@ -5668,18 +5655,28 @@
|
|||||||
|
|
||||||
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"aws-sdk/events": ["events@1.1.1", "", {}, "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw=="],
|
||||||
|
|
||||||
|
"aws-sdk/uuid": ["uuid@8.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw=="],
|
||||||
|
|
||||||
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
|
||||||
|
|
||||||
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
|
||||||
|
|
||||||
|
"bl/buffer": ["buffer@6.0.3", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA=="],
|
||||||
|
|
||||||
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
||||||
|
|
||||||
"body-parser/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
|
"body-parser/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="],
|
||||||
|
|
||||||
|
"buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"builder-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
|
"builder-util-runtime/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||||
|
|
||||||
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
"c12/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
|
"c12/dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
|
||||||
@@ -5736,6 +5733,8 @@
|
|||||||
|
|
||||||
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
"engine.io-client/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||||
|
|
||||||
|
"es-get-iterator/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
"esbuild-plugin-copy/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||||
|
|
||||||
"esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"esbuild-plugin-copy/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
@@ -5838,14 +5837,22 @@
|
|||||||
|
|
||||||
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
|
"opencode/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
|
||||||
|
|
||||||
"opencode/@solid-primitives/scheduled": ["@solid-primitives/scheduled@1.5.2", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-/j2igE0xyNaHhj6kMfcUQn5rAVSTLbAX+CDEBm25hSNBmNiHLu2lM7Usj2kJJ5j36D67bE8wR1hBNA8hjtvsQA=="],
|
|
||||||
|
|
||||||
"opencode/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
|
"opencode/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
|
||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
"opencode-poe-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=="],
|
"opencode-poe-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=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
|
||||||
|
|
||||||
|
"opencontrol/@tsconfig/bun": ["@tsconfig/bun@1.0.7", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
|
||||||
|
|
||||||
|
"opencontrol/hono": ["hono@4.7.4", "", {}, "sha512-Pst8FuGqz3L7tFF+u9Pu70eI0xa5S3LPUmrNd5Jm8nTHze9FxLTK9Kaj5g/k4UcwuJSXTP65SyHOPLrffpcAJg=="],
|
||||||
|
|
||||||
|
"opencontrol/zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="],
|
||||||
|
|
||||||
|
"opencontrol/zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="],
|
||||||
|
|
||||||
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
"openid-client/jose": ["jose@4.15.9", "", {}, "sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA=="],
|
||||||
|
|
||||||
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||||
@@ -5892,6 +5899,8 @@
|
|||||||
|
|
||||||
"raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
|
"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=="],
|
||||||
|
|
||||||
"readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
|
"readdir-glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="],
|
||||||
|
|
||||||
"restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
"restore-cursor/onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||||
@@ -5902,6 +5911,10 @@
|
|||||||
|
|
||||||
"router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
"router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="],
|
||||||
|
|
||||||
|
"safe-array-concat/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
|
"safe-push-apply/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
"send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
|
||||||
|
|
||||||
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
"send/mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="],
|
||||||
@@ -5914,6 +5927,8 @@
|
|||||||
|
|
||||||
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
|
"shiki/@shikijs/types": ["@shikijs/types@3.20.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-lhYAATn10nkZcBQ0BlzSbJA3wcmL5MXUUF8d2Zzon6saZDlToKaiRX60n2+ZaHJCmXEcZRWNzn+k9vplr8Jhsw=="],
|
||||||
|
|
||||||
|
"sitemap/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||||
|
|
||||||
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
|
|
||||||
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
"slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
||||||
@@ -5956,11 +5971,9 @@
|
|||||||
|
|
||||||
"unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
"unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="],
|
||||||
|
|
||||||
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Enm5UlL0zUCrW3792opk5h7hRWxZOZzDe6eQYVFqX9LUOGGCe1h8MZWAGim765nwzgnjlpeYOsuzZmLtRsTPlg=="],
|
"uri-js/punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||||
|
|
||||||
"venice-ai-sdk-provider/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="],
|
"venice-ai-sdk-provider/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
|
||||||
|
|
||||||
"venice-ai-sdk-provider/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="],
|
|
||||||
|
|
||||||
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
"vite-plugin-icons-spritesheet/glob": ["glob@11.1.0", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw=="],
|
||||||
|
|
||||||
@@ -5978,6 +5991,8 @@
|
|||||||
|
|
||||||
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
|
"vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="],
|
||||||
|
|
||||||
|
"which-builtin-type/isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
|
||||||
|
|
||||||
"wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
"wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
||||||
|
|
||||||
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||||
@@ -5986,6 +6001,8 @@
|
|||||||
|
|
||||||
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
"wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
|
||||||
|
|
||||||
|
"xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||||
|
|
||||||
"yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
|
"yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="],
|
||||||
|
|
||||||
"yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
|
"yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="],
|
||||||
@@ -6050,36 +6067,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.6", "", { "dependencies": { "@aws-sdk/types": "^3.973.7", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-endpoints": "^3.3.4", "tslib": "^2.6.2" } }, "sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg=="],
|
"@aws-sdk/client-cognito-identity/@aws-sdk/middleware-user-agent/@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.996.6", "", { "dependencies": { "@aws-sdk/types": "^3.973.7", "@smithy/types": "^4.14.0", "@smithy/url-parser": "^4.2.13", "@smithy/util-endpoints": "^3.3.4", "tslib": "^2.6.2" } }, "sha512-2nUQ+2ih7CShuKHpGSIYvvAIOHy52dOZguYG36zptBukhw6iFwcvGfG0tes0oZFWQqEWvgZe9HLWaNlvXGdOrg=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.972.24", "", { "dependencies": { "@nodable/entities": "2.1.0", "@smithy/types": "^4.14.1", "fast-xml-parser": "5.7.3", "tslib": "^2.6.2" } }, "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.972.39", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/credential-provider-env": "^3.972.37", "@aws-sdk/credential-provider-http": "^3.972.39", "@aws-sdk/credential-provider-login": "^3.972.41", "@aws-sdk/credential-provider-process": "^3.972.37", "@aws-sdk/credential-provider-sso": "^3.972.41", "@aws-sdk/credential-provider-web-identity": "^3.972.41", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/credential-provider-imds": "^4.3.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.972.37", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/token-providers": "3.1048.0", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/types/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/fetch-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@smithy/node-http-handler/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/property-provider": "^4.0.2", "@smithy/types": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6ESVxwCbGm7WZ17kY1fjmxQud43vzJFoLd4bmlR+idQSWdqlzGDYdcfzpjDKTcivdtNrVYmFvcH1JBUwCRAZhw=="],
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.775.0", "", { "dependencies": { "@aws-sdk/core": "3.775.0", "@aws-sdk/types": "3.775.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/property-provider": "^4.0.2", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/util-stream": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PjDQeDH/J1S0yWV32wCj2k5liRo0ssXMseCBEkCsD3SqsU8o5cU82b0hMX4sAib/RkglCSZqGO0xMiN0/7ndww=="],
|
||||||
@@ -6250,6 +6237,8 @@
|
|||||||
|
|
||||||
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
"@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
||||||
|
|
||||||
|
"@azure/core-http/xml2js/sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
|
||||||
|
|
||||||
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
"@azure/core-xml/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
||||||
|
|
||||||
"@azure/identity/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
"@azure/identity/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||||
@@ -6562,10 +6551,14 @@
|
|||||||
|
|
||||||
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"babel-plugin-module-resolver/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
|
"bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
"body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||||
|
|
||||||
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
"c12/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
|
"crc/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||||
|
|
||||||
"dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
|
"dir-compare/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="],
|
||||||
@@ -6608,8 +6601,6 @@
|
|||||||
|
|
||||||
"lazystream/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
"lazystream/readable-stream/core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
|
||||||
|
|
||||||
"lazystream/readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
|
|
||||||
|
|
||||||
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
"lazystream/readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
|
||||||
|
|
||||||
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
"lazystream/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
|
||||||
@@ -6624,6 +6615,18 @@
|
|||||||
|
|
||||||
"opencode-poe-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
"opencode-poe-auth/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||||
|
|
||||||
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||||
|
|
||||||
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||||
@@ -6636,6 +6639,8 @@
|
|||||||
|
|
||||||
"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=="],
|
"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=="],
|
||||||
|
|
||||||
|
"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=="],
|
"readdir-glob/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
|
||||||
|
|
||||||
"restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
"restore-cursor/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
|
||||||
@@ -6662,10 +6667,6 @@
|
|||||||
|
|
||||||
"unplugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
"unplugin/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||||
|
|
||||||
"venice-ai-sdk-provider/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
|
||||||
|
|
||||||
"venice-ai-sdk-provider/@ai-sdk/provider-utils/eventsource-parser": ["eventsource-parser@3.0.8", "", {}, "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ=="],
|
|
||||||
|
|
||||||
"vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
"vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||||
|
|
||||||
"vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
|
"vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
|
||||||
@@ -6758,18 +6759,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.5.8", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.2.0", "strnum": "^2.2.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-Z7Fh2nVQSb2d+poDViM063ix2ZGt9jmY1nWhPfHBOK2Hgnb/OW3P4Et3P/81SEej0J7QbWtJqxO05h8QYfK7LQ=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser": ["fast-xml-parser@5.7.3", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.7", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.972.41", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.1048.0", "", { "dependencies": { "@aws-sdk/core": "^3.974.11", "@aws-sdk/nested-clients": "^3.997.9", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.997.9", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.974.11", "@aws-sdk/signature-v4-multi-region": "^3.996.27", "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/fetch-http-handler": "^5.4.2", "@smithy/node-http-handler": "^4.7.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-QOYC8q7luzHFXrP0xYAqBctoPkynjfV0r9dqntFu4/IWMTyC1vlo1UTxFAjIPyclYw92XJyEkVCVg9v/nQnsUA=="],
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.782.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.775.0", "@aws-sdk/middleware-host-header": "3.775.0", "@aws-sdk/middleware-logger": "3.775.0", "@aws-sdk/middleware-recursion-detection": "3.775.0", "@aws-sdk/middleware-user-agent": "3.782.0", "@aws-sdk/region-config-resolver": "3.775.0", "@aws-sdk/types": "3.775.0", "@aws-sdk/util-endpoints": "3.782.0", "@aws-sdk/util-user-agent-browser": "3.775.0", "@aws-sdk/util-user-agent-node": "3.782.0", "@smithy/config-resolver": "^4.1.0", "@smithy/core": "^3.2.0", "@smithy/fetch-http-handler": "^5.0.2", "@smithy/hash-node": "^4.0.2", "@smithy/invalid-dependency": "^4.0.2", "@smithy/middleware-content-length": "^4.0.2", "@smithy/middleware-endpoint": "^4.1.0", "@smithy/middleware-retry": "^4.1.0", "@smithy/middleware-serde": "^4.0.3", "@smithy/middleware-stack": "^4.0.2", "@smithy/node-config-provider": "^4.0.2", "@smithy/node-http-handler": "^4.0.4", "@smithy/protocol-http": "^5.1.0", "@smithy/smithy-client": "^4.2.0", "@smithy/types": "^4.2.0", "@smithy/url-parser": "^4.0.2", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.8", "@smithy/util-defaults-mode-node": "^4.0.8", "@smithy/util-endpoints": "^3.0.2", "@smithy/util-middleware": "^4.0.2", "@smithy/util-retry": "^4.0.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5GlJBejo8wqMpSSEKb45WE82YxI2k73YuebjLH/eWDNQeE6VI5Bh9lA1YQ7xNkLLH8hIsb0pSfKVuwh0VEzVrg=="],
|
||||||
@@ -6966,6 +6955,30 @@
|
|||||||
|
|
||||||
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
"js-beautify/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||||
|
|
||||||
|
"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=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
|
||||||
|
|
||||||
|
"ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||||
|
|
||||||
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
||||||
|
|
||||||
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
|
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
|
||||||
@@ -6988,16 +7001,6 @@
|
|||||||
|
|
||||||
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
"@aws-sdk/client-cognito-identity/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.996.27", "", { "dependencies": { "@aws-sdk/types": "^3.973.8", "@smithy/core": "^3.24.2", "@smithy/signature-v4": "^5.4.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/client-sso/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
@@ -7072,6 +7075,8 @@
|
|||||||
|
|
||||||
"js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
"js-beautify/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
|
||||||
|
|
||||||
|
"opencontrol/@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
|
||||||
|
|
||||||
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
"pkg-dir/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||||
|
|
||||||
"pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
"pkg-up/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
|
||||||
@@ -7080,12 +7085,6 @@
|
|||||||
|
|
||||||
"tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
"tw-to-css/tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-ini/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-lambda/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-web-identity/@aws-sdk/nested-clients/@aws-sdk/signature-v4-multi-region/@smithy/signature-v4": ["@smithy/signature-v4@5.4.3", "", { "dependencies": { "@smithy/core": "^3.24.3", "@smithy/types": "^4.14.2", "tslib": "^2.6.2" } }, "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g=="],
|
|
||||||
|
|
||||||
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
"@aws-sdk/client-sts/@aws-sdk/credential-provider-node/@aws-sdk/credential-provider-sso/@aws-sdk/token-providers/@aws-sdk/nested-clients/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
|
||||||
|
|
||||||
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
"@aws-sdk/credential-provider-cognito-identity/@aws-sdk/nested-clients/@aws-sdk/core/@aws-sdk/xml-builder/fast-xml-parser/strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
|
||||||
|
|||||||
+11
-11
@@ -4,7 +4,7 @@ import { domain } from "./stage"
|
|||||||
const description = "Managed by SST (Don't edit in Honeycomb UI)"
|
const description = "Managed by SST (Don't edit in Honeycomb UI)"
|
||||||
const alertsDisabled = $app.stage !== "production"
|
const alertsDisabled = $app.stage !== "production"
|
||||||
|
|
||||||
const webhookRecipient = new honeycombio.WebhookRecipient("DiscordAlerts", {
|
const webhookRecipient = new honeycomb.WebhookRecipient("DiscordAlerts", {
|
||||||
name: $app.stage === "production" ? "Discord Alerts" : `Discord Alerts (${$app.stage})`,
|
name: $app.stage === "production" ? "Discord Alerts" : `Discord Alerts (${$app.stage})`,
|
||||||
url: `https://${domain}/honeycomb/webhook`,
|
url: `https://${domain}/honeycomb/webhook`,
|
||||||
secret: SECRET.HoneycombWebhookSecret.result,
|
secret: SECRET.HoneycombWebhookSecret.result,
|
||||||
@@ -67,7 +67,7 @@ IF(
|
|||||||
)`,
|
)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
return honeycombio.getQuerySpecificationOutput({
|
return honeycomb.getQuerySpecificationOutput({
|
||||||
breakdowns: ["model"],
|
breakdowns: ["model"],
|
||||||
calculatedFields: [failedHttpStatus],
|
calculatedFields: [failedHttpStatus],
|
||||||
calculations: [
|
calculations: [
|
||||||
@@ -99,7 +99,7 @@ const providerHttpErrorsQuery = () => {
|
|||||||
expression: `IF(GT($llm.error.code, "400"), 1, 0)`,
|
expression: `IF(GT($llm.error.code, "400"), 1, 0)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
return honeycombio.getQuerySpecificationOutput({
|
return honeycomb.getQuerySpecificationOutput({
|
||||||
breakdowns: ["provider"],
|
breakdowns: ["provider"],
|
||||||
calculatedFields: [successHttpStatus, failedProviderHttpStatus],
|
calculatedFields: [successHttpStatus, failedProviderHttpStatus],
|
||||||
calculations: [
|
calculations: [
|
||||||
@@ -140,7 +140,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
|
|||||||
{ column: "tps.output", op: "exists" },
|
{ column: "tps.output", op: "exists" },
|
||||||
]
|
]
|
||||||
|
|
||||||
return honeycombio.getQuerySpecificationOutput({
|
return honeycomb.getQuerySpecificationOutput({
|
||||||
breakdowns: ["model"],
|
breakdowns: ["model"],
|
||||||
calculations: [
|
calculations: [
|
||||||
{ op: "COUNT", name: "TOTAL", filterCombination: "AND", filters },
|
{ op: "COUNT", name: "TOTAL", filterCombination: "AND", filters },
|
||||||
@@ -157,7 +157,7 @@ const modelLowTpsQuery = (product: "go" | "zen") => {
|
|||||||
}).json
|
}).json
|
||||||
}
|
}
|
||||||
|
|
||||||
new honeycombio.Trigger("IncreasedModelHttpErrorsGo", {
|
new honeycomb.Trigger("IncreasedModelHttpErrorsGo", {
|
||||||
name: "Increased Model HTTP Errors [Go]",
|
name: "Increased Model HTTP Errors [Go]",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
@@ -177,7 +177,7 @@ new honeycombio.Trigger("IncreasedModelHttpErrorsGo", {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
new honeycombio.Trigger("IncreasedModelHttpErrorsZen", {
|
new honeycomb.Trigger("IncreasedModelHttpErrorsZen", {
|
||||||
name: "Increased Model HTTP Errors [Zen]",
|
name: "Increased Model HTTP Errors [Zen]",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
@@ -197,7 +197,7 @@ new honeycombio.Trigger("IncreasedModelHttpErrorsZen", {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
new honeycombio.Trigger("LowModelTpsGo", {
|
new honeycomb.Trigger("LowModelTpsGo", {
|
||||||
name: "Low Model TPS [Go]",
|
name: "Low Model TPS [Go]",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
@@ -217,7 +217,7 @@ new honeycombio.Trigger("LowModelTpsGo", {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
new honeycombio.Trigger("LowModelTpsZen", {
|
new honeycomb.Trigger("LowModelTpsZen", {
|
||||||
name: "Low Model TPS [Zen]",
|
name: "Low Model TPS [Zen]",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
@@ -237,7 +237,7 @@ new honeycombio.Trigger("LowModelTpsZen", {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
new honeycombio.Trigger("IncreasedProviderHttpErrors", {
|
new honeycomb.Trigger("IncreasedProviderHttpErrors", {
|
||||||
name: "Increased Provider HTTP Errors",
|
name: "Increased Provider HTTP Errors",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
@@ -257,11 +257,11 @@ new honeycombio.Trigger("IncreasedProviderHttpErrors", {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
new honeycombio.Trigger("IncreasedFreeTierRequests", {
|
new honeycomb.Trigger("IncreasedFreeTierRequests", {
|
||||||
name: "Increased Free Tier Requests",
|
name: "Increased Free Tier Requests",
|
||||||
description,
|
description,
|
||||||
disabled: alertsDisabled,
|
disabled: alertsDisabled,
|
||||||
queryJson: honeycombio.getQuerySpecificationOutput({
|
queryJson: honeycomb.getQuerySpecificationOutput({
|
||||||
calculations: [{ op: "COUNT" }],
|
calculations: [{ op: "COUNT" }],
|
||||||
filters: [
|
filters: [
|
||||||
{ column: "event_type", op: "=", value: "completions" },
|
{ column: "event_type", op: "=", value: "completions" },
|
||||||
|
|||||||
+1
-10
@@ -3,7 +3,6 @@
|
|||||||
stdenv,
|
stdenv,
|
||||||
bun,
|
bun,
|
||||||
nodejs,
|
nodejs,
|
||||||
darwin,
|
|
||||||
electron_41,
|
electron_41,
|
||||||
makeWrapper,
|
makeWrapper,
|
||||||
writableTmpDirAsHomeHook,
|
writableTmpDirAsHomeHook,
|
||||||
@@ -15,12 +14,7 @@ let
|
|||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "opencode-desktop";
|
pname = "opencode-desktop";
|
||||||
inherit (opencode)
|
inherit (opencode) version src node_modules;
|
||||||
version
|
|
||||||
src
|
|
||||||
node_modules
|
|
||||||
patches
|
|
||||||
;
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
bun
|
bun
|
||||||
@@ -29,9 +23,6 @@ stdenv.mkDerivation (finalAttrs: {
|
|||||||
writableTmpDirAsHomeHook
|
writableTmpDirAsHomeHook
|
||||||
] ++ lib.optionals stdenv.hostPlatform.isLinux [
|
] ++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||||
autoPatchelfHook
|
autoPatchelfHook
|
||||||
] ++ lib.optionals stdenv.hostPlatform.isDarwin [
|
|
||||||
# Ad-hoc sign the .app: --config.mac.identity=null below skips signing.
|
|
||||||
darwin.autoSignDarwinBinariesHook
|
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
|
buildInputs = lib.optionals stdenv.hostPlatform.isLinux [
|
||||||
|
|||||||
+4
-4
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"nodeModules": {
|
"nodeModules": {
|
||||||
"x86_64-linux": "sha256-qq8cKNcW10NRbPx2T/Gpzby4nx2qHzj0LWDwqyPl7rY=",
|
"x86_64-linux": "sha256-kCSAVPQgJROcvnnwf0Cn6PuYL25hYgTasJeBJlmnFgQ=",
|
||||||
"aarch64-linux": "sha256-UguK4nObAL/ovGZQousveoXCmkcxEgWy7XSSzxVJNEY=",
|
"aarch64-linux": "sha256-prY27Ek2QhW+4OvBJ3bHHkUDoLTA4mD3KQmOQqSbAuo=",
|
||||||
"aarch64-darwin": "sha256-NibSqZiF0XMbFhOtl2CQvFe4N/BXB5UAdt2iKv7dIcI=",
|
"aarch64-darwin": "sha256-0yIqnnjreVHTgGZLrKFpT9Cc2B2LNfmYcRByaCu7tiU=",
|
||||||
"x86_64-darwin": "sha256-ZNeZSpYitdUjXx/sULAIoLXrM6+Vz+GXQxKpdfgXh60="
|
"x86_64-darwin": "sha256-n+urvMRozB9nO5D3qyCweSa5HExFk1YGEzOt2445LEE="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -98,7 +98,7 @@
|
|||||||
"oxlint-tsgolint": "0.21.0",
|
"oxlint-tsgolint": "0.21.0",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
"semver": "^7.6.0",
|
"semver": "^7.6.0",
|
||||||
"sst": "4.13.1",
|
"sst": "3.18.10",
|
||||||
"turbo": "2.8.13"
|
"turbo": "2.8.13"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -139,7 +139,6 @@
|
|||||||
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
|
||||||
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
|
||||||
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
|
||||||
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
|
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch"
|
||||||
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { expect, test, type Page } from "@playwright/test"
|
import { expect, test, type Page } from "@playwright/test"
|
||||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
|
||||||
import { fixture, pageMessages } from "./session-timeline.fixture"
|
import { fixture, pageMessages } from "./session-timeline.fixture"
|
||||||
import { trackPageErrors, expectNoSmokeErrors } from "../utils/errors"
|
import { trackPageErrors, expectNoSmokeErrors } from "../utils/errors"
|
||||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||||
@@ -38,12 +37,12 @@ test.describe("smoke: session timeline", () => {
|
|||||||
project: fixture.project,
|
project: fixture.project,
|
||||||
pageMessages,
|
pageMessages,
|
||||||
})
|
})
|
||||||
await configureSmokePage(page, fixture.directory)
|
await configureSmokePage(page)
|
||||||
|
|
||||||
await selectHomeProject(page, fixture.project.name)
|
await openProject(page, "SmokeProject")
|
||||||
await navigateToSession(page, fixture.directory, fixture.sourceID, fixture.expected.sourceTitle)
|
await navigateToSession(page, fixture.sourceID, fixture.expected.sourceTitle)
|
||||||
await expectSessionReady(page)
|
await expectSessionReady(page, "smoke-project")
|
||||||
await navigateToSession(page, fixture.directory, fixture.targetID, fixture.expected.targetTitle)
|
await navigateToSession(page, fixture.targetID, fixture.expected.targetTitle)
|
||||||
const expectedPartIDs = fixture.expected.targetPartIDs
|
const expectedPartIDs = fixture.expected.targetPartIDs
|
||||||
const expectedMessageIDs = fixture.expected.targetMessageIDs
|
const expectedMessageIDs = fixture.expected.targetMessageIDs
|
||||||
await expectSessionTimelineReady(page, expectedPartIDs, expectedMessageIDs, errors)
|
await expectSessionTimelineReady(page, expectedPartIDs, expectedMessageIDs, errors)
|
||||||
@@ -51,7 +50,7 @@ test.describe("smoke: session timeline", () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function configureSmokePage(page: Page, directory: string) {
|
async function configureSmokePage(page: Page) {
|
||||||
await page.addInitScript(() => {
|
await page.addInitScript(() => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
"settings.v3",
|
"settings.v3",
|
||||||
@@ -64,23 +63,7 @@ async function configureSmokePage(page: Page, directory: string) {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
|
||||||
|
|
||||||
await page.addInitScript((directory) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
"opencode.global.dat:server",
|
|
||||||
JSON.stringify({
|
|
||||||
projects: {
|
|
||||||
local: [{ worktree: directory, expanded: true }],
|
|
||||||
},
|
|
||||||
lastProject: {
|
|
||||||
local: directory,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}, directory)
|
|
||||||
|
|
||||||
await page.addInitScript(() => {
|
|
||||||
const smoke = window as SmokeWindow
|
const smoke = window as SmokeWindow
|
||||||
smoke.__timelineSmokeErrorToasts = []
|
smoke.__timelineSmokeErrorToasts = []
|
||||||
smoke.__timelineSmokeForbiddenText = []
|
smoke.__timelineSmokeForbiddenText = []
|
||||||
@@ -409,20 +392,21 @@ function expectCompleteScroll(
|
|||||||
expect(expectedPartIDs.length).toBe(331)
|
expect(expectedPartIDs.length).toBe(331)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function selectHomeProject(page: Page, projectName: string) {
|
async function openProject(page: Page, projectName: string) {
|
||||||
await page.goto("/")
|
await page.goto("/")
|
||||||
await page
|
await page.getByRole("button", { name: new RegExp(projectName, "i") }).click()
|
||||||
.locator('[data-component="home-project-row"]')
|
|
||||||
.filter({ hasText: new RegExp(projectName, "i") })
|
|
||||||
.click()
|
|
||||||
await expect(page).toHaveURL(/\/$/)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function navigateToSession(page: Page, directory: string, sessionId: string, expectedTitle: string) {
|
async function navigateToSession(page: Page, sessionId: string, expectedTitle: string) {
|
||||||
await page.goto(`/${base64Encode(directory)}/session/${sessionId}`)
|
// Use evaluate to click to avoid strict visibility/animation issues during rapid e2e navigation
|
||||||
|
await page
|
||||||
|
.locator(`a[href*="${sessionId}"]`)
|
||||||
|
.first()
|
||||||
|
.evaluate((el) => (el as HTMLElement).click())
|
||||||
await expect(page.getByRole("heading", { name: expectedTitle })).toBeVisible()
|
await expect(page.getByRole("heading", { name: expectedTitle })).toBeVisible()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function expectSessionReady(page: Page) {
|
async function expectSessionReady(page: Page, projectName: string) {
|
||||||
await expect(page.getByRole("textbox", { name: /Ask anything/i })).toBeVisible()
|
await expect(page.getByText(projectName).first()).toBeVisible()
|
||||||
|
await expect(page.getByText("Ask anything...")).toBeVisible()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/app",
|
"name": "@opencode-ai/app",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -42,10 +42,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@kobalte/core": "catalog:",
|
"@kobalte/core": "catalog:",
|
||||||
"@opencode-ai/core": "workspace:*",
|
"@sentry/solid": "catalog:",
|
||||||
"@opencode-ai/sdk": "workspace:*",
|
"@opencode-ai/sdk": "workspace:*",
|
||||||
"@opencode-ai/ui": "workspace:*",
|
"@opencode-ai/ui": "workspace:*",
|
||||||
"@sentry/solid": "catalog:",
|
"@opencode-ai/core": "workspace:*",
|
||||||
"@shikijs/transformers": "3.9.2",
|
"@shikijs/transformers": "3.9.2",
|
||||||
"@solid-primitives/active-element": "2.1.3",
|
"@solid-primitives/active-element": "2.1.3",
|
||||||
"@solid-primitives/audio": "1.4.2",
|
"@solid-primitives/audio": "1.4.2",
|
||||||
@@ -54,7 +54,6 @@
|
|||||||
"@solid-primitives/i18n": "2.2.1",
|
"@solid-primitives/i18n": "2.2.1",
|
||||||
"@solid-primitives/media": "2.3.3",
|
"@solid-primitives/media": "2.3.3",
|
||||||
"@solid-primitives/resize-observer": "2.1.5",
|
"@solid-primitives/resize-observer": "2.1.5",
|
||||||
"@solid-primitives/scheduled": "1.5.3",
|
|
||||||
"@solid-primitives/scroll": "2.1.3",
|
"@solid-primitives/scroll": "2.1.3",
|
||||||
"@solid-primitives/storage": "catalog:",
|
"@solid-primitives/storage": "catalog:",
|
||||||
"@solid-primitives/timer": "1.4.4",
|
"@solid-primitives/timer": "1.4.4",
|
||||||
|
|||||||
+14
-10
@@ -48,17 +48,22 @@ import { ErrorPage } from "./pages/error"
|
|||||||
import { useCheckServerHealth } from "./utils/server-health"
|
import { useCheckServerHealth } from "./utils/server-health"
|
||||||
|
|
||||||
const HomeRoute = lazy(() => import("@/pages/home"))
|
const HomeRoute = lazy(() => import("@/pages/home"))
|
||||||
const Session = lazy(() => import("@/pages/session"))
|
const loadSession = () => import("@/pages/session")
|
||||||
|
const Session = lazy(loadSession)
|
||||||
|
const Loading = () => <div class="size-full" />
|
||||||
|
|
||||||
const SessionRoute = Object.assign(
|
if (typeof location === "object" && /\/session(?:\/|$)/.test(location.pathname)) {
|
||||||
() => (
|
void loadSession()
|
||||||
<SessionProviders>
|
}
|
||||||
<Session />
|
|
||||||
</SessionProviders>
|
const SessionRoute = () => (
|
||||||
),
|
<SessionProviders>
|
||||||
{ preload: Session.preload },
|
<Session />
|
||||||
|
</SessionProviders>
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const SessionIndexRoute = () => <Navigate href="session" />
|
||||||
|
|
||||||
function UiI18nBridge(props: ParentProps) {
|
function UiI18nBridge(props: ParentProps) {
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
|
return <I18nProvider value={{ locale: language.intl, t: language.t }}>{props.children}</I18nProvider>
|
||||||
@@ -73,7 +78,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
api?: {
|
api?: {
|
||||||
setTitlebar?: (theme: { mode: "light" | "dark" }) => Promise<void>
|
setTitlebar?: (theme: { mode: "light" | "dark" }) => Promise<void>
|
||||||
exportDebugLogs?: () => Promise<string>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -312,7 +316,7 @@ export function AppInterface(props: {
|
|||||||
>
|
>
|
||||||
<Route path="/" component={HomeRoute} />
|
<Route path="/" component={HomeRoute} />
|
||||||
<Route path="/:dir" component={DirectoryLayout}>
|
<Route path="/:dir" component={DirectoryLayout}>
|
||||||
<Route path="/" component={() => <Navigate href="session" />} />
|
<Route path="/" component={SessionIndexRoute} />
|
||||||
<Route path="/session/:id?" component={SessionRoute} />
|
<Route path="/session/:id?" component={SessionRoute} />
|
||||||
</Route>
|
</Route>
|
||||||
</Dynamic>
|
</Dynamic>
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const provider = createMemo(
|
const provider = createMemo(
|
||||||
() => providers.all().get(props.provider) ?? globalSync.data.provider.all.get(props.provider)!,
|
() =>
|
||||||
|
providers.all().find((x) => x.id === props.provider) ??
|
||||||
|
globalSync.data.provider.all.find((x) => x.id === props.provider)!,
|
||||||
)
|
)
|
||||||
const fallback = createMemo<ProviderAuthMethod[]>(() => [
|
const fallback = createMemo<ProviderAuthMethod[]>(() => [
|
||||||
{
|
{
|
||||||
@@ -524,7 +526,7 @@ export function DialogConnectProvider(props: { provider: string }) {
|
|||||||
const code = createMemo(() => {
|
const code = createMemo(() => {
|
||||||
const instructions = store.authorization?.instructions
|
const instructions = store.authorization?.instructions
|
||||||
if (instructions?.includes(":")) {
|
if (instructions?.includes(":")) {
|
||||||
return instructions.split(":").pop()?.trim()
|
return instructions.split(":")[1]?.trim()
|
||||||
}
|
}
|
||||||
return instructions
|
return instructions
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export function DialogCustomProvider(props: Props) {
|
|||||||
form,
|
form,
|
||||||
t: language.t,
|
t: language.t,
|
||||||
disabledProviders: globalSync.data.config.disabled_providers ?? [],
|
disabledProviders: globalSync.data.config.disabled_providers ?? [],
|
||||||
existingProviderIDs: new Set(globalSync.data.provider.all.keys()),
|
existingProviderIDs: new Set(globalSync.data.provider.all.map((p) => p.id)),
|
||||||
})
|
})
|
||||||
batch(() => {
|
batch(() => {
|
||||||
setForm("err", output.err)
|
setForm("err", output.err)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { type LocalProject, getAvatarColors } from "@/context/layout"
|
|||||||
import { getFilename } from "@opencode-ai/core/util/path"
|
import { getFilename } from "@opencode-ai/core/util/path"
|
||||||
import { Avatar } from "@opencode-ai/ui/avatar"
|
import { Avatar } from "@opencode-ai/ui/avatar"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { getProjectAvatarSource } from "@/pages/layout/helpers"
|
import { getProjectAvatarSource } from "@/pages/layout/sidebar-items"
|
||||||
|
|
||||||
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
|
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export const DialogSelectModelUnpaid: Component<{ model?: ModelState }> = (props
|
|||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<List
|
<List
|
||||||
class="w-full px-0"
|
class="w-full px-0"
|
||||||
key={(p) => p.id}
|
key={(x) => x?.id}
|
||||||
items={providers.popular}
|
items={providers.popular}
|
||||||
activeIcon="plus-small"
|
activeIcon="plus-small"
|
||||||
sortBy={(a, b) => {
|
sortBy={(a, b) => {
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export const DialogSelectProvider: Component = () => {
|
|||||||
key={(x) => x?.id}
|
key={(x) => x?.id}
|
||||||
items={() => {
|
items={() => {
|
||||||
language.locale()
|
language.locale()
|
||||||
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all().values()]
|
return [{ id: CUSTOM_ID, name: customLabel() }, ...providers.all()]
|
||||||
}}
|
}}
|
||||||
filterKeys={["id", "name"]}
|
filterKeys={["id", "name"]}
|
||||||
groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())}
|
groupBy={(x) => (popularProviders.includes(x.id) ? popularGroup() : otherGroup())}
|
||||||
|
|||||||
@@ -1,17 +1,6 @@
|
|||||||
import { useFilteredList } from "@opencode-ai/ui/hooks"
|
import { useFilteredList } from "@opencode-ai/ui/hooks"
|
||||||
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
import { useSpring } from "@opencode-ai/ui/motion-spring"
|
||||||
import {
|
import { createEffect, on, Component, Show, onCleanup, createMemo, createSignal, createResource } from "solid-js"
|
||||||
createEffect,
|
|
||||||
on,
|
|
||||||
Component,
|
|
||||||
Show,
|
|
||||||
onCleanup,
|
|
||||||
createMemo,
|
|
||||||
createSignal,
|
|
||||||
createResource,
|
|
||||||
Switch,
|
|
||||||
Match,
|
|
||||||
} from "solid-js"
|
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { useLocal } from "@/context/local"
|
import { useLocal } from "@/context/local"
|
||||||
import { selectionFromLines, type SelectedLineRange, useFile } from "@/context/file"
|
import { selectionFromLines, type SelectedLineRange, useFile } from "@/context/file"
|
||||||
@@ -68,14 +57,11 @@ import { ImagePreview } from "@opencode-ai/ui/image-preview"
|
|||||||
import { useQueries } from "@tanstack/solid-query"
|
import { useQueries } from "@tanstack/solid-query"
|
||||||
import { useQueryOptions } from "@/context/global-sync"
|
import { useQueryOptions } from "@/context/global-sync"
|
||||||
import { pathKey } from "@/utils/path-key"
|
import { pathKey } from "@/utils/path-key"
|
||||||
import { getFilename } from "@opencode-ai/core/util/path"
|
|
||||||
|
|
||||||
interface PromptInputProps {
|
interface PromptInputProps {
|
||||||
class?: string
|
class?: string
|
||||||
variant?: "dock" | "new-session"
|
|
||||||
ref?: (el: HTMLDivElement) => void
|
ref?: (el: HTMLDivElement) => void
|
||||||
newSessionWorktree?: string
|
newSessionWorktree?: string
|
||||||
onNewSessionWorktreeChange?: (worktree: string) => void
|
|
||||||
onNewSessionWorktreeReset?: () => void
|
onNewSessionWorktreeReset?: () => void
|
||||||
edit?: { id: string; prompt: Prompt; context: FollowupDraft["context"] }
|
edit?: { id: string; prompt: Prompt; context: FollowupDraft["context"] }
|
||||||
onEditLoaded?: () => void
|
onEditLoaded?: () => void
|
||||||
@@ -113,9 +99,6 @@ const EXAMPLES = [
|
|||||||
"prompt.example.25",
|
"prompt.example.25",
|
||||||
] as const
|
] as const
|
||||||
|
|
||||||
const MAIN_WORKTREE = "main"
|
|
||||||
const CREATE_WORKTREE = "create"
|
|
||||||
|
|
||||||
export const PromptInput: Component<PromptInputProps> = (props) => {
|
export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||||
const sdk = useSDK()
|
const sdk = useSDK()
|
||||||
const queryOptions = useQueryOptions()
|
const queryOptions = useQueryOptions()
|
||||||
@@ -1072,21 +1055,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
readClipboardImage: platform.readClipboardImage,
|
readClipboardImage: platform.readClipboardImage,
|
||||||
})
|
})
|
||||||
|
|
||||||
const fileAttachmentInput = () => (
|
|
||||||
<input
|
|
||||||
ref={(el) => (fileInputRef = el)}
|
|
||||||
type="file"
|
|
||||||
multiple
|
|
||||||
accept={ACCEPTED_FILE_TYPES.join(",")}
|
|
||||||
class="hidden"
|
|
||||||
onChange={(e) => {
|
|
||||||
const list = e.currentTarget.files
|
|
||||||
if (list) void addAttachments(Array.from(list))
|
|
||||||
e.currentTarget.value = ""
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
const variants = createMemo(() => ["default", ...local.model.variant.list()])
|
const variants = createMemo(() => ["default", ...local.model.variant.list()])
|
||||||
const accepting = createMemo(() => {
|
const accepting = createMemo(() => {
|
||||||
const id = params.id
|
const id = params.id
|
||||||
@@ -1298,99 +1266,8 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
(p) => p,
|
(p) => p,
|
||||||
)
|
)
|
||||||
|
|
||||||
const designPlaceholder = () => {
|
|
||||||
if (store.mode === "shell") return placeholder()
|
|
||||||
return "Ask anything, / for commands, @ for context..."
|
|
||||||
}
|
|
||||||
|
|
||||||
const modelControl = () => (
|
|
||||||
<Show when={!providersLoading()}>
|
|
||||||
<Show
|
|
||||||
when={providers.paid().length > 0}
|
|
||||||
fallback={
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={4}
|
|
||||||
title={language.t("command.model.choose")}
|
|
||||||
keybind={command.keybind("model.choose")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
data-action="prompt-model"
|
|
||||||
as="div"
|
|
||||||
variant="ghost"
|
|
||||||
size="normal"
|
|
||||||
class="min-w-0 max-w-[220px] justify-start text-[13px] font-[440] leading-4 text-v2-text-text-faint group"
|
|
||||||
style={control()}
|
|
||||||
onClick={() => {
|
|
||||||
void import("@/components/dialog-select-model-unpaid").then((x) => {
|
|
||||||
dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
|
||||||
<ProviderIcon
|
|
||||||
id={local.model.current()?.provider?.id ?? ""}
|
|
||||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
|
||||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<span class="truncate">{local.model.current()?.name ?? language.t("dialog.model.select.title")}</span>
|
|
||||||
<Icon name="chevron-down" size="small" class="shrink-0 text-v2-icon-icon-muted" />
|
|
||||||
</Button>
|
|
||||||
</TooltipKeybind>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={4}
|
|
||||||
title={language.t("command.model.choose")}
|
|
||||||
keybind={command.keybind("model.choose")}
|
|
||||||
>
|
|
||||||
<ModelSelectorPopover
|
|
||||||
model={local.model}
|
|
||||||
triggerAs={Button}
|
|
||||||
triggerProps={{
|
|
||||||
variant: "ghost",
|
|
||||||
size: "normal",
|
|
||||||
style: control(),
|
|
||||||
class:
|
|
||||||
"min-w-0 max-w-[220px] justify-start text-[13px] font-[440] leading-4 text-v2-text-text-faint group",
|
|
||||||
"data-action": "prompt-model",
|
|
||||||
}}
|
|
||||||
onClose={restoreFocus}
|
|
||||||
>
|
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
|
||||||
<ProviderIcon
|
|
||||||
id={local.model.current()?.provider?.id ?? ""}
|
|
||||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
|
||||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<span class="truncate">{local.model.current()?.name ?? language.t("dialog.model.select.title")}</span>
|
|
||||||
<Icon name="chevron-down" size="small" class="shrink-0 text-v2-icon-icon-muted" />
|
|
||||||
</ModelSelectorPopover>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
|
|
||||||
const newSession = () => props.variant === "new-session"
|
|
||||||
const worktrees = createMemo(() => [MAIN_WORKTREE, ...(sync.project?.sandboxes ?? []), CREATE_WORKTREE])
|
|
||||||
const currentWorktree = createMemo(() => {
|
|
||||||
if (worktrees().includes(props.newSessionWorktree ?? MAIN_WORKTREE))
|
|
||||||
return props.newSessionWorktree ?? MAIN_WORKTREE
|
|
||||||
return MAIN_WORKTREE
|
|
||||||
})
|
|
||||||
const worktreeLabel = (value: string) => {
|
|
||||||
if (value === MAIN_WORKTREE) return MAIN_WORKTREE
|
|
||||||
if (value === CREATE_WORKTREE) return language.t("session.new.worktree.create")
|
|
||||||
return getFilename(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const USE_V2_INPUT = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative size-full flex flex-col gap-0">
|
<div class="relative size-full _max-h-[320px] flex flex-col gap-0">
|
||||||
{(promptReady(), null)}
|
{(promptReady(), null)}
|
||||||
<PromptPopover
|
<PromptPopover
|
||||||
popover={store.popover}
|
popover={store.popover}
|
||||||
@@ -1407,138 +1284,121 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
commandKeybind={command.keybind}
|
commandKeybind={command.keybind}
|
||||||
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
|
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
|
||||||
/>
|
/>
|
||||||
<Switch>
|
<DockShellForm
|
||||||
<Match when={USE_V2_INPUT}>
|
onSubmit={handleSubmit}
|
||||||
<DockShellForm
|
classList={{
|
||||||
data-component={newSession() ? "session-new-composer" : "session-composer"}
|
"group/prompt-input": true,
|
||||||
onSubmit={handleSubmit}
|
"focus-within:shadow-xs-border": true,
|
||||||
classList={{
|
"border-icon-info-active border-dashed": store.draggingType !== null,
|
||||||
"group/prompt-input min-h-[96px] w-full rounded-xl bg-v2-background-bg-base shadow-[var(--v2-elevation-raised)]": true,
|
[props.class ?? ""]: !!props.class,
|
||||||
"border-icon-info-active border-dashed": store.draggingType !== null,
|
}}
|
||||||
[props.class ?? ""]: !!props.class,
|
>
|
||||||
}}
|
<PromptDragOverlay
|
||||||
|
type={store.draggingType}
|
||||||
|
label={language.t(store.draggingType === "@mention" ? "prompt.dropzone.file.label" : "prompt.dropzone.label")}
|
||||||
|
/>
|
||||||
|
<PromptContextItems
|
||||||
|
items={contextItems()}
|
||||||
|
active={(item) => {
|
||||||
|
const active = comments.active()
|
||||||
|
return !!item.commentID && item.commentID === active?.id && item.path === active?.file
|
||||||
|
}}
|
||||||
|
openComment={openComment}
|
||||||
|
remove={(item) => {
|
||||||
|
if (item.commentID) comments.remove(item.path, item.commentID)
|
||||||
|
prompt.context.remove(item.key)
|
||||||
|
}}
|
||||||
|
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
|
||||||
|
/>
|
||||||
|
<PromptImageAttachments
|
||||||
|
attachments={imageAttachments()}
|
||||||
|
onOpen={(attachment) =>
|
||||||
|
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
||||||
|
}
|
||||||
|
onRemove={removeAttachment}
|
||||||
|
removeLabel={language.t("prompt.attachment.remove")}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="relative"
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
const target = e.target
|
||||||
|
if (!(target instanceof HTMLElement)) return
|
||||||
|
if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
editorRef?.focus()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="relative max-h-[240px] overflow-y-auto no-scrollbar"
|
||||||
|
ref={(el) => (scrollRef = el)}
|
||||||
|
style={{ "scroll-padding-bottom": space }}
|
||||||
>
|
>
|
||||||
<PromptDragOverlay
|
<div
|
||||||
type={store.draggingType}
|
data-component="prompt-input"
|
||||||
label={language.t(
|
ref={(el) => {
|
||||||
store.draggingType === "@mention" ? "prompt.dropzone.file.label" : "prompt.dropzone.label",
|
editorRef = el
|
||||||
)}
|
props.ref?.(el)
|
||||||
/>
|
|
||||||
<PromptContextItems
|
|
||||||
items={contextItems()}
|
|
||||||
active={(item) => {
|
|
||||||
const active = comments.active()
|
|
||||||
return !!item.commentID && item.commentID === active?.id && item.path === active?.file
|
|
||||||
}}
|
}}
|
||||||
openComment={openComment}
|
role="textbox"
|
||||||
remove={(item) => {
|
aria-multiline="true"
|
||||||
if (item.commentID) comments.remove(item.path, item.commentID)
|
aria-label={placeholder()}
|
||||||
prompt.context.remove(item.key)
|
contenteditable="true"
|
||||||
|
autocapitalize={store.mode === "normal" ? "sentences" : "off"}
|
||||||
|
autocorrect={store.mode === "normal" ? "on" : "off"}
|
||||||
|
spellcheck={store.mode === "normal"}
|
||||||
|
inputMode="text"
|
||||||
|
// @ts-expect-error
|
||||||
|
autocomplete="off"
|
||||||
|
onInput={handleInput}
|
||||||
|
onPaste={handlePaste}
|
||||||
|
onCompositionStart={handleCompositionStart}
|
||||||
|
onCompositionEnd={handleCompositionEnd}
|
||||||
|
onBlur={handleBlur}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
classList={{
|
||||||
|
"select-text": true,
|
||||||
|
"w-full pl-3 pr-2 pt-2 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
||||||
|
"[&_[data-type=file]]:text-syntax-property": true,
|
||||||
|
"[&_[data-type=agent]]:text-syntax-type": true,
|
||||||
|
"font-mono!": store.mode === "shell",
|
||||||
}}
|
}}
|
||||||
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
|
style={{ "padding-bottom": space }}
|
||||||
/>
|
|
||||||
<PromptImageAttachments
|
|
||||||
attachments={imageAttachments()}
|
|
||||||
onOpen={(attachment) =>
|
|
||||||
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
|
||||||
}
|
|
||||||
onRemove={removeAttachment}
|
|
||||||
removeLabel={language.t("prompt.attachment.remove")}
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="relative min-h-[52px]"
|
class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate"
|
||||||
onMouseDown={(e) => {
|
classList={{ "font-mono!": store.mode === "shell" }}
|
||||||
const target = e.target
|
style={{ "padding-bottom": space, display: prompt.dirty() ? "none" : undefined }}
|
||||||
if (!(target instanceof HTMLElement)) return
|
|
||||||
if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) return
|
|
||||||
editorRef?.focus()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div class="relative max-h-[180px] overflow-y-auto no-scrollbar" ref={(el) => (scrollRef = el)}>
|
{placeholder()}
|
||||||
<div
|
|
||||||
data-component="prompt-input"
|
|
||||||
ref={(el) => {
|
|
||||||
editorRef = el
|
|
||||||
props.ref?.(el)
|
|
||||||
}}
|
|
||||||
role="textbox"
|
|
||||||
aria-multiline="true"
|
|
||||||
aria-label={designPlaceholder()}
|
|
||||||
contenteditable="true"
|
|
||||||
autocapitalize={store.mode === "normal" ? "sentences" : "off"}
|
|
||||||
autocorrect={store.mode === "normal" ? "on" : "off"}
|
|
||||||
spellcheck={store.mode === "normal"}
|
|
||||||
inputMode="text"
|
|
||||||
// @ts-expect-error
|
|
||||||
autocomplete="off"
|
|
||||||
onInput={handleInput}
|
|
||||||
onPaste={handlePaste}
|
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={handleCompositionEnd}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
classList={{
|
|
||||||
"select-text": true,
|
|
||||||
"min-h-[52px] w-full px-4 pt-4 pb-2 focus:outline-none whitespace-pre-wrap leading-5 text-[13px] font-[440] text-v2-text-text-faint [font-family:Inter,var(--font-family-sans)]": true,
|
|
||||||
"[&_[data-type=file]]:text-syntax-property": true,
|
|
||||||
"[&_[data-type=agent]]:text-syntax-type": true,
|
|
||||||
"font-mono!": store.mode === "shell",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
data-component={newSession() ? "session-new-design-text" : "session-composer-text"}
|
|
||||||
class="absolute top-0 inset-x-0 px-4 pt-4 pointer-events-none whitespace-nowrap truncate leading-5 text-[13px] font-[440] text-v2-text-text-faint [font-family:Inter,var(--font-family-sans)]"
|
|
||||||
classList={{ "font-mono!": store.mode === "shell", hidden: prompt.dirty() }}
|
|
||||||
>
|
|
||||||
{designPlaceholder()}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex h-11 items-center px-2">
|
</div>
|
||||||
<div class="flex min-w-0 flex-1 items-center gap-0">
|
|
||||||
{fileAttachmentInput()}
|
<div
|
||||||
<TooltipKeybind
|
aria-hidden="true"
|
||||||
placement="top"
|
class="pointer-events-none absolute inset-x-0 bottom-0"
|
||||||
title={language.t("prompt.action.attachFile")}
|
style={{
|
||||||
keybind={command.keybind("file.attach")}
|
height: space,
|
||||||
>
|
background:
|
||||||
<IconButton
|
"linear-gradient(to top, var(--surface-raised-stronger-non-alpha) calc(100% - 20px), transparent)",
|
||||||
data-action="prompt-attach"
|
}}
|
||||||
type="button"
|
/>
|
||||||
icon="plus"
|
|
||||||
variant="ghost"
|
<div class="pointer-events-none absolute bottom-2 right-2 flex items-center gap-2">
|
||||||
class="size-7 rounded-md p-[6px] text-v2-icon-icon-muted"
|
<input
|
||||||
style={buttons()}
|
ref={fileInputRef}
|
||||||
onClick={pick}
|
type="file"
|
||||||
disabled={store.mode !== "normal"}
|
multiple
|
||||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
accept={ACCEPTED_FILE_TYPES.join(",")}
|
||||||
aria-label={language.t("prompt.action.attachFile")}
|
class="hidden"
|
||||||
/>
|
onChange={(e) => {
|
||||||
</TooltipKeybind>
|
const list = e.currentTarget.files
|
||||||
<Show when={newSession()}>
|
if (list) void addAttachments(Array.from(list))
|
||||||
<div class="relative">
|
e.currentTarget.value = ""
|
||||||
<div class="pointer-events-none absolute left-2 top-1/2 z-10 flex size-4 -translate-y-1/2 items-center justify-center">
|
}}
|
||||||
<Icon name="sliders" size="small" />
|
/>
|
||||||
</div>
|
|
||||||
<Select
|
<div class="flex items-center gap-1 pointer-events-auto">
|
||||||
size="normal"
|
|
||||||
options={worktrees()}
|
|
||||||
current={currentWorktree()}
|
|
||||||
label={worktreeLabel}
|
|
||||||
onSelect={(value) => {
|
|
||||||
if (value) props.onNewSessionWorktreeChange?.(value)
|
|
||||||
restoreFocus()
|
|
||||||
}}
|
|
||||||
class="max-w-[175px] justify-start text-text-base [&_[data-component=icon]]:text-v2-icon-icon-muted"
|
|
||||||
valueClass="truncate pl-5 text-[13px] font-[440] leading-4 text-v2-text-text-faint"
|
|
||||||
triggerStyle={control()}
|
|
||||||
triggerProps={{ "data-action": "prompt-workspace" }}
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
{modelControl()}
|
|
||||||
</div>
|
|
||||||
<Tooltip placement="top" inactive={!working() && blank()} value={tip()}>
|
<Tooltip placement="top" inactive={!working() && blank()} value={tip()}>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-action="prompt-submit"
|
data-action="prompt-submit"
|
||||||
@@ -1547,348 +1407,209 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
|||||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
tabIndex={store.mode === "normal" ? undefined : -1}
|
||||||
icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
|
icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
class="size-7 rounded-md p-[6px] text-v2-icon-icon-muted shadow-[var(--v2-elevation-button-contrast)] disabled:opacity-50"
|
class="size-8"
|
||||||
style={{
|
|
||||||
"background-image":
|
|
||||||
"linear-gradient(180deg,var(--v2-alpha-light-20) 0%,var(--v2-alpha-light-0) 100%),linear-gradient(90deg,var(--v2-background-bg-contrast) 0%,var(--v2-background-bg-contrast) 100%)",
|
|
||||||
}}
|
|
||||||
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
||||||
/>
|
/>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</DockShellForm>
|
</div>
|
||||||
</Match>
|
|
||||||
<Match when>
|
<div class="pointer-events-none absolute bottom-2 left-2">
|
||||||
<DockShellForm
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
classList={{
|
|
||||||
"group/prompt-input": true,
|
|
||||||
"focus-within:shadow-xs-border": true,
|
|
||||||
"border-icon-info-active border-dashed": store.draggingType !== null,
|
|
||||||
[props.class ?? ""]: !!props.class,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<PromptDragOverlay
|
|
||||||
type={store.draggingType}
|
|
||||||
label={language.t(
|
|
||||||
store.draggingType === "@mention" ? "prompt.dropzone.file.label" : "prompt.dropzone.label",
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<PromptContextItems
|
|
||||||
items={contextItems()}
|
|
||||||
active={(item) => {
|
|
||||||
const active = comments.active()
|
|
||||||
return !!item.commentID && item.commentID === active?.id && item.path === active?.file
|
|
||||||
}}
|
|
||||||
openComment={openComment}
|
|
||||||
remove={(item) => {
|
|
||||||
if (item.commentID) comments.remove(item.path, item.commentID)
|
|
||||||
prompt.context.remove(item.key)
|
|
||||||
}}
|
|
||||||
t={(key) => language.t(key as Parameters<typeof language.t>[0])}
|
|
||||||
/>
|
|
||||||
<PromptImageAttachments
|
|
||||||
attachments={imageAttachments()}
|
|
||||||
onOpen={(attachment) =>
|
|
||||||
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
|
||||||
}
|
|
||||||
onRemove={removeAttachment}
|
|
||||||
removeLabel={language.t("prompt.attachment.remove")}
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
class="relative"
|
aria-hidden={store.mode !== "normal"}
|
||||||
onMouseDown={(e) => {
|
class="pointer-events-auto"
|
||||||
const target = e.target
|
style={{
|
||||||
if (!(target instanceof HTMLElement)) return
|
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
||||||
if (target.closest('[data-action="prompt-attach"], [data-action="prompt-submit"]')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
editorRef?.focus()
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<TooltipKeybind
|
||||||
class="relative max-h-[240px] overflow-y-auto no-scrollbar"
|
placement="top"
|
||||||
ref={(el) => (scrollRef = el)}
|
title={language.t("prompt.action.attachFile")}
|
||||||
style={{ "scroll-padding-bottom": space }}
|
keybind={command.keybind("file.attach")}
|
||||||
>
|
>
|
||||||
<div
|
<Button
|
||||||
data-component="prompt-input"
|
data-action="prompt-attach"
|
||||||
ref={(el) => {
|
type="button"
|
||||||
editorRef = el
|
variant="ghost"
|
||||||
props.ref?.(el)
|
class="size-8 p-0"
|
||||||
}}
|
style={buttons()}
|
||||||
role="textbox"
|
onClick={pick}
|
||||||
aria-multiline="true"
|
disabled={store.mode !== "normal"}
|
||||||
aria-label={placeholder()}
|
tabIndex={store.mode === "normal" ? undefined : -1}
|
||||||
contenteditable="true"
|
aria-label={language.t("prompt.action.attachFile")}
|
||||||
autocapitalize={store.mode === "normal" ? "sentences" : "off"}
|
|
||||||
autocorrect={store.mode === "normal" ? "on" : "off"}
|
|
||||||
spellcheck={store.mode === "normal"}
|
|
||||||
inputMode="text"
|
|
||||||
// @ts-expect-error
|
|
||||||
autocomplete="off"
|
|
||||||
onInput={handleInput}
|
|
||||||
onPaste={handlePaste}
|
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={handleCompositionEnd}
|
|
||||||
onBlur={handleBlur}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
classList={{
|
|
||||||
"select-text": true,
|
|
||||||
"w-full pl-3 pr-2 pt-2 text-14-regular text-text-strong focus:outline-none whitespace-pre-wrap": true,
|
|
||||||
"[&_[data-type=file]]:text-syntax-property": true,
|
|
||||||
"[&_[data-type=agent]]:text-syntax-type": true,
|
|
||||||
"font-mono!": store.mode === "shell",
|
|
||||||
}}
|
|
||||||
style={{ "padding-bottom": space }}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate"
|
|
||||||
classList={{ "font-mono!": store.mode === "shell" }}
|
|
||||||
style={{ "padding-bottom": space, display: prompt.dirty() ? "none" : undefined }}
|
|
||||||
>
|
>
|
||||||
{placeholder()}
|
<Icon name="plus" class="size-4.5" />
|
||||||
</div>
|
</Button>
|
||||||
</div>
|
</TooltipKeybind>
|
||||||
|
|
||||||
<div
|
|
||||||
aria-hidden="true"
|
|
||||||
class="pointer-events-none absolute inset-x-0 bottom-0"
|
|
||||||
style={{
|
|
||||||
height: space,
|
|
||||||
background:
|
|
||||||
"linear-gradient(to top, var(--surface-raised-stronger-non-alpha) calc(100% - 20px), transparent)",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="pointer-events-none absolute bottom-2 right-2 flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
ref={fileInputRef}
|
|
||||||
type="file"
|
|
||||||
multiple
|
|
||||||
accept={ACCEPTED_FILE_TYPES.join(",")}
|
|
||||||
class="hidden"
|
|
||||||
onChange={(e) => {
|
|
||||||
const list = e.currentTarget.files
|
|
||||||
if (list) void addAttachments(Array.from(list))
|
|
||||||
e.currentTarget.value = ""
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div class="flex items-center gap-1 pointer-events-auto">
|
|
||||||
<Tooltip placement="top" inactive={!working() && blank()} value={tip()}>
|
|
||||||
<IconButton
|
|
||||||
data-action="prompt-submit"
|
|
||||||
type="submit"
|
|
||||||
disabled={!working() && blank()}
|
|
||||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
|
||||||
icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
|
|
||||||
variant="primary"
|
|
||||||
class="size-8"
|
|
||||||
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
|
|
||||||
/>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="pointer-events-none absolute bottom-2 left-2">
|
|
||||||
<div
|
|
||||||
aria-hidden={store.mode !== "normal"}
|
|
||||||
class="pointer-events-auto"
|
|
||||||
style={{
|
|
||||||
"pointer-events": buttonsSpring() > 0.5 ? "auto" : "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
title={language.t("prompt.action.attachFile")}
|
|
||||||
keybind={command.keybind("file.attach")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
data-action="prompt-attach"
|
|
||||||
type="button"
|
|
||||||
variant="ghost"
|
|
||||||
class="size-8 p-0"
|
|
||||||
style={buttons()}
|
|
||||||
onClick={pick}
|
|
||||||
disabled={store.mode !== "normal"}
|
|
||||||
tabIndex={store.mode === "normal" ? undefined : -1}
|
|
||||||
aria-label={language.t("prompt.action.attachFile")}
|
|
||||||
>
|
|
||||||
<Icon name="plus" class="size-4.5" />
|
|
||||||
</Button>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DockShellForm>
|
</div>
|
||||||
<Show when={store.mode === "normal" || store.mode === "shell"}>
|
</div>
|
||||||
<DockTray attach="top">
|
</DockShellForm>
|
||||||
<div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0">
|
<Show when={store.mode === "normal" || store.mode === "shell"}>
|
||||||
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
|
<DockTray attach="top">
|
||||||
|
<div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0">
|
||||||
|
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
|
||||||
|
<div
|
||||||
|
class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
|
||||||
|
style={{
|
||||||
|
padding: "0 0px 0 8px",
|
||||||
|
...shell(),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Icon name="console" />
|
||||||
|
<span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
|
||||||
|
<div class="flex-1" />
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
class="text-text-base"
|
||||||
|
onClick={() => {
|
||||||
|
setStore("mode", "normal")
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{language.t("common.cancel")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
|
||||||
|
<Show when={!agentsLoading()}>
|
||||||
<div
|
<div
|
||||||
class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
|
data-component="prompt-agent-control"
|
||||||
style={{
|
style={agentsShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
||||||
padding: "0 0px 0 8px",
|
|
||||||
...shell(),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Icon name="console" />
|
<TooltipKeybind
|
||||||
<span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
|
placement="top"
|
||||||
<div class="flex-1" />
|
gutter={4}
|
||||||
<Button
|
title={language.t("command.agent.cycle")}
|
||||||
variant="ghost"
|
keybind={command.keybind("agent.cycle")}
|
||||||
class="text-text-base"
|
|
||||||
onClick={() => {
|
|
||||||
setStore("mode", "normal")
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{language.t("common.cancel")}
|
<Select
|
||||||
</Button>
|
size="normal"
|
||||||
|
options={agentNames()}
|
||||||
|
current={local.agent.current()?.name ?? ""}
|
||||||
|
onSelect={(value) => {
|
||||||
|
local.agent.set(value)
|
||||||
|
restoreFocus()
|
||||||
|
}}
|
||||||
|
class="capitalize max-w-[160px] text-text-base"
|
||||||
|
valueClass="truncate text-13-regular text-text-base"
|
||||||
|
triggerStyle={control()}
|
||||||
|
triggerProps={{ "data-action": "prompt-agent" }}
|
||||||
|
variant="ghost"
|
||||||
|
/>
|
||||||
|
</TooltipKeybind>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
|
</Show>
|
||||||
<Show when={!agentsLoading()}>
|
<Show when={!providersLoading()}>
|
||||||
<div
|
<Show when={store.mode !== "shell"}>
|
||||||
data-component="prompt-agent-control"
|
<div
|
||||||
style={agentsShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
data-component="prompt-model-control"
|
||||||
|
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
||||||
|
>
|
||||||
|
<Show
|
||||||
|
when={providers.paid().length > 0}
|
||||||
|
fallback={
|
||||||
|
<TooltipKeybind
|
||||||
|
placement="top"
|
||||||
|
gutter={4}
|
||||||
|
title={language.t("command.model.choose")}
|
||||||
|
keybind={command.keybind("model.choose")}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
data-action="prompt-model"
|
||||||
|
as="div"
|
||||||
|
variant="ghost"
|
||||||
|
size="normal"
|
||||||
|
class="min-w-0 max-w-[320px] text-13-regular text-text-base group"
|
||||||
|
style={control()}
|
||||||
|
onClick={() => {
|
||||||
|
void import("@/components/dialog-select-model-unpaid").then((x) => {
|
||||||
|
dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />)
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon
|
||||||
|
id={local.model.current()?.provider?.id ?? ""}
|
||||||
|
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
||||||
|
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<span class="truncate">
|
||||||
|
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
||||||
|
</span>
|
||||||
|
<Icon name="chevron-down" size="small" class="shrink-0" />
|
||||||
|
</Button>
|
||||||
|
</TooltipKeybind>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<TooltipKeybind
|
<TooltipKeybind
|
||||||
placement="top"
|
placement="top"
|
||||||
gutter={4}
|
gutter={4}
|
||||||
title={language.t("command.agent.cycle")}
|
title={language.t("command.model.choose")}
|
||||||
keybind={command.keybind("agent.cycle")}
|
keybind={command.keybind("model.choose")}
|
||||||
|
>
|
||||||
|
<ModelSelectorPopover
|
||||||
|
model={local.model}
|
||||||
|
triggerAs={Button}
|
||||||
|
triggerProps={{
|
||||||
|
variant: "ghost",
|
||||||
|
size: "normal",
|
||||||
|
style: control(),
|
||||||
|
class: "min-w-0 max-w-[320px] text-13-regular text-text-base group",
|
||||||
|
"data-action": "prompt-model",
|
||||||
|
}}
|
||||||
|
onClose={restoreFocus}
|
||||||
|
>
|
||||||
|
<Show when={local.model.current()?.provider?.id}>
|
||||||
|
<ProviderIcon
|
||||||
|
id={local.model.current()?.provider?.id ?? ""}
|
||||||
|
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
||||||
|
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
<span class="truncate">
|
||||||
|
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
||||||
|
</span>
|
||||||
|
<Icon name="chevron-down" size="small" class="shrink-0" />
|
||||||
|
</ModelSelectorPopover>
|
||||||
|
</TooltipKeybind>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
<Show when={variants().length > 2}>
|
||||||
|
<div
|
||||||
|
data-component="prompt-variant-control"
|
||||||
|
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
||||||
|
>
|
||||||
|
<TooltipKeybind
|
||||||
|
placement="top"
|
||||||
|
gutter={4}
|
||||||
|
title={language.t("command.model.variant.cycle")}
|
||||||
|
keybind={command.keybind("model.variant.cycle")}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
size="normal"
|
size="normal"
|
||||||
options={agentNames()}
|
options={variants()}
|
||||||
current={local.agent.current()?.name ?? ""}
|
current={local.model.variant.current() ?? "default"}
|
||||||
|
label={(x) => (x === "default" ? language.t("common.default") : x)}
|
||||||
onSelect={(value) => {
|
onSelect={(value) => {
|
||||||
local.agent.set(value)
|
local.model.variant.set(value === "default" ? undefined : value)
|
||||||
restoreFocus()
|
restoreFocus()
|
||||||
}}
|
}}
|
||||||
class="capitalize max-w-[160px] text-text-base"
|
class="capitalize max-w-[160px] text-text-base"
|
||||||
valueClass="truncate text-13-regular text-text-base"
|
valueClass="truncate text-13-regular text-text-base"
|
||||||
triggerStyle={control()}
|
triggerStyle={control()}
|
||||||
triggerProps={{ "data-action": "prompt-agent" }}
|
triggerProps={{ "data-action": "prompt-model-variant" }}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
/>
|
/>
|
||||||
</TooltipKeybind>
|
</TooltipKeybind>
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!providersLoading()}>
|
</Show>
|
||||||
<Show when={store.mode !== "shell"}>
|
</Show>
|
||||||
<div
|
|
||||||
data-component="prompt-model-control"
|
|
||||||
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
|
||||||
>
|
|
||||||
<Show
|
|
||||||
when={providers.paid().length > 0}
|
|
||||||
fallback={
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={4}
|
|
||||||
title={language.t("command.model.choose")}
|
|
||||||
keybind={command.keybind("model.choose")}
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
data-action="prompt-model"
|
|
||||||
as="div"
|
|
||||||
variant="ghost"
|
|
||||||
size="normal"
|
|
||||||
class="min-w-0 max-w-[320px] text-13-regular text-text-base group"
|
|
||||||
style={control()}
|
|
||||||
onClick={() => {
|
|
||||||
void import("@/components/dialog-select-model-unpaid").then((x) => {
|
|
||||||
dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />)
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
|
||||||
<ProviderIcon
|
|
||||||
id={local.model.current()?.provider?.id ?? ""}
|
|
||||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
|
||||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<span class="truncate">
|
|
||||||
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
|
||||||
</span>
|
|
||||||
<Icon name="chevron-down" size="small" class="shrink-0" />
|
|
||||||
</Button>
|
|
||||||
</TooltipKeybind>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={4}
|
|
||||||
title={language.t("command.model.choose")}
|
|
||||||
keybind={command.keybind("model.choose")}
|
|
||||||
>
|
|
||||||
<ModelSelectorPopover
|
|
||||||
model={local.model}
|
|
||||||
triggerAs={Button}
|
|
||||||
triggerProps={{
|
|
||||||
variant: "ghost",
|
|
||||||
size: "normal",
|
|
||||||
style: control(),
|
|
||||||
class: "min-w-0 max-w-[320px] text-13-regular text-text-base group",
|
|
||||||
"data-action": "prompt-model",
|
|
||||||
}}
|
|
||||||
onClose={restoreFocus}
|
|
||||||
>
|
|
||||||
<Show when={local.model.current()?.provider?.id}>
|
|
||||||
<ProviderIcon
|
|
||||||
id={local.model.current()?.provider?.id ?? ""}
|
|
||||||
class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150"
|
|
||||||
style={{ "will-change": "opacity", transform: "translateZ(0)" }}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
<span class="truncate">
|
|
||||||
{local.model.current()?.name ?? language.t("dialog.model.select.title")}
|
|
||||||
</span>
|
|
||||||
<Icon name="chevron-down" size="small" class="shrink-0" />
|
|
||||||
</ModelSelectorPopover>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<Show when={variants().length > 2}>
|
|
||||||
<div
|
|
||||||
data-component="prompt-variant-control"
|
|
||||||
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
|
|
||||||
>
|
|
||||||
<TooltipKeybind
|
|
||||||
placement="top"
|
|
||||||
gutter={4}
|
|
||||||
title={language.t("command.model.variant.cycle")}
|
|
||||||
keybind={command.keybind("model.variant.cycle")}
|
|
||||||
>
|
|
||||||
<Select
|
|
||||||
size="normal"
|
|
||||||
options={variants()}
|
|
||||||
current={local.model.variant.current() ?? "default"}
|
|
||||||
label={(x) => (x === "default" ? language.t("common.default") : x)}
|
|
||||||
onSelect={(value) => {
|
|
||||||
local.model.variant.set(value === "default" ? undefined : value)
|
|
||||||
restoreFocus()
|
|
||||||
}}
|
|
||||||
class="capitalize max-w-[160px] text-text-base"
|
|
||||||
valueClass="truncate text-13-regular text-text-base"
|
|
||||||
triggerStyle={control()}
|
|
||||||
triggerProps={{ "data-action": "prompt-model-variant" }}
|
|
||||||
variant="ghost"
|
|
||||||
/>
|
|
||||||
</TooltipKeybind>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DockTray>
|
</div>
|
||||||
</Show>
|
</div>
|
||||||
</Match>
|
</DockTray>
|
||||||
</Switch>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export function SessionContextUsage(props: SessionContextUsageProps) {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const metrics = createMemo(() => getSessionContextMetrics(messages(), [...providers.all().values()]))
|
const metrics = createMemo(() => getSessionContextMetrics(messages(), providers.all()))
|
||||||
const context = createMemo(() => metrics().context)
|
const context = createMemo(() => metrics().context)
|
||||||
const cost = createMemo(() => {
|
const cost = createMemo(() => {
|
||||||
return usd().format(metrics().totalCost)
|
return usd().format(metrics().totalCost)
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ export { SessionContextTab } from "./session-context-tab"
|
|||||||
export { SortableTab, FileVisual } from "./session-sortable-tab"
|
export { SortableTab, FileVisual } from "./session-sortable-tab"
|
||||||
export { SortableTerminalTab } from "./session-sortable-terminal-tab"
|
export { SortableTerminalTab } from "./session-sortable-terminal-tab"
|
||||||
export { NewSessionView } from "./session-new-view"
|
export { NewSessionView } from "./session-new-view"
|
||||||
export { NewSessionDesignView } from "./session-new-design-view"
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export function SessionContextTab() {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
const metrics = createMemo(() => getSessionContextMetrics(messages(), [...providers.all().values()]))
|
const metrics = createMemo(() => getSessionContextMetrics(messages(), providers.all()))
|
||||||
const ctx = createMemo(() => metrics().context)
|
const ctx = createMemo(() => metrics().context)
|
||||||
const formatter = createMemo(() => createSessionContextFormatter(language.intl()))
|
const formatter = createMemo(() => createSessionContextFormatter(language.intl()))
|
||||||
|
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import type { JSX } from "solid-js"
|
|
||||||
import { createMemo } from "solid-js"
|
|
||||||
import { useNavigate } from "@solidjs/router"
|
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
|
||||||
import { useLayout } from "@/context/layout"
|
|
||||||
import { useSDK } from "@/context/sdk"
|
|
||||||
import { useServer } from "@/context/server"
|
|
||||||
import { useSync } from "@/context/sync"
|
|
||||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
|
||||||
import { getFilename } from "@opencode-ai/core/util/path"
|
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
|
||||||
import { Select } from "@opencode-ai/ui/select"
|
|
||||||
import { WordmarkV2 } from "@opencode-ai/ui/v2/components/wordmark-v2.jsx"
|
|
||||||
|
|
||||||
const MAIN_WORKTREE = "main"
|
|
||||||
|
|
||||||
export function NewSessionDesignView(props: { worktree: string; children: JSX.Element }) {
|
|
||||||
const globalSync = useGlobalSync()
|
|
||||||
const layout = useLayout()
|
|
||||||
const navigate = useNavigate()
|
|
||||||
const sdk = useSDK()
|
|
||||||
const server = useServer()
|
|
||||||
const sync = useSync()
|
|
||||||
|
|
||||||
const projectRoot = createMemo(() => sync.project?.worktree ?? sdk.directory)
|
|
||||||
const projects = createMemo(() => {
|
|
||||||
const roots = globalSync.data.project.map((project) => project.worktree)
|
|
||||||
if (roots.includes(projectRoot())) return roots
|
|
||||||
return [projectRoot(), ...roots]
|
|
||||||
})
|
|
||||||
const branch = createMemo(() => sync.data.vcs?.branch ?? MAIN_WORKTREE)
|
|
||||||
|
|
||||||
const openProject = (directory: string | undefined) => {
|
|
||||||
if (!directory) return
|
|
||||||
if (directory === projectRoot()) return
|
|
||||||
layout.projects.open(directory)
|
|
||||||
server.projects.touch(directory)
|
|
||||||
navigate(`/${base64Encode(directory)}/session`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div data-component="session-new-design" class="relative size-full overflow-hidden bg-v2-background-bg-deep">
|
|
||||||
<div class="absolute inset-x-0 top-[25.375%] flex justify-center px-6">
|
|
||||||
<div class="w-full max-w-[720px]">
|
|
||||||
<WordmarkV2 class="h-auto w-full text-v2-icon-icon-base" />
|
|
||||||
<div class="mt-8">
|
|
||||||
{props.children}
|
|
||||||
<div class="mt-3 flex h-7 items-center gap-0 pl-2">
|
|
||||||
<Select
|
|
||||||
size="normal"
|
|
||||||
variant="ghost"
|
|
||||||
options={projects()}
|
|
||||||
current={projectRoot()}
|
|
||||||
label={getFilename}
|
|
||||||
onSelect={openProject}
|
|
||||||
class="max-w-[203px] justify-start text-text-base [&_[data-component=icon]]:text-v2-icon-icon-muted"
|
|
||||||
valueClass="truncate text-[length:13px] font-[440] text-v2-text-text-faint"
|
|
||||||
/>
|
|
||||||
<div class="relative">
|
|
||||||
<div class="pointer-events-none absolute left-2 top-1/2 z-10 flex size-4 -translate-y-1/2 items-center justify-center">
|
|
||||||
<Icon name="branch" size="small" />
|
|
||||||
</div>
|
|
||||||
<Select
|
|
||||||
size="normal"
|
|
||||||
variant="ghost"
|
|
||||||
options={[branch()]}
|
|
||||||
current={branch()}
|
|
||||||
class="max-w-[240px] justify-start text-text-base [&_[data-component=icon]]:text-v2-icon-icon-muted"
|
|
||||||
valueClass="truncate pl-5 font-[440] text-v2-text-text-faint"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -193,12 +193,6 @@ export const SettingsGeneral: Component = () => {
|
|||||||
{ initialValue: null as DisplayBackend | null },
|
{ initialValue: null as DisplayBackend | null },
|
||||||
)
|
)
|
||||||
|
|
||||||
const [pinchZoom, { mutate: setPinchZoom }] = createResource(
|
|
||||||
() => (desktop() && platform.getPinchZoomEnabled ? true : false),
|
|
||||||
() => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false),
|
|
||||||
{ initialValue: false },
|
|
||||||
)
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
void theme.loadThemes()
|
void theme.loadThemes()
|
||||||
})
|
})
|
||||||
@@ -245,13 +239,6 @@ export const SettingsGeneral: Component = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onPinchZoomChange = (checked: boolean) => {
|
|
||||||
setPinchZoom(checked)
|
|
||||||
const update = platform.setPinchZoomEnabled?.(checked)
|
|
||||||
if (!update) return
|
|
||||||
void update.catch(() => setPinchZoom(!checked))
|
|
||||||
}
|
|
||||||
|
|
||||||
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
|
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
|
||||||
{ value: "system", label: language.t("theme.scheme.system") },
|
{ value: "system", label: language.t("theme.scheme.system") },
|
||||||
{ value: "light", label: language.t("theme.scheme.light") },
|
{ value: "light", label: language.t("theme.scheme.light") },
|
||||||
@@ -742,45 +729,6 @@ export const SettingsGeneral: Component = () => {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const DisplaySection = () => (
|
|
||||||
<Show when={desktop()}>
|
|
||||||
<div class="flex flex-col gap-1">
|
|
||||||
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
|
|
||||||
|
|
||||||
<SettingsList>
|
|
||||||
<SettingsRow
|
|
||||||
title={language.t("settings.general.row.pinchZoom.title")}
|
|
||||||
description={language.t("settings.general.row.pinchZoom.description")}
|
|
||||||
>
|
|
||||||
<div data-action="settings-pinch-zoom">
|
|
||||||
<Switch checked={pinchZoom.latest} onChange={onPinchZoomChange} />
|
|
||||||
</div>
|
|
||||||
</SettingsRow>
|
|
||||||
|
|
||||||
<Show when={linux()}>
|
|
||||||
<SettingsRow
|
|
||||||
title={
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<span>{language.t("settings.general.row.wayland.title")}</span>
|
|
||||||
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
|
|
||||||
<span class="text-text-weak">
|
|
||||||
<Icon name="help" size="small" />
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
description={language.t("settings.general.row.wayland.description")}
|
|
||||||
>
|
|
||||||
<div data-action="settings-wayland">
|
|
||||||
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
|
|
||||||
</div>
|
|
||||||
</SettingsRow>
|
|
||||||
</Show>
|
|
||||||
</SettingsList>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
|
|
||||||
console.log(import.meta.env)
|
console.log(import.meta.env)
|
||||||
return (
|
return (
|
||||||
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
|
||||||
@@ -801,7 +749,31 @@ export const SettingsGeneral: Component = () => {
|
|||||||
|
|
||||||
<UpdatesSection />
|
<UpdatesSection />
|
||||||
|
|
||||||
<DisplaySection />
|
<Show when={linux()}>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
|
||||||
|
|
||||||
|
<SettingsList>
|
||||||
|
<SettingsRow
|
||||||
|
title={
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span>{language.t("settings.general.row.wayland.title")}</span>
|
||||||
|
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
|
||||||
|
<span class="text-text-weak">
|
||||||
|
<Icon name="help" size="small" />
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
description={language.t("settings.general.row.wayland.description")}
|
||||||
|
>
|
||||||
|
<div data-action="settings-wayland">
|
||||||
|
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
|
||||||
|
</div>
|
||||||
|
</SettingsRow>
|
||||||
|
</SettingsList>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
|
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
|
||||||
<AdvancedSection />
|
<AdvancedSection />
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import { Button } from "@opencode-ai/ui/button"
|
|||||||
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
|
||||||
import { useTheme } from "@opencode-ai/ui/theme/context"
|
import { useTheme } from "@opencode-ai/ui/theme/context"
|
||||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
|
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
|
||||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
|
|
||||||
|
|
||||||
import { getAvatarColors, useLayout, type LocalProject } from "@/context/layout"
|
import { useLayout } from "@/context/layout"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
@@ -19,12 +18,6 @@ import { applyPath, backPath, forwardPath } from "./titlebar-history"
|
|||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { decodeDirectory } from "@/pages/directory-layout"
|
import { decodeDirectory } from "@/pages/directory-layout"
|
||||||
import { iife } from "@opencode-ai/core/util/iife"
|
import { iife } from "@opencode-ai/core/util/iife"
|
||||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
|
||||||
import { Avatar as AvatarV2 } from "@opencode-ai/ui/v2/components/avatar-v2.jsx"
|
|
||||||
import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers"
|
|
||||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
|
||||||
import { StatusPopover } from "./status-popover"
|
|
||||||
import { SDKProvider } from "@/context/sdk"
|
|
||||||
|
|
||||||
type TauriDesktopWindow = {
|
type TauriDesktopWindow = {
|
||||||
startDragging?: () => Promise<void>
|
startDragging?: () => Promise<void>
|
||||||
@@ -47,21 +40,13 @@ type TauriApi = {
|
|||||||
const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
|
const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
|
||||||
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
|
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
|
||||||
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
|
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
|
||||||
const legacyTitlebarHeight = 40
|
const titlebarHeight = 40
|
||||||
const v2TitlebarHeight = 44
|
|
||||||
const minTitlebarZoom = 0.25
|
const minTitlebarZoom = 0.25
|
||||||
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
|
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
|
||||||
const USE_V2_TITLEBAR = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
|
|
||||||
|
|
||||||
const makeSessionHref = (b64Dir: string, sessionId: string) => `/${b64Dir}/session/${sessionId}`
|
const makeSessionHref = (b64Dir: string, sessionId: string) => `/${b64Dir}/session/${sessionId}`
|
||||||
|
|
||||||
export type TitlebarUpdate = {
|
export function Titlebar() {
|
||||||
version: () => string | undefined
|
|
||||||
installing: () => boolean
|
|
||||||
install: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
@@ -74,16 +59,14 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
|
|
||||||
const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos")
|
const mac = createMemo(() => platform.platform === "desktop" && platform.os === "macos")
|
||||||
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
|
const windows = createMemo(() => platform.platform === "desktop" && platform.os === "windows")
|
||||||
const electronWindows = createMemo(() => windows() && !tauriApi())
|
|
||||||
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
|
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
|
||||||
const web = createMemo(() => platform.platform === "web")
|
const web = createMemo(() => platform.platform === "web")
|
||||||
const zoom = () => platform.webviewZoom?.() ?? 1
|
const zoom = () => platform.webviewZoom?.() ?? 1
|
||||||
const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom())
|
const titlebarZoom = () => (windows() ? Math.max(zoom(), minTitlebarZoom) : zoom())
|
||||||
const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1)
|
const counterZoom = () => (windows() && titlebarZoom() < 1 ? 1 / titlebarZoom() : 1)
|
||||||
const minHeight = () => {
|
const minHeight = () => {
|
||||||
const height = USE_V2_TITLEBAR ? v2TitlebarHeight : legacyTitlebarHeight
|
if (mac()) return `${titlebarHeight / zoom()}px`
|
||||||
if (mac()) return `${height / zoom()}px`
|
if (windows()) return `${titlebarHeight / Math.min(titlebarZoom(), 1)}px`
|
||||||
if (windows()) return `${height / Math.min(titlebarZoom(), 1)}px`
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
const windowsControlsWidth = () => `${windowsControlsBaseWidth / Math.max(titlebarZoom(), 1)}px`
|
const windowsControlsWidth = () => `${windowsControlsBaseWidth / Math.max(titlebarZoom(), 1)}px`
|
||||||
@@ -200,41 +183,19 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<header
|
<header
|
||||||
classList={{
|
class="h-10 shrink-0 bg-background-base relative overflow-hidden flex flex-row"
|
||||||
"shrink-0 relative overflow-hidden flex flex-row": true,
|
style={{ "min-height": minHeight(), "padding-left": mac() ? `${84 / zoom()}px` : 0 }}
|
||||||
"h-11 bg-v2-background-bg-deep": USE_V2_TITLEBAR,
|
|
||||||
"h-10 bg-background-base": !USE_V2_TITLEBAR,
|
|
||||||
}}
|
|
||||||
style={{
|
|
||||||
"min-height": minHeight(),
|
|
||||||
"padding-left": mac() ? `${84 / zoom()}px` : 0,
|
|
||||||
width: electronWindows() ? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))` : undefined,
|
|
||||||
"max-width": electronWindows()
|
|
||||||
? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))`
|
|
||||||
: undefined,
|
|
||||||
"align-self": electronWindows() ? "flex-start" : undefined,
|
|
||||||
}}
|
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
onMouseDown={drag}
|
onMouseDown={drag}
|
||||||
onDblClick={maximize}
|
onDblClick={maximize}
|
||||||
>
|
>
|
||||||
<Switch>
|
<Switch>
|
||||||
<Match when={USE_V2_TITLEBAR}>
|
<Match when={import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"}>
|
||||||
{(_) => {
|
{(_) => {
|
||||||
const globalSync = useGlobalSync()
|
const globalSync = useGlobalSync()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const homeMatch = useMatch(() => "/")
|
|
||||||
|
|
||||||
const newSessionHref = () => {
|
type Tab = { dir: string; sessionId: string; params: any; href: string }
|
||||||
if (params.dir) return `/${params.dir}/session`
|
|
||||||
|
|
||||||
const project = layout.projects.list()[0]
|
|
||||||
if (!project) return "/"
|
|
||||||
|
|
||||||
return `/${base64Encode(project.worktree)}/session`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tab = { dir: string; sessionId: string; href: string }
|
|
||||||
|
|
||||||
const [tabsStore, tabsStoreActions] = iife(() => {
|
const [tabsStore, tabsStoreActions] = iife(() => {
|
||||||
const [store, setStore] = createStore<Tab[]>(
|
const [store, setStore] = createStore<Tab[]>(
|
||||||
@@ -244,6 +205,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
{
|
{
|
||||||
dir: decodeDirectory(params.dir) ?? "",
|
dir: decodeDirectory(params.dir) ?? "",
|
||||||
sessionId: params.id,
|
sessionId: params.id,
|
||||||
|
params: { id: params.id, dir: params.dir },
|
||||||
href: makeSessionHref(params.dir, params.id),
|
href: makeSessionHref(params.dir, params.id),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -286,107 +248,11 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
tabsStoreActions.addTab({
|
tabsStoreActions.addTab({
|
||||||
dir: decodeDirectory(params.dir) ?? "",
|
dir: decodeDirectory(params.dir) ?? "",
|
||||||
sessionId: params.id,
|
sessionId: params.id,
|
||||||
|
params: { id: params.id, dir: params.dir },
|
||||||
href: makeSessionHref(params.dir, params.id),
|
href: makeSessionHref(params.dir, params.id),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const projects = createMemo(() => layout.projects.list())
|
|
||||||
const projectByID = createMemo(
|
|
||||||
() => new Map(projects().flatMap((project) => (project.id ? [[project.id, project] as const] : []))),
|
|
||||||
)
|
|
||||||
|
|
||||||
const currentSessionTab = () => {
|
|
||||||
if (!params.dir || !params.id) return
|
|
||||||
const href = makeSessionHref(params.dir, params.id)
|
|
||||||
return tabsStore.find((tab) => tab.href === href)
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeCurrentSessionTab = () => {
|
|
||||||
const tab = currentSessionTab()
|
|
||||||
if (!tab) return false
|
|
||||||
tabsStoreActions.removeTab(tab.href)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeNewSessionTab = () => {
|
|
||||||
if (!(params.dir && !params.id)) return false
|
|
||||||
const last = tabsStore[tabsStore.length - 1]
|
|
||||||
if (last) navigate(last.href)
|
|
||||||
else navigate("/")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
makeEventListener(
|
|
||||||
document,
|
|
||||||
"keydown",
|
|
||||||
(event) => {
|
|
||||||
if (!event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return
|
|
||||||
if (event.key.toLowerCase() !== "w") return
|
|
||||||
if (!(closeCurrentSessionTab() || closeNewSessionTab())) return
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
},
|
|
||||||
{ capture: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
command.register(() => {
|
|
||||||
const commands = [
|
|
||||||
{
|
|
||||||
id: `tab.prev`,
|
|
||||||
category: "tab",
|
|
||||||
title: "",
|
|
||||||
keybind: `mod+option+ArrowLeft`,
|
|
||||||
hidden: true,
|
|
||||||
onSelect: () => {
|
|
||||||
let index = tabsStore.findIndex((tab) => tab.href === currentSessionTab()?.href)
|
|
||||||
if (index === -1) return
|
|
||||||
|
|
||||||
index -= 1
|
|
||||||
if (index === -1) index = tabsStore.length - 1
|
|
||||||
|
|
||||||
const next = tabsStore[index]
|
|
||||||
if (next) navigate(next.href)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `tab.next`,
|
|
||||||
category: "tab",
|
|
||||||
title: "",
|
|
||||||
keybind: `mod+option+ArrowRight`,
|
|
||||||
hidden: true,
|
|
||||||
onSelect: () => {
|
|
||||||
let index = tabsStore.findIndex((tab) => tab.href === currentSessionTab()?.href)
|
|
||||||
if (index === -1) return
|
|
||||||
|
|
||||||
index += 1
|
|
||||||
if (index === tabsStore.length) index = 0
|
|
||||||
|
|
||||||
const next = tabsStore[index]
|
|
||||||
if (next) navigate(next.href)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
...Array.from({ length: 9 }, (_, i) => {
|
|
||||||
const index = i
|
|
||||||
const number = index + 1
|
|
||||||
return {
|
|
||||||
id: `tab.${number}`,
|
|
||||||
category: "tab",
|
|
||||||
title: "",
|
|
||||||
keybind: `mod+${number}`,
|
|
||||||
disabled: layout.projects.list().length <= index,
|
|
||||||
hidden: true,
|
|
||||||
onSelect: () => {
|
|
||||||
const tab = tabsStore[index]
|
|
||||||
if (tab) navigate(tab.href)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
return commands
|
|
||||||
})
|
|
||||||
|
|
||||||
const tabsEnriched = iife(() => {
|
const tabsEnriched = iife(() => {
|
||||||
const base = mapArray(
|
const base = mapArray(
|
||||||
() => tabsStore,
|
() => tabsStore,
|
||||||
@@ -401,83 +267,78 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3">
|
||||||
class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3 py-2"
|
|
||||||
classList={{
|
|
||||||
"pl-2": mac(),
|
|
||||||
"pl-4": !mac(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ChannelIndicator />
|
<ChannelIndicator />
|
||||||
<Show when={windows() || linux()}>
|
<Show when={windows() || linux()}>
|
||||||
<WindowsAppMenu command={command} platform={platform} variant="v2" />
|
<WindowsAppMenu command={command} platform={platform} />
|
||||||
</Show>
|
</Show>
|
||||||
<IconButtonV2
|
<IconButtonV2
|
||||||
variant="ghost-muted"
|
|
||||||
size="large"
|
|
||||||
as="a"
|
as="a"
|
||||||
href="/"
|
href="/"
|
||||||
class="!w-9"
|
variant="ghost-muted"
|
||||||
icon={<IconV2 name="grid-plus" />}
|
size="large"
|
||||||
state={!!homeMatch() ? "pressed" : undefined}
|
class="!w-8"
|
||||||
/>
|
state={!!useMatch(() => "/")() ? "pressed" : undefined}
|
||||||
|
>
|
||||||
<div class="flex min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden">
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||||
<div class="flex min-w-0 flex-row items-center gap-1.5 overflow-hidden">
|
<path
|
||||||
<For each={tabsEnriched()}>
|
d="M13.9948 11.668H9.32812M11.6641 9.33203V13.9987M6.66667 9.33203V13.9987H2V9.33203H6.66667ZM6.66667 2V6.66667H2V2H6.66667ZM13.9948 2V6.66667H9.32812V2H13.9948Z"
|
||||||
{(tab, i) => (
|
stroke="currentColor"
|
||||||
<>
|
stroke-miterlimit="10"
|
||||||
{i() !== 0 && (
|
stroke-linecap="square"
|
||||||
<div class="w-[1.5px] h-3 shrink-0 rounded-full bg-[var(--v2-background-bg-layer-02)]" />
|
|
||||||
)}
|
|
||||||
<TabNavItem
|
|
||||||
href={tab.href}
|
|
||||||
title={tab.info.title}
|
|
||||||
project={projectForSession(tab.info, projects(), projectByID())}
|
|
||||||
directory={tab.dir}
|
|
||||||
onClose={() => tabsStoreActions.removeTab(tab.href)}
|
|
||||||
hideClose={tabsEnriched().length < 2}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
<Show
|
|
||||||
when={creating() && params.dir}
|
|
||||||
fallback={
|
|
||||||
<IconButtonV2
|
|
||||||
type="button"
|
|
||||||
variant="ghost-muted"
|
|
||||||
size="large"
|
|
||||||
class="shrink-0"
|
|
||||||
icon={<IconV2 name="plus" />}
|
|
||||||
as="a"
|
|
||||||
href={newSessionHref()}
|
|
||||||
aria-label={language.t("command.session.new")}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<NewSessionTabItem
|
|
||||||
href={`/${params.dir}/session`}
|
|
||||||
title={language.t("command.session.new")}
|
|
||||||
onClose={() => navigate(tabsEnriched().at(-1)?.href ?? "/")}
|
|
||||||
/>
|
/>
|
||||||
</Show>
|
</svg>
|
||||||
<div class="min-w-0 flex-1" />
|
</IconButtonV2>
|
||||||
|
<div class="flex flex-row items-center gap-2">
|
||||||
|
<For each={tabsEnriched()}>
|
||||||
|
{(tab, i) => (
|
||||||
|
<>
|
||||||
|
{i() !== 0 && <div class="w-[1.5px] h-3 rounded-full bg-[var(--v2-background-bg-layer-02)]" />}
|
||||||
|
<TabNavItem
|
||||||
|
href={tab.href}
|
||||||
|
title={tab.info.title}
|
||||||
|
onClose={() => tabsStoreActions.removeTab(tab.href)}
|
||||||
|
hideClose={tabsEnriched().length < 2}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
</div>
|
</div>
|
||||||
<Show when={currentSessionTab()?.dir} keyed>
|
<button>
|
||||||
{(dir) => (
|
<div class="p-1.5">
|
||||||
<SDKProvider directory={dir}>
|
<svg
|
||||||
<Tooltip placement="bottom" value={language.t("status.popover.trigger")}>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<StatusPopover />
|
width="16"
|
||||||
</Tooltip>
|
height="16"
|
||||||
</SDKProvider>
|
viewBox="0 0 16 16"
|
||||||
)}
|
fill="none"
|
||||||
</Show>
|
class="size-4"
|
||||||
<TitlebarUpdatePill update={props.update} />
|
>
|
||||||
<Show when={windows() && !electronWindows()}>
|
<path
|
||||||
<div data-tauri-decorum-tb class="flex flex-row" />
|
d="M7.99978 2.88867V13.1109M2.88867 7.99978H13.1109"
|
||||||
</Show>
|
stroke="#808080"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="flex-1" />
|
||||||
|
{/*<button class="px-2.5 py-1.5 bg-[rgba(0,0,0,0.08)] rounded-[6px]">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.4443 2.44436V13.5555M1.55546 13.5554H14.4443V2.44434H1.55542L1.55546 13.5554Z"
|
||||||
|
stroke="#3A3A3A"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>*/}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -497,7 +358,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
<WindowsAppMenu command={command} platform={platform} />
|
<WindowsAppMenu command={command} platform={platform} />
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={mac()}>
|
<Show when={mac()}>
|
||||||
{/*<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />*/}
|
<div class="h-full shrink-0" style={{ width: `${72 / zoom()}px` }} />
|
||||||
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
<div class="xl:hidden w-10 shrink-0 flex items-center justify-center">
|
||||||
<IconButton
|
<IconButton
|
||||||
icon="menu"
|
icon="menu"
|
||||||
@@ -641,51 +502,19 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function TitlebarUpdatePill(props: { update?: TitlebarUpdate }) {
|
function TabNavItem(props: { href: string; title: string; hideClose?: boolean; onClose: () => void }) {
|
||||||
const language = useLanguage()
|
|
||||||
const version = () => props.update?.version()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Show when={version() !== undefined}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="h-5 shrink-0 rounded-[27px] bg-[var(--v2-background-bg-accent)] px-2.5 text-[11px] font-[530] leading-[1.1] tracking-[-0.04px] text-[var(--v2-text-text-contrast)] disabled:opacity-60"
|
|
||||||
onClick={() => props.update?.install()}
|
|
||||||
disabled={props.update?.installing()}
|
|
||||||
aria-label={language.t("toast.update.action.installRestart")}
|
|
||||||
title={version() ? `Update ${version()}` : undefined}
|
|
||||||
>
|
|
||||||
Update
|
|
||||||
</button>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function DesktopTitlebarIconButton(props: Parameters<typeof IconButtonV2>[0]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function TabNavItem(props: {
|
|
||||||
href: string
|
|
||||||
title: string
|
|
||||||
project?: LocalProject
|
|
||||||
directory: string
|
|
||||||
hideClose?: boolean
|
|
||||||
onClose: () => void
|
|
||||||
}) {
|
|
||||||
const match = useMatch(() => props.href)
|
const match = useMatch(() => props.href)
|
||||||
const isActive = () => !!match()
|
const isActive = () => !!match()
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] pl-1.5 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
|
class="group flex flex-row items-center max-w-60 whitespace-nowrap [--tab-bg:var(--v2-background-bg-deep)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] bg-[var(--tab-bg)] h-7 rounded-[6px] relative overflow-hidden"
|
||||||
data-active={isActive()}
|
data-active={isActive()}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
href={props.href}
|
href={props.href}
|
||||||
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
|
class="w-full h-full pl-1.5 flex-1 max-w-full flex flex-row items-center overflow-hidden font-medium"
|
||||||
>
|
>
|
||||||
<ProjectTabAvatar project={props.project} directory={props.directory} />
|
{props.title}
|
||||||
<span class="text-clip">{props.title}</span>
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<div class="absolute right-0 inset-y-0 flex flex-row items-center pr-1 py-1 w-8 pl-2">
|
<div class="absolute right-0 inset-y-0 flex flex-row items-center pr-1 py-1 w-8 pl-2">
|
||||||
@@ -701,60 +530,23 @@ function TabNavItem(props: {
|
|||||||
variant="ghost-muted"
|
variant="ghost-muted"
|
||||||
class="opacity-0 group-hover:opacity-100 group-data-[active='true']:opacity-100"
|
class="opacity-0 group-hover:opacity-100 group-data-[active='true']:opacity-100"
|
||||||
onClick={props.onClose}
|
onClick={props.onClose}
|
||||||
icon={<IconV2 name="xmark-small" />}
|
icon={
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
class="size-4"
|
||||||
|
>
|
||||||
|
<path d="M4.25 11.75L11.75 4.25M11.75 11.75L4.25 4.25" stroke="currentColor" />
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function ProjectTabAvatar(props: { project?: LocalProject; directory: string }) {
|
|
||||||
return (
|
|
||||||
<AvatarV2
|
|
||||||
fallback={displayName(props.project ?? { worktree: props.directory })}
|
|
||||||
src={getProjectAvatarSource(props.project?.id, props.project?.icon)}
|
|
||||||
kind="org"
|
|
||||||
size="small"
|
|
||||||
{...getAvatarColors(props.project?.icon?.color)}
|
|
||||||
class="size-4 rounded"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function NewSessionTabItem(props: { href: string; title: string; onClose: () => void }) {
|
|
||||||
return (
|
|
||||||
<div class="group relative flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
|
|
||||||
<a
|
|
||||||
href={props.href}
|
|
||||||
aria-current="page"
|
|
||||||
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium leading-none text-[var(--v2-text-text-base)]"
|
|
||||||
>
|
|
||||||
<span class="flex size-4 shrink-0 rotate-90 items-center justify-center">
|
|
||||||
<IconV2 name="edit" />
|
|
||||||
</span>
|
|
||||||
<span class="truncate">{props.title}</span>
|
|
||||||
</a>
|
|
||||||
<div class="absolute right-0 inset-y-0 flex w-7 items-center justify-center">
|
|
||||||
<IconButtonV2
|
|
||||||
size="small"
|
|
||||||
variant="ghost-muted"
|
|
||||||
onMouseDown={(event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
}}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault()
|
|
||||||
event.stopPropagation()
|
|
||||||
props.onClose()
|
|
||||||
}}
|
|
||||||
icon={<IconV2 name="xmark-small" />}
|
|
||||||
aria-label="Close tab"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function ChannelIndicator() {
|
function ChannelIndicator() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { Show, type JSX } from "solid-js"
|
|||||||
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
import { IconButton } from "@opencode-ai/ui/icon-button"
|
import { IconButton } from "@opencode-ai/ui/icon-button"
|
||||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
|
|
||||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
|
|
||||||
|
|
||||||
import { useCommand } from "@/context/command"
|
import { useCommand } from "@/context/command"
|
||||||
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
|
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
|
||||||
@@ -12,7 +10,6 @@ import { usePlatform } from "@/context/platform"
|
|||||||
export function WindowsAppMenu(props: {
|
export function WindowsAppMenu(props: {
|
||||||
command: ReturnType<typeof useCommand>
|
command: ReturnType<typeof useCommand>
|
||||||
platform: ReturnType<typeof usePlatform>
|
platform: ReturnType<typeof usePlatform>
|
||||||
variant?: "legacy" | "v2"
|
|
||||||
}) {
|
}) {
|
||||||
let lastFocused: HTMLElement | undefined
|
let lastFocused: HTMLElement | undefined
|
||||||
|
|
||||||
@@ -48,32 +45,15 @@ export function WindowsAppMenu(props: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu gutter={4} modal={false} placement="bottom-start">
|
<DropdownMenu gutter={4} modal={false} placement="bottom-start">
|
||||||
{props.variant === "v2" ? (
|
<DropdownMenu.Trigger
|
||||||
<div
|
as={IconButton}
|
||||||
data-component="desktop-icon-button"
|
icon="menu"
|
||||||
class="flex h-7 w-9 shrink-0 items-center justify-center rounded-[6px] px-1"
|
variant="ghost"
|
||||||
>
|
class="titlebar-icon rounded-md shrink-0"
|
||||||
<DropdownMenu.Trigger
|
aria-label="OpenCode menu"
|
||||||
as={IconButtonV2}
|
onPointerDown={rememberFocus}
|
||||||
variant="ghost-muted"
|
onKeyDown={rememberFocus}
|
||||||
size="large"
|
/>
|
||||||
icon={<IconV2 name="menu" />}
|
|
||||||
aria-label="OpenCode menu"
|
|
||||||
onPointerDown={rememberFocus}
|
|
||||||
onKeyDown={rememberFocus}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<DropdownMenu.Trigger
|
|
||||||
as={IconButton}
|
|
||||||
icon="menu"
|
|
||||||
variant="ghost"
|
|
||||||
class="titlebar-icon rounded-md shrink-0"
|
|
||||||
aria-label="OpenCode menu"
|
|
||||||
onPointerDown={rememberFocus}
|
|
||||||
onKeyDown={rememberFocus}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
<DropdownMenu.Content class="desktop-app-menu">
|
<DropdownMenu.Content class="desktop-app-menu">
|
||||||
<DropdownMenu.Group>
|
<DropdownMenu.Group>
|
||||||
|
|||||||
@@ -232,9 +232,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
throwOnError: true,
|
throwOnError: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const dirSyncContexts = new Map<string, ReturnType<typeof createDirSdkContext>>()
|
|
||||||
const dirSdkContextRefCounts = new Map<string, number>()
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
url: currentServer.http.url,
|
url: currentServer.http.url,
|
||||||
client: sdk,
|
client: sdk,
|
||||||
@@ -252,58 +249,6 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
|
|||||||
...opts,
|
...opts,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
createDirSyncContext: (directory: string) => {
|
|
||||||
onCleanup(() => {
|
|
||||||
dirSdkContextRefCounts.set(directory, (dirSdkContextRefCounts.get(directory) ?? 0) - 1)
|
|
||||||
if (dirSdkContextRefCounts.get(directory) === 0) {
|
|
||||||
dirSyncContexts.delete(directory)
|
|
||||||
dirSdkContextRefCounts.delete(directory)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const cached = dirSyncContexts.get(directory)
|
|
||||||
if (cached) {
|
|
||||||
dirSdkContextRefCounts.set(directory, (dirSdkContextRefCounts.get(directory) ?? 0) + 1)
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
const ctx = createDirSdkContext(directory)
|
|
||||||
dirSyncContexts.set(directory, ctx)
|
|
||||||
dirSdkContextRefCounts.set(directory, 1)
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
type SDKEventMap = {
|
|
||||||
[key in Event["type"]]: Extract<Event, { type: key }>
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDirSdkContext(directory: string) {
|
|
||||||
const globalSDK = useGlobalSDK()
|
|
||||||
|
|
||||||
const client = globalSDK.createClient({
|
|
||||||
directory,
|
|
||||||
throwOnError: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const emitter = createGlobalEmitter<SDKEventMap>()
|
|
||||||
|
|
||||||
const unsub = globalSDK.event.on(directory, (event) => {
|
|
||||||
emitter.emit(event.type, event)
|
|
||||||
})
|
|
||||||
onCleanup(unsub)
|
|
||||||
|
|
||||||
return {
|
|
||||||
directory,
|
|
||||||
client,
|
|
||||||
event: emitter,
|
|
||||||
get url() {
|
|
||||||
return globalSDK.url
|
|
||||||
},
|
|
||||||
createClient(opts: Parameters<typeof globalSDK.createClient>[0]) {
|
|
||||||
return globalSDK.createClient(opts)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
import type { Config, OpencodeClient, Path, Project, ProviderAuthResponse, Todo } from "@opencode-ai/sdk/v2/client"
|
import type {
|
||||||
|
Config,
|
||||||
|
OpencodeClient,
|
||||||
|
Path,
|
||||||
|
Project,
|
||||||
|
ProviderAuthResponse,
|
||||||
|
ProviderListResponse,
|
||||||
|
Todo,
|
||||||
|
} from "@opencode-ai/sdk/v2/client"
|
||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { getFilename } from "@opencode-ai/core/util/path"
|
import { getFilename } from "@opencode-ai/core/util/path"
|
||||||
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
|
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
|
||||||
@@ -29,7 +37,6 @@ import { createRefreshQueue } from "./global-sync/queue"
|
|||||||
import { directoryKey } from "./global-sync/utils"
|
import { directoryKey } from "./global-sync/utils"
|
||||||
import { PathKey } from "@/utils/path-key"
|
import { PathKey } from "@/utils/path-key"
|
||||||
import { createDirSyncContext } from "./directory-sync"
|
import { createDirSyncContext } from "./directory-sync"
|
||||||
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
|
|
||||||
type GlobalStore = {
|
type GlobalStore = {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
@@ -39,7 +46,7 @@ type GlobalStore = {
|
|||||||
session_todo: {
|
session_todo: {
|
||||||
[sessionID: string]: Todo[]
|
[sessionID: string]: Todo[]
|
||||||
}
|
}
|
||||||
provider: NormalizedProviderListResponse
|
provider: ProviderListResponse
|
||||||
provider_auth: ProviderAuthResponse
|
provider_auth: ProviderAuthResponse
|
||||||
config: Config
|
config: Config
|
||||||
reload: undefined | "pending" | "complete"
|
reload: undefined | "pending" | "complete"
|
||||||
@@ -114,7 +121,7 @@ function createGlobalSync() {
|
|||||||
return pathQuery.data ?? EMPTY
|
return pathQuery.data ?? EMPTY
|
||||||
},
|
},
|
||||||
get provider() {
|
get provider() {
|
||||||
const EMPTY = { all: new Map(), connected: [], default: {} }
|
const EMPTY = { all: [], connected: [], default: {} }
|
||||||
if (providerQuery.isLoading) return EMPTY
|
if (providerQuery.isLoading) return EMPTY
|
||||||
return providerQuery.data ?? EMPTY
|
return providerQuery.data ?? EMPTY
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
|
||||||
import { createStore } from "solid-js/store"
|
|
||||||
import { QueryClient } from "@tanstack/solid-query"
|
|
||||||
import type { Config, OpencodeClient, Project } from "@opencode-ai/sdk/v2/client"
|
|
||||||
import type { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
import { bootstrapDirectory } from "./bootstrap"
|
|
||||||
import type { State, VcsCache } from "./types"
|
|
||||||
|
|
||||||
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
|
|
||||||
|
|
||||||
describe("bootstrapDirectory", () => {
|
|
||||||
test("marks a loading directory partial during bootstrap and complete after success", async () => {
|
|
||||||
const [store, setStore] = createStore<State>({
|
|
||||||
status: "loading",
|
|
||||||
agent: [],
|
|
||||||
command: [],
|
|
||||||
project: "",
|
|
||||||
projectMeta: undefined,
|
|
||||||
icon: undefined,
|
|
||||||
provider_ready: true,
|
|
||||||
provider,
|
|
||||||
config: {},
|
|
||||||
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
|
||||||
session: [],
|
|
||||||
sessionTotal: 0,
|
|
||||||
session_status: {},
|
|
||||||
session_working(id: string) {
|
|
||||||
return this.session_status[id]?.type !== "idle"
|
|
||||||
},
|
|
||||||
session_diff: {},
|
|
||||||
todo: {},
|
|
||||||
permission: {},
|
|
||||||
question: {},
|
|
||||||
mcp_ready: true,
|
|
||||||
mcp: {},
|
|
||||||
lsp_ready: true,
|
|
||||||
lsp: [],
|
|
||||||
vcs: undefined,
|
|
||||||
limit: 5,
|
|
||||||
message: {},
|
|
||||||
part: {},
|
|
||||||
part_text_accum_delta: {},
|
|
||||||
})
|
|
||||||
|
|
||||||
await bootstrapDirectory({
|
|
||||||
directory: "/project",
|
|
||||||
global: {
|
|
||||||
config: {} satisfies Config,
|
|
||||||
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
|
||||||
project: [{ id: "project", worktree: "/project" } as Project],
|
|
||||||
provider,
|
|
||||||
},
|
|
||||||
sdk: {
|
|
||||||
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
|
|
||||||
config: { get: async () => ({ data: {} }) },
|
|
||||||
session: { status: async () => ({ data: {} }) },
|
|
||||||
vcs: { get: async () => ({ data: undefined }) },
|
|
||||||
command: { list: async () => ({ data: [] }) },
|
|
||||||
permission: { list: async () => ({ data: [] }) },
|
|
||||||
question: { list: async () => ({ data: [] }) },
|
|
||||||
mcp: { status: async () => ({ data: {} }) },
|
|
||||||
provider: { list: async () => ({ data: { all: [], connected: [], default: {} } }) },
|
|
||||||
} as unknown as OpencodeClient,
|
|
||||||
store,
|
|
||||||
setStore,
|
|
||||||
vcsCache: { setStore() {} } as unknown as VcsCache,
|
|
||||||
loadSessions() {},
|
|
||||||
translate: (key) => key,
|
|
||||||
queryClient: new QueryClient(),
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(store.status).toBe("partial")
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
||||||
|
|
||||||
expect(store.status).toBe("complete")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -5,6 +5,7 @@ import type {
|
|||||||
PermissionRequest,
|
PermissionRequest,
|
||||||
Project,
|
Project,
|
||||||
ProviderAuthResponse,
|
ProviderAuthResponse,
|
||||||
|
ProviderListResponse,
|
||||||
QuestionRequest,
|
QuestionRequest,
|
||||||
Session,
|
Session,
|
||||||
Todo,
|
Todo,
|
||||||
@@ -19,7 +20,6 @@ import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
|
|||||||
import { formatServerError } from "@/utils/server-errors"
|
import { formatServerError } from "@/utils/server-errors"
|
||||||
import { QueryClient, queryOptions } from "@tanstack/solid-query"
|
import { QueryClient, queryOptions } from "@tanstack/solid-query"
|
||||||
import { loadMcpQuery } from "../global-sync"
|
import { loadMcpQuery } from "../global-sync"
|
||||||
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
|
|
||||||
type GlobalStore = {
|
type GlobalStore = {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
@@ -28,7 +28,7 @@ type GlobalStore = {
|
|||||||
session_todo: {
|
session_todo: {
|
||||||
[sessionID: string]: Todo[]
|
[sessionID: string]: Todo[]
|
||||||
}
|
}
|
||||||
provider: NormalizedProviderListResponse
|
provider: ProviderListResponse
|
||||||
provider_auth: ProviderAuthResponse
|
provider_auth: ProviderAuthResponse
|
||||||
config: Config
|
config: Config
|
||||||
reload: undefined | "pending" | "complete"
|
reload: undefined | "pending" | "complete"
|
||||||
@@ -208,7 +208,7 @@ export async function bootstrapDirectory(input: {
|
|||||||
config: Config
|
config: Config
|
||||||
path: Path
|
path: Path
|
||||||
project: Project[]
|
project: Project[]
|
||||||
provider: NormalizedProviderListResponse
|
provider: ProviderListResponse
|
||||||
}
|
}
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
@@ -1,63 +1,10 @@
|
|||||||
import { beforeAll, describe, expect, mock, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { createRoot, getOwner, type Owner } from "solid-js"
|
import { createRoot, getOwner } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import type { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
import type { State } from "./types"
|
import type { State } from "./types"
|
||||||
import type { QueryOptionsApi } from "../global-sync"
|
import { createChildStoreManager } from "./child-store"
|
||||||
|
|
||||||
let createChildStoreManager: typeof import("./child-store").createChildStoreManager
|
|
||||||
|
|
||||||
const child = () => createStore({} as State)
|
const child = () => createStore({} as State)
|
||||||
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
|
|
||||||
|
|
||||||
const queryOptionsApi = {
|
|
||||||
globalConfig: () => ({ queryKey: ["globalConfig"], queryFn: async () => ({}) }),
|
|
||||||
projects: () => ({ queryKey: ["projects"], queryFn: async () => [] }),
|
|
||||||
providers: (directory: string | null) => ({ queryKey: [directory, "providers"], queryFn: async () => provider }),
|
|
||||||
path: (directory: string | null) => ({
|
|
||||||
queryKey: [directory, "path"],
|
|
||||||
queryFn: async () => ({
|
|
||||||
state: "",
|
|
||||||
config: "",
|
|
||||||
worktree: "",
|
|
||||||
directory: directory ?? "",
|
|
||||||
home: "",
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
agents: (directory: string) => ({ queryKey: [directory, "agents"], queryFn: async () => [] }),
|
|
||||||
mcp: (directory: string) => ({ queryKey: [directory, "mcp"], queryFn: async () => ({}) }),
|
|
||||||
lsp: (directory: string) => ({ queryKey: [directory, "lsp"], queryFn: async () => [] }),
|
|
||||||
sessions: (directory: string) => ({ queryKey: [directory, "loadSessions"] as const }),
|
|
||||||
} as unknown as QueryOptionsApi
|
|
||||||
|
|
||||||
function createOwner(callback: (owner: Owner) => void) {
|
|
||||||
return createRoot((dispose) => {
|
|
||||||
const owner = getOwner()
|
|
||||||
if (!owner) throw new Error("owner required")
|
|
||||||
callback(owner)
|
|
||||||
|
|
||||||
return dispose
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
mock.module("@/utils/persist", () => ({
|
|
||||||
Persist: {
|
|
||||||
workspace: (...parts: string[]) => parts.join(":"),
|
|
||||||
},
|
|
||||||
persisted: (_target: string, store: unknown[]) => [store[0], store[1], null, () => true],
|
|
||||||
}))
|
|
||||||
mock.module("@tanstack/solid-query", () => ({
|
|
||||||
useQueries: () => [
|
|
||||||
{ isLoading: false, data: { state: "", config: "", worktree: "", directory: "", home: "" } },
|
|
||||||
{ isLoading: false, data: {} },
|
|
||||||
{ isLoading: false, data: [] },
|
|
||||||
{ isLoading: false, data: provider },
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
|
|
||||||
createChildStoreManager = (await import("./child-store")).createChildStoreManager
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("createChildStoreManager", () => {
|
describe("createChildStoreManager", () => {
|
||||||
test("does not evict the active directory during mark", () => {
|
test("does not evict the active directory during mark", () => {
|
||||||
@@ -75,8 +22,8 @@ describe("createChildStoreManager", () => {
|
|||||||
onBootstrap() {},
|
onBootstrap() {},
|
||||||
onDispose() {},
|
onDispose() {},
|
||||||
translate: (key) => key,
|
translate: (key) => key,
|
||||||
queryOptions: queryOptionsApi,
|
queryOptions: {} as any,
|
||||||
global: { provider },
|
global: { provider: null! },
|
||||||
})
|
})
|
||||||
|
|
||||||
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
|
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
|
||||||
@@ -90,35 +37,4 @@ describe("createChildStoreManager", () => {
|
|||||||
|
|
||||||
expect(manager.children[directory]).toBeDefined()
|
expect(manager.children[directory]).toBeDefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("starts new child stores as loading and bootstraps them on first access", () => {
|
|
||||||
const bootstraps: string[] = []
|
|
||||||
let manager: ReturnType<typeof createChildStoreManager> | undefined
|
|
||||||
|
|
||||||
const dispose = createOwner((owner) => {
|
|
||||||
manager = createChildStoreManager({
|
|
||||||
owner,
|
|
||||||
isBooting: () => false,
|
|
||||||
isLoadingSessions: () => false,
|
|
||||||
onBootstrap(directory) {
|
|
||||||
bootstraps.push(directory)
|
|
||||||
},
|
|
||||||
onDispose() {},
|
|
||||||
translate: (key) => key,
|
|
||||||
queryOptions: queryOptionsApi,
|
|
||||||
global: { provider },
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!manager) throw new Error("manager required")
|
|
||||||
|
|
||||||
const [store] = manager.child("/project")
|
|
||||||
|
|
||||||
expect(store.status).toBe("loading")
|
|
||||||
expect(bootstraps).toEqual(["/project"])
|
|
||||||
} finally {
|
|
||||||
dispose()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
|
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
|
||||||
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
|
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
import type { VcsInfo } from "@opencode-ai/sdk/v2/client"
|
import type { ProviderListResponse, VcsInfo } from "@opencode-ai/sdk/v2/client"
|
||||||
import {
|
import {
|
||||||
DIR_IDLE_TTL_MS,
|
DIR_IDLE_TTL_MS,
|
||||||
MAX_DIR_STORES,
|
MAX_DIR_STORES,
|
||||||
@@ -17,7 +17,6 @@ import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
|
|||||||
import { useQueries } from "@tanstack/solid-query"
|
import { useQueries } from "@tanstack/solid-query"
|
||||||
import { QueryOptionsApi } from "../global-sync"
|
import { QueryOptionsApi } from "../global-sync"
|
||||||
import { directoryKey, type DirectoryKey } from "./utils"
|
import { directoryKey, type DirectoryKey } from "./utils"
|
||||||
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
|
|
||||||
export function createChildStoreManager(input: {
|
export function createChildStoreManager(input: {
|
||||||
owner: Owner
|
owner: Owner
|
||||||
@@ -28,7 +27,7 @@ export function createChildStoreManager(input: {
|
|||||||
translate: (key: string, vars?: Record<string, string | number>) => string
|
translate: (key: string, vars?: Record<string, string | number>) => string
|
||||||
queryOptions: QueryOptionsApi
|
queryOptions: QueryOptionsApi
|
||||||
global: {
|
global: {
|
||||||
provider: NormalizedProviderListResponse
|
provider: ProviderListResponse
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
|
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
|
||||||
@@ -191,9 +190,10 @@ export function createChildStoreManager(input: {
|
|||||||
return !providerQuery.isLoading
|
return !providerQuery.isLoading
|
||||||
},
|
},
|
||||||
get provider() {
|
get provider() {
|
||||||
const EMPTY = { all: new Map(), connected: [], default: {} }
|
const EMPTY = { all: [], connected: [], default: {} }
|
||||||
if (providerQuery.isLoading) return EMPTY
|
if (providerQuery.isLoading) return EMPTY
|
||||||
if (providerQuery.data?.all.size === 0 && input.global.provider.all.size > 0) return input.global.provider
|
if (providerQuery.data?.all.length === 0 && input.global.provider.all.length > 0)
|
||||||
|
return input.global.provider
|
||||||
return providerQuery.data ?? EMPTY
|
return providerQuery.data ?? EMPTY
|
||||||
},
|
},
|
||||||
config: {},
|
config: {},
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
Part,
|
Part,
|
||||||
Path,
|
Path,
|
||||||
PermissionRequest,
|
PermissionRequest,
|
||||||
|
ProviderListResponse,
|
||||||
QuestionRequest,
|
QuestionRequest,
|
||||||
Session,
|
Session,
|
||||||
SessionStatus,
|
SessionStatus,
|
||||||
@@ -15,7 +16,6 @@ import type {
|
|||||||
Todo,
|
Todo,
|
||||||
VcsInfo,
|
VcsInfo,
|
||||||
} from "@opencode-ai/sdk/v2/client"
|
} from "@opencode-ai/sdk/v2/client"
|
||||||
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
import type { Accessor } from "solid-js"
|
import type { Accessor } from "solid-js"
|
||||||
import type { SetStoreFunction, Store } from "solid-js/store"
|
import type { SetStoreFunction, Store } from "solid-js/store"
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ export type State = {
|
|||||||
projectMeta: ProjectMeta | undefined
|
projectMeta: ProjectMeta | undefined
|
||||||
icon: string | undefined
|
icon: string | undefined
|
||||||
provider_ready: boolean
|
provider_ready: boolean
|
||||||
provider: NormalizedProviderListResponse
|
provider: ProviderListResponse
|
||||||
config: Config
|
config: Config
|
||||||
path: Path
|
path: Path
|
||||||
session: Session[]
|
session: Session[]
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
|
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
|
||||||
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
|
|
||||||
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
|
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
|
||||||
|
|
||||||
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||||
@@ -18,23 +17,13 @@ export function normalizeAgentList(input: unknown): Agent[] {
|
|||||||
return Object.values(input).filter(isAgent)
|
return Object.values(input).filter(isAgent)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeProviderList(input: ProviderListResponse): NormalizedProviderListResponse {
|
export function normalizeProviderList(input: ProviderListResponse): ProviderListResponse {
|
||||||
return {
|
return {
|
||||||
...input,
|
...input,
|
||||||
all: new Map(
|
all: input.all.map((provider) => ({
|
||||||
input.all.map(
|
...provider,
|
||||||
(provider) =>
|
models: Object.fromEntries(Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated")),
|
||||||
[
|
})),
|
||||||
provider.id,
|
|
||||||
{
|
|
||||||
...provider,
|
|
||||||
models: Object.fromEntries(
|
|
||||||
Object.entries(provider.models).filter(([, info]) => info.status !== "deprecated"),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
] as const,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const validModel = (model: ModelKey) => {
|
const validModel = (model: ModelKey) => {
|
||||||
const provider = providers.all().get(model.providerID)
|
const provider = providers.all().find((item) => item.id === model.providerID)
|
||||||
return !!provider?.models[model.modelID] && connected().has(model.providerID)
|
return !!provider?.models[model.modelID] && connected().has(model.providerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,23 +9,13 @@ type OpenDirectoryPickerOptions = { title?: string; multiple?: boolean }
|
|||||||
type OpenFilePickerOptions = { title?: string; multiple?: boolean; accept?: string[]; extensions?: string[] }
|
type OpenFilePickerOptions = { title?: string; multiple?: boolean; accept?: string[]; extensions?: string[] }
|
||||||
type SaveFilePickerOptions = { title?: string; defaultPath?: string }
|
type SaveFilePickerOptions = { title?: string; defaultPath?: string }
|
||||||
type UpdateInfo = { updateAvailable: boolean; version?: string }
|
type UpdateInfo = { updateAvailable: boolean; version?: string }
|
||||||
type PlatformName = "web" | "desktop"
|
|
||||||
type DesktopOS = "macos" | "windows" | "linux"
|
|
||||||
|
|
||||||
export type FatalRendererErrorLog = {
|
|
||||||
error: string
|
|
||||||
url: string
|
|
||||||
version?: string
|
|
||||||
platform: PlatformName
|
|
||||||
os?: DesktopOS
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Platform = {
|
export type Platform = {
|
||||||
/** Platform discriminator */
|
/** Platform discriminator */
|
||||||
platform: PlatformName
|
platform: "web" | "desktop"
|
||||||
|
|
||||||
/** Desktop OS (Tauri only) */
|
/** Desktop OS (Tauri only) */
|
||||||
os?: DesktopOS
|
os?: "macos" | "windows" | "linux"
|
||||||
|
|
||||||
/** App version */
|
/** App version */
|
||||||
version?: string
|
version?: string
|
||||||
@@ -93,12 +83,6 @@ export type Platform = {
|
|||||||
/** Webview zoom level (desktop only) */
|
/** Webview zoom level (desktop only) */
|
||||||
webviewZoom?: Accessor<number>
|
webviewZoom?: Accessor<number>
|
||||||
|
|
||||||
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
|
|
||||||
getPinchZoomEnabled?(): Promise<boolean> | boolean
|
|
||||||
|
|
||||||
/** Allow native pinch/Ctrl-scroll zoom gestures (desktop only) */
|
|
||||||
setPinchZoomEnabled?(enabled: boolean): Promise<void> | void
|
|
||||||
|
|
||||||
/** Run a desktop-only menu action from the app chrome */
|
/** Run a desktop-only menu action from the app chrome */
|
||||||
runDesktopMenuAction?(action: DesktopMenuAction): Promise<void> | void
|
runDesktopMenuAction?(action: DesktopMenuAction): Promise<void> | void
|
||||||
|
|
||||||
@@ -107,12 +91,6 @@ export type Platform = {
|
|||||||
|
|
||||||
/** Read image from clipboard (desktop only) */
|
/** Read image from clipboard (desktop only) */
|
||||||
readClipboardImage?(): Promise<File | null>
|
readClipboardImage?(): Promise<File | null>
|
||||||
|
|
||||||
/** Export collected diagnostic logs (desktop only) */
|
|
||||||
exportDebugLogs?(): Promise<string>
|
|
||||||
|
|
||||||
/** Record a fatal renderer error in platform logs (desktop only) */
|
|
||||||
recordFatalRendererError?(error: FatalRendererErrorLog): Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DisplayBackend = "auto" | "wayland"
|
export type DisplayBackend = "auto" | "wayland"
|
||||||
|
|||||||
@@ -1,11 +1,49 @@
|
|||||||
|
import type { Event } from "@opencode-ai/sdk/v2/client"
|
||||||
import { createSimpleContext } from "@opencode-ai/ui/context"
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
|
import { createGlobalEmitter } from "@solid-primitives/event-bus"
|
||||||
|
import { type Accessor, createEffect, createMemo, onCleanup } from "solid-js"
|
||||||
import { useGlobalSDK } from "./global-sdk"
|
import { useGlobalSDK } from "./global-sdk"
|
||||||
|
|
||||||
|
type SDKEventMap = {
|
||||||
|
[key in Event["type"]]: Extract<Event, { type: key }>
|
||||||
|
}
|
||||||
|
|
||||||
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
export const { use: useSDK, provider: SDKProvider } = createSimpleContext({
|
||||||
name: "SDK",
|
name: "SDK",
|
||||||
init: (props: { directory: string }) => {
|
init: (props: { directory: Accessor<string> }) => {
|
||||||
const globalSDK = useGlobalSDK()
|
const globalSDK = useGlobalSDK()
|
||||||
|
|
||||||
return globalSDK.createDirSyncContext(props.directory)
|
const directory = createMemo(props.directory)
|
||||||
|
const client = createMemo(() =>
|
||||||
|
globalSDK.createClient({
|
||||||
|
directory: directory(),
|
||||||
|
throwOnError: true,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const emitter = createGlobalEmitter<SDKEventMap>()
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const unsub = globalSDK.event.on(directory(), (event) => {
|
||||||
|
emitter.emit(event.type, event)
|
||||||
|
})
|
||||||
|
onCleanup(unsub)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
get directory() {
|
||||||
|
return directory()
|
||||||
|
},
|
||||||
|
get client() {
|
||||||
|
return client()
|
||||||
|
},
|
||||||
|
event: emitter,
|
||||||
|
get url() {
|
||||||
|
return globalSDK.url
|
||||||
|
},
|
||||||
|
createClient(opts: Parameters<typeof globalSDK.createClient>[0]) {
|
||||||
|
return globalSDK.createClient(opts)
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Binary } from "@opencode-ai/core/util/binary"
|
import { Binary } from "@opencode-ai/core/util/binary"
|
||||||
|
import { createSimpleContext } from "@opencode-ai/ui/context"
|
||||||
import { useGlobalSync } from "./global-sync"
|
import { useGlobalSync } from "./global-sync"
|
||||||
import { useSDK } from "./sdk"
|
import { useSDK } from "./sdk"
|
||||||
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
|
import type { Message, Part } from "@opencode-ai/sdk/v2/client"
|
||||||
@@ -9,8 +10,26 @@ function sortParts(parts: Part[]) {
|
|||||||
return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id))
|
return parts.filter((part) => !!part?.id).sort((a, b) => cmp(a.id, b.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runInflight(map: Map<string, Promise<void>>, key: string, task: () => Promise<void>) {
|
||||||
|
const pending = map.get(key)
|
||||||
|
if (pending) return pending
|
||||||
|
const promise = task().finally(() => {
|
||||||
|
map.delete(key)
|
||||||
|
})
|
||||||
|
map.set(key, promise)
|
||||||
|
return promise
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyFor = (directory: string, id: string) => `${directory}\n${id}`
|
||||||
|
|
||||||
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
|
||||||
|
|
||||||
|
function merge<T extends { id: string }>(a: readonly T[], b: readonly T[]) {
|
||||||
|
const map = new Map(a.map((item) => [item.id, item] as const))
|
||||||
|
for (const item of b) map.set(item.id, item)
|
||||||
|
return [...map.values()].sort((x, y) => cmp(x.id, y.id))
|
||||||
|
}
|
||||||
|
|
||||||
type OptimisticStore = {
|
type OptimisticStore = {
|
||||||
message: Record<string, Message[] | undefined>
|
message: Record<string, Message[] | undefined>
|
||||||
part: Record<string, Part[] | undefined>
|
part: Record<string, Part[] | undefined>
|
||||||
@@ -108,9 +127,40 @@ export function applyOptimisticRemove(draft: OptimisticStore, input: OptimisticR
|
|||||||
delete draft.part[input.messageID]
|
delete draft.part[input.messageID]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSync = () => {
|
function setOptimisticAdd(setStore: (...args: unknown[]) => void, input: OptimisticAddInput) {
|
||||||
const globalSync = useGlobalSync()
|
setStore("message", input.sessionID, (messages: Message[] | undefined) => {
|
||||||
const sdk = useSDK()
|
if (!messages) return [input.message]
|
||||||
|
const result = Binary.search(messages, input.message.id, (m) => m.id)
|
||||||
return globalSync.createDirSyncContext(sdk.directory)
|
const next = [...messages]
|
||||||
|
next.splice(result.index, 0, input.message)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
setStore("part", input.message.id, sortParts(input.parts))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOptimisticRemove(setStore: (...args: unknown[]) => void, input: OptimisticRemoveInput) {
|
||||||
|
setStore("message", input.sessionID, (messages: Message[] | undefined) => {
|
||||||
|
if (!messages) return messages
|
||||||
|
const result = Binary.search(messages, input.messageID, (m) => m.id)
|
||||||
|
if (!result.found) return messages
|
||||||
|
const next = [...messages]
|
||||||
|
next.splice(result.index, 1)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
setStore("part", (part: Record<string, Part[] | undefined>) => {
|
||||||
|
if (!(input.messageID in part)) return part
|
||||||
|
const next = { ...part }
|
||||||
|
delete next[input.messageID]
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
|
name: "Sync",
|
||||||
|
init: () => {
|
||||||
|
const globalSync = useGlobalSync()
|
||||||
|
const sdk = useSDK()
|
||||||
|
|
||||||
|
return globalSync.createDirSyncContext(sdk.directory)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -80,7 +80,6 @@ export const DESKTOP_MENU: DesktopMenu[] = [
|
|||||||
{ type: "item", label: "Settings", command: "settings.open", accelerator: { macos: "Cmd+," } },
|
{ type: "item", label: "Settings", command: "settings.open", accelerator: { macos: "Cmd+," } },
|
||||||
{ type: "item", label: "Reload Webview", action: "view.reload" },
|
{ type: "item", label: "Reload Webview", action: "view.reload" },
|
||||||
{ type: "item", label: "Restart", action: "app.relaunch" },
|
{ type: "item", label: "Restart", action: "app.relaunch" },
|
||||||
{ type: "item", label: "Export Logs...", command: "logs.export" },
|
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{ type: "item", role: "hide" },
|
{ type: "item", role: "hide" },
|
||||||
{ type: "item", role: "hideOthers" },
|
{ type: "item", role: "hideOthers" },
|
||||||
@@ -202,7 +201,6 @@ export const DESKTOP_MENU: DesktopMenu[] = [
|
|||||||
items: [
|
items: [
|
||||||
{ type: "item", label: "OpenCode Documentation", href: "https://opencode.ai/docs" },
|
{ type: "item", label: "OpenCode Documentation", href: "https://opencode.ai/docs" },
|
||||||
{ type: "item", label: "Support Forum", href: "https://discord.com/invite/opencode" },
|
{ type: "item", label: "Support Forum", href: "https://discord.com/invite/opencode" },
|
||||||
{ type: "item", label: "Export Logs...", command: "logs.export" },
|
|
||||||
{ type: "separator" },
|
{ type: "separator" },
|
||||||
{
|
{
|
||||||
type: "item",
|
type: "item",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { useParams } from "@solidjs/router"
|
import { useParams } from "@solidjs/router"
|
||||||
import { Iterable, pipe } from "effect"
|
|
||||||
import { createMemo } from "solid-js"
|
import { createMemo } from "solid-js"
|
||||||
|
|
||||||
export const popularProviders = [
|
export const popularProviders = [
|
||||||
@@ -30,32 +29,16 @@ export function useProviders() {
|
|||||||
return {
|
return {
|
||||||
all: () => providers().all,
|
all: () => providers().all,
|
||||||
default: () => providers().default,
|
default: () => providers().default,
|
||||||
popular: () =>
|
popular: () => providers().all.filter((p) => popularProviderSet.has(p.id)),
|
||||||
pipe(
|
|
||||||
providers().all,
|
|
||||||
Iterable.map(([, p]) => p),
|
|
||||||
Iterable.filter((p) => popularProviderSet.has(p.id)),
|
|
||||||
(v) => Array.from(v),
|
|
||||||
),
|
|
||||||
connected: () => {
|
connected: () => {
|
||||||
const connected = new Set(providers().connected)
|
const connected = new Set(providers().connected)
|
||||||
return pipe(
|
return providers().all.filter((p) => connected.has(p.id))
|
||||||
providers().all,
|
|
||||||
Iterable.map(([, p]) => p),
|
|
||||||
Iterable.filter((p) => connected.has(p.id)),
|
|
||||||
(v) => Array.from(v),
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
paid: () => {
|
paid: () => {
|
||||||
const connected = new Set(providers().connected)
|
const connected = new Set(providers().connected)
|
||||||
return [
|
return providers().all.filter(
|
||||||
...Iterable.filter(
|
(p) => connected.has(p.id) && (p.id !== "opencode" || Object.values(p.models).some((m) => m.cost?.input)),
|
||||||
providers().all,
|
)
|
||||||
([id]) =>
|
|
||||||
connected.has(id) &&
|
|
||||||
(id !== "opencode" || Object.values(providers().all.get(id)?.models ?? {}).some((m) => m.cost?.input)),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -470,7 +470,6 @@ export const dict = {
|
|||||||
"error.page.action.restart": "Restart",
|
"error.page.action.restart": "Restart",
|
||||||
"error.page.action.report": "Report Error",
|
"error.page.action.report": "Report Error",
|
||||||
"error.page.action.reported": "Error Reported",
|
"error.page.action.reported": "Error Reported",
|
||||||
"error.page.action.exportLogs": "Export Logs",
|
|
||||||
"error.page.action.checking": "Checking...",
|
"error.page.action.checking": "Checking...",
|
||||||
"error.page.action.checkUpdates": "Check for updates",
|
"error.page.action.checkUpdates": "Check for updates",
|
||||||
"error.page.action.updateTo": "Update to {{version}}",
|
"error.page.action.updateTo": "Update to {{version}}",
|
||||||
@@ -526,14 +525,6 @@ export const dict = {
|
|||||||
"home.recentProjects": "Recent projects",
|
"home.recentProjects": "Recent projects",
|
||||||
"home.empty.title": "No recent projects",
|
"home.empty.title": "No recent projects",
|
||||||
"home.empty.description": "Get started by opening a local project",
|
"home.empty.description": "Get started by opening a local project",
|
||||||
"home.title": "Home",
|
|
||||||
"home.projects": "Projects",
|
|
||||||
"home.project.add": "Add project",
|
|
||||||
"home.sessions.search.placeholder": "Search sessions",
|
|
||||||
"home.sessions.empty": "No sessions found",
|
|
||||||
"home.sessions.group.today": "Today",
|
|
||||||
"home.sessions.group.yesterday": "Yesterday",
|
|
||||||
"home.sessions.group.older": "Older",
|
|
||||||
|
|
||||||
"session.tab.session": "Session",
|
"session.tab.session": "Session",
|
||||||
"session.tab.review": "Review",
|
"session.tab.review": "Review",
|
||||||
@@ -784,8 +775,6 @@ export const dict = {
|
|||||||
"settings.general.row.showSessionProgressBar.title": "Show session progress bar",
|
"settings.general.row.showSessionProgressBar.title": "Show session progress bar",
|
||||||
"settings.general.row.showSessionProgressBar.description":
|
"settings.general.row.showSessionProgressBar.description":
|
||||||
"Display the animated progress bar at the top of the session when the agent is working",
|
"Display the animated progress bar at the top of the session when the agent is working",
|
||||||
"settings.general.row.pinchZoom.title": "Pinch to zoom",
|
|
||||||
"settings.general.row.pinchZoom.description": "Allow trackpad pinch and Ctrl-scroll gestures to zoom",
|
|
||||||
|
|
||||||
"settings.general.row.wayland.title": "Use native Wayland",
|
"settings.general.row.wayland.title": "Use native Wayland",
|
||||||
"settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
|
"settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
|
||||||
|
|||||||
@@ -498,14 +498,6 @@ export const dict = {
|
|||||||
"home.recentProjects": "最近项目",
|
"home.recentProjects": "最近项目",
|
||||||
"home.empty.title": "没有最近项目",
|
"home.empty.title": "没有最近项目",
|
||||||
"home.empty.description": "通过打开本地项目开始使用",
|
"home.empty.description": "通过打开本地项目开始使用",
|
||||||
"home.title": "主页",
|
|
||||||
"home.projects": "项目",
|
|
||||||
"home.project.add": "添加项目",
|
|
||||||
"home.sessions.search.placeholder": "搜索会话",
|
|
||||||
"home.sessions.empty": "未找到会话",
|
|
||||||
"home.sessions.group.today": "今天",
|
|
||||||
"home.sessions.group.yesterday": "昨天",
|
|
||||||
"home.sessions.group.older": "更早",
|
|
||||||
|
|
||||||
"session.tab.session": "会话",
|
"session.tab.session": "会话",
|
||||||
"session.tab.review": "审查",
|
"session.tab.review": "审查",
|
||||||
|
|||||||
@@ -43,7 +43,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
|
background: var(--session-progress-color);
|
||||||
clip-path: inset(0 100% 0 0 round 999px);
|
clip-path: inset(0 100% 0 0 round 999px);
|
||||||
|
animation: session-progress-whip var(--session-progress-ms, 1800ms) infinite;
|
||||||
will-change: clip-path;
|
will-change: clip-path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ export { AppBaseProviders, AppInterface } from "./app"
|
|||||||
export { ACCEPTED_FILE_EXTENSIONS, ACCEPTED_FILE_TYPES, filePickerFilters } from "./constants/file-picker"
|
export { ACCEPTED_FILE_EXTENSIONS, ACCEPTED_FILE_TYPES, filePickerFilters } from "./constants/file-picker"
|
||||||
export { useCommand } from "./context/command"
|
export { useCommand } from "./context/command"
|
||||||
export { loadLocaleDict, normalizeLocale, type Locale } from "./context/language"
|
export { loadLocaleDict, normalizeLocale, type Locale } from "./context/language"
|
||||||
export { type DisplayBackend, type FatalRendererErrorLog, type Platform, PlatformProvider } from "./context/platform"
|
export { type DisplayBackend, type Platform, PlatformProvider } from "./context/platform"
|
||||||
export { ServerConnection } from "./context/server"
|
export { ServerConnection } from "./context/server"
|
||||||
export { handleNotificationClick } from "./utils/notification-click"
|
export { handleNotificationClick } from "./utils/notification-click"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { createEffect, createMemo, createResource, type ParentProps, Show } from
|
|||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { LocalProvider } from "@/context/local"
|
import { LocalProvider } from "@/context/local"
|
||||||
import { SDKProvider } from "@/context/sdk"
|
import { SDKProvider } from "@/context/sdk"
|
||||||
import { useSync } from "@/context/sync"
|
import { SyncProvider, useSync } from "@/context/sync"
|
||||||
import { decode64 } from "@/utils/base64"
|
import { decode64 } from "@/utils/base64"
|
||||||
import { Schema } from "effect"
|
import { Schema } from "effect"
|
||||||
|
|
||||||
@@ -81,8 +81,10 @@ export default function Layout(props: ParentProps) {
|
|||||||
return (
|
return (
|
||||||
<Show when={resolved()} keyed>
|
<Show when={resolved()} keyed>
|
||||||
{(resolved) => (
|
{(resolved) => (
|
||||||
<SDKProvider directory={resolved}>
|
<SDKProvider directory={() => resolved}>
|
||||||
<DirectoryDataProvider directory={resolved}>{props.children}</DirectoryDataProvider>
|
<SyncProvider>
|
||||||
|
<DirectoryDataProvider directory={resolved}>{props.children}</DirectoryDataProvider>
|
||||||
|
</SyncProvider>
|
||||||
</SDKProvider>
|
</SDKProvider>
|
||||||
)}
|
)}
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { TextField } from "@opencode-ai/ui/text-field"
|
|||||||
import * as Sentry from "@sentry/solid"
|
import * as Sentry from "@sentry/solid"
|
||||||
import { Logo } from "@opencode-ai/ui/logo"
|
import { Logo } from "@opencode-ai/ui/logo"
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Component, createSignal, onMount, Show } from "solid-js"
|
import { Component, createSignal, Show } from "solid-js"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
import { usePlatform } from "@/context/platform"
|
import { usePlatform } from "@/context/platform"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
@@ -221,30 +221,12 @@ interface ErrorPageProps {
|
|||||||
export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const formattedError = () => formatError(props.error, language.t)
|
|
||||||
let recordedFatalError: Promise<void> | undefined
|
|
||||||
const [store, setStore] = createStore({
|
const [store, setStore] = createStore({
|
||||||
checking: false,
|
checking: false,
|
||||||
version: undefined as string | undefined,
|
version: undefined as string | undefined,
|
||||||
actionError: undefined as string | undefined,
|
actionError: undefined as string | undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
function ensureFatalErrorRecorded() {
|
|
||||||
recordedFatalError ??=
|
|
||||||
platform.recordFatalRendererError?.({
|
|
||||||
error: formattedError(),
|
|
||||||
url: location.href,
|
|
||||||
version: platform.version,
|
|
||||||
platform: platform.platform,
|
|
||||||
os: platform.os,
|
|
||||||
}) ?? Promise.resolve()
|
|
||||||
return recordedFatalError
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
void ensureFatalErrorRecorded().catch(() => undefined)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function checkForUpdates() {
|
async function checkForUpdates() {
|
||||||
if (!platform.checkUpdate) return
|
if (!platform.checkUpdate) return
|
||||||
setStore("checking", true)
|
setStore("checking", true)
|
||||||
@@ -272,17 +254,6 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function exportDebugLogs() {
|
|
||||||
const exportLogs = platform.exportDebugLogs
|
|
||||||
if (!exportLogs) return
|
|
||||||
await ensureFatalErrorRecorded()
|
|
||||||
.then(() => exportLogs())
|
|
||||||
.then(() => setStore("actionError", undefined))
|
|
||||||
.catch((err) => {
|
|
||||||
setStore("actionError", formatError(err, language.t))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative flex-1 h-screen w-screen min-h-0 flex flex-col items-center justify-center bg-background-base font-sans">
|
<div class="relative flex-1 h-screen w-screen min-h-0 flex flex-col items-center justify-center bg-background-base font-sans">
|
||||||
<div class="w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8">
|
<div class="w-2/3 max-w-3xl flex flex-col items-center justify-center gap-8">
|
||||||
@@ -292,7 +263,7 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
|||||||
<p class="text-sm text-text-weak">{language.t("error.page.description")}</p>
|
<p class="text-sm text-text-weak">{language.t("error.page.description")}</p>
|
||||||
</div>
|
</div>
|
||||||
<TextField
|
<TextField
|
||||||
value={formattedError()}
|
value={formatError(props.error, language.t)}
|
||||||
readOnly
|
readOnly
|
||||||
copyable
|
copyable
|
||||||
multiline
|
multiline
|
||||||
@@ -304,11 +275,6 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
|
|||||||
<Button size="large" onClick={platform.restart}>
|
<Button size="large" onClick={platform.restart}>
|
||||||
{language.t("error.page.action.restart")}
|
{language.t("error.page.action.restart")}
|
||||||
</Button>
|
</Button>
|
||||||
<Show when={platform.platform === "desktop" && platform.exportDebugLogs}>
|
|
||||||
<Button size="large" variant="ghost" onClick={exportDebugLogs}>
|
|
||||||
{language.t("error.page.action.exportLogs")}
|
|
||||||
</Button>
|
|
||||||
</Show>
|
|
||||||
<Show when={Sentry.isEnabled}>
|
<Show when={Sentry.isEnabled}>
|
||||||
{(_) => {
|
{(_) => {
|
||||||
const [reported, setReported] = createSignal(false)
|
const [reported, setReported] = createSignal(false)
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
import type { Session } from "@opencode-ai/sdk/v2/client"
|
import { createMemo, For, Match, Switch } from "solid-js"
|
||||||
import { createMemo, For, Match, Show, Switch } from "solid-js"
|
|
||||||
import { createStore } from "solid-js/store"
|
|
||||||
import { useQuery } from "@tanstack/solid-query"
|
|
||||||
import { Button } from "@opencode-ai/ui/button"
|
import { Button } from "@opencode-ai/ui/button"
|
||||||
import { Logo } from "@opencode-ai/ui/logo"
|
import { Logo } from "@opencode-ai/ui/logo"
|
||||||
import { Spinner } from "@opencode-ai/ui/spinner"
|
import { useLayout } from "@/context/layout"
|
||||||
import { Avatar as AvatarV2 } from "@opencode-ai/ui/v2/components/avatar-v2.jsx"
|
|
||||||
import { ButtonV2 } from "@opencode-ai/ui/v2/components/button-v2.jsx"
|
|
||||||
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
|
|
||||||
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
|
|
||||||
import { getAvatarColors, useLayout, type LocalProject } from "@/context/layout"
|
|
||||||
import { useNavigate } from "@solidjs/router"
|
import { useNavigate } from "@solidjs/router"
|
||||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||||
import { Icon } from "@opencode-ai/ui/icon"
|
import { Icon } from "@opencode-ai/ui/icon"
|
||||||
@@ -21,444 +13,8 @@ import { DialogSelectServer } from "@/components/dialog-select-server"
|
|||||||
import { useServer } from "@/context/server"
|
import { useServer } from "@/context/server"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { useNotification } from "@/context/notification"
|
|
||||||
import { usePermission } from "@/context/permission"
|
|
||||||
import { displayName, getProjectAvatarSource, projectForSession, sortedRootSessions } from "@/pages/layout/helpers"
|
|
||||||
import { getFilename } from "@opencode-ai/core/util/path"
|
|
||||||
import { sessionTitle } from "@/utils/session-title"
|
|
||||||
import { pathKey } from "@/utils/path-key"
|
|
||||||
import { messageAgentColor } from "@/utils/agent"
|
|
||||||
import { sessionPermissionRequest } from "@/pages/session/composer/session-request-tree"
|
|
||||||
|
|
||||||
const USE_HOME_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
|
|
||||||
const HOME_SESSION_LIMIT = 15
|
|
||||||
const HOME_ROW =
|
|
||||||
"flex min-w-0 w-full shrink-0 cursor-default items-center rounded-[6px] border-0 bg-transparent text-left [font-weight:530] text-v2-text-text-muted transition-colors duration-[120ms] ease-in-out hover:bg-v2-overlay-simple-overlay-hover focus-visible:bg-v2-overlay-simple-overlay-hover focus-visible:outline-none"
|
|
||||||
const HOME_PROJECT_NAV_ROW = `${HOME_ROW} h-8 gap-1.5 px-3 [&>span]:min-w-0 [&>span]:overflow-hidden [&>span]:text-ellipsis [&>span]:whitespace-nowrap`
|
|
||||||
const HOME_SECTION_LABEL = "text-v2-text-text-muted [font-weight:440]"
|
|
||||||
|
|
||||||
type HomeSessionRecord = {
|
|
||||||
session: Session
|
|
||||||
project: LocalProject
|
|
||||||
projectName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
type HomeSessionGroup = {
|
|
||||||
id: "today" | "yesterday" | "older"
|
|
||||||
title: string
|
|
||||||
sessions: HomeSessionRecord[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
if (USE_HOME_DESIGN) return <HomeDesign />
|
|
||||||
return <LegacyHome />
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeDesign() {
|
|
||||||
const sync = useGlobalSync()
|
|
||||||
const layout = useLayout()
|
|
||||||
const platform = usePlatform()
|
|
||||||
const dialog = useDialog()
|
|
||||||
const navigate = useNavigate()
|
|
||||||
const server = useServer()
|
|
||||||
const language = useLanguage()
|
|
||||||
const [state, setState] = createStore({ search: "", project: undefined as string | undefined })
|
|
||||||
|
|
||||||
const projects = createMemo(() => layout.projects.list())
|
|
||||||
const selectedProject = createMemo(
|
|
||||||
() => projects().find((project) => project.worktree === state.project) ?? projects()[0],
|
|
||||||
)
|
|
||||||
const projectDirectories = createMemo(() => {
|
|
||||||
const project = selectedProject()
|
|
||||||
if (!project) return []
|
|
||||||
return [project.worktree, ...(project.sandboxes ?? [])]
|
|
||||||
})
|
|
||||||
const search = createMemo(() => state.search.trim())
|
|
||||||
const sessionLoad = useQuery(() => ({
|
|
||||||
queryKey: ["home", "sessions", ...projectDirectories()] as const,
|
|
||||||
queryFn: async () => {
|
|
||||||
await Promise.all(projectDirectories().map((directory) => sync.project.loadSessions(directory)))
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
const projectByID = createMemo(
|
|
||||||
() => new Map(projects().flatMap((project) => (project.id ? [[project.id, project] as const] : []))),
|
|
||||||
)
|
|
||||||
const records = createMemo(() =>
|
|
||||||
[
|
|
||||||
...new Map(
|
|
||||||
projectDirectories()
|
|
||||||
.flatMap((directory) => sortedRootSessions(sync.child(directory, { bootstrap: false })[0], Date.now()))
|
|
||||||
.map((session) => [`${pathKey(session.directory)}:${session.id}`, session] as const),
|
|
||||||
).values(),
|
|
||||||
]
|
|
||||||
.sort((a, b) => (b.time.updated ?? b.time.created) - (a.time.updated ?? a.time.created))
|
|
||||||
.flatMap((session) => {
|
|
||||||
const project = projectForSession(session, projects(), projectByID())
|
|
||||||
if (!project) return []
|
|
||||||
return {
|
|
||||||
session,
|
|
||||||
project,
|
|
||||||
projectName: displayName(project),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter((record) => {
|
|
||||||
const value = search().toLowerCase()
|
|
||||||
if (!value) return true
|
|
||||||
return `${record.session.title} ${record.projectName}`.toLowerCase().includes(value)
|
|
||||||
})
|
|
||||||
.slice(0, HOME_SESSION_LIMIT),
|
|
||||||
)
|
|
||||||
const groups = createMemo(() => groupSessions(records(), language))
|
|
||||||
|
|
||||||
function selectProject(directory: string) {
|
|
||||||
if (!projects().some((project) => project.worktree === directory)) return
|
|
||||||
setState("project", directory)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addProject(directory: string) {
|
|
||||||
layout.projects.open(directory)
|
|
||||||
server.projects.touch(directory)
|
|
||||||
setState("project", directory)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openNewSession() {
|
|
||||||
const project = selectedProject()
|
|
||||||
if (!project) {
|
|
||||||
void chooseProject()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
layout.projects.open(project.worktree)
|
|
||||||
server.projects.touch(project.worktree)
|
|
||||||
navigate(`/${base64Encode(project.worktree)}/session`)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openSession(session: Session) {
|
|
||||||
const project = projectForSession(session, projects(), projectByID())
|
|
||||||
layout.projects.open(project?.worktree ?? session.directory)
|
|
||||||
server.projects.touch(project?.worktree ?? session.directory)
|
|
||||||
navigate(`/${base64Encode(session.directory)}/session/${session.id}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function chooseProject() {
|
|
||||||
function resolve(result: string | string[] | null) {
|
|
||||||
if (Array.isArray(result)) {
|
|
||||||
result.forEach(addProject)
|
|
||||||
if (result[0]) setState("project", result[0])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (result) addProject(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platform.openDirectoryPickerDialog && server.isLocal()) {
|
|
||||||
const result = await platform.openDirectoryPickerDialog?.({
|
|
||||||
title: language.t("command.project.open"),
|
|
||||||
multiple: true,
|
|
||||||
})
|
|
||||||
resolve(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.show(
|
|
||||||
() => <DialogSelectDirectory multiple={true} onSelect={resolve} />,
|
|
||||||
() => resolve(null),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openSettings() {
|
|
||||||
void import("@/components/dialog-settings").then((x) => {
|
|
||||||
dialog.show(() => <x.DialogSettings />)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="mx-auto grid w-full h-full max-w-[1080px] gap-8 px-6 pb-16 lg:grid-cols-[280px_minmax(0,720px)]">
|
|
||||||
<HomeProjectColumn
|
|
||||||
projects={projects()}
|
|
||||||
selected={selectedProject()?.worktree}
|
|
||||||
selectProject={selectProject}
|
|
||||||
chooseProject={() => void chooseProject()}
|
|
||||||
openSettings={openSettings}
|
|
||||||
openHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")}
|
|
||||||
language={language}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<section
|
|
||||||
class="min-w-0 flex-1 flex flex-col overflow-y-hidden pt-12"
|
|
||||||
aria-label={language.t("sidebar.project.recentSessions")}
|
|
||||||
>
|
|
||||||
<HomeSessionSearch
|
|
||||||
value={state.search}
|
|
||||||
placeholder={language.t("home.sessions.search.placeholder")}
|
|
||||||
onInput={(value) => setState("search", value)}
|
|
||||||
/>
|
|
||||||
<div class="mt-3 overflow-auto flex-1">
|
|
||||||
<div class="pt-3 flex flex-col gap-6">
|
|
||||||
<Show when={!sessionLoad.isLoading} fallback={<HomeSessionSkeleton label={language.t("common.loading")} />}>
|
|
||||||
<Show
|
|
||||||
when={groups().length > 0}
|
|
||||||
fallback={
|
|
||||||
<div class="flex min-w-0 flex-col gap-4">
|
|
||||||
<HomeSessionGroupHeader title={language.t("home.sessions.empty")} onNewSession={openNewSession} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<For each={groups()}>
|
|
||||||
{(group, index) => (
|
|
||||||
<div class="flex min-w-0 flex-col gap-4">
|
|
||||||
<HomeSessionGroupHeader
|
|
||||||
title={group.title}
|
|
||||||
onNewSession={index() === 0 ? openNewSession : undefined}
|
|
||||||
/>
|
|
||||||
<div class="flex min-w-0 flex-col gap-px">
|
|
||||||
<For each={group.sessions}>
|
|
||||||
{(record) => <HomeSessionRow record={record} openSession={openSession} />}
|
|
||||||
</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeProjectColumn(props: {
|
|
||||||
projects: LocalProject[]
|
|
||||||
selected?: string
|
|
||||||
selectProject: (directory: string) => void
|
|
||||||
chooseProject: () => void
|
|
||||||
openSettings: () => void
|
|
||||||
openHelp: () => void
|
|
||||||
language: ReturnType<typeof useLanguage>
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<aside class="flex min-w-0 flex-col lg:pt-[52px]" aria-label={props.language.t("home.projects")}>
|
|
||||||
<div class="flex h-7 min-w-0 items-center justify-between pl-3">
|
|
||||||
<div class={HOME_SECTION_LABEL}>{props.language.t("home.projects")}</div>
|
|
||||||
<IconButtonV2
|
|
||||||
data-action="home-add-project"
|
|
||||||
variant="ghost-muted"
|
|
||||||
size="large"
|
|
||||||
class="titlebar-icon [&_[data-slot=icon-svg]]:text-v2-icon-icon-muted"
|
|
||||||
icon={<IconV2 name="folder-add-left" />}
|
|
||||||
onClick={props.chooseProject}
|
|
||||||
aria-label={props.language.t("home.project.add")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="mt-4 flex max-h-[min(572px,calc(100vh_-_300px))] min-w-0 flex-col gap-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
|
|
||||||
<Show
|
|
||||||
when={props.projects.length > 0}
|
|
||||||
fallback={
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
|
||||||
onClick={props.chooseProject}
|
|
||||||
>
|
|
||||||
<IconV2 name="folder-add-left" size="small" />
|
|
||||||
<span>{props.language.t("home.project.add")}</span>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<For each={props.projects}>
|
|
||||||
{(project) => (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-component="home-project-row"
|
|
||||||
class={HOME_PROJECT_NAV_ROW}
|
|
||||||
classList={{ "bg-v2-overlay-simple-overlay-hover": props.selected === project.worktree }}
|
|
||||||
data-selected={props.selected === project.worktree ? "" : undefined}
|
|
||||||
aria-current={props.selected === project.worktree ? "page" : undefined}
|
|
||||||
onClick={() => props.selectProject(project.worktree)}
|
|
||||||
>
|
|
||||||
<HomeProjectAvatar project={project} />
|
|
||||||
<span>{displayName(project)}</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
<div class="mt-4 flex min-w-0 flex-col gap-1">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
|
||||||
onClick={props.openSettings}
|
|
||||||
>
|
|
||||||
<IconV2 name="settings-gear" size="small" />
|
|
||||||
<span>{props.language.t("sidebar.settings")}</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class={`${HOME_PROJECT_NAV_ROW} text-v2-text-text-faint [&>[data-slot=icon-svg]]:text-v2-icon-icon-muted`}
|
|
||||||
onClick={props.openHelp}
|
|
||||||
>
|
|
||||||
<IconV2 name="help" size="small" />
|
|
||||||
<span>{props.language.t("sidebar.help")}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeProjectAvatar(props: { project: LocalProject }) {
|
|
||||||
const name = createMemo(() => displayName(props.project))
|
|
||||||
return (
|
|
||||||
<AvatarV2
|
|
||||||
fallback={name()}
|
|
||||||
src={getProjectAvatarSource(props.project.id, props.project.icon)}
|
|
||||||
kind="org"
|
|
||||||
size="small"
|
|
||||||
{...getAvatarColors(props.project.icon?.color)}
|
|
||||||
class="size-4 rounded"
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeSessionSearch(props: { value: string; placeholder: string; onInput: (value: string) => void }) {
|
|
||||||
return (
|
|
||||||
<label class="ml-4 flex h-9 w-[calc(100%_-_48px)] sticky top-0 inset-x-0 items-center gap-2 rounded-[6px] bg-v2-background-bg-deep px-3 py-1 text-v2-icon-icon-muted transition-[background-color,box-shadow] duration-[120ms] ease-in-out focus-within:bg-v2-background-bg-base focus-within:shadow-[0_0_0_0.5px_var(--v2-border-border-focus),var(--v2-elevation-raised)]">
|
|
||||||
<IconV2 name="magnifying-glass" size="small" />
|
|
||||||
<input
|
|
||||||
class="min-w-0 flex-1 border-0 bg-transparent text-v2-text-text-base outline-0 [font-weight:440] placeholder:text-v2-text-text-faint"
|
|
||||||
value={props.value}
|
|
||||||
placeholder={props.placeholder}
|
|
||||||
aria-label={props.placeholder}
|
|
||||||
onInput={(event) => props.onInput(event.currentTarget.value)}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeSessionGroupHeader(props: { title: string; onNewSession?: () => void }) {
|
|
||||||
const language = useLanguage()
|
|
||||||
return (
|
|
||||||
<div class="flex h-7 min-w-0 items-center justify-between px-4">
|
|
||||||
<div class={HOME_SECTION_LABEL}>{props.title}</div>
|
|
||||||
<Show when={props.onNewSession}>
|
|
||||||
{(onNewSession) => (
|
|
||||||
<ButtonV2
|
|
||||||
data-action="home-new-session"
|
|
||||||
variant="ghost"
|
|
||||||
size="normal"
|
|
||||||
icon="edit"
|
|
||||||
class="h-7 px-2 text-v2-text-text-muted [font-weight:530]"
|
|
||||||
onClick={onNewSession()}
|
|
||||||
>
|
|
||||||
{language.t("command.session.new")}
|
|
||||||
</ButtonV2>
|
|
||||||
)}
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeSessionRow(props: { record: HomeSessionRecord; openSession: (session: Session) => void }) {
|
|
||||||
const globalSync = useGlobalSync()
|
|
||||||
const notification = useNotification()
|
|
||||||
const permission = usePermission()
|
|
||||||
const [sessionStore] = globalSync.child(props.record.session.directory, { bootstrap: false })
|
|
||||||
const title = createMemo(() => sessionTitle(props.record.session.title) || props.record.session.id)
|
|
||||||
const unseenCount = createMemo(() => notification.session.unseenCount(props.record.session.id))
|
|
||||||
const hasError = createMemo(() => notification.session.unseenHasError(props.record.session.id))
|
|
||||||
const hasPermissions = createMemo(
|
|
||||||
() =>
|
|
||||||
!!sessionPermissionRequest(sessionStore.session, sessionStore.permission, props.record.session.id, (item) => {
|
|
||||||
return !permission.autoResponds(item, props.record.session.directory)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
const isWorking = createMemo(() => {
|
|
||||||
if (hasPermissions()) return false
|
|
||||||
return sessionStore.session_working(props.record.session.id)
|
|
||||||
})
|
|
||||||
const tint = createMemo(() => messageAgentColor(sessionStore.message[props.record.session.id], sessionStore.agent))
|
|
||||||
const showStatus = createMemo(() => isWorking() || hasPermissions() || hasError() || unseenCount() > 0)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-component="home-session-row"
|
|
||||||
class={`${HOME_ROW} h-10 gap-2 px-6 py-3 pl-4`}
|
|
||||||
onClick={() => props.openSession(props.record.session)}
|
|
||||||
>
|
|
||||||
<Show when={showStatus()}>
|
|
||||||
<div
|
|
||||||
class="flex size-4 shrink-0 items-center justify-center"
|
|
||||||
style={{ color: tint() ?? "var(--icon-interactive-base)" }}
|
|
||||||
>
|
|
||||||
<Switch>
|
|
||||||
<Match when={isWorking()}>
|
|
||||||
<Spinner class="size-[15px]" />
|
|
||||||
</Match>
|
|
||||||
<Match when={hasPermissions()}>
|
|
||||||
<div class="size-1.5 rounded-full bg-surface-warning-strong" />
|
|
||||||
</Match>
|
|
||||||
<Match when={hasError()}>
|
|
||||||
<div class="size-1.5 rounded-full bg-text-diff-delete-base" />
|
|
||||||
</Match>
|
|
||||||
<Match when={unseenCount() > 0}>
|
|
||||||
<div class="size-1.5 rounded-full bg-text-interactive-base" />
|
|
||||||
</Match>
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
<span
|
|
||||||
class={`min-w-0 overflow-hidden text-ellipsis whitespace-nowrap text-v2-text-text-base [font-weight:530] ${props.record.projectName ? "max-w-[min(70%,480px)] flex-[0_1_auto]" : "flex-[1_1_auto]"}`}
|
|
||||||
>
|
|
||||||
{title()}
|
|
||||||
</span>
|
|
||||||
<Show when={props.record.projectName}>
|
|
||||||
<span class="min-w-0 flex-[1_1_auto] overflow-hidden text-ellipsis whitespace-nowrap text-v2-text-text-muted [font-weight:440]">
|
|
||||||
{props.record.projectName}
|
|
||||||
</span>
|
|
||||||
</Show>
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function HomeSessionSkeleton(props: { label: string }) {
|
|
||||||
return (
|
|
||||||
<div class="flex min-w-0 flex-col gap-4">
|
|
||||||
<div class="flex h-7 min-w-0 items-center justify-between px-4">
|
|
||||||
<div class={HOME_SECTION_LABEL}>{props.label}</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex min-w-0 flex-col gap-px" aria-hidden="true">
|
|
||||||
<For each={[0, 1, 2, 3]}>{() => <div class="h-10 rounded-[6px] bg-v2-background-bg-deep opacity-70" />}</For>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function groupSessions(records: HomeSessionRecord[], language: ReturnType<typeof useLanguage>): HomeSessionGroup[] {
|
|
||||||
const now = DateTime.local()
|
|
||||||
const yesterday = now.minus({ days: 1 })
|
|
||||||
const todaySessions = records.filter((record) =>
|
|
||||||
DateTime.fromMillis(record.session.time.updated ?? record.session.time.created).hasSame(now, "day"),
|
|
||||||
)
|
|
||||||
const yesterdaySessions = records.filter((record) =>
|
|
||||||
DateTime.fromMillis(record.session.time.updated ?? record.session.time.created).hasSame(yesterday, "day"),
|
|
||||||
)
|
|
||||||
const olderSessions = records.filter((record) => {
|
|
||||||
const time = DateTime.fromMillis(record.session.time.updated ?? record.session.time.created)
|
|
||||||
return !time.hasSame(now, "day") && !time.hasSame(yesterday, "day")
|
|
||||||
})
|
|
||||||
const olderTitle =
|
|
||||||
todaySessions.length === 0 && yesterdaySessions.length === 0
|
|
||||||
? language.t("sidebar.project.recentSessions")
|
|
||||||
: language.t("home.sessions.group.older")
|
|
||||||
|
|
||||||
return [
|
|
||||||
{ id: "today" as const, title: language.t("home.sessions.group.today"), sessions: todaySessions },
|
|
||||||
{ id: "yesterday" as const, title: language.t("home.sessions.group.yesterday"), sessions: yesterdaySessions },
|
|
||||||
{ id: "older" as const, title: olderTitle, sessions: olderSessions },
|
|
||||||
].filter((group) => group.sessions.length > 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
function LegacyHome() {
|
|
||||||
const sync = useGlobalSync()
|
const sync = useGlobalSync()
|
||||||
const layout = useLayout()
|
const layout = useLayout()
|
||||||
const platform = usePlatform()
|
const platform = usePlatform()
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from "solid-js"
|
} from "solid-js"
|
||||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||||
import { useLocation, useNavigate, useParams } from "@solidjs/router"
|
import { useLocation, useNavigate, useParams } from "@solidjs/router"
|
||||||
import { useQuery } from "@tanstack/solid-query"
|
|
||||||
import { useLayout, LocalProject } from "@/context/layout"
|
import { useLayout, LocalProject } from "@/context/layout"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
@@ -62,7 +61,7 @@ import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
|
|||||||
import { useCommand, type CommandOption } from "@/context/command"
|
import { useCommand, type CommandOption } from "@/context/command"
|
||||||
import { ConstrainDragXAxis, getDraggableId } from "@/utils/solid-dnd"
|
import { ConstrainDragXAxis, getDraggableId } from "@/utils/solid-dnd"
|
||||||
import { DebugBar } from "@/components/debug-bar"
|
import { DebugBar } from "@/components/debug-bar"
|
||||||
import { Titlebar, type TitlebarUpdate } from "@/components/titlebar"
|
import { Titlebar } from "@/components/titlebar"
|
||||||
import { useServer } from "@/context/server"
|
import { useServer } from "@/context/server"
|
||||||
import { useLanguage, type Locale } from "@/context/language"
|
import { useLanguage, type Locale } from "@/context/language"
|
||||||
import { pathKey } from "@/utils/path-key"
|
import { pathKey } from "@/utils/path-key"
|
||||||
@@ -89,8 +88,6 @@ import {
|
|||||||
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
|
import { ProjectDragOverlay, SortableProject, type ProjectSidebarContext } from "./layout/sidebar-project"
|
||||||
import { SidebarContent } from "./layout/sidebar-shell"
|
import { SidebarContent } from "./layout/sidebar-shell"
|
||||||
|
|
||||||
const USE_NEW_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
|
|
||||||
|
|
||||||
export default function Layout(props: ParentProps) {
|
export default function Layout(props: ParentProps) {
|
||||||
const [store, setStore, , ready] = persisted(
|
const [store, setStore, , ready] = persisted(
|
||||||
Persist.global("layout.page", ["layout.page.v1"]),
|
Persist.global("layout.page", ["layout.page.v1"]),
|
||||||
@@ -154,7 +151,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
const currentDir = createMemo(() => route().dir)
|
const currentDir = createMemo(() => route().dir)
|
||||||
|
|
||||||
const [state, setState] = createStore({
|
const [state, setState] = createStore({
|
||||||
autoselect: !initialDirectory && !USE_NEW_DESIGN,
|
autoselect: !initialDirectory,
|
||||||
busyWorkspaces: {} as Record<string, boolean>,
|
busyWorkspaces: {} as Record<string, boolean>,
|
||||||
hoverProject: undefined as string | undefined,
|
hoverProject: undefined as string | undefined,
|
||||||
scrollSessionKey: undefined as string | undefined,
|
scrollSessionKey: undefined as string | undefined,
|
||||||
@@ -165,35 +162,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
peeked: false,
|
peeked: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [update, setUpdate] = createStore({
|
|
||||||
installing: false,
|
|
||||||
})
|
|
||||||
const updateQuery = useQuery(() => ({
|
|
||||||
queryKey: ["desktop", "update"] as const,
|
|
||||||
enabled: () =>
|
|
||||||
!!platform.checkUpdate && !!platform.updateAndRestart && settings.ready() && settings.updates.startup(),
|
|
||||||
queryFn: () => platform.checkUpdate?.() ?? Promise.resolve({ updateAvailable: false, version: undefined }),
|
|
||||||
refetchInterval: (query) => (query.state.data?.updateAvailable ? false : 10 * 60 * 1000),
|
|
||||||
}))
|
|
||||||
const updateVersion = () => {
|
|
||||||
if (!settings.ready()) return
|
|
||||||
if (!settings.updates.startup()) return
|
|
||||||
if (!updateQuery.data?.updateAvailable) return
|
|
||||||
return updateQuery.data.version ?? ""
|
|
||||||
}
|
|
||||||
const installUpdate = () => {
|
|
||||||
if (!platform.updateAndRestart) return
|
|
||||||
setUpdate("installing", true)
|
|
||||||
void platform.updateAndRestart().catch(() => {
|
|
||||||
setUpdate("installing", false)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const titlebarUpdate: TitlebarUpdate = {
|
|
||||||
version: updateVersion,
|
|
||||||
installing: () => update.installing,
|
|
||||||
install: installUpdate,
|
|
||||||
}
|
|
||||||
|
|
||||||
const editor = createInlineEditorController()
|
const editor = createInlineEditorController()
|
||||||
const setBusy = (directory: string, value: boolean) => {
|
const setBusy = (directory: string, value: boolean) => {
|
||||||
const key = pathKey(directory)
|
const key = pathKey(directory)
|
||||||
@@ -396,6 +364,58 @@ export default function Layout(props: ParentProps) {
|
|||||||
setLocale(next)
|
setLocale(next)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useUpdatePolling = () =>
|
||||||
|
onMount(() => {
|
||||||
|
if (!platform.checkUpdate || !platform.updateAndRestart) return
|
||||||
|
|
||||||
|
let toastId: number | undefined
|
||||||
|
let interval: ReturnType<typeof setInterval> | undefined
|
||||||
|
|
||||||
|
const pollUpdate = () =>
|
||||||
|
platform.checkUpdate!().then(({ updateAvailable, version }) => {
|
||||||
|
if (!updateAvailable) return
|
||||||
|
if (toastId !== undefined) return
|
||||||
|
toastId = showToast({
|
||||||
|
persistent: true,
|
||||||
|
icon: "download",
|
||||||
|
title: language.t("toast.update.title"),
|
||||||
|
description: language.t("toast.update.description", { version: version ?? "" }),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: language.t("toast.update.action.installRestart"),
|
||||||
|
onClick: async () => {
|
||||||
|
await platform.updateAndRestart!()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: language.t("toast.update.action.notYet"),
|
||||||
|
onClick: "dismiss",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
if (!settings.ready()) return
|
||||||
|
|
||||||
|
if (!settings.updates.startup()) {
|
||||||
|
if (interval === undefined) return
|
||||||
|
clearInterval(interval)
|
||||||
|
interval = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (interval !== undefined) return
|
||||||
|
void pollUpdate()
|
||||||
|
interval = setInterval(pollUpdate, 10 * 60 * 1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
if (interval === undefined) return
|
||||||
|
clearInterval(interval)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const useSDKNotificationToasts = () =>
|
const useSDKNotificationToasts = () =>
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const toastBySession = new Map<string, number>()
|
const toastBySession = new Map<string, number>()
|
||||||
@@ -515,6 +535,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useUpdatePolling()
|
||||||
useSDKNotificationToasts()
|
useSDKNotificationToasts()
|
||||||
|
|
||||||
function scrollToSession(sessionId: string, sessionKey: string) {
|
function scrollToSession(sessionId: string, sessionKey: string) {
|
||||||
@@ -1028,6 +1049,19 @@ export default function Layout(props: ParentProps) {
|
|||||||
keybind: "mod+alt+arrowdown",
|
keybind: "mod+alt+arrowdown",
|
||||||
onSelect: () => navigateProjectByOffset(1),
|
onSelect: () => navigateProjectByOffset(1),
|
||||||
},
|
},
|
||||||
|
...Array.from({ length: 9 }, (_, i) => {
|
||||||
|
const index = i
|
||||||
|
const number = index + 1
|
||||||
|
return {
|
||||||
|
id: `project.${number}`,
|
||||||
|
category: language.t("command.category.project"),
|
||||||
|
title: `Open Project {number}`,
|
||||||
|
keybind: `mod+${number}`,
|
||||||
|
disabled: layout.projects.list().length <= index,
|
||||||
|
hidden: true,
|
||||||
|
onSelect: () => navigateToProjectIndex(index),
|
||||||
|
}
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
id: "provider.connect",
|
id: "provider.connect",
|
||||||
title: language.t("command.provider.connect"),
|
title: language.t("command.provider.connect"),
|
||||||
@@ -1047,18 +1081,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
keybind: "mod+comma",
|
keybind: "mod+comma",
|
||||||
onSelect: () => openSettings(),
|
onSelect: () => openSettings(),
|
||||||
},
|
},
|
||||||
...(platform.platform === "desktop" && platform.exportDebugLogs
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
id: "logs.export",
|
|
||||||
title: "Export logs",
|
|
||||||
category: language.t("command.category.settings"),
|
|
||||||
onSelect: () => {
|
|
||||||
void platform.exportDebugLogs?.()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []),
|
|
||||||
{
|
{
|
||||||
id: "session.previous",
|
id: "session.previous",
|
||||||
title: language.t("command.session.previous"),
|
title: language.t("command.session.previous"),
|
||||||
@@ -1142,21 +1164,6 @@ export default function Layout(props: ParentProps) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
if (!USE_NEW_DESIGN)
|
|
||||||
Array.from({ length: 9 }, (_, i) => {
|
|
||||||
const index = i
|
|
||||||
const number = index + 1
|
|
||||||
commands.push({
|
|
||||||
id: `project.${number}`,
|
|
||||||
category: language.t("command.category.project"),
|
|
||||||
title: `Open Project {number}`,
|
|
||||||
keybind: `mod+${number}`,
|
|
||||||
disabled: layout.projects.list().length <= index,
|
|
||||||
hidden: true,
|
|
||||||
onSelect: () => navigateToProjectIndex(index),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const [id] of availableThemeEntries()) {
|
for (const [id] of availableThemeEntries()) {
|
||||||
commands.push({
|
commands.push({
|
||||||
id: `theme.set.${id}`,
|
id: `theme.set.${id}`,
|
||||||
@@ -1819,10 +1826,8 @@ export default function Layout(props: ParentProps) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
document.documentElement.style.setProperty(
|
const sidebarWidth = layout.sidebar.opened() ? layout.sidebar.width() : 48
|
||||||
"--dialog-left-margin",
|
document.documentElement.style.setProperty("--dialog-left-margin", `${sidebarWidth}px`)
|
||||||
USE_NEW_DESIGN ? "0px" : `${layout.sidebar.opened() ? layout.sidebar.width() : 48}px`,
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const side = createMemo(() => Math.max(layout.sidebar.width(), 244))
|
const side = createMemo(() => Math.max(layout.sidebar.width(), 244))
|
||||||
@@ -2305,7 +2310,7 @@ export default function Layout(props: ParentProps) {
|
|||||||
<div
|
<div
|
||||||
class="shrink-0 px-3 py-3"
|
class="shrink-0 px-3 py-3"
|
||||||
classList={{
|
classList={{
|
||||||
hidden: store.gettingStartedDismissed || !(providers.all().size > 0 && providers.paid().length === 0),
|
hidden: store.gettingStartedDismissed || !(providers.all().length > 0 && providers.paid().length === 0),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div class="rounded-xl bg-background-base shadow-xs-border-base" data-component="getting-started">
|
<div class="rounded-xl bg-background-base shadow-xs-border-base" data-component="getting-started">
|
||||||
@@ -2363,34 +2368,10 @@ export default function Layout(props: ParentProps) {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
if (USE_NEW_DESIGN) {
|
|
||||||
return (
|
|
||||||
<div class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
|
|
||||||
{autoselecting() ?? ""}
|
|
||||||
<Titlebar update={titlebarUpdate} />
|
|
||||||
<main
|
|
||||||
class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict bg-v2-background-bg-base"
|
|
||||||
classList={{
|
|
||||||
"m-2 mt-0 rounded-[10px] shadow-[var(--v2-elevation-raised)] overflow-hidden": !!params.id || !params.dir,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Show when={!autoselecting.loading} fallback={<div class="size-full" />}>
|
|
||||||
{props.children}
|
|
||||||
</Show>
|
|
||||||
</main>
|
|
||||||
{import.meta.env.DEV && <DebugBar />}
|
|
||||||
<Toast.Region />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative bg-background-base flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
|
<div class="relative bg-background-base flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
|
||||||
{autoselecting() ?? ""}
|
{autoselecting() ?? ""}
|
||||||
<Titlebar update={titlebarUpdate} />
|
<Titlebar />
|
||||||
<Show when={updateVersion() !== undefined}>
|
|
||||||
<UpdateAvailableToast version={updateVersion() ?? ""} install={installUpdate} language={language} />
|
|
||||||
</Show>
|
|
||||||
<div class="flex-1 min-h-0 min-w-0 flex">
|
<div class="flex-1 min-h-0 min-w-0 flex">
|
||||||
<div class="flex-1 min-h-0 relative">
|
<div class="flex-1 min-h-0 relative">
|
||||||
<div class="size-full relative overflow-x-hidden">
|
<div class="size-full relative overflow-x-hidden">
|
||||||
@@ -2538,37 +2519,3 @@ export default function Layout(props: ParentProps) {
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function UpdateAvailableToast(props: {
|
|
||||||
version: string
|
|
||||||
install: () => void
|
|
||||||
language: ReturnType<typeof useLanguage>
|
|
||||||
}) {
|
|
||||||
let toastId: number | undefined
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
toastId = showToast({
|
|
||||||
persistent: true,
|
|
||||||
icon: "download",
|
|
||||||
title: props.language.t("toast.update.title"),
|
|
||||||
description: props.language.t("toast.update.description", { version: props.version }),
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: props.language.t("toast.update.action.installRestart"),
|
|
||||||
onClick: props.install,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: props.language.t("toast.update.action.notYet"),
|
|
||||||
onClick: "dismiss",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
onCleanup(() => {
|
|
||||||
if (toastId === undefined) return
|
|
||||||
toaster.dismiss(toastId)
|
|
||||||
})
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -55,29 +55,6 @@ export const childSessionOnPath = (sessions: Session[] | undefined, rootID: stri
|
|||||||
export const displayName = (project: { name?: string; worktree: string }) =>
|
export const displayName = (project: { name?: string; worktree: string }) =>
|
||||||
project.name || getFilename(project.worktree)
|
project.name || getFilename(project.worktree)
|
||||||
|
|
||||||
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
|
|
||||||
|
|
||||||
export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
|
|
||||||
if (id === OPENCODE_PROJECT_ID) return "https://opencode.ai/favicon.svg"
|
|
||||||
if (icon?.override) return icon.override
|
|
||||||
if (icon?.color) return undefined
|
|
||||||
return icon?.url
|
|
||||||
}
|
|
||||||
|
|
||||||
export function projectForSession<T extends { id?: string; worktree: string; sandboxes?: string[] }>(
|
|
||||||
session: Session,
|
|
||||||
projects: T[],
|
|
||||||
byID: Map<string, T>,
|
|
||||||
) {
|
|
||||||
const direct = byID.get(session.projectID)
|
|
||||||
if (direct) return direct
|
|
||||||
const directory = pathKey(session.directory)
|
|
||||||
return projects.find(
|
|
||||||
(project) =>
|
|
||||||
pathKey(project.worktree) === directory || project.sandboxes?.some((sandbox) => pathKey(sandbox) === directory),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const errorMessage = (err: unknown, fallback: string) => {
|
export const errorMessage = (err: unknown, fallback: string) => {
|
||||||
if (err && typeof err === "object" && "data" in err) {
|
if (err && typeof err === "object" && "data" in err) {
|
||||||
const data = (err as { data?: { message?: string } }).data
|
const data = (err as { data?: { message?: string } }).data
|
||||||
|
|||||||
@@ -15,7 +15,16 @@ import { usePermission } from "@/context/permission"
|
|||||||
import { messageAgentColor } from "@/utils/agent"
|
import { messageAgentColor } from "@/utils/agent"
|
||||||
import { sessionTitle } from "@/utils/session-title"
|
import { sessionTitle } from "@/utils/session-title"
|
||||||
import { sessionPermissionRequest } from "../session/composer/session-request-tree"
|
import { sessionPermissionRequest } from "../session/composer/session-request-tree"
|
||||||
import { childSessionOnPath, getProjectAvatarSource, hasProjectPermissions } from "./helpers"
|
import { childSessionOnPath, hasProjectPermissions } from "./helpers"
|
||||||
|
|
||||||
|
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
|
||||||
|
|
||||||
|
export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
|
||||||
|
if (id === OPENCODE_PROJECT_ID) return "https://opencode.ai/favicon.svg"
|
||||||
|
if (icon?.override) return icon?.override
|
||||||
|
if (icon?.color) return undefined
|
||||||
|
return icon?.url
|
||||||
|
}
|
||||||
|
|
||||||
export const ProjectIcon = (props: {
|
export const ProjectIcon = (props: {
|
||||||
project: LocalProject
|
project: LocalProject
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
import { makeEventListener } from "@solid-primitives/event-listener"
|
import { makeEventListener } from "@solid-primitives/event-listener"
|
||||||
import { createMediaQuery } from "@solid-primitives/media"
|
import { createMediaQuery } from "@solid-primitives/media"
|
||||||
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
import { createResizeObserver } from "@solid-primitives/resize-observer"
|
||||||
import { debounce } from "@solid-primitives/scheduled"
|
|
||||||
import { useLocal } from "@/context/local"
|
import { useLocal } from "@/context/local"
|
||||||
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
|
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
|
||||||
import { createStore } from "solid-js/store"
|
import { createStore } from "solid-js/store"
|
||||||
@@ -31,7 +30,7 @@ import { Button } from "@opencode-ai/ui/button"
|
|||||||
import { showToast } from "@opencode-ai/ui/toast"
|
import { showToast } from "@opencode-ai/ui/toast"
|
||||||
import { checksum } from "@opencode-ai/core/util/encode"
|
import { checksum } from "@opencode-ai/core/util/encode"
|
||||||
import { useLocation, useSearchParams } from "@solidjs/router"
|
import { useLocation, useSearchParams } from "@solidjs/router"
|
||||||
import { NewSessionDesignView, NewSessionView, SessionHeader } from "@/components/session"
|
import { NewSessionView, SessionHeader } from "@/components/session"
|
||||||
import { useComments } from "@/context/comments"
|
import { useComments } from "@/context/comments"
|
||||||
import { getSessionPrefetch, SESSION_PREFETCH_TTL } from "@/context/global-sync/session-prefetch"
|
import { getSessionPrefetch, SESSION_PREFETCH_TTL } from "@/context/global-sync/session-prefetch"
|
||||||
import { useGlobalSync } from "@/context/global-sync"
|
import { useGlobalSync } from "@/context/global-sync"
|
||||||
@@ -59,7 +58,6 @@ import { SessionSidePanel } from "@/pages/session/session-side-panel"
|
|||||||
import { TerminalPanel } from "@/pages/session/terminal-panel"
|
import { TerminalPanel } from "@/pages/session/terminal-panel"
|
||||||
import { useSessionCommands } from "@/pages/session/use-session-commands"
|
import { useSessionCommands } from "@/pages/session/use-session-commands"
|
||||||
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
|
import { useSessionHashScroll } from "@/pages/session/use-session-hash-scroll"
|
||||||
import { shouldUseV2NewSessionPage } from "@/pages/session/new-session-layout"
|
|
||||||
import { Identifier } from "@/utils/id"
|
import { Identifier } from "@/utils/id"
|
||||||
import { diffs as list } from "@/utils/diffs"
|
import { diffs as list } from "@/utils/diffs"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
@@ -75,7 +73,6 @@ const emptyFollowups: FollowupItem[] = []
|
|||||||
|
|
||||||
type ChangeMode = "git" | "branch" | "turn"
|
type ChangeMode = "git" | "branch" | "turn"
|
||||||
type VcsMode = "git" | "branch"
|
type VcsMode = "git" | "branch"
|
||||||
const USE_NEW_SESSION_DESIGN = import.meta.env.VITE_OPENCODE_CHANNEL !== "prod"
|
|
||||||
|
|
||||||
type SessionHistoryWindowInput = {
|
type SessionHistoryWindowInput = {
|
||||||
sessionID: () => string | undefined
|
sessionID: () => string | undefined
|
||||||
@@ -264,10 +261,8 @@ export default function Page() {
|
|||||||
|
|
||||||
const isDesktop = createMediaQuery("(min-width: 768px)")
|
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||||
const size = createSizing()
|
const size = createSizing()
|
||||||
const isV2NewSessionPage = () =>
|
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
|
||||||
shouldUseV2NewSessionPage({ channel: import.meta.env.VITE_OPENCODE_CHANNEL, sessionID: params.id })
|
const desktopFileTreeOpen = createMemo(() => isDesktop() && layout.fileTree.opened())
|
||||||
const desktopReviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened() && !isV2NewSessionPage())
|
|
||||||
const desktopFileTreeOpen = createMemo(() => isDesktop() && layout.fileTree.opened() && !isV2NewSessionPage())
|
|
||||||
const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
|
const desktopSidePanelOpen = createMemo(() => desktopReviewOpen() || desktopFileTreeOpen())
|
||||||
const sessionPanelWidth = createMemo(() => {
|
const sessionPanelWidth = createMemo(() => {
|
||||||
if (!desktopSidePanelOpen()) return "100%"
|
if (!desktopSidePanelOpen()) return "100%"
|
||||||
@@ -481,7 +476,7 @@ export default function Page() {
|
|||||||
: skipToken,
|
: skipToken,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const refreshVcs = debounce(() => void queryClient.invalidateQueries({ queryKey: vcsKey() }), 100)
|
const refreshVcs = () => void queryClient.invalidateQueries({ queryKey: vcsKey() })
|
||||||
const reviewDiffs = () => {
|
const reviewDiffs = () => {
|
||||||
if (store.changes === "git" || store.changes === "branch")
|
if (store.changes === "git" || store.changes === "branch")
|
||||||
// avoids suspense
|
// avoids suspense
|
||||||
@@ -1653,60 +1648,6 @@ export default function Page() {
|
|||||||
|
|
||||||
useUsageExceededDialogs()
|
useUsageExceededDialogs()
|
||||||
|
|
||||||
const composerRegion = (placement: "dock" | "inline") => (
|
|
||||||
<SessionComposerRegion
|
|
||||||
state={composer}
|
|
||||||
ready={!store.deferRender && messagesReady()}
|
|
||||||
centered={placement === "dock" && centered()}
|
|
||||||
placement={placement}
|
|
||||||
inputRef={(el) => {
|
|
||||||
inputRef = el
|
|
||||||
}}
|
|
||||||
newSessionWorktree={newSessionWorktree()}
|
|
||||||
onNewSessionWorktreeChange={(value) => setStore("newSessionWorktree", value)}
|
|
||||||
onNewSessionWorktreeReset={() => setStore("newSessionWorktree", "main")}
|
|
||||||
onSubmit={() => {
|
|
||||||
comments.clear()
|
|
||||||
resumeScroll()
|
|
||||||
}}
|
|
||||||
onResponseSubmit={resumeScroll}
|
|
||||||
followup={
|
|
||||||
params.id && !isChildSession()
|
|
||||||
? {
|
|
||||||
queue: queueEnabled,
|
|
||||||
items: followupDock(),
|
|
||||||
sending: sendingFollowup(),
|
|
||||||
edit: editingFollowup(),
|
|
||||||
onQueue: queueFollowup,
|
|
||||||
onAbort: () => {
|
|
||||||
const id = params.id
|
|
||||||
if (!id) return
|
|
||||||
setFollowup("paused", id, true)
|
|
||||||
},
|
|
||||||
onSend: (id) => {
|
|
||||||
void sendFollowup(params.id!, id, { manual: true })
|
|
||||||
},
|
|
||||||
onEdit: editFollowup,
|
|
||||||
onEditLoaded: clearFollowupEdit,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
revert={
|
|
||||||
rolled().length > 0
|
|
||||||
? {
|
|
||||||
items: rolled(),
|
|
||||||
restoring: restoring(),
|
|
||||||
disabled: reverting(),
|
|
||||||
onRestore: restore,
|
|
||||||
}
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
setPromptDockRef={(el) => {
|
|
||||||
promptDock = el
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
|
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
|
||||||
{sessionSync() ?? ""}
|
{sessionSync() ?? ""}
|
||||||
@@ -1737,12 +1678,12 @@ export default function Page() {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
|
{/* Session panel */}
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger flex-1 md:flex-none": true,
|
"@container relative shrink-0 flex flex-col min-h-0 h-full bg-background-stronger flex-1 md:flex-none": true,
|
||||||
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
|
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
|
||||||
!size.active() && !ui.reviewSnap,
|
!size.active() && !ui.reviewSnap,
|
||||||
"transition-[width]": !isV2NewSessionPage(),
|
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
width: sessionPanelWidth(),
|
width: sessionPanelWidth(),
|
||||||
@@ -1799,16 +1740,60 @@ export default function Page() {
|
|||||||
</Show>
|
</Show>
|
||||||
</Match>
|
</Match>
|
||||||
<Match when={true}>
|
<Match when={true}>
|
||||||
<Show when={USE_NEW_SESSION_DESIGN} fallback={<NewSessionView worktree={newSessionWorktree()} />}>
|
<NewSessionView worktree={newSessionWorktree()} />
|
||||||
<NewSessionDesignView worktree={newSessionWorktree()}>
|
|
||||||
{composerRegion("inline")}
|
|
||||||
</NewSessionDesignView>
|
|
||||||
</Show>
|
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Show when={params.id || !USE_NEW_SESSION_DESIGN}>{composerRegion("dock")}</Show>
|
<SessionComposerRegion
|
||||||
|
state={composer}
|
||||||
|
ready={!store.deferRender && messagesReady()}
|
||||||
|
centered={centered()}
|
||||||
|
inputRef={(el) => {
|
||||||
|
inputRef = el
|
||||||
|
}}
|
||||||
|
newSessionWorktree={newSessionWorktree()}
|
||||||
|
onNewSessionWorktreeReset={() => setStore("newSessionWorktree", "main")}
|
||||||
|
onSubmit={() => {
|
||||||
|
comments.clear()
|
||||||
|
resumeScroll()
|
||||||
|
}}
|
||||||
|
onResponseSubmit={resumeScroll}
|
||||||
|
followup={
|
||||||
|
params.id && !isChildSession()
|
||||||
|
? {
|
||||||
|
queue: queueEnabled,
|
||||||
|
items: followupDock(),
|
||||||
|
sending: sendingFollowup(),
|
||||||
|
edit: editingFollowup(),
|
||||||
|
onQueue: queueFollowup,
|
||||||
|
onAbort: () => {
|
||||||
|
const id = params.id
|
||||||
|
if (!id) return
|
||||||
|
setFollowup("paused", id, true)
|
||||||
|
},
|
||||||
|
onSend: (id) => {
|
||||||
|
void sendFollowup(params.id!, id, { manual: true })
|
||||||
|
},
|
||||||
|
onEdit: editFollowup,
|
||||||
|
onEditLoaded: clearFollowupEdit,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
revert={
|
||||||
|
rolled().length > 0
|
||||||
|
? {
|
||||||
|
items: rolled(),
|
||||||
|
restoring: restoring(),
|
||||||
|
disabled: reverting(),
|
||||||
|
onRestore: restore,
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
setPromptDockRef={(el) => {
|
||||||
|
promptDock = el
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<Show when={desktopReviewOpen()}>
|
<Show when={desktopReviewOpen()}>
|
||||||
<div onPointerDown={() => size.start()}>
|
<div onPointerDown={() => size.start()}>
|
||||||
|
|||||||
@@ -22,10 +22,8 @@ export function SessionComposerRegion(props: {
|
|||||||
state: SessionComposerState
|
state: SessionComposerState
|
||||||
ready: boolean
|
ready: boolean
|
||||||
centered: boolean
|
centered: boolean
|
||||||
placement?: "dock" | "inline"
|
|
||||||
inputRef: (el: HTMLDivElement) => void
|
inputRef: (el: HTMLDivElement) => void
|
||||||
newSessionWorktree: string
|
newSessionWorktree: string
|
||||||
onNewSessionWorktreeChange?: (worktree: string) => void
|
|
||||||
onNewSessionWorktreeReset: () => void
|
onNewSessionWorktreeReset: () => void
|
||||||
onSubmit: () => void
|
onSubmit: () => void
|
||||||
onResponseSubmit: () => void
|
onResponseSubmit: () => void
|
||||||
@@ -144,15 +142,11 @@ export function SessionComposerRegion(props: {
|
|||||||
<div
|
<div
|
||||||
ref={props.setPromptDockRef}
|
ref={props.setPromptDockRef}
|
||||||
data-component="session-prompt-dock"
|
data-component="session-prompt-dock"
|
||||||
classList={{
|
class="shrink-0 w-full pb-3 flex flex-col justify-center items-center bg-background-stronger pointer-events-none"
|
||||||
"w-full flex flex-col justify-center items-center pointer-events-none": true,
|
|
||||||
"shrink-0 pb-3 bg-background-stronger": props.placement !== "inline",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
classList={{
|
classList={{
|
||||||
"w-full px-3 pointer-events-auto": true,
|
"w-full px-3 pointer-events-auto": true,
|
||||||
"max-w-[720px] px-0": props.placement === "inline",
|
|
||||||
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
|
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -262,10 +256,8 @@ export function SessionComposerRegion(props: {
|
|||||||
fallback={
|
fallback={
|
||||||
<Show when={!props.state.blocked()}>
|
<Show when={!props.state.blocked()}>
|
||||||
<PromptInput
|
<PromptInput
|
||||||
variant={props.placement === "inline" ? "new-session" : undefined}
|
|
||||||
ref={props.inputRef}
|
ref={props.inputRef}
|
||||||
newSessionWorktree={props.newSessionWorktree}
|
newSessionWorktree={props.newSessionWorktree}
|
||||||
onNewSessionWorktreeChange={props.onNewSessionWorktreeChange}
|
|
||||||
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
|
onNewSessionWorktreeReset={props.onNewSessionWorktreeReset}
|
||||||
edit={props.followup?.edit}
|
edit={props.followup?.edit}
|
||||||
onEditLoaded={props.followup?.onEditLoaded}
|
onEditLoaded={props.followup?.onEditLoaded}
|
||||||
|
|||||||
@@ -1236,6 +1236,7 @@ export function MessageTimeline(props: {
|
|||||||
onClick={props.onAutoScrollInteraction}
|
onClick={props.onAutoScrollInteraction}
|
||||||
class="relative min-w-0 w-full h-full"
|
class="relative min-w-0 w-full h-full"
|
||||||
style={{
|
style={{
|
||||||
|
"--session-title-height": showHeader() ? "40px" : "0px",
|
||||||
"--sticky-accordion-top": showHeader() ? "48px" : "0px",
|
"--sticky-accordion-top": showHeader() ? "48px" : "0px",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -1255,14 +1256,16 @@ export function MessageTimeline(props: {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Show when={workingStatus() !== "hidden" && settings.general.showSessionProgressBar()}>
|
<Show when={workingStatus() !== "hidden" && settings.general.showSessionProgressBar()}>
|
||||||
<div data-component="session-progress" data-state={workingStatus()} aria-hidden="true">
|
<div
|
||||||
<div
|
data-component="session-progress"
|
||||||
data-component="session-progress-bar"
|
data-state={workingStatus()}
|
||||||
style={{
|
aria-hidden="true"
|
||||||
background: tint() ?? "var(--icon-interactive-base)",
|
style={{
|
||||||
animation: `session-progress-whip ${bar.ms}ms infinite`,
|
"--session-progress-color": tint() ?? "var(--icon-interactive-base)",
|
||||||
}}
|
"--session-progress-ms": `${bar.ms}ms`,
|
||||||
/>
|
}}
|
||||||
|
>
|
||||||
|
<div data-component="session-progress-bar" />
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div class="h-12 w-full flex items-center justify-between gap-2">
|
<div class="h-12 w-full flex items-center justify-between gap-2">
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
|
||||||
import { shouldUseV2NewSessionPage } from "./new-session-layout"
|
|
||||||
|
|
||||||
describe("shouldUseV2NewSessionPage", () => {
|
|
||||||
test("keeps prod session pages on the legacy layout", () => {
|
|
||||||
expect(shouldUseV2NewSessionPage({ channel: "prod", sessionID: "ses_123" })).toBe(false)
|
|
||||||
expect(shouldUseV2NewSessionPage({ channel: "prod" })).toBe(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("uses the v2 layout only for non-prod new-session pages", () => {
|
|
||||||
expect(shouldUseV2NewSessionPage({ channel: "dev" })).toBe(true)
|
|
||||||
expect(shouldUseV2NewSessionPage({ channel: "dev", sessionID: "ses_123" })).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export function shouldUseV2NewSessionPage(input: { channel?: "dev" | "beta" | "prod"; sessionID?: string }) {
|
|
||||||
return input.channel !== "prod" && !input.sessionID
|
|
||||||
}
|
|
||||||
@@ -55,7 +55,7 @@ export function SessionSidePanel(props: {
|
|||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const command = useCommand()
|
const command = useCommand()
|
||||||
const dialog = useDialog()
|
const dialog = useDialog()
|
||||||
const { sessionKey, tabs, view, params } = useSessionLayout()
|
const { sessionKey, tabs, view } = useSessionLayout()
|
||||||
|
|
||||||
const isDesktop = createMediaQuery("(min-width: 768px)")
|
const isDesktop = createMediaQuery("(min-width: 768px)")
|
||||||
const shown = createMemo(
|
const shown = createMemo(
|
||||||
@@ -207,7 +207,7 @@ export function SessionSidePanel(props: {
|
|||||||
})
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Show when={isDesktop() && !(import.meta.env.VITE_OPENCODE_CHANNEL !== "prod" && !params.id)}>
|
<Show when={isDesktop()}>
|
||||||
<aside
|
<aside
|
||||||
id="review-panel"
|
id="review-panel"
|
||||||
aria-label={language.t("session.panel.reviewAndFiles")}
|
aria-label={language.t("session.panel.reviewAndFiles")}
|
||||||
|
|||||||
@@ -5,13 +5,6 @@ import { fileURLToPath } from "url"
|
|||||||
|
|
||||||
const theme = fileURLToPath(new URL("./public/oc-theme-preload.js", import.meta.url))
|
const theme = fileURLToPath(new URL("./public/oc-theme-preload.js", import.meta.url))
|
||||||
|
|
||||||
const channel = (() => {
|
|
||||||
const raw = process.env.OPENCODE_CHANNEL
|
|
||||||
if (raw === "dev" || raw === "beta" || raw === "prod") return raw
|
|
||||||
if (process.env.OPENCODE_CHANNEL === "latest") return "prod"
|
|
||||||
return "dev"
|
|
||||||
})()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {import("vite").PluginOption}
|
* @type {import("vite").PluginOption}
|
||||||
*/
|
*/
|
||||||
@@ -25,9 +18,6 @@ export default [
|
|||||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
define: {
|
|
||||||
"import.meta.env.VITE_OPENCODE_CHANNEL": JSON.stringify(channel),
|
|
||||||
},
|
|
||||||
worker: {
|
worker: {
|
||||||
format: "es",
|
format: "es",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-app",
|
"name": "@opencode-ai/console-app",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -348,8 +348,8 @@ export const dict = {
|
|||||||
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
|
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
|
||||||
|
|
||||||
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
|
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
|
||||||
"zen.api.error.modelNotSupported": "Model {{model}} is not supported",
|
"zen.api.error.modelNotSupported": "Model {{model}} not supported",
|
||||||
"zen.api.error.modelFormatNotSupported": "Model {{model}} is not supported for format {{format}}",
|
"zen.api.error.modelFormatNotSupported": "Model {{model}} not supported for format {{format}}",
|
||||||
"zen.api.error.noProviderAvailable": "No provider available",
|
"zen.api.error.noProviderAvailable": "No provider available",
|
||||||
"zen.api.error.providerNotSupported": "Provider {{provider}} not supported",
|
"zen.api.error.providerNotSupported": "Provider {{provider}} not supported",
|
||||||
"zen.api.error.missingApiKey": "Missing API key.",
|
"zen.api.error.missingApiKey": "Missing API key.",
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import { Resource, waitUntil } from "@opencode-ai/console-resource"
|
||||||
|
|
||||||
|
export function createDataDumper(sessionId: string, requestId: string, projectId: string) {
|
||||||
|
return
|
||||||
|
if (Resource.App.stage !== "production") return
|
||||||
|
if (sessionId === "") return
|
||||||
|
|
||||||
|
let data: Record<string, any> = { sessionId, requestId, projectId }
|
||||||
|
let metadata: Record<string, any> = { sessionId, requestId, projectId }
|
||||||
|
|
||||||
|
return {
|
||||||
|
provideModel: (model?: string) => {
|
||||||
|
data.modelName = model
|
||||||
|
metadata.modelName = model
|
||||||
|
},
|
||||||
|
provideRequest: (request: string) => (data.request = request),
|
||||||
|
provideResponse: (response: string) => (data.response = response),
|
||||||
|
provideStream: (chunk: string) => (data.response = (data.response ?? "") + chunk),
|
||||||
|
flush: () => {
|
||||||
|
if (!data.modelName) return
|
||||||
|
|
||||||
|
const timestamp = new Date().toISOString().replace(/[^0-9]/g, "")
|
||||||
|
const year = timestamp.substring(0, 4)
|
||||||
|
const month = timestamp.substring(4, 6)
|
||||||
|
const day = timestamp.substring(6, 8)
|
||||||
|
const hour = timestamp.substring(8, 10)
|
||||||
|
const minute = timestamp.substring(10, 12)
|
||||||
|
const second = timestamp.substring(12, 14)
|
||||||
|
|
||||||
|
void waitUntil(
|
||||||
|
Resource.ZenDataNew.put(
|
||||||
|
`data/${data.modelName}/${year}/${month}/${day}/${hour}/${minute}/${second}/${requestId}.json`,
|
||||||
|
JSON.stringify({ timestamp, ...data }),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
void waitUntil(
|
||||||
|
Resource.ZenDataNew.put(
|
||||||
|
`meta/${data.modelName}/${sessionId}/${requestId}.json`,
|
||||||
|
JSON.stringify({ timestamp, ...metadata }),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import { openaiHelper } from "./provider/openai"
|
|||||||
import { oaCompatHelper } from "./provider/openai-compatible"
|
import { oaCompatHelper } from "./provider/openai-compatible"
|
||||||
import { createRateLimiter as createIpRateLimiter } from "./ipRateLimiter"
|
import { createRateLimiter as createIpRateLimiter } from "./ipRateLimiter"
|
||||||
import { createRateLimiter as createKeyRateLimiter } from "./keyRateLimiter"
|
import { createRateLimiter as createKeyRateLimiter } from "./keyRateLimiter"
|
||||||
|
import { createDataDumper } from "./dataDumper"
|
||||||
import { createTrialLimiter } from "./trialLimiter"
|
import { createTrialLimiter } from "./trialLimiter"
|
||||||
import { createStickyTracker } from "./stickyProviderTracker"
|
import { createStickyTracker } from "./stickyProviderTracker"
|
||||||
import { LiteData } from "@opencode-ai/console-core/lite.js"
|
import { LiteData } from "@opencode-ai/console-core/lite.js"
|
||||||
@@ -102,6 +103,7 @@ export async function handler(
|
|||||||
const zenApiKey = rawZenApiKey === "public" ? undefined : rawZenApiKey
|
const zenApiKey = rawZenApiKey === "public" ? undefined : rawZenApiKey
|
||||||
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
|
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
|
||||||
const requestId = input.request.headers.get("x-opencode-request") ?? ""
|
const requestId = input.request.headers.get("x-opencode-request") ?? ""
|
||||||
|
const projectId = input.request.headers.get("x-opencode-project") ?? ""
|
||||||
const ocClient = input.request.headers.get("x-opencode-client") ?? ""
|
const ocClient = input.request.headers.get("x-opencode-client") ?? ""
|
||||||
const userAgent = input.request.headers.get("user-agent") ?? ""
|
const userAgent = input.request.headers.get("user-agent") ?? ""
|
||||||
logger.metric({
|
logger.metric({
|
||||||
@@ -114,16 +116,16 @@ export async function handler(
|
|||||||
})
|
})
|
||||||
const zenData = ZenData.list(opts.modelList)
|
const zenData = ZenData.list(opts.modelList)
|
||||||
const modelInfo = validateModel(zenData, model)
|
const modelInfo = validateModel(zenData, model)
|
||||||
|
const dataDumper = createDataDumper(sessionId, requestId, projectId)
|
||||||
const trialLimiter = createTrialLimiter(modelInfo.trialProvider, ip)
|
const trialLimiter = createTrialLimiter(modelInfo.trialProvider, ip)
|
||||||
const trialProviders = await trialLimiter?.check()
|
const trialProviders = await trialLimiter?.check()
|
||||||
const rateLimiter = modelInfo.allowAnonymous
|
const rateLimiter = modelInfo.allowAnonymous
|
||||||
? createIpRateLimiter(modelInfo.id, modelInfo.rateLimit, ip, input.request)
|
? createIpRateLimiter(modelInfo.id, modelInfo.rateLimit, ip, input.request)
|
||||||
: createKeyRateLimiter(modelInfo.id, modelInfo.rateLimit, zenApiKey, input.request)
|
: createKeyRateLimiter(modelInfo.id, modelInfo.rateLimit, zenApiKey, input.request)
|
||||||
await rateLimiter?.check()
|
await rateLimiter?.check()
|
||||||
const authInfo = await authenticate(modelInfo, zenApiKey)
|
const stickyTracker = createStickyTracker(modelInfo.id, modelInfo.stickyProvider, sessionId)
|
||||||
const stickyId = sessionId ? sessionId : (authInfo?.workspaceID ?? ip)
|
|
||||||
const stickyTracker = createStickyTracker(modelInfo.id, modelInfo.stickyProvider, stickyId)
|
|
||||||
const stickyProvider = await stickyTracker?.get()
|
const stickyProvider = await stickyTracker?.get()
|
||||||
|
const authInfo = await authenticate(modelInfo, zenApiKey)
|
||||||
const billingSource = validateBilling(authInfo, modelInfo)
|
const billingSource = validateBilling(authInfo, modelInfo)
|
||||||
logger.metric({ source: billingSource })
|
logger.metric({ source: billingSource })
|
||||||
const modelTpmLimiter = createModelTpmLimiter(modelInfo.providers)
|
const modelTpmLimiter = createModelTpmLimiter(modelInfo.providers)
|
||||||
@@ -137,7 +139,8 @@ export async function handler(
|
|||||||
zenData,
|
zenData,
|
||||||
authInfo,
|
authInfo,
|
||||||
modelInfo,
|
modelInfo,
|
||||||
stickyId,
|
ip,
|
||||||
|
sessionId,
|
||||||
trialProviders,
|
trialProviders,
|
||||||
retry,
|
retry,
|
||||||
stickyProvider,
|
stickyProvider,
|
||||||
@@ -164,8 +167,13 @@ export async function handler(
|
|||||||
if (Array.isArray(v)) return [[k, v]]
|
if (Array.isArray(v)) return [[k, v]]
|
||||||
if (typeof v === "object") return [[k, replacer(v)]]
|
if (typeof v === "object") return [[k, replacer(v)]]
|
||||||
if (typeof v === "string") {
|
if (typeof v === "string") {
|
||||||
|
if (v === "$ip") return [[k, ip]]
|
||||||
if (v === "$workspace") return authInfo?.workspaceID ? [[k, authInfo?.workspaceID]] : []
|
if (v === "$workspace") return authInfo?.workspaceID ? [[k, authInfo?.workspaceID]] : []
|
||||||
if (v === "$user") return stickyId ? [[k, stickyId]] : []
|
if (v === "$session") return sessionId ? [[k, sessionId]] : []
|
||||||
|
if (v === "$user") {
|
||||||
|
const user = sessionId ?? authInfo?.workspaceID ?? ip
|
||||||
|
return user ? [[k, user]] : []
|
||||||
|
}
|
||||||
if (v.startsWith("$header.")) {
|
if (v.startsWith("$header.")) {
|
||||||
const headerValue = input.request.headers.get(v.slice(8))
|
const headerValue = input.request.headers.get(v.slice(8))
|
||||||
return headerValue ? [[k, headerValue]] : []
|
return headerValue ? [[k, headerValue]] : []
|
||||||
@@ -184,7 +192,7 @@ export async function handler(
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: (() => {
|
headers: (() => {
|
||||||
const headers = new Headers(input.request.headers)
|
const headers = new Headers(input.request.headers)
|
||||||
providerInfo.modifyHeaders(headers, providerInfo.apiKey, stickyId)
|
providerInfo.modifyHeaders(headers, body, providerInfo.apiKey)
|
||||||
Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
|
Object.entries(providerInfo.headerMappings ?? {}).forEach(([k, v]) => {
|
||||||
headers.set(k, headers.get(v)!)
|
headers.set(k, headers.get(v)!)
|
||||||
})
|
})
|
||||||
@@ -229,6 +237,10 @@ export async function handler(
|
|||||||
|
|
||||||
const { providerInfo, reqBody, res, startTimestamp } = await retriableRequest()
|
const { providerInfo, reqBody, res, startTimestamp } = await retriableRequest()
|
||||||
|
|
||||||
|
// Store model request
|
||||||
|
dataDumper?.provideModel(providerInfo.storeModel)
|
||||||
|
dataDumper?.provideRequest(reqBody)
|
||||||
|
|
||||||
// Store sticky provider
|
// Store sticky provider
|
||||||
if (res.status === 200) await stickyTracker?.set(providerInfo.id)
|
if (res.status === 200) await stickyTracker?.set(providerInfo.id)
|
||||||
|
|
||||||
@@ -269,6 +281,8 @@ export async function handler(
|
|||||||
const body = JSON.stringify(responseConverter(json))
|
const body = JSON.stringify(responseConverter(json))
|
||||||
logger.metric({ response_length: body.length })
|
logger.metric({ response_length: body.length })
|
||||||
logger.debug("RESPONSE: " + body)
|
logger.debug("RESPONSE: " + body)
|
||||||
|
dataDumper?.provideResponse(body)
|
||||||
|
dataDumper?.flush()
|
||||||
return new Response(body, {
|
return new Response(body, {
|
||||||
status: resStatus,
|
status: resStatus,
|
||||||
statusText: res.statusText,
|
statusText: res.statusText,
|
||||||
@@ -299,6 +313,7 @@ export async function handler(
|
|||||||
response_length: responseLength,
|
response_length: responseLength,
|
||||||
"timestamp.last_byte": timestampLastByte,
|
"timestamp.last_byte": timestampLastByte,
|
||||||
})
|
})
|
||||||
|
dataDumper?.flush()
|
||||||
await rateLimiter?.track()
|
await rateLimiter?.track()
|
||||||
const usage = usageParser.retrieve()
|
const usage = usageParser.retrieve()
|
||||||
if (usage) {
|
if (usage) {
|
||||||
@@ -336,6 +351,7 @@ export async function handler(
|
|||||||
|
|
||||||
responseLength += value.length
|
responseLength += value.length
|
||||||
buffer += decoder.decode(value, { stream: true })
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
dataDumper?.provideStream(buffer)
|
||||||
|
|
||||||
const parts = buffer.split(providerInfo.streamSeparator)
|
const parts = buffer.split(providerInfo.streamSeparator)
|
||||||
buffer = parts.pop() ?? ""
|
buffer = parts.pop() ?? ""
|
||||||
@@ -474,7 +490,8 @@ export async function handler(
|
|||||||
zenData: ZenData,
|
zenData: ZenData,
|
||||||
authInfo: AuthInfo,
|
authInfo: AuthInfo,
|
||||||
modelInfo: ModelInfo,
|
modelInfo: ModelInfo,
|
||||||
stickyId: string,
|
ip: string,
|
||||||
|
sessionId: string,
|
||||||
trialProviders: string[] | undefined,
|
trialProviders: string[] | undefined,
|
||||||
retry: RetryOptions,
|
retry: RetryOptions,
|
||||||
stickyProvider: string | undefined,
|
stickyProvider: string | undefined,
|
||||||
@@ -524,10 +541,11 @@ export async function handler(
|
|||||||
.flatMap((provider) => Array<typeof provider>(provider.weight).fill(provider))
|
.flatMap((provider) => Array<typeof provider>(provider.weight).fill(provider))
|
||||||
|
|
||||||
// Use the last 4 characters of session ID to select a provider
|
// Use the last 4 characters of session ID to select a provider
|
||||||
|
const identifier = sessionId.length ? sessionId : ip
|
||||||
let h = 0
|
let h = 0
|
||||||
const l = stickyId.length
|
const l = identifier.length
|
||||||
for (let i = l - 4; i < l; i++) {
|
for (let i = l - 4; i < l; i++) {
|
||||||
h = (h * 31 + stickyId.charCodeAt(i)) | 0 // 32-bit int
|
h = (h * 31 + identifier.charCodeAt(i)) | 0 // 32-bit int
|
||||||
}
|
}
|
||||||
const index = (h >>> 0) % providers.length // make unsigned + range 0..length-1
|
const index = (h >>> 0) % providers.length // make unsigned + range 0..length-1
|
||||||
const provider = providers[index || 0]
|
const provider = providers[index || 0]
|
||||||
|
|||||||
@@ -10,12 +10,9 @@ export function createRateLimiter(modelId: string, rateLimit: number | undefined
|
|||||||
const dict = i18n(localeFromRequest(request))
|
const dict = i18n(localeFromRequest(request))
|
||||||
|
|
||||||
const limits = Subscription.getFreeLimits()
|
const limits = Subscription.getFreeLimits()
|
||||||
// temporarily disable check headers
|
const headersExist = Object.entries(limits.checkHeaders).every(
|
||||||
//const headersExist = Object.entries(limits.checkHeaders).every(
|
([name, value]) => request.headers.get(name)?.toLowerCase().includes(value) ?? false,
|
||||||
// ([name, value]) => request.headers.get(name)?.toLowerCase().includes(value) ?? false,
|
)
|
||||||
//)
|
|
||||||
//const dailyLimit = !headersExist ? limits.dailyRequestsFallback : (rateLimit ?? limits.dailyRequests)
|
|
||||||
const headersExist = true
|
|
||||||
const dailyLimit = !headersExist ? limits.dailyRequestsFallback : (rateLimit ?? limits.dailyRequests)
|
const dailyLimit = !headersExist ? limits.dailyRequestsFallback : (rateLimit ?? limits.dailyRequests)
|
||||||
const isDefaultModel = headersExist && !rateLimit
|
const isDefaultModel = headersExist && !rateLimit
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) =>
|
|||||||
isBedrock
|
isBedrock
|
||||||
? `${providerApi}/model/${isBedrockModelArn ? encodeURIComponent(providerModel) : providerModel}/${isStream ? "invoke-with-response-stream" : "invoke"}`
|
? `${providerApi}/model/${isBedrockModelArn ? encodeURIComponent(providerModel) : providerModel}/${isStream ? "invoke-with-response-stream" : "invoke"}`
|
||||||
: providerApi + "/messages",
|
: providerApi + "/messages",
|
||||||
modifyHeaders: (headers: Headers, apiKey: string, _stickyId: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
if (isBedrock || isDatabricks) {
|
if (isBedrock || isDatabricks) {
|
||||||
headers.set("Authorization", `Bearer ${apiKey}`)
|
headers.set("Authorization", `Bearer ${apiKey}`)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export const googleHelper: ProviderHelper = ({ providerModel }) => ({
|
|||||||
format: "google",
|
format: "google",
|
||||||
modifyUrl: (providerApi: string, isStream?: boolean) =>
|
modifyUrl: (providerApi: string, isStream?: boolean) =>
|
||||||
`${providerApi}/models/${providerModel}:${isStream ? "streamGenerateContent?alt=sse" : "generateContent"}`,
|
`${providerApi}/models/${providerModel}:${isStream ? "streamGenerateContent?alt=sse" : "generateContent"}`,
|
||||||
modifyHeaders: (headers: Headers, apiKey: string, _stickyId: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
headers.set("x-goog-api-key", apiKey)
|
headers.set("x-goog-api-key", apiKey)
|
||||||
},
|
},
|
||||||
modifyBody: (body: Record<string, any>) => {
|
modifyBody: (body: Record<string, any>) => {
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ type Usage = {
|
|||||||
export const oaCompatHelper: ProviderHelper = ({ adjustCacheUsage }) => ({
|
export const oaCompatHelper: ProviderHelper = ({ adjustCacheUsage }) => ({
|
||||||
format: "oa-compat",
|
format: "oa-compat",
|
||||||
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
modifyUrl: (providerApi: string) => providerApi + "/chat/completions",
|
||||||
modifyHeaders: (headers: Headers, apiKey: string, stickyId: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
headers.set("authorization", `Bearer ${apiKey}`)
|
headers.set("authorization", `Bearer ${apiKey}`)
|
||||||
headers.set("x-session-affinity", stickyId)
|
headers.set("x-session-affinity", headers.get("x-opencode-session") ?? "")
|
||||||
},
|
},
|
||||||
modifyBody: (body: Record<string, any>, _workspaceID?: string) => {
|
modifyBody: (body: Record<string, any>, _workspaceID?: string) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Usage = {
|
|||||||
export const openaiHelper: ProviderHelper = ({ workspaceID }) => ({
|
export const openaiHelper: ProviderHelper = ({ workspaceID }) => ({
|
||||||
format: "openai",
|
format: "openai",
|
||||||
modifyUrl: (providerApi: string) => providerApi + "/responses",
|
modifyUrl: (providerApi: string) => providerApi + "/responses",
|
||||||
modifyHeaders: (headers: Headers, apiKey: string, _stickyId: string) => {
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => {
|
||||||
headers.set("authorization", `Bearer ${apiKey}`)
|
headers.set("authorization", `Bearer ${apiKey}`)
|
||||||
},
|
},
|
||||||
modifyBody: (body: Record<string, any>) => body,
|
modifyBody: (body: Record<string, any>) => body,
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export type ProviderHelper = (input: {
|
|||||||
}) => {
|
}) => {
|
||||||
format: ZenData.Format
|
format: ZenData.Format
|
||||||
modifyUrl: (providerApi: string, isStream?: boolean) => string
|
modifyUrl: (providerApi: string, isStream?: boolean) => string
|
||||||
modifyHeaders: (headers: Headers, apiKey: string, stickyId: string) => void
|
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
||||||
modifyBody: (body: Record<string, any>) => Record<string, any>
|
modifyBody: (body: Record<string, any>) => Record<string, any>
|
||||||
createBinaryStreamDecoder: () => ((chunk: Uint8Array) => Uint8Array | undefined) | undefined
|
createBinaryStreamDecoder: () => ((chunk: Uint8Array) => Uint8Array | undefined) | undefined
|
||||||
streamSeparator: string
|
streamSeparator: string
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
||||||
import { ModelStickyProviderTable } from "@opencode-ai/console-core/schema/ip.sql.js"
|
import { ModelStickyProviderTable } from "@opencode-ai/console-core/schema/ip.sql.js"
|
||||||
|
|
||||||
export function createStickyTracker(
|
export function createStickyTracker(modelId: string, stickyProvider: "strict" | "prefer" | undefined, session: string) {
|
||||||
modelId: string,
|
|
||||||
stickyProvider: "strict" | "prefer" | undefined,
|
|
||||||
stickyId: string,
|
|
||||||
) {
|
|
||||||
if (!stickyProvider) return
|
if (!stickyProvider) return
|
||||||
if (!stickyId) return
|
if (!session) return
|
||||||
const id = `${modelId}/${stickyId}`
|
const id = `${modelId}/${session}`
|
||||||
let _providerId: string | undefined
|
let _providerId: string | undefined
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"name": "@opencode-ai/console-core",
|
"name": "@opencode-ai/console-core",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
Vendored
+298
-1
@@ -4,7 +4,304 @@
|
|||||||
/* deno-fmt-ignore-file */
|
/* deno-fmt-ignore-file */
|
||||||
/* biome-ignore-all lint: auto-generated */
|
/* biome-ignore-all lint: auto-generated */
|
||||||
|
|
||||||
/// <reference path="../../../sst-env.d.ts" />
|
import "sst"
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"ADMIN_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AUTH_API_URL": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_ACCESS_KEY_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_SECRET_ACCESS_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_API_TOKEN": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Console": {
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
"DISCORD_SUPPORT_CHANNEL_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Database": {
|
||||||
|
"database": string
|
||||||
|
"host": string
|
||||||
|
"password": string
|
||||||
|
"port": number
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"username": string
|
||||||
|
}
|
||||||
|
"EMAILOCTOPUS_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_PRIVATE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_ID_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_SECRET_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GOOGLE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HONEYCOMB_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HoneycombWebhookSecret": {
|
||||||
|
"type": "random.index/randomPassword.RandomPassword"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2AccessKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2SecretKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_INSTANCE_URL": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_SECRET_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_WEBHOOK_SECRET": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Teams": {
|
||||||
|
"type": "sst.cloudflare.SolidStart"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"Web": {
|
||||||
|
"type": "sst.cloudflare.Astro"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"WebApp": {
|
||||||
|
"type": "sst.cloudflare.StaticSite"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"ZEN_BLACK_PRICE": {
|
||||||
|
"plan100": string
|
||||||
|
"plan20": string
|
||||||
|
"plan200": string
|
||||||
|
"product": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_LIMITS": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_LITE_PRICE": {
|
||||||
|
"firstMonth100Coupon": string
|
||||||
|
"firstMonth50Coupon": string
|
||||||
|
"price": string
|
||||||
|
"priceInr": number
|
||||||
|
"product": string
|
||||||
|
"sixMonths100Coupon": string
|
||||||
|
"threeMonths100Coupon": string
|
||||||
|
"twelveMonths100Coupon": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_MODELS1": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS10": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS11": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS12": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS13": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS14": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS15": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS16": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS17": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS18": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS19": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS2": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS20": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS21": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS22": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS23": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS24": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS25": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS26": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS27": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS28": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS29": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS3": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS30": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS4": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS5": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS6": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS7": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS8": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS9": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cloudflare
|
||||||
|
import * as cloudflare from "@cloudflare/workers-types";
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"Api": cloudflare.Service
|
||||||
|
"AuthApi": cloudflare.Service
|
||||||
|
"AuthStorage": cloudflare.KVNamespace
|
||||||
|
"Bucket": cloudflare.R2Bucket
|
||||||
|
"EnterpriseStorage": cloudflare.R2Bucket
|
||||||
|
"LogProcessor": cloudflare.Service
|
||||||
|
"Stat": cloudflare.Service
|
||||||
|
"ZenData": cloudflare.R2Bucket
|
||||||
|
"ZenDataNew": cloudflare.R2Bucket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-function",
|
"name": "@opencode-ai/console-function",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
+298
-1
@@ -4,7 +4,304 @@
|
|||||||
/* deno-fmt-ignore-file */
|
/* deno-fmt-ignore-file */
|
||||||
/* biome-ignore-all lint: auto-generated */
|
/* biome-ignore-all lint: auto-generated */
|
||||||
|
|
||||||
/// <reference path="../../../sst-env.d.ts" />
|
import "sst"
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"ADMIN_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AUTH_API_URL": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_ACCESS_KEY_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_SECRET_ACCESS_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_API_TOKEN": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Console": {
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
"DISCORD_SUPPORT_CHANNEL_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Database": {
|
||||||
|
"database": string
|
||||||
|
"host": string
|
||||||
|
"password": string
|
||||||
|
"port": number
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"username": string
|
||||||
|
}
|
||||||
|
"EMAILOCTOPUS_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_PRIVATE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_ID_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_SECRET_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GOOGLE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HONEYCOMB_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HoneycombWebhookSecret": {
|
||||||
|
"type": "random.index/randomPassword.RandomPassword"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2AccessKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2SecretKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_INSTANCE_URL": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_SECRET_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_WEBHOOK_SECRET": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Teams": {
|
||||||
|
"type": "sst.cloudflare.SolidStart"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"Web": {
|
||||||
|
"type": "sst.cloudflare.Astro"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"WebApp": {
|
||||||
|
"type": "sst.cloudflare.StaticSite"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"ZEN_BLACK_PRICE": {
|
||||||
|
"plan100": string
|
||||||
|
"plan20": string
|
||||||
|
"plan200": string
|
||||||
|
"product": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_LIMITS": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_LITE_PRICE": {
|
||||||
|
"firstMonth100Coupon": string
|
||||||
|
"firstMonth50Coupon": string
|
||||||
|
"price": string
|
||||||
|
"priceInr": number
|
||||||
|
"product": string
|
||||||
|
"sixMonths100Coupon": string
|
||||||
|
"threeMonths100Coupon": string
|
||||||
|
"twelveMonths100Coupon": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_MODELS1": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS10": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS11": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS12": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS13": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS14": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS15": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS16": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS17": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS18": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS19": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS2": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS20": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS21": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS22": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS23": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS24": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS25": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS26": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS27": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS28": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS29": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS3": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS30": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS4": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS5": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS6": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS7": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS8": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS9": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cloudflare
|
||||||
|
import * as cloudflare from "@cloudflare/workers-types";
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"Api": cloudflare.Service
|
||||||
|
"AuthApi": cloudflare.Service
|
||||||
|
"AuthStorage": cloudflare.KVNamespace
|
||||||
|
"Bucket": cloudflare.R2Bucket
|
||||||
|
"EnterpriseStorage": cloudflare.R2Bucket
|
||||||
|
"LogProcessor": cloudflare.Service
|
||||||
|
"Stat": cloudflare.Service
|
||||||
|
"ZenData": cloudflare.R2Bucket
|
||||||
|
"ZenDataNew": cloudflare.R2Bucket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/console-mail",
|
"name": "@opencode-ai/console-mail",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jsx-email/all": "2.2.3",
|
"@jsx-email/all": "2.2.3",
|
||||||
"@jsx-email/cli": "1.4.3",
|
"@jsx-email/cli": "1.4.3",
|
||||||
|
|||||||
@@ -5,11 +5,6 @@ export const Resource = new Proxy(
|
|||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
get(_target, prop: string) {
|
get(_target, prop: string) {
|
||||||
if (`SST_RESOURCE_${prop}` in env) {
|
|
||||||
// @ts-expect-error
|
|
||||||
const value = env[`SST_RESOURCE_${prop}`]
|
|
||||||
return typeof value === "string" ? JSON.parse(value) : value
|
|
||||||
}
|
|
||||||
if (prop in env) {
|
if (prop in env) {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const value = env[prop]
|
const value = env[prop]
|
||||||
|
|||||||
+298
-1
@@ -4,7 +4,304 @@
|
|||||||
/* deno-fmt-ignore-file */
|
/* deno-fmt-ignore-file */
|
||||||
/* biome-ignore-all lint: auto-generated */
|
/* biome-ignore-all lint: auto-generated */
|
||||||
|
|
||||||
/// <reference path="../../../sst-env.d.ts" />
|
import "sst"
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"ADMIN_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AUTH_API_URL": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_ACCESS_KEY_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"AWS_SES_SECRET_ACCESS_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_API_TOKEN": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Console": {
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
"DISCORD_SUPPORT_CHANNEL_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Database": {
|
||||||
|
"database": string
|
||||||
|
"host": string
|
||||||
|
"password": string
|
||||||
|
"port": number
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"username": string
|
||||||
|
}
|
||||||
|
"EMAILOCTOPUS_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"FEISHU_APP_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_APP_PRIVATE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_ID_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GITHUB_CLIENT_SECRET_CONSOLE": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"GOOGLE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HONEYCOMB_API_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"HoneycombWebhookSecret": {
|
||||||
|
"type": "random.index/randomPassword.RandomPassword"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2AccessKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"R2SecretKey": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_ID": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_CLIENT_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"SALESFORCE_INSTANCE_URL": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_PUBLISHABLE_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_SECRET_KEY": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"STRIPE_WEBHOOK_SECRET": {
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"Teams": {
|
||||||
|
"type": "sst.cloudflare.SolidStart"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"Web": {
|
||||||
|
"type": "sst.cloudflare.Astro"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"WebApp": {
|
||||||
|
"type": "sst.cloudflare.StaticSite"
|
||||||
|
"url": string
|
||||||
|
}
|
||||||
|
"ZEN_BLACK_PRICE": {
|
||||||
|
"plan100": string
|
||||||
|
"plan20": string
|
||||||
|
"plan200": string
|
||||||
|
"product": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_LIMITS": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_LITE_PRICE": {
|
||||||
|
"firstMonth100Coupon": string
|
||||||
|
"firstMonth50Coupon": string
|
||||||
|
"price": string
|
||||||
|
"priceInr": number
|
||||||
|
"product": string
|
||||||
|
"sixMonths100Coupon": string
|
||||||
|
"threeMonths100Coupon": string
|
||||||
|
"twelveMonths100Coupon": string
|
||||||
|
"type": "sst.sst.Linkable"
|
||||||
|
}
|
||||||
|
"ZEN_MODELS1": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS10": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS11": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS12": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS13": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS14": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS15": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS16": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS17": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS18": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS19": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS2": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS20": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS21": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS22": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS23": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS24": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS25": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS26": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS27": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS28": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS29": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS3": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS30": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS4": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS5": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS6": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS7": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS8": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_MODELS9": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
"ZEN_SESSION_SECRET": {
|
||||||
|
"type": "sst.sst.Secret"
|
||||||
|
"value": string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// cloudflare
|
||||||
|
import * as cloudflare from "@cloudflare/workers-types";
|
||||||
|
declare module "sst" {
|
||||||
|
export interface Resource {
|
||||||
|
"Api": cloudflare.Service
|
||||||
|
"AuthApi": cloudflare.Service
|
||||||
|
"AuthStorage": cloudflare.KVNamespace
|
||||||
|
"Bucket": cloudflare.R2Bucket
|
||||||
|
"EnterpriseStorage": cloudflare.R2Bucket
|
||||||
|
"LogProcessor": cloudflare.Service
|
||||||
|
"Stat": cloudflare.Service
|
||||||
|
"ZenData": cloudflare.R2Bucket
|
||||||
|
"ZenDataNew": cloudflare.R2Bucket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import "sst"
|
import "sst"
|
||||||
export {}
|
export {}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"name": "@opencode-ai/core",
|
"name": "@opencode-ai/core",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"ai-gateway-provider": "3.1.2",
|
"ai-gateway-provider": "3.1.2",
|
||||||
"cross-spawn": "catalog:",
|
"cross-spawn": "catalog:",
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"gitlab-ai-provider": "6.7.0",
|
"gitlab-ai-provider": "6.6.0",
|
||||||
"glob": "13.0.5",
|
"glob": "13.0.5",
|
||||||
"google-auth-library": "10.5.0",
|
"google-auth-library": "10.5.0",
|
||||||
"immer": "11.1.4",
|
"immer": "11.1.4",
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
"minimatch": "10.2.5",
|
"minimatch": "10.2.5",
|
||||||
"npm-package-arg": "13.0.2",
|
"npm-package-arg": "13.0.2",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
"venice-ai-sdk-provider": "2.0.2",
|
"venice-ai-sdk-provider": "2.0.1",
|
||||||
"xdg-basedir": "5.1.0",
|
"xdg-basedir": "5.1.0",
|
||||||
"zod": "catalog:"
|
"zod": "catalog:"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,10 +3,9 @@ import { dirname, join, relative, resolve as pathResolve } from "path"
|
|||||||
import { realpathSync } from "fs"
|
import { realpathSync } from "fs"
|
||||||
import * as NFS from "fs/promises"
|
import * as NFS from "fs/promises"
|
||||||
import { lookup } from "mime-types"
|
import { lookup } from "mime-types"
|
||||||
import { Context, Effect, FileSystem, Layer, Schema } from "effect"
|
import { Effect, FileSystem, Layer, Schema, Context } from "effect"
|
||||||
import type { PlatformError } from "effect/PlatformError"
|
import type { PlatformError } from "effect/PlatformError"
|
||||||
import { Glob } from "./util/glob"
|
import { Glob } from "./util/glob"
|
||||||
import { serviceUse } from "./effect/service-use"
|
|
||||||
|
|
||||||
export namespace AppFileSystem {
|
export namespace AppFileSystem {
|
||||||
export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
|
export class FileSystemError extends Schema.TaggedErrorClass<FileSystemError>()("FileSystemError", {
|
||||||
@@ -40,8 +39,6 @@ export namespace AppFileSystem {
|
|||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/FileSystem") {}
|
export class Service extends Context.Service<Service, Interface>()("@opencode/FileSystem") {}
|
||||||
|
|
||||||
export const use = serviceUse(Service)
|
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
export * as Git from "./git"
|
|
||||||
|
|
||||||
import path from "path"
|
|
||||||
import { Context, Effect, Layer } from "effect"
|
|
||||||
import { ChildProcess } from "effect/unstable/process"
|
|
||||||
import { AbsolutePath } from "./schema"
|
|
||||||
import { AppFileSystem } from "./filesystem"
|
|
||||||
import { AppProcess } from "./process"
|
|
||||||
|
|
||||||
export interface Repo {
|
|
||||||
/**
|
|
||||||
* The root directory of the working tree that contains the input path.
|
|
||||||
*
|
|
||||||
* For `/home/me/app/src/file.ts` in a normal clone, this is `/home/me/app`.
|
|
||||||
* For `/home/me/app-feature/src/file.ts` in a linked worktree, this is
|
|
||||||
* `/home/me/app-feature`.
|
|
||||||
*/
|
|
||||||
readonly directory: AbsolutePath
|
|
||||||
/**
|
|
||||||
* The shared Git storage directory used by this repo and any linked worktrees.
|
|
||||||
*
|
|
||||||
* For a normal clone at `/home/me/app`, this is usually `/home/me/app/.git`.
|
|
||||||
* For a linked worktree at `/home/me/app-feature` whose main checkout is
|
|
||||||
* `/home/me/app`, this is usually `/home/me/app/.git`.
|
|
||||||
*/
|
|
||||||
readonly store: AbsolutePath
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Interface {
|
|
||||||
readonly find: (input: AbsolutePath) => Effect.Effect<Repo | undefined>
|
|
||||||
readonly remote: (repo: Repo, name?: string) => Effect.Effect<string | undefined>
|
|
||||||
readonly roots: (repo: Repo) => Effect.Effect<string[]>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/GitV2") {}
|
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
|
||||||
Service,
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const fs = yield* AppFileSystem.Service
|
|
||||||
const proc = yield* AppProcess.Service
|
|
||||||
|
|
||||||
const find = Effect.fn("Git.find")(function* (input: AbsolutePath) {
|
|
||||||
const dotgit = yield* fs.up({ targets: [".git"], start: input }).pipe(
|
|
||||||
Effect.map((matches) => matches[0]),
|
|
||||||
Effect.catch(() => Effect.succeed(undefined)),
|
|
||||||
)
|
|
||||||
if (!dotgit) return undefined
|
|
||||||
|
|
||||||
const cwd = path.dirname(dotgit)
|
|
||||||
const git = run(cwd, proc)
|
|
||||||
const topLevel = yield* git(["rev-parse", "--show-toplevel"])
|
|
||||||
const commonDir = yield* git(["rev-parse", "--git-common-dir"])
|
|
||||||
if (commonDir.exitCode !== 0) return undefined
|
|
||||||
|
|
||||||
return {
|
|
||||||
directory: AbsolutePath.make(topLevel.exitCode === 0 ? resolvePath(cwd, topLevel.text) : cwd),
|
|
||||||
store: AbsolutePath.make(resolvePath(cwd, commonDir.text)),
|
|
||||||
} satisfies Repo
|
|
||||||
})
|
|
||||||
|
|
||||||
const remote = Effect.fn("Git.remote")(function* (repo: Repo, name = "origin") {
|
|
||||||
const result = yield* run(repo.directory, proc)(["remote", "get-url", name])
|
|
||||||
if (result.exitCode !== 0) return undefined
|
|
||||||
return result.text.trim() || undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
const roots = Effect.fn("Git.roots")(function* (repo: Repo) {
|
|
||||||
const result = yield* run(repo.directory, proc)(["rev-list", "--max-parents=0", "HEAD"])
|
|
||||||
if (result.exitCode !== 0) return []
|
|
||||||
return result.text
|
|
||||||
.split("\n")
|
|
||||||
.map((item) => item.trim())
|
|
||||||
.filter(Boolean)
|
|
||||||
.toSorted()
|
|
||||||
})
|
|
||||||
|
|
||||||
return Service.of({ find, remote, roots })
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(
|
|
||||||
Layer.provide(AppFileSystem.defaultLayer),
|
|
||||||
Layer.provide(AppProcess.defaultLayer),
|
|
||||||
)
|
|
||||||
|
|
||||||
interface Result {
|
|
||||||
readonly exitCode: number
|
|
||||||
readonly text: string
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(cwd: string, proc: AppProcess.Interface) {
|
|
||||||
return (args: string[]) =>
|
|
||||||
proc
|
|
||||||
.run(
|
|
||||||
ChildProcess.make("git", args, {
|
|
||||||
cwd,
|
|
||||||
extendEnv: true,
|
|
||||||
stdin: "ignore",
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
Effect.map((result) => ({ exitCode: result.exitCode, text: result.stdout.toString("utf8") }) satisfies Result),
|
|
||||||
Effect.catch(() => Effect.succeed({ exitCode: 1, text: "" } satisfies Result)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolvePath(cwd: string, value: string) {
|
|
||||||
const trimmed = value.replace(/[\r\n]+$/, "")
|
|
||||||
if (!trimmed) return cwd
|
|
||||||
const normalized = AppFileSystem.windowsPath(trimmed)
|
|
||||||
if (path.isAbsolute(normalized)) return path.normalize(normalized)
|
|
||||||
return path.resolve(cwd, normalized)
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export * as PluginBoot from "./boot"
|
export * as PluginBoot from "./boot"
|
||||||
|
|
||||||
import { Context, Deferred, Effect, Layer } from "effect"
|
import { Context, Deferred, Effect, Layer, Scope } from "effect"
|
||||||
import { AccountV2 } from "../account"
|
import { AccountV2 } from "../account"
|
||||||
import { AgentV2 } from "../agent"
|
import { AgentV2 } from "../agent"
|
||||||
import { Catalog } from "../catalog"
|
import { Catalog } from "../catalog"
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ function resolveLocation(options: Record<string, any>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function vertexEndpoint(location: string) {
|
function vertexEndpoint(location: string) {
|
||||||
if (location === "global") return "aiplatform.googleapis.com"
|
return location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`
|
||||||
return `${location}-aiplatform.googleapis.com`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceVertexVars(value: string, project: string | undefined, location: string) {
|
function replaceVertexVars(value: string, project: string | undefined, location: string) {
|
||||||
@@ -132,25 +131,16 @@ export const GoogleVertexAnthropicPlugin = PluginV2.define({
|
|||||||
"aisdk.sdk": Effect.fn(function* (evt) {
|
"aisdk.sdk": Effect.fn(function* (evt) {
|
||||||
if (evt.package !== "@ai-sdk/google-vertex/anthropic") return
|
if (evt.package !== "@ai-sdk/google-vertex/anthropic") return
|
||||||
const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic"))
|
const mod = yield* Effect.promise(() => import("@ai-sdk/google-vertex/anthropic"))
|
||||||
const project =
|
|
||||||
typeof evt.options.project === "string"
|
|
||||||
? evt.options.project
|
|
||||||
: (process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCP_PROJECT ?? process.env.GCLOUD_PROJECT)
|
|
||||||
const location =
|
|
||||||
typeof evt.options.location === "string"
|
|
||||||
? evt.options.location
|
|
||||||
: (process.env.GOOGLE_CLOUD_LOCATION ?? process.env.VERTEX_LOCATION ?? "global")
|
|
||||||
evt.sdk = mod.createVertexAnthropic({
|
evt.sdk = mod.createVertexAnthropic({
|
||||||
...evt.options,
|
...evt.options,
|
||||||
project,
|
project:
|
||||||
location,
|
typeof evt.options.project === "string"
|
||||||
// Continental multi-regions (eu, us) require Regional Endpoint Platform
|
? evt.options.project
|
||||||
// domains; the default {region}-aiplatform.googleapis.com does not resolve.
|
: (process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GCP_PROJECT ?? process.env.GCLOUD_PROJECT),
|
||||||
...((location === "eu" || location === "us") && project && !evt.options.baseURL
|
location:
|
||||||
? {
|
typeof evt.options.location === "string"
|
||||||
baseURL: `https://aiplatform.${location}.rep.googleapis.com/v1/projects/${project}/locations/${location}/publishers/anthropic/models`,
|
? evt.options.location
|
||||||
}
|
: (process.env.GOOGLE_CLOUD_LOCATION ?? process.env.VERTEX_LOCATION ?? "global"),
|
||||||
: {}),
|
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
"aisdk.language": Effect.fn(function* (evt) {
|
"aisdk.language": Effect.fn(function* (evt) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { PluginV2 } from "../../plugin"
|
import { PluginV2 } from "../../plugin"
|
||||||
|
import { ProviderV2 } from "../../provider"
|
||||||
|
|
||||||
export const VercelPlugin = PluginV2.define({
|
export const VercelPlugin = PluginV2.define({
|
||||||
id: PluginV2.ID.make("vercel"),
|
id: PluginV2.ID.make("vercel"),
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
export * as Project from "./project"
|
|
||||||
|
|
||||||
import { Context, Effect, Layer, Schema } from "effect"
|
|
||||||
import path from "path"
|
|
||||||
import { AbsolutePath, withStatics } from "./schema"
|
|
||||||
import { AppFileSystem } from "./filesystem"
|
|
||||||
import { Git } from "./git"
|
|
||||||
import { Hash } from "./util/hash"
|
|
||||||
|
|
||||||
export const ID = Schema.String.pipe(
|
|
||||||
Schema.brand("Project.ID"),
|
|
||||||
withStatics((schema) => ({
|
|
||||||
global: schema.make("global"),
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
export type ID = typeof ID.Type
|
|
||||||
|
|
||||||
export const Vcs = Schema.Union([
|
|
||||||
Schema.Struct({
|
|
||||||
type: Schema.Literal("git"),
|
|
||||||
store: AbsolutePath,
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
export type Vcs = typeof Vcs.Type
|
|
||||||
|
|
||||||
export class Info extends Schema.Class<Info>("Project.Info")({
|
|
||||||
id: ID,
|
|
||||||
vcs: Schema.optional(Vcs),
|
|
||||||
}) {}
|
|
||||||
|
|
||||||
export interface Interface {
|
|
||||||
readonly resolve: (input: AbsolutePath) => Effect.Effect<
|
|
||||||
{
|
|
||||||
previous?: ID
|
|
||||||
id: ID
|
|
||||||
directory: AbsolutePath
|
|
||||||
vcs?: Vcs
|
|
||||||
},
|
|
||||||
never
|
|
||||||
>
|
|
||||||
/**
|
|
||||||
* Temporary bridge method for writing the resolved project ID to the repo-local cache.
|
|
||||||
*
|
|
||||||
* This exists while the old opencode project service and this core project
|
|
||||||
* service work together: core resolves the ID, while the old service still owns
|
|
||||||
* database migration and persistence. The old service should call this after it
|
|
||||||
* finishes migrating from `resolve().previous` to `resolve().id`; once project
|
|
||||||
* persistence moves into core, this separate bridge method can go away.
|
|
||||||
*/
|
|
||||||
readonly commit: (input: { store: AbsolutePath; id: ID }) => Effect.Effect<void>
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Service extends Context.Service<Service, Interface>()("@opencode/ProjectV2") {}
|
|
||||||
|
|
||||||
export const layer = Layer.effect(
|
|
||||||
Service,
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const fs = yield* AppFileSystem.Service
|
|
||||||
const git = yield* Git.Service
|
|
||||||
|
|
||||||
const cached = Effect.fnUntraced(function* (dir: string) {
|
|
||||||
return yield* fs.readFileString(path.join(dir, "opencode")).pipe(
|
|
||||||
Effect.map((value) => value.trim()),
|
|
||||||
Effect.map((value) => (value ? ID.make(value) : undefined)),
|
|
||||||
Effect.catch(() => Effect.succeed(undefined)),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const remote = Effect.fnUntraced(function* (repo: Git.Repo) {
|
|
||||||
const origin = yield* git.remote(repo)
|
|
||||||
if (!origin) return undefined
|
|
||||||
const normalized = url(origin)
|
|
||||||
if (!normalized) return undefined
|
|
||||||
return ID.make(Hash.fast(`git-remote:${normalized}`))
|
|
||||||
})
|
|
||||||
|
|
||||||
function url(input: string) {
|
|
||||||
const value = input.trim()
|
|
||||||
if (!value) return undefined
|
|
||||||
|
|
||||||
try {
|
|
||||||
const parsed = new URL(value)
|
|
||||||
if (parsed.protocol === "file:") return undefined
|
|
||||||
return parts(parsed.hostname, parsed.pathname)
|
|
||||||
} catch {
|
|
||||||
const scp = value.match(/^([^@/:]+@)?([^/:]+):(.+)$/)
|
|
||||||
if (scp) return parts(scp[2], scp[3])
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parts(host: string, name: string) {
|
|
||||||
const pathname = name
|
|
||||||
.replace(/^\/+/, "")
|
|
||||||
.replace(/\.git\/?$/, "")
|
|
||||||
.replace(/\/+$/, "")
|
|
||||||
if (!host || !pathname) return undefined
|
|
||||||
return `${host.toLowerCase()}/${pathname}`
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = Effect.fnUntraced(function* (repo: Git.Repo) {
|
|
||||||
const root = (yield* git.roots(repo))[0]
|
|
||||||
return root ? ID.make(root) : undefined
|
|
||||||
})
|
|
||||||
|
|
||||||
const resolve = Effect.fn("Project.resolve")(function* (input: AbsolutePath) {
|
|
||||||
const repo = yield* git.find(input)
|
|
||||||
if (!repo) return { id: ID.global, directory: input, vcs: undefined }
|
|
||||||
|
|
||||||
const previous = yield* cached(repo.store)
|
|
||||||
const id = (yield* remote(repo)) ?? previous ?? (yield* root(repo))
|
|
||||||
|
|
||||||
return {
|
|
||||||
previous,
|
|
||||||
id: id ?? ID.global,
|
|
||||||
directory: repo.directory,
|
|
||||||
vcs: { type: "git" as const, store: repo.store },
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const commit = Effect.fn("Project.commit")(function* (input: { store: AbsolutePath; id: ID }) {
|
|
||||||
yield* fs.writeFileString(path.join(input.store, "opencode"), input.id).pipe(Effect.ignore)
|
|
||||||
})
|
|
||||||
|
|
||||||
return Service.of({ resolve, commit })
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer), Layer.provide(Git.defaultLayer))
|
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
import { Option, Schema, SchemaGetter } from "effect"
|
import { Option, Schema, SchemaGetter } from "effect"
|
||||||
|
|
||||||
export const AbsolutePath = Schema.String.pipe(Schema.brand("AbsolutePath"))
|
|
||||||
export type AbsolutePath = typeof AbsolutePath.Type
|
|
||||||
|
|
||||||
export const RelativePath = Schema.String.pipe(Schema.brand("RelativePath"))
|
|
||||||
export type RelativePath = typeof RelativePath.Type
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integer greater than zero.
|
* Integer greater than zero.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,6 +65,5 @@ export abstract class NamedError extends Error {
|
|||||||
|
|
||||||
public static readonly Unknown = NamedError.create("UnknownError", {
|
public static readonly Unknown = NamedError.create("UnknownError", {
|
||||||
message: Schema.String,
|
message: Schema.String,
|
||||||
ref: Schema.optional(Schema.String),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Catalog } from "@opencode-ai/core/catalog"
|
|||||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||||
import { CerebrasPlugin } from "@opencode-ai/core/plugin/provider/cerebras"
|
import { CerebrasPlugin } from "@opencode-ai/core/plugin/provider/cerebras"
|
||||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||||
import { it, model } from "./provider-helper"
|
import { it, model, provider } from "./provider-helper"
|
||||||
|
|
||||||
const cerebrasOptions: Record<string, unknown>[] = []
|
const cerebrasOptions: Record<string, unknown>[] = []
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect } from "bun:test"
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
import { Catalog } from "@opencode-ai/core/catalog"
|
import { Catalog } from "@opencode-ai/core/catalog"
|
||||||
import { PluginV2 } from "@opencode-ai/core/plugin"
|
import { PluginV2 } from "@opencode-ai/core/plugin"
|
||||||
import { GoogleVertexAnthropicPlugin, GoogleVertexPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
|
import { GoogleVertexAnthropicPlugin } from "@opencode-ai/core/plugin/provider/google-vertex"
|
||||||
import { ProviderV2 } from "@opencode-ai/core/provider"
|
import { ProviderV2 } from "@opencode-ai/core/provider"
|
||||||
import { fakeSelectorSdk, it, model, withEnv } from "./provider-helper"
|
import { fakeSelectorSdk, it, model, withEnv } from "./provider-helper"
|
||||||
|
|
||||||
@@ -109,73 +109,6 @@ describe("GoogleVertexAnthropicPlugin", () => {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("creates SDKs for google-vertex Anthropic models with multi-region endpoints", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const plugin = yield* PluginV2.Service
|
|
||||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
|
||||||
const result = yield* plugin.trigger(
|
|
||||||
"aisdk.sdk",
|
|
||||||
{
|
|
||||||
model: model("google-vertex", "claude-sonnet-4-5"),
|
|
||||||
package: "@ai-sdk/google-vertex/anthropic",
|
|
||||||
options: { name: "google-vertex", project: "project", location: "eu" },
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe(
|
|
||||||
"https://aiplatform.eu.rep.googleapis.com/v1/projects/project/locations/eu/publishers/anthropic/models",
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.effect("keeps configured baseURL for google-vertex Anthropic models", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const plugin = yield* PluginV2.Service
|
|
||||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
|
||||||
const result = yield* plugin.trigger(
|
|
||||||
"aisdk.sdk",
|
|
||||||
{
|
|
||||||
model: model("google-vertex", "claude-sonnet-4-5"),
|
|
||||||
package: "@ai-sdk/google-vertex/anthropic",
|
|
||||||
options: { name: "google-vertex", project: "project", location: "eu", baseURL: "https://proxy.example/v1" },
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
expect(result.sdk.languageModel("claude-sonnet-4-5").config.baseURL).toBe("https://proxy.example/v1")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.effect("selects google-vertex Anthropic language models through V2 plugins", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const plugin = yield* PluginV2.Service
|
|
||||||
yield* plugin.add(GoogleVertexPlugin)
|
|
||||||
yield* plugin.add(GoogleVertexAnthropicPlugin)
|
|
||||||
const sdkResult = yield* plugin.trigger(
|
|
||||||
"aisdk.sdk",
|
|
||||||
{
|
|
||||||
model: model("google-vertex", " claude-sonnet-4-5 "),
|
|
||||||
package: "@ai-sdk/google-vertex/anthropic",
|
|
||||||
options: { name: "google-vertex", project: "project", location: "us" },
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
const languageResult = yield* plugin.trigger(
|
|
||||||
"aisdk.language",
|
|
||||||
{
|
|
||||||
model: model("google-vertex", " claude-sonnet-4-5 "),
|
|
||||||
sdk: sdkResult.sdk,
|
|
||||||
options: {},
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
)
|
|
||||||
const language = languageResult.language as unknown as { config: { baseURL: string }; modelId: string }
|
|
||||||
expect(language.config.baseURL).toBe(
|
|
||||||
"https://aiplatform.us.rep.googleapis.com/v1/projects/project/locations/us/publishers/anthropic/models",
|
|
||||||
)
|
|
||||||
expect(language.modelId).toBe("claude-sonnet-4-5")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.effect("trims model IDs before selecting language models", () =>
|
it.effect("trims model IDs before selecting language models", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const plugin = yield* PluginV2.Service
|
const plugin = yield* PluginV2.Service
|
||||||
|
|||||||
@@ -160,32 +160,6 @@ describe("GoogleVertexPlugin", () => {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.effect("keeps OpenAI-compatible Vertex endpoint templates regional for eu", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const plugin = yield* PluginV2.Service
|
|
||||||
const catalog = yield* Catalog.Service
|
|
||||||
yield* plugin.add(GoogleVertexPlugin)
|
|
||||||
const load = yield* catalog.loader()
|
|
||||||
yield* load((catalog) =>
|
|
||||||
catalog.provider.update(ProviderV2.ID.make("google-vertex"), (provider) => {
|
|
||||||
provider.endpoint = {
|
|
||||||
type: "aisdk",
|
|
||||||
package: "@ai-sdk/openai-compatible",
|
|
||||||
url: "https://${GOOGLE_VERTEX_ENDPOINT}/v1/projects/${GOOGLE_VERTEX_PROJECT}/locations/${GOOGLE_VERTEX_LOCATION}",
|
|
||||||
}
|
|
||||||
provider.options.aisdk.provider.project = "config-project"
|
|
||||||
provider.options.aisdk.provider.location = "eu"
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
const provider = yield* catalog.provider.get(ProviderV2.ID.make("google-vertex"))
|
|
||||||
expect(provider.endpoint).toEqual({
|
|
||||||
type: "aisdk",
|
|
||||||
package: "@ai-sdk/openai-compatible",
|
|
||||||
url: "https://eu-aiplatform.googleapis.com/v1/projects/config-project/locations/eu",
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.effect("defaults location to us-central1 when only project is configured", () =>
|
it.effect("defaults location to us-central1 when only project is configured", () =>
|
||||||
withEnv(
|
withEnv(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,220 +0,0 @@
|
|||||||
import { describe, expect } from "bun:test"
|
|
||||||
import { $ } from "bun"
|
|
||||||
import fs from "fs/promises"
|
|
||||||
import path from "path"
|
|
||||||
import { Effect } from "effect"
|
|
||||||
import { Project } from "@opencode-ai/core/project"
|
|
||||||
import { AbsolutePath } from "@opencode-ai/core/schema"
|
|
||||||
import { Hash } from "@opencode-ai/core/util/hash"
|
|
||||||
import { tmpdir } from "./fixture/tmpdir"
|
|
||||||
import { testEffect } from "./lib/effect"
|
|
||||||
|
|
||||||
const it = testEffect(Project.defaultLayer)
|
|
||||||
|
|
||||||
function remoteID(remote: string) {
|
|
||||||
return Project.ID.make(Hash.fast(`git-remote:${remote}`))
|
|
||||||
}
|
|
||||||
|
|
||||||
function abs(value: string) {
|
|
||||||
return AbsolutePath.make(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function real(value: string) {
|
|
||||||
return Effect.promise(() => fs.realpath(value)).pipe(Effect.map((value) => AbsolutePath.make(value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initRepo(dir: string, opts?: { commit?: boolean; remote?: string }) {
|
|
||||||
await $`git init`.cwd(dir).quiet()
|
|
||||||
await $`git config core.fsmonitor false`.cwd(dir).quiet()
|
|
||||||
await $`git config commit.gpgsign false`.cwd(dir).quiet()
|
|
||||||
await $`git config user.email test@opencode.test`.cwd(dir).quiet()
|
|
||||||
await $`git config user.name Test`.cwd(dir).quiet()
|
|
||||||
if (opts?.commit) await $`git commit --allow-empty -m root`.cwd(dir).quiet()
|
|
||||||
if (opts?.remote) await $`git remote add origin ${opts.remote}`.cwd(dir).quiet()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function rootCommit(dir: string) {
|
|
||||||
return (await $`git rev-list --max-parents=0 HEAD`.cwd(dir).text()).trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("ProjectV2.resolve", () => {
|
|
||||||
it.live("returns global for non-git directory", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.id).toBe(Project.ID.make("global"))
|
|
||||||
expect(path.resolve(result.directory)).toBe(path.resolve(tmp.path))
|
|
||||||
expect(result.previous).toBeUndefined()
|
|
||||||
expect(result.vcs).toBeUndefined()
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("returns git global for repo with no commits and no remote", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.id).toBe(Project.ID.make("global"))
|
|
||||||
expect(result.directory).toBe(yield* real(tmp.path))
|
|
||||||
expect(result.previous).toBeUndefined()
|
|
||||||
expect(result.vcs?.type).toBe("git")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("falls back to root commit when origin is missing", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.id).toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
|
|
||||||
expect(result.directory).toBe(yield* real(tmp.path))
|
|
||||||
expect(result.previous).toBeUndefined()
|
|
||||||
expect(result.vcs?.type).toBe("git")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("prefers normalized origin over root commit", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:Acme/App.git" }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.id).toBe(remoteID("github.com/Acme/App"))
|
|
||||||
expect(result.id).not.toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
|
|
||||||
expect(result.directory).toBe(yield* real(tmp.path))
|
|
||||||
expect(result.vcs?.type).toBe("git")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("normalizes ssh and https remotes to the same id", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const ssh = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
const https = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(ssh.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
|
|
||||||
yield* Effect.promise(() => initRepo(https.path, { commit: true, remote: "https://github.com/owner/repo.git" }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const a = yield* project.resolve(abs(ssh.path))
|
|
||||||
const b = yield* project.resolve(abs(https.path))
|
|
||||||
|
|
||||||
expect(a.id).toBe(remoteID("github.com/owner/repo"))
|
|
||||||
expect(b.id).toBe(a.id)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("ignores file remotes and falls back to root commit", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: `file://${tmp.path}` }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.id).toBe(Project.ID.make(yield* Effect.promise(() => rootCommit(tmp.path))))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("returns previous cached id from common dir", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
|
|
||||||
yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "opencode"), "old-id"))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(result.previous).toBe(Project.ID.make("old-id"))
|
|
||||||
expect(result.id).toBe(remoteID("github.com/owner/repo"))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("does not write the cache while resolving", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
yield* project.resolve(abs(tmp.path))
|
|
||||||
|
|
||||||
expect(yield* Effect.promise(() => Bun.file(path.join(tmp.path, ".git", "opencode")).exists())).toBe(false)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("resolves from nested directories to repo root", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true }))
|
|
||||||
yield* Effect.promise(() => fs.mkdir(path.join(tmp.path, "a", "b"), { recursive: true }))
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(path.join(tmp.path, "a", "b")))
|
|
||||||
|
|
||||||
expect(result.directory).toBe(yield* real(tmp.path))
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
it.live("linked worktree returns opened worktree directory and previous from common dir", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const tmp = yield* Effect.acquireRelease(
|
|
||||||
Effect.promise(() => tmpdir()),
|
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]()),
|
|
||||||
)
|
|
||||||
const worktree = `${tmp.path}-worktree`
|
|
||||||
yield* Effect.addFinalizer(() =>
|
|
||||||
Effect.promise(() => $`rm -rf ${worktree}`.quiet().nothrow()).pipe(Effect.ignore),
|
|
||||||
)
|
|
||||||
yield* Effect.promise(() => initRepo(tmp.path, { commit: true, remote: "git@github.com:owner/repo.git" }))
|
|
||||||
yield* Effect.promise(() => Bun.write(path.join(tmp.path, ".git", "opencode"), "old-id"))
|
|
||||||
yield* Effect.promise(() => $`git worktree add ${worktree} -b test-${Date.now()}`.cwd(tmp.path).quiet())
|
|
||||||
const project = yield* Project.Service
|
|
||||||
|
|
||||||
const result = yield* project.resolve(abs(worktree))
|
|
||||||
|
|
||||||
expect(result.directory).toBe(yield* real(worktree))
|
|
||||||
expect(result.previous).toBe(Project.ID.make("old-id"))
|
|
||||||
expect(result.id).toBe(remoteID("github.com/owner/repo"))
|
|
||||||
expect(result.vcs?.type).toBe("git")
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
@@ -3,15 +3,14 @@ import { defineConfig } from "electron-vite"
|
|||||||
import appPlugin from "@opencode-ai/app/vite"
|
import appPlugin from "@opencode-ai/app/vite"
|
||||||
import * as fs from "node:fs/promises"
|
import * as fs from "node:fs/promises"
|
||||||
|
|
||||||
const OPENCODE_SERVER_DIST = "../opencode/dist/node"
|
|
||||||
|
|
||||||
const channel = (() => {
|
const channel = (() => {
|
||||||
const raw = process.env.OPENCODE_CHANNEL
|
const raw = process.env.OPENCODE_CHANNEL
|
||||||
if (raw === "dev" || raw === "beta" || raw === "prod") return raw
|
if (raw === "dev" || raw === "beta" || raw === "prod") return raw
|
||||||
if (process.env.OPENCODE_CHANNEL === "latest") return "prod"
|
|
||||||
return "dev"
|
return "dev"
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
const OPENCODE_SERVER_DIST = "../opencode/dist/node"
|
||||||
|
|
||||||
const nodePtyPkg = `@lydell/node-pty-${process.platform}-${process.arch}`
|
const nodePtyPkg = `@lydell/node-pty-${process.platform}-${process.arch}`
|
||||||
|
|
||||||
const sentry =
|
const sentry =
|
||||||
@@ -83,6 +82,9 @@ export default defineConfig({
|
|||||||
plugins: [appPlugin, sentry],
|
plugins: [appPlugin, sentry],
|
||||||
publicDir: "../../../app/public",
|
publicDir: "../../../app/public",
|
||||||
root: "src/renderer",
|
root: "src/renderer",
|
||||||
|
define: {
|
||||||
|
"import.meta.env.VITE_OPENCODE_CHANNEL": JSON.stringify(channel),
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/desktop",
|
"name": "@opencode-ai/desktop",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"homepage": "https://opencode.ai",
|
"homepage": "https://opencode.ai",
|
||||||
@@ -24,7 +24,6 @@
|
|||||||
},
|
},
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zip.js/zip.js": "2.7.62",
|
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"electron-context-menu": "4.1.2",
|
"electron-context-menu": "4.1.2",
|
||||||
"electron-log": "^5",
|
"electron-log": "^5",
|
||||||
|
|||||||
@@ -7,5 +7,4 @@ export const CHANNEL: Channel = raw === "dev" || raw === "beta" || raw === "prod
|
|||||||
export const SETTINGS_STORE = "opencode.settings"
|
export const SETTINGS_STORE = "opencode.settings"
|
||||||
export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl"
|
export const DEFAULT_SERVER_URL_KEY = "defaultServerUrl"
|
||||||
export const WSL_ENABLED_KEY = "wslEnabled"
|
export const WSL_ENABLED_KEY = "wslEnabled"
|
||||||
export const PINCH_ZOOM_ENABLED_KEY = "pinchZoomEnabled"
|
|
||||||
export const UPDATER_ENABLED = app.isPackaged && CHANNEL !== "dev"
|
export const UPDATER_ENABLED = app.isPackaged && CHANNEL !== "dev"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import type { InitStep, ServerReadyData, SqliteMigrationProgress, WslConfig } fr
|
|||||||
import { checkAppExists, resolveAppPath, wslPath } from "./apps"
|
import { checkAppExists, resolveAppPath, wslPath } from "./apps"
|
||||||
import { CHANNEL, UPDATER_ENABLED } from "./constants"
|
import { CHANNEL, UPDATER_ENABLED } from "./constants"
|
||||||
import { registerIpcHandlers, sendDeepLinks, sendMenuCommand, sendSqliteMigrationProgress } from "./ipc"
|
import { registerIpcHandlers, sendDeepLinks, sendMenuCommand, sendSqliteMigrationProgress } from "./ipc"
|
||||||
import { exportDebugLogs, initCrashReporter, initLogging, startNetLog, write as writeLog } from "./logging"
|
import { initLogging } from "./logging"
|
||||||
import { parseMarkdown } from "./markdown"
|
import { parseMarkdown } from "./markdown"
|
||||||
import { createMenu } from "./menu"
|
import { createMenu } from "./menu"
|
||||||
import {
|
import {
|
||||||
@@ -31,7 +31,6 @@ import {
|
|||||||
createLoadingWindow,
|
createLoadingWindow,
|
||||||
createMainWindow,
|
createMainWindow,
|
||||||
registerRendererProtocol,
|
registerRendererProtocol,
|
||||||
setRelaunchHandler,
|
|
||||||
setBackgroundColor,
|
setBackgroundColor,
|
||||||
setDockIcon,
|
setDockIcon,
|
||||||
} from "./windows"
|
} from "./windows"
|
||||||
@@ -50,7 +49,6 @@ const APP_IDS: Record<string, string> = {
|
|||||||
prod: "ai.opencode.desktop",
|
prod: "ai.opencode.desktop",
|
||||||
}
|
}
|
||||||
const TEST_ONBOARDING = process.env.OPENCODE_TEST_ONBOARDING === "1"
|
const TEST_ONBOARDING = process.env.OPENCODE_TEST_ONBOARDING === "1"
|
||||||
const jsCallStackFeature = "DocumentPolicyIncludeJSCallStacksInCrashReports"
|
|
||||||
|
|
||||||
let logger: ReturnType<typeof initLogging>
|
let logger: ReturnType<typeof initLogging>
|
||||||
let mainWindow: BrowserWindow | null = null
|
let mainWindow: BrowserWindow | null = null
|
||||||
@@ -143,7 +141,6 @@ const main = Effect.gen(function* () {
|
|||||||
)
|
)
|
||||||
if (onboardingTestRoot) app.setPath("sessionData", join(onboardingTestRoot, "session"))
|
if (onboardingTestRoot) app.setPath("sessionData", join(onboardingTestRoot, "session"))
|
||||||
logger = initLogging()
|
logger = initLogging()
|
||||||
initCrashReporter()
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setDefaultCACertificates([...new Set([...getCACertificates("default"), ...getCACertificates("system")])])
|
setDefaultCACertificates([...new Set([...getCACertificates("default"), ...getCACertificates("system")])])
|
||||||
@@ -160,8 +157,6 @@ const main = Effect.gen(function* () {
|
|||||||
ensureLoopbackNoProxy()
|
ensureLoopbackNoProxy()
|
||||||
useEnvProxy()
|
useEnvProxy()
|
||||||
app.commandLine.appendSwitch("proxy-bypass-list", "<-loopback>")
|
app.commandLine.appendSwitch("proxy-bypass-list", "<-loopback>")
|
||||||
const features = app.commandLine.getSwitchValue("enable-features")
|
|
||||||
app.commandLine.appendSwitch("enable-features", features ? `${jsCallStackFeature},${features}` : jsCallStackFeature)
|
|
||||||
if (!app.isPackaged) app.commandLine.appendSwitch("remote-debugging-port", "9222")
|
if (!app.isPackaged) app.commandLine.appendSwitch("remote-debugging-port", "9222")
|
||||||
|
|
||||||
if (!app.requestSingleInstanceLock()) {
|
if (!app.requestSingleInstanceLock()) {
|
||||||
@@ -197,21 +192,6 @@ const main = Effect.gen(function* () {
|
|||||||
void killSidecar()
|
void killSidecar()
|
||||||
})
|
})
|
||||||
|
|
||||||
app.on("child-process-gone", (_event, details) => {
|
|
||||||
writeLog("utility", "child process gone", { details }, "error")
|
|
||||||
})
|
|
||||||
|
|
||||||
app.on("render-process-gone", (_event, webContents, details) => {
|
|
||||||
writeLog("window", "app render process gone", { url: webContents.getURL(), details }, "error")
|
|
||||||
})
|
|
||||||
|
|
||||||
setRelaunchHandler(() => {
|
|
||||||
void killSidecar().finally(() => {
|
|
||||||
app.relaunch()
|
|
||||||
app.exit(0)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
||||||
process.on(signal, () => {
|
process.on(signal, () => {
|
||||||
void killSidecar().finally(() => app.exit(0))
|
void killSidecar().finally(() => app.exit(0))
|
||||||
@@ -256,8 +236,6 @@ const main = Effect.gen(function* () {
|
|||||||
checkUpdate: async () => checkUpdate(),
|
checkUpdate: async () => checkUpdate(),
|
||||||
installUpdate: async () => installUpdate(killSidecar),
|
installUpdate: async () => installUpdate(killSidecar),
|
||||||
setBackgroundColor: (color) => setBackgroundColor(color),
|
setBackgroundColor: (color) => setBackgroundColor(color),
|
||||||
exportDebugLogs: () => exportDebugLogs(),
|
|
||||||
recordFatalRendererError: (error) => writeLog("renderer", "fatal renderer error", { ...error }, "error"),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
yield* Effect.promise(() => app.whenReady())
|
yield* Effect.promise(() => app.whenReady())
|
||||||
@@ -267,13 +245,6 @@ const main = Effect.gen(function* () {
|
|||||||
registerRendererProtocol()
|
registerRendererProtocol()
|
||||||
setDockIcon()
|
setDockIcon()
|
||||||
setupAutoUpdater()
|
setupAutoUpdater()
|
||||||
yield* Effect.promise(() => startNetLog()).pipe(
|
|
||||||
Effect.catch((error) =>
|
|
||||||
Effect.sync(() => {
|
|
||||||
logger.warn("failed to start net log", error)
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
const needsMigration = ((): boolean => {
|
const needsMigration = ((): boolean => {
|
||||||
if (process.env.OPENCODE_DB === ":memory:") return false
|
if (process.env.OPENCODE_DB === ":memory:") return false
|
||||||
@@ -329,9 +300,9 @@ const main = Effect.gen(function* () {
|
|||||||
needsMigration,
|
needsMigration,
|
||||||
userDataPath: app.getPath("userData"),
|
userDataPath: app.getPath("userData"),
|
||||||
onSqliteProgress: (progress) => initEmitter.emit("sqlite", progress),
|
onSqliteProgress: (progress) => initEmitter.emit("sqlite", progress),
|
||||||
onStdout: (message) => writeLog("server", "stdout", { message }),
|
onStdout: (message) => logger.log("sidecar stdout", { message }),
|
||||||
onStderr: (message) => writeLog("server", "stderr", { message }, "warn"),
|
onStderr: (message) => logger.warn("sidecar stderr", { message }),
|
||||||
onExit: (code) => writeLog("utility", "sidecar exited", { code }, "warn"),
|
onExit: (code) => logger.warn("sidecar exited", { code }),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
server = listener
|
server = listener
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu"
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
InitStep,
|
InitStep,
|
||||||
FatalRendererError,
|
|
||||||
ServerReadyData,
|
ServerReadyData,
|
||||||
SqliteMigrationProgress,
|
SqliteMigrationProgress,
|
||||||
TitlebarTheme,
|
TitlebarTheme,
|
||||||
@@ -14,7 +13,7 @@ import type {
|
|||||||
} from "../preload/types"
|
} from "../preload/types"
|
||||||
import { runDesktopMenuAction } from "./desktop-menu-actions"
|
import { runDesktopMenuAction } from "./desktop-menu-actions"
|
||||||
import { getStore } from "./store"
|
import { getStore } from "./store"
|
||||||
import { getPinchZoomEnabled, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
|
import { setTitlebar, updateTitlebar } from "./windows"
|
||||||
|
|
||||||
const pickerFilters = (ext?: string[]) => {
|
const pickerFilters = (ext?: string[]) => {
|
||||||
if (!ext || ext.length === 0) return undefined
|
if (!ext || ext.length === 0) return undefined
|
||||||
@@ -41,8 +40,6 @@ type Deps = {
|
|||||||
checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }>
|
checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }>
|
||||||
installUpdate: () => Promise<void> | void
|
installUpdate: () => Promise<void> | void
|
||||||
setBackgroundColor: (color: string) => void
|
setBackgroundColor: (color: string) => void
|
||||||
exportDebugLogs: () => Promise<string>
|
|
||||||
recordFatalRendererError: (error: FatalRendererError) => Promise<void> | void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerIpcHandlers(deps: Deps) {
|
export function registerIpcHandlers(deps: Deps) {
|
||||||
@@ -74,10 +71,6 @@ export function registerIpcHandlers(deps: Deps) {
|
|||||||
ipcMain.handle("check-update", () => deps.checkUpdate())
|
ipcMain.handle("check-update", () => deps.checkUpdate())
|
||||||
ipcMain.handle("install-update", () => deps.installUpdate())
|
ipcMain.handle("install-update", () => deps.installUpdate())
|
||||||
ipcMain.handle("set-background-color", (_event: IpcMainInvokeEvent, color: string) => deps.setBackgroundColor(color))
|
ipcMain.handle("set-background-color", (_event: IpcMainInvokeEvent, color: string) => deps.setBackgroundColor(color))
|
||||||
ipcMain.handle("export-debug-logs", () => deps.exportDebugLogs())
|
|
||||||
ipcMain.handle("record-fatal-renderer-error", (_event: IpcMainInvokeEvent, error: FatalRendererError) =>
|
|
||||||
deps.recordFatalRendererError(error),
|
|
||||||
)
|
|
||||||
ipcMain.handle("store-get", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
ipcMain.handle("store-get", (_event: IpcMainInvokeEvent, name: string, key: string) => {
|
||||||
try {
|
try {
|
||||||
const store = getStore(name)
|
const store = getStore(name)
|
||||||
@@ -202,10 +195,6 @@ export function registerIpcHandlers(deps: Deps) {
|
|||||||
if (!win) return
|
if (!win) return
|
||||||
updateTitlebar(win)
|
updateTitlebar(win)
|
||||||
})
|
})
|
||||||
ipcMain.handle("get-pinch-zoom-enabled", () => getPinchZoomEnabled())
|
|
||||||
ipcMain.handle("set-pinch-zoom-enabled", (_event: IpcMainInvokeEvent, enabled: boolean) => {
|
|
||||||
setPinchZoomEnabled(enabled)
|
|
||||||
})
|
|
||||||
ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
|
ipcMain.handle("set-titlebar", (event: IpcMainInvokeEvent, theme: TitlebarTheme) => {
|
||||||
const win = BrowserWindow.fromWebContents(event.sender)
|
const win = BrowserWindow.fromWebContents(event.sender)
|
||||||
if (!win) return
|
if (!win) return
|
||||||
|
|||||||
@@ -1,92 +1,21 @@
|
|||||||
import { MainLogger } from "electron-log"
|
import { MainLogger } from "electron-log"
|
||||||
import log from "electron-log/main.js"
|
import log from "electron-log/main.js"
|
||||||
import { app, crashReporter, netLog, shell } from "electron"
|
import { readFileSync, readdirSync, statSync, unlinkSync } from "node:fs"
|
||||||
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs"
|
|
||||||
import { ZipWriter, BlobWriter, BlobReader } from "@zip.js/zip.js"
|
|
||||||
import { dirname, join } from "node:path"
|
import { dirname, join } from "node:path"
|
||||||
import { homedir } from "node:os"
|
|
||||||
|
|
||||||
const MAX_LOG_AGE_DAYS = 7
|
const MAX_LOG_AGE_DAYS = 7
|
||||||
const TAIL_LINES = 1000
|
const TAIL_LINES = 1000
|
||||||
const EXPORT_WINDOW = 24 * 60 * 60 * 1000
|
|
||||||
const MAX_EXPORT_FILE_SIZE = 50 * 1024 * 1024
|
|
||||||
const NET_LOG_SIZE = 20 * 1024 * 1024
|
|
||||||
|
|
||||||
let root = ""
|
|
||||||
let run = ""
|
|
||||||
let netLogPath: string | undefined
|
|
||||||
|
|
||||||
let logger: MainLogger
|
let logger: MainLogger
|
||||||
export const getLogger = () => logger
|
export const getLogger = () => logger
|
||||||
|
|
||||||
export function initLogging() {
|
export function initLogging() {
|
||||||
initRunDirectory()
|
|
||||||
log.transports.file.maxSize = 5 * 1024 * 1024
|
log.transports.file.maxSize = 5 * 1024 * 1024
|
||||||
log.transports.file.resolvePathFn = (_vars, message) =>
|
|
||||||
join(
|
|
||||||
run,
|
|
||||||
`${safeLogName(message?.scope ?? (message?.variables?.processType === "renderer" ? "renderer" : "main"))}.log`,
|
|
||||||
)
|
|
||||||
log.initialize({ preload: false, spyRendererConsole: true })
|
|
||||||
initConsoleTransport()
|
initConsoleTransport()
|
||||||
cleanup()
|
cleanup()
|
||||||
return (logger = log)
|
return (logger = log)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initCrashReporter() {
|
|
||||||
const dir = join(app.getPath("userData"), "Crashpad")
|
|
||||||
mkdirSync(dir, { recursive: true })
|
|
||||||
app.setPath("crashDumps", dir)
|
|
||||||
crashReporter.start({ uploadToServer: false, compress: true })
|
|
||||||
write("crash", "crash reporter started", { path: dir })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startNetLog() {
|
|
||||||
if (netLog.currentlyLogging) return
|
|
||||||
netLogPath = join(run, "network.netlog")
|
|
||||||
await netLog.startLogging(netLogPath, { captureMode: "default", maxFileSize: NET_LOG_SIZE })
|
|
||||||
write("network", "net log started", { path: netLogPath })
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function exportDebugLogs() {
|
|
||||||
const restartNetLog = netLog.currentlyLogging
|
|
||||||
if (restartNetLog) {
|
|
||||||
await netLog.stopLogging().catch((error) => write("network", "failed to stop net log", { error }))
|
|
||||||
}
|
|
||||||
|
|
||||||
const output = join(app.getPath("downloads"), `opencode-debug-${stamp()}.zip`)
|
|
||||||
try {
|
|
||||||
write("main", "exporting debug logs", { output })
|
|
||||||
await writeZip(output, [
|
|
||||||
{ name: "manifest.json", data: Buffer.from(JSON.stringify(manifest(), null, 2)) },
|
|
||||||
...collect(root, "desktop"),
|
|
||||||
...serverLogRoots().flatMap((dir, i) => collect(dir, `server-${i + 1}`)),
|
|
||||||
...collect(app.getPath("crashDumps"), "crashpad"),
|
|
||||||
])
|
|
||||||
shell.showItemInFolder(output)
|
|
||||||
return output
|
|
||||||
} finally {
|
|
||||||
if (restartNetLog) {
|
|
||||||
await startNetLog().catch((error) => write("network", "failed to restart net log", { error }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function write(
|
|
||||||
name: string,
|
|
||||||
message: string,
|
|
||||||
extra?: Record<string, unknown>,
|
|
||||||
level: "info" | "warn" | "error" = "info",
|
|
||||||
) {
|
|
||||||
if (!run) return
|
|
||||||
const scoped = log.scope(safeLogName(name))
|
|
||||||
if (extra !== undefined) {
|
|
||||||
scoped[level](message, extra)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
scoped[level](message)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function tail(): string {
|
export function tail(): string {
|
||||||
try {
|
try {
|
||||||
const path = log.transports.file.getFile().path
|
const path = log.transports.file.getFile().path
|
||||||
@@ -98,96 +27,23 @@ export function tail(): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRunDirectory() {
|
|
||||||
root = join(app.getPath("userData"), "logs")
|
|
||||||
run = join(root, stamp())
|
|
||||||
mkdirSync(run, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
function stamp() {
|
|
||||||
return new Date()
|
|
||||||
.toISOString()
|
|
||||||
.replace(/[-:]/g, "")
|
|
||||||
.replace(/\.\d+Z$/, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeLogName(name: string) {
|
|
||||||
return name.replace(/[^a-z0-9_.-]/gi, "_") || "main"
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup() {
|
function cleanup() {
|
||||||
const dir = root || dirname(log.transports.file.getFile().path)
|
const path = log.transports.file.getFile().path
|
||||||
|
const dir = dirname(path)
|
||||||
const cutoff = Date.now() - MAX_LOG_AGE_DAYS * 24 * 60 * 60 * 1000
|
const cutoff = Date.now() - MAX_LOG_AGE_DAYS * 24 * 60 * 60 * 1000
|
||||||
|
|
||||||
for (const entry of readdirSync(dir)) {
|
for (const entry of readdirSync(dir)) {
|
||||||
const file = join(dir, entry)
|
const file = join(dir, entry)
|
||||||
try {
|
try {
|
||||||
const info = statSync(file)
|
const info = statSync(file)
|
||||||
if (info.mtimeMs < cutoff) rmSync(file, { recursive: true, force: true })
|
if (!info.isFile()) continue
|
||||||
|
if (info.mtimeMs < cutoff) unlinkSync(file)
|
||||||
} catch {
|
} catch {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function manifest() {
|
|
||||||
return {
|
|
||||||
generated: new Date().toISOString(),
|
|
||||||
version: app.getVersion(),
|
|
||||||
name: app.getName(),
|
|
||||||
packaged: app.isPackaged,
|
|
||||||
platform: process.platform,
|
|
||||||
arch: process.arch,
|
|
||||||
versions: process.versions,
|
|
||||||
uptime: process.uptime(),
|
|
||||||
userData: app.getPath("userData"),
|
|
||||||
logs: root,
|
|
||||||
currentRun: run,
|
|
||||||
crashDumps: app.getPath("crashDumps"),
|
|
||||||
serverLogs: serverLogRoots(),
|
|
||||||
netLog: netLogPath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function serverLogRoots() {
|
|
||||||
const xdgData = process.env.XDG_DATA_HOME || join(homedir(), ".local", "share")
|
|
||||||
return [...new Set([join(xdgData, "opencode", "log"), join(app.getPath("userData"), "opencode", "log")])]
|
|
||||||
}
|
|
||||||
|
|
||||||
type Entry = { name: string; path?: string; data?: Buffer }
|
|
||||||
|
|
||||||
function collect(dir: string, prefix: string): Entry[] {
|
|
||||||
if (!existsSync(dir)) return []
|
|
||||||
const cutoff = Date.now() - EXPORT_WINDOW
|
|
||||||
const result: Entry[] = []
|
|
||||||
const walk = (current: string) => {
|
|
||||||
for (const entry of readdirSync(current)) {
|
|
||||||
const file = join(current, entry)
|
|
||||||
const info = statSync(file)
|
|
||||||
if (info.isDirectory()) {
|
|
||||||
walk(file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if (info.mtimeMs < cutoff) continue
|
|
||||||
if (info.size > MAX_EXPORT_FILE_SIZE) continue
|
|
||||||
if (file.endsWith(".heapsnapshot")) continue
|
|
||||||
result.push({ name: join(prefix, file.slice(dir.length + 1)).replace(/\\/g, "/"), path: file })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
walk(dir)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
async function writeZip(output: string, entries: Entry[]) {
|
|
||||||
const writer = new ZipWriter(new BlobWriter("application/zip"))
|
|
||||||
for (const entry of entries) {
|
|
||||||
const data = entry.data ?? readFileSync(entry.path!)
|
|
||||||
await writer.add(entry.name, new BlobReader(new Blob([new Uint8Array(data)])))
|
|
||||||
}
|
|
||||||
const zip = await writer.close()
|
|
||||||
writeFileSync(output, Buffer.from(await zip.arrayBuffer()))
|
|
||||||
}
|
|
||||||
|
|
||||||
function initConsoleTransport() {
|
function initConsoleTransport() {
|
||||||
const write = log.transports.console.writeFn.bind(log.transports.console)
|
const write = log.transports.console.writeFn.bind(log.transports.console)
|
||||||
log.transports.console.writeFn = (options) => {
|
log.transports.console.writeFn = (options) => {
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
import type { BrowserWindow } from "electron"
|
|
||||||
import { write as writeLog } from "./logging"
|
|
||||||
|
|
||||||
const sampleInterval = 1000
|
|
||||||
const samplePeriod = 15000
|
|
||||||
|
|
||||||
export function createUnresponsiveSampler(win: BrowserWindow, name: string) {
|
|
||||||
let sampleTimer: ReturnType<typeof setTimeout> | undefined
|
|
||||||
let stopTimer: ReturnType<typeof setTimeout> | undefined
|
|
||||||
let sampling = false
|
|
||||||
const samples = new Map<string, number>()
|
|
||||||
|
|
||||||
const active = () => sampling && !win.isDestroyed() && !win.webContents.isDestroyed()
|
|
||||||
const clearTimers = () => {
|
|
||||||
if (sampleTimer) clearTimeout(sampleTimer)
|
|
||||||
if (stopTimer) clearTimeout(stopTimer)
|
|
||||||
sampleTimer = undefined
|
|
||||||
stopTimer = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const schedule = () => {
|
|
||||||
sampleTimer = setTimeout(() => {
|
|
||||||
void collect()
|
|
||||||
}, sampleInterval)
|
|
||||||
}
|
|
||||||
|
|
||||||
const collect = async () => {
|
|
||||||
if (!active()) return
|
|
||||||
const stack = await win.webContents.mainFrame.collectJavaScriptCallStack().catch((error) => {
|
|
||||||
writeLog("window", "failed to collect unresponsive sample", { window: name, error }, "error")
|
|
||||||
return undefined
|
|
||||||
})
|
|
||||||
if (!active()) return
|
|
||||||
if (stack) samples.set(stack, (samples.get(stack) ?? 0) + 1)
|
|
||||||
schedule()
|
|
||||||
}
|
|
||||||
|
|
||||||
const stopAndFlush = () => {
|
|
||||||
const wasSampling = sampling
|
|
||||||
sampling = false
|
|
||||||
clearTimers()
|
|
||||||
if (samples.size === 0) return wasSampling
|
|
||||||
|
|
||||||
const entries = [...samples.entries()].sort((a, b) => b[1] - a[1])
|
|
||||||
const total = entries.reduce((sum, entry) => sum + entry[1], 0)
|
|
||||||
const message = [
|
|
||||||
"renderer unresponsive samples",
|
|
||||||
`Window: ${name}`,
|
|
||||||
`URL: ${win.isDestroyed() ? "<destroyed>" : win.webContents.getURL()}`,
|
|
||||||
...entries.map((entry) => `<${entry[1]}> ${entry[0]}`),
|
|
||||||
`Total Samples: ${total}`,
|
|
||||||
].join("\n")
|
|
||||||
writeLog("window", message, undefined, "error")
|
|
||||||
samples.clear()
|
|
||||||
return wasSampling
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = () => {
|
|
||||||
if (sampling || win.isDestroyed() || win.webContents.isDestroyed() || win.webContents.isDevToolsOpened()) return
|
|
||||||
sampling = true
|
|
||||||
samples.clear()
|
|
||||||
schedule()
|
|
||||||
stopTimer = setTimeout(stopAndFlush, samplePeriod)
|
|
||||||
}
|
|
||||||
|
|
||||||
win.on("closed", stopAndFlush)
|
|
||||||
|
|
||||||
return { start, stopAndFlush }
|
|
||||||
}
|
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
import { app, dialog } from "electron"
|
import { app, dialog } from "electron"
|
||||||
import pkg from "electron-updater"
|
import pkg from "electron-updater"
|
||||||
import { UPDATER_ENABLED } from "./constants"
|
import { UPDATER_ENABLED } from "./constants"
|
||||||
import { getLogger } from "./logging"
|
import { initLogging } from "./logging"
|
||||||
|
|
||||||
|
const logger = initLogging()
|
||||||
const { autoUpdater } = pkg
|
const { autoUpdater } = pkg
|
||||||
type UpdateCheckResult = { updateAvailable: boolean; version?: string; failed?: boolean }
|
|
||||||
let downloadedVersion: string | undefined
|
|
||||||
let pendingCheck: Promise<UpdateCheckResult> | undefined
|
|
||||||
|
|
||||||
export function setupAutoUpdater() {
|
export function setupAutoUpdater() {
|
||||||
if (!UPDATER_ENABLED) return
|
if (!UPDATER_ENABLED) return
|
||||||
const logger = getLogger()
|
|
||||||
autoUpdater.logger = logger
|
autoUpdater.logger = logger
|
||||||
autoUpdater.channel = "latest"
|
autoUpdater.channel = "latest"
|
||||||
autoUpdater.allowPrerelease = false
|
autoUpdater.allowPrerelease = false
|
||||||
@@ -25,19 +22,8 @@ export function setupAutoUpdater() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkUpdate(): Promise<UpdateCheckResult> {
|
export async function checkUpdate() {
|
||||||
if (!UPDATER_ENABLED) return { updateAvailable: false }
|
if (!UPDATER_ENABLED) return { updateAvailable: false }
|
||||||
if (downloadedVersion) return { updateAvailable: true, version: downloadedVersion }
|
|
||||||
if (pendingCheck) return pendingCheck
|
|
||||||
|
|
||||||
pendingCheck = checkAndDownloadUpdate().finally(() => {
|
|
||||||
pendingCheck = undefined
|
|
||||||
})
|
|
||||||
return pendingCheck
|
|
||||||
}
|
|
||||||
|
|
||||||
async function checkAndDownloadUpdate(): Promise<UpdateCheckResult> {
|
|
||||||
const logger = getLogger()
|
|
||||||
logger.log("checking for updates", {
|
logger.log("checking for updates", {
|
||||||
currentVersion: app.getVersion(),
|
currentVersion: app.getVersion(),
|
||||||
channel: autoUpdater.channel,
|
channel: autoUpdater.channel,
|
||||||
@@ -62,7 +48,6 @@ async function checkAndDownloadUpdate(): Promise<UpdateCheckResult> {
|
|||||||
}
|
}
|
||||||
logger.log("update available", { version })
|
logger.log("update available", { version })
|
||||||
await autoUpdater.downloadUpdate()
|
await autoUpdater.downloadUpdate()
|
||||||
downloadedVersion = version
|
|
||||||
logger.log("update download completed", { version })
|
logger.log("update download completed", { version })
|
||||||
return { updateAvailable: true, version }
|
return { updateAvailable: true, version }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -72,9 +57,8 @@ async function checkAndDownloadUpdate(): Promise<UpdateCheckResult> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function installUpdate(killSidecar: () => Promise<void>) {
|
export async function installUpdate(killSidecar: () => Promise<void>) {
|
||||||
const result = downloadedVersion ? { updateAvailable: true, version: downloadedVersion } : await checkUpdate()
|
const result = await checkUpdate()
|
||||||
const logger = getLogger()
|
if (!result.updateAvailable) {
|
||||||
if (!result.updateAvailable || !downloadedVersion) {
|
|
||||||
logger.log("install update skipped", {
|
logger.log("install update skipped", {
|
||||||
reason: result.failed ? "update check failed" : "no update available",
|
reason: result.failed ? "update check failed" : "no update available",
|
||||||
})
|
})
|
||||||
@@ -89,7 +73,6 @@ export async function installUpdate(killSidecar: () => Promise<void>) {
|
|||||||
|
|
||||||
export async function checkForUpdates(alertOnFail: boolean, killSidecar: () => Promise<void>) {
|
export async function checkForUpdates(alertOnFail: boolean, killSidecar: () => Promise<void>) {
|
||||||
if (!UPDATER_ENABLED) return
|
if (!UPDATER_ENABLED) return
|
||||||
const logger = getLogger()
|
|
||||||
logger.log("checkForUpdates invoked", { alertOnFail })
|
logger.log("checkForUpdates invoked", { alertOnFail })
|
||||||
const result = await checkUpdate()
|
const result = await checkUpdate()
|
||||||
if (!result.updateAvailable) {
|
if (!result.updateAvailable) {
|
||||||
|
|||||||
@@ -1,15 +1,8 @@
|
|||||||
import windowState from "electron-window-state"
|
import windowState from "electron-window-state"
|
||||||
import { resolveThemeVariant } from "@opencode-ai/ui/theme/resolve"
|
import { app, BrowserWindow, net, nativeImage, nativeTheme, protocol } from "electron"
|
||||||
import type { DesktopTheme } from "@opencode-ai/ui/theme/types"
|
|
||||||
import oc2ThemeJson from "../../../ui/src/theme/themes/oc-2.json"
|
|
||||||
import { app, BrowserWindow, dialog, net, nativeImage, nativeTheme, protocol } from "electron"
|
|
||||||
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
||||||
import { fileURLToPath, pathToFileURL } from "node:url"
|
import { fileURLToPath, pathToFileURL } from "node:url"
|
||||||
import type { TitlebarTheme } from "../preload/types"
|
import type { TitlebarTheme } from "../preload/types"
|
||||||
import { PINCH_ZOOM_ENABLED_KEY } from "./constants"
|
|
||||||
import { exportDebugLogs, write as writeLog } from "./logging"
|
|
||||||
import { getStore } from "./store"
|
|
||||||
import { createUnresponsiveSampler } from "./unresponsive"
|
|
||||||
|
|
||||||
const root = dirname(fileURLToPath(import.meta.url))
|
const root = dirname(fileURLToPath(import.meta.url))
|
||||||
const rendererRoot = join(root, "../renderer")
|
const rendererRoot = join(root, "../renderer")
|
||||||
@@ -18,13 +11,6 @@ const rendererHost = "renderer"
|
|||||||
const clipboardWritePermission = "clipboard-sanitized-write"
|
const clipboardWritePermission = "clipboard-sanitized-write"
|
||||||
const notificationPermission = "notifications"
|
const notificationPermission = "notifications"
|
||||||
const rendererPermissions = new Set([clipboardWritePermission, notificationPermission])
|
const rendererPermissions = new Set([clipboardWritePermission, notificationPermission])
|
||||||
const oc2Theme = oc2ThemeJson as DesktopTheme
|
|
||||||
const oc2Background = {
|
|
||||||
light: resolveThemeVariant(oc2Theme.light, false)["background-base"],
|
|
||||||
dark: resolveThemeVariant(oc2Theme.dark, true)["background-base"],
|
|
||||||
}
|
|
||||||
const documentPolicyHeader = "Document-Policy"
|
|
||||||
const jsCallStacksDocumentPolicy = "include-js-call-stacks-in-crash-reports"
|
|
||||||
|
|
||||||
protocol.registerSchemesAsPrivileged([
|
protocol.registerSchemesAsPrivileged([
|
||||||
{
|
{
|
||||||
@@ -38,23 +24,11 @@ protocol.registerSchemesAsPrivileged([
|
|||||||
])
|
])
|
||||||
|
|
||||||
let backgroundColor: string | undefined
|
let backgroundColor: string | undefined
|
||||||
let relaunchHandler = () => {
|
|
||||||
app.relaunch()
|
|
||||||
app.exit(0)
|
|
||||||
}
|
|
||||||
const titlebarThemes = new WeakMap<BrowserWindow, Partial<TitlebarTheme>>()
|
const titlebarThemes = new WeakMap<BrowserWindow, Partial<TitlebarTheme>>()
|
||||||
const pinchZoomEnabled = new WeakMap<BrowserWindow, boolean>()
|
|
||||||
const titlebarHeight = 40
|
const titlebarHeight = 40
|
||||||
const maxZoomLevel = 10
|
|
||||||
const minZoomLevel = 0.2
|
|
||||||
|
|
||||||
export function setRelaunchHandler(handler: () => void) {
|
|
||||||
relaunchHandler = handler
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setBackgroundColor(color: string) {
|
export function setBackgroundColor(color: string) {
|
||||||
backgroundColor = color
|
backgroundColor = color
|
||||||
BrowserWindow.getAllWindows().forEach((win) => win.setBackgroundColor(color))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBackgroundColor(): string | undefined {
|
export function getBackgroundColor(): string | undefined {
|
||||||
@@ -74,10 +48,6 @@ function tone() {
|
|||||||
return nativeTheme.shouldUseDarkColors ? "dark" : "light"
|
return nativeTheme.shouldUseDarkColors ? "dark" : "light"
|
||||||
}
|
}
|
||||||
|
|
||||||
function defaultBackgroundColor() {
|
|
||||||
return oc2Background[tone()]
|
|
||||||
}
|
|
||||||
|
|
||||||
function overlay(theme: Partial<TitlebarTheme> = {}, zoom = 1) {
|
function overlay(theme: Partial<TitlebarTheme> = {}, zoom = 1) {
|
||||||
const mode = theme.mode ?? tone()
|
const mode = theme.mode ?? tone()
|
||||||
return {
|
return {
|
||||||
@@ -97,20 +67,6 @@ export function updateTitlebar(win: BrowserWindow) {
|
|||||||
win.setTitleBarOverlay(overlay(titlebarThemes.get(win), win.webContents.getZoomFactor()))
|
win.setTitleBarOverlay(overlay(titlebarThemes.get(win), win.webContents.getZoomFactor()))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setPinchZoomEnabled(enabled: boolean) {
|
|
||||||
getStore().set(PINCH_ZOOM_ENABLED_KEY, enabled)
|
|
||||||
for (const win of BrowserWindow.getAllWindows()) {
|
|
||||||
pinchZoomEnabled.set(win, enabled)
|
|
||||||
win.webContents.send("pinch-zoom-enabled-changed", enabled)
|
|
||||||
if (!enabled && win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
|
|
||||||
updateZoom(win)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPinchZoomEnabled() {
|
|
||||||
return getStore().get(PINCH_ZOOM_ENABLED_KEY) === true
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setDockIcon() {
|
export function setDockIcon() {
|
||||||
if (process.platform !== "darwin") return
|
if (process.platform !== "darwin") return
|
||||||
const icon = nativeImage.createFromPath(join(iconsDir(), "dock.png"))
|
const icon = nativeImage.createFromPath(join(iconsDir(), "dock.png"))
|
||||||
@@ -133,7 +89,7 @@ export function createMainWindow() {
|
|||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
title: "OpenCode",
|
title: "OpenCode",
|
||||||
icon: iconPath(),
|
icon: iconPath(),
|
||||||
backgroundColor: backgroundColor ?? defaultBackgroundColor(),
|
backgroundColor,
|
||||||
...(process.platform === "darwin"
|
...(process.platform === "darwin"
|
||||||
? {
|
? {
|
||||||
titleBarStyle: "hidden" as const,
|
titleBarStyle: "hidden" as const,
|
||||||
@@ -156,7 +112,6 @@ export function createMainWindow() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
allowRendererPermissions(win)
|
allowRendererPermissions(win)
|
||||||
wireWindowRecovery(win, "main")
|
|
||||||
|
|
||||||
win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => {
|
win.webContents.session.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||||
const { requestHeaders } = details
|
const { requestHeaders } = details
|
||||||
@@ -166,7 +121,8 @@ export function createMainWindow() {
|
|||||||
|
|
||||||
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
win.webContents.session.webRequest.onHeadersReceived((details, callback) => {
|
||||||
const { responseHeaders = {} } = details
|
const { responseHeaders = {} } = details
|
||||||
addRendererHeaders(details.url, responseHeaders)
|
upsertKeyValue(responseHeaders, "Access-Control-Allow-Origin", ["*"])
|
||||||
|
upsertKeyValue(responseHeaders, "Access-Control-Allow-Headers", ["*"])
|
||||||
callback({ responseHeaders })
|
callback({ responseHeaders })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -191,7 +147,7 @@ export function createLoadingWindow() {
|
|||||||
show: true,
|
show: true,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
icon: iconPath(),
|
icon: iconPath(),
|
||||||
backgroundColor: backgroundColor ?? defaultBackgroundColor(),
|
backgroundColor,
|
||||||
...(process.platform === "darwin" ? { titleBarStyle: "hidden" as const } : {}),
|
...(process.platform === "darwin" ? { titleBarStyle: "hidden" as const } : {}),
|
||||||
...(process.platform === "win32"
|
...(process.platform === "win32"
|
||||||
? {
|
? {
|
||||||
@@ -209,7 +165,6 @@ export function createLoadingWindow() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
allowRendererPermissions(win)
|
allowRendererPermissions(win)
|
||||||
wireWindowRecovery(win, "loading")
|
|
||||||
|
|
||||||
loadWindow(win, "loading.html")
|
loadWindow(win, "loading.html")
|
||||||
|
|
||||||
@@ -219,40 +174,19 @@ export function createLoadingWindow() {
|
|||||||
export function registerRendererProtocol() {
|
export function registerRendererProtocol() {
|
||||||
if (protocol.isProtocolHandled(rendererProtocol)) return
|
if (protocol.isProtocolHandled(rendererProtocol)) return
|
||||||
|
|
||||||
protocol.handle(rendererProtocol, async (request) => {
|
protocol.handle(rendererProtocol, (request) => {
|
||||||
const url = new URL(request.url)
|
const url = new URL(request.url)
|
||||||
if (url.host !== rendererHost) {
|
if (url.host !== rendererHost) {
|
||||||
writeLog("protocol", "rejected host", { url: request.url }, "warn")
|
|
||||||
return new Response("Not found", { status: 404 })
|
return new Response("Not found", { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = resolve(rendererRoot, `.${decodeURIComponent(url.pathname)}`)
|
const file = resolve(rendererRoot, `.${decodeURIComponent(url.pathname)}`)
|
||||||
const rel = relative(rendererRoot, file)
|
const rel = relative(rendererRoot, file)
|
||||||
if (rel.startsWith("..") || isAbsolute(rel)) {
|
if (rel.startsWith("..") || isAbsolute(rel)) {
|
||||||
writeLog("protocol", "rejected path", { url: request.url, file }, "warn")
|
|
||||||
return new Response("Not found", { status: 404 })
|
return new Response("Not found", { status: 404 })
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return net.fetch(pathToFileURL(file).toString())
|
||||||
const response = await net.fetch(pathToFileURL(file).toString())
|
|
||||||
if (response.status >= 400) {
|
|
||||||
writeLog(
|
|
||||||
"protocol",
|
|
||||||
"fetch failed",
|
|
||||||
{
|
|
||||||
url: request.url,
|
|
||||||
file,
|
|
||||||
status: response.status,
|
|
||||||
statusText: response.statusText,
|
|
||||||
},
|
|
||||||
"error",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return addDocumentPolicy(response, file)
|
|
||||||
} catch (error) {
|
|
||||||
writeLog("protocol", "fetch error", { url: request.url, file, error }, "error")
|
|
||||||
return new Response("Not found", { status: 404 })
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,127 +201,6 @@ function loadWindow(win: BrowserWindow, html: string) {
|
|||||||
void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
|
void win.loadURL(`${rendererProtocol}://${rendererHost}/${html}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function wireWindowRecovery(win: BrowserWindow, name: string) {
|
|
||||||
let showing = false
|
|
||||||
const sampler = createUnresponsiveSampler(win, name)
|
|
||||||
|
|
||||||
const handle = async (button: string | undefined, wait: boolean) => {
|
|
||||||
if (button === "Export Logs") {
|
|
||||||
const sampling = sampler.stopAndFlush()
|
|
||||||
await exportDebugLogs().catch((error) => writeLog("main", "failed to export debug logs", { error }, "error"))
|
|
||||||
if (wait && sampling) sampler.start()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (button === "Relaunch") {
|
|
||||||
sampler.stopAndFlush()
|
|
||||||
relaunchHandler()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (button === "Quit") {
|
|
||||||
sampler.stopAndFlush()
|
|
||||||
app.quit()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const show = async (message: string, detail: string, wait: boolean) => {
|
|
||||||
if (showing || win.isDestroyed()) return
|
|
||||||
showing = true
|
|
||||||
try {
|
|
||||||
while (!win.isDestroyed()) {
|
|
||||||
const buttons = wait ? ["Relaunch", "Export Logs", "Keep Waiting"] : ["Relaunch", "Export Logs", "Quit"]
|
|
||||||
const result = await dialog.showMessageBox(win, {
|
|
||||||
type: "warning",
|
|
||||||
buttons,
|
|
||||||
defaultId: 0,
|
|
||||||
cancelId: 2,
|
|
||||||
message,
|
|
||||||
detail,
|
|
||||||
})
|
|
||||||
if (await handle(buttons[result.response], wait)) continue
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
showing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const failed = (
|
|
||||||
event: string,
|
|
||||||
errorCode: number,
|
|
||||||
errorDescription: string,
|
|
||||||
validatedURL: string,
|
|
||||||
isMainFrame: boolean,
|
|
||||||
) => {
|
|
||||||
writeLog(
|
|
||||||
"window",
|
|
||||||
"renderer load failed",
|
|
||||||
{
|
|
||||||
window: name,
|
|
||||||
event,
|
|
||||||
errorCode,
|
|
||||||
errorDescription,
|
|
||||||
validatedURL,
|
|
||||||
currentURL: win.webContents.getURL(),
|
|
||||||
isMainFrame,
|
|
||||||
},
|
|
||||||
"error",
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!isMainFrame || errorCode === -3) return
|
|
||||||
void show(
|
|
||||||
"OpenCode failed to load",
|
|
||||||
[`Window: ${name}`, `URL: ${validatedURL}`, `Error: ${errorCode} ${errorDescription}`].join("\n"),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
win.webContents.on("did-fail-load", (_event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
|
||||||
failed("did-fail-load", errorCode, errorDescription, validatedURL, isMainFrame)
|
|
||||||
})
|
|
||||||
win.webContents.on("did-fail-provisional-load", (_event, errorCode, errorDescription, validatedURL, isMainFrame) => {
|
|
||||||
failed("did-fail-provisional-load", errorCode, errorDescription, validatedURL, isMainFrame)
|
|
||||||
})
|
|
||||||
win.webContents.on("render-process-gone", (_event, details) => {
|
|
||||||
sampler.stopAndFlush()
|
|
||||||
writeLog(
|
|
||||||
"window",
|
|
||||||
"renderer process gone",
|
|
||||||
{ window: name, currentURL: win.webContents.getURL(), details },
|
|
||||||
"error",
|
|
||||||
)
|
|
||||||
void show(
|
|
||||||
"OpenCode window terminated unexpectedly",
|
|
||||||
[`Window: ${name}`, `Reason: ${details.reason}`, `Code: ${details.exitCode ?? "<unknown>"}`].join("\n"),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
win.on("unresponsive", () => {
|
|
||||||
writeLog("window", "renderer unresponsive", { window: name, currentURL: win.webContents.getURL() }, "error")
|
|
||||||
sampler.start()
|
|
||||||
void show("OpenCode is not responding", "You can relaunch the app, open the logs, or keep waiting.", true)
|
|
||||||
})
|
|
||||||
win.on("responsive", () => {
|
|
||||||
writeLog("window", "renderer responsive", { window: name, currentURL: win.webContents.getURL() }, "error")
|
|
||||||
sampler.stopAndFlush()
|
|
||||||
})
|
|
||||||
win.webContents.on("console-message", (_event, level, message, line, sourceId) => {
|
|
||||||
if (message.toLowerCase().includes("terminal") || sourceId.toLowerCase().includes("terminal")) {
|
|
||||||
writeLog("pty", "console", { window: name, level, message, line, sourceId })
|
|
||||||
}
|
|
||||||
})
|
|
||||||
win.webContents.on("preload-error", (_event, preloadPath, error) => {
|
|
||||||
writeLog("preload", "preload error", { window: name, preloadPath, error }, "error")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function addDocumentPolicy(response: Response, file: string) {
|
|
||||||
if (!file.toLowerCase().endsWith(".html")) return response
|
|
||||||
const headers = new Headers(response.headers)
|
|
||||||
headers.set(documentPolicyHeader, jsCallStacksDocumentPolicy)
|
|
||||||
return new Response(response.body, { status: response.status, statusText: response.statusText, headers })
|
|
||||||
}
|
|
||||||
|
|
||||||
function allowRendererPermissions(win: BrowserWindow) {
|
function allowRendererPermissions(win: BrowserWindow) {
|
||||||
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
win.webContents.session.setPermissionRequestHandler((webContents, permission, callback, details) => {
|
||||||
callback(
|
callback(
|
||||||
@@ -404,19 +217,8 @@ function allowRendererPermissions(win: BrowserWindow) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isTrustedRendererUrl(value?: string) {
|
function isTrustedRendererUrl(value?: string) {
|
||||||
return isRendererUrl(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function addRendererHeaders(value: string, headers: Record<string, any>) {
|
|
||||||
upsertKeyValue(headers, "Access-Control-Allow-Origin", ["*"])
|
|
||||||
upsertKeyValue(headers, "Access-Control-Allow-Headers", ["*"])
|
|
||||||
if (isRendererUrl(value, true)) upsertKeyValue(headers, documentPolicyHeader, [jsCallStacksDocumentPolicy])
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRendererUrl(value?: string, html = false) {
|
|
||||||
if (!value || !URL.canParse(value)) return false
|
if (!value || !URL.canParse(value)) return false
|
||||||
const url = new URL(value)
|
const url = new URL(value)
|
||||||
if (html && !url.pathname.endsWith(".html")) return false
|
|
||||||
if (url.protocol === `${rendererProtocol}:` && url.host === rendererHost) return true
|
if (url.protocol === `${rendererProtocol}:` && url.host === rendererHost) return true
|
||||||
const devUrl = process.env.ELECTRON_RENDERER_URL
|
const devUrl = process.env.ELECTRON_RENDERER_URL
|
||||||
if (!devUrl || !URL.canParse(devUrl)) return false
|
if (!devUrl || !URL.canParse(devUrl)) return false
|
||||||
@@ -424,29 +226,13 @@ function isRendererUrl(value?: string, html = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function wireZoom(win: BrowserWindow) {
|
function wireZoom(win: BrowserWindow) {
|
||||||
pinchZoomEnabled.set(win, getPinchZoomEnabled())
|
|
||||||
win.webContents.setZoomFactor(1)
|
win.webContents.setZoomFactor(1)
|
||||||
win.webContents.on("zoom-changed", (event, zoomDirection) => {
|
win.webContents.on("zoom-changed", () => {
|
||||||
event.preventDefault()
|
win.webContents.setZoomFactor(1)
|
||||||
if (pinchZoomEnabled.get(win)) {
|
updateTitlebar(win)
|
||||||
win.webContents.setZoomFactor(clampZoom(win.webContents.getZoomFactor() + (zoomDirection === "in" ? 0.2 : -0.2)))
|
|
||||||
updateZoom(win)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (win.webContents.getZoomFactor() !== 1) win.webContents.setZoomFactor(1)
|
|
||||||
updateZoom(win)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function clampZoom(value: number) {
|
|
||||||
return Math.min(Math.max(value, minZoomLevel), maxZoomLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateZoom(win: BrowserWindow) {
|
|
||||||
updateTitlebar(win)
|
|
||||||
win.webContents.send("zoom-factor-changed", win.webContents.getZoomFactor())
|
|
||||||
}
|
|
||||||
|
|
||||||
function upsertKeyValue(obj: Record<string, any>, keyToChange: string, value: any) {
|
function upsertKeyValue(obj: Record<string, any>, keyToChange: string, value: any) {
|
||||||
const keyToChangeLower = keyToChange.toLowerCase()
|
const keyToChangeLower = keyToChange.toLowerCase()
|
||||||
for (const key of Object.keys(obj)) {
|
for (const key of Object.keys(obj)) {
|
||||||
|
|||||||
@@ -60,18 +60,6 @@ const api: ElectronAPI = {
|
|||||||
relaunch: () => ipcRenderer.send("relaunch"),
|
relaunch: () => ipcRenderer.send("relaunch"),
|
||||||
getZoomFactor: () => ipcRenderer.invoke("get-zoom-factor"),
|
getZoomFactor: () => ipcRenderer.invoke("get-zoom-factor"),
|
||||||
setZoomFactor: (factor) => ipcRenderer.invoke("set-zoom-factor", factor),
|
setZoomFactor: (factor) => ipcRenderer.invoke("set-zoom-factor", factor),
|
||||||
getPinchZoomEnabled: () => ipcRenderer.invoke("get-pinch-zoom-enabled"),
|
|
||||||
setPinchZoomEnabled: (enabled) => ipcRenderer.invoke("set-pinch-zoom-enabled", enabled),
|
|
||||||
onPinchZoomEnabledChanged: (cb) => {
|
|
||||||
const handler = (_: unknown, enabled: boolean) => cb(enabled)
|
|
||||||
ipcRenderer.on("pinch-zoom-enabled-changed", handler)
|
|
||||||
return () => ipcRenderer.removeListener("pinch-zoom-enabled-changed", handler)
|
|
||||||
},
|
|
||||||
onZoomFactorChanged: (cb) => {
|
|
||||||
const handler = (_: unknown, factor: number) => cb(factor)
|
|
||||||
ipcRenderer.on("zoom-factor-changed", handler)
|
|
||||||
return () => ipcRenderer.removeListener("zoom-factor-changed", handler)
|
|
||||||
},
|
|
||||||
setTitlebar: (theme) => ipcRenderer.invoke("set-titlebar", theme),
|
setTitlebar: (theme) => ipcRenderer.invoke("set-titlebar", theme),
|
||||||
runDesktopMenuAction: (action) => ipcRenderer.invoke("run-desktop-menu-action", action),
|
runDesktopMenuAction: (action) => ipcRenderer.invoke("run-desktop-menu-action", action),
|
||||||
loadingWindowComplete: () => ipcRenderer.send("loading-window-complete"),
|
loadingWindowComplete: () => ipcRenderer.send("loading-window-complete"),
|
||||||
@@ -79,8 +67,6 @@ const api: ElectronAPI = {
|
|||||||
checkUpdate: () => ipcRenderer.invoke("check-update"),
|
checkUpdate: () => ipcRenderer.invoke("check-update"),
|
||||||
installUpdate: () => ipcRenderer.invoke("install-update"),
|
installUpdate: () => ipcRenderer.invoke("install-update"),
|
||||||
setBackgroundColor: (color: string) => ipcRenderer.invoke("set-background-color", color),
|
setBackgroundColor: (color: string) => ipcRenderer.invoke("set-background-color", color),
|
||||||
exportDebugLogs: () => ipcRenderer.invoke("export-debug-logs"),
|
|
||||||
recordFatalRendererError: (error) => ipcRenderer.invoke("record-fatal-renderer-error", error),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("api", api)
|
contextBridge.exposeInMainWorld("api", api)
|
||||||
|
|||||||
@@ -20,14 +20,6 @@ export type WindowConfig = {
|
|||||||
updaterEnabled: boolean
|
updaterEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FatalRendererError = {
|
|
||||||
error: string
|
|
||||||
url: string
|
|
||||||
version?: string
|
|
||||||
platform: string
|
|
||||||
os?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ElectronAPI = {
|
export type ElectronAPI = {
|
||||||
killSidecar: () => Promise<void>
|
killSidecar: () => Promise<void>
|
||||||
installCli: () => Promise<string>
|
installCli: () => Promise<string>
|
||||||
@@ -79,10 +71,6 @@ export type ElectronAPI = {
|
|||||||
relaunch: () => void
|
relaunch: () => void
|
||||||
getZoomFactor: () => Promise<number>
|
getZoomFactor: () => Promise<number>
|
||||||
setZoomFactor: (factor: number) => Promise<void>
|
setZoomFactor: (factor: number) => Promise<void>
|
||||||
getPinchZoomEnabled: () => Promise<boolean>
|
|
||||||
setPinchZoomEnabled: (enabled: boolean) => Promise<void>
|
|
||||||
onPinchZoomEnabledChanged: (cb: (enabled: boolean) => void) => () => void
|
|
||||||
onZoomFactorChanged: (cb: (factor: number) => void) => () => void
|
|
||||||
setTitlebar: (theme: TitlebarTheme) => Promise<void>
|
setTitlebar: (theme: TitlebarTheme) => Promise<void>
|
||||||
runDesktopMenuAction: (action: DesktopMenuAction) => Promise<void>
|
runDesktopMenuAction: (action: DesktopMenuAction) => Promise<void>
|
||||||
loadingWindowComplete: () => void
|
loadingWindowComplete: () => void
|
||||||
@@ -90,6 +78,4 @@ export type ElectronAPI = {
|
|||||||
checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }>
|
checkUpdate: () => Promise<{ updateAvailable: boolean; version?: string }>
|
||||||
installUpdate: () => Promise<void>
|
installUpdate: () => Promise<void>
|
||||||
setBackgroundColor: (color: string) => Promise<void>
|
setBackgroundColor: (color: string) => Promise<void>
|
||||||
exportDebugLogs: () => Promise<string>
|
|
||||||
recordFatalRendererError: (error: FatalRendererError) => Promise<void>
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import { createEffect, createResource, onCleanup, onMount, Show } from "solid-js
|
|||||||
import { render } from "solid-js/web"
|
import { render } from "solid-js/web"
|
||||||
import pkg from "../../package.json"
|
import pkg from "../../package.json"
|
||||||
import { initI18n, t } from "./i18n"
|
import { initI18n, t } from "./i18n"
|
||||||
import { resetZoom, setPinchZoomEnabled, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
|
import { resetZoom, webviewZoom, zoomIn, zoomOut } from "./webview-zoom"
|
||||||
import "./styles.css"
|
import "./styles.css"
|
||||||
import { useTheme } from "@opencode-ai/ui/theme"
|
import { useTheme } from "@opencode-ai/ui/theme"
|
||||||
|
|
||||||
@@ -216,10 +216,6 @@ const createPlatform = (): Platform => {
|
|||||||
await window.api.installUpdate()
|
await window.api.installUpdate()
|
||||||
},
|
},
|
||||||
|
|
||||||
exportDebugLogs: () => window.api.exportDebugLogs(),
|
|
||||||
|
|
||||||
recordFatalRendererError: (error) => window.api.recordFatalRendererError(error),
|
|
||||||
|
|
||||||
restart: async () => {
|
restart: async () => {
|
||||||
await window.api.killSidecar().catch(() => undefined)
|
await window.api.killSidecar().catch(() => undefined)
|
||||||
window.api.relaunch()
|
window.api.relaunch()
|
||||||
@@ -274,10 +270,6 @@ const createPlatform = (): Platform => {
|
|||||||
|
|
||||||
webviewZoom,
|
webviewZoom,
|
||||||
|
|
||||||
getPinchZoomEnabled: () => window.api.getPinchZoomEnabled(),
|
|
||||||
|
|
||||||
setPinchZoomEnabled,
|
|
||||||
|
|
||||||
runDesktopMenuAction,
|
runDesktopMenuAction,
|
||||||
|
|
||||||
checkAppExists: async (appName: string) => {
|
checkAppExists: async (appName: string) => {
|
||||||
|
|||||||
@@ -13,21 +13,9 @@ const OS_NAME = (() => {
|
|||||||
|
|
||||||
const [webviewZoom, setWebviewZoom] = createSignal(1)
|
const [webviewZoom, setWebviewZoom] = createSignal(1)
|
||||||
let requestedZoom = 1
|
let requestedZoom = 1
|
||||||
let pinchZoomEnabled = false
|
|
||||||
let wheelPinch = undefined as
|
|
||||||
| {
|
|
||||||
active: boolean
|
|
||||||
startZoom: number
|
|
||||||
totalDelta: number
|
|
||||||
timeout: ReturnType<typeof setTimeout> | undefined
|
|
||||||
}
|
|
||||||
| undefined
|
|
||||||
|
|
||||||
const MAX_ZOOM_LEVEL = 10
|
const MAX_ZOOM_LEVEL = 10
|
||||||
const MIN_ZOOM_LEVEL = 0.2
|
const MIN_ZOOM_LEVEL = 0.2
|
||||||
const WHEEL_PINCH_THRESHOLD = 20
|
|
||||||
const WHEEL_PINCH_STEP = 0.2
|
|
||||||
const WHEEL_PINCH_END_DELAY = 160
|
|
||||||
|
|
||||||
const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL)
|
const clamp = (value: number) => Math.min(Math.max(value, MIN_ZOOM_LEVEL), MAX_ZOOM_LEVEL)
|
||||||
|
|
||||||
@@ -45,77 +33,10 @@ const applyZoom = (next: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
window.api.onZoomFactorChanged((factor) => {
|
|
||||||
requestedZoom = clamp(factor)
|
|
||||||
setWebviewZoom(requestedZoom)
|
|
||||||
})
|
|
||||||
|
|
||||||
void window.api.getPinchZoomEnabled().then((enabled) => {
|
|
||||||
pinchZoomEnabled = enabled
|
|
||||||
})
|
|
||||||
|
|
||||||
window.api.onPinchZoomEnabledChanged((enabled) => {
|
|
||||||
pinchZoomEnabled = enabled
|
|
||||||
resetWheelPinch()
|
|
||||||
})
|
|
||||||
|
|
||||||
const setPinchZoomEnabled = (enabled: boolean) => {
|
|
||||||
pinchZoomEnabled = enabled
|
|
||||||
resetWheelPinch()
|
|
||||||
return window.api.setPinchZoomEnabled(enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
const resetZoom = () => applyZoom(1)
|
const resetZoom = () => applyZoom(1)
|
||||||
const zoomIn = () => applyZoom(clamp(requestedZoom + 0.2))
|
const zoomIn = () => applyZoom(clamp(requestedZoom + 0.2))
|
||||||
const zoomOut = () => applyZoom(clamp(requestedZoom - 0.2))
|
const zoomOut = () => applyZoom(clamp(requestedZoom - 0.2))
|
||||||
|
|
||||||
const resetWheelPinch = () => {
|
|
||||||
clearTimeout(wheelPinch?.timeout)
|
|
||||||
wheelPinch = undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizeWheelDelta = (event: WheelEvent) => {
|
|
||||||
if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) return event.deltaY * 16
|
|
||||||
if (event.deltaMode === WheelEvent.DOM_DELTA_PAGE) return event.deltaY * window.innerHeight
|
|
||||||
return event.deltaY
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateWheelPinch = (event: WheelEvent) => {
|
|
||||||
wheelPinch ??= {
|
|
||||||
active: false,
|
|
||||||
startZoom: requestedZoom,
|
|
||||||
totalDelta: 0,
|
|
||||||
timeout: undefined,
|
|
||||||
}
|
|
||||||
|
|
||||||
clearTimeout(wheelPinch.timeout)
|
|
||||||
wheelPinch.timeout = setTimeout(resetWheelPinch, WHEEL_PINCH_END_DELAY)
|
|
||||||
wheelPinch.totalDelta += normalizeWheelDelta(event)
|
|
||||||
|
|
||||||
if (!wheelPinch.active && Math.abs(wheelPinch.totalDelta) < WHEEL_PINCH_THRESHOLD) return
|
|
||||||
if (!wheelPinch.active) {
|
|
||||||
wheelPinch.active = true
|
|
||||||
wheelPinch.startZoom = requestedZoom
|
|
||||||
wheelPinch.totalDelta = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
wheelPinch.active = true
|
|
||||||
applyZoom(clamp(wheelPinch.startZoom - (wheelPinch.totalDelta / WHEEL_PINCH_THRESHOLD) * WHEEL_PINCH_STEP))
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener(
|
|
||||||
"wheel",
|
|
||||||
(event) => {
|
|
||||||
if (!pinchZoomEnabled) return
|
|
||||||
if (!event.ctrlKey) return
|
|
||||||
|
|
||||||
event.preventDefault()
|
|
||||||
updateWheelPinch(event)
|
|
||||||
},
|
|
||||||
{ passive: false },
|
|
||||||
)
|
|
||||||
|
|
||||||
window.addEventListener("keydown", (event) => {
|
window.addEventListener("keydown", (event) => {
|
||||||
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
|
if (!(OS_NAME === "macos" ? event.metaKey : event.ctrlKey)) return
|
||||||
|
|
||||||
@@ -135,4 +56,4 @@ window.addEventListener("keydown", (event) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export { webviewZoom, resetZoom, setPinchZoomEnabled, zoomIn, zoomOut }
|
export { webviewZoom, resetZoom, zoomIn, zoomOut }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/package.json",
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
"version": "1.15.10",
|
"version": "1.15.5",
|
||||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
-10
@@ -1,10 +0,0 @@
|
|||||||
/* This file is auto-generated by SST. Do not edit. */
|
|
||||||
/* tslint:disable */
|
|
||||||
/* eslint-disable */
|
|
||||||
/* deno-fmt-ignore-file */
|
|
||||||
/* biome-ignore-all lint: auto-generated */
|
|
||||||
|
|
||||||
/// <reference path="../../sst-env.d.ts" />
|
|
||||||
|
|
||||||
import "sst"
|
|
||||||
export {}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@opencode-ai/enterprise",
|
"name": "@opencode-ai/enterprise",
|
||||||
"version": "1.15.10",
|
"version": "1.15.6",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user