diff --git a/.github/workflows/unlock.yml b/.github/workflows/unlock.yml new file mode 100644 index 000000000000..8df1af0e36f9 --- /dev/null +++ b/.github/workflows/unlock.yml @@ -0,0 +1,52 @@ +name: unlock + +on: + workflow_dispatch: + inputs: + stage: + description: SST stage to unlock + required: true + type: choice + options: + - dev + - production + +concurrency: deploy-${{ inputs.stage }} + +permissions: + contents: read + id-token: write + +jobs: + unlock: + runs-on: ubuntu-latest + environment: ${{ inputs.stage }} + steps: + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + + - uses: ./.github/actions/setup-bun + + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: "24" + + - uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1 + with: + role-to-assume: ${{ vars.AWS_DEPLOY_ROLE_ARN }} + role-session-name: opencode-${{ github.run_id }} + aws-region: us-east-1 + + - run: bun sst unlock --stage=${{ inputs.stage }} + env: + GITHUB_TOKEN: ${{ github.token }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }} + PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }} + STRIPE_SECRET_KEY: ${{ inputs.stage == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }} + HONEYCOMB_API_KEY: ${{ secrets.HONEYCOMB_API_KEY }} + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ vars.SENTRY_ORG }} + SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} + SENTRY_RELEASE: unlock@${{ github.sha }} + VITE_SENTRY_DSN: ${{ vars.WEB_SENTRY_DSN }} + VITE_SENTRY_RELEASE: unlock@${{ github.sha }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1ab14a7b628a..a9c545efb896 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -157,7 +157,7 @@ Caveats: - If `spawn` does not work for you, you can debug the server separately: - Debug server: `bun run --inspect=ws://localhost:6499/ --cwd packages/opencode ./src/index.ts serve --port 4096`, then attach TUI with `opencode attach http://localhost:4096` - - Debug TUI: `bun run --inspect=ws://localhost:6499/ --cwd packages/opencode --conditions=browser ./src/index.ts` + - Debug TUI: `bun run --inspect=ws://localhost:6499/ --cwd packages/opencode ./src/index.ts` Other tips and tricks: diff --git a/bun.lock b/bun.lock index edc7eb6d7f34..efe01bf957f3 100644 --- a/bun.lock +++ b/bun.lock @@ -29,7 +29,7 @@ }, "packages/app": { "name": "@opencode-ai/app", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@corvu/drawer": "catalog:", "@dnd-kit/abstract": "0.5.0", @@ -78,7 +78,7 @@ "tailwindcss": "catalog:", }, "devDependencies": { - "@happy-dom/global-registrator": "20.0.11", + "@happy-dom/global-registrator": "20.12.0", "@playwright/test": "catalog:", "@sentry/vite-plugin": "catalog:", "@tailwindcss/vite": "catalog:", @@ -96,7 +96,7 @@ }, "packages/cli": { "name": "@opencode-ai/cli", - "version": "1.18.21", + "version": "1.18.30", "bin": { "lildax": "./bin/lildax.cjs", }, @@ -144,7 +144,7 @@ }, "packages/codemode": { "name": "@opencode-ai/codemode", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "acorn": "8.15.0", "effect": "catalog:", @@ -158,7 +158,7 @@ }, "packages/console/app": { "name": "@opencode-ai/console-app", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@ibm/plex": "6.4.1", @@ -178,6 +178,7 @@ "@upstash/redis": "1.38.0", "chart.js": "4.5.1", "nitro": "3.0.1-alpha.1", + "number-flow": "0.6.2", "solid-js": "catalog:", "solid-list": "0.3.0", "solid-stripe": "0.8.1", @@ -194,7 +195,7 @@ }, "packages/console/core": { "name": "@opencode-ai/console-core", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@aws-sdk/client-sts": "3.782.0", "@jsx-email/render": "1.1.1", @@ -221,7 +222,7 @@ }, "packages/console/function": { "name": "@opencode-ai/console-function", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@ai-sdk/anthropic": "3.0.82", "@ai-sdk/openai": "3.0.48", @@ -235,6 +236,7 @@ "devDependencies": { "@cloudflare/workers-types": "catalog:", "@tsconfig/node22": "22.0.2", + "@types/bun": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", "openai": "5.11.0", @@ -243,7 +245,7 @@ }, "packages/console/mail": { "name": "@opencode-ai/console-mail", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@jsx-email/all": "2.2.3", "@jsx-email/cli": "1.4.3", @@ -267,7 +269,7 @@ }, "packages/console/support": { "name": "@opencode-ai/console-support", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@cloudflare/vite-plugin": "1.15.2", "@opencode-ai/console-core": "workspace:*", @@ -287,15 +289,15 @@ }, "packages/core": { "name": "@opencode-ai/core", - "version": "1.18.21", + "version": "1.18.30", "bin": { "opencode": "./bin/opencode", }, "dependencies": { "@ai-sdk/alibaba": "1.0.17", - "@ai-sdk/amazon-bedrock": "4.0.158", - "@ai-sdk/anthropic": "3.0.82", - "@ai-sdk/azure": "3.0.88", + "@ai-sdk/amazon-bedrock": "4.0.166", + "@ai-sdk/anthropic": "3.0.111", + "@ai-sdk/azure": "3.0.93", "@ai-sdk/cerebras": "2.0.41", "@ai-sdk/cohere": "3.0.27", "@ai-sdk/deepinfra": "2.0.41", @@ -304,7 +306,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", @@ -339,7 +341,7 @@ "drizzle-orm": "catalog:", "effect": "catalog:", "fuzzysort": "3.1.0", - "gitlab-ai-provider": "6.12.1", + "gitlab-ai-provider": "6.15.0", "glob": "13.0.5", "google-auth-library": "10.5.0", "gray-matter": "4.0.3", @@ -381,7 +383,7 @@ }, "packages/desktop": { "name": "@opencode-ai/desktop", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@zip.js/zip.js": "2.7.62", "drizzle-orm": "catalog:", @@ -435,7 +437,7 @@ }, "packages/effect-drizzle-sqlite": { "name": "@opencode-ai/effect-drizzle-sqlite", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "drizzle-orm": "catalog:", "effect": "catalog:", @@ -449,7 +451,7 @@ }, "packages/effect-sqlite-node": { "name": "@opencode-ai/effect-sqlite-node", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "effect": "catalog:", }, @@ -461,7 +463,7 @@ }, "packages/enterprise": { "name": "@opencode-ai/enterprise", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@hono/standard-validator": "catalog:", "@opencode-ai/core": "workspace:*", @@ -493,7 +495,7 @@ }, "packages/function": { "name": "@opencode-ai/function", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@octokit/auth-app": "8.0.1", "@octokit/rest": "catalog:", @@ -509,7 +511,7 @@ }, "packages/http-recorder": { "name": "@opencode-ai/http-recorder", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@effect/platform-node": "4.0.0-beta.83", "@effect/platform-node-shared": "4.0.0-beta.83", @@ -540,7 +542,7 @@ }, "packages/llm": { "name": "@opencode-ai/llm", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@opencode-ai/schema": "workspace:*", "@smithy/eventstream-codec": "4.2.14", @@ -559,7 +561,7 @@ }, "packages/opencode": { "name": "opencode", - "version": "1.18.21", + "version": "1.18.30", "bin": { "opencode": "./bin/opencode", }, @@ -568,9 +570,9 @@ "@actions/github": "6.0.1", "@agentclientprotocol/sdk": "0.21.0", "@ai-sdk/alibaba": "1.0.17", - "@ai-sdk/amazon-bedrock": "4.0.158", - "@ai-sdk/anthropic": "3.0.82", - "@ai-sdk/azure": "3.0.88", + "@ai-sdk/amazon-bedrock": "4.0.166", + "@ai-sdk/anthropic": "3.0.111", + "@ai-sdk/azure": "3.0.93", "@ai-sdk/cerebras": "2.0.60", "@ai-sdk/cohere": "3.0.27", "@ai-sdk/deepinfra": "2.0.41", @@ -579,7 +581,7 @@ "@ai-sdk/google-vertex": "4.0.181", "@ai-sdk/groq": "3.0.31", "@ai-sdk/mistral": "3.0.51", - "@ai-sdk/openai": "3.0.84", + "@ai-sdk/openai": "3.0.88", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/perplexity": "3.0.26", "@ai-sdk/provider": "3.0.8", @@ -632,7 +634,7 @@ "drizzle-orm": "catalog:", "effect": "catalog:", "fuzzysort": "3.1.0", - "gitlab-ai-provider": "6.12.1", + "gitlab-ai-provider": "6.15.0", "glob": "13.0.5", "google-auth-library": "10.5.0", "gray-matter": "4.0.3", @@ -690,7 +692,7 @@ }, "packages/plugin": { "name": "@opencode-ai/plugin", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@ai-sdk/provider": "3.0.8", "@opencode-ai/sdk": "workspace:*", @@ -766,7 +768,7 @@ }, "packages/sdk/js": { "name": "@opencode-ai/sdk", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "cross-spawn": "catalog:", }, @@ -781,7 +783,7 @@ }, "packages/server": { "name": "@opencode-ai/server", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@opencode-ai/core": "workspace:*", "@opencode-ai/protocol": "workspace:*", @@ -796,7 +798,7 @@ }, "packages/session-ui": { "name": "@opencode-ai/session-ui", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@kobalte/core": "catalog:", "@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz", @@ -836,7 +838,7 @@ }, "packages/slack": { "name": "@opencode-ai/slack", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@opencode-ai/sdk": "workspace:*", "@slack/bolt": "^3.17.1", @@ -849,7 +851,7 @@ }, "packages/stats/app": { "name": "@opencode-ai/stats-app", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@ibm/plex": "6.4.1", "@kobalte/core": "catalog:", @@ -858,32 +860,25 @@ "@solidjs/meta": "catalog:", "@solidjs/router": "catalog:", "@solidjs/start": "catalog:", - "d3-geo": "3.1.1", "d3-scale": "4.0.2", "effect": "catalog:", "i18n-iso-countries": "7.14.0", "nitro": "3.0.1-alpha.1", "solid-js": "catalog:", "sst": "catalog:", - "topojson-client": "3.1.0", "vite": "catalog:", - "world-atlas": "2.0.2", }, "devDependencies": { "@cloudflare/workers-types": "catalog:", "@types/bun": "catalog:", - "@types/d3-geo": "3.1.0", "@types/d3-scale": "4.0.9", - "@types/geojson": "7946.0.16", - "@types/topojson-client": "3.1.5", - "@types/topojson-specification": "1.0.5", "@typescript/native-preview": "catalog:", "typescript": "catalog:", }, }, "packages/stats/core": { "name": "@opencode-ai/stats-core", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@aws-sdk/client-athena": "3.933.0", "@planetscale/database": "1.19.0", @@ -902,7 +897,7 @@ }, "packages/stats/server": { "name": "@opencode-ai/stats-server", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@aws-sdk/client-firehose": "3.933.0", "@effect/platform-node": "catalog:", @@ -944,7 +939,7 @@ }, "packages/tui": { "name": "@opencode-ai/tui", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@opencode-ai/core": "workspace:*", "@opencode-ai/plugin": "workspace:*", @@ -971,7 +966,7 @@ }, "packages/ui": { "name": "@opencode-ai/ui", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@kobalte/core": "catalog:", "@pierre/diffs": "catalog:", @@ -1022,7 +1017,7 @@ }, "packages/web": { "name": "@opencode-ai/web", - "version": "1.18.21", + "version": "1.18.30", "dependencies": { "@astrojs/cloudflare": "12.6.3", "@astrojs/markdown-remark": "6.3.1", @@ -1069,12 +1064,15 @@ "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", + "@ai-sdk/openai@3.0.88": "patches/@ai-sdk%2Fopenai@3.0.88.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", + "@ai-sdk/anthropic@3.0.111": "patches/@ai-sdk%2Fanthropic@3.0.111.patch", "@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", - "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", + "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", + "@ai-sdk/amazon-bedrock@4.0.166": "patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch", "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", @@ -1082,6 +1080,7 @@ "@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch", }, "overrides": { + "@ai-sdk/anthropic": "3.0.111", "@opentui/core": "catalog:", "@opentui/keymap": "catalog:", "@opentui/solid": "catalog:", @@ -1173,11 +1172,11 @@ "@ai-sdk/alibaba": ["@ai-sdk/alibaba@1.0.17", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZbE+U5bWz2JBc5DERLowx5+TKbjGBE93LqKZAWvuEn7HOSQMraxFMZuc0ST335QZJAyfBOzh7m1mPQ+y7EaaoA=="], - "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.158", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.111", "@ai-sdk/openai": "3.0.98", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.46", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yZebHEszUzPLsK+Rq5sVZJkJj7EYDgY+Lz36IGf/RSkSC5LOMDbVKoQ55S8xNzJqVjUDqlWuZiQzg40HQslmCw=="], + "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.166", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.115", "@ai-sdk/openai": "3.0.105", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.50", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-HzzQb+ks+WmRcnnT9uBdJAxaqSXz3CWgmLSe9hePAkELPzTl/nAIJh2fb2+UsTD0n9OzZfBb25k68CIxraKkoA=="], - "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.82", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-WKKou2wbhGGYV8PSALAPyV2YY4nfCqCPkyBzYtJtDA9yCcIFwsbtkTNgg7bqtLCVzeEsY7wwxRoCWy+EMfrw/A=="], + "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.111", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.46" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-atgBW8jZPr/KuaKX5FvDIHuXBI8VCol6kVeoD4P0657+VXR73QsLogXQVN/Zt5FHtq9WzpdIZseCJiXPqkgwwA=="], - "@ai-sdk/azure": ["@ai-sdk/azure@3.0.88", "", { "dependencies": { "@ai-sdk/deepseek": "2.0.47", "@ai-sdk/openai": "3.0.84", "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RRjZkB1lYplh8dpBarnvkl1j7sYLHsyXua7erL3oNcMK7fHcl4bPO5C7iQhD1O/DqD/zCceDifnege1s+8yEvw=="], + "@ai-sdk/azure": ["@ai-sdk/azure@3.0.93", "", { "dependencies": { "@ai-sdk/deepseek": "2.0.50", "@ai-sdk/openai": "3.0.88", "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9nKNmRtopjE3+L+286/4sLBDfDvmdsL7fiQXG0TE38NywxkDDLUhdr0KSllkRQ+m1/8bO7x4rIVhHSjQQiqmnQ=="], "@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.41", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kDMEpjaRdRXIUi1EH8WHwLRahyDTYv9SAJnP6VCCeq8X+tVqZbMLCqqxSG5dRknrI65ucjvzQt+FiDKTAa7AHg=="], @@ -1187,7 +1186,7 @@ "@ai-sdk/deepinfra": ["@ai-sdk/deepinfra@2.0.41", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-y6RoOP7DGWmDSiSxrUSt5p18sbz+Ixe5lMVPmdE7x+Tr5rlrzvftyHhjWHfqlAtoYERZTGFbP6tPW1OfQcrb4A=="], - "@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MzcQ321JO8OY+TVLFI81A7cIIuoeLLxrLCDD+8C1E3Ro6UFyfMtRXo9bw9OhTMRSDMo6hgSDOo4Fekz8aJtQYQ=="], + "@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.50", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-pnl22aGBIdpK0sXSJN4zDykIXTdyO9m3gFhno3SBPpdwT4Tq041Uy0i/EDWSxIiFa9JEokFGiCt4NNzcE5ntRA=="], "@ai-sdk/elevenlabs": ["@ai-sdk/elevenlabs@2.0.51", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XFONX6rAsu6d13cJVUfZfkq4a+qdThlxvoEfzYlSRa1AvALlzwX7Y6bunXxevuifT3n882+nDdWrdiYvFP0+Fw=="], @@ -1663,7 +1662,7 @@ "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="], - "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.0.11", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="], + "@happy-dom/global-registrator": ["@happy-dom/global-registrator@20.12.0", "", { "dependencies": { "@types/node": ">=20.0.0", "happy-dom": "^20.12.0" } }, "sha512-BUE55Rew3oMwBzwCwmUnV+Oxk51V3xolM39Ts6kGiBXNELjujiESwk9qc99JRr6cVrj3OTd9MJ5zQ2fpn+jy6g=="], "@hey-api/codegen-core": ["@hey-api/codegen-core@0.5.5", "", { "dependencies": { "@hey-api/types": "0.1.2", "ansi-colors": "4.1.3", "c12": "3.3.3", "color-support": "1.1.3" }, "peerDependencies": { "typescript": ">=5.5.3" } }, "sha512-f2ZHucnA2wBGAY8ipB4wn/mrEYW+WUxU2huJmUvfDO6AE2vfILSHeF3wCO39Pz4wUYPoAWZByaauftLrOfC12Q=="], @@ -2843,8 +2842,6 @@ "@types/cross-spawn": ["@types/cross-spawn@6.0.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA=="], - "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="], - "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], @@ -2865,8 +2862,6 @@ "@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="], - "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], "@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="], @@ -2945,10 +2940,6 @@ "@types/ssri": ["@types/ssri@7.1.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw=="], - "@types/topojson-client": ["@types/topojson-client@3.1.5", "", { "dependencies": { "@types/geojson": "*", "@types/topojson-specification": "*" } }, "sha512-C79rySTyPxnQNNguTZNI1Ct4D7IXgvyAs3p9HPecnl6mNrJ5+UhvGNYcZfpROYV2lMHI48kJPxwR+F9C6c7nmw=="], - - "@types/topojson-specification": ["@types/topojson-specification@1.0.5", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ=="], - "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/tsscmp": ["@types/tsscmp@1.0.2", "", {}, "sha512-cy7BRSU8GYYgxjcx0Py+8lo5MthuDhlyu076KUcYzVNXL23luYgRHkMG2fIFEc6neckeh/ntP82mw+U4QjZq+g=="], @@ -3249,6 +3240,8 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "buffer-image-size": ["buffer-image-size@0.6.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEh+kZOPY1w+gcCMobZ6ETUp9WfibndnosbpwB1iJk/8Gt5ZF2bhS6+B6bPYz424KtwsR6Rflc3tCz1/ghX2dQ=="], + "buffers": ["buffers@0.1.1", "", {}, "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ=="], "builder-util": ["builder-util@26.15.0", "", { "dependencies": { "@types/debug": "^4.1.6", "builder-util-runtime": "9.7.0", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-dUx+HxVbiNsNQ4mGe1PyoC/tBmsHwBNDLdBuqWCj+rhHFE9lHgrXiGYKAM1uNlznhAaUSyMlms84VeSSr3gOBA=="], @@ -3437,8 +3430,6 @@ "d3-format": ["d3-format@3.1.2", "", {}, "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg=="], - "d3-geo": ["d3-geo@3.1.1", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], - "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], @@ -3667,6 +3658,8 @@ "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], "estree-util-attach-comments": ["estree-util-attach-comments@3.0.0", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-cKUwm/HUcTDsYh/9FgnuFqpfquUbwIqwKM26BVCGDPVgvaCl/nDCCjUfiLlx6lsEZ3Z4RFxNbOQ60pkaEwFxGw=="], @@ -3853,7 +3846,7 @@ "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], - "gitlab-ai-provider": ["gitlab-ai-provider@6.12.1", "", { "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-Qn5iHqvjG8yktI5MWaUgdRR94l7O4WtYW0CAbhsCh1Tj0Fei/DeprOYPVyf4Nht1Ix6U2PXSYM32QOHI6Z2TDw=="], + "gitlab-ai-provider": ["gitlab-ai-provider@6.15.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-hHJsb4WP+WPTMtMC0gZcTi0wreMvR6WdEPeUPRHNlyW7yX0HuFG7v1oZ/ethbF6y/FG3/7NcirNgjZLjNneW7g=="], "glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="], @@ -3887,7 +3880,7 @@ "h3": ["h3@2.0.1-rc.4", "", { "dependencies": { "rou3": "^0.7.8", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-vZq8pEUp6THsXKXrUXX44eOqfChic2wVQ1GlSzQCBr7DeFBkfIZAo2WyNND4GSv54TAa0E4LYIK73WSPdgKUgw=="], - "happy-dom": ["happy-dom@20.9.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ=="], + "happy-dom": ["happy-dom@20.12.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "buffer-image-size": "^0.6.4", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.21.0" } }, "sha512-7uMYJu2SEwwL8vVcKp0C0lnt6d2LSGGe+T+oY79PiCJNNSgFpbxW8n5KuzpDQvrU4mt+fYiK1+Jy7Z2v39YR6g=="], "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], @@ -4581,6 +4574,8 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], + "number-flow": ["number-flow@0.6.2", "", { "dependencies": { "esm-env": "^1.1.4" } }, "sha512-MCnImG4Q5vPwhSXnov56nOuyyKn6LC+Qd7II1UiKc+ACRtug5iAtn0+CwXNxM38AC5lSowEY+oYEtZX2qMnUyw=="], + "nypm": ["nypm@0.6.6", "", { "dependencies": { "citty": "^0.2.2", "pathe": "^2.0.3", "tinyexec": "^1.1.1" }, "bin": { "nypm": "dist/cli.mjs" } }, "sha512-vRyr0r4cbBapw07Xw8xrj9Teq3o7MUD35rSaTcanDbW+aK2XHDgJFiU6ZTj2GBw7Q12ysdsyFss+Vdz4hQ0Y6Q=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -5317,8 +5312,6 @@ "toolbeam-docs-theme": ["toolbeam-docs-theme@0.4.8", "", { "peerDependencies": { "@astrojs/starlight": "^0.34.3", "astro": "^5.7.13" } }, "sha512-b+5ynEFp4Woe5a22hzNQm42lD23t13ZMihVxHbzjA50zdcM9aOSJTIjdJ0PDSd4/50HbBXcpHiQsz6rM4N88ww=="], - "topojson-client": ["topojson-client@3.1.0", "", { "dependencies": { "commander": "2" }, "bin": { "topo2geo": "bin/topo2geo", "topomerge": "bin/topomerge", "topoquantize": "bin/topoquantize" } }, "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw=="], - "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "traverse": ["traverse@0.3.9", "", {}, "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ=="], @@ -5561,8 +5554,6 @@ "workerd": ["workerd@1.20251118.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251118.0", "@cloudflare/workerd-darwin-arm64": "1.20251118.0", "@cloudflare/workerd-linux-64": "1.20251118.0", "@cloudflare/workerd-linux-arm64": "1.20251118.0", "@cloudflare/workerd-windows-64": "1.20251118.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Om5ns0Lyx/LKtYI04IV0bjIrkBgoFNg0p6urzr2asekJlfP18RqFzyqMFZKf0i9Gnjtz/JfAS/Ol6tjCe5JJsQ=="], - "world-atlas": ["world-atlas@2.0.2", "", {}, "sha512-IXfV0qwlKXpckz1FhwXVwKRjiIhOnWttOskm5CtxMsjgE/MXAYRHWJqgXOpM8IkcPBoXnyTU5lFHcYa5ChG0LQ=="], - "wrangler": ["wrangler@4.50.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.11", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20251118.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251118.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251118.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-+nuZuHZxDdKmAyXOSrHlciGshCoAPiy5dM+t6mEohWm7HpXvTHmWQGUf/na9jjWlWJHCJYOWzkA1P5HBJqrIEA=="], "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], @@ -5641,27 +5632,25 @@ "@ai-sdk/alibaba/@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=="], - "@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.111", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.46" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-atgBW8jZPr/KuaKX5FvDIHuXBI8VCol6kVeoD4P0657+VXR73QsLogXQVN/Zt5FHtq9WzpdIZseCJiXPqkgwwA=="], - - "@ai-sdk/amazon-bedrock/@ai-sdk/openai": ["@ai-sdk/openai@3.0.98", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.46" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-nAvp8pVOUJ3znJHRzZs54Y7CJQSikOW1Ty7LTdtQT+/pgtAwqhuKd8oaXbiW2xQaYBdH2i8o9AcEpcUdXIyF+g=="], + "@ai-sdk/amazon-bedrock/@ai-sdk/openai": ["@ai-sdk/openai@3.0.105", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.50" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-umx95F8gqGuhPdoW+4ofq83fuSN6aMHwISxvY6dZU51Iix5ZKId0FNru+0FQeu+xfFUrVNmf3vYQNc2FLN85xA=="], "@ai-sdk/amazon-bedrock/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], - "@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.46", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tEtld97plCFiYevsJuOkGkeuhQndeMWFBVrJS4AjnbD5AqrNSXRCe0p+BZ3Cju/sxDeeZ9ym3q9YUV8fASA7aQ=="], + "@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.50", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YAcB+7M1JhAYsHorTrWyldCyZihjCKr/QRXH2vFrara/+lwqNE7q5KzoucKLZ7ktFiUonhnhFhRoiymsq/2K2Q=="], "@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="], "@ai-sdk/amazon-bedrock/@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=="], - "@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="], + "@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], - "@ai-sdk/anthropic/@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=="], + "@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.46", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-tEtld97plCFiYevsJuOkGkeuhQndeMWFBVrJS4AjnbD5AqrNSXRCe0p+BZ3Cju/sxDeeZ9ym3q9YUV8fASA7aQ=="], - "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], + "@ai-sdk/azure/@ai-sdk/openai": ["@ai-sdk/openai@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6M4+bxK/UMijDYK2ia1hCKYma1iajFYxHDlhbinFUdhH2WbFsjoZRMpSjEMlTOBptqmh2J1JclKnprGulF8WwQ=="], "@ai-sdk/azure/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "@ai-sdk/cerebras/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], @@ -5675,7 +5664,7 @@ "@ai-sdk/deepseek/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "@ai-sdk/elevenlabs/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], @@ -5691,8 +5680,6 @@ "@ai-sdk/google/@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=="], - "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="], - "@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.108", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kwvYpRNghqt0VRKE7Hx1UWZQCUJJFqUITj24baxy+ApS0Hru0PkBJHD75a36Wc+e6e+wHcKR2MconTeJiBZigA=="], "@ai-sdk/google-vertex/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.67", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-glcEJC2mBXJKj7joFI0fRhcbdDYKTBgXMPcT6Vcnlym67tTzuNG9pFx3zblxVv8TdOxhojJja5zGG19yeGJxuA=="], @@ -6033,7 +6020,7 @@ "@opencode-ai/app/@opencode-ai/client": ["@opencode-ai/client@vendor/opencode-ai-client-1.17.13-v2.tgz", {}, "sha512-332kgNifvpQOF9e3UA+pIa5xPrMhLaQkUiNiO+meS0Ba9HjSE6hfsWnEojMkD0DPSLqPP6rCF1dDoF7U0Y0OCQ=="], - "@opencode-ai/core/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], + "@opencode-ai/core/@ai-sdk/openai": ["@ai-sdk/openai@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6M4+bxK/UMijDYK2ia1hCKYma1iajFYxHDlhbinFUdhH2WbFsjoZRMpSjEMlTOBptqmh2J1JclKnprGulF8WwQ=="], "@opencode-ai/core/@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=="], @@ -6175,12 +6162,14 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.153", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.110", "@ai-sdk/openai": "3.0.96", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iEXrLgWylCHJmznqlKLU3CqRh8UWibv+illrwmsk136FVBBvyXiGnpQrI1pGWCScVLQjBQSFQu7GJDkUEomf/A=="], - "ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.110", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rNkamQCeAUOUGr5Npg5pXZyYFH4fS1U6Mbdy3dF/NNBEI3D2Chc/ruRrwNegP0gfpX3cllP3O4jSibGBbWPZ7A=="], + "ai-gateway-provider/@ai-sdk/azure": ["@ai-sdk/azure@3.0.88", "", { "dependencies": { "@ai-sdk/deepseek": "2.0.47", "@ai-sdk/openai": "3.0.84", "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RRjZkB1lYplh8dpBarnvkl1j7sYLHsyXua7erL3oNcMK7fHcl4bPO5C7iQhD1O/DqD/zCceDifnege1s+8yEvw=="], "ai-gateway-provider/@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.60", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.54", "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rnok3cThg6awBwaDSyiZpgRpbV7pqxGYrA89LODCo5cuEHeP2h0AM0lLHP7zIkclAdXfOm4wldKi/S2T/DGCOw=="], "ai-gateway-provider/@ai-sdk/cohere": ["@ai-sdk/cohere@3.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cXLjIsSzUriPHe704IH6d+ipJ/OvczTB700p9Zma7DPgQzvxG/diyr8q/2LEsbTRiTopiKhky8dn1PJNQcJToQ=="], + "ai-gateway-provider/@ai-sdk/deepseek": ["@ai-sdk/deepseek@2.0.47", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MzcQ321JO8OY+TVLFI81A7cIIuoeLLxrLCDD+8C1E3Ro6UFyfMtRXo9bw9OhTMRSDMo6hgSDOo4Fekz8aJtQYQ=="], + "ai-gateway-provider/@ai-sdk/google": ["@ai-sdk/google@3.0.108", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kwvYpRNghqt0VRKE7Hx1UWZQCUJJFqUITj24baxy+ApS0Hru0PkBJHD75a36Wc+e6e+wHcKR2MconTeJiBZigA=="], "ai-gateway-provider/@ai-sdk/groq": ["@ai-sdk/groq@3.0.59", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-X4h60TGq4pIOXPsthatUr+bfTaYCaKGX597hG9JgcueEl4+nboCdw99ixjFKGkvYlBJwLCCfI957EmGA2QlF0w=="], @@ -6411,7 +6400,7 @@ "opencode/@ai-sdk/cerebras": ["@ai-sdk/cerebras@2.0.60", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.54", "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Rnok3cThg6awBwaDSyiZpgRpbV7pqxGYrA89LODCo5cuEHeP2h0AM0lLHP7zIkclAdXfOm4wldKi/S2T/DGCOw=="], - "opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.84", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.38" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cmgbeJL0bbY0yTJH4/AdmP5E7MjWRL9G8UdhIi0JlV/So03o82ORJofW8OzwCZPTORVQblFbpZXYGDcUd9NdUQ=="], + "opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.88", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@ai-sdk/provider-utils": "4.0.40" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6M4+bxK/UMijDYK2ia1hCKYma1iajFYxHDlhbinFUdhH2WbFsjoZRMpSjEMlTOBptqmh2J1JclKnprGulF8WwQ=="], "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=="], @@ -6515,8 +6504,6 @@ "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - "topojson-client/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "tree-sitter-bash/node-addon-api": ["node-addon-api@8.8.0", "", {}, "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA=="], "tw-to-css/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], @@ -6593,6 +6580,8 @@ "@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@ai-sdk/anthropic/@ai-sdk/provider-utils/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="], + "@ai-sdk/azure/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -6931,7 +6920,7 @@ "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "@opencode-ai/desktop/@actions/artifact/@actions/http-client": ["@actions/http-client@2.2.3", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^5.25.4" } }, "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA=="], @@ -7001,9 +6990,9 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock/@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=="], - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], + "ai-gateway-provider/@ai-sdk/azure/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="], + "ai-gateway-provider/@ai-sdk/azure/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], "ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.54", "", { "dependencies": { "@ai-sdk/provider": "3.0.12", "@ai-sdk/provider-utils": "4.0.33" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OyXt0zK8y2/ZIyWlbxTv2r1M7AK227S+Gl4BYOEF42q0wz1n5m4fwR8L4Fy/MQ4Ho6xje47MPsFcRdIqIyP6Rw=="], @@ -7015,6 +7004,10 @@ "ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="], + "ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], + + "ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="], "ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.45", "", { "dependencies": { "@ai-sdk/provider": "3.0.15", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^5.29.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7u5B/E2uZmU65SlJhhQGFHZwRCN0xOz4HHtFc4sEGV9PHbX3fGiEiZBpc/SABay1dGeJgK3VD60rvLGoWdWPXA=="], @@ -7183,7 +7176,7 @@ "opencode/@ai-sdk/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="], - "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.38", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-/HHGmtKllqjg1OLc023v9w9kK3laW7Z6TzfZukYQWCsGBbzB9p60zTvvpXFVcs44NZBVXL3viOa1HRKUbeee8g=="], + "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.40", "", { "dependencies": { "@ai-sdk/provider": "3.0.14", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-OL5IrpUm9Y8Dwy+w/vvFwPotS6m52O9W0op2oXgXdCROMJIBalBI0oro6OIBYkPxvm5Xg02GSkoQN25RlR0bnw=="], "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], @@ -7451,9 +7444,7 @@ "ai-gateway-provider/@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "ai-gateway-provider/@ai-sdk/azure/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -7461,6 +7452,8 @@ "ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + "ai-gateway-provider/@ai-sdk/deepseek/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], diff --git a/infra/console.ts b/infra/console.ts index 79556f5e0c7c..ef53d6a87ba5 100644 --- a/infra/console.ts +++ b/infra/console.ts @@ -150,8 +150,9 @@ const zenLitePrice = new stripe.Price("ZenLitePrice", { }) const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", { properties: { - product: zenLiteProduct.id, - price: zenLitePrice.id, + // Use existing Go resources in dev's checkout Stripe account. + product: $app.stage === "dev" ? "prod_U1tUscpmwtV2bG" : zenLiteProduct.id, + price: $app.stage === "dev" ? "price_1T3phhE7fOCwHSD4zS6w2NPy" : zenLitePrice.id, priceInr: 92900, firstMonth50Coupon: zenLiteCouponFirstMonth50.id, firstMonth100Coupon: zenLiteCouponFirstMonth100.id, @@ -221,6 +222,15 @@ const STRIPE_PUBLISHABLE_KEY = new sst.Secret("STRIPE_PUBLISHABLE_KEY") const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", { properties: { value: auth.url.apply((url) => url!) }, }) +// Preview branches have independent databases; do not send their workspaces to shared dev. +const migrationDomain = + $app.stage === "production" ? "opencode.ai" : $app.stage === "dev" ? "dev.opencode.ai" : undefined +const consoleMigration = new sst.Linkable("ConsoleMigration", { + properties: { + consoleUrl: migrationDomain ? `https://${migrationDomain}/console` : "", + inferenceUrl: migrationDomain ? `https://${migrationDomain}/inference` : "", + }, +}) const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", { properties: { value: stripeWebhook.secret }, }) @@ -235,6 +245,7 @@ const bucketNew = new sst.cloudflare.Bucket("ZenDataNew") const DISCORD_INCIDENT_WEBHOOK_URL = new sst.Secret("DISCORD_INCIDENT_WEBHOOK_URL") const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID") const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY") +const ENTERPRISE_SALES_INBOX_EMAIL = new sst.Secret("ENTERPRISE_SALES_INBOX_EMAIL") const SALESFORCE_CLIENT_ID = new sst.Secret("SALESFORCE_CLIENT_ID") const SALESFORCE_CLIENT_SECRET = new sst.Secret("SALESFORCE_CLIENT_SECRET") @@ -255,6 +266,7 @@ new sst.cloudflare.x.SolidStart("Console", { SECRET.UpstashRedisRestUrl, SECRET.UpstashRedisRestToken, AUTH_API_URL, + consoleMigration, STRIPE_WEBHOOK_SECRET, SECRET.SupportApiKey, DISCORD_INCIDENT_WEBHOOK_URL, @@ -263,6 +275,7 @@ new sst.cloudflare.x.SolidStart("Console", { EMAILOCTOPUS_API_KEY, AWS_SES_ACCESS_KEY_ID, AWS_SES_SECRET_ACCESS_KEY, + ENTERPRISE_SALES_INBOX_EMAIL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET, SALESFORCE_INSTANCE_URL, diff --git a/nix/hashes.json b/nix/hashes.json index acc1a09e0aaa..845909e093d8 100644 --- a/nix/hashes.json +++ b/nix/hashes.json @@ -1,8 +1,8 @@ { "nodeModules": { - "x86_64-linux": "sha256-Be1I6OG6UitofhcGu2BeNzevmoQXc4Or5r/NPzwtft4=", - "aarch64-linux": "sha256-O+d+26CQIjZ08Rn8Qm3IytdDqIMbPdhzaGOZeUKAvIU=", - "aarch64-darwin": "sha256-ObS50y/oy6fM9wSGUL/wx6O0+fTWHC04mXJNd7w/2Z0=", - "x86_64-darwin": "sha256-eoR7ZSyH62Fq2ZaW2b2QqU2FC97rYxTMeEe+djT0nto=" + "x86_64-linux": "sha256-tHl+UGkUbalkh+C5RDkRpZ3Q87tgvqnoF4xdih6QeOw=", + "aarch64-linux": "sha256-rdnbvlOOr/88SQ9zd+PfilJ4pQZdKplFyQGfdHBuBKQ=", + "aarch64-darwin": "sha256-28GpwYLMo2cN4Y9cY6/xn9R5EggFj/m1guuSxsbqisM=", + "x86_64-darwin": "sha256-eO0fs/0gwWwqPA8WElg8s7EZsxKd9J4Hws15P4xqHlc=" } } diff --git a/package.json b/package.json index 0f11d0c3966a..a3f9544410da 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "module", "packageManager": "bun@1.3.14", "scripts": { - "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", + "dev": "bun run --cwd packages/opencode src/index.ts", "dev:desktop": "bun --cwd packages/desktop dev", "dev:web": "bun --cwd packages/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", @@ -137,6 +137,7 @@ "electron" ], "overrides": { + "@ai-sdk/anthropic": "3.0.111", "@opentui/core": "catalog:", "@opentui/keymap": "catalog:", "@opentui/solid": "catalog:", @@ -145,7 +146,6 @@ }, "patchedDependencies": { "@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch", - "@ff-labs/fff-bun@0.9.3": "patches/@ff-labs%2Ffff-bun@0.9.3.patch", "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.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", @@ -160,6 +160,9 @@ "effect@4.0.0-beta.83": "patches/effect@4.0.0-beta.83.patch", "@tanstack/virtual-core@3.17.3": "patches/@tanstack%2Fvirtual-core@3.17.3.patch", "@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch", - "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch" + "@ai-sdk/groq@3.0.31": "patches/@ai-sdk%2Fgroq@3.0.31.patch", + "@ai-sdk/anthropic@3.0.111": "patches/@ai-sdk%2Fanthropic@3.0.111.patch", + "@ai-sdk/amazon-bedrock@4.0.166": "patches/@ai-sdk%2Famazon-bedrock@4.0.166.patch", + "@ai-sdk/openai@3.0.88": "patches/@ai-sdk%2Fopenai@3.0.88.patch" } } diff --git a/packages/app/AGENTS.md b/packages/app/AGENTS.md index 72a973ebd40c..5638ea8e6d1a 100644 --- a/packages/app/AGENTS.md +++ b/packages/app/AGENTS.md @@ -11,7 +11,7 @@ - `opencode dev web` proxies `https://app.opencode.ai`, so local UI/CSS changes will not show there. - For local UI changes, run the backend and app dev servers separately. -- Backend (from `packages/opencode`): `bun run --conditions=browser ./src/index.ts serve --port 4096` +- Backend (from `packages/opencode`): `bun run ./src/index.ts serve --port 4096` - App (from `packages/app`): `bun dev -- --port 4444` - Open `http://localhost:4444` to verify UI changes (it targets the backend at `http://localhost:4096`). diff --git a/packages/app/e2e/regression/session-rename.spec.ts b/packages/app/e2e/regression/session-rename.spec.ts new file mode 100644 index 000000000000..2cd97c24c1c0 --- /dev/null +++ b/packages/app/e2e/regression/session-rename.spec.ts @@ -0,0 +1,141 @@ +import { expect, test } from "@playwright/test" +import { fixture, pageMessages } from "../smoke/session-timeline.fixture" +import { mockOpenCodeServer } from "../utils/mock-server" + +test.beforeEach(async ({ page }) => { + const sessions = fixture.sessions.map((session) => ({ ...session })) + await mockOpenCodeServer(page, { + protocol: "v1", + sessions, + provider: fixture.provider, + directory: fixture.directory, + project: fixture.project, + pageMessages, + }) + await page.route(/\/session\/[^/]+(?:\?.*)?$/, async (route) => { + if (route.request().method() !== "PATCH") return route.fallback() + const id = new URL(route.request().url()).pathname.split("/").at(-1) + const session = sessions.find((item) => item.id === id) + const payload: unknown = route.request().postDataJSON() + if ( + !session || + !payload || + typeof payload !== "object" || + !("title" in payload) || + typeof payload.title !== "string" + ) + throw new Error("Invalid rename request") + session.title = payload.title + await route.fulfill({ json: session, headers: { "access-control-allow-origin": "*" } }) + }) + await page.addInitScript((directory) => { + localStorage.setItem( + "opencode.global.dat:server", + JSON.stringify({ + projects: { local: [{ worktree: directory, expanded: true }] }, + lastProject: { local: directory }, + }), + ) + }, fixture.directory) + await page.goto("/") + await page.locator('[data-component="home-session-row"]').filter({ hasText: fixture.expected.targetTitle }).click() + await expect(page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true })).toBeVisible() +}) + +for (const commit of ["Enter", "blur", "click outside"]) { + test(`saves the session heading on ${commit}`, async ({ page }) => { + await page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true }).click() + const input = page.locator('input[data-slot="session-title-child"]') + await expect(input).toBeFocused() + await input.fill("Renamed session") + if (commit === "Enter") await input.press("Enter") + if (commit === "blur") await input.press("Tab") + if (commit === "click outside") await page.getByRole("textbox", { name: "Prompt", exact: true }).click() + await expect(page.getByRole("heading", { name: "Renamed session", exact: true })).toBeVisible() + await expect(page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: "Renamed session" })).toBeVisible() + await page.reload() + await expect(page.getByRole("heading", { name: "Renamed session", exact: true })).toBeVisible() + }) +} + +test("cancels the session heading with Escape", async ({ page }) => { + await page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true }).click() + const input = page.locator('input[data-slot="session-title-child"]') + await input.fill("Discard this title") + await input.press("Escape") + await expect(page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true })).toBeVisible() + await page.reload() + await expect(page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true })).toBeVisible() +}) + +test("keeps the draft when saving the session heading fails", async ({ page }) => { + await page.route(/\/session\/[^/]+(?:\?.*)?$/, (route) => { + if (route.request().method() !== "PATCH") return route.fallback() + return route.fulfill({ status: 500, headers: { "access-control-allow-origin": "*" } }) + }) + await page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true }).click() + const input = page.locator('input[data-slot="session-title-child"]') + await input.fill("Retry this title") + await input.press("Tab") + await expect(page.getByText("Request failed", { exact: true })).toBeVisible() + await expect(input).toBeEnabled() + await expect(input).toHaveValue("Retry this title") + await expect( + page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: fixture.expected.targetTitle }), + ).toBeVisible() +}) + +test("does not save an empty session heading", async ({ page }) => { + await page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true }).click() + const input = page.locator('input[data-slot="session-title-child"]') + await input.fill(" ") + await input.press("Tab") + await expect(page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true })).toBeVisible() + await page.reload() + await expect(page.getByRole("heading", { name: fixture.expected.targetTitle, exact: true })).toBeVisible() +}) + +test("renames and closes the session tab from its context menu", async ({ page }) => { + const tab = page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: fixture.expected.targetTitle }) + await tab.click({ button: "right" }) + await expect(page.getByRole("menuitem", { name: "Rename", exact: true })).toBeVisible() + await page.keyboard.press("Escape") + await expect(page.getByRole("menuitem", { name: "Rename", exact: true })).toBeHidden() + await expect(tab).toBeFocused() + await tab.press("Shift+F10") + await page.getByRole("menuitem", { name: "Rename", exact: true }).click() + const input = page.locator('[data-slot="tab-title"][contenteditable="true"]') + await expect(input).toBeFocused() + await input.fill("Renamed from tab") + await input.press("Enter") + await expect(page.getByRole("heading", { name: "Renamed from tab", exact: true })).toBeVisible() + await page.reload() + await expect(page.getByRole("heading", { name: "Renamed from tab", exact: true })).toBeVisible() + const renamed = page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: "Renamed from tab" }) + await renamed.click({ button: "right" }) + await page.getByRole("menuitem", { name: "Close tab", exact: true }).click() + await expect(renamed).toBeHidden() + await page.getByRole("button", { name: "Home", exact: true }).click() + await expect( + page.locator('[data-component="home-session-row"]').filter({ hasText: "Renamed from tab" }), + ).toBeVisible() +}) + +test("renames an inactive tab without switching sessions", async ({ page }) => { + await page.getByRole("button", { name: "Home", exact: true }).click() + await page.locator('[data-component="home-session-row"]').filter({ hasText: fixture.expected.sourceTitle }).click() + await expect(page.getByRole("heading", { name: fixture.expected.sourceTitle, exact: true })).toBeVisible() + const tab = page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: fixture.expected.targetTitle }) + await tab.click({ button: "right" }) + await page.getByRole("menuitem", { name: "Rename", exact: true }).click() + const input = page.locator('[data-slot="tab-title"][contenteditable="true"]') + await expect(input).toBeFocused() + await input.fill("Inactive tab renamed") + await input.press("Tab") + await expect(page.getByRole("heading", { name: fixture.expected.sourceTitle, exact: true })).toBeVisible() + await expect(page).toHaveURL(new RegExp(`/session/${fixture.sourceID}$`)) + await page.locator('[data-slot="titlebar-tabs"] a').filter({ hasText: "Inactive tab renamed" }).click() + await expect(page.getByRole("heading", { name: "Inactive tab renamed", exact: true })).toBeVisible() + await page.reload() + await expect(page.getByRole("heading", { name: "Inactive tab renamed", exact: true })).toBeVisible() +}) diff --git a/packages/app/e2e/regression/subagent-child-navigation.spec.ts b/packages/app/e2e/regression/subagent-child-navigation.spec.ts index 019cc156eca1..c4ed6a78a040 100644 --- a/packages/app/e2e/regression/subagent-child-navigation.spec.ts +++ b/packages/app/e2e/regression/subagent-child-navigation.spec.ts @@ -39,7 +39,7 @@ test("shows the not found fallback when the viewed session is deleted", async ({ }) await expect(page.getByText("This session cannot be found")).toBeVisible() - await expect(page.getByRole("button", { name: "Close Tab" })).toBeVisible() + await expect(page.getByRole("button", { name: "Close Tab", exact: true })).toBeVisible() await expect(page.getByRole("heading", { name: taskDescription })).toHaveCount(0) }) diff --git a/packages/app/package.json b/packages/app/package.json index 729c16e4a2d2..17b356ed6087 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/app", - "version": "1.18.21", + "version": "1.18.30", "description": "", "type": "module", "exports": { @@ -32,7 +32,7 @@ }, "license": "MIT", "devDependencies": { - "@happy-dom/global-registrator": "20.0.11", + "@happy-dom/global-registrator": "20.12.0", "@playwright/test": "catalog:", "@sentry/vite-plugin": "catalog:", "@tailwindcss/vite": "catalog:", diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index aa1f519984bb..1081310e5f21 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -33,6 +33,7 @@ import { ExternalLink } from "@/components/external-link" import { useServerSDK } from "@/context/server-sdk" import { useServerSync } from "@/context/server-sync" import { useLanguage } from "@/context/language" +import { usePlatform } from "@/context/platform" import { useSettings } from "@/context/settings" import { popularProviders, useProviders } from "@/hooks/use-providers" import { CustomProviderForm } from "./dialog-custom-provider" @@ -386,6 +387,7 @@ function ProviderConnection(props: { const serverSDK = useServerSDK() const params = useParams() const language = useLanguage() + const platform = usePlatform() const settings = useSettings() const newLayout = settings.general.newLayoutDesigns const providers = useProviders(() => props.directory?.()) @@ -560,6 +562,11 @@ function ProviderConnection(props: { }) .then((x) => { if (!alive.value) return + if (props.provider === "opencode" && platform.platform === "desktop") { + const url = new URL(x.data.url) + url.searchParams.set("client_id", "opencode-desktop") + x.data.url = url.href + } dispatch({ type: "auth.complete", authorization: x.data }) }) .catch((e) => { diff --git a/packages/app/src/components/session/open-in-app-v2.tsx b/packages/app/src/components/session/open-in-app-v2.tsx index e26ff2e0eaba..b5291dcc32cd 100644 --- a/packages/app/src/components/session/open-in-app-v2.tsx +++ b/packages/app/src/components/session/open-in-app-v2.tsx @@ -31,7 +31,10 @@ export function OpenInAppV2(props: { directory: () => string }) { disabled={state.opening()} aria-label={language.t("session.header.open.ariaLabel", { app: state.current().label })} > - }> + } + > diff --git a/packages/app/src/components/titlebar-tab-nav.tsx b/packages/app/src/components/titlebar-tab-nav.tsx index 65493c54dff5..b016f286a250 100644 --- a/packages/app/src/components/titlebar-tab-nav.tsx +++ b/packages/app/src/components/titlebar-tab-nav.tsx @@ -1,9 +1,11 @@ import { createEffect, createMemo, createSignal, onCleanup, Show, type Ref } from "solid-js" +import { createStore } from "solid-js/store" import { makeEventListener } from "@solid-primitives/event-listener" import { createResizeObserver } from "@solid-primitives/resize-observer" import { createMutation } from "@tanstack/solid-query" import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2" import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon" +import { MenuV2 } from "@opencode-ai/ui/v2/menu-v2" import { useGlobal } from "@/context/global" import { useLanguage } from "@/context/language" import { ServerConnection, serverName } from "@/context/server" @@ -33,6 +35,8 @@ export function TabNavItem(props: { pressed?: boolean hidden?: boolean }) { + const language = useLanguage() + const [menu, setMenu] = createStore({ open: false, rename: false }) const [editing, setEditing] = createSignal(false) const [titleOverflowing, setTitleOverflowing] = createSignal(false) let tabRoot!: HTMLDivElement @@ -76,7 +80,7 @@ export function TabNavItem(props: { }) const [popoverOpen, setPopoverOpen] = createSignal(false) - const previewBlocked = () => !!props.dragging || editing() || !!props.pressed || !props.session() + const previewBlocked = () => !!props.dragging || editing() || menu.open || !!props.pressed || !props.session() const measureTitleOverflow = () => { if (!titleEl || editing()) { @@ -138,9 +142,9 @@ export function TabNavItem(props: { titleEl.textContent = value }) - const openRename = (event: MouseEvent) => { - event.preventDefault() - event.stopPropagation() + const openRename = (event?: MouseEvent) => { + event?.preventDefault() + event?.stopPropagation() if (!canOpenTabRename(props.dragging, editing(), rename.isPending)) return const session = props.session() if (!session) return @@ -171,7 +175,7 @@ export function TabNavItem(props: { onCleanup(cleanup) }) - const tab = ( + const tab = () => (
{ tabRoot = el @@ -196,7 +200,11 @@ export function TabNavItem(props: { closeTab(event) }} > - - +
} + aria-label={language.t("common.closeTab")} />
) return ( - { - if (value && previewBlocked()) return - setPopoverOpen(value) - }} - data={{ - projectName: projectName(), - title: props.session()?.title, - path: previewPath(), - serverName: serverLabel(), + { + setMenu("open", open) + if (open) setPopoverOpen(false) }} - /> + > + { + if (value && previewBlocked()) return + setPopoverOpen(value) + }} + data={{ + projectName: projectName(), + title: props.session()?.title, + path: previewPath(), + serverName: serverLabel(), + }} + /> + + { + if (!menu.rename) return + event.preventDefault() + setMenu("rename", false) + openRename() + }} + > + setMenu("rename", true)}> + {language.t("common.rename")} + + {language.t("common.closeTab")} + + + ) } diff --git a/packages/app/src/context/global-sync/home-session-index.test.ts b/packages/app/src/context/global-sync/home-session-index.test.ts index 4e40cc78eaea..9b94f1de2128 100644 --- a/packages/app/src/context/global-sync/home-session-index.test.ts +++ b/packages/app/src/context/global-sync/home-session-index.test.ts @@ -1,8 +1,10 @@ import { describe, expect, test } from "bun:test" -import type { SessionV2Info } from "@opencode-ai/sdk/v2/client" +import { QueryClient } from "@tanstack/solid-query" +import type { Session, SessionV2Info } from "@opencode-ai/sdk/v2/client" import { applyHomeSessionEvent, appendHomeSessionEvent, + createHomeSessionIndexCache, HOME_V2_SESSION_PAGE_LIMIT, loadHomeSessionIndex, homeSessionIndexSessions, @@ -151,4 +153,33 @@ describe("Home V2 session index", () => { expect(homeSessionIndexRefresh("global.disposed", true).refetch).toBe(true) expect(homeSessionIndexRefresh("session.next.moved", true).refetch).toBe(true) }) + + test("removes a session from the loaded Home index", () => { + const queryClient = new QueryClient() + const cache = createHomeSessionIndexCache(queryClient, "server") + const sessions = [ + { id: "a", time: { created: 1, updated: 1 } }, + { id: "b", time: { created: 1, updated: 1 } }, + ] as Session[] + queryClient.setQueryData(cache.indexKey, { sessions, eventSequence: 0 }) + + cache.remove("a") + + const index = queryClient.getQueryData<{ sessions: Session[] }>(cache.indexKey) + expect(index?.sessions.map((item) => item.id)).toEqual(["b"]) + }) + + test("keeps the session out of the Home list when the index is not mounted", () => { + const queryClient = new QueryClient() + const cache = createHomeSessionIndexCache(queryClient, "server") + const sessions = [ + { id: "a", time: { created: 1, updated: 1 } }, + { id: "b", time: { created: 1, updated: 1 } }, + ] as Session[] + + cache.remove("a") + + expect(queryClient.getQueryData(cache.indexKey)).toBeUndefined() + expect(cache.sessions({ sessions, eventSequence: 0 }, undefined).map((item) => item.id)).toEqual(["b"]) + }) }) diff --git a/packages/app/src/context/global-sync/home-session-index.ts b/packages/app/src/context/global-sync/home-session-index.ts index 03a085e34d51..781c39c45012 100644 --- a/packages/app/src/context/global-sync/home-session-index.ts +++ b/packages/app/src/context/global-sync/home-session-index.ts @@ -85,6 +85,7 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st const indexKey = homeSessionIndexKey(server) const eventsKey = homeSessionEventsKey(server) let connected = false + const removed = new Set() return { indexKey, @@ -97,7 +98,8 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st queryClient.setQueryData(eventsKey, (current) => trimHomeSessionEvents(current, sequence)) }, sessions(index: HomeSessionIndex | undefined, events: HomeSessionEvents | undefined) { - return homeSessionIndexSessions(index, events) + const sessions = homeSessionIndexSessions(index, events) + return removed.size === 0 ? sessions : sessions.filter((session) => !removed.has(session.id)) }, apply(event: HomeSessionEvent) { if (!queryClient.getQueryState(indexKey)) return @@ -116,6 +118,16 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st } queryClient.setQueryData(eventsKey, { sequence: next.sequence, entries: [] }) }, + remove(sessionID: string) { + removed.add(sessionID) + if (!queryClient.getQueryState(indexKey)) return + queryClient.setQueryData(indexKey, (index) => { + if (!index) return index + const at = index.sessions.findIndex((session) => session.id === sessionID) + if (at === -1) return index + return { ...index, sessions: index.sessions.toSpliced(at, 1) } + }) + }, refresh(event: Event["type"]) { const result = homeSessionIndexRefresh(event, connected) connected = result.connected diff --git a/packages/app/src/pages/home/home-sessions-controller.tsx b/packages/app/src/pages/home/home-sessions-controller.tsx index 25d896393ca6..f306f208cc80 100644 --- a/packages/app/src/pages/home/home-sessions-controller.tsx +++ b/packages/app/src/pages/home/home-sessions-controller.tsx @@ -219,13 +219,15 @@ export function createHomeSessionsController(home: HomeController) { directory: session.directory, time: { archived: Date.now() }, }), - remove: () => + remove: () => { setStore( produce((draft) => { const match = Binary.search(draft.session, session.id, (item) => item.id) if (match.found) draft.session.splice(match.index, 1) }), - ), + ) + homeSessions().remove(session.id) + }, onError: (cause) => showToast({ title: language.t("common.requestFailed"), diff --git a/packages/app/src/pages/session/session-archive.ts b/packages/app/src/pages/session/session-archive.ts index 5e1314dbd14d..396886953954 100644 --- a/packages/app/src/pages/session/session-archive.ts +++ b/packages/app/src/pages/session/session-archive.ts @@ -3,6 +3,7 @@ import { produce } from "solid-js/store" import { notifySessionTabsRemoved } from "@/components/titlebar-session-events" import { useLanguage } from "@/context/language" import { useSDK } from "@/context/sdk" +import { useServerSync } from "@/context/server-sync" import { useSync } from "@/context/sync" import { useTabs } from "@/context/tabs" import { errorMessage } from "@/pages/layout/helpers" @@ -15,6 +16,7 @@ export function useSessionArchive() { const navigate = useNavigate() const sdk = useSDK() const sync = useSync() + const serverSync = useServerSync() const tabs = useTabs() const { params } = useSessionKey() @@ -56,6 +58,7 @@ export function useSessionArchive() { }), ) sync().session.evict(sessionID) + serverSync().homeSessions.remove(sessionID) navigateAfterRemoval(sessionID, session.parentID, nextSession?.id) notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] }) }) diff --git a/packages/app/src/pages/session/timeline/message-timeline.tsx b/packages/app/src/pages/session/timeline/message-timeline.tsx index 18a153d29971..e0838825570e 100644 --- a/packages/app/src/pages/session/timeline/message-timeline.tsx +++ b/packages/app/src/pages/session/timeline/message-timeline.tsx @@ -777,6 +777,7 @@ export function MessageTimeline(props: { } const saveTitleEditor = () => { + if (!title.editing) return const id = sessionID() if (!id) return if (titleMutation.isPending) return @@ -1447,6 +1448,7 @@ export function MessageTimeline(props: { onInput={(event) => setTitle("draft", event.currentTarget.value)} onKeyDown={(event) => { event.stopPropagation() + if (event.isComposing || event.keyCode === 229) return if (event.key === "Enter") { event.preventDefault() void saveTitleEditor() @@ -1457,7 +1459,7 @@ export function MessageTimeline(props: { closeTitleEditor() } }} - onBlur={closeTitleEditor} + onBlur={saveTitleEditor} />
diff --git a/packages/cli/package.json b/packages/cli/package.json index af75216f8d6e..f6dd6bf831f3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/cli", - "version": "1.18.21", + "version": "1.18.30", "type": "module", "license": "MIT", "bin": { diff --git a/packages/codemode/package.json b/packages/codemode/package.json index 04130771723a..c516b3a98589 100644 --- a/packages/codemode/package.json +++ b/packages/codemode/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@opencode-ai/codemode", - "version": "1.18.21", + "version": "1.18.30", "description": "Effect-native confined code execution over schema-described tools", "private": true, "type": "module", diff --git a/packages/console/app/package.json b/packages/console/app/package.json index 46b387dbb8fc..3651594fd764 100644 --- a/packages/console/app/package.json +++ b/packages/console/app/package.json @@ -1,6 +1,6 @@ { "name": "@opencode-ai/console-app", - "version": "1.18.21", + "version": "1.18.30", "type": "module", "license": "MIT", "scripts": { @@ -29,6 +29,7 @@ "@upstash/redis": "1.38.0", "chart.js": "4.5.1", "nitro": "3.0.1-alpha.1", + "number-flow": "0.6.2", "solid-js": "catalog:", "solid-list": "0.3.0", "solid-stripe": "0.8.1", diff --git a/packages/console/app/src/component/go-models.ts b/packages/console/app/src/component/go-models.ts new file mode 100644 index 000000000000..6df79f4a3921 --- /dev/null +++ b/packages/console/app/src/component/go-models.ts @@ -0,0 +1,53 @@ +// Requests are per 5 hours; allowances are monthly usage at official API prices. +export const goModels = [ + { id: "kimi-k3", name: "Kimi K3", requests: 110, allowance: 15, featured: true }, + { id: "qwen3.8-max", name: "Qwen3.8 Max", requests: 160, allowance: 15 }, + { id: "grok-4.6", name: "Grok 4.6", requests: 169, allowance: 15 }, + { id: "qwen3.7-max", name: "Qwen3.7 Max", requests: 170, allowance: 30 }, + { id: "glm-5.3", name: "GLM-5.3", requests: 220, allowance: 15 }, + { id: "glm-5.2", name: "GLM-5.2", requests: 880, allowance: 60 }, + { id: "glm-5.1", name: "GLM-5.1", requests: 880, allowance: 60 }, + { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", requests: 1050, allowance: 15 }, + { id: "kimi-k2.6", name: "Kimi K2.6", requests: 1150, allowance: 60 }, + { id: "kimi-k2.7-code", name: "Kimi K2.7 Code", requests: 1350, allowance: 60, featured: true }, + { id: "hy4-preview", name: "Hy4 preview", requests: 1350, allowance: 30 }, + { id: "gpt-5.6-luna", name: "GPT 5.6 Luna", requests: 2050, allowance: 15, featured: true }, + { id: "minimax-m3", name: "MiniMax M3", requests: 3200, allowance: 60, featured: true }, + { id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", requests: 3250, allowance: 15 }, + { id: "qwen3.6-plus", name: "Qwen3.6 Plus", requests: 3300, allowance: 60 }, + { id: "minimax-m2.7", name: "MiniMax M2.7", requests: 3400, allowance: 60 }, + { id: "deepseek-v4-flash-vision-exp", name: "DeepSeek V4 Flash Vision Exp", requests: 6500, allowance: 15 }, + { id: "qwen3.7-plus", name: "Qwen3.7 Plus", requests: 4300, allowance: 60, featured: true }, + { id: "hy3", name: "Hy3", requests: 4300, allowance: 60 }, + { id: "qwen3.8-flash", name: "Qwen3.8 Flash", requests: 5400, allowance: 30 }, + { id: "glm-5.3-flash", name: "GLM-5.3-Flash", requests: 6320, allowance: 60, featured: true }, + { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", requests: 13000, allowance: 30, featured: true }, + { + id: "deepseek-flash", + name: "DeepSeek V4.1 Flash", + requests: 26000, + baseRequests: 6500, + allowance: 60, + baseAllowance: 15, + bonus: 4, + featured: true, + fresh: true, + }, + { id: "longcat-2.0", name: "LongCat-2.0", requests: 11400, allowance: 60 }, + { id: "mimo-v2.5", name: "MiMo-V2.5", requests: 30100, allowance: 60, featured: true }, + { + id: "muse-spark-1.3-contributor", + name: "Muse Spark 1.3 Contributor", + requests: 45300, + allowance: 60, + featured: true, + regions: true, + }, + { + id: "muse-spark-1.2-contributor", + name: "Muse Spark 1.2 Contributor", + requests: 45300, + allowance: 60, + regions: true, + }, +].sort((a, b) => a.requests - b.requests) diff --git a/packages/console/app/src/component/limits-graph.css b/packages/console/app/src/component/limits-graph.css new file mode 100644 index 000000000000..ddcf2dfece52 --- /dev/null +++ b/packages/console/app/src/component/limits-graph.css @@ -0,0 +1,300 @@ +[data-component="go-usage"] { + --model-width: 16.5rem; + --request-width: 5.25rem; + --allowance-width: 6.75rem; + --column-gap: 1.5rem; + --bar-origin: left; + container-type: inline-size; + margin: 0; + padding: 3rem var(--padding) 2.5rem; + font-variant-numeric: tabular-nums; + scroll-margin-top: 6rem; + -webkit-font-smoothing: antialiased; + + &:dir(rtl) { + --bar-origin: right; + } + + [data-slot="heading"] { + padding-bottom: 1.5rem; + + h2 { + color: var(--color-text-strong); + font-size: 1rem; + font-weight: 600; + } + } + + s { + display: block; + color: var(--color-text-weak); + font-size: 0.6875rem; + font-weight: 400; + } + + [data-slot="columns"], + [data-slot="model-row"] { + display: grid; + grid-template-columns: var(--model-width) minmax(0, 1fr) var(--allowance-width); + align-items: center; + column-gap: var(--column-gap); + } + + [data-slot="columns"] { + padding-bottom: 0.875rem; + border-bottom: 1px solid var(--color-border-weak); + font-size: 0.6875rem; + color: var(--color-text); + overflow-wrap: anywhere; + } + + [data-slot="requests-heading"], + [data-slot="allowance-heading"] { + text-align: end; + } + + [data-slot="rows"] { + padding-top: 0.375rem; + } + + [data-slot="model-row"] { + min-height: 2.5rem; + font-size: 0.8125rem; + } + + [data-slot="model"] { + display: flex; + align-items: baseline; + flex-wrap: wrap; + gap: 0.375rem 0.5rem; + min-width: 0; + line-height: 1.5; + color: var(--color-text); + font-weight: 400; + } + + [data-slot="badge"] { + color: var(--color-text-strong); + font-size: 0.625rem; + line-height: 1.3; + border: 1px solid var(--color-border); + padding: 0.0625rem 0.25rem; + } + + [data-slot="region"] { + display: inline-flex; + align-self: center; + padding: 0.25rem; + color: var(--color-text); + text-decoration: none; + + &:hover { + color: var(--color-text-strong); + } + } + + [data-slot="usage-value"] { + display: grid; + grid-template-columns: minmax(0, 1fr) var(--request-width); + align-items: center; + gap: var(--column-gap); + align-self: stretch; + } + + [data-slot="track"] { + display: flex; + align-items: center; + align-self: stretch; + position: relative; + isolation: isolate; + } + + [data-slot="gridline"] { + position: absolute; + inset-block: 0; + inset-inline-start: var(--position); + border-inline-start: 1px dotted var(--color-border-weak); + z-index: -1; + } + + [data-slot="bar"] { + width: var(--width); + height: 0.375rem; + background: var(--color-go-2); + transform-origin: var(--bar-origin); + } + + &[data-visible] [data-slot="bar"] { + animation: go-usage-reveal 700ms cubic-bezier(0.22, 1, 0.36, 1) both; + animation-delay: var(--delay); + } + + [data-slot="requests"] { + text-align: end; + font-weight: 600; + color: var(--color-text-strong); + } + + [data-slot="allowance"] { + text-align: end; + color: var(--color-text); + + &[data-high] { + color: var(--color-text-strong); + } + } + + [data-slot="currency"] { + color: var(--color-text-weak); + } + + [data-slot="axis"] { + padding-inline-start: calc(var(--model-width) + var(--column-gap)); + padding-inline-end: calc(var(--request-width) + var(--allowance-width) + 2 * var(--column-gap)); + height: 2.5rem; + } + + [data-slot="ticks"] { + position: relative; + height: 100%; + font-size: 0.625rem; + color: var(--color-text-weak); + + span { + position: absolute; + inset-inline-start: var(--position); + top: 0.75rem; + transform: translateX(-50%); + + &:dir(rtl) { + transform: translateX(50%); + } + } + } + + [data-slot="expand"] { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + min-height: 2.75rem; + padding: 0.75rem 0; + background: none; + border: none; + color: var(--color-text); + font-family: inherit; + font-size: 0.75rem; + cursor: pointer; + + &:hover { + color: var(--color-text-strong); + } + &[aria-expanded="true"] svg { + transform: rotate(180deg); + } + } + + :is(button, a):focus-visible { + outline: 1px solid var(--color-text-strong); + outline-offset: 4px; + } + + figcaption { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 1rem; + border-top: 1px solid var(--color-border-weak); + font-size: 0.75rem; + color: var(--color-text); + + a { + color: inherit; + white-space: nowrap; + } + } + + @container (max-width: 48rem) { + [data-slot="columns"], + [data-slot="model-row"], + [data-slot="axis"] { + --model-width: 13rem; + --request-width: 4rem; + --allowance-width: 6rem; + --column-gap: 1rem; + } + } + + @container (max-width: 38rem) { + [data-slot="columns"], + [data-slot="model-row"] { + --request-width: 5rem; + --allowance-width: 4.5rem; + --column-gap: 0.75rem; + grid-template-columns: minmax(0, 1fr) var(--request-width) var(--allowance-width); + } + + [data-slot="columns"] { + font-size: 0.625rem; + } + + [data-slot="model-row"] { + min-height: 4rem; + padding-block: 0.375rem; + row-gap: 0.25rem; + font-size: 1rem; + } + + [data-slot="model"] { + grid-column: 1 / -1; + } + [data-slot="usage-value"] { + display: contents; + } + + [data-slot="track"] { + grid-column: 1; + grid-row: 2; + align-self: center; + height: 4px; + } + + [data-slot="bar"] { + height: 4px; + } + [data-slot="requests"] { + grid-column: 2; + grid-row: 2; + } + [data-slot="allowance"] { + grid-column: 3; + grid-row: 2; + } + [data-slot="gridline"], + [data-slot="axis"] { + display: none; + } + [data-slot="expand"] { + font-size: 0.8125rem; + } + figcaption { + margin-top: 1rem; + } + } + + @media (prefers-reduced-motion: reduce) { + &[data-visible] [data-slot="bar"] { + animation: none; + } + } +} + +@keyframes go-usage-reveal { + from { + transform: scaleX(0); + } + to { + transform: scaleX(1); + } +} diff --git a/packages/console/app/src/component/limits-graph.tsx b/packages/console/app/src/component/limits-graph.tsx new file mode 100644 index 000000000000..410fd48e4392 --- /dev/null +++ b/packages/console/app/src/component/limits-graph.tsx @@ -0,0 +1,166 @@ +import "./limits-graph.css" +import { For, Show, createMemo, createSignal, createUniqueId, onCleanup, onMount } from "solid-js" +import { useI18n } from "~/context/i18n" +import { useLanguage } from "~/context/language" +import { goModels } from "./go-models" + +// Compress the request range, with the same domain in both views. +const max = Math.max(...goModels.map((model) => model.requests)) +const position = (requests: number) => + 4 + Math.pow(Math.log10(Math.max(requests / 100, 1)) / Math.log10(max / 100), 2.2) * 96 +const ticks = [100, 1000, 10000, 40000] + +export function LimitsGraph(props: { href: string }) { + const i18n = useI18n() + const language = useLanguage() + const id = createUniqueId() + const [expanded, setExpanded] = createSignal(false) + const [visible, setVisible] = createSignal(false) + const models = createMemo(() => goModels.filter((model) => expanded() || model.featured || model.fresh)) + const format = createMemo(() => new Intl.NumberFormat(language.tag(language.locale()))) + const compact = createMemo( + () => new Intl.NumberFormat(language.tag(language.locale()), { notation: "compact", maximumFractionDigits: 1 }), + ) + const currency = createMemo( + () => + new Intl.NumberFormat(language.tag(language.locale()), { + style: "currency", + currency: "USD", + currencyDisplay: "narrowSymbol", + maximumFractionDigits: 0, + }), + ) + let root!: HTMLElement + + onMount(() => { + if (!window.IntersectionObserver) return setVisible(true) + const observer = new IntersectionObserver( + (entries) => { + if (!entries.some((entry) => entry.isIntersecting)) return + setVisible(true) + observer.disconnect() + }, + { threshold: 0.1 }, + ) + observer.observe(root) + onCleanup(() => observer.disconnect()) + }) + + return ( +
+
+

{i18n.t("go.graph.period")}

+
+ +
+
+
+ {i18n.t("go.graph.model")} +
+
+ {i18n.t("go.graph.requests")} +
+
+ {i18n.t("go.graph.allowance")} +
+
+
+ + {(model, index) => ( +
+
+ {model.name} + + {i18n.t("go.graph.new")} + + + {i18n.t("go.graph.bonus", { count: model.bonus! })} + + + + + + +
+
+ +
= 60 ? "" : undefined}> + + {currency().format(model.baseAllowance!)}{" "} + + + + {(part) => ( + {part.value} + )} + + +
+
+ )} + +
+
+ + + +
+ + {i18n.t("go.graph.usageLimits")} +
+
+ ) +} diff --git a/packages/console/app/src/component/rolling-number.tsx b/packages/console/app/src/component/rolling-number.tsx new file mode 100644 index 000000000000..df1af6ca20cf --- /dev/null +++ b/packages/console/app/src/component/rolling-number.tsx @@ -0,0 +1,58 @@ +import NumberFlow from "number-flow" +import { continuous } from "number-flow/plugins" +import { createEffect, onCleanup, onMount } from "solid-js" + +export function RollingNumber(props: { + value: number + target: number + timing: () => { duration: number; easing: string; spinEasing: string } +}) { + let root!: HTMLSpanElement + // Keep the server-rendered text stable while the custom element owns its contents. + const initial = props.value.toLocaleString() + const growing = props.target.toLocaleString().length > initial.length + + onMount(() => { + if (new Intl.NumberFormat().resolvedOptions().numberingSystem !== "latn") { + createEffect(() => { + root.textContent = props.value.toLocaleString() + }) + return + } + + const flow = new NumberFlow() + flow.format = { useGrouping: true, maximumFractionDigits: 0 } + flow.trend = 1 + if (growing) flow.plugins = [continuous] + flow.opacityTiming = { duration: 180, easing: "ease-out" } + const motion = window.matchMedia("(prefers-reduced-motion: reduce)") + const updateMotion = () => { + flow.animated = !motion.matches + } + updateMotion() + motion.addEventListener("change", updateMotion) + onCleanup(() => motion.removeEventListener("change", updateMotion)) + root.replaceChildren(flow) + createEffect(() => { + const value = props.value + if (flow.value === value) return + const timing = props.timing() + flow.transformTiming = { duration: timing.duration, easing: timing.easing } + flow.spinTiming = { + duration: timing.duration, + easing: growing ? "cubic-bezier(0.45, 0, 0.55, 1)" : timing.spinEasing, + } + flow.update(value) + }) + }) + + return ( + + {initial} + + ) +} diff --git a/packages/console/app/src/context/auth.ts b/packages/console/app/src/context/auth.ts index aed07a630f8c..90669d764703 100644 --- a/packages/console/app/src/context/auth.ts +++ b/packages/console/app/src/context/auth.ts @@ -1,6 +1,7 @@ import { getRequestEvent } from "solid-js/web" import { and, Database, eq, inArray, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js" +import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" import { redirect } from "@solidjs/router" import { Actor } from "@opencode-ai/console-core/actor.js" @@ -79,8 +80,15 @@ export const getActor = async (workspace?: string): Promise => { if (accounts.length) { const user = await Database.use((tx) => tx - .select() + .select({ + id: UserTable.id, + workspaceID: UserTable.workspaceID, + accountID: UserTable.accountID, + role: UserTable.role, + migratedAt: WorkspaceTable.migrated_at, + }) .from(UserTable) + .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID)) .where( and( eq(UserTable.workspaceID, workspace), @@ -93,6 +101,15 @@ export const getActor = async (workspace?: string): Promise => { .then((x) => x[0]), ) if (user) { + if (user.migratedAt) { + const destination = Resource.ConsoleMigration.consoleUrl + if (!destination) throw new Error("New Console URL is not configured") + evt.response.headers.set("Cache-Control", "no-store") + throw redirect(`${destination}/login`, { + status: evt.request.method === "GET" || evt.request.method === "HEAD" ? 302 : 303, + headers: { "Cache-Control": "no-store" }, + }) + } await Database.use((tx) => tx .update(UserTable) diff --git a/packages/console/app/src/i18n/ar.ts b/packages/console/app/src/i18n/ar.ts index d648ceee2153..fac1d6475833 100644 --- a/packages/console/app/src/i18n/ar.ts +++ b/packages/console/app/src/i18n/ar.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "يحصل DeepSeek V4.1 Flash على حدود استخدام مضاعفة 4 مرات لفترة محدودة", + "go.graph.bonus": "استخدام مضاعف {{count}} مرات", "nav.github": "GitHub", "nav.docs": "الوثائق", "nav.changelog": "سجل التغييرات", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "إلغاء", "common.creating": "جارٍ الإنشاء...", "common.create": "إنشاء", + "common.contactUs": "اتصل بنا", "common.videoUnsupported": "متصفحك لا يدعم وسم الفيديو.", "common.figure": "شكل {{n}}.", @@ -224,6 +227,9 @@ export const dict = { "zen.faq.q8": "هل يمكنني استخدام Zen مع وكلاء برمجة آخرين؟", "zen.faq.a8": "بينما يعمل Zen بشكل رائع مع OpenCode، يمكنك استخدام Zen مع أي وكيل. اتبع تعليمات الإعداد في وكيل البرمجة المفضل لديك.", + "zen.faq.q9": "هل يمكنني استرداد أموالي؟", + "zen.faq.a9": + "قد تكون مؤهلًا لاسترداد أموالك إذا تم الخصم خلال آخر 14 يومًا ولم تستخدم الرصيد الناتج عن عملية الشراء هذه. {{contact}} لطلب استرداد الأموال.", "zen.cta.start": "ابدأ مع Zen", "zen.pricing.title": "أضف رصيد 20 دولار (دفع حسب الاستخدام)", @@ -252,9 +258,7 @@ export const dict = { "zen.privacy.exceptionsLink": "الاستثناءات التالية", "go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع", - "go.banner.text": "Ox Alpha Free متاح على Go لفترة محدودة", - "go.meta.description": - "يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.", + "go.meta.description": "يبلغ سعر Go ‏$10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.", "go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع", "go.hero.body": "يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.", @@ -263,13 +267,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "اشترك في Go", "go.cta.price": "$10/شهر", - "go.cta.promo": "$5 للشهر الأول", - "go.pricing.body": - "استخدمه مع أي وكيل. $5 للشهر الأول، ثم $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.", + "go.pricing.body": "استخدمه مع أي وكيل. $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.", "go.graph.free": "مجاني", "go.graph.freePill": "Big Pickle ونماذج مجانية", "go.graph.go": "Go", "go.graph.label": "الطلبات كل 5 ساعات", + "go.graph.period": "الاستخدام", + "go.graph.model": "النموذج", + "go.graph.requests": "الطلبات المقدّرة / ٥ ساعات", + "go.graph.allowance": "الاستخدام الشهري", + "go.graph.new": "جديد", + "go.graph.scale": "مقياس غير خطي لعدد الطلبات", + "go.graph.showAll": "عرض جميع النماذج ({{count}})", + "go.graph.showLess": "عرض نماذج أقل", "go.graph.limitedRegions": "مناطق محدودة", "go.graph.limitedTime": "لفترة محدودة", "go.graph.usageLimits": "حدود الاستخدام", @@ -298,20 +308,20 @@ export const dict = { "go.testimonials.frank.quote": "أتمنى لو كنت لا أزال في Nvidia.", "go.problem.title": "ما المشكلة التي يحلها Go؟", "go.problem.body": - "نحن نركز على تقديم تجربة OpenCode لأكبر عدد ممكن من الناس. OpenCode Go هو اشتراك منخفض التكلفة: $5 للشهر الأول، ثم $10/شهر. يوفر حدودا سخية ووصولا موثوقا إلى نماذج المصدر المفتوح الأكثر قدرة.", + "نحن نركز على تقديم تجربة OpenCode لأكبر عدد ممكن من الناس. OpenCode Go هو اشتراك منخفض التكلفة بسعر $10/شهر. يوفر حدودا سخية ووصولا موثوقا إلى نماذج المصدر المفتوح الأكثر قدرة.", "go.problem.subtitle": " ", "go.problem.item1": "أسعار اشتراك منخفضة التكلفة", "go.problem.item2": "حدود سخية ووصول موثوق", "go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين", "go.problem.item4": "مجموعة منسقة من النماذج المختبرة للبرمجة الوكيلة", "go.how.title": "كيف يعمل Go", - "go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.", + "go.how.body": "يبلغ سعر Go ‏$10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.", "go.how.step1.title": "أنشئ حسابًا", "go.how.step1.beforeLink": "اتبع", "go.how.step1.link": "تعليمات الإعداد", "go.how.step2.title": "اشترك في Go", - "go.how.step2.link": "$5 للشهر الأول", - "go.how.step2.afterLink": "ثم $10/شهر مع حدود سخية", + "go.how.step2.link": "$10/شهر", + "go.how.step2.afterLink": "مع حدود سخية", "go.how.step3.title": "ابدأ البرمجة", "go.how.step3.body": "مع وصول موثوق لنماذج مفتوحة المصدر", "go.privacy.title": "خصوصيتك مهمة بالنسبة لنا", @@ -327,11 +337,11 @@ export const dict = { "go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.", "go.faq.q3": "هل Go هو نفسه Zen؟", "go.faq.a3": - "لا. يعتمد Zen على الدفع حسب الاستخدام، بينما يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى مجموعة منسقة من النماذج.", + "لا. يعتمد Zen على الدفع حسب الاستخدام، بينما يبلغ سعر Go ‏$10/شهر، مع حدود سخية ووصول موثوق إلى مجموعة منسقة من النماذج.", "go.faq.q4": "كم تكلفة Go؟", "go.faq.a4.p1.beforePricing": "تكلفة Go", - "go.faq.a4.p1.pricingLink": "$5 للشهر الأول", - "go.faq.a4.p1.afterPricing": "ثم $10/شهر مع حدود سخية.", + "go.faq.a4.p1.pricingLink": "$10/شهر", + "go.faq.a4.p1.afterPricing": "مع حدود سخية.", "go.faq.a4.p2.beforeAccount": "يمكنك إدارة اشتراكك في", "go.faq.a4.p2.accountLink": "حسابك", "go.faq.a4.p3": "ألغِ في أي وقت.", @@ -352,7 +362,7 @@ export const dict = { "go.faq.a5.museRetention": "أسعار توكنات مخفّضة للغاية مقابل منح الإذن باستخدام مطالباتك وإكمالات النموذج لتدريب نماذج Meta المستقبلية.", "go.faq.a5.learnMore": "اعرف المزيد", - "go.faq.a5.deepseekRetention": "تُجدَّد اتفاقية ZDR شهريًا. الاتفاقية الحالية سارية حتى 31 أغسطس 2026.", + "go.faq.a5.deepseekRetention": "تُجدَّد اتفاقية ZDR شهريًا. الاتفاقية الحالية سارية حتى 30 سبتمبر 2026.", "go.faq.a5.beforeExceptions": "تتم استضافة نماذج Go في الولايات المتحدة. يتبع المزودون سياسة عدم الاحتفاظ بالبيانات ولا يستخدمون بياناتك لتدريب النماذج، مع", "go.faq.a5.exceptionsLink": "الاستثناءات التالية", @@ -365,7 +375,10 @@ export const dict = { "go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟", "go.faq.a9": - "تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة قدرها 200 طلب/يوم. يقدّم Go مجموعة منسقة من النماذج مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، وأسبوعية، وشهرية)، تعادل تقريبًا $12 لكل 5 ساعات، و$30 في الأسبوع، و$60 في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).", + "تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة قدرها 200 طلب/يوم. يقدّم Go مجموعة منسقة من النماذج مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، وأسبوعية، وشهرية)، تعادل الحصص الأساسية فيها تقريبًا $12 لكل 5 ساعات و$30 في الأسبوع و$60 في الشهر؛ وقد تختلف الحصص حسب النموذج (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).", + "go.faq.q10": "هل يمكنني استرداد أموالي؟", + "go.faq.a10": + "قد تكون مؤهلًا لاسترداد أموالك إذا تم الخصم خلال آخر 14 يومًا ولم تستخدم مخصصات Go خلال فترة الفوترة تلك. {{contact}} لطلب استرداد الأموال.", "zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.", "zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم", @@ -664,10 +677,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "بضع ثوان", "workspace.lite.subscription.message": "أنت مشترك في OpenCode Go.", "workspace.lite.subscription.manage": "إدارة الاشتراك", - "workspace.lite.subscription.rollingUsage": "الاستخدام المتجدد", + "workspace.lite.subscription.rollingUsage": "الاستخدام خلال 5 ساعات", + "workspace.lite.subscription.rollingQuota": "الحصة خلال 5 ساعات", "workspace.lite.subscription.weeklyUsage": "الاستخدام الأسبوعي", + "workspace.lite.subscription.weeklyQuota": "الحصة الأسبوعية", "workspace.lite.subscription.monthlyUsage": "الاستخدام الشهري", + "workspace.lite.subscription.monthlyQuota": "الحصة الشهرية", "workspace.lite.subscription.resetsIn": "إعادة تعيين في", + "workspace.lite.subscription.showDetails": "إظهار التفاصيل", + "workspace.lite.subscription.hideDetails": "إخفاء التفاصيل", + "workspace.lite.subscription.model": "النموذج", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "الإجمالي", "workspace.lite.subscription.useBalance": "استخدم رصيدك المتوفر بعد الوصول إلى حدود الاستخدام", "workspace.lite.subscription.selectProvider": 'اختر "OpenCode Go" كمزود في إعدادات opencode الخاصة بك لاستخدام نماذج Go.', @@ -680,8 +701,8 @@ export const dict = { "workspace.lite.other.message": "عضو آخر في مساحة العمل هذه مشترك بالفعل في OpenCode Go. يمكن لعضو واحد فقط لكل مساحة عمل الاشتراك.", "workspace.lite.promo.description": - "يبدأ OpenCode Go بسعر {{price}}، ثم $10/شهر، ويوفر وصولا موثوقا لنماذج البرمجة المفتوحة الشهيرة مع حدود استخدام سخية.", - "workspace.lite.promo.price": "$5 للشهر الأول", + "يبلغ سعر OpenCode Go {{price}}، ويوفر وصولا موثوقا لنماذج البرمجة المفتوحة الشهيرة مع حدود استخدام سخية.", + "workspace.lite.promo.price": "$10/شهر", "workspace.lite.promo.modelsTitle": "ما يتضمنه", "workspace.lite.promo.footer": "صُممت الخطة بشكل أساسي للمستخدمين الدوليين، وتوفر وصولًا عالميًا مستقرًا. قد تتغير الأسعار وحدود الاستخدام بينما نتعلم من الاستخدام المبكر والملاحظات.", diff --git a/packages/console/app/src/i18n/br.ts b/packages/console/app/src/i18n/br.ts index 6554f390ee7c..a2e5867a63dc 100644 --- a/packages/console/app/src/i18n/br.ts +++ b/packages/console/app/src/i18n/br.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash tem limites de uso 4x maiores por tempo limitado", + "go.graph.bonus": "{{count}}× mais uso", "nav.github": "GitHub", "nav.docs": "Documentação", "nav.changelog": "Changelog", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Cancelar", "common.creating": "Criando...", "common.create": "Criar", + "common.contactUs": "Contate-nos", "common.videoUnsupported": "Seu navegador não suporta a tag de vídeo.", "common.figure": "Fig {{n}}.", @@ -228,6 +231,9 @@ export const dict = { "zen.faq.q8": "Posso usar o Zen com outros agentes de codificação?", "zen.faq.a8": "Embora o Zen funcione muito bem com o OpenCode, você pode usar o Zen com qualquer agente. Siga as instruções de configuração no seu agente de codificação preferido.", + "zen.faq.q9": "Posso receber um reembolso?", + "zen.faq.a9": + "Você pode ter direito a um reembolso se a cobrança foi feita nos últimos 14 dias e você não usou os créditos dessa compra. {{contact}} para solicitar um reembolso.", "zen.cta.start": "Comece com o Zen", "zen.pricing.title": "Adicionar $20 de saldo pré-pago", @@ -256,9 +262,8 @@ export const dict = { "zen.privacy.exceptionsLink": "seguintes exceções", "go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos", - "go.banner.text": "Ox Alpha Free está disponível no Go por tempo limitado", "go.meta.description": - "O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de uso e acesso confiável aos principais modelos de codificação.", + "O Go custa $10/mês, com limites generosos de uso e acesso confiável aos principais modelos de codificação.", "go.hero.title": "Modelos de codificação de baixo custo para todos", "go.hero.body": "O Go traz a codificação com agentes para programadores em todo o mundo. Oferecendo limites generosos e acesso confiável aos modelos de código aberto mais capazes, para que você possa construir com agentes poderosos sem se preocupar com custos ou disponibilidade.", @@ -267,13 +272,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Assinar o Go", "go.cta.price": "$10/mês", - "go.cta.promo": "$5 no primeiro mês", "go.pricing.body": - "Use com qualquer agente. $5 no primeiro mês, depois $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.", + "Use com qualquer agente. $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.", "go.graph.free": "Grátis", "go.graph.freePill": "Big Pickle e modelos gratuitos", "go.graph.go": "Go", "go.graph.label": "Requisições por 5 horas", + "go.graph.period": "Uso", + "go.graph.model": "Modelo", + "go.graph.requests": "Requisições estimadas / 5 h", + "go.graph.allowance": "Uso mensal", + "go.graph.new": "Novo", + "go.graph.scale": "Escala não linear de requisições", + "go.graph.showAll": "Ver todos os {{count}} modelos", + "go.graph.showLess": "Mostrar menos modelos", "go.graph.limitedRegions": "regiões limitadas", "go.graph.limitedTime": "tempo limitado", "go.graph.usageLimits": "Limites de uso", @@ -303,21 +315,20 @@ export const dict = { "go.testimonials.frank.quote": "Eu queria ainda estar na Nvidia.", "go.problem.title": "Que problema o Go resolve?", "go.problem.body": - "Estamos focados em levar a experiência do OpenCode para o maior número de pessoas possível. OpenCode Go é uma assinatura de baixo custo: $5 no primeiro mês, depois $10/mês. Oferece limites generosos e acesso confiável aos modelos open source mais capazes.", + "Estamos focados em levar a experiência do OpenCode para o maior número de pessoas possível. OpenCode Go é uma assinatura de baixo custo de $10/mês. Oferece limites generosos e acesso confiável aos modelos open source mais capazes.", "go.problem.subtitle": " ", "go.problem.item1": "Preço de assinatura de baixo custo", "go.problem.item2": "Limites generosos e acesso confiável", "go.problem.item3": "Feito para o maior número possível de programadores", "go.problem.item4": "Uma seleção de modelos testados para codificação com agentes", "go.how.title": "Como o Go funciona", - "go.how.body": - "O Go começa em $5 no primeiro mês, depois $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.", + "go.how.body": "O Go custa $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.", "go.how.step1.title": "Crie uma conta", "go.how.step1.beforeLink": "siga as", "go.how.step1.link": "instruções de configuração", "go.how.step2.title": "Assinar o Go", - "go.how.step2.link": "$5 no primeiro mês", - "go.how.step2.afterLink": "depois $10/mês com limites generosos", + "go.how.step2.link": "$10/mês", + "go.how.step2.afterLink": "com limites generosos", "go.how.step3.title": "Comece a codificar", "go.how.step3.body": "com acesso confiável a modelos de código aberto", "go.privacy.title": "Sua privacidade é importante para nós", @@ -334,11 +345,11 @@ export const dict = { "go.faq.a2": "O Go inclui os modelos listados abaixo, com limites generosos e acesso confiável.", "go.faq.q3": "O Go é o mesmo que o Zen?", "go.faq.a3": - "Não. Zen é pay-as-you-go, enquanto o Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos e acesso confiável a uma seleção de modelos.", + "Não. Zen é pay-as-you-go, enquanto o Go custa $10/mês, com limites generosos e acesso confiável a uma seleção de modelos.", "go.faq.q4": "Quanto custa o Go?", "go.faq.a4.p1.beforePricing": "O Go custa", - "go.faq.a4.p1.pricingLink": "$5 no primeiro mês", - "go.faq.a4.p1.afterPricing": "depois $10/mês com limites generosos.", + "go.faq.a4.p1.pricingLink": "$10/mês", + "go.faq.a4.p1.afterPricing": "com limites generosos.", "go.faq.a4.p2.beforeAccount": "Você pode gerenciar sua assinatura em sua", "go.faq.a4.p2.accountLink": "conta", "go.faq.a4.p3": "Cancele a qualquer momento.", @@ -360,7 +371,7 @@ export const dict = { "Preços de tokens com grandes descontos em troca da permissão para usar seus prompts e respostas geradas para treinar futuros modelos da Meta.", "go.faq.a5.learnMore": "Saiba mais", "go.faq.a5.deepseekRetention": - "O acordo de ZDR é renovado mensalmente. O acordo atual é válido até 31 de agosto de 2026.", + "O acordo de ZDR é renovado mensalmente. O acordo atual é válido até 30 de setembro de 2026.", "go.faq.a5.beforeExceptions": "Os modelos Go são hospedados nos EUA. Os provedores seguem uma política de retenção zero e não usam seus dados para treinamento de modelos, com as", "go.faq.a5.exceptionsLink": "seguintes exceções", @@ -374,7 +385,10 @@ export const dict = { "go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?", "go.faq.a9": - "Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go oferece uma seleção de modelos com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a $12 por 5 horas, $30 por semana e $60 por mês (as contagens reais de requisições variam de acordo com o modelo e o uso).", + "Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go oferece uma seleção de modelos com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a cotas básicas de $12 por 5 horas, $30 por semana e $60 por mês; as cotas específicas podem variar por modelo (as contagens reais de requisições variam de acordo com o modelo e o uso).", + "go.faq.q10": "Posso receber um reembolso?", + "go.faq.a10": + "Você pode ter direito a um reembolso se a cobrança foi feita nos últimos 14 dias e você não usou sua cota do Go durante esse período de faturamento. {{contact}} para solicitar um reembolso.", "zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.", "zen.api.error.modelNotSupported": "Modelo {{model}} não suportado", @@ -675,10 +689,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "alguns segundos", "workspace.lite.subscription.message": "Você assina o OpenCode Go.", "workspace.lite.subscription.manage": "Gerenciar Assinatura", - "workspace.lite.subscription.rollingUsage": "Uso Contínuo", + "workspace.lite.subscription.rollingUsage": "Uso de 5 horas", + "workspace.lite.subscription.rollingQuota": "Cota de 5 horas", "workspace.lite.subscription.weeklyUsage": "Uso Semanal", + "workspace.lite.subscription.weeklyQuota": "Cota Semanal", "workspace.lite.subscription.monthlyUsage": "Uso Mensal", + "workspace.lite.subscription.monthlyQuota": "Cota Mensal", "workspace.lite.subscription.resetsIn": "Reinicia em", + "workspace.lite.subscription.showDetails": "Mostrar detalhes", + "workspace.lite.subscription.hideDetails": "Ocultar detalhes", + "workspace.lite.subscription.model": "Modelo", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Total", "workspace.lite.subscription.useBalance": "Use seu saldo disponível após atingir os limites de uso", "workspace.lite.subscription.selectProvider": 'Selecione "OpenCode Go" como provedor na sua configuração do opencode para usar os modelos Go.', @@ -691,8 +713,8 @@ export const dict = { "workspace.lite.other.message": "Outro membro neste workspace já assina o OpenCode Go. Apenas um membro por workspace pode assinar.", "workspace.lite.promo.description": - "O OpenCode Go começa em {{price}}, depois $10/mês, e oferece acesso confiável a modelos de codificação abertos populares com limites de uso generosos.", - "workspace.lite.promo.price": "$5 no primeiro mês", + "O OpenCode Go custa {{price}} e oferece acesso confiável a modelos de codificação abertos populares com limites de uso generosos.", + "workspace.lite.promo.price": "$10/mês", "workspace.lite.promo.modelsTitle": "O que está incluído", "workspace.lite.promo.footer": "O plano foi desenvolvido principalmente para usuários internacionais e oferece acesso global estável. Os preços e limites de uso podem mudar à medida que aprendemos com o uso inicial e o feedback recebido.", diff --git a/packages/console/app/src/i18n/da.ts b/packages/console/app/src/i18n/da.ts index 336f3b8276d9..d2787bb611eb 100644 --- a/packages/console/app/src/i18n/da.ts +++ b/packages/console/app/src/i18n/da.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash får firedoblet brugsgrænse i en begrænset periode", + "go.graph.bonus": "{{count}}× forbrug", "nav.github": "GitHub", "nav.docs": "Dokumentation", "nav.changelog": "Changelog", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Annuller", "common.creating": "Opretter...", "common.create": "Opret", + "common.contactUs": "Kontakt os", "common.videoUnsupported": "Din browser understøtter ikke video-tagget.", "common.figure": "Fig {{n}}.", @@ -226,6 +229,9 @@ export const dict = { "zen.faq.q8": "Kan jeg bruge Zen med andre kodningsagenter?", "zen.faq.a8": "Selvom Zen fungerer godt med OpenCode, kan du bruge Zen med enhver agent. Følg opsætningsinstruktionerne i din foretrukne kodningsagent.", + "zen.faq.q9": "Kan jeg få en refusion?", + "zen.faq.a9": + "Du kan muligvis få en refusion, hvis opkrævningen blev foretaget inden for de seneste 14 dage, og du ikke har brugt kreditten fra dette køb. {{contact}} for at anmode om en refusion.", "zen.cta.start": "Kom godt i gang med Zen", "zen.pricing.title": "Tilføj $20 Pay as you go-saldo", @@ -254,9 +260,8 @@ export const dict = { "zen.privacy.exceptionsLink": "følgende undtagelser", "go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle", - "go.banner.text": "Ox Alpha Free er tilgængelig på Go i en begrænset periode", "go.meta.description": - "Go starter ved $5 for den første måned, derefter $10/måned, med generøse brugsgrænser og pålidelig adgang til førende kodningsmodeller.", + "Go koster $10/måned, med generøse brugsgrænser og pålidelig adgang til førende kodningsmodeller.", "go.hero.title": "Kodningsmodeller til lav pris for alle", "go.hero.body": "Go bringer agentisk kodning til programmører over hele verden. Med generøse grænser og pålidelig adgang til de mest kapable open source-modeller, så du kan bygge med kraftfulde agenter uden at bekymre dig om omkostninger eller tilgængelighed.", @@ -265,16 +270,22 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Abonner på Go", "go.cta.price": "$10/måned", - "go.cta.promo": "$5 første måned", - "go.pricing.body": - "Brug med enhver agent. $5 første måned, derefter $10/måned. Tank op med kredit efter behov. Afmeld når som helst.", + "go.pricing.body": "Brug med enhver agent. $10/måned. Tank op med kredit efter behov. Afmeld når som helst.", "go.graph.free": "Gratis", "go.graph.freePill": "Big Pickle og gratis modeller", "go.graph.go": "Go", "go.graph.label": "Forespørgsler pr. 5 timer", + "go.graph.period": "Forbrug", + "go.graph.model": "Model", + "go.graph.requests": "Estimerede anmodninger / 5 timer", + "go.graph.allowance": "Månedligt forbrug", + "go.graph.new": "Ny", + "go.graph.scale": "Ikke-lineær skala for anmodninger", + "go.graph.showAll": "Vis alle {{count}} modeller", + "go.graph.showLess": "Vis færre modeller", "go.graph.limitedRegions": "begrænsede regioner", "go.graph.limitedTime": "begrænset periode", - "go.graph.usageLimits": "Brugsgrænser", + "go.graph.usageLimits": "Forbrugsgrænser", "go.graph.aria": "Forespørgsler pr. 5t: {{free}} vs {{go}}", "go.testimonials.brand.zen": "Zen", @@ -300,21 +311,20 @@ export const dict = { "go.testimonials.frank.quote": "Jeg ville ønske, jeg stadig var hos Nvidia.", "go.problem.title": "Hvilket problem løser Go?", "go.problem.body": - "Vi fokuserer på at bringe OpenCode-oplevelsen ud til så mange som muligt. OpenCode Go er et lavprisabonnement: $5 for den første måned, derefter $10/måned. Det giver generøse grænser og pålidelig adgang til de mest kapable open source-modeller.", + "Vi fokuserer på at bringe OpenCode-oplevelsen ud til så mange som muligt. OpenCode Go er et lavprisabonnement til $10/måned. Det giver generøse grænser og pålidelig adgang til de mest kapable open source-modeller.", "go.problem.subtitle": " ", "go.problem.item1": "Lavpris abonnementspriser", "go.problem.item2": "Generøse grænser og pålidelig adgang", "go.problem.item3": "Bygget til så mange programmører som muligt", "go.problem.item4": "Et kurateret modeludvalg testet til agentisk kodning", "go.how.title": "Hvordan Go virker", - "go.how.body": - "Go starter ved $5 for den første måned, derefter $10/måned. Du kan bruge det med OpenCode eller enhver agent.", + "go.how.body": "Go koster $10/måned. Du kan bruge det med OpenCode eller enhver agent.", "go.how.step1.title": "Opret en konto", "go.how.step1.beforeLink": "følg", "go.how.step1.link": "opsætningsinstruktionerne", "go.how.step2.title": "Abonner på Go", - "go.how.step2.link": "$5 første måned", - "go.how.step2.afterLink": "derefter $10/måned med generøse grænser", + "go.how.step2.link": "$10/måned", + "go.how.step2.afterLink": "med generøse grænser", "go.how.step3.title": "Start kodning", "go.how.step3.body": "med pålidelig adgang til open source-modeller", "go.privacy.title": "Dit privatliv er vigtigt for os", @@ -331,11 +341,11 @@ export const dict = { "go.faq.a2": "Go inkluderer modellerne nedenfor med generøse grænser og pålidelig adgang.", "go.faq.q3": "Er Go det samme som Zen?", "go.faq.a3": - "Nej. Zen er pay-as-you-go, mens Go starter ved $5 for den første måned, derefter $10/måned, med generøse grænser og pålidelig adgang til et kurateret modeludvalg.", + "Nej. Zen er pay-as-you-go, mens Go koster $10/måned, med generøse grænser og pålidelig adgang til et kurateret modeludvalg.", "go.faq.q4": "Hvad koster Go?", "go.faq.a4.p1.beforePricing": "Go koster", - "go.faq.a4.p1.pricingLink": "$5 første måned", - "go.faq.a4.p1.afterPricing": "derefter $10/måned med generøse grænser.", + "go.faq.a4.p1.pricingLink": "$10/måned", + "go.faq.a4.p1.afterPricing": "med generøse grænser.", "go.faq.a4.p2.beforeAccount": "Du kan administrere dit abonnement i din", "go.faq.a4.p2.accountLink": "konto", "go.faq.a4.p3": "Annuller til enhver tid.", @@ -357,7 +367,7 @@ export const dict = { "Kraftigt nedsatte tokenpriser til gengæld for tilladelse til at bruge dine prompts og modelsvar til at træne fremtidige Meta-modeller.", "go.faq.a5.learnMore": "Læs mere", "go.faq.a5.deepseekRetention": - "ZDR-aftalen fornyes månedligt. Den nuværende aftale er gyldig til og med 31. august 2026.", + "ZDR-aftalen fornyes månedligt. Den nuværende aftale er gyldig til og med 30. september 2026.", "go.faq.a5.beforeExceptions": "Go-modeller hostes i USA. Udbydere følger en nulopbevaringspolitik og bruger ikke dine data til modeltræning, med de", @@ -371,7 +381,10 @@ export const dict = { "go.faq.q9": "Hvad er forskellen på gratis modeller og Go?", "go.faq.a9": - "Gratis modeller inkluderer Big Pickle plus kampagnemodeller, der er tilgængelige på det pågældende tidspunkt, med en kvote på 200 forespørgsler/dag. Go tilbyder et kurateret modeludvalg med højere forespørgselskvoter håndhævet over rullende perioder (5 timer, ugentligt og månedligt), omtrent svarende til $12 pr. 5 timer, $30 pr. uge og $60 pr. måned (det faktiske antal forespørgsler varierer efter model og brug).", + "Gratis modeller inkluderer Big Pickle plus kampagnemodeller, der er tilgængelige på det pågældende tidspunkt, med en kvote på 200 forespørgsler/dag. Go tilbyder et kurateret modeludvalg med højere forespørgselskvoter håndhævet over rullende perioder (5 timer, ugentligt og månedligt), omtrent svarende til basiskvoter på $12 pr. 5 timer, $30 pr. uge og $60 pr. måned; modelspecifikke kvoter kan variere (det faktiske antal forespørgsler varierer efter model og brug).", + "go.faq.q10": "Kan jeg få en refusion?", + "go.faq.a10": + "Du kan muligvis få en refusion, hvis opkrævningen blev foretaget inden for de seneste 14 dage, og du ikke har brugt din Go-kvote i den pågældende faktureringsperiode. {{contact}} for at anmode om en refusion.", "zen.api.error.rateLimitExceeded": "Hastighedsgrænse overskredet. Prøv venligst igen senere.", "zen.api.error.modelNotSupported": "Model {{model}} understøttes ikke", @@ -672,10 +685,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "et par sekunder", "workspace.lite.subscription.message": "Du abonnerer på OpenCode Go.", "workspace.lite.subscription.manage": "Administrer abonnement", - "workspace.lite.subscription.rollingUsage": "Løbende forbrug", + "workspace.lite.subscription.rollingUsage": "5-timers forbrug", + "workspace.lite.subscription.rollingQuota": "5-timers kvote", "workspace.lite.subscription.weeklyUsage": "Ugentligt forbrug", + "workspace.lite.subscription.weeklyQuota": "Ugentlig kvote", "workspace.lite.subscription.monthlyUsage": "Månedligt forbrug", + "workspace.lite.subscription.monthlyQuota": "Månedlig kvote", "workspace.lite.subscription.resetsIn": "Nulstiller i", + "workspace.lite.subscription.showDetails": "Vis detaljer", + "workspace.lite.subscription.hideDetails": "Skjul detaljer", + "workspace.lite.subscription.model": "Model", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "I alt", "workspace.lite.subscription.useBalance": "Brug din tilgængelige saldo, når du har nået forbrugsgrænserne", "workspace.lite.subscription.selectProvider": 'Vælg "OpenCode Go" som udbyder i din opencode-konfiguration for at bruge Go-modeller.', @@ -688,8 +709,8 @@ export const dict = { "workspace.lite.other.message": "Et andet medlem i dette workspace abonnerer allerede på OpenCode Go. Kun ét medlem pr. workspace kan abonnere.", "workspace.lite.promo.description": - "OpenCode Go starter ved {{price}}, derefter $10/måned, og giver pålidelig adgang til populære åbne kodningsmodeller med generøse brugsgrænser.", - "workspace.lite.promo.price": "$5 for den første måned", + "OpenCode Go koster {{price}} og giver pålidelig adgang til populære åbne kodningsmodeller med generøse brugsgrænser.", + "workspace.lite.promo.price": "$10/måned", "workspace.lite.promo.modelsTitle": "Hvad er inkluderet", "workspace.lite.promo.footer": "Planen er primært udviklet til internationale brugere og giver stabil adgang i hele verden. Priser og forbrugsgrænser kan ændre sig, efterhånden som vi lærer af de første brugserfaringer og tilbagemeldinger.", diff --git a/packages/console/app/src/i18n/de.ts b/packages/console/app/src/i18n/de.ts index 5f1218caa6ee..8fb78e76e611 100644 --- a/packages/console/app/src/i18n/de.ts +++ b/packages/console/app/src/i18n/de.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash erhält für begrenzte Zeit 4x Nutzungslimits", + "go.graph.bonus": "{{count}}× Nutzung", "nav.github": "GitHub", "nav.docs": "Dokumentation", "nav.changelog": "Changelog", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Abbrechen", "common.creating": "Erstelle...", "common.create": "Erstellen", + "common.contactUs": "Kontaktiere uns", "common.videoUnsupported": "Dein Browser unterstützt das Video-Tag nicht.", "common.figure": "Abb. {{n}}.", @@ -228,6 +231,9 @@ export const dict = { "zen.faq.q8": "Kann ich Zen mit anderen Coding-Agents nutzen?", "zen.faq.a8": "Während Zen großartig mit OpenCode funktioniert, kannst du Zen mit jedem Agent nutzen. Folge den Einrichtungsanweisungen in deinem bevorzugten Coding-Agent.", + "zen.faq.q9": "Kann ich eine Rückerstattung erhalten?", + "zen.faq.a9": + "Du hast möglicherweise Anspruch auf eine Rückerstattung, wenn die Zahlung innerhalb der letzten 14 Tage erfolgt ist und du das Guthaben aus diesem Kauf nicht verwendet hast. {{contact}}, um eine Rückerstattung anzufordern.", "zen.cta.start": "Starte mit Zen", "zen.pricing.title": "Füge $20 Pay-as-you-go Guthaben hinzu", @@ -256,9 +262,8 @@ export const dict = { "zen.privacy.exceptionsLink": "folgenden Ausnahmen", "go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle", - "go.banner.text": "Ox Alpha Free ist für begrenzte Zeit auf Go verfügbar", "go.meta.description": - "Go beginnt bei $5 für deinen ersten Monat, danach $10/Monat, mit großzügigen Nutzungslimits und zuverlässigem Zugang zu führenden Coding-Modellen.", + "Go kostet $10/Monat, mit großzügigen Nutzungslimits und zuverlässigem Zugang zu führenden Coding-Modellen.", "go.hero.title": "Kostengünstige Coding-Modelle für alle", "go.hero.body": "Go bringt Agentic Coding zu Programmierern auf der ganzen Welt. Mit großzügigen Limits und zuverlässigem Zugang zu den leistungsfähigsten Open-Source-Modellen, damit du mit leistungsstarken Agenten entwickeln kannst, ohne dir Gedanken über Kosten oder Verfügbarkeit zu machen.", @@ -267,13 +272,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Go abonnieren", "go.cta.price": "$10/Monat", - "go.cta.promo": "$5 im ersten Monat", - "go.pricing.body": - "Mit jedem Agenten nutzbar. $5 im ersten Monat, danach $10/Monat. Guthaben bei Bedarf aufladen. Jederzeit kündbar.", + "go.pricing.body": "Mit jedem Agenten nutzbar. $10/Monat. Guthaben bei Bedarf aufladen. Jederzeit kündbar.", "go.graph.free": "Kostenlos", "go.graph.freePill": "Big Pickle und kostenlose Modelle", "go.graph.go": "Go", "go.graph.label": "Anfragen pro 5 Stunden", + "go.graph.period": "Nutzung", + "go.graph.model": "Modell", + "go.graph.requests": "Geschätzte Anfragen / 5 Std.", + "go.graph.allowance": "Monatliche Nutzung", + "go.graph.new": "Neu", + "go.graph.scale": "Nichtlineare Skala für Anfragen", + "go.graph.showAll": "Alle {{count}} Modelle anzeigen", + "go.graph.showLess": "Weniger Modelle anzeigen", "go.graph.limitedRegions": "begrenzte Regionen", "go.graph.limitedTime": "begrenzte Zeit", "go.graph.usageLimits": "Nutzungslimits", @@ -302,21 +313,20 @@ export const dict = { "go.testimonials.frank.quote": "Ich wünschte, ich wäre noch bei Nvidia.", "go.problem.title": "Welches Problem löst Go?", "go.problem.body": - "Wir konzentrieren uns darauf, die OpenCode-Erfahrung so vielen Menschen wie möglich zugänglich zu machen. OpenCode Go ist ein kostengünstiges Abonnement: $5 im ersten Monat, danach $10/Monat. Es bietet großzügige Limits und zuverlässigen Zugang zu den leistungsfähigsten Open-Source-Modellen.", + "Wir konzentrieren uns darauf, die OpenCode-Erfahrung so vielen Menschen wie möglich zugänglich zu machen. OpenCode Go ist ein kostengünstiges Abonnement für $10/Monat. Es bietet großzügige Limits und zuverlässigen Zugang zu den leistungsfähigsten Open-Source-Modellen.", "go.problem.subtitle": " ", "go.problem.item1": "Kostengünstiges Abonnement", "go.problem.item2": "Großzügige Limits und zuverlässiger Zugang", "go.problem.item3": "Für so viele Programmierer wie möglich gebaut", "go.problem.item4": "Eine kuratierte, für Agentic Coding getestete Modellauswahl", "go.how.title": "Wie Go funktioniert", - "go.how.body": - "Go beginnt bei $5 für den ersten Monat, danach $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.", + "go.how.body": "Go kostet $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.", "go.how.step1.title": "Konto erstellen", "go.how.step1.beforeLink": "folge den", "go.how.step1.link": "Einrichtungsanweisungen", "go.how.step2.title": "Go abonnieren", - "go.how.step2.link": "$5 im ersten Monat", - "go.how.step2.afterLink": "danach $10/Monat mit großzügigen Limits", + "go.how.step2.link": "$10/Monat", + "go.how.step2.afterLink": "mit großzügigen Limits", "go.how.step3.title": "Loslegen mit Coding", "go.how.step3.body": "mit zuverlässigem Zugang zu Open-Source-Modellen", "go.privacy.title": "Deine Privatsphäre ist uns wichtig", @@ -333,11 +343,11 @@ export const dict = { "go.faq.a2": "Go umfasst die unten aufgeführten Modelle mit großzügigen Limits und zuverlässigem Zugriff.", "go.faq.q3": "Ist Go dasselbe wie Zen?", "go.faq.a3": - "Nein. Zen ist Pay-as-you-go, während Go bei $5 für deinen ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu einer kuratierten Modellauswahl.", + "Nein. Zen ist Pay-as-you-go, während Go $10/Monat kostet, mit großzügigen Limits und zuverlässigem Zugang zu einer kuratierten Modellauswahl.", "go.faq.q4": "Wie viel kostet Go?", "go.faq.a4.p1.beforePricing": "Go kostet", - "go.faq.a4.p1.pricingLink": "$5 im ersten Monat", - "go.faq.a4.p1.afterPricing": "danach $10/Monat mit großzügigen Limits.", + "go.faq.a4.p1.pricingLink": "$10/Monat", + "go.faq.a4.p1.afterPricing": "mit großzügigen Limits.", "go.faq.a4.p2.beforeAccount": "Du kannst dein Abonnement in deinem", "go.faq.a4.p2.accountLink": "Konto verwalten", "go.faq.a4.p3": "Jederzeit kündbar.", @@ -359,7 +369,7 @@ export const dict = { "Stark vergünstigte Tokenpreise im Gegenzug für die Erlaubnis, deine Prompts und Vervollständigungen zum Trainieren zukünftiger Meta-Modelle zu verwenden.", "go.faq.a5.learnMore": "Mehr erfahren", "go.faq.a5.deepseekRetention": - "Die ZDR-Vereinbarung wird monatlich erneuert. Die aktuelle Vereinbarung gilt bis einschließlich 31. August 2026.", + "Die ZDR-Vereinbarung wird monatlich erneuert. Die aktuelle Vereinbarung gilt bis einschließlich 30. September 2026.", "go.faq.a5.beforeExceptions": "Go-Modelle werden in den USA gehostet. Anbieter verfolgen eine Zero-Retention-Politik und nutzen deine Daten nicht für das Training von Modellen, mit den", "go.faq.a5.exceptionsLink": "folgenden Ausnahmen", @@ -373,7 +383,10 @@ export const dict = { "go.faq.q9": "Was ist der Unterschied zwischen kostenlosen Modellen und Go?", "go.faq.a9": - "Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go bietet eine kuratierte Modellauswahl mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).", + "Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go bietet eine kuratierte Modellauswahl mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu Basiskontingenten von $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat; modellspezifische Kontingente können abweichen (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).", + "go.faq.q10": "Kann ich eine Rückerstattung erhalten?", + "go.faq.a10": + "Du hast möglicherweise Anspruch auf eine Rückerstattung, wenn die Zahlung innerhalb der letzten 14 Tage erfolgt ist und du dein Go-Kontingent in diesem Abrechnungszeitraum nicht genutzt hast. {{contact}}, um eine Rückerstattung anzufordern.", "zen.api.error.rateLimitExceeded": "Ratenlimit überschritten. Bitte versuche es später erneut.", "zen.api.error.modelNotSupported": "Modell {{model}} wird nicht unterstützt", @@ -674,10 +687,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "einige Sekunden", "workspace.lite.subscription.message": "Du hast OpenCode Go abonniert.", "workspace.lite.subscription.manage": "Abo verwalten", - "workspace.lite.subscription.rollingUsage": "Fortlaufende Nutzung", + "workspace.lite.subscription.rollingUsage": "5-Stunden-Nutzung", + "workspace.lite.subscription.rollingQuota": "5-Stunden-Kontingent", "workspace.lite.subscription.weeklyUsage": "Wöchentliche Nutzung", + "workspace.lite.subscription.weeklyQuota": "Wöchentliches Kontingent", "workspace.lite.subscription.monthlyUsage": "Monatliche Nutzung", + "workspace.lite.subscription.monthlyQuota": "Monatliches Kontingent", "workspace.lite.subscription.resetsIn": "Setzt zurück in", + "workspace.lite.subscription.showDetails": "Details anzeigen", + "workspace.lite.subscription.hideDetails": "Details ausblenden", + "workspace.lite.subscription.model": "Modell", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Gesamt", "workspace.lite.subscription.useBalance": "Nutze dein verfügbares Guthaben, nachdem die Nutzungslimits erreicht sind", "workspace.lite.subscription.selectProvider": 'Wähle "OpenCode Go" als Anbieter in deiner opencode-Konfiguration, um Go-Modelle zu verwenden.', @@ -690,8 +711,8 @@ export const dict = { "workspace.lite.other.message": "Ein anderes Mitglied in diesem Workspace hat OpenCode Go bereits abonniert. Nur ein Mitglied pro Workspace kann abonnieren.", "workspace.lite.promo.description": - "OpenCode Go startet bei {{price}}, danach $10/Monat, und bietet zuverlässigen Zugang zu beliebten offenen Coding-Modellen mit großzügigen Nutzungslimits.", - "workspace.lite.promo.price": "$5 im ersten Monat", + "OpenCode Go kostet {{price}} und bietet zuverlässigen Zugang zu beliebten offenen Coding-Modellen mit großzügigen Nutzungslimits.", + "workspace.lite.promo.price": "$10/Monat", "workspace.lite.promo.modelsTitle": "Was enthalten ist", "workspace.lite.promo.footer": "Der Plan richtet sich in erster Linie an internationale Nutzer und bietet stabilen weltweiten Zugriff. Preise und Nutzungslimits können sich ändern, wenn wir Erkenntnisse aus der ersten Nutzung und dem Feedback gewinnen.", diff --git a/packages/console/app/src/i18n/en.ts b/packages/console/app/src/i18n/en.ts index 9b57bb875034..4469c3353c51 100644 --- a/packages/console/app/src/i18n/en.ts +++ b/packages/console/app/src/i18n/en.ts @@ -1,4 +1,6 @@ export const dict = { + "go.promo.deepseek": "DeepSeek V4.1 Flash gets 4× usage limits for a limited time", + "go.graph.bonus": "{{count}}× usage", "nav.github": "GitHub", "nav.docs": "Docs", "nav.data": "Data", @@ -225,6 +227,9 @@ export const dict = { "zen.faq.q8": "Can I use Zen with other coding agents?", "zen.faq.a8": "While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in your preferred coding agent.", + "zen.faq.q9": "Can I get a refund?", + "zen.faq.a9": + "You may qualify for a refund if the charge was made within the last 14 days and you have not used the credits from that purchase. {{contact}} to request a refund.", "zen.cta.start": "Get started with Zen", "zen.pricing.title": "Add $20 Pay as you go balance", @@ -253,9 +258,7 @@ export const dict = { "zen.privacy.exceptionsLink": "following exceptions", "go.title": "OpenCode Go | Low cost coding models for everyone", - "go.banner.text": "Ox Alpha Free is available on Go for a limited time", - "go.meta.description": - "Go starts at $5 for your first month, then $10/month, with generous usage limits and reliable access to leading coding models.", + "go.meta.description": "Go costs $10/month, with generous usage limits and reliable access to leading coding models.", "go.hero.title": "Low cost coding models for everyone", "go.hero.body": "Go brings agentic coding to programmers around the world. Offering generous limits and reliable access to the most capable open-source models, so you can build with powerful agents without worrying about cost or availability.", @@ -264,12 +267,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Subscribe to Go", "go.cta.price": "$10/month", - "go.cta.promo": "$5 first month", - "go.pricing.body": "Use with any agent. $5 first month, then $10/month. Top up credit if needed. Cancel any time.", + "go.pricing.body": "Use with any agent. $10/month. Top up credit if needed. Cancel any time.", "go.graph.free": "Free", "go.graph.freePill": "Big Pickle and free models", "go.graph.go": "Go", - "go.graph.label": "Requests per 5 hour", + "go.graph.label": "Requests / 5 hours", + "go.graph.period": "Usage", + "go.graph.model": "Model", + "go.graph.requests": "Est. requests / 5 hr", + "go.graph.allowance": "Monthly usage", + "go.graph.new": "New", + "go.graph.scale": "Nonlinear request scale", + "go.graph.showAll": "View all {{count}} models", + "go.graph.showLess": "Show fewer models", "go.graph.limitedRegions": "limited regions", "go.graph.limitedTime": "limited time", "go.graph.tick": "{{n}}x", @@ -299,20 +309,20 @@ export const dict = { "go.testimonials.frank.quote": "I wish I was still at Nvidia.", "go.problem.title": "What problem is Go solving?", "go.problem.body": - "We're focused on bringing the OpenCode experience to as many people as possible. OpenCode Go is a low cost subscription: $5 for your first month, then $10/month. It provides generous limits and reliable access to the most capable open source models.", + "We're focused on bringing the OpenCode experience to as many people as possible. OpenCode Go is a low cost $10/month subscription. It provides generous limits and reliable access to the most capable open source models.", "go.problem.subtitle": " ", "go.problem.item1": "Low cost subscription pricing", "go.problem.item2": "Generous limits and reliable access", "go.problem.item3": "Built for as many programmers as possible", "go.problem.item4": "A curated model lineup tested for agentic coding", "go.how.title": "How Go works", - "go.how.body": "Go starts at $5 for your first month, then $10/month. You can use it with OpenCode or any agent.", + "go.how.body": "Go costs $10/month. You can use it with OpenCode or any agent.", "go.how.step1.title": "Create an account", "go.how.step1.beforeLink": "follow the", "go.how.step1.link": "setup instructions", "go.how.step2.title": "Subscribe to Go", - "go.how.step2.link": "$5 first month", - "go.how.step2.afterLink": "then $10/month with generous limits", + "go.how.step2.link": "$10/month", + "go.how.step2.afterLink": "with generous limits", "go.how.step3.title": "Start coding", "go.how.step3.body": "with reliable access to open-source models", "go.privacy.title": "Your privacy is important to us", @@ -329,11 +339,11 @@ export const dict = { "go.faq.a2": "Go includes the models listed below, with generous limits and reliable access.", "go.faq.q3": "Is Go the same as Zen?", "go.faq.a3": - "No. Zen is pay-as-you-go, while Go starts at $5 for your first month, then $10/month, with generous limits and reliable access to a curated model lineup.", + "No. Zen is pay-as-you-go, while Go costs $10/month, with generous limits and reliable access to a curated model lineup.", "go.faq.q4": "How much does Go cost?", "go.faq.a4.p1.beforePricing": "Go costs", - "go.faq.a4.p1.pricingLink": "$5 first month", - "go.faq.a4.p1.afterPricing": "then $10/month with generous limits.", + "go.faq.a4.p1.pricingLink": "$10/month", + "go.faq.a4.p1.afterPricing": "with generous limits.", "go.faq.a4.p2.beforeAccount": "You can manage your subscription in your", "go.faq.a4.p2.accountLink": "account", "go.faq.a4.p3": "Cancel any time.", @@ -354,7 +364,7 @@ export const dict = { "go.faq.a5.museRetention": "Heavily discounted token pricing in exchange for permission to use your prompts and completions to train future Meta models.", "go.faq.a5.deepseekRetention": - "ZDR agreement is renewed monthly. The current agreement is valid through August 31, 2026.", + "ZDR agreement is renewed monthly. The current agreement is valid through September 30, 2026.", "go.faq.a5.learnMore": "Learn more", "go.faq.a5.beforeExceptions": @@ -369,7 +379,10 @@ export const dict = { "go.faq.q9": "What is the difference between free models and Go?", "go.faq.a9": - "Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go offers a curated model lineup 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 offers a curated model lineup with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to base allowances of $12 per 5 hours, $30 per week, and $60 per month; model-specific allowances may differ (actual request counts vary by model and usage).", + "go.faq.q10": "Can I get a refund?", + "go.faq.a10": + "You may qualify for a refund if the charge was made within the last 14 days and you have not used your Go allowance during that billing period. {{contact}} to request a refund.", "zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.", "zen.api.error.modelNotSupported": "Model {{model}} is not supported", @@ -671,10 +684,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "a few seconds", "workspace.lite.subscription.message": "You are subscribed to OpenCode Go.", "workspace.lite.subscription.manage": "Manage Subscription", - "workspace.lite.subscription.rollingUsage": "Rolling Usage", + "workspace.lite.subscription.rollingUsage": "5-hour Usage", + "workspace.lite.subscription.rollingQuota": "5-hour Quota", "workspace.lite.subscription.weeklyUsage": "Weekly Usage", + "workspace.lite.subscription.weeklyQuota": "Weekly Quota", "workspace.lite.subscription.monthlyUsage": "Monthly Usage", + "workspace.lite.subscription.monthlyQuota": "Monthly Quota", "workspace.lite.subscription.resetsIn": "Resets in", + "workspace.lite.subscription.showDetails": "Show details", + "workspace.lite.subscription.hideDetails": "Hide details", + "workspace.lite.subscription.model": "Model", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Total", "workspace.lite.subscription.useBalance": "Use your available balance after reaching the usage limits", "workspace.lite.subscription.selectProvider": 'Select "OpenCode Go" as the provider in your opencode configuration to use Go models.', @@ -687,8 +708,8 @@ export const dict = { "workspace.lite.other.message": "Another member in this workspace is already subscribed to OpenCode Go. Only one member per workspace can subscribe.", "workspace.lite.promo.description": - "OpenCode Go starts at {{price}}, then $10/month, and provides reliable access to popular open coding models with generous usage limits.", - "workspace.lite.promo.price": "$5 for your first month", + "OpenCode Go costs {{price}} and provides reliable access to popular open coding models with generous usage limits.", + "workspace.lite.promo.price": "$10/month", "workspace.lite.promo.modelsTitle": "What's Included", "workspace.lite.promo.footer": "The plan is designed primarily for international users and provides stable global access. Pricing and usage limits may change as we learn from early usage and feedback.", diff --git a/packages/console/app/src/i18n/es.ts b/packages/console/app/src/i18n/es.ts index da0edf3e846b..9750bc584e27 100644 --- a/packages/console/app/src/i18n/es.ts +++ b/packages/console/app/src/i18n/es.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash tiene límites de uso 4x mayores por tiempo limitado", + "go.graph.bonus": "{{count}}× de uso", "nav.github": "GitHub", "nav.docs": "Documentación", "nav.changelog": "Registro de cambios", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Cancelar", "common.creating": "Creando...", "common.create": "Crear", + "common.contactUs": "Contáctanos", "common.videoUnsupported": "Tu navegador no soporta la etiqueta de video.", "common.figure": "Fig {{n}}.", @@ -229,6 +232,9 @@ export const dict = { "zen.faq.q8": "¿Puedo usar Zen con otros agentes de codificación?", "zen.faq.a8": "Aunque Zen funciona genial con OpenCode, puedes usar Zen con cualquier agente. Sigue las instrucciones de configuración en tu agente de codificación preferido.", + "zen.faq.q9": "¿Puedo obtener un reembolso?", + "zen.faq.a9": + "Podrías tener derecho a un reembolso si el cargo se realizó en los últimos 14 días y no has utilizado el crédito de esa compra. {{contact}} para solicitar un reembolso.", "zen.cta.start": "Empieza con Zen", "zen.pricing.title": "Añade $20 de saldo prepago", @@ -257,9 +263,8 @@ export const dict = { "zen.privacy.exceptionsLink": "siguientes excepciones", "go.title": "OpenCode Go | Modelos de programación de bajo coste para todos", - "go.banner.text": "Ox Alpha Free está disponible en Go por tiempo limitado", "go.meta.description": - "Go comienza en $5 el primer mes, luego 10 $/mes, con límites de uso generosos y acceso fiable a modelos de programación líderes.", + "Go cuesta 10 $/mes, con límites de uso generosos y acceso fiable a modelos de programación líderes.", "go.hero.title": "Modelos de programación de bajo coste para todos", "go.hero.body": "Go lleva la programación agéntica a programadores de todo el mundo. Ofrece límites generosos y acceso fiable a los modelos de código abierto más capaces, para que puedas crear con agentes potentes sin preocuparte por el coste o la disponibilidad.", @@ -268,13 +273,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Suscribirse a Go", "go.cta.price": "10 $/mes", - "go.cta.promo": "$5 el primer mes", "go.pricing.body": - "Úsalo con cualquier agente. $5 el primer mes, luego 10 $/mes. Recarga crédito si es necesario. Cancela en cualquier momento.", + "Úsalo con cualquier agente. 10 $/mes. Recarga crédito si es necesario. Cancela en cualquier momento.", "go.graph.free": "Gratis", "go.graph.freePill": "Big Pickle y modelos gratuitos", "go.graph.go": "Go", "go.graph.label": "Solicitudes por 5 horas", + "go.graph.period": "Uso", + "go.graph.model": "Modelo", + "go.graph.requests": "Peticiones estimadas / 5 h", + "go.graph.allowance": "Uso mensual", + "go.graph.new": "Nuevo", + "go.graph.scale": "Escala no lineal de peticiones", + "go.graph.showAll": "Ver los {{count}} modelos", + "go.graph.showLess": "Mostrar menos modelos", "go.graph.limitedRegions": "regiones limitadas", "go.graph.limitedTime": "tiempo limitado", "go.graph.usageLimits": "Límites de uso", @@ -304,20 +316,20 @@ export const dict = { "go.testimonials.frank.quote": "Ojalá siguiera en Nvidia.", "go.problem.title": "¿Qué problema resuelve Go?", "go.problem.body": - "Nos enfocamos en llevar la experiencia de OpenCode a tantas personas como sea posible. OpenCode Go es una suscripción de bajo coste: $5 el primer mes, luego 10 $/mes. Proporciona límites generosos y acceso fiable a los modelos de código abierto más capaces.", + "Nos enfocamos en llevar la experiencia de OpenCode a tantas personas como sea posible. OpenCode Go es una suscripción de bajo coste de 10 $/mes. Proporciona límites generosos y acceso fiable a los modelos de código abierto más capaces.", "go.problem.subtitle": " ", "go.problem.item1": "Precios de suscripción de bajo coste", "go.problem.item2": "Límites generosos y acceso fiable", "go.problem.item3": "Creado para tantos programadores como sea posible", "go.problem.item4": "Una selección de modelos probados para programación agéntica", "go.how.title": "Cómo funciona Go", - "go.how.body": "Go comienza en $5 el primer mes, luego 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.", + "go.how.body": "Go cuesta 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.", "go.how.step1.title": "Crear una cuenta", "go.how.step1.beforeLink": "sigue las", "go.how.step1.link": "instrucciones de configuración", "go.how.step2.title": "Suscribirse a Go", - "go.how.step2.link": "$5 el primer mes", - "go.how.step2.afterLink": "luego 10 $/mes con límites generosos", + "go.how.step2.link": "10 $/mes", + "go.how.step2.afterLink": "con límites generosos", "go.how.step3.title": "Empezar a programar", "go.how.step3.body": "con acceso fiable a modelos de código abierto", "go.privacy.title": "Tu privacidad es importante para nosotros", @@ -334,11 +346,11 @@ export const dict = { "go.faq.a2": "Go incluye los modelos que se indican abajo, con límites generosos y acceso confiable.", "go.faq.q3": "¿Es Go lo mismo que Zen?", "go.faq.a3": - "No. Zen es de pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a una selección de modelos.", + "No. Zen es de pago por uso, mientras que Go cuesta 10 $/mes, con límites generosos y acceso fiable a una selección de modelos.", "go.faq.q4": "¿Cuánto cuesta Go?", "go.faq.a4.p1.beforePricing": "Go cuesta", - "go.faq.a4.p1.pricingLink": "$5 el primer mes", - "go.faq.a4.p1.afterPricing": "luego 10 $/mes con límites generosos.", + "go.faq.a4.p1.pricingLink": "10 $/mes", + "go.faq.a4.p1.afterPricing": "con límites generosos.", "go.faq.a4.p2.beforeAccount": "Puedes gestionar tu suscripción en tu", "go.faq.a4.p2.accountLink": "cuenta", "go.faq.a4.p3": "Cancela en cualquier momento.", @@ -360,7 +372,7 @@ export const dict = { "Precios de tokens muy reducidos a cambio de permitir que tus prompts y las respuestas generadas se utilicen para entrenar futuros modelos de Meta.", "go.faq.a5.learnMore": "Más información", "go.faq.a5.deepseekRetention": - "El acuerdo de ZDR se renueva mensualmente. El acuerdo actual es válido hasta el 31 de agosto de 2026.", + "El acuerdo de ZDR se renueva mensualmente. El acuerdo actual es válido hasta el 30 de septiembre de 2026.", "go.faq.a5.beforeExceptions": "Los modelos de Go están alojados en EE. UU. Los proveedores siguen una política de retención cero y no utilizan tus datos para el entrenamiento de modelos, con las", "go.faq.a5.exceptionsLink": "siguientes excepciones", @@ -374,7 +386,10 @@ export const dict = { "go.faq.q9": "¿Cuál es la diferencia entre los modelos gratuitos y Go?", "go.faq.a9": - "Los modelos gratuitos incluyen Big Pickle y los modelos promocionales disponibles en ese momento, con una cuota de 200 solicitudes/día. Go ofrece una selección de modelos con cuotas de solicitudes más altas aplicadas en ventanas móviles (de 5 horas, semanales y mensuales), aproximadamente equivalentes a 12 $ por 5 horas, 30 $ por semana y 60 $ por mes (la cantidad real de solicitudes varía según el modelo y el uso).", + "Los modelos gratuitos incluyen Big Pickle y los modelos promocionales disponibles en ese momento, con una cuota de 200 solicitudes/día. Go ofrece una selección de modelos con cuotas de solicitudes más altas aplicadas en ventanas móviles (de 5 horas, semanales y mensuales), aproximadamente equivalentes a cuotas base de 12 $ por 5 horas, 30 $ por semana y 60 $ por mes; las cuotas específicas pueden variar según el modelo (la cantidad real de solicitudes varía según el modelo y el uso).", + "go.faq.q10": "¿Puedo obtener un reembolso?", + "go.faq.a10": + "Podrías tener derecho a un reembolso si el cargo se realizó en los últimos 14 días y no has utilizado tu cuota de Go durante ese periodo de facturación. {{contact}} para solicitar un reembolso.", "zen.api.error.rateLimitExceeded": "Límite de tasa excedido. Por favor, inténtalo de nuevo más tarde.", "zen.api.error.modelNotSupported": "Modelo {{model}} no soportado", @@ -675,10 +690,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "unos pocos segundos", "workspace.lite.subscription.message": "Estás suscrito a OpenCode Go.", "workspace.lite.subscription.manage": "Gestionar Suscripción", - "workspace.lite.subscription.rollingUsage": "Uso Continuo", + "workspace.lite.subscription.rollingUsage": "Uso de 5 horas", + "workspace.lite.subscription.rollingQuota": "Cuota de 5 horas", "workspace.lite.subscription.weeklyUsage": "Uso Semanal", + "workspace.lite.subscription.weeklyQuota": "Cuota Semanal", "workspace.lite.subscription.monthlyUsage": "Uso Mensual", + "workspace.lite.subscription.monthlyQuota": "Cuota Mensual", "workspace.lite.subscription.resetsIn": "Se reinicia en", + "workspace.lite.subscription.showDetails": "Mostrar detalles", + "workspace.lite.subscription.hideDetails": "Ocultar detalles", + "workspace.lite.subscription.model": "Modelo", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Total", "workspace.lite.subscription.useBalance": "Usa tu saldo disponible después de alcanzar los límites de uso", "workspace.lite.subscription.selectProvider": 'Selecciona "OpenCode Go" como proveedor en tu configuración de opencode para usar los modelos Go.', @@ -691,8 +714,8 @@ export const dict = { "workspace.lite.other.message": "Otro miembro de este espacio de trabajo ya está suscrito a OpenCode Go. Solo un miembro por espacio de trabajo puede suscribirse.", "workspace.lite.promo.description": - "OpenCode Go comienza en {{price}}, luego $10/mes, y ofrece acceso confiable a modelos de codificación abiertos populares con límites de uso generosos.", - "workspace.lite.promo.price": "$5 el primer mes", + "OpenCode Go cuesta {{price}} y ofrece acceso confiable a modelos de codificación abiertos populares con límites de uso generosos.", + "workspace.lite.promo.price": "$10/mes", "workspace.lite.promo.modelsTitle": "Qué incluye", "workspace.lite.promo.footer": "El plan está diseñado principalmente para usuarios internacionales y ofrece un acceso global estable. Los precios y los límites de uso pueden cambiar a medida que aprendemos del uso inicial y de los comentarios recibidos.", diff --git a/packages/console/app/src/i18n/fr.ts b/packages/console/app/src/i18n/fr.ts index 29f7a1958dd7..5281b6b3e54f 100644 --- a/packages/console/app/src/i18n/fr.ts +++ b/packages/console/app/src/i18n/fr.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash bénéficie de limites d’utilisation 4x supérieures pour une durée limitée", + "go.graph.bonus": "{{count}}× d’utilisation", "app.meta.description": "OpenCode - L'agent de code open source.", "nav.github": "GitHub", "nav.docs": "Documentation", @@ -57,6 +59,7 @@ export const dict = { "common.cancel": "Annuler", "common.creating": "Création...", "common.create": "Créer", + "common.contactUs": "Contactez-nous", "common.videoUnsupported": "Votre navigateur ne prend pas en charge la balise vidéo.", "common.figure": "Fig {{n}}.", @@ -227,6 +230,9 @@ export const dict = { "zen.faq.q8": "Puis-je utiliser Zen avec d'autres agents de code ?", "zen.faq.a8": "Zen fonctionne très bien avec OpenCode, mais vous pouvez utiliser Zen avec n'importe quel agent. Suivez les instructions de configuration dans votre agent préféré.", + "zen.faq.q9": "Puis-je obtenir un remboursement ?", + "zen.faq.a9": + "Vous pourriez avoir droit à un remboursement si le paiement a été effectué au cours des 14 derniers jours et que vous n’avez pas utilisé le crédit de cet achat. {{contact}} pour demander un remboursement.", "zen.cta.start": "Commencez avec Zen", "zen.pricing.title": "Ajoutez 20 $ de solde Pay as you go", @@ -258,9 +264,8 @@ export const dict = { "zen.privacy.exceptionsLink": "exceptions suivantes", "go.title": "OpenCode Go | Modèles de code à faible coût pour tous", - "go.banner.text": "Ox Alpha Free est disponible sur Go pour une durée limitée", "go.meta.description": - "Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites d'utilisation généreuses et un accès fiable aux principaux modèles de codage.", + "Go coûte 10 $/mois, avec des limites d'utilisation généreuses et un accès fiable aux principaux modèles de codage.", "go.hero.title": "Modèles de code à faible coût pour tous", "go.hero.body": "Go apporte le codage agentique aux programmeurs du monde entier. Offrant des limites généreuses et un accès fiable aux modèles open source les plus capables, pour que vous puissiez construire avec des agents puissants sans vous soucier du coût ou de la disponibilité.", @@ -269,16 +274,23 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "S'abonner à Go", "go.cta.price": "10 $/mois", - "go.cta.promo": "$5 le premier mois", "go.pricing.body": - "Utilisez-le avec n'importe quel agent. $5 le premier mois, puis 10 $/mois. Rechargez du crédit si nécessaire. Annulez à tout moment.", + "Utilisez-le avec n'importe quel agent. 10 $/mois. Rechargez du crédit si nécessaire. Annulez à tout moment.", "go.graph.free": "Gratuit", "go.graph.freePill": "Big Pickle et modèles gratuits", "go.graph.go": "Go", "go.graph.label": "Requêtes par tranche de 5 heures", + "go.graph.period": "Utilisation", + "go.graph.model": "Modèle", + "go.graph.requests": "Requêtes estimées / 5 h", + "go.graph.allowance": "Utilisation mensuelle", + "go.graph.new": "Nouveau", + "go.graph.scale": "Échelle non linéaire des requêtes", + "go.graph.showAll": "Voir les {{count}} modèles", + "go.graph.showLess": "Afficher moins de modèles", "go.graph.limitedRegions": "régions limitées", "go.graph.limitedTime": "durée limitée", - "go.graph.usageLimits": "Limites d'utilisation", + "go.graph.usageLimits": "Limites d’utilisation", "go.graph.aria": "Requêtes par 5h : {{free}} vs {{go}}", "go.testimonials.brand.zen": "Zen", @@ -304,21 +316,20 @@ export const dict = { "go.testimonials.frank.quote": "J'aimerais être encore chez Nvidia.", "go.problem.title": "Quel problème Go résout-il ?", "go.problem.body": - "Nous nous efforçons d'apporter l'expérience OpenCode au plus grand nombre. OpenCode Go est un abonnement à faible coût : $5 pour le premier mois, puis 10 $/mois. Il offre des limites généreuses et un accès fiable aux modèles open source les plus performants.", + "Nous nous efforçons d'apporter l'expérience OpenCode au plus grand nombre. OpenCode Go est un abonnement à faible coût de 10 $/mois. Il offre des limites généreuses et un accès fiable aux modèles open source les plus performants.", "go.problem.subtitle": " ", "go.problem.item1": "Prix d'abonnement bas", "go.problem.item2": "Limites généreuses et accès fiable", "go.problem.item3": "Conçu pour autant de programmeurs que possible", "go.problem.item4": "Une sélection de modèles testés pour le codage agentique", "go.how.title": "Comment fonctionne Go", - "go.how.body": - "Go commence à $5 pour le premier mois, puis 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.", + "go.how.body": "Go coûte 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.", "go.how.step1.title": "Créez un compte", "go.how.step1.beforeLink": "suivez les", "go.how.step1.link": "instructions de configuration", "go.how.step2.title": "Abonnez-vous à Go", - "go.how.step2.link": "$5 le premier mois", - "go.how.step2.afterLink": "puis 10 $/mois avec des limites généreuses", + "go.how.step2.link": "10 $/mois", + "go.how.step2.afterLink": "avec des limites généreuses", "go.how.step3.title": "Commencez à coder", "go.how.step3.body": "avec un accès fiable aux modèles open source", "go.privacy.title": "Votre vie privée est importante pour nous", @@ -335,11 +346,11 @@ export const dict = { "go.faq.a2": "Go inclut les modèles ci-dessous, avec des limites généreuses et un accès fiable.", "go.faq.q3": "Est-ce que Go est la même chose que Zen ?", "go.faq.a3": - "Non. Zen est un paiement à l'utilisation, tandis que Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites généreuses et un accès fiable à une sélection de modèles.", + "Non. Zen est un paiement à l'utilisation, tandis que Go coûte 10 $/mois, avec des limites généreuses et un accès fiable à une sélection de modèles.", "go.faq.q4": "Combien coûte Go ?", "go.faq.a4.p1.beforePricing": "Go coûte", - "go.faq.a4.p1.pricingLink": "$5 le premier mois", - "go.faq.a4.p1.afterPricing": "puis 10 $/mois avec des limites généreuses.", + "go.faq.a4.p1.pricingLink": "10 $/mois", + "go.faq.a4.p1.afterPricing": "avec des limites généreuses.", "go.faq.a4.p2.beforeAccount": "Vous pouvez gérer votre abonnement dans votre", "go.faq.a4.p2.accountLink": "compte", "go.faq.a4.p3": "Annulez à tout moment.", @@ -361,7 +372,7 @@ export const dict = { "Des tarifs de tokens fortement réduits en échange de l’autorisation d’utiliser vos prompts et vos complétions pour entraîner de futurs modèles Meta.", "go.faq.a5.learnMore": "En savoir plus", "go.faq.a5.deepseekRetention": - "L’accord ZDR est renouvelé chaque mois. L’accord actuel est valable jusqu’au 31 août 2026.", + "L’accord ZDR est renouvelé chaque mois. L’accord actuel est valable jusqu’au 30 septembre 2026.", "go.faq.a5.beforeExceptions": "Les modèles Go sont hébergés aux États-Unis. Les fournisseurs suivent une politique de rétention zéro et n'utilisent pas vos données pour l'entraînement des modèles, avec les", @@ -375,7 +386,10 @@ export const dict = { "Oui, vous pouvez utiliser Go avec n'importe quel agent. Suivez les instructions de configuration dans votre agent de code préféré.", "go.faq.q9": "Quelle est la différence entre les modèles gratuits et Go ?", "go.faq.a9": - "Les modèles gratuits incluent Big Pickle ainsi que les modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go propose une sélection de modèles avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalents à 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois (le nombre réel de requêtes varie selon le modèle et l'utilisation).", + "Les modèles gratuits incluent Big Pickle ainsi que les modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go propose une sélection de modèles avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalents à des quotas de base de 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois ; les quotas propres à chaque modèle peuvent varier (le nombre réel de requêtes varie selon le modèle et l'utilisation).", + "go.faq.q10": "Puis-je obtenir un remboursement ?", + "go.faq.a10": + "Vous pourriez avoir droit à un remboursement si le paiement a été effectué au cours des 14 derniers jours et que vous n’avez pas utilisé votre quota Go pendant cette période de facturation. {{contact}} pour demander un remboursement.", "zen.api.error.rateLimitExceeded": "Limite de débit dépassée. Veuillez réessayer plus tard.", "zen.api.error.modelNotSupported": "Modèle {{model}} non pris en charge", @@ -681,10 +695,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "quelques secondes", "workspace.lite.subscription.message": "Vous êtes abonné à OpenCode Go.", "workspace.lite.subscription.manage": "Gérer l'abonnement", - "workspace.lite.subscription.rollingUsage": "Utilisation glissante", + "workspace.lite.subscription.rollingUsage": "Utilisation sur 5 heures", + "workspace.lite.subscription.rollingQuota": "Quota sur 5 heures", "workspace.lite.subscription.weeklyUsage": "Utilisation hebdomadaire", + "workspace.lite.subscription.weeklyQuota": "Quota hebdomadaire", "workspace.lite.subscription.monthlyUsage": "Utilisation mensuelle", + "workspace.lite.subscription.monthlyQuota": "Quota mensuel", "workspace.lite.subscription.resetsIn": "Réinitialisation dans", + "workspace.lite.subscription.showDetails": "Afficher les détails", + "workspace.lite.subscription.hideDetails": "Masquer les détails", + "workspace.lite.subscription.model": "Modèle", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Total", "workspace.lite.subscription.useBalance": "Utilisez votre solde disponible après avoir atteint les limites d'utilisation", "workspace.lite.subscription.selectProvider": @@ -698,8 +720,8 @@ export const dict = { "workspace.lite.other.message": "Un autre membre de cet espace de travail est déjà abonné à OpenCode Go. Un seul membre par espace de travail peut s'abonner.", "workspace.lite.promo.description": - "OpenCode Go commence à {{price}}, puis 10 $/mois, et offre un accès fiable aux modèles de code ouverts populaires avec des limites d'utilisation généreuses.", - "workspace.lite.promo.price": "$5 le premier mois", + "OpenCode Go coûte {{price}} et offre un accès fiable aux modèles de code ouverts populaires avec des limites d'utilisation généreuses.", + "workspace.lite.promo.price": "10 $/mois", "workspace.lite.promo.modelsTitle": "Ce qui est inclus", "workspace.lite.promo.footer": "Ce forfait est principalement conçu pour les utilisateurs internationaux et offre un accès mondial stable. Les tarifs et les limites d'utilisation peuvent évoluer à mesure que nous tirons les enseignements des premières utilisations et des retours reçus.", diff --git a/packages/console/app/src/i18n/it.ts b/packages/console/app/src/i18n/it.ts index 18d26e0d3436..cb2f3d7d7a83 100644 --- a/packages/console/app/src/i18n/it.ts +++ b/packages/console/app/src/i18n/it.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash offre limiti di utilizzo 4x superiori per un periodo limitato", + "go.graph.bonus": "Utilizzo {{count}}×", "nav.github": "GitHub", "nav.docs": "Documentazione", "nav.changelog": "Changelog", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Annulla", "common.creating": "Creazione...", "common.create": "Crea", + "common.contactUs": "Contattaci", "common.videoUnsupported": "Il tuo browser non supporta il tag video.", "common.figure": "Fig {{n}}.", @@ -226,6 +229,9 @@ export const dict = { "zen.faq.q8": "Posso usare Zen con altri agenti di coding?", "zen.faq.a8": "Anche se Zen funziona alla grande con OpenCode, puoi usare Zen con qualsiasi agente. Segui le istruzioni di configurazione nel tuo agente di coding preferito.", + "zen.faq.q9": "Posso ottenere un rimborso?", + "zen.faq.a9": + "Potresti avere diritto a un rimborso se l'addebito è stato effettuato negli ultimi 14 giorni e non hai utilizzato il credito di quell'acquisto. {{contact}} per richiedere un rimborso.", "zen.cta.start": "Inizia con Zen", "zen.pricing.title": "Aggiungi $20 di saldo a consumo", @@ -254,9 +260,8 @@ export const dict = { "zen.privacy.exceptionsLink": "seguenti eccezioni", "go.title": "OpenCode Go | Modelli di coding a basso costo per tutti", - "go.banner.text": "Ox Alpha Free è disponibile su Go per un periodo limitato", "go.meta.description": - "Go inizia a $5 per il primo mese, poi $10/mese, con limiti di utilizzo generosi e un accesso affidabile ai principali modelli di coding.", + "Go costa $10/mese, con limiti di utilizzo generosi e un accesso affidabile ai principali modelli di coding.", "go.hero.title": "Modelli di coding a basso costo per tutti", "go.hero.body": "Go porta il coding agentico ai programmatori di tutto il mondo. Offrendo limiti generosi e un accesso affidabile ai modelli open source più capaci, in modo da poter costruire con agenti potenti senza preoccuparsi dei costi o della disponibilità.", @@ -265,13 +270,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Abbonati a Go", "go.cta.price": "$10/mese", - "go.cta.promo": "$5 il primo mese", "go.pricing.body": - "Usalo con qualsiasi agente. $5 il primo mese, poi $10/mese. Ricarica il credito se necessario. Annulla in qualsiasi momento.", + "Usalo con qualsiasi agente. $10/mese. Ricarica il credito se necessario. Annulla in qualsiasi momento.", "go.graph.free": "Gratis", "go.graph.freePill": "Big Pickle e modelli gratuiti", "go.graph.go": "Go", "go.graph.label": "Richieste ogni 5 ore", + "go.graph.period": "Utilizzo", + "go.graph.model": "Modello", + "go.graph.requests": "Richieste stimate / 5 ore", + "go.graph.allowance": "Utilizzo mensile", + "go.graph.new": "Nuovo", + "go.graph.scale": "Scala non lineare delle richieste", + "go.graph.showAll": "Mostra tutti i {{count}} modelli", + "go.graph.showLess": "Mostra meno modelli", "go.graph.limitedRegions": "regioni limitate", "go.graph.limitedTime": "periodo limitato", "go.graph.usageLimits": "Limiti di utilizzo", @@ -300,20 +312,20 @@ export const dict = { "go.testimonials.frank.quote": "Vorrei essere ancora a Nvidia.", "go.problem.title": "Quale problema risolve Go?", "go.problem.body": - "Ci concentriamo nel portare l'esperienza OpenCode a quante più persone possibile. OpenCode Go è un abbonamento a basso costo: $5 il primo mese, poi $10/mese. Offre limiti generosi e accesso affidabile ai modelli open source più capaci.", + "Ci concentriamo nel portare l'esperienza OpenCode a quante più persone possibile. OpenCode Go è un abbonamento a basso costo da $10/mese. Offre limiti generosi e accesso affidabile ai modelli open source più capaci.", "go.problem.subtitle": " ", "go.problem.item1": "Prezzo di abbonamento a basso costo", "go.problem.item2": "Limiti generosi e accesso affidabile", "go.problem.item3": "Costruito per il maggior numero possibile di programmatori", "go.problem.item4": "Una selezione curata di modelli testati per il coding agentico", "go.how.title": "Come funziona Go", - "go.how.body": "Go inizia a $5 per il primo mese, poi $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.", + "go.how.body": "Go costa $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.", "go.how.step1.title": "Crea un account", "go.how.step1.beforeLink": "segui le", "go.how.step1.link": "istruzioni di configurazione", "go.how.step2.title": "Abbonati a Go", - "go.how.step2.link": "$5 il primo mese", - "go.how.step2.afterLink": "poi $10/mese con limiti generosi", + "go.how.step2.link": "$10/mese", + "go.how.step2.afterLink": "con limiti generosi", "go.how.step3.title": "Inizia a programmare", "go.how.step3.body": "con accesso affidabile ai modelli open source", "go.privacy.title": "La tua privacy è importante per noi", @@ -330,11 +342,11 @@ export const dict = { "go.faq.a2": "Go include i modelli elencati di seguito, con limiti generosi e accesso affidabile.", "go.faq.q3": "Go è lo stesso di Zen?", "go.faq.a3": - "No. Zen è a consumo, mentre Go inizia a $5 per il primo mese, poi $10/mese, con limiti generosi e un accesso affidabile a una selezione curata di modelli.", + "No. Zen è a consumo, mentre Go costa $10/mese, con limiti generosi e un accesso affidabile a una selezione curata di modelli.", "go.faq.q4": "Quanto costa Go?", "go.faq.a4.p1.beforePricing": "Go costa", - "go.faq.a4.p1.pricingLink": "$5 il primo mese", - "go.faq.a4.p1.afterPricing": "poi $10/mese con limiti generosi.", + "go.faq.a4.p1.pricingLink": "$10/mese", + "go.faq.a4.p1.afterPricing": "con limiti generosi.", "go.faq.a4.p2.beforeAccount": "Puoi gestire il tuo abbonamento nel tuo", "go.faq.a4.p2.accountLink": "account", "go.faq.a4.p3": "Annulla in qualsiasi momento.", @@ -354,7 +366,7 @@ export const dict = { "I log di monitoraggio degli abusi vengono generati per l'utilizzo di tutte le funzionalità API e conservati per un massimo di 30 giorni.", "go.faq.a5.learnMore": "Scopri di più", "go.faq.a5.deepseekRetention": - "L'accordo ZDR viene rinnovato mensilmente. L'accordo attuale è valido fino al 31 agosto 2026.", + "L'accordo ZDR viene rinnovato mensilmente. L'accordo attuale è valido fino al 30 settembre 2026.", "go.faq.a5.museRetention": "Prezzi dei token fortemente scontati in cambio dell'autorizzazione a utilizzare i tuoi prompt e completamenti per addestrare futuri modelli Meta.", "go.faq.a5.beforeExceptions": @@ -370,7 +382,10 @@ export const dict = { "go.faq.q9": "Qual è la differenza tra i modelli gratuiti e Go?", "go.faq.a9": - "I modelli gratuiti includono Big Pickle più i modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go offre una selezione curata di modelli con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a $12 ogni 5 ore, $30 a settimana e $60 al mese (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).", + "I modelli gratuiti includono Big Pickle più i modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go offre una selezione curata di modelli con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a quote base di $12 ogni 5 ore, $30 a settimana e $60 al mese; le quote specifiche possono variare in base al modello (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).", + "go.faq.q10": "Posso ottenere un rimborso?", + "go.faq.a10": + "Potresti avere diritto a un rimborso se l'addebito è stato effettuato negli ultimi 14 giorni e non hai utilizzato la tua quota Go durante quel periodo di fatturazione. {{contact}} per richiedere un rimborso.", "zen.api.error.rateLimitExceeded": "Limite di richieste superato. Riprova più tardi.", "zen.api.error.modelNotSupported": "Modello {{model}} non supportato", @@ -673,10 +688,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "pochi secondi", "workspace.lite.subscription.message": "Sei abbonato a OpenCode Go.", "workspace.lite.subscription.manage": "Gestisci Abbonamento", - "workspace.lite.subscription.rollingUsage": "Utilizzo Continuativo", + "workspace.lite.subscription.rollingUsage": "Utilizzo su 5 ore", + "workspace.lite.subscription.rollingQuota": "Quota su 5 ore", "workspace.lite.subscription.weeklyUsage": "Utilizzo Settimanale", + "workspace.lite.subscription.weeklyQuota": "Quota Settimanale", "workspace.lite.subscription.monthlyUsage": "Utilizzo Mensile", + "workspace.lite.subscription.monthlyQuota": "Quota Mensile", "workspace.lite.subscription.resetsIn": "Si resetta tra", + "workspace.lite.subscription.showDetails": "Mostra dettagli", + "workspace.lite.subscription.hideDetails": "Nascondi dettagli", + "workspace.lite.subscription.model": "Modello", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Totale", "workspace.lite.subscription.useBalance": "Usa il tuo saldo disponibile dopo aver raggiunto i limiti di utilizzo", "workspace.lite.subscription.selectProvider": 'Seleziona "OpenCode Go" come provider nella tua configurazione opencode per utilizzare i modelli Go.', @@ -689,8 +712,8 @@ export const dict = { "workspace.lite.other.message": "Un altro membro in questo workspace è già abbonato a OpenCode Go. Solo un membro per workspace può abbonarsi.", "workspace.lite.promo.description": - "OpenCode Go parte da {{price}}, poi $10/mese, e offre un accesso affidabile a popolari modelli di coding aperti con generosi limiti di utilizzo.", - "workspace.lite.promo.price": "$5 il primo mese", + "OpenCode Go costa {{price}} e offre un accesso affidabile a popolari modelli di coding aperti con generosi limiti di utilizzo.", + "workspace.lite.promo.price": "$10/mese", "workspace.lite.promo.modelsTitle": "Cosa è incluso", "workspace.lite.promo.footer": "Il piano è pensato principalmente per gli utenti internazionali e offre un accesso globale stabile. I prezzi e i limiti di utilizzo potrebbero cambiare in base a quanto apprenderemo dall'utilizzo iniziale e dai feedback.", diff --git a/packages/console/app/src/i18n/ja.ts b/packages/console/app/src/i18n/ja.ts index 7521971c2bf3..2ae754c47d90 100644 --- a/packages/console/app/src/i18n/ja.ts +++ b/packages/console/app/src/i18n/ja.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flashの利用上限が期間限定で4倍に", + "go.graph.bonus": "利用枠{{count}}倍", "nav.github": "GitHub", "nav.docs": "ドキュメント", "nav.changelog": "変更履歴", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "キャンセル", "common.creating": "作成中...", "common.create": "作成", + "common.contactUs": "お問い合わせ", "common.videoUnsupported": "お使いのブラウザは video タグをサポートしていません。", "common.figure": "図 {{n}}.", @@ -224,6 +227,9 @@ export const dict = { "zen.faq.q8": "他のコーディングエージェントでもZenを使えますか?", "zen.faq.a8": "ZenはOpenCodeとの相性が良いですが、どのエージェントでもZenを利用できます。お使いのコーディングエージェントのセットアップ手順に従ってください。", + "zen.faq.q9": "返金を受けられますか?", + "zen.faq.a9": + "請求から14日以内で、その購入分のクレジットを一切使用していない場合、返金の対象となる可能性があります。返金を希望する場合は、{{contact}}ください。", "zen.cta.start": "Zenをはじめる", "zen.pricing.title": "$20の従量課金制残高を追加", @@ -253,9 +259,8 @@ export const dict = { "zen.privacy.exceptionsLink": "以下の例外", "go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル", - "go.banner.text": "Ox Alpha Freeは期間限定でGoで利用できます", "go.meta.description": - "Goは最初の月$5、その後$10/月で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。", + "Goは月額$10で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。", "go.hero.title": "すべての人のための低価格なコーディングモデル", "go.hero.body": "Goは、世界中のプログラマーにエージェント型コーディングをもたらします。最も高性能なオープンソースモデルへの十分な制限と安定したアクセスを提供し、コストや可用性を気にすることなく強力なエージェントで構築できます。", @@ -264,13 +269,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Goを購読する", "go.cta.price": "$10/月", - "go.cta.promo": "初月 $5", "go.pricing.body": - "どのエージェントでも使えます。最初の月$5、その後$10/月。必要に応じてクレジットを追加。いつでもキャンセルできます。", + "どのエージェントでも使えます。月額$10。必要に応じてクレジットを追加。いつでもキャンセルできます。", "go.graph.free": "無料", "go.graph.freePill": "Big Pickleと無料モデル", "go.graph.go": "Go", "go.graph.label": "5時間あたりのリクエスト数", + "go.graph.period": "利用枠", + "go.graph.model": "モデル", + "go.graph.requests": "推定リクエスト数 / 5時間", + "go.graph.allowance": "月間利用枠", + "go.graph.new": "新着", + "go.graph.scale": "リクエスト数は非線形目盛りで表示", + "go.graph.showAll": "全{{count}}モデルを表示", + "go.graph.showLess": "折りたたむ", "go.graph.limitedRegions": "一部の地域に限定", "go.graph.limitedTime": "期間限定", "go.graph.usageLimits": "利用制限", @@ -300,20 +312,20 @@ export const dict = { "go.testimonials.frank.quote": "まだNvidiaにいられたらよかったのに。", "go.problem.title": "Goはどのような問題を解決していますか?", "go.problem.body": - "私たちはOpenCodeの体験をできるだけ多くの人に届けることに注力しています。OpenCode Goは低価格のサブスクリプションで、最初の月は$5、その後は$10/月です。ゆとりある上限と、最も高性能なオープンソースモデルへの信頼できるアクセスを提供します。", + "私たちはOpenCodeの体験をできるだけ多くの人に届けることに注力しています。OpenCode Goは月額$10の低価格なサブスクリプションです。ゆとりある上限と、最も高性能なオープンソースモデルへの信頼できるアクセスを提供します。", "go.problem.subtitle": " ", "go.problem.item1": "低価格なサブスクリプション料金", "go.problem.item2": "十分な制限と安定したアクセス", "go.problem.item3": "できるだけ多くのプログラマーのために構築", "go.problem.item4": "エージェント型コーディング向けにテストされた厳選モデルラインナップ", "go.how.title": "Goの仕組み", - "go.how.body": "Goは最初の月$5、その後$10/月で始まります。OpenCodeまたは任意のエージェントで使えます。", + "go.how.body": "Goは月額$10です。OpenCodeまたは任意のエージェントで使えます。", "go.how.step1.title": "アカウントを作成", "go.how.step1.beforeLink": "", "go.how.step1.link": "セットアップ手順はこちら", "go.how.step2.title": "Goを購読する", - "go.how.step2.link": "最初の月$5", - "go.how.step2.afterLink": "その後$10/月、ゆとりある上限付き", + "go.how.step2.link": "月額$10", + "go.how.step2.afterLink": "ゆとりある上限付き", "go.how.step3.title": "コーディングを開始", "go.how.step3.body": "オープンソースモデルへの安定したアクセスで", "go.privacy.title": "あなたのプライバシーは私たちにとって重要です", @@ -330,11 +342,11 @@ export const dict = { "go.faq.a2": "Go には、十分な利用上限と安定したアクセスを備えた、以下のモデルが含まれます。", "go.faq.q3": "GoはZenと同じですか?", "go.faq.a3": - "いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で、厳選されたモデルラインナップへのゆとりある上限と安定したアクセスを提供します。", + "いいえ。Zenは従量課金制ですが、Goは月額$10で、厳選されたモデルラインナップへのゆとりある上限と安定したアクセスを提供します。", "go.faq.q4": "Goの料金は?", "go.faq.a4.p1.beforePricing": "Goは", - "go.faq.a4.p1.pricingLink": "最初の月$5", - "go.faq.a4.p1.afterPricing": "その後$10/月、ゆとりある上限付き。", + "go.faq.a4.p1.pricingLink": "月額$10", + "go.faq.a4.p1.afterPricing": "ゆとりある上限付き。", "go.faq.a4.p2.beforeAccount": "管理画面:", "go.faq.a4.p2.accountLink": "アカウント", "go.faq.a4.p3": "いつでもキャンセル可能です。", @@ -354,7 +366,7 @@ export const dict = { "go.faq.a5.museRetention": "将来のMetaモデルのトレーニングにプロンプトと生成結果を使用する許可と引き換えに、トークン料金が大幅に割引されます。", "go.faq.a5.learnMore": "詳しく見る", - "go.faq.a5.deepseekRetention": "ZDR契約は毎月更新されます。現在の契約は2026年8月31日まで有効です。", + "go.faq.a5.deepseekRetention": "ZDR契約は毎月更新されます。現在の契約は2026年9月30日まで有効です。", "go.faq.a5.beforeExceptions": "Goのモデルは米国でホストされています。プロバイダーはゼロ保持ポリシーに従い、モデルのトレーニングにデータを使用しません(", "go.faq.a5.exceptionsLink": "以下の例外", @@ -368,7 +380,10 @@ export const dict = { "go.faq.q9": "無料モデルとGoの違いは何ですか?", "go.faq.a9": - "無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。Goでは厳選されたモデルラインナップを利用でき、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。これは概算で5時間あたり$12、週間$30、月間$60相当です(実際のリクエスト数はモデルと使用状況により異なります)。", + "無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。Goでは厳選されたモデルラインナップを利用でき、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。基本利用枠では概算で5時間あたり$12、週間$30、月間$60相当ですが、モデル別の利用枠は異なる場合があります(実際のリクエスト数はモデルと使用状況により異なります)。", + "go.faq.q10": "返金を受けられますか?", + "go.faq.a10": + "請求から14日以内で、その請求期間中にGoの利用枠を一切使用していない場合、返金の対象となる可能性があります。返金を希望する場合は、{{contact}}ください。", "zen.api.error.rateLimitExceeded": "レート制限を超えました。後でもう一度お試しください。", "zen.api.error.modelNotSupported": "モデル {{model}} はサポートされていません", @@ -671,10 +686,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "数秒", "workspace.lite.subscription.message": "あなたは OpenCode Go を購読しています。", "workspace.lite.subscription.manage": "サブスクリプションの管理", - "workspace.lite.subscription.rollingUsage": "ローリング利用量", + "workspace.lite.subscription.rollingUsage": "5時間利用量", + "workspace.lite.subscription.rollingQuota": "5時間上限", "workspace.lite.subscription.weeklyUsage": "週間利用量", + "workspace.lite.subscription.weeklyQuota": "週間上限", "workspace.lite.subscription.monthlyUsage": "月間利用量", + "workspace.lite.subscription.monthlyQuota": "月間上限", "workspace.lite.subscription.resetsIn": "リセットまで", + "workspace.lite.subscription.showDetails": "詳細を表示", + "workspace.lite.subscription.hideDetails": "詳細を非表示", + "workspace.lite.subscription.model": "モデル", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "合計", "workspace.lite.subscription.useBalance": "利用限度額に達したら利用可能な残高を使用する", "workspace.lite.subscription.selectProvider": "Go モデルを使用するには、opencode の設定で「OpenCode Go」をプロバイダーとして選択してください。", @@ -687,8 +710,8 @@ export const dict = { "workspace.lite.other.message": "このワークスペースの別のメンバーが既に OpenCode Go を購読しています。ワークスペースにつき1人のメンバーのみが購読できます。", "workspace.lite.promo.description": - "OpenCode Goは{{price}}で始まり、その後は$10/月で、人気の高いオープンコーディングモデルへの安定したアクセスと余裕のある利用枠を提供します。", - "workspace.lite.promo.price": "初月$5", + "OpenCode Goは{{price}}で、人気の高いオープンコーディングモデルへの安定したアクセスと余裕のある利用枠を提供します。", + "workspace.lite.promo.price": "$10/月", "workspace.lite.promo.modelsTitle": "含まれるもの", "workspace.lite.promo.footer": "このプランは主に海外のユーザー向けに設計されており、世界中から安定してご利用いただけます。料金と利用上限は、初期の利用状況やフィードバックを踏まえて変更される場合があります。", diff --git a/packages/console/app/src/i18n/ko.ts b/packages/console/app/src/i18n/ko.ts index 8a05597de5ba..e9b5c92071c4 100644 --- a/packages/console/app/src/i18n/ko.ts +++ b/packages/console/app/src/i18n/ko.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash 사용 한도가 한시적으로 4배 확대됩니다", + "go.graph.bonus": "사용량 {{count}}배", "nav.github": "GitHub", "nav.docs": "문서", "nav.changelog": "변경 내역", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "취소", "common.creating": "생성 중...", "common.create": "만들기", + "common.contactUs": "문의하기", "common.videoUnsupported": "브라우저가 비디오 태그를 지원하지 않습니다.", "common.figure": "그림 {{n}}.", @@ -222,6 +225,9 @@ export const dict = { "zen.faq.q8": "다른 코딩 에이전트와 Zen을 사용할 수 있나요?", "zen.faq.a8": "Zen은 OpenCode와 훌륭하게 작동하지만, 어떤 에이전트와도 Zen을 사용할 수 있습니다. 선호하는 코딩 에이전트의 설정 지침을 따르세요.", + "zen.faq.q9": "환불받을 수 있나요?", + "zen.faq.a9": + "결제일로부터 14일 이내이며 해당 구매로 받은 크레딧을 전혀 사용하지 않은 경우 환불 대상이 될 수 있습니다. 환불을 요청하려면 {{contact}}를 선택해 주세요.", "zen.cta.start": "Zen 시작하기", "zen.pricing.title": "$20 선불 잔액 추가", @@ -250,9 +256,7 @@ export const dict = { "zen.privacy.exceptionsLink": "다음 예외", "go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델", - "go.banner.text": "Ox Alpha Free가 한정된 기간 동안 Go에서 제공됩니다", - "go.meta.description": - "Go는 첫 달 $5, 이후 $10/월로 시작하며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.", + "go.meta.description": "Go는 월 $10이며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.", "go.hero.title": "모두를 위한 저비용 코딩 모델", "go.hero.body": "Go는 전 세계 프로그래머들에게 에이전트 코딩을 제공합니다. 가장 유능한 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공하므로, 비용이나 가용성 걱정 없이 강력한 에이전트로 빌드할 수 있습니다.", @@ -261,13 +265,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Go 구독하기", "go.cta.price": "$10/월", - "go.cta.promo": "첫 달 $5", "go.pricing.body": - "어떤 에이전트와도 사용할 수 있습니다. 첫 달 $5, 이후 $10/월. 필요하면 크레딧을 충전하세요. 언제든지 취소할 수 있습니다.", + "어떤 에이전트와도 사용할 수 있습니다. 월 $10. 필요하면 크레딧을 충전하세요. 언제든지 취소할 수 있습니다.", "go.graph.free": "무료", "go.graph.freePill": "Big Pickle 및 무료 모델", "go.graph.go": "Go", "go.graph.label": "5시간당 요청 수", + "go.graph.period": "사용량", + "go.graph.model": "모델", + "go.graph.requests": "예상 요청 횟수 / 5시간", + "go.graph.allowance": "월간 사용량", + "go.graph.new": "신규", + "go.graph.scale": "요청 수는 비선형 눈금으로 표시됩니다", + "go.graph.showAll": "전체 {{count}}개 모델 보기", + "go.graph.showLess": "접기", "go.graph.limitedRegions": "일부 지역에서만 제공", "go.graph.limitedTime": "한정된 기간", "go.graph.usageLimits": "사용 한도", @@ -297,20 +308,20 @@ export const dict = { "go.testimonials.frank.quote": "아직 Nvidia에 있었으면 좋았을 텐데요.", "go.problem.title": "Go는 어떤 문제를 해결하나요?", "go.problem.body": - "우리는 가능한 많은 사람들에게 OpenCode 경험을 제공하는 데 집중하고 있습니다. OpenCode Go는 저렴한 구독 서비스로, 첫 달 $5, 이후 $10/월입니다. 넉넉한 한도와 가장 뛰어난 오픈 소스 모델에 대한 안정적인 액세스를 제공합니다.", + "우리는 가능한 많은 사람들에게 OpenCode 경험을 제공하는 데 집중하고 있습니다. OpenCode Go는 월 $10의 저렴한 구독 서비스입니다. 넉넉한 한도와 가장 뛰어난 오픈 소스 모델에 대한 안정적인 액세스를 제공합니다.", "go.problem.subtitle": " ", "go.problem.item1": "저렴한 구독 가격", "go.problem.item2": "넉넉한 한도와 안정적인 액세스", "go.problem.item3": "가능한 한 많은 프로그래머를 위해 제작됨", "go.problem.item4": "에이전트 코딩용으로 테스트된 엄선된 모델 라인업", "go.how.title": "Go 작동 방식", - "go.how.body": "Go는 첫 달 $5, 이후 $10/월로 시작합니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.", + "go.how.body": "Go는 월 $10입니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.", "go.how.step1.title": "계정 생성", "go.how.step1.beforeLink": "", "go.how.step1.link": "설정 지침을 따르세요", "go.how.step2.title": "Go 구독", - "go.how.step2.link": "첫 달 $5", - "go.how.step2.afterLink": "이후 $10/월, 넉넉한 한도 포함", + "go.how.step2.link": "월 $10", + "go.how.step2.afterLink": "넉넉한 한도 포함", "go.how.step3.title": "코딩 시작", "go.how.step3.body": "오픈 소스 모델에 대한 안정적인 액세스와 함께", "go.privacy.title": "귀하의 프라이버시는 우리에게 중요합니다", @@ -326,11 +337,11 @@ export const dict = { "go.faq.a2": "Go에는 넉넉한 한도와 안정적인 액세스를 제공하는 아래 모델이 포함됩니다.", "go.faq.q3": "Go는 Zen과 같은가요?", "go.faq.a3": - "아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, 엄선된 모델 라인업에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.", + "아니요. Zen은 종량제인 반면, Go는 월 $10이며, 엄선된 모델 라인업에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.", "go.faq.q4": "Go 비용은 얼마인가요?", "go.faq.a4.p1.beforePricing": "Go 비용은", - "go.faq.a4.p1.pricingLink": "첫 달 $5", - "go.faq.a4.p1.afterPricing": "이후 $10/월, 넉넉한 한도 포함.", + "go.faq.a4.p1.pricingLink": "월 $10", + "go.faq.a4.p1.afterPricing": "넉넉한 한도 포함.", "go.faq.a4.p2.beforeAccount": "구독 관리는 다음에서 가능합니다:", "go.faq.a4.p2.accountLink": "계정", "go.faq.a4.p3": "언제든지 취소할 수 있습니다.", @@ -350,7 +361,7 @@ export const dict = { "go.faq.a5.museRetention": "향후 Meta 모델 학습에 사용자의 프롬프트와 생성 결과를 사용할 수 있도록 허용하는 대신 토큰 가격이 대폭 할인됩니다.", "go.faq.a5.learnMore": "자세히 알아보기", - "go.faq.a5.deepseekRetention": "ZDR 계약은 매월 갱신됩니다. 현재 계약은 2026년 8월 31일까지 유효합니다.", + "go.faq.a5.deepseekRetention": "ZDR 계약은 매월 갱신됩니다. 현재 계약은 2026년 9월 30일까지 유효합니다.", "go.faq.a5.beforeExceptions": "Go 모델은 미국에서 호스팅됩니다. 제공자들은 데이터 보존 금지 정책을 따르며 모델 학습에 데이터를 사용하지 않습니다. 단,", "go.faq.a5.exceptionsLink": "다음 예외", @@ -363,7 +374,10 @@ export const dict = { "go.faq.q9": "무료 모델과 Go의 차이점은 무엇인가요?", "go.faq.a9": - "무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 엄선된 모델 라인업을 제공하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 이는 대략 5시간당 $12, 주당 $30, 월 $60에 해당합니다(실제 요청 수는 모델 및 사용량에 따라 다름).", + "무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 엄선된 모델 라인업을 제공하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 기본 할당량은 대략 5시간당 $12, 주당 $30, 월 $60에 해당하며 모델별 할당량은 다를 수 있습니다(실제 요청 수는 모델 및 사용량에 따라 다름).", + "go.faq.q10": "환불받을 수 있나요?", + "go.faq.a10": + "결제일로부터 14일 이내이며 해당 결제 기간에 Go 사용 한도를 전혀 사용하지 않은 경우 환불 대상이 될 수 있습니다. 환불을 요청하려면 {{contact}}를 선택해 주세요.", "zen.api.error.rateLimitExceeded": "속도 제한을 초과했습니다. 나중에 다시 시도해 주세요.", "zen.api.error.modelNotSupported": "{{model}} 모델은 지원되지 않습니다", @@ -663,10 +677,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "몇 초", "workspace.lite.subscription.message": "현재 OpenCode Go를 구독 중입니다.", "workspace.lite.subscription.manage": "구독 관리", - "workspace.lite.subscription.rollingUsage": "롤링 사용량", + "workspace.lite.subscription.rollingUsage": "5시간 사용량", + "workspace.lite.subscription.rollingQuota": "5시간 할당량", "workspace.lite.subscription.weeklyUsage": "주간 사용량", + "workspace.lite.subscription.weeklyQuota": "주간 할당량", "workspace.lite.subscription.monthlyUsage": "월간 사용량", + "workspace.lite.subscription.monthlyQuota": "월간 할당량", "workspace.lite.subscription.resetsIn": "초기화까지 남은 시간:", + "workspace.lite.subscription.showDetails": "상세 정보 보기", + "workspace.lite.subscription.hideDetails": "상세 정보 숨기기", + "workspace.lite.subscription.model": "모델", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "합계", "workspace.lite.subscription.useBalance": "사용 한도 도달 후에는 보유 잔액 사용", "workspace.lite.subscription.selectProvider": 'Go 모델을 사용하려면 opencode 설정에서 "OpenCode Go"를 공급자로 선택하세요.', @@ -679,8 +701,8 @@ export const dict = { "workspace.lite.other.message": "이 워크스페이스의 다른 멤버가 이미 OpenCode Go를 구독 중입니다. 워크스페이스당 한 명의 멤버만 구독할 수 있습니다.", "workspace.lite.promo.description": - "OpenCode Go는 {{price}}부터 시작하며, 이후 $10/월로 넉넉한 사용량 한도와 함께 인기 있는 오픈 코딩 모델에 대한 안정적인 액세스를 제공합니다.", - "workspace.lite.promo.price": "첫 달 $5", + "OpenCode Go는 {{price}}로 넉넉한 사용량 한도와 함께 인기 있는 오픈 코딩 모델에 대한 안정적인 액세스를 제공합니다.", + "workspace.lite.promo.price": "$10/월", "workspace.lite.promo.modelsTitle": "포함 내역", "workspace.lite.promo.footer": "이 플랜은 주로 해외 사용자를 위해 설계되었으며, 전 세계에서 안정적으로 이용할 수 있습니다. 초기 이용 현황과 피드백을 반영하는 과정에서 가격과 사용 한도가 변경될 수 있습니다.", diff --git a/packages/console/app/src/i18n/no.ts b/packages/console/app/src/i18n/no.ts index 02b15686dea7..fc2636a30057 100644 --- a/packages/console/app/src/i18n/no.ts +++ b/packages/console/app/src/i18n/no.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash får 4x bruksgrense i en begrenset periode", + "go.graph.bonus": "{{count}}× bruk", "nav.github": "GitHub", "nav.docs": "Dokumentasjon", "nav.changelog": "Endringslogg", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Avbryt", "common.creating": "Oppretter...", "common.create": "Opprett", + "common.contactUs": "Kontakt oss", "common.videoUnsupported": "Nettleseren din støtter ikke video-taggen.", "common.figure": "Fig {{n}}.", @@ -226,6 +229,9 @@ export const dict = { "zen.faq.q8": "Kan jeg bruke Zen med andre kodeagenter?", "zen.faq.a8": "Selv om Zen fungerer veldig bra med OpenCode, kan du bruke Zen med hvilken som helst agent. Følg oppsettinstruksjonene i din foretrukne kodeagent.", + "zen.faq.q9": "Kan jeg få refusjon?", + "zen.faq.a9": + "Du kan ha rett på refusjon hvis belastningen ble gjort i løpet av de siste 14 dagene og du ikke har brukt noe av kreditten fra kjøpet. {{contact}} for å be om refusjon.", "zen.cta.start": "Kom i gang med Zen", "zen.pricing.title": "Legg til $20 Pay as you go-saldo", @@ -254,9 +260,8 @@ export const dict = { "zen.privacy.exceptionsLink": "følgende unntak", "go.title": "OpenCode Go | Rimelige kodemodeller for alle", - "go.banner.text": "Ox Alpha Free er tilgjengelig på Go i en begrenset periode", "go.meta.description": - "Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.", + "Go koster $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.", "go.hero.title": "Rimelige kodemodeller for alle", "go.hero.body": "Go bringer agent-koding til programmerere over hele verden. Med rause grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene, kan du bygge med kraftige agenter uten å bekymre deg for kostnader eller tilgjengelighet.", @@ -265,13 +270,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Abonner på Go", "go.cta.price": "$10/måned", - "go.cta.promo": "$5 første måned", - "go.pricing.body": - "Bruk med hvilken som helst agent. $5 første måned, deretter $10/måned. Fyll på kreditt ved behov. Avslutt når som helst.", + "go.pricing.body": "Bruk med hvilken som helst agent. $10/måned. Fyll på kreditt ved behov. Avslutt når som helst.", "go.graph.free": "Gratis", "go.graph.freePill": "Big Pickle og gratis modeller", "go.graph.go": "Go", "go.graph.label": "Forespørsler per 5 timer", + "go.graph.period": "Bruk", + "go.graph.model": "Modell", + "go.graph.requests": "Estimerte forespørsler / 5 timer", + "go.graph.allowance": "Månedlig bruk", + "go.graph.new": "Ny", + "go.graph.scale": "Ikke-lineær skala for forespørsler", + "go.graph.showAll": "Vis alle {{count}} modeller", + "go.graph.showLess": "Vis færre modeller", "go.graph.limitedRegions": "begrensede regioner", "go.graph.limitedTime": "begrenset periode", "go.graph.usageLimits": "Bruksgrenser", @@ -300,21 +311,20 @@ export const dict = { "go.testimonials.frank.quote": "Jeg skulle ønske jeg fortsatt var hos Nvidia.", "go.problem.title": "Hvilket problem løser Go?", "go.problem.body": - "Vi fokuserer på å bringe OpenCode-opplevelsen til så mange som mulig. OpenCode Go er et rimelig abonnement: $5 for den første måneden, deretter $10/måned. Det gir sjenerøse grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene.", + "Vi fokuserer på å bringe OpenCode-opplevelsen til så mange som mulig. OpenCode Go er et rimelig abonnement til $10/måned. Det gir sjenerøse grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene.", "go.problem.subtitle": " ", "go.problem.item1": "Rimelig abonnementspris", "go.problem.item2": "Rause grenser og pålitelig tilgang", "go.problem.item3": "Bygget for så mange programmerere som mulig", "go.problem.item4": "Et kuratert modellutvalg testet for agent-koding", "go.how.title": "Hvordan Go fungerer", - "go.how.body": - "Go starter på $5 for den første måneden, deretter $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.", + "go.how.body": "Go koster $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.", "go.how.step1.title": "Opprett en konto", "go.how.step1.beforeLink": "følg", "go.how.step1.link": "oppsettsinstruksjonene", "go.how.step2.title": "Abonner på Go", - "go.how.step2.link": "$5 første måned", - "go.how.step2.afterLink": "deretter $10/måned med sjenerøse grenser", + "go.how.step2.link": "$10/måned", + "go.how.step2.afterLink": "med sjenerøse grenser", "go.how.step3.title": "Begynn å kode", "go.how.step3.body": "med pålitelig tilgang til åpen kildekode-modeller", "go.privacy.title": "Personvernet ditt er viktig for oss", @@ -331,11 +341,11 @@ export const dict = { "go.faq.a2": "Go inkluderer modellene nedenfor, med høye grenser og pålitelig tilgang.", "go.faq.q3": "Er Go det samme som Zen?", "go.faq.a3": - "Nei. Zen er betaling etter bruk, mens Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse grenser og pålitelig tilgang til et kuratert modellutvalg.", + "Nei. Zen er betaling etter bruk, mens Go koster $10/måned, med sjenerøse grenser og pålitelig tilgang til et kuratert modellutvalg.", "go.faq.q4": "Hva koster Go?", "go.faq.a4.p1.beforePricing": "Go koster", - "go.faq.a4.p1.pricingLink": "$5 første måned", - "go.faq.a4.p1.afterPricing": "deretter $10/måned med sjenerøse grenser.", + "go.faq.a4.p1.pricingLink": "$10/måned", + "go.faq.a4.p1.afterPricing": "med sjenerøse grenser.", "go.faq.a4.p2.beforeAccount": "Du kan administrere abonnementet ditt i din", "go.faq.a4.p2.accountLink": "konto", "go.faq.a4.p3": "Avslutt når som helst.", @@ -355,7 +365,7 @@ export const dict = { "Logger for overvåking av misbruk genereres for all bruk av API-funksjoner og oppbevares i opptil 30 dager.", "go.faq.a5.learnMore": "Les mer", "go.faq.a5.deepseekRetention": - "ZDR-avtalen fornyes månedlig. Den gjeldende avtalen er gyldig til og med 31. august 2026.", + "ZDR-avtalen fornyes månedlig. Den gjeldende avtalen er gyldig til og med 30. september 2026.", "go.faq.a5.museRetention": "Kraftig rabatterte tokenpriser i bytte mot tillatelse til å bruke ledetekstene og fullføringene dine til å trene fremtidige Meta-modeller.", @@ -372,7 +382,10 @@ export const dict = { "go.faq.q9": "Hva er forskjellen mellom gratis modeller og Go?", "go.faq.a9": - "Gratis modeller inkluderer Big Pickle pluss kampanjemodeller som er tilgjengelige på det tidspunktet, med en kvote på 200 forespørsler/dag. Go tilbyr et kuratert modellutvalg med høyere forespørselskvoter som håndheves over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende $12 per 5 timer, $30 per uke og $60 per måned (faktiske forespørselsantall varierer etter modell og bruk).", + "Gratis modeller inkluderer Big Pickle pluss kampanjemodeller som er tilgjengelige på det tidspunktet, med en kvote på 200 forespørsler/dag. Go tilbyr et kuratert modellutvalg med høyere forespørselskvoter som håndheves over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende basiskvoter på $12 per 5 timer, $30 per uke og $60 per måned; modellspesifikke kvoter kan variere (faktiske forespørselsantall varierer etter modell og bruk).", + "go.faq.q10": "Kan jeg få refusjon?", + "go.faq.a10": + "Du kan ha rett på refusjon hvis belastningen ble gjort i løpet av de siste 14 dagene og du ikke har brukt noe av Go-kvoten i den faktureringsperioden. {{contact}} for å be om refusjon.", "zen.api.error.rateLimitExceeded": "Rate limit overskredet. Vennligst prøv igjen senere.", "zen.api.error.modelNotSupported": "Modell {{model}} støttes ikke", @@ -673,10 +686,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "noen få sekunder", "workspace.lite.subscription.message": "Du abonnerer på OpenCode Go.", "workspace.lite.subscription.manage": "Administrer abonnement", - "workspace.lite.subscription.rollingUsage": "Løpende bruk", + "workspace.lite.subscription.rollingUsage": "5-timers bruk", + "workspace.lite.subscription.rollingQuota": "5-timers kvote", "workspace.lite.subscription.weeklyUsage": "Ukentlig bruk", + "workspace.lite.subscription.weeklyQuota": "Ukentlig kvote", "workspace.lite.subscription.monthlyUsage": "Månedlig bruk", + "workspace.lite.subscription.monthlyQuota": "Månedlig kvote", "workspace.lite.subscription.resetsIn": "Nullstilles om", + "workspace.lite.subscription.showDetails": "Vis detaljer", + "workspace.lite.subscription.hideDetails": "Skjul detaljer", + "workspace.lite.subscription.model": "Modell", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Totalt", "workspace.lite.subscription.useBalance": "Bruk din tilgjengelige saldo etter å ha nådd bruksgrensene", "workspace.lite.subscription.selectProvider": 'Velg "OpenCode Go" som leverandør i opencode-konfigurasjonen din for å bruke Go-modeller.', @@ -689,8 +710,8 @@ export const dict = { "workspace.lite.other.message": "Et annet medlem i dette arbeidsområdet abonnerer allerede på OpenCode Go. Kun ett medlem per arbeidsområde kan abonnere.", "workspace.lite.promo.description": - "OpenCode Go starter på {{price}}, deretter $10/måned, og gir pålitelig tilgang til populære åpne kodingsmodeller med sjenerøse bruksgrenser.", - "workspace.lite.promo.price": "$5 for den første måneden", + "OpenCode Go koster {{price}} og gir pålitelig tilgang til populære åpne kodingsmodeller med sjenerøse bruksgrenser.", + "workspace.lite.promo.price": "$10/måned", "workspace.lite.promo.modelsTitle": "Hva som er inkludert", "workspace.lite.promo.footer": "Planen er primært utviklet for internasjonale brukere og gir stabil global tilgang. Priser og bruksgrenser kan endres etter hvert som vi lærer av tidlig bruk og tilbakemeldinger.", diff --git a/packages/console/app/src/i18n/pl.ts b/packages/console/app/src/i18n/pl.ts index 782747742893..4a4e1ba724a6 100644 --- a/packages/console/app/src/i18n/pl.ts +++ b/packages/console/app/src/i18n/pl.ts @@ -2,6 +2,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash oferuje 4x wyższe limity użycia przez ograniczony czas", + "go.graph.bonus": "{{count}}× większy limit", "nav.github": "GitHub", "nav.docs": "Dokumentacja", "nav.changelog": "Dziennik zmian", @@ -57,6 +59,7 @@ export const dict = { "common.cancel": "Anuluj", "common.creating": "Tworzenie...", "common.create": "Utwórz", + "common.contactUs": "Skontaktuj się z nami", "common.videoUnsupported": "Twoja przeglądarka nie obsługuje znacznika wideo.", "common.figure": "Rys. {{n}}.", @@ -227,6 +230,9 @@ export const dict = { "zen.faq.q8": "Czy mogę używać Zen z innymi agentami kodującymi?", "zen.faq.a8": "Chociaż Zen świetnie działa z OpenCode, możesz używać Zen z dowolnym agentem. Postępuj zgodnie z instrukcjami konfiguracji w swoim preferowanym agencie.", + "zen.faq.q9": "Czy mogę otrzymać zwrot pieniędzy?", + "zen.faq.a9": + "Możesz kwalifikować się do zwrotu, jeśli opłata została pobrana w ciągu ostatnich 14 dni i nie wykorzystano żadnych środków z tego zakupu. {{contact}}, aby poprosić o zwrot.", "zen.cta.start": "Zacznij korzystać z Zen", "zen.pricing.title": "Dodaj 20$ salda Pay as you go", @@ -255,9 +261,8 @@ export const dict = { "zen.privacy.exceptionsLink": "następującymi wyjątkami", "go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego", - "go.banner.text": "Ox Alpha Free jest dostępny w Go przez ograniczony czas", "go.meta.description": - "Go kosztuje $5 za pierwszy miesiąc, a następnie $10/miesiąc, oferując hojne limity użycia i niezawodny dostęp do wiodących modeli do kodowania.", + "Go kosztuje $10/miesiąc, oferując hojne limity użycia i niezawodny dostęp do wiodących modeli do kodowania.", "go.hero.title": "Niskokosztowe modele do kodowania dla każdego", "go.hero.body": "Go udostępnia programowanie z agentami programistom na całym świecie. Oferuje hojne limity i niezawodny dostęp do najzdolniejszych modeli open source, dzięki czemu możesz budować za pomocą potężnych agentów, nie martwiąc się o koszty czy dostępność.", @@ -266,13 +271,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Zasubskrybuj Go", "go.cta.price": "$10/miesiąc", - "go.cta.promo": "$5 pierwszy miesiąc", "go.pricing.body": - "Używaj z dowolnym agentem. $5 za pierwszy miesiąc, potem $10/miesiąc. Doładuj konto w razie potrzeby. Anuluj w dowolnym momencie.", + "Używaj z dowolnym agentem. $10/miesiąc. Doładuj konto w razie potrzeby. Anuluj w dowolnym momencie.", "go.graph.free": "Darmowe", "go.graph.freePill": "Big Pickle i darmowe modele", "go.graph.go": "Go", "go.graph.label": "Żądania na 5 godzin", + "go.graph.period": "Użycie", + "go.graph.model": "Model", + "go.graph.requests": "Szac. żądania / 5 godz.", + "go.graph.allowance": "Miesięczne użycie", + "go.graph.new": "Nowość", + "go.graph.scale": "Nieliniowa skala liczby żądań", + "go.graph.showAll": "Pokaż wszystkie modele ({{count}})", + "go.graph.showLess": "Pokaż mniej modeli", "go.graph.limitedRegions": "ograniczone regiony", "go.graph.limitedTime": "ograniczony czas", "go.graph.usageLimits": "Limity użycia", @@ -301,21 +313,20 @@ export const dict = { "go.testimonials.frank.quote": "Chciałbym wciąż być w Nvidia.", "go.problem.title": "Jaki problem rozwiązuje Go?", "go.problem.body": - "Skupiamy się na udostępnieniu doświadczenia OpenCode jak największej liczbie osób. OpenCode Go to tania subskrypcja: $5 za pierwszy miesiąc, potem $10/miesiąc. Zapewnia hojne limity i niezawodny dostęp do najbardziej wydajnych modeli open source.", + "Skupiamy się na udostępnieniu doświadczenia OpenCode jak największej liczbie osób. OpenCode Go to tania subskrypcja za $10/miesiąc. Zapewnia hojne limity i niezawodny dostęp do najbardziej wydajnych modeli open source.", "go.problem.subtitle": " ", "go.problem.item1": "Niskokosztowa cena subskrypcji", "go.problem.item2": "Hojne limity i niezawodny dostęp", "go.problem.item3": "Stworzony dla jak największej liczby programistów", "go.problem.item4": "Starannie dobrany zestaw modeli przetestowanych pod kątem kodowania z agentami", "go.how.title": "Jak działa Go", - "go.how.body": - "Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.", + "go.how.body": "Go kosztuje $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.", "go.how.step1.title": "Załóż konto", "go.how.step1.beforeLink": "postępuj zgodnie z", "go.how.step1.link": "instrukcją konfiguracji", "go.how.step2.title": "Zasubskrybuj Go", - "go.how.step2.link": "$5 za pierwszy miesiąc", - "go.how.step2.afterLink": "potem $10/miesiąc z hojnymi limitami", + "go.how.step2.link": "$10/miesiąc", + "go.how.step2.afterLink": "z hojnymi limitami", "go.how.step3.title": "Zacznij kodować", "go.how.step3.body": "z niezawodnym dostępem do modeli open source", "go.privacy.title": "Twoja prywatność jest dla nas ważna", @@ -332,11 +343,11 @@ export const dict = { "go.faq.a2": "Go obejmuje poniższe modele z wysokimi limitami i niezawodnym dostępem.", "go.faq.q3": "Czy Go to to samo co Zen?", "go.faq.a3": - "Nie. Zen działa w modelu płatności za użycie, natomiast Go kosztuje $5 za pierwszy miesiąc, a następnie $10/miesiąc, oferując hojne limity i niezawodny dostęp do starannie dobranego zestawu modeli.", + "Nie. Zen działa w modelu płatności za użycie, natomiast Go kosztuje $10/miesiąc, oferując hojne limity i niezawodny dostęp do starannie dobranego zestawu modeli.", "go.faq.q4": "Ile kosztuje Go?", "go.faq.a4.p1.beforePricing": "Go kosztuje", - "go.faq.a4.p1.pricingLink": "$5 za pierwszy miesiąc", - "go.faq.a4.p1.afterPricing": "potem $10/miesiąc z hojnymi limitami.", + "go.faq.a4.p1.pricingLink": "$10/miesiąc", + "go.faq.a4.p1.afterPricing": "z hojnymi limitami.", "go.faq.a4.p2.beforeAccount": "Możesz zarządzać subskrypcją na swoim", "go.faq.a4.p2.accountLink": "koncie", "go.faq.a4.p3": "Anuluj w dowolnym momencie.", @@ -355,7 +366,7 @@ export const dict = { "go.faq.a5.gptRetention": "Dzienniki monitorowania nadużyć są generowane dla każdego użycia funkcji API i przechowywane przez maksymalnie 30 dni.", "go.faq.a5.learnMore": "Dowiedz się więcej", - "go.faq.a5.deepseekRetention": "Umowa ZDR jest odnawiana co miesiąc. Obecna umowa obowiązuje do 31 sierpnia 2026 r.", + "go.faq.a5.deepseekRetention": "Umowa ZDR jest odnawiana co miesiąc. Obecna umowa obowiązuje do 30 września 2026 r.", "go.faq.a5.museRetention": "Znacznie obniżone ceny tokenów w zamian za zgodę na wykorzystanie Twoich promptów i odpowiedzi do trenowania przyszłych modeli Meta.", @@ -372,7 +383,10 @@ export const dict = { "go.faq.q9": "Jaka jest różnica między darmowymi modelami a Go?", "go.faq.a9": - "Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go oferuje starannie dobrany zestaw modeli z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), odpowiadającymi w przybliżeniu $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie (rzeczywista liczba zapytań zależy od modelu i użycia).", + "Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go oferuje starannie dobrany zestaw modeli z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), odpowiadającymi w przybliżeniu bazowym limitom $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie; limity mogą się różnić zależnie od modelu (rzeczywista liczba zapytań zależy od modelu i użycia).", + "go.faq.q10": "Czy mogę otrzymać zwrot pieniędzy?", + "go.faq.a10": + "Możesz kwalifikować się do zwrotu, jeśli opłata została pobrana w ciągu ostatnich 14 dni i nie wykorzystano żadnej części limitu Go w tym okresie rozliczeniowym. {{contact}}, aby poprosić o zwrot.", "zen.api.error.rateLimitExceeded": "Przekroczono limit zapytań. Spróbuj ponownie później.", "zen.api.error.modelNotSupported": "Model {{model}} nie jest obsługiwany", @@ -673,10 +687,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "kilka sekund", "workspace.lite.subscription.message": "Subskrybujesz OpenCode Go.", "workspace.lite.subscription.manage": "Zarządzaj subskrypcją", - "workspace.lite.subscription.rollingUsage": "Użycie kroczące", + "workspace.lite.subscription.rollingUsage": "Użycie w ciągu 5 godzin", + "workspace.lite.subscription.rollingQuota": "Limit 5-godzinny", "workspace.lite.subscription.weeklyUsage": "Użycie tygodniowe", + "workspace.lite.subscription.weeklyQuota": "Limit tygodniowy", "workspace.lite.subscription.monthlyUsage": "Użycie miesięczne", + "workspace.lite.subscription.monthlyQuota": "Limit miesięczny", "workspace.lite.subscription.resetsIn": "Resetuje się za", + "workspace.lite.subscription.showDetails": "Pokaż szczegóły", + "workspace.lite.subscription.hideDetails": "Ukryj szczegóły", + "workspace.lite.subscription.model": "Model", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Łącznie", "workspace.lite.subscription.useBalance": "Użyj dostępnego salda po osiągnięciu limitów użycia", "workspace.lite.subscription.selectProvider": 'Wybierz "OpenCode Go" jako dostawcę w konfiguracji opencode, aby używać modeli Go.', @@ -689,8 +711,8 @@ export const dict = { "workspace.lite.other.message": "Inny członek tego obszaru roboczego już subskrybuje OpenCode Go. Tylko jeden członek na obszar roboczy może subskrybować.", "workspace.lite.promo.description": - "OpenCode Go zaczyna się od {{price}}, potem $10/miesiąc, i zapewnia niezawodny dostęp do popularnych otwartych modeli kodowania z hojnymi limitami użycia.", - "workspace.lite.promo.price": "$5 za pierwszy miesiąc", + "OpenCode Go kosztuje {{price}} i zapewnia niezawodny dostęp do popularnych otwartych modeli kodowania z hojnymi limitami użycia.", + "workspace.lite.promo.price": "$10/miesiąc", "workspace.lite.promo.modelsTitle": "Co zawiera", "workspace.lite.promo.footer": "Plan został opracowany przede wszystkim z myślą o użytkownikach z całego świata i zapewnia stabilny globalny dostęp. Ceny i limity użycia mogą ulec zmianie w miarę zdobywania doświadczeń na podstawie początkowego korzystania z usługi i otrzymywanych opinii.", diff --git a/packages/console/app/src/i18n/ru.ts b/packages/console/app/src/i18n/ru.ts index 97029f3f895c..37f6e43b06d6 100644 --- a/packages/console/app/src/i18n/ru.ts +++ b/packages/console/app/src/i18n/ru.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash получает 4x лимиты использования на ограниченное время", + "go.graph.bonus": "Лимит ×{{count}}", "nav.github": "GitHub", "nav.docs": "Документация", "nav.changelog": "Список изменений", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "Отмена", "common.creating": "Создание...", "common.create": "Создать", + "common.contactUs": "Свяжитесь с нами", "common.videoUnsupported": "Ваш браузер не поддерживает видео тег.", "common.figure": "Рис {{n}}.", @@ -230,6 +233,9 @@ export const dict = { "zen.faq.q8": "Могу ли я использовать Zen с другими кодинг-агентами?", "zen.faq.a8": "Хотя Zen отлично работает с OpenCode, вы можете использовать Zen с любым агентом. Следуйте инструкциям по настройке в вашем любимом агенте.", + "zen.faq.q9": "Могу ли я получить возврат средств?", + "zen.faq.a9": + "Вы можете претендовать на возврат, если списание произошло в течение последних 14 дней и средства, полученные при этой покупке, не были использованы. {{contact}}, чтобы запросить возврат.", "zen.cta.start": "Начать работу с Zen", "zen.pricing.title": "Пополнить баланс на $20 (Pay as you go)", @@ -258,9 +264,8 @@ export const dict = { "zen.privacy.exceptionsLink": "следующими исключениями", "go.title": "OpenCode Go | Недорогие модели для кодинга для всех", - "go.banner.text": "Ox Alpha Free доступна в Go в течение ограниченного времени", "go.meta.description": - "Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.", + "Go стоит $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.", "go.hero.title": "Недорогие модели для кодинга для всех", "go.hero.body": "Go открывает доступ к агентам-программистам разработчикам по всему миру. Предлагая щедрые лимиты и надежный доступ к наиболее способным моделям с открытым исходным кодом, вы можете создавать проекты с мощными агентами, не беспокоясь о затратах или доступности.", @@ -269,13 +274,20 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Подписаться на Go", "go.cta.price": "$10/месяц", - "go.cta.promo": "$5 первый месяц", "go.pricing.body": - "Используйте с любым агентом. $5 за первый месяц, затем $10/месяц. Пополняйте баланс при необходимости. Отменить можно в любое время.", + "Используйте с любым агентом. $10/месяц. Пополняйте баланс при необходимости. Отменить можно в любое время.", "go.graph.free": "Бесплатно", "go.graph.freePill": "Big Pickle и бесплатные модели", "go.graph.go": "Go", "go.graph.label": "Запросов за 5 часов", + "go.graph.period": "Использование", + "go.graph.model": "Модель", + "go.graph.requests": "Примерное число запросов / 5 ч", + "go.graph.allowance": "Использование за месяц", + "go.graph.new": "Новое", + "go.graph.scale": "Нелинейная шкала числа запросов", + "go.graph.showAll": "Показать все модели ({{count}})", + "go.graph.showLess": "Показать меньше моделей", "go.graph.limitedRegions": "доступно в отдельных регионах", "go.graph.limitedTime": "ограниченное время", "go.graph.usageLimits": "Лимиты использования", @@ -305,21 +317,20 @@ export const dict = { "go.testimonials.frank.quote": "Жаль, что я больше не в Nvidia.", "go.problem.title": "Какую проблему решает Go?", "go.problem.body": - "Мы стремимся сделать OpenCode доступным для как можно большего числа людей. OpenCode Go - это недорогая подписка: $5 за первый месяц, затем $10/месяц. Она предоставляет щедрые лимиты и надежный доступ к самым мощным моделям с открытым исходным кодом.", + "Мы стремимся сделать OpenCode доступным для как можно большего числа людей. OpenCode Go - это недорогая подписка за $10/месяц. Она предоставляет щедрые лимиты и надежный доступ к самым мощным моделям с открытым исходным кодом.", "go.problem.subtitle": " ", "go.problem.item1": "Недорогая подписка", "go.problem.item2": "Щедрые лимиты и надежный доступ", "go.problem.item3": "Создан для максимального числа программистов", "go.problem.item4": "Отобранные модели, протестированные для агентного программирования", "go.how.title": "Как работает Go", - "go.how.body": - "Go начинается с $5 за первый месяц, затем $10/месяц. Вы можете использовать его с OpenCode или любым агентом.", + "go.how.body": "Go стоит $10/месяц. Вы можете использовать его с OpenCode или любым агентом.", "go.how.step1.title": "Создайте аккаунт", "go.how.step1.beforeLink": "следуйте", "go.how.step1.link": "инструкциям по настройке", "go.how.step2.title": "Подпишитесь на Go", - "go.how.step2.link": "$5 за первый месяц", - "go.how.step2.afterLink": "затем $10/месяц с щедрыми лимитами", + "go.how.step2.link": "$10/месяц", + "go.how.step2.afterLink": "с щедрыми лимитами", "go.how.step3.title": "Начните кодить", "go.how.step3.body": "с надежным доступом к open-source моделям", "go.privacy.title": "Ваша приватность важна для нас", @@ -336,11 +347,11 @@ export const dict = { "go.faq.a2": "Go включает перечисленные ниже модели с щедрыми лимитами и надежным доступом.", "go.faq.q3": "Go — это то же самое, что и Zen?", "go.faq.a3": - "Нет. Zen оплачивается по мере использования, а Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты и надежный доступ к отобранным моделям.", + "Нет. Zen оплачивается по мере использования, а Go стоит $10/месяц и предлагает щедрые лимиты и надежный доступ к отобранным моделям.", "go.faq.q4": "Сколько стоит Go?", "go.faq.a4.p1.beforePricing": "Go стоит", - "go.faq.a4.p1.pricingLink": "$5 за первый месяц", - "go.faq.a4.p1.afterPricing": "затем $10/месяц с щедрыми лимитами.", + "go.faq.a4.p1.pricingLink": "$10/месяц", + "go.faq.a4.p1.afterPricing": "с щедрыми лимитами.", "go.faq.a4.p2.beforeAccount": "Вы можете управлять подпиской в своем", "go.faq.a4.p2.accountLink": "аккаунте", "go.faq.a4.p3": "Отмена в любое время.", @@ -360,7 +371,7 @@ export const dict = { "Журналы мониторинга злоупотреблений создаются при любом использовании функций API и хранятся до 30 дней.", "go.faq.a5.learnMore": "Подробнее", "go.faq.a5.deepseekRetention": - "Соглашение ZDR продлевается ежемесячно. Текущее соглашение действует до 31 августа 2026 года.", + "Соглашение ZDR продлевается ежемесячно. Текущее соглашение действует до 30 сентября 2026 года.", "go.faq.a5.museRetention": "Значительно сниженная стоимость токенов в обмен на разрешение использовать ваши промпты и ответы для обучения будущих моделей Meta.", @@ -377,7 +388,10 @@ export const dict = { "go.faq.q9": "В чем разница между бесплатными моделями и Go?", "go.faq.a9": - "Бесплатные модели включают Big Pickle и доступные на данный момент промо-модели с квотой 200 запросов/день. Go предлагает набор отобранных моделей с более высокими квотами запросов, применяемыми в скользящих окнах (5 часов, неделя и месяц), что примерно эквивалентно $12 за 5 часов, $30 в неделю и $60 в месяц (фактическое количество запросов зависит от модели и использования).", + "Бесплатные модели включают Big Pickle и доступные на данный момент промо-модели с квотой 200 запросов/день. Go предлагает набор отобранных моделей с более высокими квотами запросов, применяемыми в скользящих окнах (5 часов, неделя и месяц), что примерно эквивалентно базовым лимитам $12 за 5 часов, $30 в неделю и $60 в месяц; лимиты для отдельных моделей могут отличаться (фактическое количество запросов зависит от модели и использования).", + "go.faq.q10": "Могу ли я получить возврат средств?", + "go.faq.a10": + "Вы можете претендовать на возврат, если списание произошло в течение последних 14 дней и вы не использовали лимит Go в этом расчетном периоде. {{contact}}, чтобы запросить возврат.", "zen.api.error.rateLimitExceeded": "Превышен лимит запросов. Пожалуйста, попробуйте позже.", "zen.api.error.modelNotSupported": "Модель {{model}} не поддерживается", @@ -680,10 +694,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "несколько секунд", "workspace.lite.subscription.message": "Вы подписаны на OpenCode Go.", "workspace.lite.subscription.manage": "Управление подпиской", - "workspace.lite.subscription.rollingUsage": "Скользящее использование", + "workspace.lite.subscription.rollingUsage": "Использование за 5 часов", + "workspace.lite.subscription.rollingQuota": "Квота на 5 часов", "workspace.lite.subscription.weeklyUsage": "Недельное использование", + "workspace.lite.subscription.weeklyQuota": "Недельная квота", "workspace.lite.subscription.monthlyUsage": "Ежемесячное использование", + "workspace.lite.subscription.monthlyQuota": "Ежемесячная квота", "workspace.lite.subscription.resetsIn": "Сброс через", + "workspace.lite.subscription.showDetails": "Показать подробности", + "workspace.lite.subscription.hideDetails": "Скрыть подробности", + "workspace.lite.subscription.model": "Модель", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Итого", "workspace.lite.subscription.useBalance": "Использовать доступный баланс после достижения лимитов", "workspace.lite.subscription.selectProvider": 'Выберите "OpenCode Go" в качестве провайдера в настройках opencode для использования моделей Go.', @@ -696,8 +718,8 @@ export const dict = { "workspace.lite.other.message": "Другой участник в этом рабочем пространстве уже подписан на OpenCode Go. Только один участник в рабочем пространстве может оформить подписку.", "workspace.lite.promo.description": - "OpenCode Go начинается с {{price}}, затем $10/месяц и предоставляет надежный доступ к популярным открытым моделям кодирования с щедрыми лимитами использования.", - "workspace.lite.promo.price": "$5 за первый месяц", + "OpenCode Go стоит {{price}} и предоставляет надежный доступ к популярным открытым моделям кодирования с щедрыми лимитами использования.", + "workspace.lite.promo.price": "$10/месяц", "workspace.lite.promo.modelsTitle": "Что включено", "workspace.lite.promo.footer": "План предназначен в первую очередь для пользователей по всему миру и обеспечивает стабильный глобальный доступ. Цены и лимиты использования могут меняться по мере изучения первых результатов использования и отзывов.", diff --git a/packages/console/app/src/i18n/th.ts b/packages/console/app/src/i18n/th.ts index 3d1de2536a96..97a80f039418 100644 --- a/packages/console/app/src/i18n/th.ts +++ b/packages/console/app/src/i18n/th.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash เพิ่มโควตาการใช้งานเป็น 4 เท่าในช่วงเวลาจำกัด", + "go.graph.bonus": "ใช้งาน {{count}} เท่า", "nav.github": "GitHub", "nav.docs": "เอกสาร", "nav.changelog": "บันทึกการเปลี่ยนแปลง", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "ยกเลิก", "common.creating": "กำลังสร้าง...", "common.create": "สร้าง", + "common.contactUs": "ติดต่อเรา", "common.videoUnsupported": "เบราว์เซอร์ของคุณไม่รองรับแท็ก video", "common.figure": "รูปที่ {{n}}", @@ -225,6 +228,9 @@ export const dict = { "zen.faq.q8": "ฉันสามารถใช้ Zen กับเอเจนต์เขียนโค้ดอื่นได้หรือไม่?", "zen.faq.a8": "แม้ว่า Zen จะทำงานได้ดีเยี่ยมกับ OpenCode แต่คุณสามารถใช้ Zen กับเอเจนต์ใดก็ได้ เพียงทำตามคำแนะนำการตั้งค่าในเอเจนต์เขียนโค้ดที่คุณต้องการ", + "zen.faq.q9": "ฉันขอเงินคืนได้หรือไม่?", + "zen.faq.a9": + "หากมีการเรียกเก็บเงินภายใน 14 วันที่ผ่านมาและคุณยังไม่ได้ใช้เครดิตใดๆ จากการซื้อนั้น คุณอาจมีสิทธิ์ได้รับเงินคืน {{contact}}เพื่อขอเงินคืน", "zen.cta.start": "เริ่มต้นใช้งาน Zen", "zen.pricing.title": "เติมเงิน $20 แบบ Pay as you go", @@ -253,9 +259,8 @@ export const dict = { "zen.privacy.exceptionsLink": "ข้อยกเว้นดังนี้", "go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน", - "go.banner.text": "Ox Alpha Free พร้อมใช้งานบน Go ในช่วงเวลาจำกัด", "go.meta.description": - "Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้", + "Go มีราคา $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้", "go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน", "go.hero.body": "Go นำการเขียนโค้ดแบบเอเจนต์มาสู่นักเขียนโปรแกรมทั่วโลก เสนอขีดจำกัดที่กว้างขวางและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดได้อย่างน่าเชื่อถือ เพื่อให้คุณสามารถสร้างสรรค์ด้วยเอเจนต์ที่ทรงพลังโดยไม่ต้องกังวลเรื่องค่าใช้จ่ายหรือความพร้อมใช้งาน", @@ -264,12 +269,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "สมัครสมาชิก Go", "go.cta.price": "$10/เดือน", - "go.cta.promo": "$5 เดือนแรก", - "go.pricing.body": "ใช้กับเอเจนต์ใดก็ได้ $5 ในเดือนแรก จากนั้น $10/เดือน เติมเครดิตหากจำเป็น ยกเลิกได้ตลอดเวลา", + "go.pricing.body": "ใช้กับเอเจนต์ใดก็ได้ $10/เดือน เติมเครดิตหากจำเป็น ยกเลิกได้ตลอดเวลา", "go.graph.free": "ฟรี", "go.graph.freePill": "Big Pickle และโมเดลฟรี", "go.graph.go": "Go", "go.graph.label": "คำขอต่อ 5 ชั่วโมง", + "go.graph.period": "การใช้งาน", + "go.graph.model": "โมเดล", + "go.graph.requests": "requests โดยประมาณ / 5 ชม.", + "go.graph.allowance": "ปริมาณการใช้งานรายเดือน", + "go.graph.new": "ใหม่", + "go.graph.scale": "มาตราส่วนจำนวนคำขอแบบไม่เป็นเชิงเส้น", + "go.graph.showAll": "ดูโมเดลทั้งหมด {{count}} โมเดล", + "go.graph.showLess": "แสดงโมเดลน้อยลง", "go.graph.limitedRegions": "เฉพาะบางภูมิภาค", "go.graph.limitedTime": "ช่วงเวลาจำกัด", "go.graph.usageLimits": "ขีดจำกัดการใช้งาน", @@ -298,20 +310,20 @@ export const dict = { "go.testimonials.frank.quote": "ผมหวังว่าผมจะยังอยู่ที่ Nvidia", "go.problem.title": "Go แก้ปัญหาอะไร?", "go.problem.body": - "เรามุ่งมั่นที่จะนำประสบการณ์ OpenCode ไปสู่ผู้คนให้ได้มากที่สุด OpenCode Go เป็นการสมัครสมาชิกราคาประหยัด: $5 สำหรับเดือนแรก จากนั้น $10/เดือน โดยมอบขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดอย่างเชื่อถือได้", + "เรามุ่งมั่นที่จะนำประสบการณ์ OpenCode ไปสู่ผู้คนให้ได้มากที่สุด OpenCode Go เป็นการสมัครสมาชิกราคาประหยัด $10/เดือน โดยมอบขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดอย่างเชื่อถือได้", "go.problem.subtitle": " ", "go.problem.item1": "ราคาการสมัครสมาชิกที่ต่ำ", "go.problem.item2": "ขีดจำกัดที่กว้างขวางและการเข้าถึงที่เชื่อถือได้", "go.problem.item3": "สร้างขึ้นเพื่อโปรแกรมเมอร์จำนวนมากที่สุดเท่าที่จะเป็นไปได้", "go.problem.item4": "ชุดโมเดลที่คัดสรรและผ่านการทดสอบสำหรับการเขียนโค้ดแบบเอเจนต์", "go.how.title": "Go ทำงานอย่างไร", - "go.how.body": "Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน คุณสามารถใช้กับ OpenCode หรือเอเจนต์ใดก็ได้", + "go.how.body": "Go มีราคา $10/เดือน คุณสามารถใช้กับ OpenCode หรือเอเจนต์ใดก็ได้", "go.how.step1.title": "สร้างบัญชี", "go.how.step1.beforeLink": "ทำตาม", "go.how.step1.link": "คำแนะนำการตั้งค่า", "go.how.step2.title": "สมัครสมาชิก Go", - "go.how.step2.link": "$5 เดือนแรก", - "go.how.step2.afterLink": "จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อ", + "go.how.step2.link": "$10/เดือน", + "go.how.step2.afterLink": "พร้อมขีดจำกัดที่เอื้อเฟื้อ", "go.how.step3.title": "เริ่มเขียนโค้ด", "go.how.step3.body": "ด้วยการเข้าถึงโมเดลโอเพนซอร์สที่เชื่อถือได้", "go.privacy.title": "ความเป็นส่วนตัวของคุณสำคัญสำหรับเรา", @@ -328,11 +340,11 @@ export const dict = { "go.faq.a2": "Go รวมโมเดลด้านล่างนี้ พร้อมขีดจำกัดที่มากและการเข้าถึงที่เชื่อถือได้", "go.faq.q3": "Go เหมือนกับ Zen หรือไม่?", "go.faq.a3": - "ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ขณะที่ Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงชุดโมเดลที่คัดสรรอย่างเชื่อถือได้", + "ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ขณะที่ Go มีราคา $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงชุดโมเดลที่คัดสรรอย่างเชื่อถือได้", "go.faq.q4": "Go ราคาเท่าไหร่?", "go.faq.a4.p1.beforePricing": "Go ราคา", - "go.faq.a4.p1.pricingLink": "$5 เดือนแรก", - "go.faq.a4.p1.afterPricing": "จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อ", + "go.faq.a4.p1.pricingLink": "$10/เดือน", + "go.faq.a4.p1.afterPricing": "พร้อมขีดจำกัดที่เอื้อเฟื้อ", "go.faq.a4.p2.beforeAccount": "คุณสามารถจัดการการสมัครสมาชิกของคุณได้ใน", "go.faq.a4.p2.accountLink": "บัญชีของคุณ", "go.faq.a4.p3": "ยกเลิกได้ตลอดเวลา", @@ -353,7 +365,7 @@ export const dict = { "go.faq.a5.museRetention": "ราคาของ token ลดลงอย่างมาก โดยแลกกับการอนุญาตให้นำพรอมต์และผลลัพธ์ที่สร้างขึ้นของคุณไปใช้ฝึกโมเดล Meta ในอนาคต", "go.faq.a5.learnMore": "ดูข้อมูลเพิ่มเติม", - "go.faq.a5.deepseekRetention": "ข้อตกลง ZDR จะต่ออายุทุกเดือน ข้อตกลงปัจจุบันมีผลใช้ถึงวันที่ 31 สิงหาคม 2026", + "go.faq.a5.deepseekRetention": "ข้อตกลง ZDR จะต่ออายุทุกเดือน ข้อตกลงปัจจุบันมีผลใช้ถึงวันที่ 30 กันยายน 2026", "go.faq.a5.beforeExceptions": "โมเดล Go โฮสต์ในสหรัฐอเมริกา ผู้ให้บริการปฏิบัติตามนโยบายไม่เก็บรักษาข้อมูล (zero-retention policy) และไม่ใช้ข้อมูลของคุณสำหรับการฝึกโมเดล โดยมี", @@ -367,7 +379,10 @@ export const dict = { "go.faq.q9": "ความแตกต่างระหว่างโมเดลฟรีและ Go คืออะไร?", "go.faq.a9": - "โมเดลฟรีประกอบด้วย Big Pickle และโมเดลโปรโมชันที่มีให้บริการในขณะนั้น โดยมีโควตา 200 คำขอ/วัน Go นำเสนอชุดโมเดลที่คัดสรร พร้อมโควตาคำขอที่สูงกว่าซึ่งบังคับใช้ตามกรอบเวลาแบบต่อเนื่อง (5 ชั่วโมง, รายสัปดาห์ และรายเดือน) เทียบเท่าประมาณ $12 ต่อ 5 ชั่วโมง, $30 ต่อสัปดาห์ และ $60 ต่อเดือน (จำนวนคำขอจริงแตกต่างกันไปตามโมเดลและการใช้งาน)", + "โมเดลฟรีประกอบด้วย Big Pickle และโมเดลโปรโมชันที่มีให้บริการในขณะนั้น โดยมีโควตา 200 คำขอ/วัน Go นำเสนอชุดโมเดลที่คัดสรร พร้อมโควตาคำขอที่สูงกว่าซึ่งบังคับใช้ตามกรอบเวลาแบบต่อเนื่อง (5 ชั่วโมง, รายสัปดาห์ และรายเดือน) เทียบเท่าโควตาพื้นฐานประมาณ $12 ต่อ 5 ชั่วโมง, $30 ต่อสัปดาห์ และ $60 ต่อเดือน โดยโควตาเฉพาะอาจแตกต่างกันไปตามโมเดล (จำนวนคำขอจริงแตกต่างกันไปตามโมเดลและการใช้งาน)", + "go.faq.q10": "ฉันขอเงินคืนได้หรือไม่?", + "go.faq.a10": + "หากมีการเรียกเก็บเงินภายใน 14 วันที่ผ่านมาและคุณยังไม่ได้ใช้สิทธิ์การใช้งาน Go เลยในรอบการเรียกเก็บเงินนั้น คุณอาจมีสิทธิ์ได้รับเงินคืน {{contact}}เพื่อขอเงินคืน", "zen.api.error.rateLimitExceeded": "เกินขีดจำกัดอัตราการใช้งาน กรุณาลองใหม่ในภายหลัง", "zen.api.error.modelNotSupported": "ไม่รองรับโมเดล {{model}}", @@ -668,10 +683,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "ไม่กี่วินาที", "workspace.lite.subscription.message": "คุณได้สมัครสมาชิก OpenCode Go แล้ว", "workspace.lite.subscription.manage": "จัดการการสมัครสมาชิก", - "workspace.lite.subscription.rollingUsage": "การใช้งานแบบหมุนเวียน", + "workspace.lite.subscription.rollingUsage": "การใช้งานใน 5 ชั่วโมง", + "workspace.lite.subscription.rollingQuota": "โควตา 5 ชั่วโมง", "workspace.lite.subscription.weeklyUsage": "การใช้งานรายสัปดาห์", + "workspace.lite.subscription.weeklyQuota": "โควตารายสัปดาห์", "workspace.lite.subscription.monthlyUsage": "การใช้งานรายเดือน", + "workspace.lite.subscription.monthlyQuota": "โควตารายเดือน", "workspace.lite.subscription.resetsIn": "รีเซ็ตใน", + "workspace.lite.subscription.showDetails": "แสดงรายละเอียด", + "workspace.lite.subscription.hideDetails": "ซ่อนรายละเอียด", + "workspace.lite.subscription.model": "โมเดล", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "รวม", "workspace.lite.subscription.useBalance": "ใช้ยอดคงเหลือของคุณหลังจากถึงขีดจำกัดการใช้งาน", "workspace.lite.subscription.selectProvider": 'เลือก "OpenCode Go" เป็นผู้ให้บริการในการตั้งค่า opencode ของคุณเพื่อใช้โมเดล Go', @@ -684,8 +707,8 @@ export const dict = { "workspace.lite.other.message": "สมาชิกคนอื่นใน Workspace นี้ได้สมัคร OpenCode Go แล้ว สามารถสมัครได้เพียงหนึ่งคนต่อหนึ่ง Workspace เท่านั้น", "workspace.lite.promo.description": - "OpenCode Go เริ่มต้นที่ {{price}} จากนั้น $10/เดือน และมอบการเข้าถึงโมเดลการเขียนโค้ดแบบเปิดยอดนิยมอย่างเสถียรพร้อมขีดจำกัดการใช้งานที่ให้มาอย่างเหลือเฟือ", - "workspace.lite.promo.price": "$5 สำหรับเดือนแรก", + "OpenCode Go ราคา {{price}} และมอบการเข้าถึงโมเดลการเขียนโค้ดแบบเปิดยอดนิยมอย่างเสถียรพร้อมขีดจำกัดการใช้งานที่ให้มาอย่างเหลือเฟือ", + "workspace.lite.promo.price": "$10/เดือน", "workspace.lite.promo.modelsTitle": "สิ่งที่รวมอยู่ด้วย", "workspace.lite.promo.footer": "แผนนี้ออกแบบมาสำหรับผู้ใช้งานต่างประเทศเป็นหลักและให้การเข้าถึงที่เสถียรทั่วโลก ราคาและขีดจำกัดการใช้งานอาจเปลี่ยนแปลงได้ตามสิ่งที่เราเรียนรู้จากการใช้งานและข้อเสนอแนะในช่วงแรก", diff --git a/packages/console/app/src/i18n/tr.ts b/packages/console/app/src/i18n/tr.ts index 3942034935e0..4289706cc66e 100644 --- a/packages/console/app/src/i18n/tr.ts +++ b/packages/console/app/src/i18n/tr.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash sınırlı bir süre için 4x kullanım limiti sunuyor", + "go.graph.bonus": "{{count}}× kullanım", "nav.github": "GitHub", "nav.docs": "Dokümantasyon", "nav.changelog": "Değişiklik günlüğü", @@ -58,6 +60,7 @@ export const dict = { "common.cancel": "İptal", "common.creating": "Oluşturuluyor...", "common.create": "Oluştur", + "common.contactUs": "Bize ulaşın", "common.videoUnsupported": "Tarayıcınız video etiketini desteklemiyor.", "common.figure": "Şekil {{n}}.", @@ -227,6 +230,9 @@ export const dict = { "zen.faq.q8": "Zen'i diğer kodlama ajanlarıyla kullanabilir miyim?", "zen.faq.a8": "Zen OpenCode ile harika çalışır, ama Zen'i herhangi bir ajan ile kullanabilirsiniz. Tercih ettiğiniz kodlama ajanında kurulum talimatlarını izleyin.", + "zen.faq.q9": "Para iadesi alabilir miyim?", + "zen.faq.a9": + "Ücret son 14 gün içinde tahsil edildiyse ve bu satın alımdan gelen kredilerin hiçbirini kullanmadıysanız para iadesine hak kazanabilirsiniz. {{contact}} ve para iadesi talep edin.", "zen.cta.start": "Zen'i kullanmaya başlayın", "zen.pricing.title": "20$ Kullandıkça öde bakiyesi ekle", @@ -256,9 +262,8 @@ export const dict = { "zen.privacy.exceptionsLink": "aşağıdaki istisnalar", "go.title": "OpenCode Go | Herkes için düşük maliyetli kodlama modelleri", - "go.banner.text": "Ox Alpha Free sınırlı bir süre için Go'da kullanılabilir", "go.meta.description": - "Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; cömert kullanım limitleri ve önde gelen kodlama modellerine güvenilir erişim sunar.", + "Go ayda 10$'dır; cömert kullanım limitleri ve önde gelen kodlama modellerine güvenilir erişim sunar.", "go.hero.title": "Herkes için düşük maliyetli kodlama modelleri", "go.hero.body": "Go, dünya çapındaki programcılara ajan tabanlı kodlama getiriyor. En yetenekli açık kaynaklı modellere cömert limitler ve güvenilir erişim sunarak, maliyet veya erişilebilirlik konusunda endişelenmeden güçlü ajanlarla geliştirme yapmanızı sağlar.", @@ -267,13 +272,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Go'ya abone ol", "go.cta.price": "Ayda 10$", - "go.cta.promo": "İlk ay $5", - "go.pricing.body": - "Herhangi bir ajanla kullanın. İlk ay $5, sonrasında ayda 10$. Gerekirse kredi yükleyin. İstediğiniz zaman iptal edin.", + "go.pricing.body": "Herhangi bir ajanla kullanın. Ayda 10$. Gerekirse kredi yükleyin. İstediğiniz zaman iptal edin.", "go.graph.free": "Ücretsiz", "go.graph.freePill": "Big Pickle ve ücretsiz modeller", "go.graph.go": "Go", "go.graph.label": "5 saat başına istekler", + "go.graph.period": "Kullanım", + "go.graph.model": "Model", + "go.graph.requests": "Tahmini istek / 5 saat", + "go.graph.allowance": "Aylık kullanım", + "go.graph.new": "Yeni", + "go.graph.scale": "Doğrusal olmayan istek ölçeği", + "go.graph.showAll": "{{count}} modelin tümünü göster", + "go.graph.showLess": "Daha az model göster", "go.graph.limitedRegions": "sınırlı bölgeler", "go.graph.limitedTime": "sınırlı süre", "go.graph.usageLimits": "Kullanım limitleri", @@ -303,21 +314,20 @@ export const dict = { "go.testimonials.frank.quote": "Keşke hala Nvidia'da olsaydım.", "go.problem.title": "Go hangi sorunu çözüyor?", "go.problem.body": - "OpenCode deneyimini mümkün olduğunca çok kişiye ulaştırmaya odaklandık. OpenCode Go düşük maliyetli bir aboneliktir: İlk ay $5, sonrasında ayda 10$. Cömert limitler ve en yetenekli açık kaynak modellere güvenilir erişim sağlar.", + "OpenCode deneyimini mümkün olduğunca çok kişiye ulaştırmaya odaklandık. OpenCode Go, ayda 10$ olan düşük maliyetli bir aboneliktir. Cömert limitler ve en yetenekli açık kaynak modellere güvenilir erişim sağlar.", "go.problem.subtitle": " ", "go.problem.item1": "Düşük maliyetli abonelik fiyatlandırması", "go.problem.item2": "Cömert limitler ve güvenilir erişim", "go.problem.item3": "Mümkün olduğunca çok programcı için geliştirildi", "go.problem.item4": "Ajan tabanlı kodlama için test edilmiş, özenle seçilmiş model seçenekleri", "go.how.title": "Go nasıl çalışır?", - "go.how.body": - "Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar. OpenCode veya herhangi bir ajanla kullanabilirsiniz.", + "go.how.body": "Go ayda 10$'dır. OpenCode veya herhangi bir ajanla kullanabilirsiniz.", "go.how.step1.title": "Bir hesap oluşturun", "go.how.step1.beforeLink": "takip edin", "go.how.step1.link": "kurulum talimatları", "go.how.step2.title": "Go'ya abone olun", - "go.how.step2.link": "İlk ay $5", - "go.how.step2.afterLink": "sonrasında cömert limitlerle ayda 10$", + "go.how.step2.link": "Ayda 10$", + "go.how.step2.afterLink": "cömert limitlerle", "go.how.step3.title": "Kodlamaya başlayın", "go.how.step3.body": "açık kaynaklı modellere güvenilir erişimle", "go.privacy.title": "Gizliliğiniz bizim için önemlidir", @@ -334,11 +344,11 @@ export const dict = { "go.faq.a2": "Go, aşağıda listelenen modelleri cömert limitler ve güvenilir erişimle sunar.", "go.faq.q3": "Go, Zen ile aynı mı?", "go.faq.a3": - "Hayır. Zen kullandıkça öde modelidir; Go ise ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar ve özenle seçilmiş model seçeneklerine cömert limitlerle güvenilir erişim sunar.", + "Hayır. Zen kullandıkça öde modelidir; Go ise ayda 10$'dır ve özenle seçilmiş model seçeneklerine cömert limitlerle güvenilir erişim sunar.", "go.faq.q4": "Go ne kadar?", "go.faq.a4.p1.beforePricing": "Go'nun maliyeti", - "go.faq.a4.p1.pricingLink": "İlk ay $5", - "go.faq.a4.p1.afterPricing": "sonrasında cömert limitlerle ayda 10$.", + "go.faq.a4.p1.pricingLink": "ayda 10$", + "go.faq.a4.p1.afterPricing": "cömert limitlerle.", "go.faq.a4.p2.beforeAccount": "Aboneliğinizi", "go.faq.a4.p2.accountLink": "hesabınızdan", "go.faq.a4.p3": "yönetebilirsiniz. İstediğiniz zaman iptal edin.", @@ -358,7 +368,7 @@ export const dict = { "Tüm API özelliklerinin kullanımı için kötüye kullanım izleme günlükleri oluşturulur ve 30 güne kadar saklanır.", "go.faq.a5.learnMore": "Daha fazla bilgi", "go.faq.a5.deepseekRetention": - "ZDR anlaşması aylık olarak yenilenir. Mevcut anlaşma 31 Ağustos 2026 tarihine kadar geçerlidir.", + "ZDR anlaşması aylık olarak yenilenir. Mevcut anlaşma 30 Eylül 2026 tarihine kadar geçerlidir.", "go.faq.a5.museRetention": "İstemlerinizi ve tamamlamalarınızı gelecekteki Meta modellerini eğitmek için kullanma izni karşılığında büyük ölçüde indirimli token fiyatları.", @@ -375,7 +385,10 @@ export const dict = { "go.faq.q9": "Ücretsiz modeller ve Go arasındaki fark nedir?", "go.faq.a9": - "Ücretsiz modeller, günlük 200 istek kotasıyla Big Pickle'ı ve o sırada mevcut olan promosyonel modelleri içerir. Go ise kayan zaman aralıklarında (5 saatlik, haftalık ve aylık) uygulanan daha yüksek istek kotalarıyla özenle seçilmiş model seçenekleri sunar. Bu kotalar kabaca her 5 saatte 12$, haftada 30$ ve ayda 60$ değerine eşdeğerdir (gerçek istek sayıları modele ve kullanıma göre değişir).", + "Ücretsiz modeller, günlük 200 istek kotasıyla Big Pickle'ı ve o sırada mevcut olan promosyonel modelleri içerir. Go ise kayan zaman aralıklarında (5 saatlik, haftalık ve aylık) uygulanan daha yüksek istek kotalarıyla özenle seçilmiş model seçenekleri sunar. Bu kotalar kabaca her 5 saatte 12$, haftada 30$ ve ayda 60$ değerindeki temel kullanım haklarına eşdeğerdir; modele özgü kullanım hakları farklılık gösterebilir (gerçek istek sayıları modele ve kullanıma göre değişir).", + "go.faq.q10": "Para iadesi alabilir miyim?", + "go.faq.a10": + "Ücret son 14 gün içinde tahsil edildiyse ve ilgili faturalandırma döneminde Go kullanım hakkınızı hiç kullanmadıysanız para iadesine hak kazanabilirsiniz. {{contact}} ve para iadesi talep edin.", "zen.api.error.rateLimitExceeded": "İstek limiti aşıldı. Lütfen daha sonra tekrar deneyin.", "zen.api.error.modelNotSupported": "{{model}} modeli desteklenmiyor", @@ -676,10 +689,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "birkaç saniye", "workspace.lite.subscription.message": "OpenCode Go abonesisiniz.", "workspace.lite.subscription.manage": "Aboneliği Yönet", - "workspace.lite.subscription.rollingUsage": "Devam Eden Kullanım", + "workspace.lite.subscription.rollingUsage": "5 Saatlik Kullanım", + "workspace.lite.subscription.rollingQuota": "5 Saatlik Kota", "workspace.lite.subscription.weeklyUsage": "Haftalık Kullanım", + "workspace.lite.subscription.weeklyQuota": "Haftalık Kota", "workspace.lite.subscription.monthlyUsage": "Aylık Kullanım", + "workspace.lite.subscription.monthlyQuota": "Aylık Kota", "workspace.lite.subscription.resetsIn": "Sıfırlama süresi", + "workspace.lite.subscription.showDetails": "Ayrıntıları göster", + "workspace.lite.subscription.hideDetails": "Ayrıntıları gizle", + "workspace.lite.subscription.model": "Model", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Toplam", "workspace.lite.subscription.useBalance": "Kullanım limitlerine ulaştıktan sonra mevcut bakiyenizi kullanın", "workspace.lite.subscription.selectProvider": 'Go modellerini kullanmak için opencode yapılandırmanızda "OpenCode Go"\'yu sağlayıcı olarak seçin.', @@ -692,8 +713,8 @@ export const dict = { "workspace.lite.other.message": "Bu çalışma alanındaki başka bir üye zaten OpenCode Go abonesi. Çalışma alanı başına yalnızca bir üye abone olabilir.", "workspace.lite.promo.description": - "OpenCode Go {{price}} fiyatından başlar, sonrasında ayda 10$ olur ve cömert kullanım limitleriyle popüler açık kodlama modellerine güvenilir erişim sağlar.", - "workspace.lite.promo.price": "İlk ay $5", + "OpenCode Go {{price}} fiyatıyla cömert kullanım limitleri ve popüler açık kodlama modellerine güvenilir erişim sağlar.", + "workspace.lite.promo.price": "Ayda 10$", "workspace.lite.promo.modelsTitle": "Neler Dahil", "workspace.lite.promo.footer": "Plan öncelikle uluslararası kullanıcılar için tasarlanmıştır ve istikrarlı küresel erişim sağlar. Erken kullanım ve geri bildirimlerden öğrendiklerimiz doğrultusunda fiyatlandırma ve kullanım limitleri değişebilir.", diff --git a/packages/console/app/src/i18n/uk.ts b/packages/console/app/src/i18n/uk.ts index 587b3a133e27..4efac1d76762 100644 --- a/packages/console/app/src/i18n/uk.ts +++ b/packages/console/app/src/i18n/uk.ts @@ -2,6 +2,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash отримує 4x ліміти використання протягом обмеженого часу", + "go.graph.bonus": "Ліміт ×{{count}}", "nav.github": "GitHub", "nav.docs": "Документація", "nav.changelog": "Журнал змін", @@ -226,6 +228,9 @@ export const dict = { "zen.faq.q8": "Чи можна використовувати Zen з іншими агентами кодування?", "zen.faq.a8": "Хоча Zen чудово працює з OpenCode, ви можете використовувати Zen з будь-яким агентом. Дотримуйтесь інструкцій з налаштування у вашому агенті.", + "zen.faq.q9": "Чи можу я отримати повернення коштів?", + "zen.faq.a9": + "Ви можете претендувати на повернення, якщо кошти було списано протягом останніх 14 днів і ви не використали кошти, отримані внаслідок цієї покупки. {{contact}}, щоб подати запит на повернення.", "zen.cta.start": "Почати з Zen", "zen.pricing.title": "Додати $20 балансу Pay as you go", @@ -255,9 +260,8 @@ export const dict = { "zen.privacy.exceptionsLink": "такими винятками", "go.title": "OpenCode Go | Недорогі моделі кодування для всіх", - "go.banner.text": "Ox Alpha Free доступна в Go протягом обмеженого часу", "go.meta.description": - "Go починається від $5 за перший місяць, потім $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.", + "Go коштує $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.", "go.hero.title": "Недорогі моделі кодування для всіх", "go.hero.body": "Go надає агентне програмування програмістам у всьому світі, пропонуючи щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.", @@ -266,13 +270,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "Підписатися на Go", "go.cta.price": "$10/місяць", - "go.cta.promo": "$5 перший місяць", - "go.pricing.body": - "Використовуйте з будь-яким агентом. $5 перший місяць, потім $10/місяць. Поповнюйте за потреби. Скасуйте в будь-який час.", + "go.pricing.body": "Використовуйте з будь-яким агентом. $10/місяць. Поповнюйте за потреби. Скасуйте в будь-який час.", "go.graph.free": "Безкоштовно", "go.graph.freePill": "Big Pickle та безкоштовні моделі", "go.graph.go": "Go", "go.graph.label": "Запитів за 5 годин", + "go.graph.period": "Використання", + "go.graph.model": "Модель", + "go.graph.requests": "Оцінка запитів / 5 год", + "go.graph.allowance": "Використання за місяць", + "go.graph.new": "Нове", + "go.graph.scale": "Нелінійна шкала кількості запитів", + "go.graph.showAll": "Показати всі моделі ({{count}})", + "go.graph.showLess": "Показати менше моделей", "go.graph.limitedRegions": "обмежені регіони", "go.graph.limitedTime": "обмежений час", "go.graph.usageLimits": "Ліміти використання", @@ -301,21 +311,20 @@ export const dict = { "go.testimonials.frank.quote": "Хотів би я досі бути в Nvidia.", "go.problem.title": "Яку проблему вирішує Go?", "go.problem.body": - "Ми зосереджені на тому, щоб зробити досвід OpenCode доступним для якомога більшої кількості людей. OpenCode Go — це недорога підписка: $5 за перший місяць, потім $10/місяць. Вона надає щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.", + "Ми зосереджені на тому, щоб зробити досвід OpenCode доступним для якомога більшої кількості людей. OpenCode Go — це недорога підписка за $10/місяць. Вона надає щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.", "go.problem.subtitle": " ", "go.problem.item1": "Недорога підписка", "go.problem.item2": "Щедрі ліміти та надійний доступ", "go.problem.item3": "Створено для якомога більшої кількості програмістів", "go.problem.item4": "Добірка моделей, протестованих для агентного кодування", "go.how.title": "Як працює Go", - "go.how.body": - "Go починається від $5 за перший місяць, потім $10/місяць. Використовуйте з OpenCode або будь-яким агентом.", + "go.how.body": "Go коштує $10/місяць. Використовуйте з OpenCode або будь-яким агентом.", "go.how.step1.title": "Створіть обліковий запис", "go.how.step1.beforeLink": "дотримуйтесь", "go.how.step1.link": "інструкцій з налаштування", "go.how.step2.title": "Підпишіться на Go", - "go.how.step2.link": "$5 перший місяць", - "go.how.step2.afterLink": "потім $10/місяць із щедрими лімітами", + "go.how.step2.link": "$10/місяць", + "go.how.step2.afterLink": "із щедрими лімітами", "go.how.step3.title": "Почніть кодувати", "go.how.step3.body": "з надійним доступом до моделей з відкритим кодом", "go.privacy.title": "Ваша конфіденційність важлива для нас", @@ -332,11 +341,11 @@ export const dict = { "go.faq.a2": "Go включає моделі, перелічені нижче, із щедрими лімітами та надійним доступом.", "go.faq.q3": "Чи Go те саме, що Zen?", "go.faq.a3": - "Ні. Zen — це плата за використання, тоді як Go починається від $5 за перший місяць, потім $10/місяць, із щедрими лімітами та надійним доступом до добірки моделей.", + "Ні. Zen — це плата за використання, тоді як Go коштує $10/місяць, із щедрими лімітами та надійним доступом до добірки моделей.", "go.faq.q4": "Скільки коштує Go?", "go.faq.a4.p1.beforePricing": "Go коштує", - "go.faq.a4.p1.pricingLink": "$5 за перший місяць", - "go.faq.a4.p1.afterPricing": "потім $10/місяць із щедрими лімітами.", + "go.faq.a4.p1.pricingLink": "$10/місяць", + "go.faq.a4.p1.afterPricing": "із щедрими лімітами.", "go.faq.a4.p2.beforeAccount": "Ви можете керувати підпискою в", "go.faq.a4.p2.accountLink": "обліковому записі", "go.faq.a4.p3": "Скасуйте в будь-який час.", @@ -355,7 +364,7 @@ export const dict = { "go.faq.a5.gptRetention": "Журнали моніторингу зловживань створюються для всіх випадків використання функцій API та зберігаються до 30 днів.", "go.faq.a5.learnMore": "Докладніше", - "go.faq.a5.deepseekRetention": "Угода ZDR поновлюється щомісяця. Поточна угода дійсна до 31 серпня 2026 року.", + "go.faq.a5.deepseekRetention": "Угода ZDR поновлюється щомісяця. Поточна угода дійсна до 30 вересня 2026 року.", "go.faq.a5.museRetention": "Значно знижені ціни на токени в обмін на дозвіл використовувати ваші промпти та відповіді для навчання майбутніх моделей Meta.", @@ -371,7 +380,10 @@ export const dict = { "go.faq.q9": "Яка різниця між безкоштовними моделями та Go?", "go.faq.a9": - "Безкоштовні моделі включають Big Pickle та доступні на той момент акційні моделі з квотою 200 запитів/день. Go пропонує добірку моделей із вищими квотами запитів, що застосовуються протягом ковзних періодів (5 годин, тижня та місяця), приблизно еквівалентними $12 за 5 годин, $30 на тиждень і $60 на місяць (фактична кількість запитів залежить від моделі та використання).", + "Безкоштовні моделі включають Big Pickle та доступні на той момент акційні моделі з квотою 200 запитів/день. Go пропонує добірку моделей із вищими квотами запитів, що застосовуються протягом ковзних періодів (5 годин, тижня та місяця), приблизно еквівалентними базовим лімітам $12 за 5 годин, $30 на тиждень і $60 на місяць; ліміти для окремих моделей можуть відрізнятися (фактична кількість запитів залежить від моделі та використання).", + "go.faq.q10": "Чи можу я отримати повернення коштів?", + "go.faq.a10": + "Ви можете претендувати на повернення, якщо кошти було списано протягом останніх 14 днів і ви не використали ліміт Go протягом цього розрахункового періоду. {{contact}}, щоб подати запит на повернення.", "zen.api.error.rateLimitExceeded": "Перевищено ліміт запитів. Спробуйте пізніше.", "zen.api.error.modelNotSupported": "Модель {{model}} не підтримується", @@ -672,10 +684,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "кілька секунд", "workspace.lite.subscription.message": "Ви підписані на OpenCode Go.", "workspace.lite.subscription.manage": "Керувати підпискою", - "workspace.lite.subscription.rollingUsage": "Ковзне використання", + "workspace.lite.subscription.rollingUsage": "Використання за 5 годин", + "workspace.lite.subscription.rollingQuota": "Квота на 5 годин", "workspace.lite.subscription.weeklyUsage": "Тижневе використання", + "workspace.lite.subscription.weeklyQuota": "Тижнева квота", "workspace.lite.subscription.monthlyUsage": "Місячне використання", + "workspace.lite.subscription.monthlyQuota": "Місячна квота", "workspace.lite.subscription.resetsIn": "Скидається через", + "workspace.lite.subscription.showDetails": "Показати подробиці", + "workspace.lite.subscription.hideDetails": "Приховати подробиці", + "workspace.lite.subscription.model": "Модель", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "Усього", "workspace.lite.subscription.useBalance": "Використовуйте доступний баланс після досягнення лімітів", "workspace.lite.subscription.selectProvider": 'Виберіть "OpenCode Go" як провайдера в конфігурації opencode.', "workspace.lite.providers.title": "Провайдери", @@ -685,8 +705,8 @@ export const dict = { "workspace.lite.black.message": "Ви вже підписані на OpenCode Black або в списку очікування. Спочатку скасуйте підписку, якщо хочете перейти на Go.", "workspace.lite.other.message": "Інший учасник цього робочого простору вже підписаний на OpenCode Go.", - "workspace.lite.promo.description": "OpenCode Go починається від {{price}}, потім $10/місяць, із щедрими лімітами.", - "workspace.lite.promo.price": "$5 за перший місяць", + "workspace.lite.promo.description": "OpenCode Go коштує {{price}} і має щедрі ліміти.", + "workspace.lite.promo.price": "$10/місяць", "workspace.lite.promo.modelsTitle": "Що включено", "workspace.lite.promo.footer": "План призначений насамперед для міжнародних користувачів і забезпечує стабільний глобальний доступ. Ціни та ліміти використання можуть змінюватися з урахуванням перших даних про використання та відгуків.", diff --git a/packages/console/app/src/i18n/zh.ts b/packages/console/app/src/i18n/zh.ts index 5ffc220cc965..8c1aff6f6083 100644 --- a/packages/console/app/src/i18n/zh.ts +++ b/packages/console/app/src/i18n/zh.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash 限时享受 4 倍使用额度", + "go.graph.bonus": "{{count}} 倍用量", "nav.github": "GitHub", "nav.docs": "文档", "nav.changelog": "更新日志", @@ -59,6 +61,7 @@ export const dict = { "common.cancel": "取消", "common.creating": "正在创建...", "common.create": "创建", + "common.contactUs": "联系我们", "common.videoUnsupported": "您的浏览器不支持 video 标签。", "common.figure": "图 {{n}}.", @@ -218,6 +221,9 @@ export const dict = { "zen.faq.q8": "我可以在其他编程代理中使用 Zen 吗?", "zen.faq.a8": "虽然 Zen 与 OpenCode 配合效果极佳,但您可以在任何代理中使用 Zen。请按照您首选编程代理中的设置说明进行操作。", + "zen.faq.q9": "我可以退款吗?", + "zen.faq.a9": + "如果扣款发生在过去 14 天内,并且您尚未使用该次购买的任何额度,您可能符合退款条件。请{{contact}}申请退款。", "zen.cta.start": "开始使用 Zen", "zen.pricing.title": "充值 $20 (即用即付)", @@ -244,8 +250,7 @@ export const dict = { "zen.privacy.exceptionsLink": "以下例外情况除外", "go.title": "OpenCode Go | 人人可用的低成本编程模型", - "go.banner.text": "Ox Alpha Free 限时加入 Go", - "go.meta.description": "Go 首月 $5,之后 $10/月,提供充裕的使用限额,并可可靠访问领先的编程模型。", + "go.meta.description": "Go 每月 $10,提供充裕的使用限额,并可可靠访问领先的编程模型。", "go.hero.title": "人人可用的低成本编程模型", "go.hero.body": "Go 将代理编程带给全世界的程序员。提供充裕的限额和对最强大的开源模型的可靠访问,让您可以利用强大的代理进行构建,而无需担心成本或可用性。", @@ -254,12 +259,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "订阅 Go", "go.cta.price": "$10/月", - "go.cta.promo": "首月 $5", - "go.pricing.body": "可配合任何代理使用。首月 $5,之后 $10/月。如有需要可充值。随时取消。", + "go.pricing.body": "可配合任何代理使用。每月 $10。如有需要可充值。随时取消。", "go.graph.free": "免费", "go.graph.freePill": "Big Pickle 和免费模型", "go.graph.go": "Go", "go.graph.label": "每 5 小时请求数", + "go.graph.period": "使用额度", + "go.graph.model": "模型", + "go.graph.requests": "预估请求数 / 5 小时", + "go.graph.allowance": "每月使用额度", + "go.graph.new": "新", + "go.graph.scale": "请求数采用非线性刻度", + "go.graph.showAll": "查看全部 {{count}} 个模型", + "go.graph.showLess": "收起模型", "go.graph.limitedRegions": "仅限部分地区", "go.graph.limitedTime": "限时", "go.graph.usageLimits": "使用限制", @@ -288,20 +300,20 @@ export const dict = { "go.testimonials.frank.quote": "我希望我还在 Nvidia。", "go.problem.title": "Go 解决了什么问题?", "go.problem.body": - "我们致力于将 OpenCode 体验带给尽可能多的人。OpenCode Go 是一款低成本订阅服务:首月 $5,之后 $10/月。它提供充裕的额度,并让您能可靠地使用最强大的开源模型。", + "我们致力于将 OpenCode 体验带给尽可能多的人。OpenCode Go 是一款每月 $10 的低成本订阅服务。它提供充裕的额度,并让您能可靠地使用最强大的开源模型。", "go.problem.subtitle": " ", "go.problem.item1": "低成本订阅定价", "go.problem.item2": "充裕的限额和可靠的访问", "go.problem.item3": "为尽可能多的程序员打造", "go.problem.item4": "经过代理编程测试的精选模型阵容", "go.how.title": "Go 如何工作", - "go.how.body": "Go 起价为首月 $5,之后 $10/月。您可以将其与 OpenCode 或任何代理搭配使用。", + "go.how.body": "Go 每月 $10。您可以将其与 OpenCode 或任何代理搭配使用。", "go.how.step1.title": "创建账户", "go.how.step1.beforeLink": "遵循", "go.how.step1.link": "设置说明", "go.how.step2.title": "订阅 Go", - "go.how.step2.link": "首月 $5", - "go.how.step2.afterLink": "之后 $10/月,额度充裕", + "go.how.step2.link": "每月 $10", + "go.how.step2.afterLink": "额度充裕", "go.how.step3.title": "开始编程", "go.how.step3.body": "可靠访问开源模型", "go.privacy.title": "您的隐私对我们很重要", @@ -314,11 +326,11 @@ export const dict = { "go.faq.q2": "Go 包含哪些模型?", "go.faq.a2": "Go 包含下方列出的模型,提供充足的限额和可靠的访问。", "go.faq.q3": "Go 和 Zen 一样吗?", - "go.faq.a3": "不。Zen 是按量付费,而 Go 首月 $5,之后 $10/月,提供充裕的限额,并可可靠访问精选模型阵容。", + "go.faq.a3": "不。Zen 是按量付费,而 Go 每月 $10,提供充裕的限额,并可可靠访问精选模型阵容。", "go.faq.q4": "Go 多少钱?", "go.faq.a4.p1.beforePricing": "Go 费用为", - "go.faq.a4.p1.pricingLink": "首月 $5", - "go.faq.a4.p1.afterPricing": "之后 $10/月,额度充裕。", + "go.faq.a4.p1.pricingLink": "每月 $10", + "go.faq.a4.p1.afterPricing": "额度充裕。", "go.faq.a4.p2.beforeAccount": "您可以在您的", "go.faq.a4.p2.accountLink": "账户", "go.faq.a4.p3": "中管理订阅。随时取消。", @@ -337,7 +349,7 @@ export const dict = { "go.faq.a5.gptRetention": "所有 API 功能的使用都会生成滥用监控日志,并最多保留 30 天。", "go.faq.a5.museRetention": "以允许使用你的提示词和补全结果训练未来的 Meta 模型为交换,token 价格可获得大幅折扣。", "go.faq.a5.learnMore": "了解更多", - "go.faq.a5.deepseekRetention": "ZDR 协议每月续签。当前协议有效期至 2026 年 8 月 31 日。", + "go.faq.a5.deepseekRetention": "ZDR 协议每月续签。当前协议有效期至 2026 年 9 月 30 日。", "go.faq.a5.beforeExceptions": "Go 模型托管在美国。提供商遵循零留存政策,不使用您的数据进行模型训练,", "go.faq.a5.exceptionsLink": "以下例外情况除外", "go.faq.q6": "我可以充值余额吗?", @@ -349,7 +361,10 @@ export const dict = { "go.faq.q9": "免费模型和 Go 之间的区别是什么?", "go.faq.a9": - "免费模型包含 Big Pickle 加上当时可用的促销模型,每天有 200 次请求的配额。Go 提供精选模型阵容,并在滚动窗口(5 小时、每周和每月)内执行更高的请求配额,大致相当于每 5 小时 $12、每周 $30 和每月 $60(实际请求计数因模型和使用情况而异)。", + "免费模型包含 Big Pickle 加上当时可用的促销模型,每天有 200 次请求的配额。Go 提供精选模型阵容,并在滚动窗口(5 小时、每周和每月)内执行更高的请求配额,大致相当于每 5 小时 $12、每周 $30 和每月 $60 的基础额度;具体额度可能因模型而异(实际请求计数因模型和使用情况而异)。", + "go.faq.q10": "我可以退款吗?", + "go.faq.a10": + "如果扣款发生在过去 14 天内,并且您在该计费周期内尚未使用任何 Go 额度,您可能符合退款条件。请{{contact}}申请退款。", "zen.api.error.rateLimitExceeded": "超出速率限制。请稍后重试。", "zen.api.error.modelNotSupported": "不支持模型 {{model}}", @@ -643,10 +658,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "几秒钟", "workspace.lite.subscription.message": "您已订阅 OpenCode Go。", "workspace.lite.subscription.manage": "管理订阅", - "workspace.lite.subscription.rollingUsage": "滚动用量", + "workspace.lite.subscription.rollingUsage": "5 小时用量", + "workspace.lite.subscription.rollingQuota": "5 小时配额", "workspace.lite.subscription.weeklyUsage": "每周用量", + "workspace.lite.subscription.weeklyQuota": "每周配额", "workspace.lite.subscription.monthlyUsage": "每月用量", + "workspace.lite.subscription.monthlyQuota": "每月配额", "workspace.lite.subscription.resetsIn": "重置于", + "workspace.lite.subscription.showDetails": "显示详情", + "workspace.lite.subscription.hideDetails": "隐藏详情", + "workspace.lite.subscription.model": "模型", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "总计", "workspace.lite.subscription.useBalance": "达到使用限额后使用您的可用余额", "workspace.lite.subscription.selectProvider": "在你的 opencode 配置中选择「OpenCode Go」作为提供商,即可使用 Go 模型。", @@ -657,8 +680,8 @@ export const dict = { "workspace.lite.black.message": "您当前已订阅 OpenCode Black 或在候补名单中。如需切换到 Go,请先取消订阅。", "workspace.lite.other.message": "此工作区中的另一位成员已经订阅了 OpenCode Go。每个工作区只有一名成员可以订阅。", "workspace.lite.promo.description": - "OpenCode Go 起价为 {{price}},之后 $10/月,并提供对流行开放编码模型的可靠访问,同时享有充裕的使用限额。", - "workspace.lite.promo.price": "首月 $5", + "OpenCode Go 每月 {{price}},并提供对流行开放编码模型的可靠访问,同时享有充裕的使用限额。", + "workspace.lite.promo.price": "$10/月", "workspace.lite.promo.modelsTitle": "包含模型", "workspace.lite.promo.footer": "该计划主要面向国际用户,提供稳定的全球访问体验。随着我们持续了解早期使用情况并收集反馈,定价和使用限额可能会有所调整。", diff --git a/packages/console/app/src/i18n/zht.ts b/packages/console/app/src/i18n/zht.ts index 274542dae31a..a4a5f4bd4a73 100644 --- a/packages/console/app/src/i18n/zht.ts +++ b/packages/console/app/src/i18n/zht.ts @@ -3,6 +3,8 @@ import { dict as en } from "./en" export const dict = { ...en, + "go.promo.deepseek": "DeepSeek V4.1 Flash 限時享有 4 倍使用額度", + "go.graph.bonus": "{{count}} 倍用量", "nav.github": "GitHub", "nav.docs": "文件", "nav.changelog": "更新日誌", @@ -59,6 +61,7 @@ export const dict = { "common.cancel": "取消", "common.creating": "正在建立...", "common.create": "建立", + "common.contactUs": "聯絡我們", "common.videoUnsupported": "你的瀏覽器不支援 video 標籤。", "common.figure": "圖 {{n}}.", @@ -217,6 +220,8 @@ export const dict = { "zen.faq.q8": "我可以在其他編碼代理中使用 Zen 嗎?", "zen.faq.a8": "Zen 與 OpenCode 搭配得很好,但你也可以在任何代理中使用 Zen。請在你偏好的編碼代理中按照設定說明進行配置。", + "zen.faq.q9": "我可以退款嗎?", + "zen.faq.a9": "若扣款發生在過去 14 天內,且你尚未使用該次購買的任何額度,你可能符合退款資格。請{{contact}}申請退款。", "zen.cta.start": "開始使用 Zen", "zen.pricing.title": "儲值 $20 即用即付餘額", @@ -244,8 +249,7 @@ export const dict = { "zen.privacy.exceptionsLink": "以下例外情況", "go.title": "OpenCode Go | 低成本全民編碼模型", - "go.banner.text": "Ox Alpha Free 限時加入 Go", - "go.meta.description": "Go 首月 $5,之後 $10/月,提供充裕的使用限額,並可穩定存取領先的編碼模型。", + "go.meta.description": "Go 每月 $10,提供充裕的使用限額,並可穩定存取領先的編碼模型。", "go.hero.title": "低成本全民編碼模型", "go.hero.body": "Go 將代理編碼帶給全世界的程式設計師。提供寬裕的限額以及對最強大開源模型的穩定存取,讓你可以使用強大的代理進行構建,而無需擔心成本或可用性。", @@ -254,12 +258,19 @@ export const dict = { "go.cta.template": "{{text}} {{price}}", "go.cta.text": "訂閱 Go", "go.cta.price": "$10/月", - "go.cta.promo": "首月 $5", - "go.pricing.body": "可搭配任何代理使用。首月 $5,之後 $10/月。如有需要可儲值。隨時取消。", + "go.pricing.body": "可搭配任何代理使用。每月 $10。如有需要可儲值。隨時取消。", "go.graph.free": "免費", "go.graph.freePill": "Big Pickle 與免費模型", "go.graph.go": "Go", "go.graph.label": "每 5 小時請求數", + "go.graph.period": "使用額度", + "go.graph.model": "模型", + "go.graph.requests": "預估請求次數 / 5 小時", + "go.graph.allowance": "每月使用額度", + "go.graph.new": "新", + "go.graph.scale": "請求數採用非線性刻度", + "go.graph.showAll": "查看全部 {{count}} 個模型", + "go.graph.showLess": "收起模型", "go.graph.limitedRegions": "僅限部分地區", "go.graph.limitedTime": "限時", "go.graph.usageLimits": "使用限制", @@ -288,20 +299,20 @@ export const dict = { "go.testimonials.frank.quote": "我希望我還在 Nvidia。", "go.problem.title": "Go 正在解決什麼問題?", "go.problem.body": - "我們致力於將 OpenCode 體驗帶給盡可能多的人。OpenCode Go 是一款低成本訂閱服務:首月 $5,之後 $10/月。它提供充裕的額度,並讓您能可靠地使用最強大的開源模型。", + "我們致力於將 OpenCode 體驗帶給盡可能多的人。OpenCode Go 是一款每月 $10 的低成本訂閱服務。它提供充裕的額度,並讓您能可靠地使用最強大的開源模型。", "go.problem.subtitle": " ", "go.problem.item1": "低成本訂閱定價", "go.problem.item2": "寬裕的限額與穩定存取", "go.problem.item3": "專為盡可能多的程式設計師打造", "go.problem.item4": "針對代理編碼測試的精選模型陣容", "go.how.title": "Go 如何運作", - "go.how.body": "Go 起價為首月 $5,之後 $10/月。您可以將其與 OpenCode 或任何代理搭配使用。", + "go.how.body": "Go 每月 $10。您可以將其與 OpenCode 或任何代理搭配使用。", "go.how.step1.title": "建立帳號", "go.how.step1.beforeLink": "遵循", "go.how.step1.link": "設定說明", "go.how.step2.title": "訂閱 Go", - "go.how.step2.link": "首月 $5", - "go.how.step2.afterLink": "之後 $10/月,額度充裕", + "go.how.step2.link": "每月 $10", + "go.how.step2.afterLink": "額度充裕", "go.how.step3.title": "開始編碼", "go.how.step3.body": "穩定存取開源模型", "go.privacy.title": "你的隱私對我們很重要", @@ -314,11 +325,11 @@ export const dict = { "go.faq.q2": "Go 包含哪些模型?", "go.faq.a2": "Go 包含下方列出的模型,提供充足的額度與穩定的存取。", "go.faq.q3": "Go 與 Zen 一樣嗎?", - "go.faq.a3": "不。Zen 是按量付費,而 Go 首月 $5,之後 $10/月,提供充裕的限額,並可穩定存取精選模型陣容。", + "go.faq.a3": "不。Zen 是按量付費,而 Go 每月 $10,提供充裕的限額,並可穩定存取精選模型陣容。", "go.faq.q4": "Go 費用是多少?", "go.faq.a4.p1.beforePricing": "Go 費用為", - "go.faq.a4.p1.pricingLink": "首月 $5", - "go.faq.a4.p1.afterPricing": "之後 $10/月,額度充裕。", + "go.faq.a4.p1.pricingLink": "每月 $10", + "go.faq.a4.p1.afterPricing": "額度充裕。", "go.faq.a4.p2.beforeAccount": "你可以在你的", "go.faq.a4.p2.accountLink": "帳戶", "go.faq.a4.p3": "中管理訂閱。隨時取消。", @@ -337,7 +348,7 @@ export const dict = { "go.faq.a5.gptRetention": "所有 API 功能的使用都會產生濫用監控日誌,並保留最多 30 天。", "go.faq.a5.museRetention": "以允許使用您的提示詞和生成結果來訓練未來的 Meta 模型為交換,token 價格可享大幅折扣。", "go.faq.a5.learnMore": "了解更多", - "go.faq.a5.deepseekRetention": "ZDR 協議每月續簽。目前的協議有效至 2026 年 8 月 31 日。", + "go.faq.a5.deepseekRetention": "ZDR 協議每月續簽。目前的協議有效至 2026 年 9 月 30 日。", "go.faq.a5.beforeExceptions": "Go 模型託管在美國。供應商遵循零留存政策,不會將你的資料用於模型訓練,但有", "go.faq.a5.exceptionsLink": "以下例外", "go.faq.q6": "我可以儲值額度嗎?", @@ -349,7 +360,10 @@ export const dict = { "go.faq.q9": "免費模型與 Go 有什麼區別?", "go.faq.a9": - "免費模型包括 Big Pickle 以及當時可用的促銷模型,配額為 200 次請求/天。Go 提供精選模型陣容,並在滾動視窗(5 小時、每週和每月)內提供更高的請求配額,大約相當於每 5 小時 $12、每週 $30 和每月 $60(實際請求數因模型和使用情況而異)。", + "免費模型包括 Big Pickle 以及當時可用的促銷模型,配額為 200 次請求/天。Go 提供精選模型陣容,並在滾動視窗(5 小時、每週和每月)內提供更高的請求配額,大約相當於每 5 小時 $12、每週 $30 和每月 $60 的基礎額度;具體額度可能因模型而異(實際請求數因模型和使用情況而異)。", + "go.faq.q10": "我可以退款嗎?", + "go.faq.a10": + "若扣款發生在過去 14 天內,且你在該計費期間尚未使用任何 Go 額度,你可能符合退款資格。請{{contact}}申請退款。", "zen.api.error.rateLimitExceeded": "超出頻率限制。請稍後再試。", "zen.api.error.modelNotSupported": "不支援模型 {{model}}", @@ -643,10 +657,18 @@ export const dict = { "workspace.lite.time.fewSeconds": "幾秒", "workspace.lite.subscription.message": "您已訂閱 OpenCode Go。", "workspace.lite.subscription.manage": "管理訂閱", - "workspace.lite.subscription.rollingUsage": "滾動使用量", + "workspace.lite.subscription.rollingUsage": "5 小時使用量", + "workspace.lite.subscription.rollingQuota": "5 小時配額", "workspace.lite.subscription.weeklyUsage": "每週使用量", + "workspace.lite.subscription.weeklyQuota": "每週配額", "workspace.lite.subscription.monthlyUsage": "每月使用量", + "workspace.lite.subscription.monthlyQuota": "每月配額", "workspace.lite.subscription.resetsIn": "重置時間:", + "workspace.lite.subscription.showDetails": "顯示詳情", + "workspace.lite.subscription.hideDetails": "隱藏詳情", + "workspace.lite.subscription.model": "模型", + "workspace.lite.subscription.contribution": "%", + "workspace.lite.subscription.total": "總計", "workspace.lite.subscription.useBalance": "達到使用限制後使用您的可用餘額", "workspace.lite.subscription.selectProvider": "在您的 opencode 設定中選擇「OpenCode Go」作為提供商,即可使用 Go 模型。", @@ -657,8 +679,8 @@ export const dict = { "workspace.lite.black.message": "您目前已訂閱 OpenCode Black 或在候補名單中。若要切換至 Go,請先取消訂閱。", "workspace.lite.other.message": "此工作區中的另一位成員已訂閱 OpenCode Go。每個工作區只能有一位成員訂閱。", "workspace.lite.promo.description": - "OpenCode Go 起價為 {{price}},之後 $10/月,並提供對熱門開放編碼模型的可靠存取,同時享有充裕的使用額度。", - "workspace.lite.promo.price": "首月 $5", + "OpenCode Go 每月 {{price}},並提供對熱門開放編碼模型的可靠存取,同時享有充裕的使用額度。", + "workspace.lite.promo.price": "$10/月", "workspace.lite.promo.modelsTitle": "包含模型", "workspace.lite.promo.footer": "此方案主要為國際使用者設計,提供穩定的全球存取服務。隨著我們從初期使用情況和回饋中持續了解需求,價格和使用額度可能會有所調整。", diff --git a/packages/console/app/src/lib/inference-proxy.ts b/packages/console/app/src/lib/inference-proxy.ts new file mode 100644 index 000000000000..b572c361b655 --- /dev/null +++ b/packages/console/app/src/lib/inference-proxy.ts @@ -0,0 +1,117 @@ +import { Resource } from "@opencode-ai/console-resource" +import { and, Database, eq, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" +import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js" +import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js" +import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" + +const paths: Record = { + "POST /zen/v1/chat/completions": "/openai/v1/chat/completions", + "POST /zen/v1/responses": "/openai/v1/responses", + "POST /zen/v1/messages": "/anthropic/v1/messages", + "POST /zen/go/v1/chat/completions": "/go/openai/v1/chat/completions", + "POST /zen/go/v1/responses": "/go/openai/v1/responses", + "POST /zen/go/v1/messages": "/go/anthropic/v1/messages", + "GET /zen/v1/models": "/v1/models", + "GET /zen/go/v1/models": "/go/v1/models", + "GET /zen/go/v1/usage": "/go/v1/usage", +} + +export async function proxyInference( + request: Request, + generation?: { + provider?: "openai" | "anthropic" | "google" + /** The provider's native model ID, not the public Zen alias. */ + model?: string + body: (model?: string) => ReadableStream + }, +): Promise { + const url = new URL(request.url) + const path = + paths[`${request.method} ${url.pathname}`] ?? + (request.method === "POST" && + /^\/zen\/v1\/models\/[^/]+:(?:generateContent|streamGenerateContent)$/.test(url.pathname) + ? url.pathname.replace("/zen/v1/models/", "/google/v1beta/models/") + : undefined) + if (!path) return undefined + + const go = url.pathname.startsWith("/zen/go/") + const key = url.pathname.endsWith("/messages") + ? request.headers.get("x-api-key") + : path.startsWith("/google/") + ? request.headers.get("x-goog-api-key") + : request.headers.get("authorization")?.split(" ")[1] + if (!key || key === "public") return undefined + + // Routing only; the destination owns authentication and revocation after cutover. + const workspace = await Database.use((tx) => + tx + .select({ + id: WorkspaceTable.id, + migratedAt: WorkspaceTable.migrated_at, + provider: ProviderTable.provider, + }) + .from(KeyTable) + .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID)) + .leftJoin( + ProviderTable, + !go && generation?.provider + ? and( + eq(ProviderTable.workspaceID, KeyTable.workspaceID), + eq(ProviderTable.provider, generation.provider), + isNull(ProviderTable.timeDeleted), + sql`length(${ProviderTable.credentials}) > 0`, + ) + : sql`false`, + ) + .where(eq(KeyTable.key, key)) + .limit(1) + .then((rows) => rows[0]), + ) + if (!workspace?.migratedAt) return undefined + const model = workspace.provider ? generation?.model : undefined + if (workspace.provider && !model) throw new Error("Legacy BYOK model mapping is unavailable") + + const destination = new URL(Resource.ConsoleMigration.inferenceUrl) + // Imported connections must use this same workspace/provider-derived ID. + const target = model + ? `/custom/conn_${workspace.id.slice(4)}_${workspace.provider}${ + path.startsWith("/google/") + ? `/models/${encodeURIComponent(model)}${url.pathname.slice(url.pathname.lastIndexOf(":"))}` + : url.pathname.slice("/zen/v1".length) + }` + : path + destination.pathname = `${destination.pathname.replace(/\/$/, "")}${target}` + destination.search = url.search + destination.hash = "" + + // Model extraction has already read part of the body; forward its replay stream. + const forwarded = new Request( + destination, + generation ? new Request(request, { method: request.method, body: generation.body(model) }) : request, + ) + // Migrated requests use ordinary destination authentication and accounting. + for (const name of [ + "x-zen", + "x-zen-model", + "x-zen-ip", + "cf-access-client-id", + "cf-access-client-secret", + "host", + "content-length", + ]) + forwarded.headers.delete(name) + forwarded.headers.set("authorization", `Bearer ${key}`) + const ip = request.headers.get("cf-connecting-ip") + if (ip) forwarded.headers.set("x-real-ip", ip) + const requestID = request.headers.get("x-opencode-request-id") ?? request.headers.get("x-opencode-request") + if (requestID) forwarded.headers.set("x-opencode-request-id", requestID) + + return fetch(forwarded, { redirect: "manual" }) +} + +export function inferenceUnavailable() { + return Response.json( + { error: { type: "api_error", message: "Inference routing is unavailable. Please retry later." } }, + { status: 503, headers: { "Cache-Control": "no-store" } }, + ) +} diff --git a/packages/console/app/src/lib/language.ts b/packages/console/app/src/lib/language.ts index a196f663db4c..773f63d61508 100644 --- a/packages/console/app/src/lib/language.ts +++ b/packages/console/app/src/lib/language.ts @@ -134,6 +134,34 @@ const DOCS_LOCALE = { "zh-tw": "zht", } as const satisfies Record +// Heading IDs from the localized Go documentation. +const GO_USAGE_LIMITS = { + en: "usage-limits", + zh: "使用限制", + zht: "使用限制", + ko: "사용-한도", + de: "nutzungslimits", + es: "límites-de-uso", + fr: "limites-dutilisation", + it: "limiti-di-utilizzo", + da: "forbrugsgrænser", + ja: "利用制限", + pl: "limity-użycia", + ru: "лимиты-использования", + uk: "usage-limits", + ar: "حدود-الاستخدام", + no: "bruksgrenser", + br: "limites-de-uso", + th: "usage-limits", + tr: "kullanım-limitleri", +} satisfies Record + +export function goUsageLimits(locale: Locale) { + // No Ukrainian Go docs yet; the explicit English path overrides the locale cookie. + if (locale === "uk") return "/docs/en/go/#usage-limits" + return docs(locale, `/docs/go/#${GO_USAGE_LIMITS[locale]}`) +} + function suffix(pathname: string) { const index = pathname.search(/[?#]/) if (index === -1) { diff --git a/packages/console/app/src/lib/lite-usage.ts b/packages/console/app/src/lib/lite-usage.ts new file mode 100644 index 000000000000..618c89e92d75 --- /dev/null +++ b/packages/console/app/src/lib/lite-usage.ts @@ -0,0 +1,73 @@ +export type LiteUsageBreakdownSource = { + model: string + name: string + cost: number + quotaCost: number + multiplier?: number + estimated: boolean +} + +export type LiteUsageBreakdownItem = { + model: string + name: string + cost?: number + multiplier?: number + quotaCost: number + contributionPercent: number + estimated: boolean +} + +export function buildLiteUsageBreakdown(input: { usage: number; limit: number; sources: LiteUsageBreakdownSource[] }) { + // Legacy usage can resolve to the same rate as a separately grouped recorded multiplier. + const groups = new Map() + input.sources.forEach((item) => { + const key = JSON.stringify([item.model, item.multiplier]) + const row = groups.get(key) + if (!row) { + groups.set(key, { ...item }) + return + } + row.cost += item.cost + row.quotaCost += item.quotaCost + row.estimated ||= item.estimated + }) + const rows: LiteUsageBreakdownItem[] = Array.from(groups.values()) + .filter((item) => item.cost !== 0 || item.quotaCost !== 0) + .sort((a, b) => b.quotaCost - a.quotaCost) + .map((item) => ({ + ...item, + contributionPercent: 0, + })) + + const usagePercent = getUsagePercent(input.usage, input.limit) + const target = Math.max(0, Math.round(usagePercent * 10)) + const totalQuota = rows.reduce((total, item) => total + Math.max(0, item.quotaCost), 0) + const units = rows.map((item) => { + const exact = totalQuota === 0 ? 0 : (Math.max(0, item.quotaCost) / totalQuota) * target + const value = Math.floor(exact) + return { item, exact, value } + }) + const remaining = target - units.reduce((total, item) => total + item.value, 0) + const ranked = units.toSorted((a, b) => b.exact - b.value - (a.exact - a.value)) + Array.from({ length: ranked.length === 0 ? 0 : remaining }).forEach((_, index) => { + ranked[index % ranked.length].value += 1 + }) + units.forEach((unit) => (unit.item.contributionPercent = unit.value / 10)) + + return { + usage: input.usage, + limit: input.limit, + usagePercent, + rows, + } +} + +export function getModelQuotaLimit(limit: number, multiplier?: number) { + if (multiplier === undefined || multiplier <= 0) return + return limit / multiplier +} + +export function getUsagePercent(amount: number, limit: number) { + if (limit === 0) return 0 + return Math.round((amount / limit) * 1000) / 10 +} diff --git a/packages/console/app/src/lib/request-country.ts b/packages/console/app/src/lib/request-country.ts index 2eb2e22d6ac5..5806fc2f64ac 100644 --- a/packages/console/app/src/lib/request-country.ts +++ b/packages/console/app/src/lib/request-country.ts @@ -31,7 +31,12 @@ export function countryFromRequest(request: Request | undefined) { export function isModelCountryRestricted(model: string, country: string | undefined) { return ( - ["muse-spark-1.2-contributor", "muse-spark-1.2-contributor-free"].includes(model) && + [ + "muse-spark-1.3-contributor", + "muse-spark-1.3-contributor-free", + "muse-spark-1.2-contributor", + "muse-spark-1.2-contributor-free", + ].includes(model) && country !== undefined && MUSE_SPARK_BLOCKED_COUNTRIES.has(country.toUpperCase()) ) diff --git a/packages/console/app/src/lib/server-action.ts b/packages/console/app/src/lib/server-action.ts new file mode 100644 index 000000000000..1d82b5697824 --- /dev/null +++ b/packages/console/app/src/lib/server-action.ts @@ -0,0 +1,11 @@ +export function sanitizeServerActionRequest(request: Request) { + const requestUrl = new URL(request.url) + if (requestUrl.pathname !== "/_server") return request + + const referer = request.headers.get("referer") + if (referer && URL.canParse(referer) && new URL(referer).origin === requestUrl.origin) return request + + const sanitized = new Request(request) + sanitized.headers.set("referer", requestUrl.origin) + return sanitized +} diff --git a/packages/console/app/src/lib/stats-proxy.ts b/packages/console/app/src/lib/stats-proxy.ts index 48e95bd74a16..b8db97c06396 100644 --- a/packages/console/app/src/lib/stats-proxy.ts +++ b/packages/console/app/src/lib/stats-proxy.ts @@ -1,8 +1,9 @@ import type { APIEvent } from "@solidjs/start/server" -import { Resource } from "@opencode-ai/console-resource" +import { Resource, waitUntil } from "@opencode-ai/console-resource" import { LOCALE_HEADER, cookie, localeFromRequest, route, tag } from "~/lib/language" const dataPath = "/data" +const statsCacheParam = "__opencode_stats_locale" export async function statsProxy(evt: APIEvent) { const req = evt.request.clone() @@ -10,6 +11,13 @@ export async function statsProxy(evt: APIEvent) { const redirect = redirectToLocalizedData(req, new URL(req.url), locale) if (redirect) return redirect + const cache = defaultCache(caches) + const cacheKey = statsCacheKey(req, locale) + if (cacheKey) { + const cached = await cache.match(cacheKey) + if (cached) return withStatsCacheStatus(cached, "HIT") + } + const targetUrl = new URL(req.url) targetUrl.protocol = "https:" targetUrl.hostname = Resource.App.stage === "production" ? "stats.opencode.ai" : "stats.dev.opencode.ai" @@ -40,13 +48,18 @@ export async function statsProxy(evt: APIEvent) { headers.delete("content-encoding") headers.delete("content-length") headers.delete("etag") + headers.delete("set-cookie") appendVary(headers, "Accept-Language", "Cookie", LOCALE_HEADER) - return new Response(rewriteStatsHtml(await response.text()), { + const result = new Response(rewriteStatsHtml(await response.text()), { status: response.status, statusText: response.statusText, headers, }) + if (!cacheKey || !response.ok) return result + + void waitUntil(cache.put(cacheKey, result.clone())) + return withStatsCacheStatus(result, "MISS") } export function statsRedirect(evt: APIEvent) { @@ -64,6 +77,34 @@ function rewriteStatsHtml(html: string) { return html.replaceAll('"/_build/', `"${dataPath}/_build/`).replaceAll("'/_build/", `'${dataPath}/_build/`) } +function statsCacheKey(request: Request, locale: ReturnType): Request | undefined { + if (request.method !== "GET") return undefined + if (!acceptsHtml(request)) return undefined + if (isDataBypassPath(new URL(request.url).pathname)) return undefined + + const url = new URL(request.url) + const additionalModels = url.searchParams.get("add") + url.search = "" + if (additionalModels) url.searchParams.set("add", additionalModels) + url.searchParams.set(statsCacheParam, locale) + return new Request(url) +} + +function defaultCache(storage: CacheStorage) { + if (!isCloudflareCacheStorage(storage)) throw new Error("Cloudflare default cache is unavailable") + return storage.default +} + +function isCloudflareCacheStorage(storage: CacheStorage): storage is CacheStorage & { default: Cache } { + return "default" in storage +} + +function withStatsCacheStatus(response: Response, status: "HIT" | "MISS") { + const headers = new Headers(response.headers) + headers.set("x-opencode-stats-cache", status) + return new Response(response.body, { status: response.status, statusText: response.statusText, headers }) +} + function redirectToLocalizedData(request: Request, url: URL, locale: ReturnType) { if (locale === "en") return null if (request.headers.get(LOCALE_HEADER)) return null diff --git a/packages/console/app/src/middleware.ts b/packages/console/app/src/middleware.ts index ad5aa09e2ab9..614cc87bcf00 100644 --- a/packages/console/app/src/middleware.ts +++ b/packages/console/app/src/middleware.ts @@ -1,9 +1,12 @@ import { createMiddleware } from "@solidjs/start/middleware" import { LOCALE_HEADER, cookie, fromPathname, strip } from "~/lib/language" import { normalizeReferralCode, referralCookie } from "~/lib/referral-invite" +import { sanitizeServerActionRequest } from "~/lib/server-action" export default createMiddleware({ - onRequest(event) { + async onRequest(event) { + event.request = sanitizeServerActionRequest(event.request) + const url = new URL(event.request.url) const locale = fromPathname(url.pathname) if (locale) { diff --git a/packages/console/app/src/routes/api/enterprise.ts b/packages/console/app/src/routes/api/enterprise.ts index ff8f229e8b9e..65f5c72b2311 100644 --- a/packages/console/app/src/routes/api/enterprise.ts +++ b/packages/console/app/src/routes/api/enterprise.ts @@ -98,7 +98,7 @@ ${body.phone ? `${body.phone}
` : ""}`.trim() return false }), AWS.sendEmail({ - to: "contact@anoma.ly", + to: Resource.ENTERPRISE_SALES_INBOX_EMAIL.value, subject: `Enterprise Inquiry from ${body.name}`, body: emailContent, replyTo: body.email, diff --git a/packages/console/app/src/routes/api/support/actions/block-workspaces.ts b/packages/console/app/src/routes/api/support/actions/block-workspaces.ts new file mode 100644 index 000000000000..9f1fc0e9a3eb --- /dev/null +++ b/packages/console/app/src/routes/api/support/actions/block-workspaces.ts @@ -0,0 +1,22 @@ +import type { APIEvent } from "@solidjs/start/server" +import { Workspace } from "@opencode-ai/console-core/workspace.js" +import { safeEqual } from "@opencode-ai/console-core/util/crypto.js" +import { Resource } from "@opencode-ai/console-resource" +import z from "zod" + +const Body = z.object({ workspaceIDs: z.array(z.string().startsWith("wrk_")).min(1) }) + +export async function POST(event: APIEvent) { + if (!safeEqual(event.request.headers.get("authorization") ?? "", `Bearer ${Resource.SUPPORT_API_KEY.value}`)) { + return Response.json({ error: "Unauthorized" }, { status: 401 }) + } + + const body = Body.safeParse(await event.request.json().catch(() => undefined)) + if (!body.success) { + return Response.json({ error: "Invalid request", issues: body.error.issues }, { status: 400 }) + } + // Missing IDs do not fail the batch; they are reported per item in the result. + return Workspace.blockBatch(body.data) + .then((result) => Response.json({ success: true, message: "Workspaces blocked", result })) + .catch((error) => Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 400 })) +} diff --git a/packages/console/app/src/routes/api/support/actions/reset-quota.ts b/packages/console/app/src/routes/api/support/actions/reset-quota.ts new file mode 100644 index 000000000000..6f5c2812cfe0 --- /dev/null +++ b/packages/console/app/src/routes/api/support/actions/reset-quota.ts @@ -0,0 +1,24 @@ +import type { APIEvent } from "@solidjs/start/server" +import { Quota } from "@opencode-ai/console-core/quota.js" +import { safeEqual } from "@opencode-ai/console-core/util/crypto.js" +import { Resource } from "@opencode-ai/console-resource" +import z from "zod" + +const Body = z.object({ + workspaceID: z.string().startsWith("wrk_"), + plan: z.enum(["lite", "subscription"]), +}) + +export async function POST(event: APIEvent) { + if (!safeEqual(event.request.headers.get("authorization") ?? "", `Bearer ${Resource.SUPPORT_API_KEY.value}`)) { + return Response.json({ error: "Unauthorized" }, { status: 401 }) + } + + const body = Body.safeParse(await event.request.json().catch(() => undefined)) + if (!body.success) { + return Response.json({ error: "Invalid request", issues: body.error.issues }, { status: 400 }) + } + return Quota.reset(body.data) + .then((result) => Response.json({ success: true, message: "Usage counters reset to zero", result })) + .catch((error) => Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 400 })) +} diff --git a/packages/console/app/src/routes/api/support/actions/unblock-workspaces.ts b/packages/console/app/src/routes/api/support/actions/unblock-workspaces.ts new file mode 100644 index 000000000000..59ac1f354ebf --- /dev/null +++ b/packages/console/app/src/routes/api/support/actions/unblock-workspaces.ts @@ -0,0 +1,22 @@ +import type { APIEvent } from "@solidjs/start/server" +import { Workspace } from "@opencode-ai/console-core/workspace.js" +import { safeEqual } from "@opencode-ai/console-core/util/crypto.js" +import { Resource } from "@opencode-ai/console-resource" +import z from "zod" + +const Body = z.object({ workspaceIDs: z.array(z.string().startsWith("wrk_")).min(1) }) + +export async function POST(event: APIEvent) { + if (!safeEqual(event.request.headers.get("authorization") ?? "", `Bearer ${Resource.SUPPORT_API_KEY.value}`)) { + return Response.json({ error: "Unauthorized" }, { status: 401 }) + } + + const body = Body.safeParse(await event.request.json().catch(() => undefined)) + if (!body.success) { + return Response.json({ error: "Invalid request", issues: body.error.issues }, { status: 400 }) + } + // Missing IDs do not fail the batch; they are reported per item in the result. + return Workspace.unblockBatch(body.data) + .then((result) => Response.json({ success: true, message: "Workspaces unblocked", result })) + .catch((error) => Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 400 })) +} diff --git a/packages/console/app/src/routes/black/index.tsx b/packages/console/app/src/routes/black/index.tsx index 8bce3cd464f7..b8f01842d0c2 100644 --- a/packages/console/app/src/routes/black/index.tsx +++ b/packages/console/app/src/routes/black/index.tsx @@ -103,9 +103,6 @@ export default function Black() { - - {i18n.t("black.action.continue")} - diff --git a/packages/console/app/src/routes/black/subscribe/[plan].tsx b/packages/console/app/src/routes/black/subscribe/[plan].tsx deleted file mode 100644 index c29c5fac80a9..000000000000 --- a/packages/console/app/src/routes/black/subscribe/[plan].tsx +++ /dev/null @@ -1,489 +0,0 @@ -import { A, createAsync, query, redirect, useParams } from "@solidjs/router" -import { Title } from "@solidjs/meta" -import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js" -import { type Stripe, type PaymentMethod, loadStripe } from "@stripe/stripe-js" -import { Elements, PaymentElement, useStripe, useElements, AddressElement } from "solid-stripe" -import { PlanID, plans } from "../common" -import { getActor, useAuthSession } from "~/context/auth" -import { withActor } from "~/context/auth.withActor" -import { Actor } from "@opencode-ai/console-core/actor.js" -import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js" -import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" -import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js" -import { createList } from "solid-list" -import { Modal } from "~/component/modal" -import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js" -import { Billing } from "@opencode-ai/console-core/billing.js" -import { useI18n } from "~/context/i18n" -import { useLanguage } from "~/context/language" -import { formError } from "~/lib/form-error" -import { Resource } from "@opencode-ai/console-resource" - -const getEnabled = query(async () => { - "use server" - return Resource.App.stage !== "production" -}, "black.subscribe.enabled") - -const plansMap = Object.fromEntries(plans.map((p) => [p.id, p])) as Record -const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!) - -const getWorkspaces = query(async (plan: string) => { - "use server" - const actor = await getActor() - if (actor.type === "public") throw redirect("/auth/authorize?continue=/black/subscribe/" + plan) - return withActor(async () => { - return Database.use((tx) => - tx - .select({ - id: WorkspaceTable.id, - name: WorkspaceTable.name, - slug: WorkspaceTable.slug, - billing: { - customerID: BillingTable.customerID, - paymentMethodID: BillingTable.paymentMethodID, - paymentMethodType: BillingTable.paymentMethodType, - paymentMethodLast4: BillingTable.paymentMethodLast4, - subscriptionID: BillingTable.subscriptionID, - timeSubscriptionBooked: BillingTable.timeSubscriptionBooked, - }, - }) - .from(UserTable) - .innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id)) - .innerJoin(BillingTable, eq(WorkspaceTable.id, BillingTable.workspaceID)) - .where( - and( - eq(UserTable.accountID, Actor.account()), - isNull(WorkspaceTable.timeDeleted), - isNull(UserTable.timeDeleted), - ), - ), - ) - }) -}, "black.subscribe.workspaces") - -const createSetupIntent = async (input: { plan: string; workspaceID: string }) => { - "use server" - const { plan, workspaceID } = input - - if (!plan || !["20", "100", "200"].includes(plan)) return { error: formError.invalidPlan } - if (!workspaceID) return { error: formError.workspaceRequired } - - return withActor(async () => { - const session = await useAuthSession() - const account = session.data.account?.[session.data.current ?? ""] - const email = account?.email - - const customer = await Database.use((tx) => - tx - .select({ - customerID: BillingTable.customerID, - subscriptionID: BillingTable.subscriptionID, - }) - .from(BillingTable) - .where(eq(BillingTable.workspaceID, workspaceID)) - .then((rows) => rows[0]), - ) - if (customer?.subscriptionID) { - return { error: formError.alreadySubscribed } - } - - let customerID = customer?.customerID - if (!customerID) { - const customer = await Billing.stripe().customers.create({ - email, - metadata: { - workspaceID, - }, - }) - customerID = customer.id - await Database.use((tx) => - tx - .update(BillingTable) - .set({ - customerID, - }) - .where(eq(BillingTable.workspaceID, workspaceID)), - ) - } - - const intent = await Billing.stripe().setupIntents.create({ - customer: customerID, - payment_method_types: ["card"], - metadata: { - workspaceID, - }, - }) - - return { clientSecret: intent.client_secret ?? undefined } - }, workspaceID) -} - -const bookSubscription = async (input: { - workspaceID: string - plan: PlanID - paymentMethodID: string - paymentMethodType: string - paymentMethodLast4?: string -}) => { - "use server" - return withActor( - () => - Database.use((tx) => - tx - .update(BillingTable) - .set({ - paymentMethodID: input.paymentMethodID, - paymentMethodType: input.paymentMethodType, - paymentMethodLast4: input.paymentMethodLast4, - subscriptionPlan: input.plan, - timeSubscriptionBooked: new Date(), - }) - .where(eq(BillingTable.workspaceID, input.workspaceID)), - ), - input.workspaceID, - ) -} - -interface SuccessData { - plan: string - paymentMethodType: string - paymentMethodLast4?: string -} - -function Failure(props: { message: string }) { - const i18n = useI18n() - - return ( -
-

- {i18n.t("black.subscribe.failurePrefix")} {props.message} -

-
- ) -} - -function Success(props: SuccessData) { - const i18n = useI18n() - - return ( -
-

{i18n.t("black.subscribe.success.title")}

-
-
-
{i18n.t("black.subscribe.success.subscriptionPlan")}
-
{i18n.t("black.subscribe.success.planName", { plan: props.plan })}
-
-
-
{i18n.t("black.subscribe.success.amount")}
-
{i18n.t("black.subscribe.success.amountValue", { plan: props.plan })}
-
-
-
{i18n.t("black.subscribe.success.paymentMethod")}
-
- {props.paymentMethodType}}> - - {props.paymentMethodType} - {props.paymentMethodLast4} - - -
-
-
-
{i18n.t("black.subscribe.success.dateJoined")}
-
{new Date().toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}
-
-
-

{i18n.t("black.subscribe.success.chargeNotice")}

-
- ) -} - -function IntentForm(props: { plan: PlanID; workspaceID: string; onSuccess: (data: SuccessData) => void }) { - const i18n = useI18n() - const stripe = useStripe() - const elements = useElements() - const [error, setError] = createSignal(undefined) - const [loading, setLoading] = createSignal(false) - - const handleSubmit = async (e: Event) => { - e.preventDefault() - if (!stripe() || !elements()) return - - setLoading(true) - setError(undefined) - - const result = await elements()!.submit() - if (result.error) { - setError(result.error.message ?? i18n.t("black.subscribe.error.generic")) - setLoading(false) - return - } - - const { error: confirmError, setupIntent } = await stripe()!.confirmSetup({ - elements: elements()!, - confirmParams: { - expand: ["payment_method"], - payment_method_data: { - allow_redisplay: "always", - }, - }, - redirect: "if_required", - }) - - if (confirmError) { - setError(confirmError.message ?? i18n.t("black.subscribe.error.generic")) - setLoading(false) - return - } - - if (setupIntent?.status === "succeeded") { - const pm = setupIntent.payment_method as PaymentMethod - - await bookSubscription({ - workspaceID: props.workspaceID, - plan: props.plan, - paymentMethodID: pm.id, - paymentMethodType: pm.type, - paymentMethodLast4: pm.card?.last4, - }) - - props.onSuccess({ - plan: props.plan, - paymentMethodType: pm.type, - paymentMethodLast4: pm.card?.last4, - }) - } - - setLoading(false) - } - - return ( -
- - - -

{error()}

-
- -

{i18n.t("black.subscribe.form.chargeNotice")}

- - ) -} - -export default function BlackSubscribe() { - const params = useParams() - const i18n = useI18n() - const language = useLanguage() - const enabled = createAsync(() => getEnabled()) - const planData = plansMap[(params.plan as PlanID) ?? "20"] ?? plansMap["20"] - const plan = planData.id - - const workspaces = createAsync(() => getWorkspaces(plan)) - const [selectedWorkspace, setSelectedWorkspace] = createSignal(undefined) - const [success, setSuccess] = createSignal(undefined) - const [failure, setFailure] = createSignal(undefined) - const [clientSecret, setClientSecret] = createSignal(undefined) - const [stripe, setStripe] = createSignal(undefined) - - const formatError = (error: string) => { - if (error === formError.invalidPlan) return i18n.t("black.subscribe.error.invalidPlan") - if (error === formError.workspaceRequired) return i18n.t("black.subscribe.error.workspaceRequired") - if (error === formError.alreadySubscribed) return i18n.t("black.subscribe.error.alreadySubscribed") - if (error === "Invalid plan") return i18n.t("black.subscribe.error.invalidPlan") - if (error === "Workspace ID is required") return i18n.t("black.subscribe.error.workspaceRequired") - if (error === "This workspace already has a subscription") return i18n.t("black.subscribe.error.alreadySubscribed") - return error - } - - // Resolve stripe promise once - createEffect(() => { - void stripePromise.then((s) => { - if (s) setStripe(s) - }) - }) - - // Auto-select if only one workspace - createEffect(() => { - const ws = workspaces() - if (ws?.length === 1 && !selectedWorkspace()) { - setSelectedWorkspace(ws[0].id) - } - }) - - // Fetch setup intent when workspace is selected (unless workspace already has payment method) - createEffect(async () => { - const id = selectedWorkspace() - if (!id) return - - const ws = workspaces()?.find((w) => w.id === id) - if (ws?.billing?.subscriptionID) { - setFailure(i18n.t("black.subscribe.error.alreadySubscribed")) - return - } - if (ws?.billing?.paymentMethodID) { - if (!ws?.billing?.timeSubscriptionBooked) { - await bookSubscription({ - workspaceID: id, - plan: planData.id, - paymentMethodID: ws.billing.paymentMethodID!, - paymentMethodType: ws.billing.paymentMethodType!, - paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined, - }) - } - setSuccess({ - plan: planData.id, - paymentMethodType: ws.billing.paymentMethodType!, - paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined, - }) - return - } - - const result = await createSetupIntent({ plan, workspaceID: id }) - if (result.error) { - setFailure(formatError(result.error)) - } else if ("clientSecret" in result) { - setClientSecret(result.clientSecret) - } - }) - - // Keyboard navigation for workspace picker - const { active, setActive, onKeyDown } = createList({ - items: () => workspaces()?.map((w) => w.id) ?? [], - initialActive: null, - }) - - const handleSelectWorkspace = (id: string) => { - setSelectedWorkspace(id) - } - - let listRef: HTMLUListElement | undefined - - // Show workspace picker if multiple workspaces and none selected - const showWorkspacePicker = () => { - const ws = workspaces() - return ws && ws.length > 1 && !selectedWorkspace() - } - - return ( - - {i18n.t("black.subscribe.title")} -
-
- - {(data) => } - {(data) => } - - <> -
-

{i18n.t("black.subscribe.title")}

-

- ${planData.id}{" "} - {i18n.t("black.price.perMonth")} - - {(multiplier) => {i18n.t(multiplier())}} - -

-
-
-

{i18n.t("black.subscribe.paymentMethod")}

- - -

- {selectedWorkspace() - ? i18n.t("black.subscribe.loadingPaymentForm") - : i18n.t("black.subscribe.selectWorkspaceToContinue")} -

-
- } - > - - - - - -
-
-
- - {/* Workspace picker modal */} - {}} - title={i18n.t("black.workspace.selectPlan")} - variant="black" - > -
-
    { - if (e.key === "Enter" && active()) { - handleSelectWorkspace(active()!) - } else { - onKeyDown(e) - } - }} - > - - {(workspace) => ( -
  • setActive(workspace.id)} - onClick={() => handleSelectWorkspace(workspace.id)} - > - [*] - {workspace.name || workspace.slug} -
  • - )} -
    -
-
-
-

- {i18n.t("black.finePrint.beforeTerms")} ·{" "} - {i18n.t("black.finePrint.terms")} -

-
-
- ) -} diff --git a/packages/console/app/src/routes/go/index.css b/packages/console/app/src/routes/go/index.css index a329e2981efb..6793b6c32bb1 100644 --- a/packages/console/app/src/routes/go/index.css +++ b/packages/console/app/src/routes/go/index.css @@ -21,10 +21,22 @@ } } -@keyframes go-graph-bar { +@keyframes go-graph-reveal { to { + clip-path: inset(0 0 0 0) fill-box; + } +} + +@keyframes go-graph-label { + to { + mask-position: 0 0; opacity: 1; - transform: scaleX(1); + } +} + +@keyframes go-graph-grid { + to { + mask-position: 0 100%; } } @@ -351,10 +363,6 @@ body { [data-slot="text"] { color: var(--color-text-strong); line-height: 1.4; - - @media (max-width: 30.625rem) { - display: none; - } } } @@ -477,6 +485,46 @@ body { } [data-component="limit-graph"] { + --reveal-duration: 950ms; + --bonus-duration: 1500ms; + --digit-easing: cubic-bezier(0.22, 1, 0.36, 1); + --grid-duration: 2000ms; + --grid-easing: cubic-bezier(0.22, 1, 0.36, 1); + --bar-delay: 240ms; + /* Critically damped response, shared by the bars, labels, and rolling digits. */ + --spring-easing: linear( + 0, + 0.02991, + 0.10078, + 0.19179, + 0.28962, + 0.38611, + 0.47651, + 0.55839, + 0.63079, + 0.69365, + 0.74748, + 0.79306, + 0.83131, + 0.86315, + 0.8895, + 0.91117, + 0.92892, + 0.94339, + 0.95515, + 0.96467, + 0.97236, + 0.97855, + 0.98352, + 0.98751, + 0.9907, + 0.99324, + 0.99527, + 0.99689, + 0.99817, + 0.99919, + 1 + ); margin: 0 auto; width: calc(100% - 120px); max-width: calc(100% - 120px); @@ -491,6 +539,7 @@ body { } [data-slot="plot"] { + container-type: inline-size; position: relative; overflow: visible; width: 100%; @@ -515,6 +564,7 @@ body { } [data-slot="xlabels"] [data-xlabel] { + opacity: 0; position: absolute; left: var(--x); top: var(--y); @@ -551,11 +601,26 @@ body { } } + [data-slot="xlabels"] [data-xlabel], + [data-slot="pills"] [data-label], + [data-bonus] { + mask-image: linear-gradient(to right, #000 40%, transparent 60%); + mask-size: 250% 100%; + mask-position: 100% 0; + mask-repeat: no-repeat; + } + [data-slot="pills"] { position: absolute; inset: 0; pointer-events: none; + [data-label] { + display: inline-flex; + align-items: center; + gap: 8px; + } + [data-item] { position: absolute; left: var(--x); @@ -573,7 +638,6 @@ body { font-size: 13px; line-height: 20px; box-sizing: border-box; - opacity: 0; } @media (max-width: 60rem) { @@ -605,14 +669,20 @@ body { [data-value] { color: var(--color-text-strong); font-weight: 600; + font-variant-numeric: tabular-nums; white-space: nowrap; + + number-flow { + line-height: 1; + } } [data-bonus] { + opacity: 0; color: var(--color-text-weak); font-size: 12px; font-weight: 400; - line-height: 1; + line-height: inherit; white-space: nowrap; @media (max-width: 40rem) { @@ -685,11 +755,22 @@ body { opacity: 0.55; } + [data-grid], + [data-stub] { + mask-image: linear-gradient(to top, #000 48%, transparent 52%); + mask-size: 100% 250%; + mask-position: 0 0; + mask-repeat: no-repeat; + mask-origin: stroke-box; + mask-clip: stroke-box; + } + + [data-animate="bar"], + [data-animate="bonus"] { + clip-path: inset(0 100% 0 0) fill-box; + } + [data-bar] { - transform-box: fill-box; - transform-origin: left center; - opacity: 0; - transform: scaleX(0.02); fill: var(--bar-go); stroke: none; } @@ -760,18 +841,65 @@ body { animation-delay: var(--d, 0ms); } - &[data-visible] [data-bar] { - animation: go-graph-bar 560ms cubic-bezier(0.2, 0.7, 0.2, 1) forwards; + &[data-visible] [data-grid], + &[data-visible] [data-stub] { + animation: go-graph-grid var(--grid-duration) var(--grid-easing) forwards; animation-delay: var(--d, 0ms); } - &[data-visible] [data-slot="pills"] [data-item] { - opacity: 1; - transition: opacity 240ms ease; - transition-delay: var(--d, 0ms); + &[data-visible] [data-slot="xlabels"] [data-xlabel] { + animation: go-graph-label 300ms ease-out forwards; + animation-delay: var(--d, 0ms); + } + + &[data-visible] [data-animate="bar"] { + animation: go-graph-reveal var(--reveal-duration) var(--spring-easing) forwards; + animation-delay: calc(var(--bar-delay) + var(--d, 0ms)); + } + + &[data-visible] [data-slot="pills"] [data-label] { + animation: go-graph-label 400ms ease-out forwards; + animation-delay: calc(var(--bar-delay) + var(--d, 0ms) + var(--reveal-duration) * 0.4); + } + + &[data-boosted] [data-animate="bonus"] { + animation: go-graph-reveal var(--bonus-duration) var(--spring-easing) forwards; + animation-delay: var(--bonus-delay); + } + + &[data-boosted] [data-slot="pills"] [data-item][data-promo] { + translate: var(--travel) 0; + transition: translate var(--bonus-duration) var(--spring-easing) var(--bonus-delay); + + @media (max-width: 60rem) { + &[data-edge] { + translate: none; + transition: none; + } + } + } + + &[data-boosted] [data-bonus] { + animation: go-graph-label 400ms ease-out forwards; + animation-delay: var(--bonus-delay); } @media (prefers-reduced-motion: reduce) { + [data-grid], + [data-stub], + &[data-visible] [data-grid], + &[data-visible] [data-stub] { + mask-image: none; + animation: none; + } + + [data-slot="xlabels"] [data-xlabel], + &[data-visible] [data-slot="xlabels"] [data-xlabel] { + opacity: 1; + mask-image: none; + animation: none; + } + [data-animate="line"] { stroke-dashoffset: 0; animation: none; @@ -781,9 +909,12 @@ body { transform: none; animation: none; } - [data-bar] { + [data-animate="bar"], + &[data-visible] [data-animate="bar"], + [data-animate="bonus"], + &[data-boosted] [data-animate="bonus"] { opacity: 1; - transform: none; + clip-path: none; animation: none; } [data-row], @@ -792,8 +923,16 @@ body { transition: none; } - [data-slot="pills"] [data-item] { + [data-slot="pills"] [data-label], + &[data-visible] [data-slot="pills"] [data-label], + [data-bonus], + &[data-boosted] [data-bonus] { opacity: 1; + mask-image: none; + animation: none; + } + + &[data-boosted] [data-slot="pills"] [data-item][data-promo] { transition: none; } } @@ -855,6 +994,78 @@ body { gap: 14px; } } + + @media (max-width: 32rem) { + svg { + overflow: visible; + } + + [data-slot="xlabels"] { + transform: translateY(62px); + + [data-tick="10"] { + display: none; + } + } + + [data-slot="bars"] > [data-model="ox-alpha-free"] { + transform: translateY(39px); + } + + [data-slot="pills"] [data-item][data-edge] { + left: 3.846153846%; + right: auto; + width: calc(100% - 3.846153846%); + max-width: 100%; + height: auto; + padding: 0; + background: none; + line-height: 16px; + gap: 3px 8px; + flex-wrap: wrap; + justify-content: flex-start; + + [data-label] { + max-width: 100%; + flex-wrap: wrap; + gap: 3px 8px; + } + + &[data-model="muse-spark-1.3-contributor"] { + transform: translateY(11px); + + [data-regions] { + flex-basis: 100%; + } + } + + &[data-model="ox-alpha-free"] { + transform: translateY(51px); + } + } + + figcaption { + margin-top: 90px; + } + } + + @media (max-width: 21.25rem) { + [data-slot="xlabels"] { + transform: translateY(100px); + } + + [data-slot="bars"] > [data-model="ox-alpha-free"] { + transform: translateY(58px); + } + + [data-slot="pills"] [data-item][data-edge][data-model="ox-alpha-free"] { + transform: translateY(70px); + } + + figcaption { + margin-top: 128px; + } + } } } diff --git a/packages/console/app/src/routes/go/index.tsx b/packages/console/app/src/routes/go/index.tsx index 025599067395..95f0e338947a 100644 --- a/packages/console/app/src/routes/go/index.tsx +++ b/packages/console/app/src/routes/go/index.tsx @@ -1,7 +1,7 @@ import "./index.css" import { createAsync, query } from "@solidjs/router" import { Title, Meta } from "@solidjs/meta" -import { For, createMemo, createSignal, onCleanup, onMount } from "solid-js" +import { For, createMemo } from "solid-js" //import { HttpHeader } from "@solidjs/start" import goLogoLight from "../../asset/go-ornate-light.svg" import goLogoDark from "../../asset/go-ornate-dark.svg" @@ -10,12 +10,14 @@ import { Faq } from "~/component/faq" import { Legal } from "~/component/legal" import { Footer } from "~/component/footer" import { Header } from "~/component/header" +import { LimitsGraph } from "~/component/limits-graph" import { config } from "~/config" import { getLastSeenWorkspaceID } from "../workspace/common" import { IconMiniMax, IconMiMo, IconZai, IconAlibaba, IconDeepSeek } from "~/component/icon" import { useI18n } from "~/context/i18n" import { useLanguage } from "~/context/language" import { LocaleLinks } from "~/component/locale-links" +import { goUsageLimits } from "~/lib/language" const checkLoggedIn = query(async () => { "use server" @@ -23,225 +25,35 @@ const checkLoggedIn = query(async () => { }, "checkLoggedIn.get") const models = [ - { name: "Grok 4.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" }, + { name: "Grok 4.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" }, { name: "GPT 5.6 Luna", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" }, + { name: "GLM-5.3-Flash", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "GLM-5.3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "GLM-5.2", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "GLM-5.1", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Kimi K3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Kimi K2.7 Code", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Kimi K2.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, + { name: "LongCat-2.0", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "MiMo-V2.5-Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "MiMo-V2.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Qwen3.8 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, + { name: "Qwen3.8 Flash", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Qwen3.7 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Qwen3.7 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Qwen3.6 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "MiniMax M3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "MiniMax M2.7", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, + { name: "Muse Spark 1.3 Contributor", training: "go.faq.a5.used", retention: "go.faq.a5.notZdr" }, { name: "Muse Spark 1.2 Contributor", training: "go.faq.a5.used", retention: "go.faq.a5.notZdr" }, + { name: "DeepSeek V4.1 Flash", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "DeepSeek V4 Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "DeepSeek V4 Flash", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, + { name: "DeepSeek V4 Flash Vision Exp", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, + { name: "Hy4 preview", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, { name: "Hy3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" }, ] as const -function LimitsGraph(props: { href: string }) { - let root!: HTMLElement - const [visible, setVisible] = createSignal(false) - - const i18n = useI18n() - - onMount(() => { - if (typeof IntersectionObserver === "undefined") return setVisible(true) - const observer = new IntersectionObserver( - (entries) => { - const entry = entries[0] - if (!entry?.isIntersecting) return - setVisible(true) - observer.disconnect() - }, - { threshold: 0.35 }, - ) - observer.observe(root) - onCleanup(() => observer.disconnect()) - }) - - const baseline = 100 - const graph = [ - { id: "kimi-k3", name: "Kimi K3", req: 110, d: "50ms" }, - { id: "grok-4.5", name: "Grok 4.5", req: 120, d: "75ms" }, - { id: "qwen3.8-max", name: "Qwen3.8 Max", req: 160, d: "90ms" }, - { id: "glm-5.2", name: "GLM-5.2", req: 880, d: "100ms" }, - { id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 1050, d: "150ms" }, - { id: "gpt-5.6-luna", name: "GPT 5.6 Luna", req: 2050, d: "290ms" }, - { id: "minimax-m3", name: "MiniMax M3", req: 3200, d: "210ms" }, - { id: "qwen3.7-plus", name: "Qwen3.7 Plus", req: 4300, d: "300ms" }, - { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 7600, d: "330ms" }, - { id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, d: "340ms" }, - { id: "hy3", name: "Hy3", req: 34400, baseReq: 4300, d: "320ms" }, - { id: "muse-spark-1.2-contributor", name: "Muse Spark 1.2 Contributor", req: 45300, edge: true, d: "360ms" }, - { id: "ox-alpha-free", name: "Ox Alpha Free", req: Infinity, infinite: true, edge: true, d: "400ms" }, - ] - - const w = 1040 - const chartW = 720 - const left = 40 - const right = 60 - const top = 18 - const bottom = 44 - const plot = chartW - left - right - const infiniteX = w - 180 - - const ratio = (n: number) => n / baseline - const rmax = Math.max(1, ...graph.filter((m) => !("infinite" in m)).map((m) => ratio(m.req))) - const log = (n: number) => Math.log10(Math.max(n, 1)) - const base = 24 - const p = 2.2 - const x = (r: number) => left + base + Math.pow(log(r) / log(rmax), p) * (plot - base) - const ticks = [1, 5, 10, 25, 50, 100, 250].filter((t) => t <= rmax) - const labels = (() => { - const set = new Set() - let last = -Infinity - for (const t of ticks) { - if (t === 1) { - set.add(t) - last = x(t) - continue - } - const pos = x(t) - if (pos - last < 44) continue - set.add(t) - last = pos - } - return set - })() - const shown = ticks.filter((t) => labels.has(t)) - const bh = 8 - const gap = 20 - const step = bh + gap - const gy = (i: number) => top + 22 + step * i - const h = gy(graph.length - 1) + bottom - const my = graph.length < 2 ? gy(0) : (gy(0) + gy(graph.length - 1)) / 2 - const px = (n: number) => `${(n / w) * 100}%` - const py = (n: number) => `${(n / h) * 100}%` - const lx = px(left - 16) - const ty = py(h - 18) - - return ( -
-
- - - - - - -
- - {(m, i) => ( - - {"infinite" in m ? "∞" : m.req.toLocaleString()} - {m.name} - {m.id === "muse-spark-1.2-contributor" && ( - - ( - - {i18n.t("go.graph.limitedRegions")} - - ) - - )} - {"infinite" in m && ({i18n.t("go.graph.limitedTime")})} - {m.baseReq && 8x usage} - - )} - -
-
- -
-
-
-
- {i18n.t("go.graph.label")} - - {i18n.t("go.graph.usageLimits")} - -
-
-
-
-
- ) -} - export default function Home() { const workspaceID = createAsync(() => checkLoggedIn()) const subscribeUrl = createMemo(() => (workspaceID() ? `/workspace/${workspaceID()}/go` : "/auth")) @@ -272,7 +84,7 @@ export default function Home() {
{i18n.t("home.banner.badge")}
- {i18n.t("go.banner.text")} + {i18n.t("go.promo.deepseek")}
@@ -375,12 +187,7 @@ export default function Home() { {(part) => { if (part === "{{text}}") return {i18n.t("go.cta.text")} if (part === "{{price}}") { - return ( - - {i18n.t("go.cta.price")} - {i18n.t("go.cta.promo")} - - ) + return {i18n.t("go.cta.price")} } return part }} @@ -402,7 +209,7 @@ export default function Home() {
- +
@@ -515,7 +322,7 @@ export default function Home() {

- Grok 4.5: {i18n.t("go.faq.a5.grokRetention")}{" "} + Grok 4.6: {i18n.t("go.faq.a5.grokRetention")}{" "} {i18n.t("go.faq.a5.learnMore")} @@ -528,6 +335,13 @@ export default function Home() { .

+

+ Muse Spark 1.3 Contributor: {i18n.t("go.faq.a5.museRetention")}{" "} + + {i18n.t("go.faq.a5.learnMore")} + + . +

Muse Spark 1.2 Contributor: {i18n.t("go.faq.a5.museRetention")}{" "} @@ -550,6 +364,15 @@ export default function Home() {

  • {i18n.t("go.faq.a8")}
  • +
  • + + + {(part) => + part === "{{contact}}" ? {i18n.t("common.contactUs")} : part + } + + +
  • diff --git a/packages/console/app/src/routes/legal/privacy-policy/index.tsx b/packages/console/app/src/routes/legal/privacy-policy/index.tsx index 4426e0ddd08d..f7d3b1fb41f8 100644 --- a/packages/console/app/src/routes/legal/privacy-policy/index.tsx +++ b/packages/console/app/src/routes/legal/privacy-policy/index.tsx @@ -237,9 +237,8 @@ export default function PrivacyPolicy() {
      -
    • Providing, Customizing and Improving the Services
    • -
    • Marketing the Services
    • -
    • Corresponding with You
    • +
    • Passing through to upstream provider to provide services
    • +
    • Not stored
    diff --git a/packages/console/app/src/routes/legal/terms-of-service/index.tsx b/packages/console/app/src/routes/legal/terms-of-service/index.tsx index 7847c44bdc83..76544fc841c9 100644 --- a/packages/console/app/src/routes/legal/terms-of-service/index.tsx +++ b/packages/console/app/src/routes/legal/terms-of-service/index.tsx @@ -21,7 +21,7 @@ export default function TermsOfService() {

    Terms of Use

    -

    Effective date: Mar 6, 2026

    +

    Effective date: Aug 15, 2026

    Welcome to OpenCode. Please read on to learn the rules and restrictions that govern your use of @@ -154,6 +154,11 @@ export default function TermsOfService() { is dangerous, harmful, fraudulent, deceptive, threatening, harassing, defamatory, obscene, or otherwise objectionable; +

  • + creates, maintains, or uses accounts in bulk, or creates, maintains, or uses multiple accounts to + circumvent usage limits, access restrictions, billing obligations, promotions, suspensions, or any + other restriction or policy applicable to the Services; +
  • automatically or programmatically extracts data or Output (defined below);
  • Represent that the Output was human-generated when it was not;
  • diff --git a/packages/console/app/src/routes/oauth/opencode/client.json.ts b/packages/console/app/src/routes/oauth/opencode/client.json.ts new file mode 100644 index 000000000000..edbdfc981698 --- /dev/null +++ b/packages/console/app/src/routes/oauth/opencode/client.json.ts @@ -0,0 +1,38 @@ +import type { APIEvent } from "@solidjs/start/server" + +// OAuth Client ID Metadata Document for the opencode client. +// Spec: https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/ +// +// When an MCP server's authorization server supports this, opencode sends this URL as its OAuth client_id +// instead of registering a new client. The authorization server fetches the document to learn our name and +// allowed redirect URIs. The client_id field must equal the exact URL the document was fetched from, so it is +// built from the request origin and stays valid on dev.opencode.ai as well as production. +// +// redirect_uris have no port because opencode binds an ephemeral port per login. RFC 8252 section 7.3 has +// authorization servers ignore the port when matching loopback redirects for native apps. +const PATH = "/oauth/opencode/client.json" + +const cache = "public, max-age=300" + +export function GET(event: APIEvent) { + const origin = new URL(event.request.url).origin + const document = { + client_id: origin + PATH, + client_name: "opencode", + client_uri: origin, + logo_uri: origin + "/web-app-manifest-512x512.png", + application_type: "native", + redirect_uris: ["http://127.0.0.1/callback", "http://localhost/callback"], + grant_types: ["authorization_code", "refresh_token"], + response_types: ["code"], + token_endpoint_auth_method: "none", + token_endpoint_auth_methods_supported: ["none"], + } + return new Response(JSON.stringify(document, null, 2), { + headers: { + "Content-Type": "application/json", + "Cache-Control": cache, + "Access-Control-Allow-Origin": "*", + }, + }) +} diff --git a/packages/console/app/src/routes/stripe/webhook.ts b/packages/console/app/src/routes/stripe/webhook.ts index e1e4e6cbd39f..05f40e4fd21a 100644 --- a/packages/console/app/src/routes/stripe/webhook.ts +++ b/packages/console/app/src/routes/stripe/webhook.ts @@ -205,6 +205,13 @@ export async function POST(input: APIEvent) { } else if (productID === BlackData.productID()) { await Billing.unsubscribeBlack({ subscriptionID }) } + + const latestInvoice = body.data.object.latest_invoice + const invoiceID = typeof latestInvoice === "string" ? latestInvoice : latestInvoice?.id + if (invoiceID) { + const invoice = await Billing.stripe().invoices.retrieve(invoiceID) + if (invoice.status === "open") await Billing.stripe().invoices.voidInvoice(invoiceID) + } } if (body.type === "invoice.payment_succeeded") { if ( diff --git a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx index c9a72c08791f..f1b9bb933a7f 100644 --- a/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/billing/reload-section.tsx @@ -38,21 +38,25 @@ const setReload = action(async (form: FormData) => { } return json( - await Database.use((tx) => - tx - .update(BillingTable) - .set({ - reload: reloadValue, - ...(reloadAmount !== null ? { reloadAmount } : {}), - ...(reloadTrigger !== null ? { reloadTrigger } : {}), - ...(reloadValue - ? { - reloadError: null, - timeReloadError: null, - } - : {}), - }) - .where(eq(BillingTable.workspaceID, workspaceID)), + await withActor( + () => + Database.use((tx) => + tx + .update(BillingTable) + .set({ + reload: reloadValue, + ...(reloadAmount !== null ? { reloadAmount } : {}), + ...(reloadTrigger !== null ? { reloadTrigger } : {}), + ...(reloadValue + ? { + reloadError: null, + timeReloadError: null, + } + : {}), + }) + .where(eq(BillingTable.workspaceID, workspaceID)), + ), + workspaceID, ), { revalidate: queryBillingInfo.key }, ) diff --git a/packages/console/app/src/routes/workspace/[id]/go/lite-section.module.css b/packages/console/app/src/routes/workspace/[id]/go/lite-section.module.css index f19e0e46cf19..77556d946297 100644 --- a/packages/console/app/src/routes/workspace/[id]/go/lite-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/go/lite-section.module.css @@ -19,6 +19,7 @@ [data-slot="usage-item"] { flex: 1; + min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); @@ -60,6 +61,119 @@ color: var(--color-text-muted); } + [data-slot="usage-details"] { + margin-top: var(--space-1); + } + + [data-slot="usage-details-trigger"] { + display: inline-flex; + align-items: center; + gap: var(--space-2); + width: fit-content; + padding: 0; + border: 0; + background-color: transparent; + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + cursor: pointer; + + &:hover:not(:disabled) { + background-color: transparent; + border-color: transparent; + } + + svg { + transition: transform 0.2s ease; + } + + &[aria-expanded="true"] svg { + transform: rotate(180deg); + } + + [data-slot="hide-details"] { + display: none; + } + + &[aria-expanded="true"] [data-slot="show-details"] { + display: none; + } + + &[aria-expanded="true"] [data-slot="hide-details"] { + display: inline; + } + } + + [data-slot="usage-details-content"] { + width: 100%; + margin-top: var(--space-3); + } + + [data-slot="usage-details-loading"] { + width: 100%; + margin-top: var(--space-3); + color: var(--color-text-muted); + font-size: var(--font-size-sm); + } + + [data-slot="usage-details-empty"] { + margin: 0; + color: var(--color-text-muted); + font-size: var(--font-size-sm); + } + + [data-slot="usage-details-table"] { + overflow-x: auto; + border: 1px solid var(--color-border-muted); + border-radius: var(--border-radius-sm); + + table { + width: 100%; + min-width: 28rem; + table-layout: fixed; + border-collapse: collapse; + font-size: var(--font-size-sm); + white-space: nowrap; + } + + th, + td { + width: 25%; + padding: var(--space-2) var(--space-3); + border-bottom: 1px solid var(--color-border-muted); + text-align: end; + font-variant-numeric: tabular-nums; + } + + th { + color: var(--color-text-muted); + font-size: var(--font-size-xs); + font-weight: 500; + text-transform: uppercase; + } + + th:first-child, + td:first-child { + text-align: start; + } + + td:first-child { + max-width: 13rem; + overflow: hidden; + color: var(--color-text); + text-overflow: ellipsis; + } + + tbody tr:last-child td { + border-bottom: 0; + } + + [data-slot="usage-total"] td { + border-top: 1px solid var(--color-border); + color: var(--color-text); + font-weight: 600; + } + } + [data-slot="setting-row"] { display: flex; align-items: center; diff --git a/packages/console/app/src/routes/workspace/[id]/go/lite-section.tsx b/packages/console/app/src/routes/workspace/[id]/go/lite-section.tsx index 8d3fd37ff2d2..ed3c4d740676 100644 --- a/packages/console/app/src/routes/workspace/[id]/go/lite-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/go/lite-section.tsx @@ -1,15 +1,19 @@ import { action, useParams, useAction, useSubmission, json, query, createAsync } from "@solidjs/router" import { createStore } from "solid-js/store" -import { createMemo, For, Show } from "solid-js" +import { createMemo, createSignal, For, Show } from "solid-js" import { Modal } from "~/component/modal" import { Billing } from "@opencode-ai/console-core/billing.js" -import { Database, eq, and, isNull } from "@opencode-ai/console-core/drizzle/index.js" -import { BillingTable, LiteTable } from "@opencode-ai/console-core/schema/billing.sql.js" +import { Database, eq, and, gte, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js" +import { BillingTable, LiteTable, UsageTable } from "@opencode-ai/console-core/schema/billing.sql.js" +import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js" import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js" import { Actor } from "@opencode-ai/console-core/actor.js" import { Workspace } from "@opencode-ai/console-core/workspace.js" import { Subscription } from "@opencode-ai/console-core/subscription.js" import { LiteData } from "@opencode-ai/console-core/lite.js" +import { ZenData } from "@opencode-ai/console-core/model.js" +import { getMonthlyBounds, getWeekBounds } from "@opencode-ai/console-core/util/date.js" +import { centsToMicroCents } from "@opencode-ai/console-core/util/price.js" import { withActor } from "~/context/auth.withActor" import { queryBillingInfo } from "../../common" import styles from "./lite-section.module.css" @@ -20,8 +24,12 @@ import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time" import { createReferralFromCookie } from "~/lib/referral-invite" import { getRequestEvent } from "solid-js/web" import { countryFromRequest } from "~/lib/request-country" +import { checkCheckoutRateLimit } from "~/routes/zen/util/redis" -import { IconAlipay, IconUpi } from "~/component/icon" +import { IconAlipay, IconChevron, IconUpi } from "~/component/icon" +import { buildLiteUsageBreakdown, getModelQuotaLimit, getUsagePercent } from "~/lib/lite-usage" + +type LiteUsageWindow = "rolling" | "weekly" | "monthly" export const queryLiteSubscription = query(async (workspaceID: string) => { "use server" @@ -51,6 +59,33 @@ export const queryLiteSubscription = query(async (workspaceID: string) => { const limits = LiteData.getLimits() const mine = row.userID === Actor.userID() + const now = new Date() + const rollingCutoff = new Date(now.getTime() - limits.rollingWindow * 3600 * 1000) + const week = getWeekBounds(now) + const month = getMonthlyBounds(now, row.timeCreated) + const rollingActive = !!row.timeRollingUpdated && row.timeRollingUpdated >= rollingCutoff + const weeklyActive = !!row.timeWeeklyUpdated && row.timeWeeklyUpdated >= week.start + const monthlyActive = !!row.timeMonthlyUpdated && row.timeMonthlyUpdated >= month.start + const rollingLimit = centsToMicroCents(limits.rollingLimit * 100) + const weeklyLimit = centsToMicroCents(limits.weeklyLimit * 100) + const monthlyLimit = centsToMicroCents(limits.monthlyLimit * 100) + const rollingUsage = Subscription.analyzeRollingUsage({ + limit: limits.rollingLimit, + window: limits.rollingWindow, + usage: row.rollingUsage ?? 0, + timeUpdated: row.timeRollingUpdated ?? now, + }) + const weeklyUsage = Subscription.analyzeWeeklyUsage({ + limit: limits.weeklyLimit, + usage: row.weeklyUsage ?? 0, + timeUpdated: row.timeWeeklyUpdated ?? now, + }) + const monthlyUsage = Subscription.analyzeMonthlyUsage({ + limit: limits.monthlyLimit, + usage: row.monthlyUsage ?? 0, + timeUpdated: row.timeMonthlyUpdated ?? now, + timeSubscribed: row.timeCreated, + }) return { mine, @@ -58,27 +93,126 @@ export const queryLiteSubscription = query(async (workspaceID: string) => { allowTraining: row.allowTraining ?? false, region: row.region ?? (await Workspace.setDefaultRegion({ country: countryFromRequest(getRequestEvent()?.request) })), - rollingUsage: Subscription.analyzeRollingUsage({ - limit: limits.rollingLimit, - window: limits.rollingWindow, - usage: row.rollingUsage ?? 0, - timeUpdated: row.timeRollingUpdated ?? new Date(), - }), - weeklyUsage: Subscription.analyzeWeeklyUsage({ - limit: limits.weeklyLimit, - usage: row.weeklyUsage ?? 0, - timeUpdated: row.timeWeeklyUpdated ?? new Date(), - }), - monthlyUsage: Subscription.analyzeMonthlyUsage({ - limit: limits.monthlyLimit, - usage: row.monthlyUsage ?? 0, - timeUpdated: row.timeMonthlyUpdated ?? new Date(), - timeSubscribed: row.timeCreated, - }), + rollingUsage: { + ...rollingUsage, + usage: rollingActive ? (row.rollingUsage ?? 0) : 0, + limit: rollingLimit, + usagePercent: getUsagePercent(rollingActive ? (row.rollingUsage ?? 0) : 0, rollingLimit), + }, + weeklyUsage: { + ...weeklyUsage, + usage: weeklyActive ? (row.weeklyUsage ?? 0) : 0, + limit: weeklyLimit, + usagePercent: getUsagePercent(weeklyActive ? (row.weeklyUsage ?? 0) : 0, weeklyLimit), + }, + monthlyUsage: { + ...monthlyUsage, + usage: monthlyActive ? (row.monthlyUsage ?? 0) : 0, + limit: monthlyLimit, + usagePercent: getUsagePercent(monthlyActive ? (row.monthlyUsage ?? 0) : 0, monthlyLimit), + }, } }, workspaceID) }, "lite.subscription.get") +export const queryLiteUsageDetails = query(async (workspaceID: string, window: LiteUsageWindow) => { + "use server" + return withActor(async () => { + if (window !== "rolling" && window !== "weekly" && window !== "monthly") return null + const row = await Database.use((tx) => + tx + .select({ + userID: LiteTable.userID, + rollingUsage: LiteTable.rollingUsage, + weeklyUsage: LiteTable.weeklyUsage, + monthlyUsage: LiteTable.monthlyUsage, + timeRollingUpdated: LiteTable.timeRollingUpdated, + timeWeeklyUpdated: LiteTable.timeWeeklyUpdated, + timeMonthlyUpdated: LiteTable.timeMonthlyUpdated, + timeCreated: LiteTable.timeCreated, + }) + .from(LiteTable) + .where(and(eq(LiteTable.workspaceID, Actor.workspace()), isNull(LiteTable.timeDeleted))) + .then((result) => result[0]), + ) + if (!row || row.userID !== Actor.userID()) return null + + const limits = LiteData.getLimits() + const now = new Date() + const detail = (() => { + if (window === "rolling") { + const active = + !!row.timeRollingUpdated && + row.timeRollingUpdated >= new Date(now.getTime() - limits.rollingWindow * 3600 * 1000) + return { + start: active ? row.timeRollingUpdated! : now, + usage: active ? (row.rollingUsage ?? 0) : 0, + limit: centsToMicroCents(limits.rollingLimit * 100), + } + } + if (window === "weekly") { + const start = getWeekBounds(now).start + return { + start, + usage: row.timeWeeklyUpdated && row.timeWeeklyUpdated >= start ? (row.weeklyUsage ?? 0) : 0, + limit: centsToMicroCents(limits.weeklyLimit * 100), + } + } + const start = getMonthlyBounds(now, row.timeCreated).start + return { + start, + usage: row.timeMonthlyUpdated && row.timeMonthlyUpdated >= start ? (row.monthlyUsage ?? 0) : 0, + limit: centsToMicroCents(limits.monthlyLimit * 100), + } + })() + const modelData = Object.fromEntries( + Object.entries(ZenData.list("lite").models).map(([id, value]) => { + const models = Array.isArray(value) ? value : [value] + const multipliers = new Set(models.map((model) => model.costMultiplier)) + return [id, { name: models[0].name, multiplier: multipliers.size === 1 ? models[0].costMultiplier : undefined }] + }), + ) + const usageRows = await Database.use((tx) => + tx + .select({ + model: UsageTable.model, + multiplier: sql`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier'))`, + cost: sql`SUM(${UsageTable.cost})`, + quotaCost: sql`SUM(CASE WHEN JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier') IS NOT NULL THEN ROUND(${UsageTable.cost} * CAST(JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier')) AS DECIMAL(20, 8))) ELSE 0 END)`, + }) + .from(UsageTable) + .innerJoin(KeyTable, and(eq(KeyTable.id, UsageTable.keyID), eq(KeyTable.workspaceID, UsageTable.workspaceID))) + .where( + and( + eq(UsageTable.workspaceID, Actor.workspace()), + eq(KeyTable.userID, row.userID), + gte(UsageTable.timeCreated, detail.start), + sql`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.plan')) = 'lite'`, + ), + ) + .groupBy(UsageTable.model, sql`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier'))`), + ) + + return buildLiteUsageBreakdown({ + usage: detail.usage, + limit: detail.limit, + sources: usageRows.map((usage) => { + const cost = Number(usage.cost) + const info = modelData[usage.model] + const multiplier = usage.multiplier === null ? info?.multiplier : Number(usage.multiplier) + return { + model: usage.model, + name: info?.name ?? usage.model, + cost, + quotaCost: usage.multiplier === null ? Math.round(cost * (multiplier ?? 1)) : Number(usage.quotaCost), + multiplier, + estimated: usage.multiplier === null, + } + }), + }) + }, workspaceID) +}, "lite.subscription.usage") + type LiteSubscription = Awaited> const createLiteCheckoutUrl = action( @@ -86,6 +220,7 @@ const createLiteCheckoutUrl = action( "use server" return json( await withActor(async () => { + await checkCheckoutRateLimit(Actor.account()) const data = await Billing.generateLiteCheckoutUrl({ successUrl, cancelUrl, method }) await createReferralFromCookie() return { error: undefined, data } @@ -174,7 +309,16 @@ const setGoAllowTraining = action(async (form: FormData) => { ) }, "go.allowTraining.set") -function LiteUsageItem(props: { label: string; usage: { usagePercent: number; resetInSec: number } }) { +type LiteUsage = NonNullable["rollingUsage"] +type LiteUsageDetailsData = NonNullable>> + +function LiteUsageItem(props: { + id: LiteUsageWindow + label: string + usage: LiteUsage + open: boolean + onToggle: () => void +}) { const i18n = useI18n() return ( @@ -183,17 +327,178 @@ function LiteUsageItem(props: { label: string; usage: { usagePercent: number; re {props.label} {props.usage.usagePercent}%
  • -
    -
    +
    +
    {i18n.t("workspace.lite.subscription.resetsIn")}{" "} {formatResetTime(props.usage.resetInSec, i18n, liteResetTimeKeys)} + 0}> +
    + +
    +
    +
    + ) +} + +function LiteUsageDetails(props: { + id: LiteUsageWindow + label: string + quotaLabel: string + usage: LiteUsageDetailsData +}) { + const i18n = useI18n() + const language = useLanguage() + const money = (amount: number) => + new Intl.NumberFormat(language.tag(language.locale()), { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 4, + }).format(amount / 100_000_000) + const totalPercentage = () => + Number(props.usage.rows.reduce((total, row) => total + row.contributionPercent, 0).toFixed(1)) + + return ( +
    +
    + + + + + + + + + + + + {(row) => { + const quota = getModelQuotaLimit(props.usage.limit, row.multiplier) + return ( + + + + + + + ) + }} + + + + + + +
    {i18n.t("workspace.lite.subscription.model")}{props.label}{props.quotaLabel}{i18n.t("workspace.lite.subscription.contribution")}
    + {row.name} + {row.cost === undefined ? "-" : money(row.cost)}{quota === undefined ? "-" : money(quota)}{row.contributionPercent}%
    {i18n.t("workspace.lite.subscription.total")}{totalPercentage()}%
    +
    ) } +function LiteUsageGroup(props: { lite: NonNullable }) { + const params = useParams() + const i18n = useI18n() + const [open, setOpen] = createSignal() + const [store, setStore] = createStore({ + details: {} as Partial>, + loading: undefined as LiteUsageWindow | undefined, + }) + const items = () => + [ + { + id: "rolling", + label: i18n.t("workspace.lite.subscription.rollingUsage"), + quotaLabel: i18n.t("workspace.lite.subscription.rollingQuota"), + usage: props.lite.rollingUsage, + }, + { + id: "weekly", + label: i18n.t("workspace.lite.subscription.weeklyUsage"), + quotaLabel: i18n.t("workspace.lite.subscription.weeklyQuota"), + usage: props.lite.weeklyUsage, + }, + { + id: "monthly", + label: i18n.t("workspace.lite.subscription.monthlyUsage"), + quotaLabel: i18n.t("workspace.lite.subscription.monthlyQuota"), + usage: props.lite.monthlyUsage, + }, + ] as const + const selected = createMemo(() => items().find((item) => item.id === open())) + + async function toggle(id: LiteUsageWindow) { + if (open() === id) { + setOpen() + return + } + setOpen(id) + if (store.details[id] !== undefined) return + setStore("loading", id) + const details = await queryLiteUsageDetails(params.id!, id).catch(() => null) + setStore("details", id, details) + setStore("loading", (current) => (current === id ? undefined : current)) + } + + return ( + <> +
    + + {(item) => ( + toggle(item.id)} + /> + )} + +
    + + {(item) => { + const details = () => store.details[item().id] + return ( + +
    {i18n.t("workspace.lite.loading")}
    +
    + } + > + {(usage) => ( + + )} +
    + ) + }} + + + ) +} + export function LiteSection(props: { lite: LiteSubscription | undefined }) { const params = useParams() const i18n = useI18n() @@ -261,11 +566,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) { .
    -
    - - - -
    +

    {i18n.t("workspace.lite.subscription.useBalance")}

    @@ -339,29 +640,35 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {

    {i18n.t("workspace.lite.promo.modelsTitle")}

      -
    • Grok 4.5
    • +
    • Grok 4.6
    • GPT 5.6 Luna
    • +
    • GLM-5.3-Flash
    • GLM-5.3
    • GLM-5.2
    • GLM-5.1
    • Kimi K3
    • Kimi K2.7 Code
    • Kimi K2.6
    • +
    • LongCat-2.0
    • MiniMax M3
    • MiniMax M2.7
    • +
    • Muse Spark 1.3 Contributor
    • Muse Spark 1.2 Contributor
    • Qwen3.8 Max
    • +
    • Qwen3.8 Flash
    • Qwen3.7 Max
    • Qwen3.7 Plus
    • Qwen3.6 Plus
    • +
    • DeepSeek V4.1 Flash — {i18n.t("go.graph.bonus", { count: 4 })}
    • DeepSeek V4 Pro
    • DeepSeek V4 Flash
    • DeepSeek V4 Flash Vision Exp
    • MiMo-V2.5
    • MiMo-V2.5-Pro
    • +
    • Hy4 preview
    • Hy3
    • -
    • Ox Alpha Free
    +

    {i18n.t("go.promo.deepseek")}

    {i18n.t("workspace.lite.promo.footer")}

    @@ -579,10 +590,7 @@ function CompareModelDetail(props: { model: ModelCatalogEntry }) { {props.model.name}
    -

    - {props.model.description ?? - `${props.model.name} is an AI model from ${formatCatalogLabName(props.model.lab)}.`} -

    +

    {props.model.description ?? `${props.model.name} is an AI model.`}

    @@ -788,9 +796,11 @@ function LabLogo(props: { lab: string; label: string; size: "large" | "small" | const iconId = () => getProviderIconId(props.lab) return ( - - + + + + ) } @@ -831,11 +841,13 @@ function buildComparisonModel( modelParam: string, catalog: ModelCatalogEntry | null, stats: StatsModelComparisonEntry | null, + catalogLabs: readonly string[], ): ComparisonModel { + const lab = catalog?.lab ?? stats?.provider ?? catalogSlug(labParam) return { name: catalog?.name ?? stats?.model ?? formatParamName(modelParam), - lab: catalog?.lab ?? stats?.provider ?? catalogSlug(labParam), - labName: formatCatalogLabName(catalog?.lab ?? stats?.provider ?? labParam), + lab, + labName: isKnownCatalogLab(lab, catalogLabs) ? formatCatalogLabName(lab) : undefined, slug: catalog?.slug ?? stats?.slug ?? catalogSlug(modelParam), catalog, stats, @@ -874,10 +886,7 @@ function buildComparisonDetailSections(models: readonly ComparisonModel[]): Comp { title: "Overview", rows: [ - comparisonDetailRow( - "Author", - models.map((model) => linkedTextCell(model.stats?.author ?? model.labName, labHref(model.lab))), - ), + comparisonDetailRow("Author", models.map(providerDetailCell)), comparisonDetailRow( "Context length", models.map((model) => limitCell(model.catalog?.limit?.context)), @@ -896,10 +905,7 @@ function buildComparisonDetailSections(models: readonly ComparisonModel[]): Comp "Output modalities", models.map((model) => textCell(formatCatalogModalities(model.catalog?.modalities.output ?? []))), ), - comparisonDetailRow( - "Providers", - models.map((model) => linkedTextCell(model.labName, labHref(model.lab))), - ), + comparisonDetailRow("Providers", models.map(providerDetailCell)), ], }, { @@ -949,6 +955,17 @@ function buildComparisonDetailSections(models: readonly ComparisonModel[]): Comp ], usage: models.map((model) => model.stats?.usage ?? []), }, + { + title: "Retention", + badge: "Week 1", + rows: [ + comparisonDetailRow( + "Returning users", + models.map((model) => retentionCell(model.stats?.weeklyRetention)), + "higher", + ), + ], + }, ] } @@ -1001,6 +1018,15 @@ function comparisonRef(model: ComparisonModel): ComparisonModelRef { } } +function comparisonModelLabel(model: ComparisonModel) { + return model.labName ? `${model.name} from ${model.labName}` : model.name +} + +function providerDetailCell(model: ComparisonModel): ComparisonDetailCell { + if (!model.labName) return textCell("") + return linkedTextCell(model.stats?.author ?? model.labName ?? "", labHref(model.lab)) +} + function textCell(value: string): ComparisonDetailCell { return { value } } @@ -1031,6 +1057,15 @@ function percentCell(value: number | undefined): ComparisonDetailCell { return value === undefined ? { value: "No usage" } : { value: formatPercent(value), score: value } } +function retentionCell(value: RetentionEntry | null | undefined): ComparisonDetailCell { + if (!value || value.rank === null) return { value: "Pending" } + return { + value: formatPercent(value.rate), + unit: `${formatTokens(value.eligibleUserWeeks)} user-weeks`, + score: value.rate, + } +} + function tokenCell(value: number | undefined, trend: number | undefined): ComparisonDetailCell { if (value === undefined) return { value: "No usage" } return { value: formatTokens(value), score: value, trend } diff --git a/packages/stats/app/src/i18n.ts b/packages/stats/app/src/i18n.ts index 1ba4f0298a68..0b8053694ead 100644 --- a/packages/stats/app/src/i18n.ts +++ b/packages/stats/app/src/i18n.ts @@ -126,12 +126,9 @@ const en = { "home.noMarketDescription": "No model rows matched this range.", "home.marketChart": "Market share by model author", "home.noData": "No data", - "home.geoTitle": "Geo Breakdown", - "home.geoDescription": "Tokens used by country.", + "home.geoTitle": "Geographic Breakdown", "home.noGeoTitle": "No geo data", "home.noGeoDescription": "No geo rows matched this range.", - "home.worldMap": "World map of token usage by country", - "home.geoMapTitle": "Geo Breakdown map", "home.unknown": "Unknown", "home.tokenCostTitle": "Token Cost", "home.tokenCostDescription": "Price per 1M tokens.", @@ -236,10 +233,8 @@ const en = { "model.averageTokensSession": "Average tokens / session", "model.cacheRatio": "Cache Ratio", "model.inputTokens": "input tokens", - "model.geoDescription": "OpenCode model tokens used by country.", "model.noGeoTitle": "No geo data", "model.noGeoDescription": "No OpenCode geo rows matched this model.", - "model.worldMap": "World map of model token usage by country", "model.peersDescription": "Nearby models by recent OpenCode token volume.", "model.noPeersTitle": "No peers", "model.noPeersDescription": "Peer rankings appear after usage lands.", diff --git a/packages/stats/app/src/i18n/ar.ts b/packages/stats/app/src/i18n/ar.ts index de9bd48f327c..b24304310272 100644 --- a/packages/stats/app/src/i18n/ar.ts +++ b/packages/stats/app/src/i18n/ar.ts @@ -108,11 +108,8 @@ export const dict = { "home.marketChart": "حصة السوق حسب مؤلف النموذج", "home.noData": "لا توجد بيانات", "home.geoTitle": "التوزيع الجغرافي", - "home.geoDescription": "الرموز المستخدمة حسب البلد.", "home.noGeoTitle": "لا توجد بيانات جغرافية", "home.noGeoDescription": "لم تطابق أي صفوف جغرافية هذا النطاق.", - "home.worldMap": "خريطة عالمية لاستخدام الرموز حسب البلد", - "home.geoMapTitle": "خريطة التوزيع الجغرافي", "home.unknown": "غير معروف", "home.tokenCostTitle": "تكلفة الرموز", "home.tokenCostDescription": "السعر لكل مليون رمز.", @@ -216,10 +213,8 @@ export const dict = { "model.tokensSession": "الرموز / الجلسة", "model.cacheRatio": "نسبة التخزين المؤقت", "model.inputTokens": "رموز الإدخال", - "model.geoDescription": "رموز نموذج OpenCode المستخدمة حسب البلد.", "model.noGeoTitle": "لا توجد بيانات جغرافية", "model.noGeoDescription": "لم تطابق أي صفوف جغرافية في OpenCode هذا النموذج.", - "model.worldMap": "خريطة عالمية لاستخدام رموز النموذج حسب البلد", "model.peersDescription": "نماذج قريبة حسب حجم رموز OpenCode الأخير.", "model.noPeersTitle": "لا توجد نماذج مشابهة", "model.noPeersDescription": "تظهر ترتيبات النماذج المشابهة بعد وصول الاستخدام.", diff --git a/packages/stats/app/src/i18n/br.ts b/packages/stats/app/src/i18n/br.ts index d129f45578cb..5bf9e44dc7e6 100644 --- a/packages/stats/app/src/i18n/br.ts +++ b/packages/stats/app/src/i18n/br.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Participação de mercado por autor do modelo", "home.noData": "Sem dados", "home.geoTitle": "Distribuição geográfica", - "home.geoDescription": "Tokens usados por país.", "home.noGeoTitle": "Sem dados geográficos", "home.noGeoDescription": "Nenhuma linha geográfica correspondeu a este intervalo.", - "home.worldMap": "Mapa-múndi do uso de tokens por país", - "home.geoMapTitle": "Mapa da distribuição geográfica", "home.unknown": "Desconhecido", "home.tokenCostTitle": "Custo de tokens", "home.tokenCostDescription": "Preço por 1 milhão de tokens.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Tokens / sessão", "model.cacheRatio": "Taxa de cache", "model.inputTokens": "tokens de entrada", - "model.geoDescription": "Tokens do modelo OpenCode usados por país.", "model.noGeoTitle": "Sem dados geográficos", "model.noGeoDescription": "Nenhuma linha geográfica do OpenCode correspondeu a este modelo.", - "model.worldMap": "Mapa-múndi do uso de tokens do modelo por país", "model.peersDescription": "Modelos próximos por volume recente de tokens do OpenCode.", "model.noPeersTitle": "Sem pares", "model.noPeersDescription": "Os rankings de pares aparecem depois que o uso chega.", diff --git a/packages/stats/app/src/i18n/da.ts b/packages/stats/app/src/i18n/da.ts index 58298bbb2a84..5fdf2841e7ba 100644 --- a/packages/stats/app/src/i18n/da.ts +++ b/packages/stats/app/src/i18n/da.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Markedsandel efter modelforfatter", "home.noData": "Ingen data", "home.geoTitle": "Geografisk opdeling", - "home.geoDescription": "Tokens brugt efter land.", "home.noGeoTitle": "Ingen geodata", "home.noGeoDescription": "Ingen georækker matchede dette interval.", - "home.worldMap": "Verdenskort over tokenbrug efter land", - "home.geoMapTitle": "Kort over geografisk opdeling", "home.unknown": "Ukendt", "home.tokenCostTitle": "Tokenomkostning", "home.tokenCostDescription": "Pris pr. 1 mio. tokens.", @@ -217,10 +214,8 @@ export const dict = { "model.tokensSession": "Tokens / session", "model.cacheRatio": "Cacheandel", "model.inputTokens": "inputtokens", - "model.geoDescription": "OpenCode-modeltokens brugt efter land.", "model.noGeoTitle": "Ingen geodata", "model.noGeoDescription": "Ingen OpenCode-georækker matchede denne model.", - "model.worldMap": "Verdenskort over modeltokenbrug efter land", "model.peersDescription": "Nærliggende modeller efter seneste OpenCode-tokenvolumen.", "model.noPeersTitle": "Ingen lignende modeller", "model.noPeersDescription": "Ranglister over lignende modeller vises, når brug lander.", diff --git a/packages/stats/app/src/i18n/de.ts b/packages/stats/app/src/i18n/de.ts index 44e83026c3b5..95a2f06fa7d8 100644 --- a/packages/stats/app/src/i18n/de.ts +++ b/packages/stats/app/src/i18n/de.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Marktanteil nach Modellautor", "home.noData": "Keine Daten", "home.geoTitle": "Geografische Aufschlüsselung", - "home.geoDescription": "Nach Land verwendete Tokens.", "home.noGeoTitle": "Keine Geodaten", "home.noGeoDescription": "Keine Geozeilen passten zu diesem Zeitraum.", - "home.worldMap": "Weltkarte der Tokennutzung nach Land", - "home.geoMapTitle": "Karte der geografischen Aufschlüsselung", "home.unknown": "Unbekannt", "home.tokenCostTitle": "Tokenkosten", "home.tokenCostDescription": "Preis pro 1 Mio. Tokens.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Tokens / Sitzung", "model.cacheRatio": "Cache-Anteil", "model.inputTokens": "Eingabetokens", - "model.geoDescription": "OpenCode-Modelltokens nach Land.", "model.noGeoTitle": "Keine Geodaten", "model.noGeoDescription": "Keine OpenCode-Geozeilen passten zu diesem Modell.", - "model.worldMap": "Weltkarte der Modelltokennutzung nach Land", "model.peersDescription": "Nahe Modelle nach aktuellem OpenCode-Tokenvolumen.", "model.noPeersTitle": "Keine Vergleichsmodelle", "model.noPeersDescription": "Vergleichsrankings erscheinen, nachdem Nutzung eingegangen ist.", diff --git a/packages/stats/app/src/i18n/es.ts b/packages/stats/app/src/i18n/es.ts index 09d90d79acca..78d2e61c24a9 100644 --- a/packages/stats/app/src/i18n/es.ts +++ b/packages/stats/app/src/i18n/es.ts @@ -108,11 +108,8 @@ export const dict = { "home.marketChart": "Cuota de mercado por autor del modelo", "home.noData": "Sin datos", "home.geoTitle": "Desglose geográfico", - "home.geoDescription": "Tokens usados por país.", "home.noGeoTitle": "Sin datos geográficos", "home.noGeoDescription": "Ninguna fila geográfica coincidió con este rango.", - "home.worldMap": "Mapa mundial del uso de tokens por país", - "home.geoMapTitle": "Mapa de desglose geográfico", "home.unknown": "Desconocido", "home.tokenCostTitle": "Coste de tokens", "home.tokenCostDescription": "Precio por 1 M de tokens.", @@ -218,10 +215,8 @@ export const dict = { "model.tokensSession": "Tokens / sesión", "model.cacheRatio": "Ratio de caché", "model.inputTokens": "tokens de entrada", - "model.geoDescription": "Tokens del modelo de OpenCode usados por país.", "model.noGeoTitle": "Sin datos geográficos", "model.noGeoDescription": "Ninguna fila geográfica de OpenCode coincidió con este modelo.", - "model.worldMap": "Mapa mundial del uso de tokens del modelo por país", "model.peersDescription": "Modelos cercanos por volumen reciente de tokens de OpenCode.", "model.noPeersTitle": "Sin modelos similares", "model.noPeersDescription": "Las clasificaciones de modelos similares aparecen después de que llegue uso.", diff --git a/packages/stats/app/src/i18n/fr.ts b/packages/stats/app/src/i18n/fr.ts index bb87d056644a..bbc1d5cca718 100644 --- a/packages/stats/app/src/i18n/fr.ts +++ b/packages/stats/app/src/i18n/fr.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Part de marché par auteur de modèle", "home.noData": "Aucune donnée", "home.geoTitle": "Répartition géographique", - "home.geoDescription": "Tokens utilisés par pays.", "home.noGeoTitle": "Aucune donnée géographique", "home.noGeoDescription": "Aucune ligne géographique ne correspondait à cette période.", - "home.worldMap": "Carte mondiale de l'utilisation des tokens par pays", - "home.geoMapTitle": "Carte de répartition géographique", "home.unknown": "Inconnu", "home.tokenCostTitle": "Coût des tokens", "home.tokenCostDescription": "Prix par million de tokens.", @@ -220,10 +217,8 @@ export const dict = { "model.tokensSession": "Tokens / session", "model.cacheRatio": "Taux de cache", "model.inputTokens": "tokens d'entrée", - "model.geoDescription": "Tokens du modèle OpenCode utilisés par pays.", "model.noGeoTitle": "Aucune donnée géographique", "model.noGeoDescription": "Aucune ligne géographique OpenCode ne correspondait à ce modèle.", - "model.worldMap": "Carte mondiale de l'utilisation des tokens du modèle par pays", "model.peersDescription": "Modèles proches par volume récent de tokens OpenCode.", "model.noPeersTitle": "Aucun modèle proche", "model.noPeersDescription": "Les classements de modèles proches apparaissent après l'arrivée de l'utilisation.", diff --git a/packages/stats/app/src/i18n/it.ts b/packages/stats/app/src/i18n/it.ts index b815f1681bfb..e4a4b12969fc 100644 --- a/packages/stats/app/src/i18n/it.ts +++ b/packages/stats/app/src/i18n/it.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Quota di mercato per autore del modello", "home.noData": "Nessun dato", "home.geoTitle": "Ripartizione geografica", - "home.geoDescription": "Token usati per paese.", "home.noGeoTitle": "Nessun dato geografico", "home.noGeoDescription": "Nessuna riga geografica corrispondeva a questo intervallo.", - "home.worldMap": "Mappa mondiale dell'utilizzo dei token per paese", - "home.geoMapTitle": "Mappa della ripartizione geografica", "home.unknown": "Sconosciuto", "home.tokenCostTitle": "Costo token", "home.tokenCostDescription": "Prezzo per 1 M di token.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Token / sessione", "model.cacheRatio": "Rapporto cache", "model.inputTokens": "token di input", - "model.geoDescription": "Token del modello OpenCode usati per paese.", "model.noGeoTitle": "Nessun dato geografico", "model.noGeoDescription": "Nessuna riga geografica OpenCode corrispondeva a questo modello.", - "model.worldMap": "Mappa mondiale dell'utilizzo dei token del modello per paese", "model.peersDescription": "Modelli vicini per volume recente di token OpenCode.", "model.noPeersTitle": "Nessun modello simile", "model.noPeersDescription": "Le classifiche dei modelli simili appaiono dopo l'arrivo dell'utilizzo.", diff --git a/packages/stats/app/src/i18n/ja.ts b/packages/stats/app/src/i18n/ja.ts index 57db5511abf3..1826329523aa 100644 --- a/packages/stats/app/src/i18n/ja.ts +++ b/packages/stats/app/src/i18n/ja.ts @@ -111,11 +111,8 @@ export const dict = { "home.marketChart": "モデル作者別マーケットシェア", "home.noData": "データなし", "home.geoTitle": "地域別内訳", - "home.geoDescription": "国別のトークン使用量。", "home.noGeoTitle": "地域データがありません", "home.noGeoDescription": "この期間に一致する地域行はありません。", - "home.worldMap": "国別トークン使用量の世界地図", - "home.geoMapTitle": "地域別内訳マップ", "home.unknown": "不明", "home.tokenCostTitle": "トークンコスト", "home.tokenCostDescription": "100万トークンあたりの価格。", @@ -220,10 +217,8 @@ export const dict = { "model.tokensSession": "トークン / セッション", "model.cacheRatio": "キャッシュ比率", "model.inputTokens": "入力トークン", - "model.geoDescription": "国別のOpenCodeモデルのトークン使用量。", "model.noGeoTitle": "地域データがありません", "model.noGeoDescription": "このモデルに一致するOpenCode地域行はありません。", - "model.worldMap": "国別モデル別トークン使用量の世界地図", "model.peersDescription": "最近のOpenCodeトークン量が近いモデル。", "model.noPeersTitle": "類似モデルがありません", "model.noPeersDescription": "使用量が届くと類似モデルのランキングが表示されます。", diff --git a/packages/stats/app/src/i18n/ko.ts b/packages/stats/app/src/i18n/ko.ts index 92003e0221db..d55ba5a606d5 100644 --- a/packages/stats/app/src/i18n/ko.ts +++ b/packages/stats/app/src/i18n/ko.ts @@ -111,11 +111,8 @@ export const dict = { "home.marketChart": "모델 작성자별 시장 점유율", "home.noData": "데이터 없음", "home.geoTitle": "지역별 분포", - "home.geoDescription": "국가별 사용 토큰입니다.", "home.noGeoTitle": "지역 데이터 없음", "home.noGeoDescription": "이 범위에 맞는 지역 행이 없습니다.", - "home.worldMap": "국가별 토큰 사용량 세계 지도", - "home.geoMapTitle": "지역별 분포 지도", "home.unknown": "알 수 없음", "home.tokenCostTitle": "토큰 비용", "home.tokenCostDescription": "100만 토큰당 가격입니다.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "토큰 / 세션", "model.cacheRatio": "캐시 비율", "model.inputTokens": "입력 토큰", - "model.geoDescription": "국가별 OpenCode 모델 토큰 사용량입니다.", "model.noGeoTitle": "지역 데이터 없음", "model.noGeoDescription": "이 모델과 일치하는 OpenCode 지역 행이 없습니다.", - "model.worldMap": "국가별 모델 토큰 사용량 세계 지도", "model.peersDescription": "최근 OpenCode 토큰 볼륨이 가까운 모델입니다.", "model.noPeersTitle": "비슷한 모델 없음", "model.noPeersDescription": "사용량이 들어오면 비슷한 모델 순위가 표시됩니다.", diff --git a/packages/stats/app/src/i18n/no.ts b/packages/stats/app/src/i18n/no.ts index bdc3d80e347a..26ec3e80bfb1 100644 --- a/packages/stats/app/src/i18n/no.ts +++ b/packages/stats/app/src/i18n/no.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Markedsandel etter modellforfatter", "home.noData": "Ingen data", "home.geoTitle": "Geografisk fordeling", - "home.geoDescription": "Tokens brukt etter land.", "home.noGeoTitle": "Ingen geodata", "home.noGeoDescription": "Ingen georader matchet dette intervallet.", - "home.worldMap": "Verdenskart over tokenbruk etter land", - "home.geoMapTitle": "Kart over geografisk fordeling", "home.unknown": "Ukjent", "home.tokenCostTitle": "Tokenkostnad", "home.tokenCostDescription": "Pris per 1 mill. tokens.", @@ -218,10 +215,8 @@ export const dict = { "model.tokensSession": "Tokens / økt", "model.cacheRatio": "Cacheandel", "model.inputTokens": "inndata-tokens", - "model.geoDescription": "OpenCode-modelltokens brukt etter land.", "model.noGeoTitle": "Ingen geodata", "model.noGeoDescription": "Ingen OpenCode-georader matchet denne modellen.", - "model.worldMap": "Verdenskart over modelltokenbruk etter land", "model.peersDescription": "Nærliggende modeller etter nylig OpenCode-tokenvolum.", "model.noPeersTitle": "Ingen lignende modeller", "model.noPeersDescription": "Rangeringer for lignende modeller vises etter at bruk lander.", diff --git a/packages/stats/app/src/i18n/pl.ts b/packages/stats/app/src/i18n/pl.ts index 5bf944bcd43f..e82ddeff0b9a 100644 --- a/packages/stats/app/src/i18n/pl.ts +++ b/packages/stats/app/src/i18n/pl.ts @@ -108,11 +108,8 @@ export const dict = { "home.marketChart": "Udział w rynku według autora modelu", "home.noData": "Brak danych", "home.geoTitle": "Podział geograficzny", - "home.geoDescription": "Tokeny użyte według kraju.", "home.noGeoTitle": "Brak danych geograficznych", "home.noGeoDescription": "Żadne wiersze geograficzne nie pasowały do tego zakresu.", - "home.worldMap": "Mapa świata użycia tokenów według kraju", - "home.geoMapTitle": "Mapa podziału geograficznego", "home.unknown": "Nieznane", "home.tokenCostTitle": "Koszt tokenów", "home.tokenCostDescription": "Cena za 1 mln tokenów.", @@ -217,10 +214,8 @@ export const dict = { "model.tokensSession": "Tokeny / sesja", "model.cacheRatio": "Współczynnik cache", "model.inputTokens": "tokeny wejściowe", - "model.geoDescription": "Tokeny modelu OpenCode użyte według kraju.", "model.noGeoTitle": "Brak danych geograficznych", "model.noGeoDescription": "Żadne wiersze geograficzne OpenCode nie pasowały do tego modelu.", - "model.worldMap": "Mapa świata użycia tokenów modelu według kraju", "model.peersDescription": "Pobliskie modele według ostatniego wolumenu tokenów OpenCode.", "model.noPeersTitle": "Brak podobnych modeli", "model.noPeersDescription": "Rankingi podobnych modeli pojawią się po nadejściu użycia.", diff --git a/packages/stats/app/src/i18n/ru.ts b/packages/stats/app/src/i18n/ru.ts index a1422c8f7476..fe850a3094e7 100644 --- a/packages/stats/app/src/i18n/ru.ts +++ b/packages/stats/app/src/i18n/ru.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Доля рынка по автору модели", "home.noData": "Нет данных", "home.geoTitle": "Географический разрез", - "home.geoDescription": "Токены, использованные по странам.", "home.noGeoTitle": "Нет геоданных", "home.noGeoDescription": "Нет географических строк для этого диапазона.", - "home.worldMap": "Карта мира использования токенов по странам", - "home.geoMapTitle": "Карта географического разреза", "home.unknown": "Неизвестно", "home.tokenCostTitle": "Стоимость токенов", "home.tokenCostDescription": "Цена за 1 млн токенов.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Токены / сеанс", "model.cacheRatio": "Доля кэша", "model.inputTokens": "входные токены", - "model.geoDescription": "Токены модели OpenCode, использованные по странам.", "model.noGeoTitle": "Нет геоданных", "model.noGeoDescription": "Нет географических строк OpenCode для этой модели.", - "model.worldMap": "Карта мира использования токенов модели по странам", "model.peersDescription": "Близкие модели по недавнему объему токенов OpenCode.", "model.noPeersTitle": "Нет похожих моделей", "model.noPeersDescription": "Рейтинги похожих моделей появятся после использования.", diff --git a/packages/stats/app/src/i18n/th.ts b/packages/stats/app/src/i18n/th.ts index bfa93338487f..84f79a21f7f6 100644 --- a/packages/stats/app/src/i18n/th.ts +++ b/packages/stats/app/src/i18n/th.ts @@ -110,11 +110,8 @@ export const dict = { "home.marketChart": "ส่วนแบ่งตลาดตามผู้สร้างโมเดล", "home.noData": "ไม่มีข้อมูล", "home.geoTitle": "แยกตามภูมิศาสตร์", - "home.geoDescription": "token ที่ใช้แยกตามประเทศ", "home.noGeoTitle": "ไม่มีข้อมูลภูมิศาสตร์", "home.noGeoDescription": "ไม่มีแถวภูมิศาสตร์ที่ตรงกับช่วงเวลานี้", - "home.worldMap": "แผนที่โลกของการใช้ token แยกตามประเทศ", - "home.geoMapTitle": "แผนที่แยกตามภูมิศาสตร์", "home.unknown": "ไม่ทราบ", "home.tokenCostTitle": "ต้นทุน Token", "home.tokenCostDescription": "ราคาต่อ 1 ล้าน token", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Token / เซสชัน", "model.cacheRatio": "อัตราแคช", "model.inputTokens": "input token", - "model.geoDescription": "token ของโมเดล OpenCode ที่ใช้แยกตามประเทศ", "model.noGeoTitle": "ไม่มีข้อมูลภูมิศาสตร์", "model.noGeoDescription": "ไม่มีแถวภูมิศาสตร์ของ OpenCode ที่ตรงกับโมเดลนี้", - "model.worldMap": "แผนที่โลกของการใช้ token ของโมเดลแยกตามประเทศ", "model.peersDescription": "โมเดลใกล้เคียงตามปริมาณ token ล่าสุดของ OpenCode", "model.noPeersTitle": "ไม่มีโมเดลใกล้เคียง", "model.noPeersDescription": "อันดับโมเดลใกล้เคียงจะแสดงหลังจากมีการใช้งานเข้ามา", diff --git a/packages/stats/app/src/i18n/tr.ts b/packages/stats/app/src/i18n/tr.ts index baa992f23397..27926c9d548f 100644 --- a/packages/stats/app/src/i18n/tr.ts +++ b/packages/stats/app/src/i18n/tr.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Model yazarına göre pazar payı", "home.noData": "Veri yok", "home.geoTitle": "Coğrafi Dağılım", - "home.geoDescription": "Ülkeye göre kullanılan tokenlar.", "home.noGeoTitle": "Coğrafi veri yok", "home.noGeoDescription": "Bu aralıkla eşleşen coğrafi satır yok.", - "home.worldMap": "Ülkeye göre token kullanımının dünya haritası", - "home.geoMapTitle": "Coğrafi Dağılım haritası", "home.unknown": "Bilinmiyor", "home.tokenCostTitle": "Token Maliyeti", "home.tokenCostDescription": "1 milyon token başına fiyat.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Token / Oturum", "model.cacheRatio": "Önbellek Oranı", "model.inputTokens": "giriş tokenları", - "model.geoDescription": "Ülkeye göre kullanılan OpenCode model tokenları.", "model.noGeoTitle": "Coğrafi veri yok", "model.noGeoDescription": "Bu modelle eşleşen OpenCode coğrafi satırı yok.", - "model.worldMap": "Ülkeye göre model token kullanımının dünya haritası", "model.peersDescription": "Son OpenCode token hacmine göre yakındaki modeller.", "model.noPeersTitle": "Benzer yok", "model.noPeersDescription": "Benzer model sıralamaları kullanım geldikten sonra görünür.", diff --git a/packages/stats/app/src/i18n/uk.ts b/packages/stats/app/src/i18n/uk.ts index 5a6eb1c67777..eb43f81aa819 100644 --- a/packages/stats/app/src/i18n/uk.ts +++ b/packages/stats/app/src/i18n/uk.ts @@ -109,11 +109,8 @@ export const dict = { "home.marketChart": "Частка ринку за автором моделі", "home.noData": "Немає даних", "home.geoTitle": "Географічний розріз", - "home.geoDescription": "Токени, використані за країнами.", "home.noGeoTitle": "Немає геоданих", "home.noGeoDescription": "Жодні географічні рядки не відповідали цьому діапазону.", - "home.worldMap": "Карта світу використання токенів за країнами", - "home.geoMapTitle": "Карта географічного розрізу", "home.unknown": "Невідомо", "home.tokenCostTitle": "Вартість токенів", "home.tokenCostDescription": "Ціна за 1 млн токенів.", @@ -219,10 +216,8 @@ export const dict = { "model.tokensSession": "Токени / сеанс", "model.cacheRatio": "Частка кешу", "model.inputTokens": "вхідні токени", - "model.geoDescription": "Токени моделі OpenCode, використані за країнами.", "model.noGeoTitle": "Немає геоданих", "model.noGeoDescription": "Жодні географічні рядки OpenCode не відповідали цій моделі.", - "model.worldMap": "Карта світу використання токенів моделі за країнами", "model.peersDescription": "Близькі моделі за нещодавнім обсягом токенів OpenCode.", "model.noPeersTitle": "Немає схожих моделей", "model.noPeersDescription": "Рейтинги схожих моделей з'являться після використання.", diff --git a/packages/stats/app/src/i18n/zh.ts b/packages/stats/app/src/i18n/zh.ts index 628a4b31bf98..f41222bf82aa 100644 --- a/packages/stats/app/src/i18n/zh.ts +++ b/packages/stats/app/src/i18n/zh.ts @@ -110,11 +110,8 @@ export const dict = { "home.marketChart": "按模型作者显示的市场份额", "home.noData": "无数据", "home.geoTitle": "地理分布", - "home.geoDescription": "按国家/地区统计的 token 使用量。", "home.noGeoTitle": "无地理数据", "home.noGeoDescription": "没有符合该时间范围的地理行。", - "home.worldMap": "按国家/地区显示 token 使用量的世界地图", - "home.geoMapTitle": "地理分布地图", "home.unknown": "未知", "home.tokenCostTitle": "Token 成本", "home.tokenCostDescription": "每 100 万 token 的价格。", @@ -218,10 +215,8 @@ export const dict = { "model.tokensSession": "Token / 会话", "model.cacheRatio": "缓存比例", "model.inputTokens": "输入 token", - "model.geoDescription": "按国家/地区统计的 OpenCode 模型 token 使用量。", "model.noGeoTitle": "无地理数据", "model.noGeoDescription": "没有符合此模型的 OpenCode 地理行。", - "model.worldMap": "按国家/地区显示模型 token 使用量的世界地图", "model.peersDescription": "按近期 OpenCode token 用量排列的相近模型。", "model.noPeersTitle": "无同类模型", "model.noPeersDescription": "使用量到达后会显示同类模型排名。", diff --git a/packages/stats/app/src/i18n/zht.ts b/packages/stats/app/src/i18n/zht.ts index b8de598c72e1..9f603436726a 100644 --- a/packages/stats/app/src/i18n/zht.ts +++ b/packages/stats/app/src/i18n/zht.ts @@ -110,11 +110,8 @@ export const dict = { "home.marketChart": "按模型作者顯示的市場佔有率", "home.noData": "無數據", "home.geoTitle": "地理分布", - "home.geoDescription": "按國家/地區統計的 token 使用量。", "home.noGeoTitle": "無地理數據", "home.noGeoDescription": "沒有符合該時間範圍的地理列。", - "home.worldMap": "按國家/地區顯示 token 使用量的世界地圖", - "home.geoMapTitle": "地理分布地圖", "home.unknown": "未知", "home.tokenCostTitle": "Token 成本", "home.tokenCostDescription": "每 100 萬 token 的價格。", @@ -218,10 +215,8 @@ export const dict = { "model.tokensSession": "Token / 工作階段", "model.cacheRatio": "快取比例", "model.inputTokens": "輸入 token", - "model.geoDescription": "按國家/地區統計的 OpenCode 模型 token 使用量。", "model.noGeoTitle": "無地理數據", "model.noGeoDescription": "沒有符合此模型的 OpenCode 地理列。", - "model.worldMap": "按國家/地區顯示模型 token 使用量的世界地圖", "model.peersDescription": "按近期 OpenCode token 用量排列的相近模型。", "model.noPeersTitle": "無同類模型", "model.noPeersDescription": "使用量到達後會顯示同類模型排名。", diff --git a/packages/stats/app/src/routes/[lab]/[model].tsx b/packages/stats/app/src/routes/[lab]/[model].tsx index ad931aab1c23..6753afa3cb42 100644 --- a/packages/stats/app/src/routes/[lab]/[model].tsx +++ b/packages/stats/app/src/routes/[lab]/[model].tsx @@ -9,6 +9,7 @@ import { type ModelUsagePoint, type StatsModelData, } from "@opencode-ai/stats-core/domain/home" +import { statModel } from "@opencode-ai/stats-core/domain/model-normalization" import { createAsync, query, useParams } from "@solidjs/router" import { createMemo, createSignal, createUniqueId, For, onMount, Show, type JSX } from "solid-js" import { getRequestEvent } from "solid-js/web" @@ -16,8 +17,13 @@ import { LocaleLinks } from "../../component/locale-links" import { useI18n } from "../../context/i18n" import { useLanguage } from "../../context/language" import { localizedUrl } from "../../lib/language" -import { findModelCatalogEntry, formatCatalogLabName, loadModelCatalog, type ModelCatalogEntry } from "../model-catalog" -import { geoMapHeight, geoMapWidth, worldBorderPath, worldCountryMarkers, worldCountryPaths } from "../geo-map" +import { + findModelCatalogEntry, + formatCatalogLabName, + isKnownCatalogLab, + loadModelCatalog, + type ModelCatalogEntry, +} from "../model-catalog" import { SectionHeading } from "../section-heading" import { runStatsEffect } from "../../stats-runtime" import { setStatsPageCacheHeaders } from "../stats-cache" @@ -41,6 +47,8 @@ import { } from "../stats-shell" const statsUnfurlPath = "banner.png" +const glmFlashCatalogId = "zhipuai/glm-5.3-flash" +const glmFlashModel = "glm-5.3-flash" const shortMonths = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"] as const type IsoCountryCode = readonly [string, string, string] @@ -50,7 +58,7 @@ type ModelPageCatalog = { labs: { id: string; name: string }[] labModels: ModelCatalogOption[] } -type StatsModelPageData = Omit & { country: CountryEntry[] } +type StatsModelPageData = StatsModelData type ModelPageData = { catalog: ModelPageCatalog; stats: StatsModelPageData | null } const countryNumericIds = new Map( @@ -73,7 +81,7 @@ const getModelPageData = query(async (labParam: string, modelParam: string) => { .find((item) => item.id === (entry?.lab ?? providerSlug(labParam))) ?.models.map((item) => ({ id: item.id, lab: item.lab, slug: item.slug, name: item.name })) ?? [], }, - stats: stats ? { ...stats, country: stats.country["2M"] } : null, + stats, } satisfies ModelPageData }, "getStatsModelPageData") @@ -90,14 +98,23 @@ export default function StatsModel() { const stats = createMemo(() => page()?.stats) const githubStars = createAsync(() => getGitHubStars()) const [themePreference, setThemePreference] = createSignal("system") - const modelName = createMemo(() => catalogEntry()?.name ?? stats()?.model ?? modelParam() ?? i18n.t("model.fallback")) - const labName = createMemo(() => formatCatalogLabName(catalogEntry()?.lab ?? stats()?.provider ?? labParam())) - const modelTitle = createMemo(() => i18n.t("model.title", { model: modelName() })) - const modelDescription = createMemo(() => i18n.t("model.description", { model: modelName() })) - const modelPath = createMemo( - () => - `/data/${catalogEntry()?.id ?? [labParam(), stats()?.slug ?? modelParam()].filter((part) => part.length > 0).join("/")}`, + const canonicalModel = createMemo(() => statModel(stats()?.model ?? modelParam(), undefined)) + const modelName = createMemo( + () => catalogEntry()?.name ?? publicModelName(canonicalModel()) ?? i18n.t("model.fallback"), ) + const lab = createMemo(() => catalogEntry()?.lab ?? stats()?.provider ?? labParam()) + const catalogLabs = createMemo(() => page()?.catalog.labs.map((item) => item.id) ?? []) + const labName = createMemo(() => (isKnownCatalogLab(lab(), catalogLabs()) ? formatCatalogLabName(lab()) : undefined)) + const formerName = createMemo(() => formerModelName(canonicalModel())) + const searchModelName = createMemo(() => (formerName() ? `${modelName()} (formerly ${formerName()})` : modelName())) + const modelTitle = createMemo(() => i18n.t("model.title", { model: searchModelName() })) + const modelDescription = createMemo(() => i18n.t("model.description", { model: searchModelName() })) + const modelPath = createMemo(() => { + const fallback = formerName() + ? glmFlashCatalogId + : [labParam(), stats()?.slug ?? canonicalModel()].filter((part) => part.length > 0).join("/") + return `/data/${catalogEntry()?.id ?? fallback}` + }) const modelUrl = createMemo(() => localizedUrl(language.locale(), modelPath())) const statsUnfurlUrl = new URL(statsUnfurlPath, localizedUrl("en", "/data/")).toString() const modelHeaderLinks = createMemo(() => [ @@ -168,6 +185,7 @@ export default function StatsModel() { catalog={catalogEntry() ?? null} catalogData={page()?.catalog ?? null} labName={labName()} + formerName={formerName()} /> @@ -175,9 +193,14 @@ export default function StatsModel() { - + props.catalog?.lab ?? props.data?.provider ?? props.labName + const labId = () => props.catalog?.lab ?? props.data?.provider + const hasLab = () => props.labName !== undefined const modelName = () => props.catalog?.name ?? props.data?.model ?? i18n.t("model.fallback") const weights = () => props.catalog?.weights[0] const labs = () => props.catalogData?.labs ?? [] @@ -270,35 +295,28 @@ function ModelHero(props: { Data - / - 0} - fallback={ - - {props.labName} - - - } - > - ({ - href: language.route(`${import.meta.env.BASE_URL}${lab.id}`), - label: lab.name, - value: lab.id, - }))} - value={providerSlug(labId())} - variant="model" - /> + + / + 0} fallback={{props.labName}}> + ({ + href: language.route(`${import.meta.env.BASE_URL}${lab.id}`), + label: lab.name, + value: lab.id, + }))} + value={providerSlug(labId() ?? "")} + variant="model" + /> + / 0} fallback={ - - {modelName()} - + + {modelName()} } > @@ -317,9 +335,11 @@ function ModelHero(props: {
    - - + + + +

    {modelName()}

    @@ -337,6 +357,7 @@ function ModelHero(props: { when={props.data} fallback={

    + {(name) => {`Formerly ${name()}.`}} Listed across the shared model catalog.

    @@ -344,6 +365,7 @@ function ModelHero(props: { > {(data) => (

    + {(name) => {`Formerly ${name()}.`}} Ranked {formatHeroRank(data().rank)} @@ -471,6 +493,7 @@ function ModelMomentumSection(props: { data: StatsModelPageData | null }) { value={formatInteger(data().totals.sessions)} /> + () const data = createMemo(() => props.data) - const countryById = createMemo( - () => - new Map( - data().flatMap((country) => { - const id = countryNumericId(country.country) - return id ? [[id, country] as const] : [] - }), - ), - ) const maxTokens = createMemo(() => Math.max(0, ...data().map((country) => country.tokens)) || 1) const topCountries = createMemo(() => data().slice(0, 15)) - const active = createMemo(() => data().find((country) => country.country === activeCountry()) ?? data()[0]) return (

    - + 0} fallback={} >
    -
    - - - {(country) => ( -
    - #{String(country().rank).padStart(2, "0")} - {formatCountryName(country().country, language.tag(language.locale()), i18n)} -

    - {formatGeoTokens(country().tokens)} - {formatGeoShare(country().share)} -

    -
    - )} -
    -
    - activeCountry: string | undefined - maxTokens: number - onActiveCountryChange: (country: string | undefined) => void -}) { - const i18n = useI18n() - const opacityScale = createMemo(() => scaleSqrt().domain([0, props.maxTokens]).range([0.26, 0.96]).clamp(true)) - const countryOpacity = (country: CountryEntry | undefined) => { - if (!country || country.tokens <= 0) return 0 - const opacity = opacityScale()(country.tokens) - if (props.activeCountry === country.country) return 1 - if (!props.activeCountry) return opacity - return Math.max(0.18, opacity * 0.36) - } - - return ( - - {i18n.t("home.geoMapTitle")} - - - {(country) => { - const entry = () => props.countryById.get(country.id) - return ( - - - - - {(country) => { - const entry = () => props.countryById.get(country.id) - return ( - - - ) - }} - - - - - ) -} - function GeoCountryList(props: { data: CountryEntry[] activeCountry: string | undefined @@ -1082,7 +989,7 @@ function GeoCountryList(props: { ) } -function ModelPeersSection(props: { data: StatsModelPageData | null }) { +function ModelPeersSection(props: { data: StatsModelPageData | null; catalogLabs: readonly string[] }) { const i18n = useI18n() return (
    @@ -1095,7 +1002,9 @@ function ModelPeersSection(props: { data: StatsModelPageData | null }) { >
      - {(peer) => } + {(peer) => ( + + )}
    @@ -1113,23 +1022,29 @@ function MetricCard(props: { label: string; value: string; detail?: string; stat ) } -function PeerRow(props: { peer: ModelPeerEntry; active: boolean }) { +function PeerRow(props: { peer: ModelPeerEntry; active: boolean; catalogLabs: readonly string[] }) { const language = useLanguage() + const hasProvider = () => isKnownCatalogLab(props.peer.provider, props.catalogLabs) return (
  • {String(props.peer.rank).padStart(2, "0")} - - + + + + {props.peer.model} - {props.peer.author} + + {props.peer.author} + {formatTokens(props.peer.tokens)} @@ -1137,7 +1052,7 @@ function PeerRow(props: { peer: ModelPeerEntry; active: boolean }) { ) } -function SectionTitle(props: { href: string; title: string; description: string }) { +function SectionTitle(props: { href: string; title: string; description?: string }) { return } @@ -1152,6 +1067,7 @@ function ModelEmptyState(props: { title: string; description: string; compact?: function modelComparisonPairs( catalogModels: ModelCatalogOption[] | undefined, + catalogLabs: readonly string[], catalogEntry: ModelCatalogEntry | null, data: StatsModelPageData | null, ) { @@ -1166,7 +1082,7 @@ function modelComparisonPairs( name: peer.model, lab: peer.provider, slug: peer.slug, - labName: peer.author, + labName: isKnownCatalogLab(peer.provider, catalogLabs) ? peer.author : undefined, metric: `#${peer.rank} / ${formatTokens(peer.tokens)}`, }, detail: "Usage peer", @@ -1192,7 +1108,7 @@ function modelComparisonRef( name: data.model, lab: data.provider, slug: data.slug, - labName: data.author, + labName: undefined, metric: `#${data.rank}`, } } @@ -1554,3 +1470,13 @@ function providerSlug(provider: string) { .replace(/^-+|-+$/g, "") .replace(/-{2,}/g, "-") } + +function formerModelName(model: string) { + return statModel(model, undefined) === glmFlashModel ? "ox-alpha" : undefined +} + +function publicModelName(model: string) { + if (model === "unknown") return undefined + if (model === glmFlashModel) return "GLM-5.3-Flash" + return model +} diff --git a/packages/stats/app/src/routes/compare-cards.tsx b/packages/stats/app/src/routes/compare-cards.tsx index 24077bc3b9c4..7861f9b32fa1 100644 --- a/packages/stats/app/src/routes/compare-cards.tsx +++ b/packages/stats/app/src/routes/compare-cards.tsx @@ -119,17 +119,24 @@ function ComparisonCardIcon() { } function ComparisonPanelCard(props: { pair: ComparisonPair }) { + const firstLabName = () => props.pair.first.labName + const secondLabName = () => props.pair.second.labName + return ( {props.pair.detail} {props.pair.first.name} vs {props.pair.second.name} -

    - {props.pair.first.labName ?? formatCatalogLabName(props.pair.first.lab)} - - {props.pair.second.labName ?? formatCatalogLabName(props.pair.second.lab)} -

    + +

    + {(name) => {name()}} + + + + {(name) => {name()}} +

    +
    {props.pair.first.metric ?? "Listed"} / {props.pair.second.metric ?? "Listed"} @@ -143,14 +150,11 @@ function ComparisonLabLogo(props: { model: ComparisonModelRef }) { const iconId = () => providerIconId(props.model.lab) return ( - - + + + + ) } diff --git a/packages/stats/app/src/routes/compare-radar.tsx b/packages/stats/app/src/routes/compare-radar.tsx index d7403809ac87..35ada12885f1 100644 --- a/packages/stats/app/src/routes/compare-radar.tsx +++ b/packages/stats/app/src/routes/compare-radar.tsx @@ -9,7 +9,7 @@ const toolUseBenchmarkPattern = /(terminal bench|claw eval|tau ?(?:bench|2|3))/ export type ComparisonRadarModel = { name: string - labName: string + labName?: string catalog: ModelCatalogEntry | null } @@ -61,7 +61,7 @@ export function ComparisonRadar(props: ComparisonRadarProps) {