mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-24 17:41:37 +08:00
Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03c40d3fe1 | |||
| 3dd1b30539 | |||
| e80db11160 | |||
| a7f2373a05 | |||
| b02acc1e30 | |||
| 5a8335857b | |||
| 88c6c7abc7 | |||
| a6183af6f3 | |||
| 2e018f70f2 | |||
| 1364769e51 | |||
| 350c726aa8 | |||
| ee91f190c5 | |||
| 501ff62cbf | |||
| e03db9bc69 | |||
| 403627b031 | |||
| 5cf4e13588 | |||
| 4ae176520d | |||
| a74c472ffb | |||
| a97622c801 | |||
| 7f209437b5 | |||
| 228e9095ba | |||
| df23b7f948 | |||
| 631f67a9f3 | |||
| 199a4cdbea | |||
| a453386e9d | |||
| ca34cecc03 | |||
| 95daf90670 | |||
| 0b934e9516 | |||
| 5de2f241c6 | |||
| 193de13a88 | |||
| 43e89ea167 | |||
| b7ca4f91d9 | |||
| b3f1a96c6d | |||
| 859106eb17 | |||
| 486e8460b1 | |||
| 28a62b7148 | |||
| a9a6fad0fa | |||
| b6914b39db | |||
| 72635a3720 | |||
| 9f8db119fc | |||
| f69beceaff | |||
| 830d5eb535 | |||
| 5cd8e68fdd | |||
| dff8fbc149 | |||
| ac1758c0e6 | |||
| d6855b6b47 | |||
| eebd85f5d1 | |||
| ecbc6ccac8 | |||
| 0b082b065d | |||
| 57ef382843 | |||
| f914cac3d4 | |||
| 53fec37d8d | |||
| a03bba5f2c | |||
| 56d4fb24a1 | |||
| 13e3744f76 | |||
| e207624c48 | |||
| ea2d59d7ca | |||
| bec9ee41af | |||
| c470c79513 | |||
| 23ec4f55c8 | |||
| 337fd144d2 | |||
| 1ddaeec480 | |||
| 7c2199d84a | |||
| bbd72fb8b0 | |||
| e2894562f8 | |||
| 70b4ca8c18 | |||
| 4db1eec321 | |||
| 79903a4cf7 | |||
| e8548ae8b2 | |||
| 7774580e61 | |||
| 5b1e31988e | |||
| 02a167e048 | |||
| 500c46ec79 | |||
| 5cf9f517cf | |||
| 5d5c35ee71 | |||
| 51f86c8537 | |||
| 20a7743876 | |||
| 31afdd5938 | |||
| 4178fd74f1 | |||
| 3f31139064 | |||
| 475b408119 | |||
| 70f74112e3 | |||
| 03cb632435 | |||
| dd417f1a6b | |||
| 8b9f89e7e8 | |||
| c0f09afef5 | |||
| 8a6cf2c9aa | |||
| a935432b5c | |||
| 7561b4a050 | |||
| d8eb3b80fb | |||
| 08c483dc36 | |||
| d2efd81fb3 | |||
| 79d503150c | |||
| f12e14cf16 | |||
| b578b7261f | |||
| bbe4c952d7 | |||
| 3a9d4e78b6 | |||
| 8d1f8916d3 | |||
| 05028334b2 | |||
| b04697366f | |||
| 4eb29a64f0 |
@@ -20,3 +20,4 @@ Slickstef11
|
||||
usrnk1
|
||||
vimtor
|
||||
StarpTech
|
||||
vaprdev
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@corvu/drawer": "catalog:",
|
||||
"@dnd-kit/abstract": "0.5.0",
|
||||
@@ -96,7 +96,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"bin": {
|
||||
"lildax": "./bin/lildax.cjs",
|
||||
},
|
||||
@@ -144,7 +144,7 @@
|
||||
},
|
||||
"packages/codemode": {
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"acorn": "8.15.0",
|
||||
"effect": "catalog:",
|
||||
@@ -158,7 +158,7 @@
|
||||
},
|
||||
"packages/console/app": {
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@ibm/plex": "6.4.1",
|
||||
@@ -195,7 +195,7 @@
|
||||
},
|
||||
"packages/console/core": {
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
@@ -222,7 +222,7 @@
|
||||
},
|
||||
"packages/console/function": {
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "3.0.82",
|
||||
"@ai-sdk/openai": "3.0.48",
|
||||
@@ -245,7 +245,7 @@
|
||||
},
|
||||
"packages/console/mail": {
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
@@ -269,7 +269,7 @@
|
||||
},
|
||||
"packages/console/support": {
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "1.15.2",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
@@ -289,7 +289,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@opencode-ai/core",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -297,20 +297,20 @@
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.166",
|
||||
"@ai-sdk/anthropic": "3.0.111",
|
||||
"@ai-sdk/azure": "3.0.88",
|
||||
"@ai-sdk/azure": "3.0.93",
|
||||
"@ai-sdk/cerebras": "2.0.41",
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/gateway": "3.0.191",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@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",
|
||||
"@ai-sdk/provider-utils": "4.0.23",
|
||||
"@ai-sdk/provider": "3.0.16",
|
||||
"@ai-sdk/provider-utils": "4.0.51",
|
||||
"@ai-sdk/togetherai": "2.0.41",
|
||||
"@ai-sdk/vercel": "2.0.39",
|
||||
"@ai-sdk/xai": "3.0.102",
|
||||
@@ -341,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",
|
||||
@@ -383,7 +383,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@opencode-ai/desktop",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@zip.js/zip.js": "2.7.62",
|
||||
"drizzle-orm": "catalog:",
|
||||
@@ -437,7 +437,7 @@
|
||||
},
|
||||
"packages/effect-drizzle-sqlite": {
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"drizzle-orm": "catalog:",
|
||||
"effect": "catalog:",
|
||||
@@ -451,7 +451,7 @@
|
||||
},
|
||||
"packages/effect-sqlite-node": {
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"effect": "catalog:",
|
||||
},
|
||||
@@ -463,7 +463,7 @@
|
||||
},
|
||||
"packages/enterprise": {
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@hono/standard-validator": "catalog:",
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
@@ -495,7 +495,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "catalog:",
|
||||
@@ -511,7 +511,7 @@
|
||||
},
|
||||
"packages/http-recorder": {
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@effect/platform-node": "4.0.0-beta.83",
|
||||
"@effect/platform-node-shared": "4.0.0-beta.83",
|
||||
@@ -542,7 +542,7 @@
|
||||
},
|
||||
"packages/llm": {
|
||||
"name": "@opencode-ai/llm",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@opencode-ai/schema": "workspace:*",
|
||||
"@smithy/eventstream-codec": "4.2.14",
|
||||
@@ -561,7 +561,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -572,19 +572,19 @@
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.166",
|
||||
"@ai-sdk/anthropic": "3.0.111",
|
||||
"@ai-sdk/azure": "3.0.88",
|
||||
"@ai-sdk/azure": "3.0.93",
|
||||
"@ai-sdk/cerebras": "2.0.60",
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/gateway": "3.0.191",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@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",
|
||||
"@ai-sdk/provider": "3.0.16",
|
||||
"@ai-sdk/togetherai": "2.0.41",
|
||||
"@ai-sdk/vercel": "2.0.39",
|
||||
"@ai-sdk/xai": "3.0.102",
|
||||
@@ -634,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",
|
||||
@@ -692,7 +692,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@ai-sdk/provider": "3.0.8",
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
@@ -768,7 +768,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"cross-spawn": "catalog:",
|
||||
},
|
||||
@@ -783,7 +783,7 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@opencode-ai/server",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/protocol": "workspace:*",
|
||||
@@ -798,7 +798,7 @@
|
||||
},
|
||||
"packages/session-ui": {
|
||||
"name": "@opencode-ai/session-ui",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
|
||||
@@ -838,7 +838,7 @@
|
||||
},
|
||||
"packages/slack": {
|
||||
"name": "@opencode-ai/slack",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
"@slack/bolt": "^3.17.1",
|
||||
@@ -851,7 +851,7 @@
|
||||
},
|
||||
"packages/stats/app": {
|
||||
"name": "@opencode-ai/stats-app",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
@@ -878,7 +878,7 @@
|
||||
},
|
||||
"packages/stats/core": {
|
||||
"name": "@opencode-ai/stats-core",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-athena": "3.933.0",
|
||||
"@planetscale/database": "1.19.0",
|
||||
@@ -897,7 +897,7 @@
|
||||
},
|
||||
"packages/stats/server": {
|
||||
"name": "@opencode-ai/stats-server",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-firehose": "3.933.0",
|
||||
"@effect/platform-node": "catalog:",
|
||||
@@ -939,7 +939,7 @@
|
||||
},
|
||||
"packages/tui": {
|
||||
"name": "@opencode-ai/tui",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@opencode-ai/core": "workspace:*",
|
||||
"@opencode-ai/plugin": "workspace:*",
|
||||
@@ -966,7 +966,7 @@
|
||||
},
|
||||
"packages/ui": {
|
||||
"name": "@opencode-ai/ui",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@kobalte/core": "catalog:",
|
||||
"@pierre/diffs": "catalog:",
|
||||
@@ -1017,7 +1017,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode-ai/web",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -1064,6 +1064,7 @@
|
||||
"@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",
|
||||
@@ -1175,7 +1176,7 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -1185,13 +1186,13 @@
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@ai-sdk/fireworks": ["@ai-sdk/fireworks@2.0.76", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.67", "@ai-sdk/provider": "3.0.15", "@ai-sdk/provider-utils": "4.0.45" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-yg1ulgemh6BLMrs2vBNbtjV70NhenFI3Z7psB7s94FOEcGevvyV1qdFoqsgBZ7QyuUGwnC645c+eBFFtPAr5SQ=="],
|
||||
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="],
|
||||
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.191", "", { "dependencies": { "@ai-sdk/provider": "3.0.16", "@ai-sdk/provider-utils": "4.0.51", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-7f9h+Vb/kmBTiqj6wtoKZbOuckvnpRp/eoxzYZg0GOAfYkdnixs+8JTGW3+nlHZXza17riNL5FpC0pEuh8aV1Q=="],
|
||||
|
||||
"@ai-sdk/google": ["@ai-sdk/google@3.0.73", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o2MuIeyvZrFIeIbnbA8Thrr63irdyUBh0uWBZ2lY6yFeXuE/tcwyXF74bDKS4KvTu84uFpQfpbS/LXHGKKXz+g=="],
|
||||
|
||||
@@ -1207,9 +1208,9 @@
|
||||
|
||||
"@ai-sdk/perplexity": ["@ai-sdk/perplexity@3.0.26", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-dXzrVsLR5f6tr+U04jq4AXoRroGFBTvODnLgss0SWbzNjGGQg3XqtQ9j7rCLo6o8qbYGuAHvqUrIpUCuiscuFg=="],
|
||||
|
||||
"@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
"@ai-sdk/provider": ["@ai-sdk/provider@3.0.16", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-9Av6kg0t/IN/dcYAAmEJ4B9OPhcEJqwSR+GfHEA8olRkindXpUHadc3p3cyvgFUQFTVm1G5thtsmgZ9yVb2w3A=="],
|
||||
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "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-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
|
||||
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.51", "", { "dependencies": { "@ai-sdk/provider": "3.0.16", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8", "undici": "^6.28.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ukLTs9x1Xm6lxSIwbJIxYQxbZHmVeIczNntARZrBcOa6pjdBhqeZnATNnJMHa7M9dZ8Ji5NJbpKpvz7o5XyBtg=="],
|
||||
|
||||
"@ai-sdk/togetherai": ["@ai-sdk/togetherai@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-k3p9e3k0/gpDDyTtvafsK4HYR4D/aUQW/kzCwWo1+CzdBU84i4L14gWISC/mv6tgSicMXHcEUd521fPufQwNlg=="],
|
||||
|
||||
@@ -3845,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=="],
|
||||
|
||||
@@ -5631,6 +5632,10 @@
|
||||
|
||||
"@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/alibaba/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/alibaba/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "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-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
|
||||
|
||||
"@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=="],
|
||||
@@ -5645,25 +5650,31 @@
|
||||
|
||||
"@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": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
"@ai-sdk/cohere/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/cohere/@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=="],
|
||||
|
||||
"@ai-sdk/deepgram/@ai-sdk/provider": ["@ai-sdk/provider@3.0.15", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-XeZW1CcDF2GMbH4wejW6xBRI2QCOgnkVYUnxoeDadB1mf85riL2bMUeDoh+6gJ/r4mjNfzUPW8OjLjvwTP0u1Q=="],
|
||||
|
||||
"@ai-sdk/deepgram/@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-sdk/deepinfra/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/deepinfra/@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=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -5687,22 +5698,36 @@
|
||||
|
||||
"@ai-sdk/google-vertex/@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-sdk/groq/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/groq/@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=="],
|
||||
|
||||
"@ai-sdk/mistral/@ai-sdk/provider": ["@ai-sdk/provider@3.0.14", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-5X1k57JBJ4H7H1QjX7CnJYAB1I19r/trVZTMcSms7/kLNZ8RaU4Nt2agcwZzv82Hfx6Q7/TOLU7agAKeFfc8cA=="],
|
||||
|
||||
"@ai-sdk/mistral/@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/openai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/openai/@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=="],
|
||||
|
||||
"@ai-sdk/openai-compatible/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/openai-compatible/@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=="],
|
||||
|
||||
"@ai-sdk/perplexity/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/perplexity/@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=="],
|
||||
|
||||
"@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/provider-utils/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="],
|
||||
|
||||
"@ai-sdk/togetherai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/togetherai/@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=="],
|
||||
|
||||
"@ai-sdk/vercel/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@ai-sdk/vercel/@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=="],
|
||||
|
||||
"@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.56", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cQrN6OUn/jvsY3OdsU6Wn+ss7vp1iwIcakZKSlSRMnYqShBfyT7Qht+eqmgxs7w9ttrw6FAG6o11AiBs+iEsTA=="],
|
||||
@@ -6019,7 +6044,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=="],
|
||||
|
||||
@@ -6033,6 +6058,8 @@
|
||||
|
||||
"@opencode-ai/llm/@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=="],
|
||||
|
||||
"@opencode-ai/plugin/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@opencode-ai/script/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
|
||||
|
||||
"@opencode-ai/session-ui/@opencode-ai/client": ["@opencode-ai/client@../app/vendor/opencode-ai-client-1.17.13-v2.tgz", {}, "sha512-332kgNifvpQOF9e3UA+pIa5xPrMhLaQkUiNiO+meS0Ba9HjSE6hfsWnEojMkD0DPSLqPP6rCF1dDoF7U0Y0OCQ=="],
|
||||
@@ -6159,12 +6186,22 @@
|
||||
|
||||
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="],
|
||||
|
||||
"ai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"ai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "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-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
|
||||
|
||||
"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/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=="],
|
||||
@@ -6395,7 +6432,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=="],
|
||||
|
||||
@@ -6569,6 +6606,8 @@
|
||||
|
||||
"@actions/github/@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@12.6.0", "", { "dependencies": { "@octokit/openapi-types": "^20.0.0" } }, "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw=="],
|
||||
|
||||
"@ai-sdk/alibaba/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@ai-sdk/amazon-bedrock/@ai-sdk/provider-utils/undici": ["undici@6.28.0", "", {}, "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA=="],
|
||||
@@ -6915,7 +6954,11 @@
|
||||
|
||||
"@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/core/@ai-sdk/openai-compatible/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"@opencode-ai/core/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "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-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
|
||||
|
||||
"@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=="],
|
||||
|
||||
@@ -6985,6 +7028,10 @@
|
||||
|
||||
"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/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/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=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/cerebras/@ai-sdk/provider": ["@ai-sdk/provider@3.0.12", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-sj9DWTJ2Ze0WR9qsiOPqoqzNx3OxL6iMxHImbhvoe9qOspekbzxNDMiJ4TIGfYHYh9w4OmBjz3prvqhzTi96+Q=="],
|
||||
@@ -6995,6 +7042,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=="],
|
||||
@@ -7011,6 +7062,8 @@
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/perplexity/@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/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
|
||||
|
||||
"ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
|
||||
@@ -7163,7 +7216,11 @@
|
||||
|
||||
"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=="],
|
||||
|
||||
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="],
|
||||
|
||||
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "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-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="],
|
||||
|
||||
"p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
|
||||
|
||||
@@ -7413,6 +7470,8 @@
|
||||
|
||||
"@octokit/rest/@octokit/core/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@27.0.0", "", {}, "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA=="],
|
||||
|
||||
"@opencode-ai/core/@ai-sdk/openai-compatible/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@opencode-ai/core/@ai-sdk/openai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@opencode-ai/desktop/@actions/artifact/@actions/http-client/undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="],
|
||||
@@ -7431,12 +7490,16 @@
|
||||
|
||||
"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/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=="],
|
||||
|
||||
"ai-gateway-provider/@ai-sdk/cohere/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"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=="],
|
||||
@@ -7521,6 +7584,8 @@
|
||||
|
||||
"opencode/@ai-sdk/cerebras/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"opencode/@ai-sdk/openai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"pkg-dir/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
|
||||
|
||||
+5
-2
@@ -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,
|
||||
@@ -244,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")
|
||||
@@ -273,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,
|
||||
|
||||
+4
-4
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"nodeModules": {
|
||||
"x86_64-linux": "sha256-SVvFPO+KuS67+6XGPhaB3cIuc3XUyM0XVccy5v8afS4=",
|
||||
"aarch64-linux": "sha256-HJRrSu5u0TEg214d2RAbM1C+nmrxvIR/d7JlSBOGb9I=",
|
||||
"aarch64-darwin": "sha256-ytmHSdC0PVGAJSbpt/+YwL5ySF3w6r/9uZpZACPppkI=",
|
||||
"x86_64-darwin": "sha256-ma7K4K+xn7Jz3+YPA/n8ly1o308UNoM/DO9Z8yZSAKE="
|
||||
"x86_64-linux": "sha256-FY/I7zxmWA4tMvFZG5WijdqBcDc0No3a/YmKuxlluNg=",
|
||||
"aarch64-linux": "sha256-a8FrM4BYf4u5MIswlWz6Jg+WX3yRyGwwBaBnRssSkAo=",
|
||||
"aarch64-darwin": "sha256-/njY2BbhqncqQ+gODoTmhBJKTT22VCFAhh8L3kwgHfU=",
|
||||
"x86_64-darwin": "sha256-KGCH9XI/es3r93wwObkQr2+P6J9LaQG8lSvpehG0ebI="
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -162,6 +162,7 @@
|
||||
"@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/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/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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
import { base64Encode } from "@opencode-ai/core/util/encode"
|
||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
||||
|
||||
const draftID = "draft_legacy_new_session"
|
||||
const directory = "C:/OpenCode/LegacyNewSession"
|
||||
const server = `http://${process.env.PLAYWRIGHT_SERVER_HOST ?? "127.0.0.1"}:${process.env.PLAYWRIGHT_SERVER_PORT ?? "4096"}`
|
||||
|
||||
test("redirects a draft to the legacy new-session route", async ({ page }) => {
|
||||
await mockOpenCodeServer(page, {
|
||||
directory,
|
||||
project: {
|
||||
id: "proj_legacy_new_session",
|
||||
worktree: directory,
|
||||
vcs: "git",
|
||||
name: "legacy-new-session",
|
||||
time: { created: 1700000000000, updated: 1700000000000 },
|
||||
sandboxes: [],
|
||||
},
|
||||
provider: { all: [], connected: [], default: {} },
|
||||
sessions: [],
|
||||
pageMessages: () => ({ items: [] }),
|
||||
})
|
||||
await page.addInitScript(
|
||||
({ directory, draftID, server }) => {
|
||||
localStorage.setItem("settings.v3", JSON.stringify({ general: { newLayoutDesigns: false } }))
|
||||
localStorage.setItem("app-version.v1", JSON.stringify({ version: "1.17.20" }))
|
||||
localStorage.setItem(
|
||||
"opencode.window.browser.dat:tabs",
|
||||
JSON.stringify([{ type: "draft", draftID, server, directory }]),
|
||||
)
|
||||
},
|
||||
{ directory, draftID, server },
|
||||
)
|
||||
|
||||
await page.goto(`/new-session?draftId=${draftID}`)
|
||||
|
||||
await expect(page).toHaveURL(`/${base64Encode(directory)}/session`)
|
||||
await expect(page.locator("header[data-tauri-drag-region]")).toBeVisible()
|
||||
await expect(page.locator('[data-component="prompt-input"]')).toBeVisible()
|
||||
})
|
||||
@@ -128,7 +128,7 @@ test.describe("session timeline projection", () => {
|
||||
await expect(page.locator('[data-timeline-row="TurnGap"]')).toBeVisible()
|
||||
})
|
||||
|
||||
test("renders comment strips and historical diff summary overflow", async ({ page }) => {
|
||||
test("renders inline comments and historical diff summary overflow", async ({ page }) => {
|
||||
const user = userMessage(
|
||||
[
|
||||
userText("The user made the following comment regarding lines 4 through 8 of src/a.ts: Keep this stable", {
|
||||
@@ -152,14 +152,10 @@ test.describe("session timeline projection", () => {
|
||||
parentID: "msg_2000_diff_next_user",
|
||||
created: 1700000011000,
|
||||
})
|
||||
await setupTimeline(page, {
|
||||
messages: [user, assistantMessage(), nextUser, nextAssistant],
|
||||
settings: { newLayoutDesigns: false },
|
||||
})
|
||||
await setupTimeline(page, { messages: [user, assistantMessage(), nextUser, nextAssistant] })
|
||||
const scroller = page.locator(".scroll-view__viewport", { has: page.locator("[data-timeline-row]") })
|
||||
await scroller.evaluate((element) => (element.scrollTop = 0))
|
||||
|
||||
await expect(page.locator('[data-timeline-row="CommentStrip"]')).toBeVisible()
|
||||
await expect(page.getByText("Keep this stable", { exact: true })).toBeVisible()
|
||||
await expect(page.locator('[data-timeline-row="DiffSummary"]')).toBeVisible()
|
||||
await expect(page.getByText(/show all/i)).toBeVisible()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/app",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"description": "",
|
||||
"type": "module",
|
||||
"exports": {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 })}
|
||||
>
|
||||
<Show when={state.opening()} fallback={<AppIcon id={state.current().icon} class="size-[18px]" />}>
|
||||
<Show
|
||||
when={state.opening()}
|
||||
fallback={<AppIcon id={state.current().icon} style={{ width: "20px", height: "20px" }} />}
|
||||
>
|
||||
<Spinner class="size-3.5" />
|
||||
</Show>
|
||||
</SplitButtonV2Action>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/cli",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/codemode",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"description": "Effect-native confined code execution over schema-described tools",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-app",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,266 +1,165 @@
|
||||
import { For, createSignal, onCleanup, onMount } from "solid-js"
|
||||
import "./limits-graph.css"
|
||||
import { For, Show, createMemo, createSignal, createUniqueId, onCleanup, onMount } from "solid-js"
|
||||
import { useI18n } from "~/context/i18n"
|
||||
import { RollingNumber } from "./rolling-number"
|
||||
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 }) {
|
||||
let root!: HTMLElement
|
||||
const [visible, setVisible] = createSignal(false)
|
||||
const [boosted, setBoosted] = createSignal(false)
|
||||
const [promoted, setPromoted] = createSignal<string[]>([])
|
||||
let timer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
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(() => {
|
||||
const motion = window.matchMedia("(prefers-reduced-motion: reduce)")
|
||||
const finish = () => {
|
||||
if (!motion.matches) return
|
||||
clearTimeout(timer)
|
||||
setVisible(true)
|
||||
setBoosted(true)
|
||||
setPromoted(bonuses.map((model) => model.id))
|
||||
}
|
||||
motion.addEventListener("change", finish)
|
||||
onCleanup(() => {
|
||||
clearTimeout(timer)
|
||||
motion.removeEventListener("change", finish)
|
||||
})
|
||||
if (motion.matches) return finish()
|
||||
if (typeof IntersectionObserver === "undefined") return setVisible(true)
|
||||
if (!window.IntersectionObserver) return setVisible(true)
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const entry = entries[0]
|
||||
if (!entry?.isIntersecting || entry.intersectionRatio < 0.35) return
|
||||
if (!entries.some((entry) => entry.isIntersecting)) return
|
||||
setVisible(true)
|
||||
observer.disconnect()
|
||||
},
|
||||
{ threshold: 0.35 },
|
||||
{ threshold: 0.1 },
|
||||
)
|
||||
observer.observe(root)
|
||||
onCleanup(() => observer.disconnect())
|
||||
})
|
||||
|
||||
const baseline = 100
|
||||
const graph = [
|
||||
{ id: "kimi-k3", name: "Kimi K3", req: 110 },
|
||||
{ id: "grok-4.6", name: "Grok 4.6", req: 169 },
|
||||
{ id: "hy4-preview", name: "Hy4 preview", req: 1350 },
|
||||
{ id: "gpt-5.6-luna", name: "GPT 5.6 Luna", req: 2050 },
|
||||
{ id: "glm-5.3-flash", name: "GLM-5.3-Flash", req: 3160, baseReq: 1580, bonus: "2x usage" },
|
||||
{ id: "minimax-m3", name: "MiniMax M3", req: 3200 },
|
||||
{ id: "qwen3.7-plus", name: "Qwen3.7 Plus", req: 4300 },
|
||||
{ id: "hy3", name: "Hy3", req: 4300 },
|
||||
{ id: "qwen3.8-flash", name: "Qwen3.8 Flash", req: 5400 },
|
||||
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 7600 },
|
||||
{ id: "longcat-2.0", name: "LongCat-2.0", req: 11400 },
|
||||
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100 },
|
||||
{ id: "muse-spark-1.3-contributor", name: "Muse Spark 1.3 Contributor", req: 45300, edge: true },
|
||||
].map((model, index) => ({ ...model, d: `${50 + index * 25}ms` }))
|
||||
const bonuses = graph.filter((model) => model.baseReq)
|
||||
|
||||
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<number>()
|
||||
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)
|
||||
const timing = () => {
|
||||
const style = getComputedStyle(root)
|
||||
return {
|
||||
duration: Number.parseFloat(style.getPropertyValue("--bonus-duration")),
|
||||
easing: style.getPropertyValue("--spring-easing").trim(),
|
||||
spinEasing: style.getPropertyValue("--digit-easing").trim(),
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<figure
|
||||
data-component="limit-graph"
|
||||
aria-label={i18n.t("go.graph.label")}
|
||||
data-visible={visible() ? "" : undefined}
|
||||
data-boosted={boosted() ? "" : undefined}
|
||||
ref={root}
|
||||
onAnimationStart={(event) => {
|
||||
if (!(event.target instanceof SVGElement) || event.animationName !== "go-graph-reveal") return
|
||||
if (event.target.hasAttribute("data-stage-end") && !boosted()) {
|
||||
const duration = Number.parseFloat(getComputedStyle(root).getPropertyValue("--reveal-duration"))
|
||||
timer = setTimeout(() => setBoosted(true), duration * 0.6)
|
||||
return
|
||||
}
|
||||
if (event.target.dataset.animate !== "bonus") return
|
||||
const model = event.target.dataset.model
|
||||
if (!model) return
|
||||
setPromoted((current) => [...current, model])
|
||||
}}
|
||||
id="usage"
|
||||
dir={language.dir(language.locale())}
|
||||
data-component="go-usage"
|
||||
data-visible={visible() ? "" : undefined}
|
||||
aria-labelledby={`${id}-title`}
|
||||
>
|
||||
<div data-slot="plot">
|
||||
<svg
|
||||
viewBox={`0 0 ${w} ${h}`}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-hidden="true"
|
||||
style={{ height: `${h}px` }}
|
||||
>
|
||||
<g data-slot="grid">
|
||||
<For each={ticks}>
|
||||
{(t, i) => (
|
||||
<line x1={x(t)} y1={h - bottom} x2={x(t)} y2={top} data-grid style={{ "--d": `${i() * 100}ms` }} />
|
||||
)}
|
||||
</For>
|
||||
</g>
|
||||
<div data-slot="heading">
|
||||
<h2 id={`${id}-title`}>{i18n.t("go.graph.period")}</h2>
|
||||
</div>
|
||||
|
||||
<line x1={left} y1={h - bottom} x2={left} y2={top} data-stub />
|
||||
|
||||
<g data-slot="bars">
|
||||
<For each={graph}>
|
||||
{(m, i) => (
|
||||
<>
|
||||
<rect
|
||||
data-animate="bar"
|
||||
data-model={m.id}
|
||||
data-stage-end={i() === graph.length - 1 ? "" : undefined}
|
||||
style={{ "--d": m.d }}
|
||||
x={left}
|
||||
y={gy(i()) - bh / 2}
|
||||
width={Math.max(0, ("infinite" in m ? infiniteX : x(ratio(m.baseReq ?? m.req))) - left)}
|
||||
height={bh}
|
||||
data-bar
|
||||
data-kind={"infinite" in m ? "infinite" : "go"}
|
||||
/>
|
||||
{m.baseReq && (
|
||||
<rect
|
||||
data-animate="bonus"
|
||||
data-model={m.id}
|
||||
style={{ "--bonus-delay": `${bonuses.indexOf(m) * 60}ms` }}
|
||||
x={x(ratio(m.baseReq)) + 2}
|
||||
y={gy(i()) - bh / 2}
|
||||
width={Math.max(0, x(ratio(m.req)) - x(ratio(m.baseReq)) - 2)}
|
||||
height={bh}
|
||||
data-bar
|
||||
data-kind="promo"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</For>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div data-slot="ylabels" aria-hidden="true">
|
||||
<span data-ylabel style={{ "--x": lx, "--y": py(my) } as any}>
|
||||
{i18n.t("go.graph.go")}
|
||||
</span>
|
||||
<div role="table" aria-labelledby={`${id}-title`} id={`${id}-models`}>
|
||||
<div role="row" data-slot="columns">
|
||||
<div role="columnheader" data-slot="model-heading">
|
||||
{i18n.t("go.graph.model")}
|
||||
</div>
|
||||
<div role="columnheader" data-slot="requests-heading">
|
||||
{i18n.t("go.graph.requests")}
|
||||
</div>
|
||||
<div role="columnheader" data-slot="allowance-heading">
|
||||
{i18n.t("go.graph.allowance")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-slot="xlabels" aria-hidden="true">
|
||||
<For each={shown}>
|
||||
{(t) => (
|
||||
<span
|
||||
data-xlabel
|
||||
data-tick={t}
|
||||
style={{ "--x": px(x(t)), "--y": ty, "--d": `${ticks.indexOf(t) * 100}ms` }}
|
||||
>
|
||||
{i18n.t("go.graph.tick", { n: t })}
|
||||
</span>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
|
||||
<div data-slot="pills">
|
||||
<For each={graph}>
|
||||
{(m, i) => (
|
||||
<span
|
||||
data-item
|
||||
data-kind="go"
|
||||
data-model={m.id}
|
||||
data-edge={"edge" in m ? "" : undefined}
|
||||
data-infinite={"infinite" in m ? "" : undefined}
|
||||
data-promo={m.baseReq ? "" : undefined}
|
||||
style={{
|
||||
"--x": px("infinite" in m ? infiniteX : x(ratio(m.baseReq ?? m.req))),
|
||||
"--y": py(gy(i())),
|
||||
"--d": m.d,
|
||||
"--bonus-delay": `${Math.max(0, bonuses.indexOf(m)) * 60}ms`,
|
||||
"--travel": `${"infinite" in m ? 0 : ((x(ratio(m.req)) - x(ratio(m.baseReq ?? m.req))) / w) * 100}cqw`,
|
||||
}}
|
||||
>
|
||||
<span data-label>
|
||||
{!("infinite" in m) && m.baseReq ? (
|
||||
<RollingNumber
|
||||
value={promoted().includes(m.id) ? m.req : m.baseReq}
|
||||
target={m.req}
|
||||
timing={timing}
|
||||
/>
|
||||
) : (
|
||||
<span data-value>{"infinite" in m ? "\u221e" : m.req.toLocaleString()}</span>
|
||||
)}
|
||||
<span data-name>{m.name}</span>
|
||||
{m.id === "muse-spark-1.3-contributor" && (
|
||||
<span data-regions>
|
||||
(
|
||||
<a href="https://ai.developer.meta.com/legal/geographic-use-policy">
|
||||
{i18n.t("go.graph.limitedRegions")}
|
||||
</a>
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
{"infinite" in m && <span data-limited>({i18n.t("go.graph.limitedTime")})</span>}
|
||||
</span>
|
||||
{"bonus" in m && <span data-bonus>{m.bonus}</span>}
|
||||
</span>
|
||||
<div role="rowgroup" data-slot="rows">
|
||||
<For each={models()}>
|
||||
{(model, index) => (
|
||||
<div role="row" data-slot="model-row" data-model={model.id} style={{ "--delay": `${index() * 30}ms` }}>
|
||||
<div role="rowheader" data-slot="model">
|
||||
<bdi>{model.name}</bdi>
|
||||
<Show when={model.fresh}>
|
||||
<span data-slot="badge">{i18n.t("go.graph.new")}</span>
|
||||
</Show>
|
||||
<Show when={model.bonus}>
|
||||
<span data-slot="badge">{i18n.t("go.graph.bonus", { count: model.bonus! })}</span>
|
||||
</Show>
|
||||
<Show when={model.regions}>
|
||||
<a
|
||||
data-slot="region"
|
||||
href="https://ai.developer.meta.com/legal/geographic-use-policy"
|
||||
aria-label={`${model.name}: ${i18n.t("go.graph.limitedRegions")}`}
|
||||
title={i18n.t("go.graph.limitedRegions")}
|
||||
>
|
||||
<svg viewBox="0 0 16 16" width="13" height="13" fill="none" aria-hidden="true">
|
||||
<circle cx="8" cy="8" r="6" stroke="currentColor" />
|
||||
<ellipse cx="8" cy="8" rx="2.5" ry="6" stroke="currentColor" />
|
||||
<path d="M2 8h12" stroke="currentColor" />
|
||||
</svg>
|
||||
</a>
|
||||
</Show>
|
||||
</div>
|
||||
<div role="cell" data-slot="usage-value">
|
||||
<div data-slot="track" aria-hidden="true">
|
||||
<For each={ticks}>
|
||||
{(tick) => <span data-slot="gridline" style={{ "--position": `${position(tick)}%` }} />}
|
||||
</For>
|
||||
<div data-slot="bar" style={{ "--width": `${position(model.requests)}%` }} />
|
||||
</div>
|
||||
<div data-slot="requests">
|
||||
<Show when={model.baseRequests}>
|
||||
<s>{format().format(model.baseRequests!)}</s>{" "}
|
||||
</Show>
|
||||
<bdi>{format().format(model.requests)}</bdi>
|
||||
</div>
|
||||
</div>
|
||||
<div role="cell" data-slot="allowance" data-high={model.allowance >= 60 ? "" : undefined}>
|
||||
<Show when={model.baseAllowance}>
|
||||
<s>{currency().format(model.baseAllowance!)}</s>{" "}
|
||||
</Show>
|
||||
<bdi>
|
||||
<For each={currency().formatToParts(model.allowance)}>
|
||||
{(part) => (
|
||||
<span data-slot={part.type === "currency" ? "currency" : undefined}>{part.value}</span>
|
||||
)}
|
||||
</For>
|
||||
</bdi>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<figcaption>
|
||||
<div data-slot="caption-row">
|
||||
<div data-slot="caption-left">
|
||||
<div data-slot="caption-meta">
|
||||
<span data-slot="caption-label">{i18n.t("go.graph.label")}</span>
|
||||
<a data-slot="caption-link" href={props.href}>
|
||||
{i18n.t("go.graph.usageLimits")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="axis" aria-hidden="true" title={i18n.t("go.graph.scale")}>
|
||||
<div data-slot="ticks">
|
||||
<For each={ticks}>
|
||||
{(tick) => <span style={{ "--position": `${position(tick)}%` }}>{compact().format(tick)}</span>}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<figcaption>
|
||||
<button
|
||||
type="button"
|
||||
data-slot="expand"
|
||||
aria-expanded={expanded()}
|
||||
aria-controls={`${id}-models`}
|
||||
onClick={() => {
|
||||
if (expanded()) {
|
||||
const top = root.getBoundingClientRect().top
|
||||
if (top < 80) window.scrollTo({ top: window.scrollY + top - 96, behavior: "instant" })
|
||||
}
|
||||
setExpanded(!expanded())
|
||||
}}
|
||||
>
|
||||
{expanded()
|
||||
? i18n.t("go.graph.showLess")
|
||||
: i18n.t("go.graph.showAll", { count: format().format(goModels.length) })}
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
||||
<path d="m4 6 4 4 4-4" stroke="currentColor" stroke-width="1.25" />
|
||||
</svg>
|
||||
</button>
|
||||
<a href={props.href}>{i18n.t("go.graph.usageLimits")}</a>
|
||||
</figcaption>
|
||||
</figure>
|
||||
)
|
||||
|
||||
@@ -9,8 +9,8 @@ export const config = {
|
||||
github: {
|
||||
repoUrl: "https://github.com/anomalyco/opencode",
|
||||
starsFormatted: {
|
||||
compact: "195K",
|
||||
full: "195,000",
|
||||
compact: "208K",
|
||||
full: "208,000",
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -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,7 +258,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "الاستثناءات التالية",
|
||||
|
||||
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
|
||||
"go.banner.text": "يحصل GLM-5.3-Flash على حدود استخدام مضاعفة لفترة محدودة",
|
||||
"go.meta.description": "يبلغ سعر Go $10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.",
|
||||
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
|
||||
"go.hero.body":
|
||||
@@ -267,6 +272,14 @@ export const dict = {
|
||||
"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": "حدود الاستخدام",
|
||||
@@ -349,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": "الاستثناءات التالية",
|
||||
@@ -362,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}} غير مدعوم",
|
||||
|
||||
@@ -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,7 +262,6 @@ 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": "GLM-5.3-Flash tem limites de uso 2x maiores por tempo limitado",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -273,6 +278,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -358,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",
|
||||
@@ -372,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",
|
||||
|
||||
@@ -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,7 +260,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende undtagelser",
|
||||
|
||||
"go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
|
||||
"go.banner.text": "GLM-5.3-Flash får fordoblet brugsgrænse i en begrænset periode",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -270,9 +275,17 @@ export const dict = {
|
||||
"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",
|
||||
@@ -354,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",
|
||||
@@ -368,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",
|
||||
|
||||
@@ -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,7 +262,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "folgenden Ausnahmen",
|
||||
|
||||
"go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
|
||||
"go.banner.text": "GLM-5.3-Flash erhält für begrenzte Zeit 2x Nutzungslimits",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -272,6 +277,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -356,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",
|
||||
@@ -370,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",
|
||||
|
||||
@@ -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,7 +258,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "following exceptions",
|
||||
|
||||
"go.title": "OpenCode Go | Low cost coding models for everyone",
|
||||
"go.banner.text": "GLM-5.3-Flash gets 2× usage limits for a limited time",
|
||||
"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":
|
||||
@@ -268,6 +272,14 @@ export const dict = {
|
||||
"go.graph.freePill": "Big Pickle and free models",
|
||||
"go.graph.go": "Go",
|
||||
"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",
|
||||
@@ -352,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":
|
||||
@@ -367,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",
|
||||
|
||||
@@ -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,7 +263,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "siguientes excepciones",
|
||||
|
||||
"go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
|
||||
"go.banner.text": "GLM-5.3-Flash tiene límites de uso 2x mayores por tiempo limitado",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -274,6 +279,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -359,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",
|
||||
@@ -373,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",
|
||||
|
||||
@@ -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,7 +264,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "exceptions suivantes",
|
||||
|
||||
"go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
|
||||
"go.banner.text": "GLM-5.3-Flash bénéficie de limites d’utilisation 2x supérieures pour une durée limitée",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -275,9 +280,17 @@ export const dict = {
|
||||
"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",
|
||||
@@ -359,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",
|
||||
@@ -373,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",
|
||||
|
||||
@@ -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,7 +260,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "seguenti eccezioni",
|
||||
|
||||
"go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
|
||||
"go.banner.text": "GLM-5.3-Flash offre limiti di utilizzo 2x superiori per un periodo limitato",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -271,6 +276,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -353,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":
|
||||
@@ -369,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",
|
||||
|
||||
@@ -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,7 +259,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下の例外",
|
||||
|
||||
"go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
|
||||
"go.banner.text": "GLM-5.3-Flashの利用上限が期間限定で2倍に",
|
||||
"go.meta.description":
|
||||
"Goは月額$10で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。",
|
||||
"go.hero.title": "すべての人のための低価格なコーディングモデル",
|
||||
@@ -270,6 +275,14 @@ export const dict = {
|
||||
"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": "利用制限",
|
||||
@@ -353,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": "以下の例外",
|
||||
@@ -367,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}} はサポートされていません",
|
||||
|
||||
@@ -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,7 +256,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "다음 예외",
|
||||
|
||||
"go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
|
||||
"go.banner.text": "GLM-5.3-Flash 사용 한도가 한시적으로 2배 확대됩니다",
|
||||
"go.meta.description": "Go는 월 $10이며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
|
||||
"go.hero.title": "모두를 위한 저비용 코딩 모델",
|
||||
"go.hero.body":
|
||||
@@ -266,6 +271,14 @@ export const dict = {
|
||||
"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": "사용 한도",
|
||||
@@ -348,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": "다음 예외",
|
||||
@@ -361,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}} 모델은 지원되지 않습니다",
|
||||
|
||||
@@ -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,7 +260,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "følgende unntak",
|
||||
|
||||
"go.title": "OpenCode Go | Rimelige kodemodeller for alle",
|
||||
"go.banner.text": "GLM-5.3-Flash får 2x bruksgrense i en begrenset periode",
|
||||
"go.meta.description":
|
||||
"Go koster $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.",
|
||||
"go.hero.title": "Rimelige kodemodeller for alle",
|
||||
@@ -270,6 +275,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -352,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.",
|
||||
|
||||
@@ -369,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",
|
||||
|
||||
@@ -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,7 +261,6 @@ 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": "GLM-5.3-Flash oferuje 2x wyższe limity użycia przez ograniczony czas",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -272,6 +277,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -353,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.",
|
||||
|
||||
@@ -370,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",
|
||||
|
||||
@@ -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,7 +264,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "следующими исключениями",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогие модели для кодинга для всех",
|
||||
"go.banner.text": "GLM-5.3-Flash получает 2x лимиты использования на ограниченное время",
|
||||
"go.meta.description":
|
||||
"Go стоит $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.",
|
||||
"go.hero.title": "Недорогие модели для кодинга для всех",
|
||||
@@ -275,6 +280,14 @@ export const dict = {
|
||||
"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": "Лимиты использования",
|
||||
@@ -358,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.",
|
||||
|
||||
@@ -375,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}} не поддерживается",
|
||||
|
||||
@@ -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,7 +259,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "ข้อยกเว้นดังนี้",
|
||||
|
||||
"go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
"go.banner.text": "GLM-5.3-Flash เพิ่มโควตาการใช้งานเป็น 2 เท่าในช่วงเวลาจำกัด",
|
||||
"go.meta.description":
|
||||
"Go มีราคา $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้",
|
||||
"go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
|
||||
@@ -269,6 +274,14 @@ export const dict = {
|
||||
"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": "ขีดจำกัดการใช้งาน",
|
||||
@@ -352,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) และไม่ใช้ข้อมูลของคุณสำหรับการฝึกโมเดล โดยมี",
|
||||
@@ -366,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}}",
|
||||
|
||||
@@ -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,7 +262,6 @@ 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": "GLM-5.3-Flash sınırlı bir süre için 2x kullanım limiti sunuyor",
|
||||
"go.meta.description":
|
||||
"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",
|
||||
@@ -272,6 +277,14 @@ export const dict = {
|
||||
"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",
|
||||
@@ -355,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ı.",
|
||||
|
||||
@@ -372,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",
|
||||
|
||||
@@ -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,7 +260,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "такими винятками",
|
||||
|
||||
"go.title": "OpenCode Go | Недорогі моделі кодування для всіх",
|
||||
"go.banner.text": "GLM-5.3-Flash отримує 2x ліміти використання протягом обмеженого часу",
|
||||
"go.meta.description":
|
||||
"Go коштує $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.",
|
||||
"go.hero.title": "Недорогі моделі кодування для всіх",
|
||||
@@ -271,6 +275,14 @@ export const dict = {
|
||||
"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": "Ліміти використання",
|
||||
@@ -352,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.",
|
||||
|
||||
@@ -368,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}} не підтримується",
|
||||
|
||||
@@ -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,7 +250,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情况除外",
|
||||
|
||||
"go.title": "OpenCode Go | 人人可用的低成本编程模型",
|
||||
"go.banner.text": "GLM-5.3-Flash 限时享受 2 倍使用额度",
|
||||
"go.meta.description": "Go 每月 $10,提供充裕的使用限额,并可可靠访问领先的编程模型。",
|
||||
"go.hero.title": "人人可用的低成本编程模型",
|
||||
"go.hero.body":
|
||||
@@ -259,6 +264,14 @@ export const dict = {
|
||||
"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": "使用限制",
|
||||
@@ -336,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": "我可以充值余额吗?",
|
||||
@@ -348,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}}",
|
||||
|
||||
@@ -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,7 +249,6 @@ export const dict = {
|
||||
"zen.privacy.exceptionsLink": "以下例外情況",
|
||||
|
||||
"go.title": "OpenCode Go | 低成本全民編碼模型",
|
||||
"go.banner.text": "GLM-5.3-Flash 限時享有 2 倍使用額度",
|
||||
"go.meta.description": "Go 每月 $10,提供充裕的使用限額,並可穩定存取領先的編碼模型。",
|
||||
"go.hero.title": "低成本全民編碼模型",
|
||||
"go.hero.body":
|
||||
@@ -259,6 +263,14 @@ export const dict = {
|
||||
"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": "使用限制",
|
||||
@@ -336,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": "我可以儲值額度嗎?",
|
||||
@@ -348,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}}",
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { and, Database, eq, isNotNull, 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<string, string | undefined> = {
|
||||
"GET /zen/v1/models": "/openai/v1/models",
|
||||
"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, clientIP?: string): Promise<Response | undefined> {
|
||||
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<Uint8Array>
|
||||
},
|
||||
): Promise<Response | undefined> {
|
||||
const url = new URL(request.url)
|
||||
const path =
|
||||
paths[`${request.method} ${url.pathname}`] ??
|
||||
@@ -20,36 +34,86 @@ export async function proxyInference(request: Request, clientIP?: string): Promi
|
||||
: undefined)
|
||||
if (!path) return undefined
|
||||
|
||||
const key = path.startsWith("/anthropic/")
|
||||
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({ migratedAt: WorkspaceTable.migrated_at })
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.where(eq(KeyTable.key, key))
|
||||
.limit(1)
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
if (!workspace?.migratedAt) return undefined
|
||||
// New Console keys are never in the legacy key table; every legacy key is `sk-`.
|
||||
const legacy = !key.startsWith("oc_sk_")
|
||||
const workspace = legacy ? await migratedWorkspace(key, generation?.provider) : undefined
|
||||
if (legacy && !workspace) 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)
|
||||
destination.pathname = `${destination.pathname.replace(/\/$/, "")}${path}`
|
||||
// Imported connections must use this same workspace/provider-derived ID.
|
||||
const target =
|
||||
model && workspace
|
||||
? `/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 = ""
|
||||
|
||||
const forwarded = new Request(destination, request)
|
||||
// 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") ?? clientIP
|
||||
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" })
|
||||
}
|
||||
|
||||
// Routing only; the destination owns authentication and revocation after cutover.
|
||||
function migratedWorkspace(key: string, provider?: string) {
|
||||
return Database.use((tx) =>
|
||||
tx
|
||||
.select({ id: WorkspaceTable.id, provider: ProviderTable.provider })
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.leftJoin(
|
||||
ProviderTable,
|
||||
provider
|
||||
? and(
|
||||
eq(ProviderTable.workspaceID, KeyTable.workspaceID),
|
||||
eq(ProviderTable.provider, provider),
|
||||
isNull(ProviderTable.timeDeleted),
|
||||
sql`length(${ProviderTable.credentials}) > 0`,
|
||||
)
|
||||
: sql`false`,
|
||||
)
|
||||
.where(and(eq(KeyTable.key, key), isNotNull(WorkspaceTable.migrated_at)))
|
||||
.limit(1)
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
}
|
||||
|
||||
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" } },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,34 @@ const DOCS_LOCALE = {
|
||||
"zh-tw": "zht",
|
||||
} as const satisfies Record<string, Locale>
|
||||
|
||||
// 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<Locale, string>
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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<typeof localeFromRequest>): 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<typeof localeFromRequest>) {
|
||||
if (locale === "en") return null
|
||||
if (request.headers.get(LOCALE_HEADER)) return null
|
||||
|
||||
@@ -2,7 +2,6 @@ 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"
|
||||
import { proxyInference } from "~/lib/inference-proxy"
|
||||
|
||||
export default createMiddleware({
|
||||
async onRequest(event) {
|
||||
@@ -20,12 +19,5 @@ export default createMiddleware({
|
||||
|
||||
const referralCode = normalizeReferralCode(url.searchParams.get("ref"))
|
||||
if (referralCode) event.response.headers.append("set-cookie", referralCookie(referralCode))
|
||||
|
||||
return proxyInference(event.request, event.clientAddress).catch(() =>
|
||||
Response.json(
|
||||
{ error: { type: "api_error", message: "Inference routing is unavailable. Please retry later." } },
|
||||
{ status: 503, headers: { "Cache-Control": "no-store" } },
|
||||
),
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
@@ -98,7 +98,7 @@ ${body.phone ? `${body.phone}<br>` : ""}`.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,
|
||||
|
||||
@@ -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 }))
|
||||
}
|
||||
@@ -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 }))
|
||||
}
|
||||
@@ -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 }))
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { redirect } from "@solidjs/router"
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { getLastSeenWorkspaceID } from "../workspace/common"
|
||||
import { localeFromRequest, route } from "~/lib/language"
|
||||
@@ -7,6 +8,11 @@ export async function GET(input: APIEvent) {
|
||||
const locale = localeFromRequest(input.request)
|
||||
try {
|
||||
const workspaceID = await getLastSeenWorkspaceID()
|
||||
if (!workspaceID) {
|
||||
const destination = Resource.ConsoleMigration.consoleUrl
|
||||
if (!destination) throw new Error("New Console URL is not configured")
|
||||
return redirect(`${destination}/login`, { headers: { "Cache-Control": "no-store" } })
|
||||
}
|
||||
return redirect(route(locale, `/workspace/${workspaceID}`))
|
||||
} catch {
|
||||
return redirect("/auth/authorize")
|
||||
|
||||
@@ -363,10 +363,6 @@ body {
|
||||
[data-slot="text"] {
|
||||
color: var(--color-text-strong);
|
||||
line-height: 1.4;
|
||||
|
||||
@media (max-width: 30.625rem) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import { IconMiniMax, IconMiMo, IconZai, IconAlibaba, IconDeepSeek } from "~/com
|
||||
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"
|
||||
@@ -45,8 +46,10 @@ const models = [
|
||||
{ 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
|
||||
@@ -81,7 +84,7 @@ export default function Home() {
|
||||
<div data-component="desktop-app-banner">
|
||||
<span data-slot="badge">{i18n.t("home.banner.badge")}</span>
|
||||
<div data-slot="content">
|
||||
<span data-slot="text">{i18n.t("go.banner.text")}</span>
|
||||
<span data-slot="text">{i18n.t("go.promo.deepseek")}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="hero-copy">
|
||||
@@ -206,7 +209,7 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
<section data-component="comparison">
|
||||
<LimitsGraph href={language.route("/docs/go/#usage-limits")} />
|
||||
<LimitsGraph href={goUsageLimits(language.locale())} />
|
||||
</section>
|
||||
|
||||
<section data-component="problem">
|
||||
@@ -361,6 +364,15 @@ export default function Home() {
|
||||
<li>
|
||||
<Faq question={i18n.t("go.faq.q8")}>{i18n.t("go.faq.a8")}</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question={i18n.t("go.faq.q10")}>
|
||||
<For each={i18n.t("go.faq.a10").split(/(\{\{contact\}\})/g)}>
|
||||
{(part) =>
|
||||
part === "{{contact}}" ? <a href="mailto:help@anoma.ly">{i18n.t("common.contactUs")}</a> : part
|
||||
}
|
||||
</For>
|
||||
</Faq>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -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": "*",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -659,6 +659,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
<li>Qwen3.7 Max</li>
|
||||
<li>Qwen3.7 Plus</li>
|
||||
<li>Qwen3.6 Plus</li>
|
||||
<li>DeepSeek V4.1 Flash — {i18n.t("go.graph.bonus", { count: 4 })}</li>
|
||||
<li>DeepSeek V4 Pro</li>
|
||||
<li>DeepSeek V4 Flash</li>
|
||||
<li>DeepSeek V4 Flash Vision Exp</li>
|
||||
@@ -667,6 +668,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||
<li>Hy4 preview</li>
|
||||
<li>Hy3</li>
|
||||
</ul>
|
||||
<p data-slot="promo-description">{i18n.t("go.promo.deepseek")}</p>
|
||||
<p data-slot="promo-description">{i18n.t("workspace.lite.promo.footer")}</p>
|
||||
<div data-slot="subscribe-actions">
|
||||
<button
|
||||
|
||||
@@ -53,6 +53,7 @@ export async function getLastSeenWorkspaceID() {
|
||||
eq(UserTable.accountID, actor.properties.accountID),
|
||||
isNull(UserTable.timeDeleted),
|
||||
isNull(WorkspaceTable.timeDeleted),
|
||||
isNull(WorkspaceTable.migrated_at),
|
||||
),
|
||||
)
|
||||
.orderBy(desc(UserTable.timeSeen))
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||
import { buildModelsResponse, buildOptionsResponse } from "../../util/modelsHandler"
|
||||
import { inferenceUnavailable, proxyInference } from "~/lib/inference-proxy"
|
||||
|
||||
export async function OPTIONS(_input: APIEvent) {
|
||||
return buildOptionsResponse()
|
||||
}
|
||||
|
||||
export async function GET(_input: APIEvent) {
|
||||
export async function GET(input: APIEvent) {
|
||||
const response = await proxyInference(input.request).catch(inferenceUnavailable)
|
||||
if (response) return response
|
||||
const models = Object.keys(ZenData.list("lite").models)
|
||||
return buildModelsResponse(models)
|
||||
}
|
||||
|
||||
@@ -6,8 +6,11 @@ import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { LiteData } from "@opencode-ai/console-core/lite.js"
|
||||
import { Subscription } from "@opencode-ai/console-core/subscription.js"
|
||||
import { inferenceUnavailable, proxyInference } from "~/lib/inference-proxy"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const response = await proxyInference(input.request).catch(inferenceUnavailable)
|
||||
if (response) return response
|
||||
const apiKey = input.request.headers.get("authorization")?.match(/^Bearer (\S+)$/)?.[1]
|
||||
|
||||
if (!apiKey) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "./index.css"
|
||||
import { createAsync, query } from "@solidjs/router"
|
||||
import { Title, Meta } from "@solidjs/meta"
|
||||
import { For } from "solid-js"
|
||||
//import { HttpHeader } from "@solidjs/start"
|
||||
import zenLogoLight from "../../asset/zen-ornate-light.svg"
|
||||
import zenLogoDark from "../../asset/zen-ornate-dark.svg"
|
||||
@@ -321,6 +322,15 @@ export default function Home() {
|
||||
<li>
|
||||
<Faq question={i18n.t("zen.faq.q8")}>{i18n.t("zen.faq.a8")}</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question={i18n.t("zen.faq.q9")}>
|
||||
<For each={i18n.t("zen.faq.a9").split(/(\{\{contact\}\})/g)}>
|
||||
{(part) =>
|
||||
part === "{{contact}}" ? <a href="mailto:help@anoma.ly">{i18n.t("common.contactUs")}</a> : part
|
||||
}
|
||||
</For>
|
||||
</Faq>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ import { countryFromRequest, isModelCountryRestricted } from "~/lib/request-coun
|
||||
import { isPeakPricing } from "./pricing"
|
||||
import { prepareRequestBody } from "./requestBody"
|
||||
import { requiresGoTrainingConsent } from "./trainingConsent"
|
||||
import { inferenceUnavailable, proxyInference } from "~/lib/inference-proxy"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type PreparedBody = Awaited<ReturnType<typeof prepareRequestBody>>
|
||||
@@ -100,6 +101,26 @@ export async function handler(
|
||||
const ip = rawIp.includes(":") ? rawIp.split(":").slice(0, 4).join(":") : rawIp
|
||||
const rawZenApiKey = opts.parseApiKey(input.request.headers)
|
||||
const zenApiKey = rawZenApiKey === "public" ? undefined : rawZenApiKey
|
||||
const zenData = ZenData.list(opts.modelList)
|
||||
if (model) {
|
||||
// Read routing metadata without running legacy model, auth, or balance checks.
|
||||
const configured = zenData.models[model]
|
||||
const entry = Array.isArray(configured)
|
||||
? configured.find((entry) => entry.formatFilter === opts.format)
|
||||
: configured
|
||||
const response = await proxyInference(input.request, {
|
||||
provider: opts.modelList === "full" ? entry?.byokProvider : undefined,
|
||||
model:
|
||||
opts.modelList === "full"
|
||||
? entry?.providers.find((provider) => provider.id === entry.byokProvider)?.model
|
||||
: undefined,
|
||||
body: (providerModel) => requestBody?.stream(providerModel ?? model, false) ?? body,
|
||||
}).catch(() => {
|
||||
void (requestBody ? requestBody.cancel() : body.cancel()).catch(() => {})
|
||||
return inferenceUnavailable()
|
||||
})
|
||||
if (response) return response
|
||||
}
|
||||
const sessionId = input.request.headers.get("x-opencode-session") ?? ""
|
||||
const requestId = input.request.headers.get("x-opencode-request") ?? ""
|
||||
const ocClient = input.request.headers.get("x-opencode-client") ?? ""
|
||||
@@ -112,7 +133,6 @@ export async function handler(
|
||||
user_agent: userAgent,
|
||||
"model.tier": opts.modelList === "full" ? "zen" : "go",
|
||||
})
|
||||
const zenData = ZenData.list(opts.modelList)
|
||||
const modelInfo = validateModel(zenData, model)
|
||||
const country = countryFromRequest(input.request)
|
||||
if (isModelCountryRestricted(modelInfo.id, country)) throw new RegionError(t("zen.api.error.countryNotAllowed"))
|
||||
@@ -140,7 +160,7 @@ export async function handler(
|
||||
if (
|
||||
authInfo &&
|
||||
opts.modelList === "lite" &&
|
||||
["deepseek-v4-flash", "deepseek-v4-pro"].includes(modelInfo.id) &&
|
||||
["deepseek-v4.1-flash", "deepseek-flash", "deepseek-v4-flash", "deepseek-v4-pro"].includes(modelInfo.id) &&
|
||||
!allowedRegions?.includes("cn")
|
||||
)
|
||||
throw new RegionError(
|
||||
@@ -232,6 +252,8 @@ export async function handler(
|
||||
})
|
||||
if (isNewInference) {
|
||||
headers.set("x-zen-model", model)
|
||||
if (opts.modelList === "lite")
|
||||
headers.set("x-zen-billing-source", billingSource === "lite" ? "go" : "credit")
|
||||
}
|
||||
headers.delete("host")
|
||||
headers.delete("content-length")
|
||||
@@ -241,6 +263,7 @@ export async function handler(
|
||||
headers.delete("x-opencode-client")
|
||||
headers.delete("x-opencode-request")
|
||||
headers.delete("x-zen-model")
|
||||
headers.delete("x-zen-billing-source")
|
||||
}
|
||||
return headers
|
||||
})(),
|
||||
@@ -1003,7 +1026,8 @@ export async function handler(
|
||||
modelInfo.costPeak && isPeakPricing(new Date())
|
||||
? modelInfo.costPeak
|
||||
: modelInfo.cost200K &&
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) >
|
||||
modelInfo.cost200K.threshold
|
||||
? modelInfo.cost200K
|
||||
: modelInfo.cost
|
||||
|
||||
@@ -1078,6 +1102,8 @@ export async function handler(
|
||||
authInfo = authInfo!
|
||||
|
||||
const cost = centsToMicroCents(totalCostInCent)
|
||||
// Keep period bounds and persisted timestamps on one snapshot when a queued write crosses a reset boundary.
|
||||
const trackedAt = new Date()
|
||||
|
||||
// For hot workspaces, batch balance/usage updates through Redis to avoid
|
||||
// row-level lock contention on BillingTable/UserTable. Returns the amount
|
||||
@@ -1118,7 +1144,7 @@ export async function handler(
|
||||
if (billingSource === "subscription") {
|
||||
const plan = authInfo.billing.subscription!.plan
|
||||
const black = BlackData.getLimits({ plan })
|
||||
const week = getWeekBounds(new Date())
|
||||
const week = getWeekBounds(trackedAt)
|
||||
const rollingWindowSeconds = black.rollingWindow * 3600
|
||||
return [
|
||||
db
|
||||
@@ -1126,11 +1152,17 @@ export async function handler(
|
||||
.set({
|
||||
fixedUsage: sql`
|
||||
CASE
|
||||
WHEN ${SubscriptionTable.timeFixedUpdated} >= ${week.end} THEN ${SubscriptionTable.fixedUsage}
|
||||
WHEN ${SubscriptionTable.timeFixedUpdated} >= ${week.start} THEN ${SubscriptionTable.fixedUsage} + ${cost}
|
||||
ELSE ${cost}
|
||||
END
|
||||
`,
|
||||
timeFixedUpdated: sql`now()`,
|
||||
timeFixedUpdated: sql`
|
||||
CASE
|
||||
WHEN ${SubscriptionTable.timeFixedUpdated} > ${trackedAt} THEN ${SubscriptionTable.timeFixedUpdated}
|
||||
ELSE ${trackedAt}
|
||||
END
|
||||
`,
|
||||
rollingUsage: sql`
|
||||
CASE
|
||||
WHEN UNIX_TIMESTAMP(${SubscriptionTable.timeRollingUpdated}) >= UNIX_TIMESTAMP(now()) - ${rollingWindowSeconds} THEN ${SubscriptionTable.rollingUsage} + ${cost}
|
||||
@@ -1154,8 +1186,8 @@ export async function handler(
|
||||
}
|
||||
if (billingSource === "lite") {
|
||||
const lite = LiteData.getLimits()
|
||||
const week = getWeekBounds(new Date())
|
||||
const month = getMonthlyBounds(new Date(), authInfo.lite!.timeCreated)
|
||||
const week = getWeekBounds(trackedAt)
|
||||
const month = getMonthlyBounds(trackedAt, authInfo.lite!.timeCreated)
|
||||
const rollingWindowSeconds = lite.rollingWindow * 3600
|
||||
const quotaCost = Math.round(cost * modelInfo.costMultiplier)
|
||||
return [
|
||||
@@ -1164,18 +1196,30 @@ export async function handler(
|
||||
.set({
|
||||
monthlyUsage: sql`
|
||||
CASE
|
||||
WHEN ${LiteTable.timeMonthlyUpdated} >= ${month.end} THEN ${LiteTable.monthlyUsage}
|
||||
WHEN ${LiteTable.timeMonthlyUpdated} >= ${month.start} THEN ${LiteTable.monthlyUsage} + ${quotaCost}
|
||||
ELSE ${quotaCost}
|
||||
END
|
||||
`,
|
||||
timeMonthlyUpdated: sql`now()`,
|
||||
timeMonthlyUpdated: sql`
|
||||
CASE
|
||||
WHEN ${LiteTable.timeMonthlyUpdated} > ${trackedAt} THEN ${LiteTable.timeMonthlyUpdated}
|
||||
ELSE ${trackedAt}
|
||||
END
|
||||
`,
|
||||
weeklyUsage: sql`
|
||||
CASE
|
||||
WHEN ${LiteTable.timeWeeklyUpdated} >= ${week.end} THEN ${LiteTable.weeklyUsage}
|
||||
WHEN ${LiteTable.timeWeeklyUpdated} >= ${week.start} THEN ${LiteTable.weeklyUsage} + ${quotaCost}
|
||||
ELSE ${quotaCost}
|
||||
END
|
||||
`,
|
||||
timeWeeklyUpdated: sql`now()`,
|
||||
timeWeeklyUpdated: sql`
|
||||
CASE
|
||||
WHEN ${LiteTable.timeWeeklyUpdated} > ${trackedAt} THEN ${LiteTable.timeWeeklyUpdated}
|
||||
ELSE ${trackedAt}
|
||||
END
|
||||
`,
|
||||
rollingUsage: sql`
|
||||
CASE
|
||||
WHEN UNIX_TIMESTAMP(${LiteTable.timeRollingUpdated}) >= UNIX_TIMESTAMP(now()) - ${rollingWindowSeconds} THEN ${LiteTable.rollingUsage} + ${quotaCost}
|
||||
|
||||
@@ -51,7 +51,10 @@ export const openaiHelper: ProviderHelper = ({ workspaceID }) => ({
|
||||
const cacheReadTokens = usage.input_tokens_details?.cached_tokens ?? undefined
|
||||
const cacheWriteTokens = usage.input_tokens_details?.cache_write_tokens ?? undefined
|
||||
return {
|
||||
inputTokens: inputTokens - (cacheReadTokens ?? 0),
|
||||
// OpenAI's input_tokens includes both cached_tokens and cache_write_tokens;
|
||||
// each is billed separately at its own rate. Clamp to zero so a provider
|
||||
// reporting overlapping detail fields cannot drive input cost negative.
|
||||
inputTokens: Math.max(0, inputTokens - (cacheReadTokens ?? 0) - (cacheWriteTokens ?? 0)),
|
||||
outputTokens,
|
||||
reasoningTokens,
|
||||
cacheReadTokens,
|
||||
|
||||
@@ -5,14 +5,20 @@ import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
|
||||
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
|
||||
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
||||
import { buildOptionsResponse, buildModelsResponse } from "~/routes/zen/util/modelsHandler"
|
||||
import { inferenceUnavailable, proxyInference } from "~/lib/inference-proxy"
|
||||
|
||||
export async function OPTIONS(_input: APIEvent) {
|
||||
return buildOptionsResponse()
|
||||
}
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const apiKey = input.request.headers.get("authorization")?.split(" ")[1]
|
||||
if (apiKey && apiKey !== "public") {
|
||||
const response = await proxyInference(input.request).catch(inferenceUnavailable)
|
||||
if (response) return response
|
||||
}
|
||||
|
||||
const disabledModels = await (() => {
|
||||
const apiKey = input.request.headers.get("authorization")?.split(" ")[1]
|
||||
if (!apiKey) return [] as string[]
|
||||
|
||||
return Database.use((tx) =>
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("provider usage extraction", () => {
|
||||
|
||||
expect(providers.google.normalizeUsage(usage)).toEqual({
|
||||
inputTokens: 6,
|
||||
outputTokens: 3,
|
||||
outputTokens: 5,
|
||||
reasoningTokens: 2,
|
||||
cacheReadTokens: 4,
|
||||
cacheWrite5mTokens: undefined,
|
||||
@@ -42,7 +42,7 @@ describe("provider usage extraction", () => {
|
||||
|
||||
expect(providers.google.normalizeUsage(usageParser.retrieve())).toEqual({
|
||||
inputTokens: 6,
|
||||
outputTokens: 3,
|
||||
outputTokens: 5,
|
||||
reasoningTokens: 2,
|
||||
cacheReadTokens: 4,
|
||||
cacheWrite5mTokens: undefined,
|
||||
@@ -73,7 +73,23 @@ describe("provider usage extraction", () => {
|
||||
)
|
||||
|
||||
expect(providers.openai.normalizeUsage(usageParser.retrieve())).toEqual({
|
||||
inputTokens: 6,
|
||||
inputTokens: 3,
|
||||
outputTokens: 2,
|
||||
reasoningTokens: undefined,
|
||||
cacheReadTokens: 4,
|
||||
cacheWrite5mTokens: 3,
|
||||
cacheWrite1hTokens: undefined,
|
||||
})
|
||||
})
|
||||
|
||||
test("clamps input tokens when detail fields overlap", () => {
|
||||
const usageParser = providers.openai.createUsageParser()
|
||||
usageParser.parse(
|
||||
'event: response.completed\ndata: {"response":{"usage":{"input_tokens":5,"input_tokens_details":{"cached_tokens":4,"cache_write_tokens":3},"output_tokens":2}}}',
|
||||
)
|
||||
|
||||
expect(providers.openai.normalizeUsage(usageParser.retrieve())).toEqual({
|
||||
inputTokens: 0,
|
||||
outputTokens: 2,
|
||||
reasoningTokens: undefined,
|
||||
cacheReadTokens: 4,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -19,11 +19,17 @@ export namespace ZenData {
|
||||
cacheWrite1h: z.number().optional(),
|
||||
})
|
||||
|
||||
// Long-context tier. The flip threshold defaults to 200_000 for backward
|
||||
// compatibility with existing ZEN_MODELS secrets.
|
||||
const ModelCostTierSchema = ModelCostSchema.extend({
|
||||
threshold: z.number().default(200_000),
|
||||
})
|
||||
|
||||
const ModelSchema = z.object({
|
||||
name: z.string(),
|
||||
cost: ModelCostSchema,
|
||||
costMultiplier: z.number().default(1),
|
||||
cost200K: ModelCostSchema.optional(),
|
||||
cost200K: ModelCostTierSchema.optional(),
|
||||
costPeak: ModelCostSchema.optional(),
|
||||
allowAnonymous: z.boolean().optional(),
|
||||
byokProvider: z.enum(["openai", "anthropic", "google"]).optional(),
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { z } from "zod"
|
||||
import { and, Database, eq, isNull } from "./drizzle"
|
||||
import { LiteTable, SubscriptionTable } from "./schema/billing.sql"
|
||||
import { Identifier } from "./identifier"
|
||||
import { fn } from "./util/fn"
|
||||
|
||||
export namespace Quota {
|
||||
// Zero all usage counters of one plan for a workspace, using the same
|
||||
// workspace-scoped addressing as unsubscribeLite/unsubscribeBlack and
|
||||
// Billing.subtractLiteUsage. Timestamps are left untouched on purpose: a
|
||||
// zeroed counter with a current-window stamp reads as empty and the next
|
||||
// write accumulates on top of zero, so the active windows stay undisturbed.
|
||||
export const reset = fn(
|
||||
z.object({
|
||||
workspaceID: Identifier.schema("workspace"),
|
||||
plan: z.enum(["lite", "subscription"]),
|
||||
}),
|
||||
async (input) => {
|
||||
if (input.plan === "lite") {
|
||||
return Database.transaction(async (db) => {
|
||||
const where = and(eq(LiteTable.workspaceID, input.workspaceID), isNull(LiteTable.timeDeleted))
|
||||
const rows = await db
|
||||
.select({
|
||||
rollingUsage: LiteTable.rollingUsage,
|
||||
weeklyUsage: LiteTable.weeklyUsage,
|
||||
monthlyUsage: LiteTable.monthlyUsage,
|
||||
})
|
||||
.from(LiteTable)
|
||||
.where(where)
|
||||
if (rows.length === 0) throw new Error("No lite usage counters found for workspace")
|
||||
|
||||
await db.update(LiteTable).set({ rollingUsage: 0, weeklyUsage: 0, monthlyUsage: 0 }).where(where)
|
||||
return {
|
||||
plan: "lite" as const,
|
||||
before: {
|
||||
rollingUsage: rows.reduce((sum, row) => sum + (row.rollingUsage ?? 0), 0),
|
||||
weeklyUsage: rows.reduce((sum, row) => sum + (row.weeklyUsage ?? 0), 0),
|
||||
monthlyUsage: rows.reduce((sum, row) => sum + (row.monthlyUsage ?? 0), 0),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
if (input.plan === "subscription") {
|
||||
return Database.transaction(async (db) => {
|
||||
const where = and(eq(SubscriptionTable.workspaceID, input.workspaceID), isNull(SubscriptionTable.timeDeleted))
|
||||
const rows = await db
|
||||
.select({ rollingUsage: SubscriptionTable.rollingUsage, fixedUsage: SubscriptionTable.fixedUsage })
|
||||
.from(SubscriptionTable)
|
||||
.where(where)
|
||||
if (rows.length === 0) throw new Error("No subscription usage counters found for workspace")
|
||||
|
||||
await db.update(SubscriptionTable).set({ rollingUsage: 0, fixedUsage: 0 }).where(where)
|
||||
return {
|
||||
plan: "subscription" as const,
|
||||
before: {
|
||||
rollingUsage: rows.reduce((sum, row) => sum + (row.rollingUsage ?? 0), 0),
|
||||
fixedUsage: rows.reduce((sum, row) => sum + (row.fixedUsage ?? 0), 0),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
throw new Error(`Unknown plan: ${input.plan}`)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { BillingTable } from "./schema/billing.sql"
|
||||
import { WorkspaceTable } from "./schema/workspace.sql"
|
||||
import { AccountTable } from "./schema/account.sql"
|
||||
import { Key } from "./key"
|
||||
import { and, eq, isNull, sql } from "drizzle-orm"
|
||||
import { and, eq, inArray, isNull, sql } from "drizzle-orm"
|
||||
|
||||
export namespace Workspace {
|
||||
export const Region = z.enum(["us", "eu", "sg", "cn"])
|
||||
@@ -109,6 +109,26 @@ export namespace Workspace {
|
||||
},
|
||||
)
|
||||
|
||||
export const blockBatch = fn(
|
||||
z.object({
|
||||
workspaceIDs: z.array(Identifier.schema("workspace")).min(1),
|
||||
}),
|
||||
async (input) => {
|
||||
const { applied, notFound } = await setBlockedBatch(input, true)
|
||||
return { blocked: applied, notFound }
|
||||
},
|
||||
)
|
||||
|
||||
export const unblockBatch = fn(
|
||||
z.object({
|
||||
workspaceIDs: z.array(Identifier.schema("workspace")).min(1),
|
||||
}),
|
||||
async (input) => {
|
||||
const { applied, notFound } = await setBlockedBatch(input, false)
|
||||
return { unblocked: applied, notFound }
|
||||
},
|
||||
)
|
||||
|
||||
export const remove = fn(z.void(), async () => {
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
@@ -118,3 +138,27 @@ export namespace Workspace {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// No size cap by design; large IN lists degrade on Vitess, so chunks stay fixed-size and
|
||||
// sequential to share one connection inside ambient transactions. Existence comes from the
|
||||
// follow-up select rather than rowsAffected, so no-op rows (already in the target state)
|
||||
// still report as applied instead of "not found".
|
||||
const setBlockedBatch = async (input: { workspaceIDs: string[] }, isBlocked: boolean) => {
|
||||
const ids = [...new Set(input.workspaceIDs)]
|
||||
return await Database.use(async (tx) => {
|
||||
const applied = new Set<string>()
|
||||
for (let index = 0; index < ids.length; index += 500) {
|
||||
const chunk = ids.slice(index, index + 500)
|
||||
await tx.update(WorkspaceTable).set({ is_blocked: isBlocked }).where(inArray(WorkspaceTable.id, chunk))
|
||||
const rows = await tx
|
||||
.select({ id: WorkspaceTable.id })
|
||||
.from(WorkspaceTable)
|
||||
.where(inArray(WorkspaceTable.id, chunk))
|
||||
for (const row of rows) applied.add(row.id)
|
||||
}
|
||||
return {
|
||||
applied: ids.filter((id) => applied.has(id)),
|
||||
notFound: ids.filter((id) => !applied.has(id)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { ZenData } from "../src/model"
|
||||
|
||||
const base = {
|
||||
zenModels: {
|
||||
"gpt-5.6-sol": {
|
||||
name: "GPT-5.6 Sol",
|
||||
cost: { input: 2, output: 10 },
|
||||
costMultiplier: 1,
|
||||
providers: [{ id: "openai", model: "gpt-5.6-sol" }],
|
||||
},
|
||||
},
|
||||
liteModels: {},
|
||||
providers: { openai: { api: "https://api.openai.com/v1", apiKey: "test" } },
|
||||
}
|
||||
|
||||
const entry = (data: ReturnType<typeof ZenData.validate>) => {
|
||||
const value = data.zenModels["gpt-5.6-sol"]
|
||||
return Array.isArray(value) ? value[0] : value
|
||||
}
|
||||
|
||||
describe("ZenData cost200K threshold", () => {
|
||||
test("defaults to 200_000 when not configured", () => {
|
||||
const data = ZenData.validate({
|
||||
...base,
|
||||
zenModels: {
|
||||
"gpt-5.6-sol": {
|
||||
...base.zenModels["gpt-5.6-sol"],
|
||||
// The secret arrives as parsed JSON in production, so untyped here too.
|
||||
cost200K: JSON.parse('{"input":4,"output":15}'),
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(entry(data).cost200K?.threshold).toBe(200_000)
|
||||
})
|
||||
|
||||
test("accepts an explicit 272_000 threshold", () => {
|
||||
const data = ZenData.validate({
|
||||
...base,
|
||||
zenModels: {
|
||||
"gpt-5.6-sol": {
|
||||
...base.zenModels["gpt-5.6-sol"],
|
||||
cost200K: { input: 4, output: 15, threshold: 272_000 },
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(entry(data).cost200K?.threshold).toBe(272_000)
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-function",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-mail",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"dependencies": {
|
||||
"@jsx-email/all": "2.2.3",
|
||||
"@jsx-email/cli": "1.4.3",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/console-support",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "@opencode-ai/core",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -64,20 +64,20 @@
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.166",
|
||||
"@ai-sdk/anthropic": "3.0.111",
|
||||
"@ai-sdk/azure": "3.0.88",
|
||||
"@ai-sdk/azure": "3.0.93",
|
||||
"@ai-sdk/cerebras": "2.0.41",
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/gateway": "3.0.191",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@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",
|
||||
"@ai-sdk/provider-utils": "4.0.23",
|
||||
"@ai-sdk/provider": "3.0.16",
|
||||
"@ai-sdk/provider-utils": "4.0.51",
|
||||
"@ai-sdk/togetherai": "2.0.41",
|
||||
"@ai-sdk/vercel": "2.0.39",
|
||||
"@ai-sdk/xai": "3.0.102",
|
||||
@@ -108,7 +108,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",
|
||||
|
||||
@@ -13,15 +13,23 @@ type MantleSDK = {
|
||||
// specific model/region combinations. Keep the mapping narrow and avoid
|
||||
// double-prefixing model IDs that models.dev already marks as global/us/eu/etc.
|
||||
function resolveModelID(modelID: string, region: string | undefined) {
|
||||
if (modelID.startsWith("arn:")) return modelID
|
||||
|
||||
const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]
|
||||
if (crossRegionPrefixes.some((prefix) => modelID.startsWith(prefix))) return modelID
|
||||
|
||||
const resolvedRegion = region ?? "us-east-1"
|
||||
const regionPrefix = resolvedRegion.split("-")[0]
|
||||
if (regionPrefix === "us") {
|
||||
const requiresPrefix = ["nova-micro", "nova-lite", "nova-pro", "nova-premier", "nova-2", "claude", "deepseek"].some(
|
||||
(item) => modelID.includes(item),
|
||||
)
|
||||
const requiresPrefix = [
|
||||
"nova-micro",
|
||||
"nova-lite",
|
||||
"nova-pro",
|
||||
"nova-premier",
|
||||
"nova-2",
|
||||
"claude",
|
||||
"deepseek.r1",
|
||||
].some((item) => modelID.includes(item))
|
||||
if (requiresPrefix && !resolvedRegion.startsWith("us-gov")) return `${regionPrefix}.${modelID}`
|
||||
return modelID
|
||||
}
|
||||
|
||||
@@ -108,15 +108,20 @@ export const Info = Schema.Struct({
|
||||
headerTimeout: Schema.optional(
|
||||
Schema.Union([PositiveInt, Schema.Literal(false)]).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout.",
|
||||
"Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout.",
|
||||
}),
|
||||
).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds to wait for response headers. Provider integrations may set defaults. Set to false to disable timeout.",
|
||||
"Timeout in milliseconds to wait for response headers (default: 300000). Set to false to disable timeout.",
|
||||
}),
|
||||
chunkTimeout: Schema.optional(PositiveInt).annotate({
|
||||
chunkTimeout: Schema.optional(
|
||||
Schema.Union([PositiveInt, Schema.Literal(false)]).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds between streamed SSE chunks for this provider (default: 300000). If no chunk arrives within this window, the request is aborted. Set to false to disable timeout.",
|
||||
}),
|
||||
).annotate({
|
||||
description:
|
||||
"Timeout in milliseconds between streamed SSE chunks for this provider. If no chunk arrives within this window, the request is aborted.",
|
||||
"Timeout in milliseconds between streamed SSE chunks for this provider (default: 300000). If no chunk arrives within this window, the request is aborted. Set to false to disable timeout.",
|
||||
}),
|
||||
}),
|
||||
[Schema.Record(Schema.String, Schema.Any)],
|
||||
|
||||
@@ -528,6 +528,13 @@ describe("AmazonBedrockPlugin", () => {
|
||||
{ region: "us-east-1", modelID: "amazon.nova-2-lite-v1:0", expected: "us.amazon.nova-2-lite-v1:0" },
|
||||
{ region: "us-east-1", modelID: "anthropic.claude-sonnet-4-5", expected: "us.anthropic.claude-sonnet-4-5" },
|
||||
{ region: "us-east-1", modelID: "deepseek.r1-v1:0", expected: "us.deepseek.r1-v1:0" },
|
||||
{ region: "us-east-1", modelID: "us.deepseek.r1-v1:0", expected: "us.deepseek.r1-v1:0" },
|
||||
{ region: "us-east-1", modelID: "deepseek.v3.2", expected: "deepseek.v3.2" },
|
||||
{
|
||||
region: "us-east-1",
|
||||
modelID: "arn:aws:bedrock:us-east-1::foundation-model/deepseek.v3.2",
|
||||
expected: "arn:aws:bedrock:us-east-1::foundation-model/deepseek.v3.2",
|
||||
},
|
||||
{ region: "us-gov-west-1", modelID: "anthropic.claude-sonnet-4-5", expected: "anthropic.claude-sonnet-4-5" },
|
||||
{ region: "us-east-1", modelID: "cohere.command-r-plus-v1:0", expected: "cohere.command-r-plus-v1:0" },
|
||||
{ region: "eu-west-1", modelID: "anthropic.claude-sonnet-4-5", expected: "eu.anthropic.claude-sonnet-4-5" },
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { expect, test } from "bun:test"
|
||||
|
||||
test("disables public npm security audits", () => {
|
||||
expect(process.env.NPM_CONFIG_AUDIT).toBe("false")
|
||||
})
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "path"
|
||||
|
||||
process.env.OPENCODE_DB = ":memory:"
|
||||
process.env.NPM_CONFIG_AUDIT = "false"
|
||||
process.env.OPENCODE_MODELS_PATH = path.join(import.meta.dir, "plugin", "fixtures", "models-dev.json")
|
||||
process.env.OPENCODE_DISABLE_MODELS_FETCH = "true"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@opencode-ai/desktop",
|
||||
"private": true,
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"homepage": "https://opencode.ai",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "@opencode-ai/effect-drizzle-sqlite",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "@opencode-ai/effect-sqlite-node",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/enterprise",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode-ai/function",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "@opencode-ai/http-recorder",
|
||||
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "@opencode-ai/llm",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "1.18.26",
|
||||
"version": "1.18.31",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
@@ -58,19 +58,19 @@
|
||||
"@ai-sdk/alibaba": "1.0.17",
|
||||
"@ai-sdk/amazon-bedrock": "4.0.166",
|
||||
"@ai-sdk/anthropic": "3.0.111",
|
||||
"@ai-sdk/azure": "3.0.88",
|
||||
"@ai-sdk/azure": "3.0.93",
|
||||
"@ai-sdk/cerebras": "2.0.60",
|
||||
"@ai-sdk/cohere": "3.0.27",
|
||||
"@ai-sdk/deepinfra": "2.0.41",
|
||||
"@ai-sdk/gateway": "3.0.104",
|
||||
"@ai-sdk/gateway": "3.0.191",
|
||||
"@ai-sdk/google": "3.0.73",
|
||||
"@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",
|
||||
"@ai-sdk/provider": "3.0.16",
|
||||
"@ai-sdk/togetherai": "2.0.41",
|
||||
"@ai-sdk/vercel": "2.0.39",
|
||||
"@ai-sdk/xai": "3.0.102",
|
||||
@@ -120,7 +120,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",
|
||||
|
||||
@@ -61,8 +61,11 @@ export function buildEffortSelectOption(input: {
|
||||
description: "Available effort levels for this model",
|
||||
category: "thought_level",
|
||||
type: "select",
|
||||
currentValue: selectVariant(input.currentVariant, input.variants),
|
||||
options: input.variants.map((variant) => ({
|
||||
currentValue:
|
||||
input.currentVariant === DEFAULT_VARIANT_VALUE
|
||||
? DEFAULT_VARIANT_VALUE
|
||||
: selectVariant(input.currentVariant, input.variants),
|
||||
options: [...new Set([...input.variants, DEFAULT_VARIANT_VALUE])].map((variant) => ({
|
||||
value: variant,
|
||||
name: formatVariantName(variant),
|
||||
})),
|
||||
|
||||
@@ -134,7 +134,7 @@ export class Subscription {
|
||||
sessionId: message.info.sessionID,
|
||||
update: {
|
||||
sessionUpdate,
|
||||
messageId: message.info.id,
|
||||
messageId: part.type === "reasoning" ? part.id : message.info.id,
|
||||
...chunk,
|
||||
},
|
||||
})
|
||||
@@ -248,7 +248,7 @@ export class Subscription {
|
||||
sessionId: session.id,
|
||||
update: {
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
messageId: props.messageID,
|
||||
messageId: props.partID,
|
||||
content: {
|
||||
type: "text",
|
||||
text: props.delta,
|
||||
|
||||
@@ -31,10 +31,10 @@ import {
|
||||
} from "@agentclientprotocol/sdk"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import type { AssistantMessage, Message, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||
import type { AssistantMessage, Message, OpencodeClient, Session, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||
import { Context, Effect, Layer, ManagedRuntime } from "effect"
|
||||
import * as ACPError from "./error"
|
||||
import { buildConfigOptions, parseModelSelection } from "./config-option"
|
||||
import { buildConfigOptions, DEFAULT_VARIANT_VALUE, parseModelSelection } from "./config-option"
|
||||
import { promptContentToParts } from "./content"
|
||||
import { Directory } from "./directory"
|
||||
import { ACPEvent } from "./event"
|
||||
@@ -210,7 +210,7 @@ export function make(input: {
|
||||
|
||||
const loadSession = Effect.fn("ACP.loadSession")(function* (params: LoadSessionRequest) {
|
||||
const snapshot = yield* directorySnapshot(params.cwd)
|
||||
yield* request(
|
||||
const backing = yield* request(
|
||||
() => input.sdk.session.get({ directory: params.cwd, sessionID: params.sessionId }, { throwOnError: true }),
|
||||
"session",
|
||||
)
|
||||
@@ -218,15 +218,18 @@ export function make(input: {
|
||||
() => input.sdk.session.messages({ directory: params.cwd, sessionID: params.sessionId }, { throwOnError: true }),
|
||||
"session",
|
||||
)
|
||||
const restored = restoreFromMessages(messages.map((item) => item.info))
|
||||
const model = restored.model ?? selectDefaultModel(snapshot)
|
||||
const restored = restoreSession(
|
||||
snapshot,
|
||||
backing,
|
||||
messages.map((item) => item.info),
|
||||
)
|
||||
const state = yield* session.load({
|
||||
id: params.sessionId,
|
||||
cwd: params.cwd,
|
||||
mcpServers: params.mcpServers,
|
||||
model,
|
||||
variant: restored.variant ?? selectVariant(snapshot, model),
|
||||
modeId: restored.modeId ?? (snapshot.availableModes.length > 0 ? snapshot.defaultModeID : undefined),
|
||||
model: restored.model,
|
||||
variant: restored.variant,
|
||||
modeId: restored.modeId,
|
||||
})
|
||||
sessionSnapshots.set(state.id, snapshot)
|
||||
|
||||
@@ -236,7 +239,7 @@ export function make(input: {
|
||||
|
||||
return {
|
||||
configOptions: configOptions(snapshot, {
|
||||
model: state.model ?? model,
|
||||
model: state.model ?? restored.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
}),
|
||||
@@ -291,7 +294,7 @@ export function make(input: {
|
||||
|
||||
const resumeSession = Effect.fn("ACP.resumeSession")(function* (params: ResumeSessionRequest) {
|
||||
const snapshot = yield* directorySnapshot(params.cwd)
|
||||
yield* request(
|
||||
const backing = yield* request(
|
||||
() => input.sdk.session.get({ directory: params.cwd, sessionID: params.sessionId }, { throwOnError: true }),
|
||||
"session",
|
||||
)
|
||||
@@ -303,15 +306,18 @@ export function make(input: {
|
||||
),
|
||||
"session",
|
||||
)
|
||||
const restored = restoreFromMessages(messages.map((item) => item.info))
|
||||
const model = restored.model ?? selectDefaultModel(snapshot)
|
||||
const restored = restoreSession(
|
||||
snapshot,
|
||||
backing,
|
||||
messages.map((item) => item.info),
|
||||
)
|
||||
const state = yield* session.load({
|
||||
id: params.sessionId,
|
||||
cwd: params.cwd,
|
||||
mcpServers: params.mcpServers ?? [],
|
||||
model,
|
||||
variant: restored.variant ?? selectVariant(snapshot, model),
|
||||
modeId: restored.modeId ?? (snapshot.availableModes.length > 0 ? snapshot.defaultModeID : undefined),
|
||||
model: restored.model,
|
||||
variant: restored.variant,
|
||||
modeId: restored.modeId,
|
||||
})
|
||||
sessionSnapshots.set(state.id, snapshot)
|
||||
|
||||
@@ -320,7 +326,7 @@ export function make(input: {
|
||||
|
||||
return {
|
||||
configOptions: configOptions(snapshot, {
|
||||
model: state.model ?? model,
|
||||
model: state.model ?? restored.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
}),
|
||||
@@ -371,15 +377,18 @@ export function make(input: {
|
||||
input.sdk.session.messages({ directory: params.cwd, sessionID: forked.id, limit: 20 }, { throwOnError: true }),
|
||||
"session",
|
||||
)
|
||||
const restored = restoreFromMessages(messages.map((item) => item.info))
|
||||
const model = restored.model ?? selectDefaultModel(snapshot)
|
||||
const restored = restoreSession(
|
||||
snapshot,
|
||||
forked,
|
||||
messages.map((item) => item.info),
|
||||
)
|
||||
const state = yield* session.load({
|
||||
id: forked.id,
|
||||
cwd: params.cwd,
|
||||
mcpServers: params.mcpServers ?? [],
|
||||
model,
|
||||
variant: restored.variant ?? selectVariant(snapshot, model),
|
||||
modeId: restored.modeId ?? (snapshot.availableModes.length > 0 ? snapshot.defaultModeID : undefined),
|
||||
model: restored.model,
|
||||
variant: restored.variant,
|
||||
modeId: restored.modeId,
|
||||
})
|
||||
sessionSnapshots.set(state.id, snapshot)
|
||||
|
||||
@@ -390,7 +399,7 @@ export function make(input: {
|
||||
return {
|
||||
sessionId: state.id,
|
||||
configOptions: configOptions(snapshot, {
|
||||
model: state.model ?? model,
|
||||
model: state.model ?? restored.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
}),
|
||||
@@ -408,23 +417,25 @@ export function make(input: {
|
||||
|
||||
if (params.configId === "model") {
|
||||
const selected = yield* parseSelectedModel(snapshot, params.value)
|
||||
const variant = selected.variant ?? selectVariant(snapshot, selected.model)
|
||||
const variant = selectModelVariant(snapshot, current, selected)
|
||||
const state = yield* session
|
||||
.setVariant(params.sessionId, Directory.variants(snapshot, selected.model) ? variant : undefined)
|
||||
.pipe(Effect.andThen(session.setModel(params.sessionId, selected.model)))
|
||||
const options = configOptions(snapshot, {
|
||||
model: state.model ?? selected.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
})
|
||||
yield* sendConfigOptionUpdate(input.connection, params.sessionId, options)
|
||||
return {
|
||||
configOptions: configOptions(snapshot, {
|
||||
model: state.model ?? selected.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
}),
|
||||
configOptions: options,
|
||||
}
|
||||
}
|
||||
|
||||
if (params.configId === "effort") {
|
||||
const model = current.model ?? selectDefaultModel(snapshot)
|
||||
const variants = Directory.variants(snapshot, model)
|
||||
if (!variants || !Object.keys(variants).includes(params.value)) {
|
||||
if (!variants || !hasVariant(variants, params.value)) {
|
||||
return yield* new ACPError.InvalidEffortError({ effort: params.value })
|
||||
}
|
||||
const state = yield* session.setVariant(params.sessionId, params.value)
|
||||
@@ -468,14 +479,18 @@ export function make(input: {
|
||||
const current = yield* session.get(params.sessionId)
|
||||
const snapshot = yield* configSnapshot(current)
|
||||
const selected = yield* parseSelectedModel(snapshot, params.modelId)
|
||||
yield* session
|
||||
.setVariant(
|
||||
params.sessionId,
|
||||
Directory.variants(snapshot, selected.model)
|
||||
? (selected.variant ?? selectVariant(snapshot, selected.model))
|
||||
: undefined,
|
||||
)
|
||||
const state = yield* session
|
||||
.setVariant(params.sessionId, selectModelVariant(snapshot, current, selected))
|
||||
.pipe(Effect.andThen(session.setModel(params.sessionId, selected.model)))
|
||||
yield* sendConfigOptionUpdate(
|
||||
input.connection,
|
||||
params.sessionId,
|
||||
configOptions(snapshot, {
|
||||
model: state.model ?? selected.model,
|
||||
variant: state.variant,
|
||||
modeId: state.modeId,
|
||||
}),
|
||||
)
|
||||
return {}
|
||||
})
|
||||
|
||||
@@ -899,6 +914,24 @@ function selectVariant(snapshot: Directory.Snapshot, model: Directory.DefaultMod
|
||||
return Object.keys(variants)[0]
|
||||
}
|
||||
|
||||
function selectModelVariant(
|
||||
snapshot: Directory.Snapshot,
|
||||
current: ACPSession.Info,
|
||||
selected: { model: Directory.DefaultModel; variant?: string },
|
||||
) {
|
||||
const variants = Directory.variants(snapshot, selected.model)
|
||||
if (!variants) return
|
||||
if (selected.variant) return selected.variant
|
||||
if (sameModel(selected.model, current.model) && current.variant && hasVariant(variants, current.variant))
|
||||
return current.variant
|
||||
return selectVariant(snapshot, selected.model)
|
||||
}
|
||||
|
||||
function hasVariant(variants: Directory.ModelVariants, variant: string) {
|
||||
// "default" is also the persisted sentinel for no explicit variant override.
|
||||
return variant === DEFAULT_VARIANT_VALUE || Object.hasOwn(variants, variant)
|
||||
}
|
||||
|
||||
function configOptions(snapshot: Directory.Snapshot, session: ConfigState) {
|
||||
return buildConfigOptions({
|
||||
providers: Object.values(snapshot.providers),
|
||||
@@ -909,6 +942,25 @@ function configOptions(snapshot: Directory.Snapshot, session: ConfigState) {
|
||||
})
|
||||
}
|
||||
|
||||
function sendConfigOptionUpdate(
|
||||
connection: ServiceConnection | undefined,
|
||||
sessionId: string,
|
||||
options: ReturnType<typeof configOptions>,
|
||||
) {
|
||||
if (!connection) return Effect.void
|
||||
return Effect.tryPromise({
|
||||
try: () =>
|
||||
connection.sessionUpdate({
|
||||
sessionId,
|
||||
update: {
|
||||
sessionUpdate: "config_option_update",
|
||||
configOptions: options,
|
||||
},
|
||||
}),
|
||||
catch: () => undefined,
|
||||
}).pipe(Effect.ignore)
|
||||
}
|
||||
|
||||
function parseSelectedModel(snapshot: Directory.Snapshot, modelId: string) {
|
||||
const selected = parseModelSelection(modelId, Object.values(snapshot.providers))
|
||||
const provider = snapshot.providers[ProviderV2.ID.make(selected.model.providerID)]
|
||||
@@ -1034,6 +1086,75 @@ function stableStringify(value: unknown): string {
|
||||
.join(",")}}`
|
||||
}
|
||||
|
||||
function restoreSession(
|
||||
snapshot: Directory.Snapshot,
|
||||
backing: Pick<Session, "agent" | "model">,
|
||||
messages: MessageInfo[],
|
||||
) {
|
||||
const history = restoreFromMessages(messages)
|
||||
const durable = restoreDurableModel(backing.model)
|
||||
const model = restoreModel(snapshot, durable.model, history.model)
|
||||
return {
|
||||
model,
|
||||
variant: restoreVariant(snapshot, model, durable, history),
|
||||
modeId: restoreMode(snapshot, backing.agent, history.modeId),
|
||||
}
|
||||
}
|
||||
|
||||
function restoreDurableModel(model: Session["model"] | undefined) {
|
||||
if (!model) return {}
|
||||
return {
|
||||
model: {
|
||||
providerID: ProviderV2.ID.make(model.providerID),
|
||||
modelID: ModelV2.ID.make(model.id),
|
||||
},
|
||||
variant: model.variant,
|
||||
}
|
||||
}
|
||||
|
||||
function restoreModel(
|
||||
snapshot: Directory.Snapshot,
|
||||
durable: Directory.DefaultModel | undefined,
|
||||
history: Directory.DefaultModel | undefined,
|
||||
) {
|
||||
if (durable && hasModel(snapshot, durable)) return durable
|
||||
if (history && hasModel(snapshot, history)) return history
|
||||
return selectDefaultModel(snapshot)
|
||||
}
|
||||
|
||||
function restoreVariant(
|
||||
snapshot: Directory.Snapshot,
|
||||
model: Directory.DefaultModel,
|
||||
durable: { model?: Directory.DefaultModel; variant?: string },
|
||||
history: { model?: Directory.DefaultModel; variant?: string },
|
||||
) {
|
||||
const variants = Directory.variants(snapshot, model)
|
||||
if (!variants) return
|
||||
if (sameModel(model, durable.model) && durable.variant && hasVariant(variants, durable.variant))
|
||||
return durable.variant
|
||||
if (sameModel(model, history.model) && history.variant && hasVariant(variants, history.variant))
|
||||
return history.variant
|
||||
return selectVariant(snapshot, model)
|
||||
}
|
||||
|
||||
function restoreMode(snapshot: Directory.Snapshot, durable: string | undefined, history: string | undefined) {
|
||||
if (hasMode(snapshot, durable)) return durable
|
||||
if (hasMode(snapshot, history)) return history
|
||||
if (snapshot.availableModes.length > 0) return snapshot.defaultModeID
|
||||
}
|
||||
|
||||
function hasModel(snapshot: Directory.Snapshot, model: Directory.DefaultModel) {
|
||||
return Boolean(snapshot.providers[model.providerID]?.models[model.modelID])
|
||||
}
|
||||
|
||||
function hasMode(snapshot: Directory.Snapshot, modeId: string | undefined) {
|
||||
return Boolean(modeId && snapshot.availableModes.some((mode) => mode.id === modeId))
|
||||
}
|
||||
|
||||
function sameModel(left: Directory.DefaultModel, right: Directory.DefaultModel | undefined) {
|
||||
return left.providerID === right?.providerID && left.modelID === right.modelID
|
||||
}
|
||||
|
||||
function restoreFromMessages(messages: readonly MessageInfo[]) {
|
||||
const user = messages.findLast(
|
||||
(message) => message.role === "user" && message.model?.providerID && message.model.modelID,
|
||||
|
||||
@@ -303,7 +303,7 @@ export const TuiThreadCommand = cmd({
|
||||
unguard?.()
|
||||
} catch {}
|
||||
}
|
||||
process.exit(0)
|
||||
process.exit()
|
||||
},
|
||||
})
|
||||
// scratch
|
||||
|
||||
@@ -361,6 +361,7 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
|
||||
if (!incoming.model.providerID.includes("github-copilot")) return
|
||||
|
||||
output.headers["X-GitHub-Api-Version"] = API_VERSION
|
||||
output.headers["X-Interaction-Id"] = incoming.sessionID
|
||||
if (incoming.agent === "title") {
|
||||
output.headers["X-Interaction-Type"] = "agent-session-name-generation"
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ function build(key: string, remote: SelectableItem, url: string, prev?: Model):
|
||||
variants[effort] = {
|
||||
thinking: {
|
||||
type: "adaptive",
|
||||
...(model.api.id.includes("opus-4.7") ? { display: "summarized" } : {}),
|
||||
display: "summarized",
|
||||
},
|
||||
effort,
|
||||
}
|
||||
|
||||
@@ -297,8 +297,11 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||
if (ALLOWED_MODELS.has(model.api.id)) return true
|
||||
if (DISALLOWED_MODELS.has(model.api.id)) return false
|
||||
if (model.api.id === "gpt-5.6") return false
|
||||
const match = model.api.id.match(/^gpt-(\d+\.\d+)/)
|
||||
return match ? parseFloat(match[1]) > 5.4 : false
|
||||
const match = model.api.id.match(/^gpt-(\d+)(?:\.(\d+))?/)
|
||||
if (!match) return false
|
||||
const major = Number(match[1])
|
||||
const minor = Number(match[2] ?? 0)
|
||||
return major > 5 || (major === 5 && minor > 4)
|
||||
})
|
||||
.map(([modelID, model]) => [
|
||||
modelID,
|
||||
|
||||
@@ -376,6 +376,10 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
|
||||
// Skip region prefixing if model already has a cross-region inference profile prefix
|
||||
// Models from models.dev may already include prefixes like us., eu., global., etc.
|
||||
if (modelID.startsWith("arn:")) {
|
||||
return sdk.languageModel(modelID)
|
||||
}
|
||||
|
||||
const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]
|
||||
if (crossRegionPrefixes.some((prefix) => modelID.startsWith(prefix))) {
|
||||
return sdk.languageModel(modelID)
|
||||
@@ -398,7 +402,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
|
||||
"nova-premier",
|
||||
"nova-2",
|
||||
"claude",
|
||||
"deepseek",
|
||||
"deepseek.r1",
|
||||
].some((m) => modelID.includes(m))
|
||||
const isGovCloud = region.startsWith("us-gov")
|
||||
if (modelRequiresPrefix && !isGovCloud) {
|
||||
@@ -1792,8 +1796,8 @@ const layer = Layer.effect(
|
||||
if (existing) return existing
|
||||
|
||||
const customFetch = options["fetch"]
|
||||
const chunkTimeout = options["chunkTimeout"]
|
||||
const headerTimeout = options["headerTimeout"]
|
||||
const chunkTimeout = options["chunkTimeout"] ?? 300_000
|
||||
const headerTimeout = options["headerTimeout"] ?? 300_000
|
||||
delete options["chunkTimeout"]
|
||||
delete options["headerTimeout"]
|
||||
|
||||
|
||||
@@ -1824,11 +1824,14 @@ function reasoningEffort(model: Provider.Model, effort: string) {
|
||||
case "ai-gateway-provider":
|
||||
case "merge-gateway-ai-sdk-provider":
|
||||
return { reasoningEffort: effort }
|
||||
case "gitlab-ai-provider":
|
||||
if (model.family?.startsWith("gpt")) return { reasoningEffort: effort }
|
||||
if (model.family?.startsWith("claude")) return { thinking: { type: "adaptive", effort } }
|
||||
return
|
||||
case "@ai-sdk/cohere":
|
||||
case "@ai-sdk/perplexity":
|
||||
case "@ai-sdk/vercel":
|
||||
case "@ai-sdk/alibaba":
|
||||
case "gitlab-ai-provider":
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ export const errorLayer = HttpRouter.middleware<{ handles: unknown }>()((effect)
|
||||
ConfigErrorV1.JsonError.isInstance(error) ||
|
||||
ConfigErrorV1.InvalidError.isInstance(error) ||
|
||||
ConfigErrorV1.FrontmatterError.isInstance(error) ||
|
||||
ConfigErrorV1.DirectoryTypoError.isInstance(error)
|
||||
ConfigErrorV1.DirectoryTypoError.isInstance(error) ||
|
||||
ConfigErrorV1.RemoteAuthError.isInstance(error)
|
||||
) {
|
||||
return Effect.succeed(HttpServerResponse.jsonUnsafe(error.toObject(), { status: 400 }))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
You are an AI agent powered by OpenCode, a coding agent harness. Help the user accomplish their goals using the tools you have available.
|
||||
|
||||
# Harness
|
||||
- Responses are rendered as GitHub-flavored Markdown.
|
||||
- `<system-reminder>` blocks are harness instructions, not user-authored content. Read and follow them.
|
||||
- Prefer parallelizing independent tool calls.
|
||||
- Do not use a skill based solely on keywords, superficial relevance, or its availability. Avoid re-reading skills already available in the conversation unless needed.
|
||||
- Prefer dedicated tools over shell commands; fall back to the shell when a tool cannot do what you need.
|
||||
- Do not chain shell commands with separators like `echo "====";` or `printf '---'`; the output becomes noisy in a way that makes the user's side of the conversation worse.
|
||||
|
||||
# Communication
|
||||
|
||||
State the main point clearly and early. Keep responses clear and concise, and avoid unnecessary technical jargon. Use only as much structure as needed, and include technical detail only when it helps the conversation. Use clear file paths when referring to files.
|
||||
|
||||
When describing your work, avoid adding what you won't do, what will remain unchanged, or how you'll separate or categorize results. Do not introduce unprompted alternatives through framing such as "X, not Y" or "This isn't about X. It's about Y."
|
||||
|
||||
## Autonomy
|
||||
|
||||
Infer the user's intent and your task scope from their instructions and the prior conversation context. You should bias towards action and carry out the user's intended task until it is completed. If the intent is unclear, progress towards the goal using the available information and ask for clarification while continuing independent work when possible.
|
||||
|
||||
When the user's prompt indicates a request for action, such as "can you...", "I want to...", "help me..." and similar expressions, treat these as instructions to take action. Do not stop at acknowledging capability (e.g. "Yes…"), proposing a plan, or offering to continue. Do not settle for a partial or "helpful enough" solution to save time, effort, or tokens. Continue until the user's intended goal is fulfilled, even when it requires sustained work.
|
||||
|
||||
## Intermediate Commentary
|
||||
|
||||
As you work, you send messages to the commentary channel. These are how you collaborate with the user while you work: stating assumptions and providing updates. Keep them concise and quickly scannable, and send them only when they add real information, such as a discovery, a tradeoff, or a blocker. Do not narrate routine reads, searches, or edits.
|
||||
|
||||
By default, treat new messages received during ongoing work as steering the active task rather than replacing it. Incorporate corrections and constraints, and answer questions briefly in commentary before continuing. Replace the task only when the user clearly cancels it or requests an incompatible objective.
|
||||
|
||||
Do not put a final response, such as a blocking or clarifying question, in the commentary channel. The final answer must always be fully self-contained.
|
||||
|
||||
## Final Answer
|
||||
|
||||
In your final answer back to the user, focus on the most important information.
|
||||
|
||||
# Working in codebases
|
||||
|
||||
- Keep changes consistent with the structure, naming, style, and patterns of the surrounding code.
|
||||
- Treat unfamiliar files or changes as potential user work and investigate before deleting or overwriting them.
|
||||
- Do not introduce unsolicited warnings, disclaimers, approval flows, or safety/compliance checklists due to hypothetical risk.
|
||||
- Do not write tests for reversible, low-impact changes or that mirror the implementation. If you do choose to verify your work with tests, make sure that the tests are meaningful and necessary to verify implementation.
|
||||
- Run tests appropriate to the change and complete required checks. Once those pass, broaden or repeat testing only when new changes, failures, or unresolved concerns justify it; otherwise, continue toward completing the task.
|
||||
|
||||
|
||||
# Delegation
|
||||
|
||||
Do not spawn subagents unless the user or applicable AGENTS.md/skill instructions explicitly ask for subagents, delegation, or parallel agent work.
|
||||
@@ -8,6 +8,7 @@ import PROMPT_DEFAULT from "./prompt/default.txt"
|
||||
import PROMPT_BEAST from "./prompt/beast.txt"
|
||||
import PROMPT_GEMINI from "./prompt/gemini.txt"
|
||||
import PROMPT_GPT from "./prompt/gpt.txt"
|
||||
import PROMPT_ASTRA from "./prompt/gpt-astra.txt"
|
||||
import PROMPT_KIMI from "./prompt/kimi.txt"
|
||||
import PROMPT_META from "./prompt/meta.txt"
|
||||
|
||||
@@ -32,6 +33,7 @@ export function provider(model: Provider.Model) {
|
||||
if (model.api.id.includes("gpt-4") || model.api.id.includes("o1") || model.api.id.includes("o3"))
|
||||
return [PROMPT_BEAST]
|
||||
if (model.api.id.includes("gpt")) {
|
||||
if (model.api.id.includes("gpt-6")) return [PROMPT_ASTRA]
|
||||
if (model.api.id.includes("codex")) {
|
||||
return [PROMPT_CODEX]
|
||||
}
|
||||
|
||||
@@ -114,6 +114,17 @@ describe("acp config options", () => {
|
||||
expect(buildEffortSelectOption({ variants: [] })).toBeUndefined()
|
||||
})
|
||||
|
||||
test("exposes an explicit default even when the provider only lists named variants", () => {
|
||||
expect(buildEffortSelectOption({ variants: ["low", "medium"], currentVariant: "default" })).toMatchObject({
|
||||
currentValue: "default",
|
||||
options: [
|
||||
{ value: "low", name: "Low" },
|
||||
{ value: "medium", name: "Medium" },
|
||||
{ value: "default", name: "Default" },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
||||
test("builds the mode select option with descriptions when present", () => {
|
||||
expect(
|
||||
buildModeSelectOption({
|
||||
|
||||
@@ -352,6 +352,40 @@ describe("acp event routing", () => {
|
||||
).toEqual(["agent_thought_chunk", "agent_thought_chunk"])
|
||||
})
|
||||
|
||||
it("uses reasoning part ids as ACP thought message boundaries", async () => {
|
||||
const harness = createHarness()
|
||||
await createKnownSession(harness.session, "ses_reasoning", {
|
||||
messageId: "msg_reasoning",
|
||||
partId: "part_first",
|
||||
partType: "reasoning",
|
||||
})
|
||||
await Effect.runPromise(
|
||||
harness.session.recordPartMetadata({
|
||||
sessionId: "ses_reasoning",
|
||||
messageId: "msg_reasoning",
|
||||
partId: "part_second",
|
||||
partType: "reasoning",
|
||||
role: "assistant",
|
||||
}),
|
||||
)
|
||||
|
||||
await harness.subscription.handle(textDelta("ses_reasoning", "msg_reasoning", "part_first", "First"))
|
||||
await harness.subscription.handle(textDelta("ses_reasoning", "msg_reasoning", "part_second", "Second"))
|
||||
|
||||
expect(harness.updates.map((update) => update.update)).toEqual([
|
||||
{
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
messageId: "part_first",
|
||||
content: { type: "text", text: "First" },
|
||||
},
|
||||
{
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
messageId: "part_second",
|
||||
content: { type: "text", text: "Second" },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("does not create extra subscriptions on repeated loadSession", async () => {
|
||||
const harness = createHarness()
|
||||
let subscription: ACPEvent.Subscription | undefined
|
||||
|
||||
@@ -194,6 +194,20 @@ describe("ACP service sessions", () => {
|
||||
messages: readonly { info: unknown; parts: readonly unknown[] }[] = [],
|
||||
options?: {
|
||||
abort?: (input: { sessionID: string }) => Promise<{ data: boolean }>
|
||||
get?: () => Promise<{
|
||||
data: {
|
||||
id: string
|
||||
agent?: string
|
||||
model?: { id: string; providerID: string; variant?: string }
|
||||
}
|
||||
}>
|
||||
fork?: (input: { sessionID: string }) => Promise<{
|
||||
data: {
|
||||
id: string
|
||||
agent?: string
|
||||
model?: { id: string; providerID: string; variant?: string }
|
||||
}
|
||||
}>
|
||||
prompt?: (input: unknown) => Promise<{ data: { info: ReturnType<typeof assistantInfo> } }>
|
||||
sessionUpdate?: (update: SessionNotification) => Promise<void>
|
||||
},
|
||||
@@ -243,7 +257,7 @@ describe("ACP service sessions", () => {
|
||||
},
|
||||
session: {
|
||||
create: () => Promise.resolve({ data: { id: "ses_new" } }),
|
||||
get: () => Promise.resolve({ data: { id: "ses_loaded" } }),
|
||||
get: options?.get ?? (() => Promise.resolve({ data: { id: "ses_loaded" } })),
|
||||
list: (input: { directory?: string }) =>
|
||||
Promise.resolve({
|
||||
data: input.directory ? sessions.filter((session) => session.directory === input.directory) : sessions,
|
||||
@@ -292,7 +306,7 @@ describe("ACP service sessions", () => {
|
||||
}),
|
||||
fork: (input: { sessionID: string }) => {
|
||||
forks.push(input.sessionID)
|
||||
return Promise.resolve({ data: { id: `fork_${input.sessionID}` } })
|
||||
return options?.fork?.(input) ?? Promise.resolve({ data: { id: `fork_${input.sessionID}` } })
|
||||
},
|
||||
},
|
||||
mcp: {
|
||||
@@ -378,6 +392,149 @@ describe("ACP service sessions", () => {
|
||||
expect(result.configOptions?.find((option) => option.id === "mode")?.currentValue).toBe("plan")
|
||||
})
|
||||
|
||||
it("restores durable model variant and mode before message history", async () => {
|
||||
const { service } = makeService(
|
||||
[
|
||||
{
|
||||
info: {
|
||||
role: "assistant",
|
||||
providerID: "test",
|
||||
modelID: "second-model",
|
||||
variant: "medium",
|
||||
mode: "build",
|
||||
},
|
||||
parts: [],
|
||||
},
|
||||
],
|
||||
{
|
||||
get: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: "ses_loaded",
|
||||
agent: "plan",
|
||||
model: { providerID: "test", id: "test-model", variant: "high" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const loaded = await Effect.runPromise(
|
||||
service.loadSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }),
|
||||
)
|
||||
const resumed = await Effect.runPromise(
|
||||
service.resumeSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }),
|
||||
)
|
||||
|
||||
expect(select(loaded, "model")?.currentValue).toBe("test/test-model")
|
||||
expect(select(loaded, "effort")?.currentValue).toBe("high")
|
||||
expect(select(loaded, "mode")?.currentValue).toBe("plan")
|
||||
expect(select(resumed, "model")?.currentValue).toBe("test/test-model")
|
||||
expect(select(resumed, "effort")?.currentValue).toBe("high")
|
||||
expect(select(resumed, "mode")?.currentValue).toBe("plan")
|
||||
})
|
||||
|
||||
it.each(["loadSession", "resumeSession"] as const)("%s preserves default effort", async (method) => {
|
||||
const { service, prompts, updates } = makeService(
|
||||
[
|
||||
{
|
||||
info: {
|
||||
role: "user",
|
||||
model: { providerID: "test", modelID: "second-model", variant: "medium" },
|
||||
agent: "build",
|
||||
},
|
||||
parts: [],
|
||||
},
|
||||
],
|
||||
{
|
||||
get: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: "ses_loaded",
|
||||
agent: "build",
|
||||
model: { providerID: "test", id: "second-model", variant: "default" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const restored = await Effect.runPromise(
|
||||
service[method]({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }),
|
||||
)
|
||||
expect(select(restored, "effort")?.currentValue).toBe("default")
|
||||
expect(flattenSelectOptions(select(restored, "effort")).map((option) => option.value)).toContain("default")
|
||||
|
||||
await Effect.runPromise(service.setSessionModel({ sessionId: "ses_loaded", modelId: "test/second-model" }))
|
||||
const update = updates.findLast((item) => item.update.sessionUpdate === "config_option_update")?.update
|
||||
if (update?.sessionUpdate !== "config_option_update") throw new Error("missing config option update")
|
||||
expect(select({ configOptions: update.configOptions }, "effort")?.currentValue).toBe("default")
|
||||
|
||||
const synchronized = await Effect.runPromise(
|
||||
service.setSessionConfigOption({ sessionId: "ses_loaded", configId: "model", value: "test/second-model" }),
|
||||
)
|
||||
expect(select(synchronized, "effort")?.currentValue).toBe("default")
|
||||
|
||||
await Effect.runPromise(service.prompt({ sessionId: "ses_loaded", prompt: [{ type: "text", text: "hello" }] }))
|
||||
expect(prompts).toEqual([expect.objectContaining({ variant: "default" })])
|
||||
|
||||
await Effect.runPromise(
|
||||
service.setSessionConfigOption({ sessionId: "ses_loaded", configId: "effort", value: "medium" }),
|
||||
)
|
||||
const reset = await Effect.runPromise(
|
||||
service.setSessionConfigOption({ sessionId: "ses_loaded", configId: "effort", value: "default" }),
|
||||
)
|
||||
expect(select(reset, "effort")?.currentValue).toBe("default")
|
||||
})
|
||||
|
||||
it("falls back from stale durable state to valid message state", async () => {
|
||||
const { service } = makeService(
|
||||
[
|
||||
{
|
||||
info: {
|
||||
role: "assistant",
|
||||
providerID: "test",
|
||||
modelID: "second-model",
|
||||
variant: "medium",
|
||||
mode: "plan",
|
||||
},
|
||||
parts: [],
|
||||
},
|
||||
],
|
||||
{
|
||||
get: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: "ses_loaded",
|
||||
agent: "missing",
|
||||
model: { providerID: "missing", id: "missing", variant: "missing" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const loaded = await Effect.runPromise(
|
||||
service.loadSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }),
|
||||
)
|
||||
|
||||
expect(select(loaded, "model")?.currentValue).toBe("test/second-model")
|
||||
expect(select(loaded, "effort")?.currentValue).toBe("medium")
|
||||
expect(select(loaded, "mode")?.currentValue).toBe("plan")
|
||||
})
|
||||
|
||||
it("restores default effort from history when durable model state is absent", async () => {
|
||||
const { service } = makeService([
|
||||
{
|
||||
info: {
|
||||
role: "user",
|
||||
model: { providerID: "test", modelID: "second-model", variant: "default" },
|
||||
agent: "build",
|
||||
},
|
||||
parts: [],
|
||||
},
|
||||
])
|
||||
const resumed = await Effect.runPromise(service.resumeSession({ cwd: "/workspace", sessionId: "ses_loaded" }))
|
||||
expect(select(resumed, "effort")?.currentValue).toBe("default")
|
||||
})
|
||||
|
||||
it("replays loaded session transcript chunks", async () => {
|
||||
const { service, updates } = makeService([
|
||||
{
|
||||
@@ -418,6 +575,47 @@ describe("ACP service sessions", () => {
|
||||
])
|
||||
})
|
||||
|
||||
it("replays reasoning parts as separate ACP thought messages", async () => {
|
||||
const { service, updates } = makeService([
|
||||
{
|
||||
info: { id: "msg_assistant", sessionID: "ses_loaded", role: "assistant" },
|
||||
parts: [
|
||||
{
|
||||
id: "part_first",
|
||||
sessionID: "ses_loaded",
|
||||
messageID: "msg_assistant",
|
||||
type: "reasoning",
|
||||
text: "First",
|
||||
time: { start: 1, end: 2 },
|
||||
},
|
||||
{
|
||||
id: "part_second",
|
||||
sessionID: "ses_loaded",
|
||||
messageID: "msg_assistant",
|
||||
type: "reasoning",
|
||||
text: "Second",
|
||||
time: { start: 3, end: 4 },
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
await Effect.runPromise(service.loadSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }))
|
||||
|
||||
expect(updates.map((item) => item.update).filter((item) => item.sessionUpdate === "agent_thought_chunk")).toEqual([
|
||||
{
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
messageId: "part_first",
|
||||
content: { type: "text", text: "First" },
|
||||
},
|
||||
{
|
||||
sessionUpdate: "agent_thought_chunk",
|
||||
messageId: "part_second",
|
||||
content: { type: "text", text: "Second" },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it("lists sessions sorted by updated time with cursor support", async () => {
|
||||
const { service } = makeService()
|
||||
const first = await Effect.runPromise(service.listSessions({ cwd: "/workspace" }))
|
||||
@@ -559,6 +757,41 @@ describe("ACP service sessions", () => {
|
||||
expect(forks).toEqual(["ses_parent"])
|
||||
})
|
||||
|
||||
it("restores fork state from the durable fork before message history", async () => {
|
||||
const { service } = makeService(
|
||||
[
|
||||
{
|
||||
info: {
|
||||
role: "assistant",
|
||||
providerID: "test",
|
||||
modelID: "test-model",
|
||||
variant: "default",
|
||||
mode: "build",
|
||||
},
|
||||
parts: [],
|
||||
},
|
||||
],
|
||||
{
|
||||
fork: (input) =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: `fork_${input.sessionID}`,
|
||||
agent: "plan",
|
||||
model: { providerID: "test", id: "second-model", variant: "medium" },
|
||||
},
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const forked = await Effect.runPromise(
|
||||
service.forkSession({ cwd: "/workspace", sessionId: "ses_parent", mcpServers: [] }),
|
||||
)
|
||||
|
||||
expect(select(forked, "model")?.currentValue).toBe("test/second-model")
|
||||
expect(select(forked, "effort")?.currentValue).toBe("medium")
|
||||
expect(select(forked, "mode")?.currentValue).toBe("plan")
|
||||
})
|
||||
|
||||
it("restores model variant and mode from the latest user message", async () => {
|
||||
const { service } = makeService([
|
||||
{
|
||||
@@ -772,7 +1005,7 @@ describe("ACP service sessions", () => {
|
||||
})
|
||||
|
||||
it("switches model and returns updated model and effort options", async () => {
|
||||
const { service } = makeService()
|
||||
const { service, updates } = makeService()
|
||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||
const updated = await Effect.runPromise(
|
||||
service.setSessionConfigOption({
|
||||
@@ -784,7 +1017,49 @@ describe("ACP service sessions", () => {
|
||||
|
||||
expect(select(updated, "model")?.currentValue).toBe("test/second-model")
|
||||
expect(select(updated, "effort")?.currentValue).toBe("low")
|
||||
expect(flattenSelectOptions(select(updated, "effort")).map((option) => option.value)).toEqual(["low", "medium"])
|
||||
expect(flattenSelectOptions(select(updated, "effort")).map((option) => option.value)).toEqual([
|
||||
"low",
|
||||
"medium",
|
||||
"default",
|
||||
])
|
||||
expect(updates.findLast((item) => item.update.sessionUpdate === "config_option_update")?.update).toEqual({
|
||||
sessionUpdate: "config_option_update",
|
||||
configOptions: updated.configOptions,
|
||||
})
|
||||
})
|
||||
|
||||
it("publishes updated model-dependent options for legacy model changes", async () => {
|
||||
const { service, updates } = makeService()
|
||||
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
|
||||
|
||||
await Effect.runPromise(service.setSessionModel({ sessionId: session.sessionId, modelId: "test/second-model" }))
|
||||
|
||||
const update = updates.findLast((item) => item.update.sessionUpdate === "config_option_update")?.update
|
||||
expect(update?.sessionUpdate).toBe("config_option_update")
|
||||
if (update?.sessionUpdate !== "config_option_update") throw new Error("missing config option update")
|
||||
expect(select({ configOptions: update.configOptions }, "model")?.currentValue).toBe("test/second-model")
|
||||
expect(select({ configOptions: update.configOptions }, "effort")?.currentValue).toBe("low")
|
||||
})
|
||||
|
||||
it("preserves restored effort when legacy clients synchronize the same model", async () => {
|
||||
const { service, updates } = makeService([], {
|
||||
get: () =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
id: "ses_loaded",
|
||||
agent: "build",
|
||||
model: { providerID: "test", id: "test-model", variant: "high" },
|
||||
},
|
||||
}),
|
||||
})
|
||||
await Effect.runPromise(service.resumeSession({ cwd: "/workspace", sessionId: "ses_loaded", mcpServers: [] }))
|
||||
|
||||
await Effect.runPromise(service.setSessionModel({ sessionId: "ses_loaded", modelId: "test/test-model" }))
|
||||
|
||||
const update = updates.findLast((item) => item.update.sessionUpdate === "config_option_update")?.update
|
||||
expect(update?.sessionUpdate).toBe("config_option_update")
|
||||
if (update?.sessionUpdate !== "config_option_update") throw new Error("missing config option update")
|
||||
expect(select({ configOptions: update.configOptions }, "effort")?.currentValue).toBe("high")
|
||||
})
|
||||
|
||||
it("switches effort and returns the updated effort current value", async () => {
|
||||
@@ -1360,7 +1635,12 @@ function categories(result: NewSessionResponse | LoadSessionResponse) {
|
||||
}
|
||||
|
||||
function select(
|
||||
result: SetSessionConfigOptionResponse | ResumeSessionResponse | NewSessionResponse | ForkSessionResponse,
|
||||
result:
|
||||
| SetSessionConfigOptionResponse
|
||||
| ResumeSessionResponse
|
||||
| LoadSessionResponse
|
||||
| NewSessionResponse
|
||||
| ForkSessionResponse,
|
||||
id: string,
|
||||
) {
|
||||
return result.configOptions?.find(
|
||||
|
||||
@@ -71,7 +71,7 @@ describe("opencode acp config option subprocess", () => {
|
||||
|
||||
expect(effort.category).toBe("thought_level")
|
||||
expect(effort.currentValue).toBe("low")
|
||||
expect(flattenSelectOptions(effort).map((option) => option.value)).toEqual(["low", "high"])
|
||||
expect(flattenSelectOptions(effort).map((option) => option.value)).toEqual(["low", "high", "default"])
|
||||
}),
|
||||
60_000,
|
||||
)
|
||||
|
||||
@@ -326,6 +326,41 @@ describe("plugin.codex", () => {
|
||||
)
|
||||
})
|
||||
|
||||
test.each([
|
||||
["gpt-6-astra", true],
|
||||
["gpt-6", true],
|
||||
["gpt-6.0-astra", true],
|
||||
["gpt-7", true],
|
||||
["gpt-10", true],
|
||||
["gpt-5.5-astra", true],
|
||||
["gpt-5.9", true],
|
||||
["gpt-5.10", true],
|
||||
["gpt-5.10-astra", true],
|
||||
["gpt-5.40", true],
|
||||
["gpt-5", false],
|
||||
["gpt-5.4-astra", false],
|
||||
["gpt-5.04-astra", false],
|
||||
["gpt-4.1", false],
|
||||
["gpt-4.99", false],
|
||||
["gpt-5.5-pro", false],
|
||||
["gpt-5.6", false],
|
||||
["gpt-6garbage", true],
|
||||
["gpt-6.", true],
|
||||
["gpt-6.1.2", true],
|
||||
["not-a-gpt-model", false],
|
||||
])("filters OAuth model %s by GPT major and minor versions", async (id, allowed) => {
|
||||
const hooks = await CodexAuthPlugin({} as never)
|
||||
const provider = {
|
||||
models: {
|
||||
[id]: { id, api: { id }, limit: {}, cost: {}, options: {} },
|
||||
},
|
||||
}
|
||||
|
||||
const models = await hooks.provider!.models!(provider as never, { auth: { type: "oauth" } } as never)
|
||||
|
||||
expect(Object.keys(models)).toEqual(allowed ? [id] : [])
|
||||
})
|
||||
|
||||
test("deduplicates concurrent Codex token refreshes", async () => {
|
||||
const refreshedAccess = createTestJwt({
|
||||
"https://api.openai.com/auth": { chatgpt_compute_residency: "eu" },
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { expect, test } from "bun:test"
|
||||
import type { Hooks } from "@opencode-ai/plugin"
|
||||
import { CopilotAuthPlugin } from "@/plugin/github-copilot/copilot"
|
||||
|
||||
type ChatHeaders = NonNullable<Hooks["chat.headers"]>
|
||||
|
||||
async function hook() {
|
||||
const hooks = await CopilotAuthPlugin({
|
||||
directory: "",
|
||||
project: {} as never,
|
||||
worktree: "",
|
||||
experimental_workspace: { register() {} },
|
||||
serverUrl: new URL("http://localhost"),
|
||||
$: {} as never,
|
||||
client: {
|
||||
session: {
|
||||
message: async () => ({ data: { parts: [] } }),
|
||||
get: async () => ({ data: {} }),
|
||||
},
|
||||
} as never,
|
||||
})
|
||||
return hooks["chat.headers"]!
|
||||
}
|
||||
|
||||
function input(sessionID: string, providerID: string, npm: string) {
|
||||
return {
|
||||
sessionID,
|
||||
agent: "build",
|
||||
model: { providerID, api: { npm } },
|
||||
message: { id: "msg_test", sessionID },
|
||||
} as Parameters<ChatHeaders>[0]
|
||||
}
|
||||
|
||||
test.each([
|
||||
["github-copilot", "@ai-sdk/github-copilot"],
|
||||
["github-copilot", "@ai-sdk/anthropic"],
|
||||
["github-copilot-enterprise", "@ai-sdk/github-copilot"],
|
||||
["github-copilot-enterprise", "@ai-sdk/anthropic"],
|
||||
])("uses the session ID for %s interaction headers with %s", async (providerID, npm) => {
|
||||
const headers = await hook()
|
||||
for (const sessionID of ["ses_one", "ses_one", "ses_two"]) {
|
||||
const output = { headers: { "x-existing": "preserved" } }
|
||||
await headers(input(sessionID, providerID, npm), output)
|
||||
expect(output.headers).toMatchObject({
|
||||
"X-Interaction-Id": sessionID,
|
||||
"x-existing": "preserved",
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test("does not add interaction headers to other providers", async () => {
|
||||
const headers = await hook()
|
||||
const output = { headers: { "x-existing": "preserved" } }
|
||||
await headers(input("ses_one", "openai", "@ai-sdk/openai"), output)
|
||||
expect(output.headers).toEqual({ "x-existing": "preserved" })
|
||||
})
|
||||
@@ -229,6 +229,49 @@ it.instance(
|
||||
{ config: { provider: { "amazon-bedrock": { options: { region: "us-east-1" } } } } },
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"Bedrock: preserves explicit DeepSeek model identifiers",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
yield* set("AWS_BEARER_TOKEN_BEDROCK", "test-bearer-token")
|
||||
const provider = yield* Provider.Service
|
||||
const deepseek = yield* provider.getLanguage(
|
||||
yield* provider.getModel(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("deepseek.v3.2")),
|
||||
)
|
||||
const r1 = yield* provider.getLanguage(
|
||||
yield* provider.getModel(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("deepseek.r1-v1:0")),
|
||||
)
|
||||
const profile = yield* provider.getLanguage(
|
||||
yield* provider.getModel(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("us.deepseek.r1-v1:0")),
|
||||
)
|
||||
const arn = yield* provider.getLanguage(
|
||||
yield* provider.getModel(ProviderV2.ID.amazonBedrock, ModelV2.ID.make("deepseek-v3-2-arn")),
|
||||
)
|
||||
expect((deepseek as { modelId: string }).modelId).toBe("deepseek.v3.2")
|
||||
expect((r1 as { modelId: string }).modelId).toBe("us.deepseek.r1-v1:0")
|
||||
expect((profile as { modelId: string }).modelId).toBe("us.deepseek.r1-v1:0")
|
||||
expect((arn as { modelId: string }).modelId).toBe("arn:aws:bedrock:us-east-1::foundation-model/deepseek.v3.2")
|
||||
}),
|
||||
{
|
||||
config: {
|
||||
provider: {
|
||||
"amazon-bedrock": {
|
||||
options: { region: "us-east-1" },
|
||||
models: {
|
||||
"deepseek.v3.2": { name: "DeepSeek V3.2" },
|
||||
"deepseek.r1-v1:0": { name: "DeepSeek R1" },
|
||||
"us.deepseek.r1-v1:0": { name: "DeepSeek R1 US" },
|
||||
"deepseek-v3-2-arn": {
|
||||
id: "arn:aws:bedrock:us-east-1::foundation-model/deepseek.v3.2",
|
||||
name: "DeepSeek V3.2 ARN",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
// Cross-region inference profile prefix handling.
|
||||
// Models from models.dev may come with prefixes already (e.g. us., eu., global.).
|
||||
// These should NOT be double-prefixed when passed to the SDK.
|
||||
|
||||
@@ -13,6 +13,8 @@ import { Env } from "@/env"
|
||||
import { Plugin } from "@/plugin"
|
||||
import { Provider } from "@/provider/provider"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
import { SessionRetry } from "@/session/retry"
|
||||
|
||||
afterEach(async () => {
|
||||
await disposeAllInstances()
|
||||
@@ -46,7 +48,48 @@ it.live("headerTimeout does not abort delayed SSE body after headers arrive", ()
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("chunkTimeout raises a response stream error when SSE body stalls", () =>
|
||||
for (const timeout of ["chunkTimeout", "headerTimeout"] as const) {
|
||||
it.live(`default ${timeout} is applied at fetch without changing provider options`, () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedBodyServer(250)),
|
||||
(server) => Effect.sync(() => server.server.close()),
|
||||
)
|
||||
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const configured = yield* provider.getProvider(ProviderV2.ID.make("test"))
|
||||
const signals: (AbortSignal | null | undefined)[] = []
|
||||
configured.options.fetch = (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
signals.push(init?.signal)
|
||||
return fetch(input, init)
|
||||
}
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("test"), ModelV2.ID.make("test-model"))
|
||||
const language = yield* provider.getLanguage(model)
|
||||
yield* Effect.acquireRelease(
|
||||
Effect.promise(() =>
|
||||
language.doStream({ prompt: [{ role: "user", content: [{ type: "text", text: "hello" }] }] }),
|
||||
),
|
||||
(result) => Effect.promise(() => result.stream.cancel()),
|
||||
)
|
||||
|
||||
expect(signals).toHaveLength(1)
|
||||
expect(signals[0]).toBeInstanceOf(AbortSignal)
|
||||
expect(configured.options[timeout]).toBeUndefined()
|
||||
}),
|
||||
{
|
||||
config: providerConfig(server.url, {
|
||||
[timeout === "chunkTimeout" ? "headerTimeout" : "chunkTimeout"]: false,
|
||||
}),
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
it.live("configured chunkTimeout raises a retryable response stream error when SSE body stalls", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedBodyServer(250)),
|
||||
@@ -74,12 +117,41 @@ it.live("chunkTimeout raises a response stream error when SSE body stalls", () =
|
||||
}
|
||||
})
|
||||
expect(error).toBeInstanceOf(ProviderError.ResponseStreamError)
|
||||
expect(
|
||||
SessionRetry.retryable(MessageV2.fromError(error, { providerID: model.providerID }), model.providerID),
|
||||
).toEqual({ message: "SSE read timed out" })
|
||||
}),
|
||||
{ config: providerConfig(server.url, { chunkTimeout: 50 }) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("chunkTimeout can be disabled with false", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedBodyServer(250)),
|
||||
(server) => Effect.sync(() => server.server.close()),
|
||||
)
|
||||
|
||||
yield* provideTmpdirInstance(
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const provider = yield* Provider.Service
|
||||
const configured = yield* provider.getProvider(ProviderV2.ID.make("test"))
|
||||
expect(configured.options.chunkTimeout).toBe(false)
|
||||
const model = yield* provider.getModel(ProviderV2.ID.make("test"), ModelV2.ID.make("test-model"))
|
||||
const result = streamText({
|
||||
model: yield* provider.getLanguage(model),
|
||||
messages: [{ role: "user", content: "hello" }],
|
||||
})
|
||||
|
||||
expect(yield* Effect.promise(() => result.text)).toBe("late")
|
||||
}),
|
||||
{ config: providerConfig(server.url, { chunkTimeout: false }) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("headerTimeout aborts when response headers do not arrive", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
@@ -112,7 +184,7 @@ it.live("headerTimeout aborts when response headers do not arrive", () =>
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("headerTimeout is opt-in for non-OpenAI providers", () =>
|
||||
it.live("headerTimeout can be disabled with false for non-OpenAI providers", () =>
|
||||
Effect.gen(function* () {
|
||||
const server = yield* Effect.acquireRelease(
|
||||
Effect.promise(() => delayedHeaderServer(100)),
|
||||
@@ -131,12 +203,12 @@ it.live("headerTimeout is opt-in for non-OpenAI providers", () =>
|
||||
|
||||
expect(yield* Effect.promise(() => result.text)).toBe("ok")
|
||||
}),
|
||||
{ config: providerConfig(server.url) },
|
||||
{ config: providerConfig(server.url, { headerTimeout: false }) },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("OpenAI Codex headerTimeout default can be disabled by config", () =>
|
||||
it.live("OpenAI Codex header and chunk timeout defaults can be disabled by config", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* withAuthContent(
|
||||
Effect.gen(function* () {
|
||||
@@ -146,8 +218,9 @@ it.live("OpenAI Codex headerTimeout default can be disabled by config", () =>
|
||||
const provider = yield* Provider.Service
|
||||
const openai = yield* provider.getProvider(ProviderV2.ID.openai)
|
||||
expect(openai.options.headerTimeout).toBe(false)
|
||||
expect(openai.options.chunkTimeout).toBe(false)
|
||||
}),
|
||||
{ config: { provider: { openai: { options: { headerTimeout: false } } } } },
|
||||
{ config: { provider: { openai: { options: { headerTimeout: false, chunkTimeout: false } } } } },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -3798,10 +3798,11 @@ describe("ProviderTransform sampling defaults - DeepSeek", () => {
|
||||
|
||||
describe("ProviderTransform.reasoningVariants", () => {
|
||||
const model = (reasoning_options: ModelsDev.Model["reasoning_options"]) => ({ reasoning_options }) as ModelsDev.Model
|
||||
const target = (npm: string, id = "test-model") =>
|
||||
const target = (npm: string, id = "test-model", family = "") =>
|
||||
({
|
||||
id,
|
||||
providerID: "test",
|
||||
family,
|
||||
api: { id, npm, url: "" },
|
||||
capabilities: { reasoning: true },
|
||||
limit: { output: 64_000 },
|
||||
@@ -3951,6 +3952,19 @@ describe("ProviderTransform.reasoningVariants", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("maps GitLab model efforts to provider-specific options", () => {
|
||||
const options = model([{ type: "effort", values: ["max"] }])
|
||||
const openai = target("gitlab-ai-provider", "duo-chat-gpt-5-6-sol", "gpt-sol")
|
||||
const anthropic = target("gitlab-ai-provider", "duo-chat-opus-4-8", "claude-opus")
|
||||
|
||||
expect(ProviderTransform.reasoningVariants(options, openai)).toEqual({
|
||||
max: { reasoningEffort: "max" },
|
||||
})
|
||||
expect(ProviderTransform.reasoningVariants(options, anthropic)).toEqual({
|
||||
max: { thinking: { type: "adaptive", effort: "max" } },
|
||||
})
|
||||
})
|
||||
|
||||
test("uses adaptive reasoning config for Anthropic models on Bedrock", () => {
|
||||
expect(
|
||||
ProviderTransform.reasoningVariants(
|
||||
@@ -4150,7 +4164,7 @@ describe("ProviderTransform.reasoningVariants", () => {
|
||||
expect(ProviderTransform.reasoningVariants(effort, target("@ai-sdk/github-copilot", "gemini-3-pro"))).toEqual({})
|
||||
})
|
||||
|
||||
test.each(["@ai-sdk/cohere", "@ai-sdk/perplexity", "@ai-sdk/vercel", "@ai-sdk/alibaba", "gitlab-ai-provider"])(
|
||||
test.each(["@ai-sdk/cohere", "@ai-sdk/perplexity", "@ai-sdk/vercel", "@ai-sdk/alibaba"])(
|
||||
"does not invent effort controls for %s",
|
||||
(npm) => {
|
||||
expect(ProviderTransform.reasoningVariants(model([{ type: "effort", values: ["high"] }]), target(npm))).toEqual(
|
||||
|
||||
@@ -83,6 +83,27 @@ describe("HttpApi error middleware", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("returns remote auth defects as structured client errors", () =>
|
||||
Effect.gen(function* () {
|
||||
const configError = new ConfigErrorV1.RemoteAuthError({
|
||||
url: "https://example.com",
|
||||
remote: "https://config.example.com/opencode.json",
|
||||
})
|
||||
|
||||
yield* HttpRouter.add("GET", "/remote-auth-error", Effect.die(configError)).pipe(
|
||||
Layer.provide(errorLayer),
|
||||
HttpRouter.serve,
|
||||
Layer.build,
|
||||
)
|
||||
|
||||
const response = yield* HttpClientRequest.get("/remote-auth-error").pipe(HttpClient.execute)
|
||||
const body = yield* response.json
|
||||
|
||||
expect(response.status).toBe(400)
|
||||
expect(body).toEqual(configError.toObject())
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("does not map storage not-found defects to 404", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* HttpRouter.add(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user