Compare commits

...
53 Commits
Author SHA1 Message Date
Ravitez Dondeti fd9bd448a2 docs: mention Exa and Parallel as web search backends (#38395) 2026-08-25 19:57:17 -05:00
opencode-agent[bot]andneriousy b72b50006b fix(core): recover legacy database migration history (#45061)
Co-authored-by: neriousy <34747899+neriousy@users.noreply.github.com>
2026-08-25 21:58:35 +02:00
Jack ac1c048e64 docs(go): add Grok 4.6 (#45042) 2026-08-26 01:27:48 +08:00
Dax 8615731d46 fix(console): rate limit checkout session creation (#45007) 2026-08-25 11:25:59 -04:00
OpeOginniandBrendan Allan a7444bf944 fix(ui): restore focus in stacked dialogs (#44928)
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
2026-08-25 19:05:49 +08:00
Frank 69aaa22793 zen: void invoice of cancelled subscription 2026-08-25 05:26:31 -04:00
opencode-agent[bot] 322e2b9dd5 chore: generate 2026-08-25 09:07:56 +00:00
Frank afd6f3b064 zen: display quota usage breakdown 2026-08-25 05:06:33 -04:00
Frank 1e86be2bc5 update inference headers 2026-08-25 04:22:20 -04:00
Nathan ThomassinandBrendan Allan a57230b80b fix(app): drop archived sessions from home list right away (#44905)
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
2026-08-25 07:34:24 +00:00
Frank 6bb1a76e58 update inference headers 2026-08-25 03:30:20 -04:00
Frank bdcb6be649 update inference headers 2026-08-25 03:14:21 -04:00
opencode 2f36ffe35d sync release versions for v1.18.23 2026-08-25 06:30:46 +00:00
Frank 31c409a865 update inference headers 2026-08-25 01:54:38 -04:00
Dax Raad d0ceaef6aa docs(console): prohibit abusive multi-account use 2026-08-24 23:32:44 -04:00
Charlie GleasonandClaude Opus 4.8 3ef72fe8f6 fix(provider): route non-native Cloudflare AI Gateway providers via the REST API (#44828)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-24 22:01:17 -05:00
51070b6f59 docs: clarify prompt data handling (#44854)
Co-authored-by: thdxr <826656+thdxr@users.noreply.github.com>
Co-authored-by: Dax <mail@thdxr.com>
2026-08-24 22:31:47 -04:00
Filip 18b4cb6819 docs(github): correct action token configuration (#44793) 2026-08-25 00:29:56 +02:00
Frank 0561bac189 add client header replacement 2026-08-24 17:40:33 -04:00
Filip f4019cab3e fix(github): support immutable OIDC subjects (#44776) 2026-08-24 23:23:22 +02:00
Charlie GleasonandClaude Sonnet 5 f8b4dd70ac fix(provider): send Anthropic's dashed native slug through the AI Gateway (#44281)
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 16:15:26 -05:00
opencode-agent[bot]andrekram1-node 611cc73d84 fix(opencode): send parent session header (#44752)
Co-authored-by: rekram1-node <rekram1-node@users.noreply.github.com>
2026-08-24 15:24:47 -05:00
Frank 7cde8329bc update model parser 2026-08-24 13:19:53 -04:00
Frank be15db5861 Revert "delay removing first month discount"
This reverts commit 754bb7e390.
2026-08-24 12:07:27 -04:00
opencode 55f984126c sync release versions for v1.18.22 2026-08-24 14:37:14 +00:00
opencode-agent[bot] 2a6be0a03b chore: generate 2026-08-24 12:50:53 +00:00
Dax 2a36236132 fix(opencode): normalize upgrade endpoint (#44686) 2026-08-24 12:49:33 +00:00
opencode-agent[bot]andnexxeln 105b398c2a docs(acp): update Zed custom agent config (#44658)
Co-authored-by: nexxeln <95541290+nexxeln@users.noreply.github.com>
2026-08-24 16:22:48 +05:30
opencode-agent[bot] 9fa27bd41c chore: generate 2026-08-24 10:04:12 +00:00
Jack 6bb772215b docs(go): add LongCat-2.0 (#44636) 2026-08-24 18:02:51 +08:00
Jack 03521003fa docs(go): clarify DeepSeek weekend pricing (#44637) 2026-08-24 16:21:37 +08:00
Frank 754bb7e390 delay removing first month discount 2026-08-24 04:00:55 -04:00
Frank 4371298fff fix(console): discontinue first-month Go discount (#44633) 2026-08-24 03:54:38 -04:00
opencode-agent[bot] 3f2e0e89ec chore: generate 2026-08-24 07:18:00 +00:00
Frank 4dbeeddc77 discontinue first month discount 2026-08-24 03:01:02 -04:00
Frank 4161695885 Merge branch 'dev' of github.com:anomalyco/opencode into dev 2026-08-24 02:19:03 -04:00
Frank 7bbfe425f6 always allow ox alpha in go 2026-08-24 02:18:34 -04:00
Frank f2a1d547f1 fix(console): set duplex for streamed zen requests 2026-08-24 02:13:33 -04:00
b1ce938ebe fix(console): support DeepSeek weekend pricing (#44305)
Co-authored-by: MrMushrooooom <MrMushrooooom@users.noreply.github.com>
Co-authored-by: Frank <frank@anoma.ly>
2026-08-24 01:54:31 -04:00
opencode-agent[bot] 03bba464d4 chore: generate 2026-08-23 17:15:36 +00:00
Dax 63a883a4f7 refactor(console): stream zen bodies without modifiers (#44472) 2026-08-23 13:14:18 -04:00
Dax b3bad6b581 fix(console): revert scanned zen request streaming (#44470) 2026-08-23 13:03:20 -04:00
opencode-agent[bot] ca10088bdf chore: generate 2026-08-23 16:51:36 +00:00
Dax dd3f915956 fix(console): release streamed zen prefixes (#44465) 2026-08-23 12:50:18 -04:00
opencode-agent[bot] c11c41bd86 chore: generate 2026-08-23 16:48:45 +00:00
Dax fa117558ee fix(console): scan zen model before streaming (#44463) 2026-08-23 12:47:29 -04:00
Frank 17b47301ba feat(console): add unblock workspace action 2026-08-23 12:46:02 -04:00
Dax 3d31e4bcec fix(console): revert streamed zen request bodies (#44444) 2026-08-23 11:54:07 -04:00
opencode-agent[bot] bb72277407 chore: generate 2026-08-23 15:19:54 +00:00
Dax 32c3637da0 fix(console): stream zen request bodies (#44429) 2026-08-23 11:18:38 -04:00
Dax Raad 9d466cd849 fix(stats): normalize model casing 2026-08-23 10:33:11 -04:00
opencode-agent[bot] e3bd6e0947 chore: generate 2026-08-23 14:00:45 +00:00
Dax dc13c6bb3d fix(console): reduce zen request memory (#44403) 2026-08-23 09:59:29 -04:00
197 changed files with 2424 additions and 1352 deletions
+28 -28
View File
@@ -29,7 +29,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@corvu/drawer": "catalog:",
"@dnd-kit/abstract": "0.5.0",
@@ -96,7 +96,7 @@
},
"packages/cli": {
"name": "@opencode-ai/cli",
"version": "1.18.21",
"version": "1.18.23",
"bin": {
"lildax": "./bin/lildax.cjs",
},
@@ -144,7 +144,7 @@
},
"packages/codemode": {
"name": "@opencode-ai/codemode",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"acorn": "8.15.0",
"effect": "catalog:",
@@ -158,7 +158,7 @@
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -194,7 +194,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -221,7 +221,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@ai-sdk/anthropic": "3.0.82",
"@ai-sdk/openai": "3.0.48",
@@ -243,7 +243,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -267,7 +267,7 @@
},
"packages/console/support": {
"name": "@opencode-ai/console-support",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@opencode-ai/console-core": "workspace:*",
@@ -287,7 +287,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.18.21",
"version": "1.18.23",
"bin": {
"opencode": "./bin/opencode",
},
@@ -381,7 +381,7 @@
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"drizzle-orm": "catalog:",
@@ -435,7 +435,7 @@
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
@@ -449,7 +449,7 @@
},
"packages/effect-sqlite-node": {
"name": "@opencode-ai/effect-sqlite-node",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"effect": "catalog:",
},
@@ -461,7 +461,7 @@
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@hono/standard-validator": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -493,7 +493,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -509,7 +509,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@effect/platform-node": "4.0.0-beta.83",
"@effect/platform-node-shared": "4.0.0-beta.83",
@@ -540,7 +540,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@opencode-ai/schema": "workspace:*",
"@smithy/eventstream-codec": "4.2.14",
@@ -559,7 +559,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.18.21",
"version": "1.18.23",
"bin": {
"opencode": "./bin/opencode",
},
@@ -690,7 +690,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@ai-sdk/provider": "3.0.8",
"@opencode-ai/sdk": "workspace:*",
@@ -766,7 +766,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -781,7 +781,7 @@
},
"packages/server": {
"name": "@opencode-ai/server",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/protocol": "workspace:*",
@@ -796,7 +796,7 @@
},
"packages/session-ui": {
"name": "@opencode-ai/session-ui",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/client": "file:../app/vendor/opencode-ai-client-1.17.13-v2.tgz",
@@ -836,7 +836,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -849,7 +849,7 @@
},
"packages/stats/app": {
"name": "@opencode-ai/stats-app",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@ibm/plex": "6.4.1",
"@kobalte/core": "catalog:",
@@ -883,7 +883,7 @@
},
"packages/stats/core": {
"name": "@opencode-ai/stats-core",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@aws-sdk/client-athena": "3.933.0",
"@planetscale/database": "1.19.0",
@@ -902,7 +902,7 @@
},
"packages/stats/server": {
"name": "@opencode-ai/stats-server",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@aws-sdk/client-firehose": "3.933.0",
"@effect/platform-node": "catalog:",
@@ -944,7 +944,7 @@
},
"packages/tui": {
"name": "@opencode-ai/tui",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/plugin": "workspace:*",
@@ -971,7 +971,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@kobalte/core": "catalog:",
"@pierre/diffs": "catalog:",
@@ -1022,7 +1022,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.18.21",
"version": "1.18.23",
"description": "",
"type": "module",
"exports": {
@@ -1,8 +1,10 @@
import { describe, expect, test } from "bun:test"
import type { SessionV2Info } from "@opencode-ai/sdk/v2/client"
import { QueryClient } from "@tanstack/solid-query"
import type { Session, SessionV2Info } from "@opencode-ai/sdk/v2/client"
import {
applyHomeSessionEvent,
appendHomeSessionEvent,
createHomeSessionIndexCache,
HOME_V2_SESSION_PAGE_LIMIT,
loadHomeSessionIndex,
homeSessionIndexSessions,
@@ -151,4 +153,33 @@ describe("Home V2 session index", () => {
expect(homeSessionIndexRefresh("global.disposed", true).refetch).toBe(true)
expect(homeSessionIndexRefresh("session.next.moved", true).refetch).toBe(true)
})
test("removes a session from the loaded Home index", () => {
const queryClient = new QueryClient()
const cache = createHomeSessionIndexCache(queryClient, "server")
const sessions = [
{ id: "a", time: { created: 1, updated: 1 } },
{ id: "b", time: { created: 1, updated: 1 } },
] as Session[]
queryClient.setQueryData(cache.indexKey, { sessions, eventSequence: 0 })
cache.remove("a")
const index = queryClient.getQueryData<{ sessions: Session[] }>(cache.indexKey)
expect(index?.sessions.map((item) => item.id)).toEqual(["b"])
})
test("keeps the session out of the Home list when the index is not mounted", () => {
const queryClient = new QueryClient()
const cache = createHomeSessionIndexCache(queryClient, "server")
const sessions = [
{ id: "a", time: { created: 1, updated: 1 } },
{ id: "b", time: { created: 1, updated: 1 } },
] as Session[]
cache.remove("a")
expect(queryClient.getQueryData(cache.indexKey)).toBeUndefined()
expect(cache.sessions({ sessions, eventSequence: 0 }, undefined).map((item) => item.id)).toEqual(["b"])
})
})
@@ -85,6 +85,7 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st
const indexKey = homeSessionIndexKey(server)
const eventsKey = homeSessionEventsKey(server)
let connected = false
const removed = new Set<string>()
return {
indexKey,
@@ -97,7 +98,8 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st
queryClient.setQueryData<HomeSessionEvents>(eventsKey, (current) => trimHomeSessionEvents(current, sequence))
},
sessions(index: HomeSessionIndex | undefined, events: HomeSessionEvents | undefined) {
return homeSessionIndexSessions(index, events)
const sessions = homeSessionIndexSessions(index, events)
return removed.size === 0 ? sessions : sessions.filter((session) => !removed.has(session.id))
},
apply(event: HomeSessionEvent) {
if (!queryClient.getQueryState(indexKey)) return
@@ -116,6 +118,16 @@ export function createHomeSessionIndexCache(queryClient: QueryClient, server: st
}
queryClient.setQueryData<HomeSessionEvents>(eventsKey, { sequence: next.sequence, entries: [] })
},
remove(sessionID: string) {
removed.add(sessionID)
if (!queryClient.getQueryState(indexKey)) return
queryClient.setQueryData<HomeSessionIndex>(indexKey, (index) => {
if (!index) return index
const at = index.sessions.findIndex((session) => session.id === sessionID)
if (at === -1) return index
return { ...index, sessions: index.sessions.toSpliced(at, 1) }
})
},
refresh(event: Event["type"]) {
const result = homeSessionIndexRefresh(event, connected)
connected = result.connected
@@ -219,13 +219,15 @@ export function createHomeSessionsController(home: HomeController) {
directory: session.directory,
time: { archived: Date.now() },
}),
remove: () =>
remove: () => {
setStore(
produce((draft) => {
const match = Binary.search(draft.session, session.id, (item) => item.id)
if (match.found) draft.session.splice(match.index, 1)
}),
),
)
homeSessions().remove(session.id)
},
onError: (cause) =>
showToast({
title: language.t("common.requestFailed"),
@@ -3,6 +3,7 @@ import { produce } from "solid-js/store"
import { notifySessionTabsRemoved } from "@/components/titlebar-session-events"
import { useLanguage } from "@/context/language"
import { useSDK } from "@/context/sdk"
import { useServerSync } from "@/context/server-sync"
import { useSync } from "@/context/sync"
import { useTabs } from "@/context/tabs"
import { errorMessage } from "@/pages/layout/helpers"
@@ -15,6 +16,7 @@ export function useSessionArchive() {
const navigate = useNavigate()
const sdk = useSDK()
const sync = useSync()
const serverSync = useServerSync()
const tabs = useTabs()
const { params } = useSessionKey()
@@ -56,6 +58,7 @@ export function useSessionArchive() {
}),
)
sync().session.evict(sessionID)
serverSync().homeSessions.remove(sessionID)
navigateAfterRemoval(sessionID, session.parentID, nextSession?.id)
notifySessionTabsRemoved({ directory: sdk().directory, sessionIDs: [sessionID] })
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/cli",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"bin": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/codemode",
"version": "1.18.21",
"version": "1.18.23",
"description": "Effect-native confined code execution over schema-described tools",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"scripts": {
+20 -15
View File
@@ -253,8 +253,7 @@ export const dict = {
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
"go.banner.text": "Ox Alpha Free متاح على Go لفترة محدودة",
"go.meta.description":
"يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.",
"go.meta.description": "يبلغ سعر Go ‏$10/شهر، مع حدود استخدام سخية ووصول موثوق إلى نماذج البرمجة الرائدة.",
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
"go.hero.body":
"يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.",
@@ -263,9 +262,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "اشترك في Go",
"go.cta.price": "$10/شهر",
"go.cta.promo": "$5 للشهر الأول",
"go.pricing.body":
"استخدمه مع أي وكيل. $5 للشهر الأول، ثم $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.",
"go.pricing.body": "استخدمه مع أي وكيل. $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.",
"go.graph.free": "مجاني",
"go.graph.freePill": "Big Pickle ونماذج مجانية",
"go.graph.go": "Go",
@@ -298,20 +295,20 @@ export const dict = {
"go.testimonials.frank.quote": "أتمنى لو كنت لا أزال في Nvidia.",
"go.problem.title": "ما المشكلة التي يحلها Go؟",
"go.problem.body":
"نحن نركز على تقديم تجربة OpenCode لأكبر عدد ممكن من الناس. OpenCode Go هو اشتراك منخفض التكلفة: $5 للشهر الأول، ثم $10/شهر. يوفر حدودا سخية ووصولا موثوقا إلى نماذج المصدر المفتوح الأكثر قدرة.",
"نحن نركز على تقديم تجربة OpenCode لأكبر عدد ممكن من الناس. OpenCode Go هو اشتراك منخفض التكلفة بسعر $10/شهر. يوفر حدودا سخية ووصولا موثوقا إلى نماذج المصدر المفتوح الأكثر قدرة.",
"go.problem.subtitle": " ",
"go.problem.item1": "أسعار اشتراك منخفضة التكلفة",
"go.problem.item2": "حدود سخية ووصول موثوق",
"go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين",
"go.problem.item4": "مجموعة منسقة من النماذج المختبرة للبرمجة الوكيلة",
"go.how.title": "كيف يعمل Go",
"go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
"go.how.body": "يبلغ سعر Go $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
"go.how.step1.title": "أنشئ حسابًا",
"go.how.step1.beforeLink": "اتبع",
"go.how.step1.link": "تعليمات الإعداد",
"go.how.step2.title": "اشترك في Go",
"go.how.step2.link": "$5 للشهر الأول",
"go.how.step2.afterLink": "ثم $10/شهر مع حدود سخية",
"go.how.step2.link": "$10/شهر",
"go.how.step2.afterLink": "مع حدود سخية",
"go.how.step3.title": "ابدأ البرمجة",
"go.how.step3.body": "مع وصول موثوق لنماذج مفتوحة المصدر",
"go.privacy.title": "خصوصيتك مهمة بالنسبة لنا",
@@ -327,11 +324,11 @@ export const dict = {
"go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.",
"go.faq.q3": "هل Go هو نفسه Zen؟",
"go.faq.a3":
"لا. يعتمد Zen على الدفع حسب الاستخدام، بينما يبدأ Go بسعر $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى مجموعة منسقة من النماذج.",
"لا. يعتمد Zen على الدفع حسب الاستخدام، بينما يبلغ سعر Go $10/شهر، مع حدود سخية ووصول موثوق إلى مجموعة منسقة من النماذج.",
"go.faq.q4": "كم تكلفة Go؟",
"go.faq.a4.p1.beforePricing": "تكلفة Go",
"go.faq.a4.p1.pricingLink": "$5 للشهر الأول",
"go.faq.a4.p1.afterPricing": "ثم $10/شهر مع حدود سخية.",
"go.faq.a4.p1.pricingLink": "$10/شهر",
"go.faq.a4.p1.afterPricing": "مع حدود سخية.",
"go.faq.a4.p2.beforeAccount": "يمكنك إدارة اشتراكك في",
"go.faq.a4.p2.accountLink": "حسابك",
"go.faq.a4.p3": "ألغِ في أي وقت.",
@@ -664,10 +661,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "بضع ثوان",
"workspace.lite.subscription.message": "أنت مشترك في OpenCode Go.",
"workspace.lite.subscription.manage": "إدارة الاشتراك",
"workspace.lite.subscription.rollingUsage": "الاستخدام المتجدد",
"workspace.lite.subscription.rollingUsage": "الاستخدام خلال 5 ساعات",
"workspace.lite.subscription.rollingQuota": "الحصة خلال 5 ساعات",
"workspace.lite.subscription.weeklyUsage": "الاستخدام الأسبوعي",
"workspace.lite.subscription.weeklyQuota": "الحصة الأسبوعية",
"workspace.lite.subscription.monthlyUsage": "الاستخدام الشهري",
"workspace.lite.subscription.monthlyQuota": "الحصة الشهرية",
"workspace.lite.subscription.resetsIn": "إعادة تعيين في",
"workspace.lite.subscription.showDetails": "إظهار التفاصيل",
"workspace.lite.subscription.hideDetails": "إخفاء التفاصيل",
"workspace.lite.subscription.model": "النموذج",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "الإجمالي",
"workspace.lite.subscription.useBalance": "استخدم رصيدك المتوفر بعد الوصول إلى حدود الاستخدام",
"workspace.lite.subscription.selectProvider":
'اختر "OpenCode Go" كمزود في إعدادات opencode الخاصة بك لاستخدام نماذج Go.',
@@ -680,8 +685,8 @@ export const dict = {
"workspace.lite.other.message":
"عضو آخر في مساحة العمل هذه مشترك بالفعل في OpenCode Go. يمكن لعضو واحد فقط لكل مساحة عمل الاشتراك.",
"workspace.lite.promo.description":
"يبدأ OpenCode Go بسعر {{price}}، ثم $10/شهر، ويوفر وصولا موثوقا لنماذج البرمجة المفتوحة الشهيرة مع حدود استخدام سخية.",
"workspace.lite.promo.price": "$5 للشهر الأول",
"يبلغ سعر OpenCode Go {{price}}، ويوفر وصولا موثوقا لنماذج البرمجة المفتوحة الشهيرة مع حدود استخدام سخية.",
"workspace.lite.promo.price": "$10/شهر",
"workspace.lite.promo.modelsTitle": "ما يتضمنه",
"workspace.lite.promo.footer":
"صُممت الخطة بشكل أساسي للمستخدمين الدوليين، وتوفر وصولًا عالميًا مستقرًا. قد تتغير الأسعار وحدود الاستخدام بينما نتعلم من الاستخدام المبكر والملاحظات.",
+20 -14
View File
@@ -258,7 +258,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
"go.banner.text": "Ox Alpha Free está disponível no Go por tempo limitado",
"go.meta.description":
"O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de uso e acesso confiável aos principais modelos de codificação.",
"O Go custa $10/mês, com limites generosos de uso e acesso confiável aos principais modelos de codificação.",
"go.hero.title": "Modelos de codificação de baixo custo para todos",
"go.hero.body":
"O Go traz a codificação com agentes para programadores em todo o mundo. Oferecendo limites generosos e acesso confiável aos modelos de código aberto mais capazes, para que você possa construir com agentes poderosos sem se preocupar com custos ou disponibilidade.",
@@ -267,9 +267,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Assinar o Go",
"go.cta.price": "$10/mês",
"go.cta.promo": "$5 no primeiro mês",
"go.pricing.body":
"Use com qualquer agente. $5 no primeiro mês, depois $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.",
"Use com qualquer agente. $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.",
"go.graph.free": "Grátis",
"go.graph.freePill": "Big Pickle e modelos gratuitos",
"go.graph.go": "Go",
@@ -303,21 +302,20 @@ export const dict = {
"go.testimonials.frank.quote": "Eu queria ainda estar na Nvidia.",
"go.problem.title": "Que problema o Go resolve?",
"go.problem.body":
"Estamos focados em levar a experiência do OpenCode para o maior número de pessoas possível. OpenCode Go é uma assinatura de baixo custo: $5 no primeiro mês, depois $10/mês. Oferece limites generosos e acesso confiável aos modelos open source mais capazes.",
"Estamos focados em levar a experiência do OpenCode para o maior número de pessoas possível. OpenCode Go é uma assinatura de baixo custo de $10/mês. Oferece limites generosos e acesso confiável aos modelos open source mais capazes.",
"go.problem.subtitle": " ",
"go.problem.item1": "Preço de assinatura de baixo custo",
"go.problem.item2": "Limites generosos e acesso confiável",
"go.problem.item3": "Feito para o maior número possível de programadores",
"go.problem.item4": "Uma seleção de modelos testados para codificação com agentes",
"go.how.title": "Como o Go funciona",
"go.how.body":
"O Go começa em $5 no primeiro mês, depois $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.",
"go.how.body": "O Go custa $10/mês. Você pode usá-lo com o OpenCode ou qualquer agente.",
"go.how.step1.title": "Crie uma conta",
"go.how.step1.beforeLink": "siga as",
"go.how.step1.link": "instruções de configuração",
"go.how.step2.title": "Assinar o Go",
"go.how.step2.link": "$5 no primeiro mês",
"go.how.step2.afterLink": "depois $10/mês com limites generosos",
"go.how.step2.link": "$10/mês",
"go.how.step2.afterLink": "com limites generosos",
"go.how.step3.title": "Comece a codificar",
"go.how.step3.body": "com acesso confiável a modelos de código aberto",
"go.privacy.title": "Sua privacidade é importante para nós",
@@ -334,11 +332,11 @@ export const dict = {
"go.faq.a2": "O Go inclui os modelos listados abaixo, com limites generosos e acesso confiável.",
"go.faq.q3": "O Go é o mesmo que o Zen?",
"go.faq.a3":
"Não. Zen é pay-as-you-go, enquanto o Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos e acesso confiável a uma seleção de modelos.",
"Não. Zen é pay-as-you-go, enquanto o Go custa $10/mês, com limites generosos e acesso confiável a uma seleção de modelos.",
"go.faq.q4": "Quanto custa o Go?",
"go.faq.a4.p1.beforePricing": "O Go custa",
"go.faq.a4.p1.pricingLink": "$5 no primeiro mês",
"go.faq.a4.p1.afterPricing": "depois $10/mês com limites generosos.",
"go.faq.a4.p1.pricingLink": "$10/mês",
"go.faq.a4.p1.afterPricing": "com limites generosos.",
"go.faq.a4.p2.beforeAccount": "Você pode gerenciar sua assinatura em sua",
"go.faq.a4.p2.accountLink": "conta",
"go.faq.a4.p3": "Cancele a qualquer momento.",
@@ -675,10 +673,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "alguns segundos",
"workspace.lite.subscription.message": "Você assina o OpenCode Go.",
"workspace.lite.subscription.manage": "Gerenciar Assinatura",
"workspace.lite.subscription.rollingUsage": "Uso Contínuo",
"workspace.lite.subscription.rollingUsage": "Uso de 5 horas",
"workspace.lite.subscription.rollingQuota": "Cota de 5 horas",
"workspace.lite.subscription.weeklyUsage": "Uso Semanal",
"workspace.lite.subscription.weeklyQuota": "Cota Semanal",
"workspace.lite.subscription.monthlyUsage": "Uso Mensal",
"workspace.lite.subscription.monthlyQuota": "Cota Mensal",
"workspace.lite.subscription.resetsIn": "Reinicia em",
"workspace.lite.subscription.showDetails": "Mostrar detalhes",
"workspace.lite.subscription.hideDetails": "Ocultar detalhes",
"workspace.lite.subscription.model": "Modelo",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Total",
"workspace.lite.subscription.useBalance": "Use seu saldo disponível após atingir os limites de uso",
"workspace.lite.subscription.selectProvider":
'Selecione "OpenCode Go" como provedor na sua configuração do opencode para usar os modelos Go.',
@@ -691,8 +697,8 @@ export const dict = {
"workspace.lite.other.message":
"Outro membro neste workspace já assina o OpenCode Go. Apenas um membro por workspace pode assinar.",
"workspace.lite.promo.description":
"O OpenCode Go começa em {{price}}, depois $10/mês, e oferece acesso confiável a modelos de codificação abertos populares com limites de uso generosos.",
"workspace.lite.promo.price": "$5 no primeiro mês",
"O OpenCode Go custa {{price}} e oferece acesso confiável a modelos de codificação abertos populares com limites de uso generosos.",
"workspace.lite.promo.price": "$10/mês",
"workspace.lite.promo.modelsTitle": "O que está incluído",
"workspace.lite.promo.footer":
"O plano foi desenvolvido principalmente para usuários internacionais e oferece acesso global estável. Os preços e limites de uso podem mudar à medida que aprendemos com o uso inicial e o feedback recebido.",
+20 -15
View File
@@ -256,7 +256,7 @@ export const dict = {
"go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
"go.banner.text": "Ox Alpha Free er tilgængelig på Go i en begrænset periode",
"go.meta.description":
"Go starter ved $5 for den første måned, derefter $10/måned, med generøse brugsgrænser og pålidelig adgang til førende kodningsmodeller.",
"Go koster $10/måned, med generøse brugsgrænser og pålidelig adgang til førende kodningsmodeller.",
"go.hero.title": "Kodningsmodeller til lav pris for alle",
"go.hero.body":
"Go bringer agentisk kodning til programmører over hele verden. Med generøse grænser og pålidelig adgang til de mest kapable open source-modeller, så du kan bygge med kraftfulde agenter uden at bekymre dig om omkostninger eller tilgængelighed.",
@@ -265,9 +265,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Abonner på Go",
"go.cta.price": "$10/måned",
"go.cta.promo": "$5 første måned",
"go.pricing.body":
"Brug med enhver agent. $5 første måned, derefter $10/måned. Tank op med kredit efter behov. Afmeld når som helst.",
"go.pricing.body": "Brug med enhver agent. $10/måned. Tank op med kredit efter behov. Afmeld når som helst.",
"go.graph.free": "Gratis",
"go.graph.freePill": "Big Pickle og gratis modeller",
"go.graph.go": "Go",
@@ -300,21 +298,20 @@ export const dict = {
"go.testimonials.frank.quote": "Jeg ville ønske, jeg stadig var hos Nvidia.",
"go.problem.title": "Hvilket problem løser Go?",
"go.problem.body":
"Vi fokuserer på at bringe OpenCode-oplevelsen ud til så mange som muligt. OpenCode Go er et lavprisabonnement: $5 for den første måned, derefter $10/måned. Det giver generøse grænser og pålidelig adgang til de mest kapable open source-modeller.",
"Vi fokuserer på at bringe OpenCode-oplevelsen ud til så mange som muligt. OpenCode Go er et lavprisabonnement til $10/måned. Det giver generøse grænser og pålidelig adgang til de mest kapable open source-modeller.",
"go.problem.subtitle": " ",
"go.problem.item1": "Lavpris abonnementspriser",
"go.problem.item2": "Generøse grænser og pålidelig adgang",
"go.problem.item3": "Bygget til så mange programmører som muligt",
"go.problem.item4": "Et kurateret modeludvalg testet til agentisk kodning",
"go.how.title": "Hvordan Go virker",
"go.how.body":
"Go starter ved $5 for den første måned, derefter $10/måned. Du kan bruge det med OpenCode eller enhver agent.",
"go.how.body": "Go koster $10/måned. Du kan bruge det med OpenCode eller enhver agent.",
"go.how.step1.title": "Opret en konto",
"go.how.step1.beforeLink": "følg",
"go.how.step1.link": "opsætningsinstruktionerne",
"go.how.step2.title": "Abonner på Go",
"go.how.step2.link": "$5 første måned",
"go.how.step2.afterLink": "derefter $10/måned med generøse grænser",
"go.how.step2.link": "$10/måned",
"go.how.step2.afterLink": "med generøse grænser",
"go.how.step3.title": "Start kodning",
"go.how.step3.body": "med pålidelig adgang til open source-modeller",
"go.privacy.title": "Dit privatliv er vigtigt for os",
@@ -331,11 +328,11 @@ export const dict = {
"go.faq.a2": "Go inkluderer modellerne nedenfor med generøse grænser og pålidelig adgang.",
"go.faq.q3": "Er Go det samme som Zen?",
"go.faq.a3":
"Nej. Zen er pay-as-you-go, mens Go starter ved $5 for den første måned, derefter $10/måned, med generøse grænser og pålidelig adgang til et kurateret modeludvalg.",
"Nej. Zen er pay-as-you-go, mens Go koster $10/måned, med generøse grænser og pålidelig adgang til et kurateret modeludvalg.",
"go.faq.q4": "Hvad koster Go?",
"go.faq.a4.p1.beforePricing": "Go koster",
"go.faq.a4.p1.pricingLink": "$5 første måned",
"go.faq.a4.p1.afterPricing": "derefter $10/måned med generøse grænser.",
"go.faq.a4.p1.pricingLink": "$10/måned",
"go.faq.a4.p1.afterPricing": "med generøse grænser.",
"go.faq.a4.p2.beforeAccount": "Du kan administrere dit abonnement i din",
"go.faq.a4.p2.accountLink": "konto",
"go.faq.a4.p3": "Annuller til enhver tid.",
@@ -672,10 +669,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "et par sekunder",
"workspace.lite.subscription.message": "Du abonnerer på OpenCode Go.",
"workspace.lite.subscription.manage": "Administrer abonnement",
"workspace.lite.subscription.rollingUsage": "Løbende forbrug",
"workspace.lite.subscription.rollingUsage": "5-timers forbrug",
"workspace.lite.subscription.rollingQuota": "5-timers kvote",
"workspace.lite.subscription.weeklyUsage": "Ugentligt forbrug",
"workspace.lite.subscription.weeklyQuota": "Ugentlig kvote",
"workspace.lite.subscription.monthlyUsage": "Månedligt forbrug",
"workspace.lite.subscription.monthlyQuota": "Månedlig kvote",
"workspace.lite.subscription.resetsIn": "Nulstiller i",
"workspace.lite.subscription.showDetails": "Vis detaljer",
"workspace.lite.subscription.hideDetails": "Skjul detaljer",
"workspace.lite.subscription.model": "Model",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "I alt",
"workspace.lite.subscription.useBalance": "Brug din tilgængelige saldo, når du har nået forbrugsgrænserne",
"workspace.lite.subscription.selectProvider":
'Vælg "OpenCode Go" som udbyder i din opencode-konfiguration for at bruge Go-modeller.',
@@ -688,8 +693,8 @@ export const dict = {
"workspace.lite.other.message":
"Et andet medlem i dette workspace abonnerer allerede på OpenCode Go. Kun ét medlem pr. workspace kan abonnere.",
"workspace.lite.promo.description":
"OpenCode Go starter ved {{price}}, derefter $10/måned, og giver pålidelig adgang til populære åbne kodningsmodeller med generøse brugsgrænser.",
"workspace.lite.promo.price": "$5 for den første måned",
"OpenCode Go koster {{price}} og giver pålidelig adgang til populære åbne kodningsmodeller med generøse brugsgrænser.",
"workspace.lite.promo.price": "$10/måned",
"workspace.lite.promo.modelsTitle": "Hvad er inkluderet",
"workspace.lite.promo.footer":
"Planen er primært udviklet til internationale brugere og giver stabil adgang i hele verden. Priser og forbrugsgrænser kan ændre sig, efterhånden som vi lærer af de første brugserfaringer og tilbagemeldinger.",
+20 -15
View File
@@ -258,7 +258,7 @@ export const dict = {
"go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
"go.banner.text": "Ox Alpha Free ist für begrenzte Zeit auf Go verfügbar",
"go.meta.description":
"Go beginnt bei $5 für deinen ersten Monat, danach $10/Monat, mit großzügigen Nutzungslimits und zuverlässigem Zugang zu führenden Coding-Modellen.",
"Go kostet $10/Monat, mit großzügigen Nutzungslimits und zuverlässigem Zugang zu führenden Coding-Modellen.",
"go.hero.title": "Kostengünstige Coding-Modelle für alle",
"go.hero.body":
"Go bringt Agentic Coding zu Programmierern auf der ganzen Welt. Mit großzügigen Limits und zuverlässigem Zugang zu den leistungsfähigsten Open-Source-Modellen, damit du mit leistungsstarken Agenten entwickeln kannst, ohne dir Gedanken über Kosten oder Verfügbarkeit zu machen.",
@@ -267,9 +267,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Go abonnieren",
"go.cta.price": "$10/Monat",
"go.cta.promo": "$5 im ersten Monat",
"go.pricing.body":
"Mit jedem Agenten nutzbar. $5 im ersten Monat, danach $10/Monat. Guthaben bei Bedarf aufladen. Jederzeit kündbar.",
"go.pricing.body": "Mit jedem Agenten nutzbar. $10/Monat. Guthaben bei Bedarf aufladen. Jederzeit kündbar.",
"go.graph.free": "Kostenlos",
"go.graph.freePill": "Big Pickle und kostenlose Modelle",
"go.graph.go": "Go",
@@ -302,21 +300,20 @@ export const dict = {
"go.testimonials.frank.quote": "Ich wünschte, ich wäre noch bei Nvidia.",
"go.problem.title": "Welches Problem löst Go?",
"go.problem.body":
"Wir konzentrieren uns darauf, die OpenCode-Erfahrung so vielen Menschen wie möglich zugänglich zu machen. OpenCode Go ist ein kostengünstiges Abonnement: $5 im ersten Monat, danach $10/Monat. Es bietet großzügige Limits und zuverlässigen Zugang zu den leistungsfähigsten Open-Source-Modellen.",
"Wir konzentrieren uns darauf, die OpenCode-Erfahrung so vielen Menschen wie möglich zugänglich zu machen. OpenCode Go ist ein kostengünstiges Abonnement für $10/Monat. Es bietet großzügige Limits und zuverlässigen Zugang zu den leistungsfähigsten Open-Source-Modellen.",
"go.problem.subtitle": " ",
"go.problem.item1": "Kostengünstiges Abonnement",
"go.problem.item2": "Großzügige Limits und zuverlässiger Zugang",
"go.problem.item3": "Für so viele Programmierer wie möglich gebaut",
"go.problem.item4": "Eine kuratierte, für Agentic Coding getestete Modellauswahl",
"go.how.title": "Wie Go funktioniert",
"go.how.body":
"Go beginnt bei $5 für den ersten Monat, danach $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.",
"go.how.body": "Go kostet $10/Monat. Du kannst es mit OpenCode oder jedem Agenten nutzen.",
"go.how.step1.title": "Konto erstellen",
"go.how.step1.beforeLink": "folge den",
"go.how.step1.link": "Einrichtungsanweisungen",
"go.how.step2.title": "Go abonnieren",
"go.how.step2.link": "$5 im ersten Monat",
"go.how.step2.afterLink": "danach $10/Monat mit großzügigen Limits",
"go.how.step2.link": "$10/Monat",
"go.how.step2.afterLink": "mit großzügigen Limits",
"go.how.step3.title": "Loslegen mit Coding",
"go.how.step3.body": "mit zuverlässigem Zugang zu Open-Source-Modellen",
"go.privacy.title": "Deine Privatsphäre ist uns wichtig",
@@ -333,11 +330,11 @@ export const dict = {
"go.faq.a2": "Go umfasst die unten aufgeführten Modelle mit großzügigen Limits und zuverlässigem Zugriff.",
"go.faq.q3": "Ist Go dasselbe wie Zen?",
"go.faq.a3":
"Nein. Zen ist Pay-as-you-go, während Go bei $5 für deinen ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu einer kuratierten Modellauswahl.",
"Nein. Zen ist Pay-as-you-go, während Go $10/Monat kostet, mit großzügigen Limits und zuverlässigem Zugang zu einer kuratierten Modellauswahl.",
"go.faq.q4": "Wie viel kostet Go?",
"go.faq.a4.p1.beforePricing": "Go kostet",
"go.faq.a4.p1.pricingLink": "$5 im ersten Monat",
"go.faq.a4.p1.afterPricing": "danach $10/Monat mit großzügigen Limits.",
"go.faq.a4.p1.pricingLink": "$10/Monat",
"go.faq.a4.p1.afterPricing": "mit großzügigen Limits.",
"go.faq.a4.p2.beforeAccount": "Du kannst dein Abonnement in deinem",
"go.faq.a4.p2.accountLink": "Konto verwalten",
"go.faq.a4.p3": "Jederzeit kündbar.",
@@ -674,10 +671,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "einige Sekunden",
"workspace.lite.subscription.message": "Du hast OpenCode Go abonniert.",
"workspace.lite.subscription.manage": "Abo verwalten",
"workspace.lite.subscription.rollingUsage": "Fortlaufende Nutzung",
"workspace.lite.subscription.rollingUsage": "5-Stunden-Nutzung",
"workspace.lite.subscription.rollingQuota": "5-Stunden-Kontingent",
"workspace.lite.subscription.weeklyUsage": "Wöchentliche Nutzung",
"workspace.lite.subscription.weeklyQuota": "Wöchentliches Kontingent",
"workspace.lite.subscription.monthlyUsage": "Monatliche Nutzung",
"workspace.lite.subscription.monthlyQuota": "Monatliches Kontingent",
"workspace.lite.subscription.resetsIn": "Setzt zurück in",
"workspace.lite.subscription.showDetails": "Details anzeigen",
"workspace.lite.subscription.hideDetails": "Details ausblenden",
"workspace.lite.subscription.model": "Modell",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Gesamt",
"workspace.lite.subscription.useBalance": "Nutze dein verfügbares Guthaben, nachdem die Nutzungslimits erreicht sind",
"workspace.lite.subscription.selectProvider":
'Wähle "OpenCode Go" als Anbieter in deiner opencode-Konfiguration, um Go-Modelle zu verwenden.',
@@ -690,8 +695,8 @@ export const dict = {
"workspace.lite.other.message":
"Ein anderes Mitglied in diesem Workspace hat OpenCode Go bereits abonniert. Nur ein Mitglied pro Workspace kann abonnieren.",
"workspace.lite.promo.description":
"OpenCode Go startet bei {{price}}, danach $10/Monat, und bietet zuverlässigen Zugang zu beliebten offenen Coding-Modellen mit großzügigen Nutzungslimits.",
"workspace.lite.promo.price": "$5 im ersten Monat",
"OpenCode Go kostet {{price}} und bietet zuverlässigen Zugang zu beliebten offenen Coding-Modellen mit großzügigen Nutzungslimits.",
"workspace.lite.promo.price": "$10/Monat",
"workspace.lite.promo.modelsTitle": "Was enthalten ist",
"workspace.lite.promo.footer":
"Der Plan richtet sich in erster Linie an internationale Nutzer und bietet stabilen weltweiten Zugriff. Preise und Nutzungslimits können sich ändern, wenn wir Erkenntnisse aus der ersten Nutzung und dem Feedback gewinnen.",
+20 -14
View File
@@ -254,8 +254,7 @@ export const dict = {
"go.title": "OpenCode Go | Low cost coding models for everyone",
"go.banner.text": "Ox Alpha Free is available on Go for a limited time",
"go.meta.description":
"Go starts at $5 for your first month, then $10/month, with generous usage limits and reliable access to leading coding models.",
"go.meta.description": "Go costs $10/month, with generous usage limits and reliable access to leading coding models.",
"go.hero.title": "Low cost coding models for everyone",
"go.hero.body":
"Go brings agentic coding to programmers around the world. Offering generous limits and reliable access to the most capable open-source models, so you can build with powerful agents without worrying about cost or availability.",
@@ -264,8 +263,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Subscribe to Go",
"go.cta.price": "$10/month",
"go.cta.promo": "$5 first month",
"go.pricing.body": "Use with any agent. $5 first month, then $10/month. Top up credit if needed. Cancel any time.",
"go.pricing.body": "Use with any agent. $10/month. Top up credit if needed. Cancel any time.",
"go.graph.free": "Free",
"go.graph.freePill": "Big Pickle and free models",
"go.graph.go": "Go",
@@ -299,20 +297,20 @@ export const dict = {
"go.testimonials.frank.quote": "I wish I was still at Nvidia.",
"go.problem.title": "What problem is Go solving?",
"go.problem.body":
"We're focused on bringing the OpenCode experience to as many people as possible. OpenCode Go is a low cost subscription: $5 for your first month, then $10/month. It provides generous limits and reliable access to the most capable open source models.",
"We're focused on bringing the OpenCode experience to as many people as possible. OpenCode Go is a low cost $10/month subscription. It provides generous limits and reliable access to the most capable open source models.",
"go.problem.subtitle": " ",
"go.problem.item1": "Low cost subscription pricing",
"go.problem.item2": "Generous limits and reliable access",
"go.problem.item3": "Built for as many programmers as possible",
"go.problem.item4": "A curated model lineup tested for agentic coding",
"go.how.title": "How Go works",
"go.how.body": "Go starts at $5 for your first month, then $10/month. You can use it with OpenCode or any agent.",
"go.how.body": "Go costs $10/month. You can use it with OpenCode or any agent.",
"go.how.step1.title": "Create an account",
"go.how.step1.beforeLink": "follow the",
"go.how.step1.link": "setup instructions",
"go.how.step2.title": "Subscribe to Go",
"go.how.step2.link": "$5 first month",
"go.how.step2.afterLink": "then $10/month with generous limits",
"go.how.step2.link": "$10/month",
"go.how.step2.afterLink": "with generous limits",
"go.how.step3.title": "Start coding",
"go.how.step3.body": "with reliable access to open-source models",
"go.privacy.title": "Your privacy is important to us",
@@ -329,11 +327,11 @@ export const dict = {
"go.faq.a2": "Go includes the models listed below, with generous limits and reliable access.",
"go.faq.q3": "Is Go the same as Zen?",
"go.faq.a3":
"No. Zen is pay-as-you-go, while Go starts at $5 for your first month, then $10/month, with generous limits and reliable access to a curated model lineup.",
"No. Zen is pay-as-you-go, while Go costs $10/month, with generous limits and reliable access to a curated model lineup.",
"go.faq.q4": "How much does Go cost?",
"go.faq.a4.p1.beforePricing": "Go costs",
"go.faq.a4.p1.pricingLink": "$5 first month",
"go.faq.a4.p1.afterPricing": "then $10/month with generous limits.",
"go.faq.a4.p1.pricingLink": "$10/month",
"go.faq.a4.p1.afterPricing": "with generous limits.",
"go.faq.a4.p2.beforeAccount": "You can manage your subscription in your",
"go.faq.a4.p2.accountLink": "account",
"go.faq.a4.p3": "Cancel any time.",
@@ -671,10 +669,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "a few seconds",
"workspace.lite.subscription.message": "You are subscribed to OpenCode Go.",
"workspace.lite.subscription.manage": "Manage Subscription",
"workspace.lite.subscription.rollingUsage": "Rolling Usage",
"workspace.lite.subscription.rollingUsage": "5-hour Usage",
"workspace.lite.subscription.rollingQuota": "5-hour Quota",
"workspace.lite.subscription.weeklyUsage": "Weekly Usage",
"workspace.lite.subscription.weeklyQuota": "Weekly Quota",
"workspace.lite.subscription.monthlyUsage": "Monthly Usage",
"workspace.lite.subscription.monthlyQuota": "Monthly Quota",
"workspace.lite.subscription.resetsIn": "Resets in",
"workspace.lite.subscription.showDetails": "Show details",
"workspace.lite.subscription.hideDetails": "Hide details",
"workspace.lite.subscription.model": "Model",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Total",
"workspace.lite.subscription.useBalance": "Use your available balance after reaching the usage limits",
"workspace.lite.subscription.selectProvider":
'Select "OpenCode Go" as the provider in your opencode configuration to use Go models.',
@@ -687,8 +693,8 @@ export const dict = {
"workspace.lite.other.message":
"Another member in this workspace is already subscribed to OpenCode Go. Only one member per workspace can subscribe.",
"workspace.lite.promo.description":
"OpenCode Go starts at {{price}}, then $10/month, and provides reliable access to popular open coding models with generous usage limits.",
"workspace.lite.promo.price": "$5 for your first month",
"OpenCode Go costs {{price}} and provides reliable access to popular open coding models with generous usage limits.",
"workspace.lite.promo.price": "$10/month",
"workspace.lite.promo.modelsTitle": "What's Included",
"workspace.lite.promo.footer":
"The plan is designed primarily for international users and provides stable global access. Pricing and usage limits may change as we learn from early usage and feedback.",
+20 -13
View File
@@ -259,7 +259,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
"go.banner.text": "Ox Alpha Free está disponible en Go por tiempo limitado",
"go.meta.description":
"Go comienza en $5 el primer mes, luego 10 $/mes, con límites de uso generosos y acceso fiable a modelos de programación líderes.",
"Go cuesta 10 $/mes, con límites de uso generosos y acceso fiable a modelos de programación líderes.",
"go.hero.title": "Modelos de programación de bajo coste para todos",
"go.hero.body":
"Go lleva la programación agéntica a programadores de todo el mundo. Ofrece límites generosos y acceso fiable a los modelos de código abierto más capaces, para que puedas crear con agentes potentes sin preocuparte por el coste o la disponibilidad.",
@@ -268,9 +268,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Suscribirse a Go",
"go.cta.price": "10 $/mes",
"go.cta.promo": "$5 el primer mes",
"go.pricing.body":
"Úsalo con cualquier agente. $5 el primer mes, luego 10 $/mes. Recarga crédito si es necesario. Cancela en cualquier momento.",
"Úsalo con cualquier agente. 10 $/mes. Recarga crédito si es necesario. Cancela en cualquier momento.",
"go.graph.free": "Gratis",
"go.graph.freePill": "Big Pickle y modelos gratuitos",
"go.graph.go": "Go",
@@ -304,20 +303,20 @@ export const dict = {
"go.testimonials.frank.quote": "Ojalá siguiera en Nvidia.",
"go.problem.title": "¿Qué problema resuelve Go?",
"go.problem.body":
"Nos enfocamos en llevar la experiencia de OpenCode a tantas personas como sea posible. OpenCode Go es una suscripción de bajo coste: $5 el primer mes, luego 10 $/mes. Proporciona límites generosos y acceso fiable a los modelos de código abierto más capaces.",
"Nos enfocamos en llevar la experiencia de OpenCode a tantas personas como sea posible. OpenCode Go es una suscripción de bajo coste de 10 $/mes. Proporciona límites generosos y acceso fiable a los modelos de código abierto más capaces.",
"go.problem.subtitle": " ",
"go.problem.item1": "Precios de suscripción de bajo coste",
"go.problem.item2": "Límites generosos y acceso fiable",
"go.problem.item3": "Creado para tantos programadores como sea posible",
"go.problem.item4": "Una selección de modelos probados para programación agéntica",
"go.how.title": "Cómo funciona Go",
"go.how.body": "Go comienza en $5 el primer mes, luego 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.",
"go.how.body": "Go cuesta 10 $/mes. Puedes usarlo con OpenCode o cualquier agente.",
"go.how.step1.title": "Crear una cuenta",
"go.how.step1.beforeLink": "sigue las",
"go.how.step1.link": "instrucciones de configuración",
"go.how.step2.title": "Suscribirse a Go",
"go.how.step2.link": "$5 el primer mes",
"go.how.step2.afterLink": "luego 10 $/mes con límites generosos",
"go.how.step2.link": "10 $/mes",
"go.how.step2.afterLink": "con límites generosos",
"go.how.step3.title": "Empezar a programar",
"go.how.step3.body": "con acceso fiable a modelos de código abierto",
"go.privacy.title": "Tu privacidad es importante para nosotros",
@@ -334,11 +333,11 @@ export const dict = {
"go.faq.a2": "Go incluye los modelos que se indican abajo, con límites generosos y acceso confiable.",
"go.faq.q3": "¿Es Go lo mismo que Zen?",
"go.faq.a3":
"No. Zen es de pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a una selección de modelos.",
"No. Zen es de pago por uso, mientras que Go cuesta 10 $/mes, con límites generosos y acceso fiable a una selección de modelos.",
"go.faq.q4": "¿Cuánto cuesta Go?",
"go.faq.a4.p1.beforePricing": "Go cuesta",
"go.faq.a4.p1.pricingLink": "$5 el primer mes",
"go.faq.a4.p1.afterPricing": "luego 10 $/mes con límites generosos.",
"go.faq.a4.p1.pricingLink": "10 $/mes",
"go.faq.a4.p1.afterPricing": "con límites generosos.",
"go.faq.a4.p2.beforeAccount": "Puedes gestionar tu suscripción en tu",
"go.faq.a4.p2.accountLink": "cuenta",
"go.faq.a4.p3": "Cancela en cualquier momento.",
@@ -675,10 +674,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "unos pocos segundos",
"workspace.lite.subscription.message": "Estás suscrito a OpenCode Go.",
"workspace.lite.subscription.manage": "Gestionar Suscripción",
"workspace.lite.subscription.rollingUsage": "Uso Continuo",
"workspace.lite.subscription.rollingUsage": "Uso de 5 horas",
"workspace.lite.subscription.rollingQuota": "Cuota de 5 horas",
"workspace.lite.subscription.weeklyUsage": "Uso Semanal",
"workspace.lite.subscription.weeklyQuota": "Cuota Semanal",
"workspace.lite.subscription.monthlyUsage": "Uso Mensual",
"workspace.lite.subscription.monthlyQuota": "Cuota Mensual",
"workspace.lite.subscription.resetsIn": "Se reinicia en",
"workspace.lite.subscription.showDetails": "Mostrar detalles",
"workspace.lite.subscription.hideDetails": "Ocultar detalles",
"workspace.lite.subscription.model": "Modelo",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Total",
"workspace.lite.subscription.useBalance": "Usa tu saldo disponible después de alcanzar los límites de uso",
"workspace.lite.subscription.selectProvider":
'Selecciona "OpenCode Go" como proveedor en tu configuración de opencode para usar los modelos Go.',
@@ -691,8 +698,8 @@ export const dict = {
"workspace.lite.other.message":
"Otro miembro de este espacio de trabajo ya está suscrito a OpenCode Go. Solo un miembro por espacio de trabajo puede suscribirse.",
"workspace.lite.promo.description":
"OpenCode Go comienza en {{price}}, luego $10/mes, y ofrece acceso confiable a modelos de codificación abiertos populares con límites de uso generosos.",
"workspace.lite.promo.price": "$5 el primer mes",
"OpenCode Go cuesta {{price}} y ofrece acceso confiable a modelos de codificación abiertos populares con límites de uso generosos.",
"workspace.lite.promo.price": "$10/mes",
"workspace.lite.promo.modelsTitle": "Qué incluye",
"workspace.lite.promo.footer":
"El plan está diseñado principalmente para usuarios internacionales y ofrece un acceso global estable. Los precios y los límites de uso pueden cambiar a medida que aprendemos del uso inicial y de los comentarios recibidos.",
+20 -14
View File
@@ -260,7 +260,7 @@ export const dict = {
"go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
"go.banner.text": "Ox Alpha Free est disponible sur Go pour une durée limitée",
"go.meta.description":
"Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites d'utilisation généreuses et un accès fiable aux principaux modèles de codage.",
"Go coûte 10 $/mois, avec des limites d'utilisation généreuses et un accès fiable aux principaux modèles de codage.",
"go.hero.title": "Modèles de code à faible coût pour tous",
"go.hero.body":
"Go apporte le codage agentique aux programmeurs du monde entier. Offrant des limites généreuses et un accès fiable aux modèles open source les plus capables, pour que vous puissiez construire avec des agents puissants sans vous soucier du coût ou de la disponibilité.",
@@ -269,9 +269,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "S'abonner à Go",
"go.cta.price": "10 $/mois",
"go.cta.promo": "$5 le premier mois",
"go.pricing.body":
"Utilisez-le avec n'importe quel agent. $5 le premier mois, puis 10 $/mois. Rechargez du crédit si nécessaire. Annulez à tout moment.",
"Utilisez-le avec n'importe quel agent. 10 $/mois. Rechargez du crédit si nécessaire. Annulez à tout moment.",
"go.graph.free": "Gratuit",
"go.graph.freePill": "Big Pickle et modèles gratuits",
"go.graph.go": "Go",
@@ -304,21 +303,20 @@ export const dict = {
"go.testimonials.frank.quote": "J'aimerais être encore chez Nvidia.",
"go.problem.title": "Quel problème Go résout-il ?",
"go.problem.body":
"Nous nous efforçons d'apporter l'expérience OpenCode au plus grand nombre. OpenCode Go est un abonnement à faible coût : $5 pour le premier mois, puis 10 $/mois. Il offre des limites généreuses et un accès fiable aux modèles open source les plus performants.",
"Nous nous efforçons d'apporter l'expérience OpenCode au plus grand nombre. OpenCode Go est un abonnement à faible coût de 10 $/mois. Il offre des limites généreuses et un accès fiable aux modèles open source les plus performants.",
"go.problem.subtitle": " ",
"go.problem.item1": "Prix d'abonnement bas",
"go.problem.item2": "Limites généreuses et accès fiable",
"go.problem.item3": "Conçu pour autant de programmeurs que possible",
"go.problem.item4": "Une sélection de modèles testés pour le codage agentique",
"go.how.title": "Comment fonctionne Go",
"go.how.body":
"Go commence à $5 pour le premier mois, puis 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.",
"go.how.body": "Go coûte 10 $/mois. Vous pouvez l'utiliser avec OpenCode ou n'importe quel agent.",
"go.how.step1.title": "Créez un compte",
"go.how.step1.beforeLink": "suivez les",
"go.how.step1.link": "instructions de configuration",
"go.how.step2.title": "Abonnez-vous à Go",
"go.how.step2.link": "$5 le premier mois",
"go.how.step2.afterLink": "puis 10 $/mois avec des limites généreuses",
"go.how.step2.link": "10 $/mois",
"go.how.step2.afterLink": "avec des limites généreuses",
"go.how.step3.title": "Commencez à coder",
"go.how.step3.body": "avec un accès fiable aux modèles open source",
"go.privacy.title": "Votre vie privée est importante pour nous",
@@ -335,11 +333,11 @@ export const dict = {
"go.faq.a2": "Go inclut les modèles ci-dessous, avec des limites généreuses et un accès fiable.",
"go.faq.q3": "Est-ce que Go est la même chose que Zen ?",
"go.faq.a3":
"Non. Zen est un paiement à l'utilisation, tandis que Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites généreuses et un accès fiable à une sélection de modèles.",
"Non. Zen est un paiement à l'utilisation, tandis que Go coûte 10 $/mois, avec des limites généreuses et un accès fiable à une sélection de modèles.",
"go.faq.q4": "Combien coûte Go ?",
"go.faq.a4.p1.beforePricing": "Go coûte",
"go.faq.a4.p1.pricingLink": "$5 le premier mois",
"go.faq.a4.p1.afterPricing": "puis 10 $/mois avec des limites généreuses.",
"go.faq.a4.p1.pricingLink": "10 $/mois",
"go.faq.a4.p1.afterPricing": "avec des limites généreuses.",
"go.faq.a4.p2.beforeAccount": "Vous pouvez gérer votre abonnement dans votre",
"go.faq.a4.p2.accountLink": "compte",
"go.faq.a4.p3": "Annulez à tout moment.",
@@ -681,10 +679,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "quelques secondes",
"workspace.lite.subscription.message": "Vous êtes abonné à OpenCode Go.",
"workspace.lite.subscription.manage": "Gérer l'abonnement",
"workspace.lite.subscription.rollingUsage": "Utilisation glissante",
"workspace.lite.subscription.rollingUsage": "Utilisation sur 5 heures",
"workspace.lite.subscription.rollingQuota": "Quota sur 5 heures",
"workspace.lite.subscription.weeklyUsage": "Utilisation hebdomadaire",
"workspace.lite.subscription.weeklyQuota": "Quota hebdomadaire",
"workspace.lite.subscription.monthlyUsage": "Utilisation mensuelle",
"workspace.lite.subscription.monthlyQuota": "Quota mensuel",
"workspace.lite.subscription.resetsIn": "Réinitialisation dans",
"workspace.lite.subscription.showDetails": "Afficher les détails",
"workspace.lite.subscription.hideDetails": "Masquer les détails",
"workspace.lite.subscription.model": "Modèle",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Total",
"workspace.lite.subscription.useBalance":
"Utilisez votre solde disponible après avoir atteint les limites d'utilisation",
"workspace.lite.subscription.selectProvider":
@@ -698,8 +704,8 @@ export const dict = {
"workspace.lite.other.message":
"Un autre membre de cet espace de travail est déjà abonné à OpenCode Go. Un seul membre par espace de travail peut s'abonner.",
"workspace.lite.promo.description":
"OpenCode Go commence à {{price}}, puis 10 $/mois, et offre un accès fiable aux modèles de code ouverts populaires avec des limites d'utilisation généreuses.",
"workspace.lite.promo.price": "$5 le premier mois",
"OpenCode Go coûte {{price}} et offre un accès fiable aux modèles de code ouverts populaires avec des limites d'utilisation généreuses.",
"workspace.lite.promo.price": "10 $/mois",
"workspace.lite.promo.modelsTitle": "Ce qui est inclus",
"workspace.lite.promo.footer":
"Ce forfait est principalement conçu pour les utilisateurs internationaux et offre un accès mondial stable. Les tarifs et les limites d'utilisation peuvent évoluer à mesure que nous tirons les enseignements des premières utilisations et des retours reçus.",
+20 -13
View File
@@ -256,7 +256,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
"go.banner.text": "Ox Alpha Free è disponibile su Go per un periodo limitato",
"go.meta.description":
"Go inizia a $5 per il primo mese, poi $10/mese, con limiti di utilizzo generosi e un accesso affidabile ai principali modelli di coding.",
"Go costa $10/mese, con limiti di utilizzo generosi e un accesso affidabile ai principali modelli di coding.",
"go.hero.title": "Modelli di coding a basso costo per tutti",
"go.hero.body":
"Go porta il coding agentico ai programmatori di tutto il mondo. Offrendo limiti generosi e un accesso affidabile ai modelli open source più capaci, in modo da poter costruire con agenti potenti senza preoccuparsi dei costi o della disponibilità.",
@@ -265,9 +265,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Abbonati a Go",
"go.cta.price": "$10/mese",
"go.cta.promo": "$5 il primo mese",
"go.pricing.body":
"Usalo con qualsiasi agente. $5 il primo mese, poi $10/mese. Ricarica il credito se necessario. Annulla in qualsiasi momento.",
"Usalo con qualsiasi agente. $10/mese. Ricarica il credito se necessario. Annulla in qualsiasi momento.",
"go.graph.free": "Gratis",
"go.graph.freePill": "Big Pickle e modelli gratuiti",
"go.graph.go": "Go",
@@ -300,20 +299,20 @@ export const dict = {
"go.testimonials.frank.quote": "Vorrei essere ancora a Nvidia.",
"go.problem.title": "Quale problema risolve Go?",
"go.problem.body":
"Ci concentriamo nel portare l'esperienza OpenCode a quante più persone possibile. OpenCode Go è un abbonamento a basso costo: $5 il primo mese, poi $10/mese. Offre limiti generosi e accesso affidabile ai modelli open source più capaci.",
"Ci concentriamo nel portare l'esperienza OpenCode a quante più persone possibile. OpenCode Go è un abbonamento a basso costo da $10/mese. Offre limiti generosi e accesso affidabile ai modelli open source più capaci.",
"go.problem.subtitle": " ",
"go.problem.item1": "Prezzo di abbonamento a basso costo",
"go.problem.item2": "Limiti generosi e accesso affidabile",
"go.problem.item3": "Costruito per il maggior numero possibile di programmatori",
"go.problem.item4": "Una selezione curata di modelli testati per il coding agentico",
"go.how.title": "Come funziona Go",
"go.how.body": "Go inizia a $5 per il primo mese, poi $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.",
"go.how.body": "Go costa $10/mese. Puoi usarlo con OpenCode o qualsiasi agente.",
"go.how.step1.title": "Crea un account",
"go.how.step1.beforeLink": "segui le",
"go.how.step1.link": "istruzioni di configurazione",
"go.how.step2.title": "Abbonati a Go",
"go.how.step2.link": "$5 il primo mese",
"go.how.step2.afterLink": "poi $10/mese con limiti generosi",
"go.how.step2.link": "$10/mese",
"go.how.step2.afterLink": "con limiti generosi",
"go.how.step3.title": "Inizia a programmare",
"go.how.step3.body": "con accesso affidabile ai modelli open source",
"go.privacy.title": "La tua privacy è importante per noi",
@@ -330,11 +329,11 @@ export const dict = {
"go.faq.a2": "Go include i modelli elencati di seguito, con limiti generosi e accesso affidabile.",
"go.faq.q3": "Go è lo stesso di Zen?",
"go.faq.a3":
"No. Zen è a consumo, mentre Go inizia a $5 per il primo mese, poi $10/mese, con limiti generosi e un accesso affidabile a una selezione curata di modelli.",
"No. Zen è a consumo, mentre Go costa $10/mese, con limiti generosi e un accesso affidabile a una selezione curata di modelli.",
"go.faq.q4": "Quanto costa Go?",
"go.faq.a4.p1.beforePricing": "Go costa",
"go.faq.a4.p1.pricingLink": "$5 il primo mese",
"go.faq.a4.p1.afterPricing": "poi $10/mese con limiti generosi.",
"go.faq.a4.p1.pricingLink": "$10/mese",
"go.faq.a4.p1.afterPricing": "con limiti generosi.",
"go.faq.a4.p2.beforeAccount": "Puoi gestire il tuo abbonamento nel tuo",
"go.faq.a4.p2.accountLink": "account",
"go.faq.a4.p3": "Annulla in qualsiasi momento.",
@@ -673,10 +672,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "pochi secondi",
"workspace.lite.subscription.message": "Sei abbonato a OpenCode Go.",
"workspace.lite.subscription.manage": "Gestisci Abbonamento",
"workspace.lite.subscription.rollingUsage": "Utilizzo Continuativo",
"workspace.lite.subscription.rollingUsage": "Utilizzo su 5 ore",
"workspace.lite.subscription.rollingQuota": "Quota su 5 ore",
"workspace.lite.subscription.weeklyUsage": "Utilizzo Settimanale",
"workspace.lite.subscription.weeklyQuota": "Quota Settimanale",
"workspace.lite.subscription.monthlyUsage": "Utilizzo Mensile",
"workspace.lite.subscription.monthlyQuota": "Quota Mensile",
"workspace.lite.subscription.resetsIn": "Si resetta tra",
"workspace.lite.subscription.showDetails": "Mostra dettagli",
"workspace.lite.subscription.hideDetails": "Nascondi dettagli",
"workspace.lite.subscription.model": "Modello",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Totale",
"workspace.lite.subscription.useBalance": "Usa il tuo saldo disponibile dopo aver raggiunto i limiti di utilizzo",
"workspace.lite.subscription.selectProvider":
'Seleziona "OpenCode Go" come provider nella tua configurazione opencode per utilizzare i modelli Go.',
@@ -689,8 +696,8 @@ export const dict = {
"workspace.lite.other.message":
"Un altro membro in questo workspace è già abbonato a OpenCode Go. Solo un membro per workspace può abbonarsi.",
"workspace.lite.promo.description":
"OpenCode Go parte da {{price}}, poi $10/mese, e offre un accesso affidabile a popolari modelli di coding aperti con generosi limiti di utilizzo.",
"workspace.lite.promo.price": "$5 il primo mese",
"OpenCode Go costa {{price}} e offre un accesso affidabile a popolari modelli di coding aperti con generosi limiti di utilizzo.",
"workspace.lite.promo.price": "$10/mese",
"workspace.lite.promo.modelsTitle": "Cosa è incluso",
"workspace.lite.promo.footer":
"Il piano è pensato principalmente per gli utenti internazionali e offre un accesso globale stabile. I prezzi e i limiti di utilizzo potrebbero cambiare in base a quanto apprenderemo dall'utilizzo iniziale e dai feedback.",
+20 -13
View File
@@ -255,7 +255,7 @@ export const dict = {
"go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
"go.banner.text": "Ox Alpha Freeは期間限定でGoで利用できます",
"go.meta.description":
"Goは最初の月$5、その後$10/月で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。",
"Goは月額$10で、主要なコーディングモデルへのゆとりある利用上限と安定したアクセスを提供します。",
"go.hero.title": "すべての人のための低価格なコーディングモデル",
"go.hero.body":
"Goは、世界中のプログラマーにエージェント型コーディングをもたらします。最も高性能なオープンソースモデルへの十分な制限と安定したアクセスを提供し、コストや可用性を気にすることなく強力なエージェントで構築できます。",
@@ -264,9 +264,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Goを購読する",
"go.cta.price": "$10/月",
"go.cta.promo": "初月 $5",
"go.pricing.body":
"どのエージェントでも使えます。最初の月$5、その後$10/月。必要に応じてクレジットを追加。いつでもキャンセルできます。",
"どのエージェントでも使えます。月額$10。必要に応じてクレジットを追加。いつでもキャンセルできます。",
"go.graph.free": "無料",
"go.graph.freePill": "Big Pickleと無料モデル",
"go.graph.go": "Go",
@@ -300,20 +299,20 @@ export const dict = {
"go.testimonials.frank.quote": "まだNvidiaにいられたらよかったのに。",
"go.problem.title": "Goはどのような問題を解決していますか?",
"go.problem.body":
"私たちはOpenCodeの体験をできるだけ多くの人に届けることに注力しています。OpenCode Goは低価格サブスクリプションで、最初の月は$5、その後は$10/月です。ゆとりある上限と、最も高性能なオープンソースモデルへの信頼できるアクセスを提供します。",
"私たちはOpenCodeの体験をできるだけ多くの人に届けることに注力しています。OpenCode Goは月額$10の低価格サブスクリプションです。ゆとりある上限と、最も高性能なオープンソースモデルへの信頼できるアクセスを提供します。",
"go.problem.subtitle": " ",
"go.problem.item1": "低価格なサブスクリプション料金",
"go.problem.item2": "十分な制限と安定したアクセス",
"go.problem.item3": "できるだけ多くのプログラマーのために構築",
"go.problem.item4": "エージェント型コーディング向けにテストされた厳選モデルラインナップ",
"go.how.title": "Goの仕組み",
"go.how.body": "Goは最初の月$5、その後$10/月で始まります。OpenCodeまたは任意のエージェントで使えます。",
"go.how.body": "Goは月額$10です。OpenCodeまたは任意のエージェントで使えます。",
"go.how.step1.title": "アカウントを作成",
"go.how.step1.beforeLink": "",
"go.how.step1.link": "セットアップ手順はこちら",
"go.how.step2.title": "Goを購読する",
"go.how.step2.link": "最初の月$5",
"go.how.step2.afterLink": "その後$10/月、ゆとりある上限付き",
"go.how.step2.link": "月額$10",
"go.how.step2.afterLink": "ゆとりある上限付き",
"go.how.step3.title": "コーディングを開始",
"go.how.step3.body": "オープンソースモデルへの安定したアクセスで",
"go.privacy.title": "あなたのプライバシーは私たちにとって重要です",
@@ -330,11 +329,11 @@ export const dict = {
"go.faq.a2": "Go には、十分な利用上限と安定したアクセスを備えた、以下のモデルが含まれます。",
"go.faq.q3": "GoはZenと同じですか?",
"go.faq.a3":
"いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で、厳選されたモデルラインナップへのゆとりある上限と安定したアクセスを提供します。",
"いいえ。Zenは従量課金制ですが、Goは月額$10で、厳選されたモデルラインナップへのゆとりある上限と安定したアクセスを提供します。",
"go.faq.q4": "Goの料金は?",
"go.faq.a4.p1.beforePricing": "Goは",
"go.faq.a4.p1.pricingLink": "最初の月$5",
"go.faq.a4.p1.afterPricing": "その後$10/月、ゆとりある上限付き。",
"go.faq.a4.p1.pricingLink": "月額$10",
"go.faq.a4.p1.afterPricing": "ゆとりある上限付き。",
"go.faq.a4.p2.beforeAccount": "管理画面:",
"go.faq.a4.p2.accountLink": "アカウント",
"go.faq.a4.p3": "いつでもキャンセル可能です。",
@@ -671,10 +670,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "数秒",
"workspace.lite.subscription.message": "あなたは OpenCode Go を購読しています。",
"workspace.lite.subscription.manage": "サブスクリプションの管理",
"workspace.lite.subscription.rollingUsage": "ローリング利用量",
"workspace.lite.subscription.rollingUsage": "5時間利用量",
"workspace.lite.subscription.rollingQuota": "5時間上限",
"workspace.lite.subscription.weeklyUsage": "週間利用量",
"workspace.lite.subscription.weeklyQuota": "週間上限",
"workspace.lite.subscription.monthlyUsage": "月間利用量",
"workspace.lite.subscription.monthlyQuota": "月間上限",
"workspace.lite.subscription.resetsIn": "リセットまで",
"workspace.lite.subscription.showDetails": "詳細を表示",
"workspace.lite.subscription.hideDetails": "詳細を非表示",
"workspace.lite.subscription.model": "モデル",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "合計",
"workspace.lite.subscription.useBalance": "利用限度額に達したら利用可能な残高を使用する",
"workspace.lite.subscription.selectProvider":
"Go モデルを使用するには、opencode の設定で「OpenCode Go」をプロバイダーとして選択してください。",
@@ -687,8 +694,8 @@ export const dict = {
"workspace.lite.other.message":
"このワークスペースの別のメンバーが既に OpenCode Go を購読しています。ワークスペースにつき1人のメンバーのみが購読できます。",
"workspace.lite.promo.description":
"OpenCode Goは{{price}}で始まり、その後は$10/月で、人気の高いオープンコーディングモデルへの安定したアクセスと余裕のある利用枠を提供します。",
"workspace.lite.promo.price": "初月$5",
"OpenCode Goは{{price}}で、人気の高いオープンコーディングモデルへの安定したアクセスと余裕のある利用枠を提供します。",
"workspace.lite.promo.price": "$10/月",
"workspace.lite.promo.modelsTitle": "含まれるもの",
"workspace.lite.promo.footer":
"このプランは主に海外のユーザー向けに設計されており、世界中から安定してご利用いただけます。料金と利用上限は、初期の利用状況やフィードバックを踏まえて変更される場合があります。",
+20 -14
View File
@@ -251,8 +251,7 @@ export const dict = {
"go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
"go.banner.text": "Ox Alpha Free가 한정된 기간 동안 Go에서 제공됩니다",
"go.meta.description":
"Go는 첫 달 $5, 이후 $10/월로 시작하며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
"go.meta.description": "Go는 월 $10이며, 넉넉한 사용 한도와 주요 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
"go.hero.title": "모두를 위한 저비용 코딩 모델",
"go.hero.body":
"Go는 전 세계 프로그래머들에게 에이전트 코딩을 제공합니다. 가장 유능한 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공하므로, 비용이나 가용성 걱정 없이 강력한 에이전트로 빌드할 수 있습니다.",
@@ -261,9 +260,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Go 구독하기",
"go.cta.price": "$10/월",
"go.cta.promo": "첫 달 $5",
"go.pricing.body":
"어떤 에이전트와도 사용할 수 있습니다. 첫 달 $5, 이후 $10/월. 필요하면 크레딧을 충전하세요. 언제든지 취소할 수 있습니다.",
"어떤 에이전트와도 사용할 수 있습니다. $10. 필요하면 크레딧을 충전하세요. 언제든지 취소할 수 있습니다.",
"go.graph.free": "무료",
"go.graph.freePill": "Big Pickle 및 무료 모델",
"go.graph.go": "Go",
@@ -297,20 +295,20 @@ export const dict = {
"go.testimonials.frank.quote": "아직 Nvidia에 있었으면 좋았을 텐데요.",
"go.problem.title": "Go는 어떤 문제를 해결하나요?",
"go.problem.body":
"우리는 가능한 많은 사람들에게 OpenCode 경험을 제공하는 데 집중하고 있습니다. OpenCode Go는 저렴한 구독 서비스로, 첫 달 $5, 이후 $10/월입니다. 넉넉한 한도와 가장 뛰어난 오픈 소스 모델에 대한 안정적인 액세스를 제공합니다.",
"우리는 가능한 많은 사람들에게 OpenCode 경험을 제공하는 데 집중하고 있습니다. OpenCode Go는 월 $10의 저렴한 구독 서비스입니다. 넉넉한 한도와 가장 뛰어난 오픈 소스 모델에 대한 안정적인 액세스를 제공합니다.",
"go.problem.subtitle": " ",
"go.problem.item1": "저렴한 구독 가격",
"go.problem.item2": "넉넉한 한도와 안정적인 액세스",
"go.problem.item3": "가능한 한 많은 프로그래머를 위해 제작됨",
"go.problem.item4": "에이전트 코딩용으로 테스트된 엄선된 모델 라인업",
"go.how.title": "Go 작동 방식",
"go.how.body": "Go는 첫 달 $5, 이후 $10/월로 시작합니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.",
"go.how.body": "Go는 월 $10입니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.",
"go.how.step1.title": "계정 생성",
"go.how.step1.beforeLink": "",
"go.how.step1.link": "설정 지침을 따르세요",
"go.how.step2.title": "Go 구독",
"go.how.step2.link": "첫 달 $5",
"go.how.step2.afterLink": "이후 $10/월, 넉넉한 한도 포함",
"go.how.step2.link": "월 $10",
"go.how.step2.afterLink": "넉넉한 한도 포함",
"go.how.step3.title": "코딩 시작",
"go.how.step3.body": "오픈 소스 모델에 대한 안정적인 액세스와 함께",
"go.privacy.title": "귀하의 프라이버시는 우리에게 중요합니다",
@@ -326,11 +324,11 @@ export const dict = {
"go.faq.a2": "Go에는 넉넉한 한도와 안정적인 액세스를 제공하는 아래 모델이 포함됩니다.",
"go.faq.q3": "Go는 Zen과 같은가요?",
"go.faq.a3":
"아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, 엄선된 모델 라인업에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
"아니요. Zen은 종량제인 반면, Go는 월 $10이며, 엄선된 모델 라인업에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
"go.faq.q4": "Go 비용은 얼마인가요?",
"go.faq.a4.p1.beforePricing": "Go 비용은",
"go.faq.a4.p1.pricingLink": "첫 달 $5",
"go.faq.a4.p1.afterPricing": "이후 $10/월, 넉넉한 한도 포함.",
"go.faq.a4.p1.pricingLink": "월 $10",
"go.faq.a4.p1.afterPricing": "넉넉한 한도 포함.",
"go.faq.a4.p2.beforeAccount": "구독 관리는 다음에서 가능합니다:",
"go.faq.a4.p2.accountLink": "계정",
"go.faq.a4.p3": "언제든지 취소할 수 있습니다.",
@@ -663,10 +661,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "몇 초",
"workspace.lite.subscription.message": "현재 OpenCode Go를 구독 중입니다.",
"workspace.lite.subscription.manage": "구독 관리",
"workspace.lite.subscription.rollingUsage": "롤링 사용량",
"workspace.lite.subscription.rollingUsage": "5시간 사용량",
"workspace.lite.subscription.rollingQuota": "5시간 할당량",
"workspace.lite.subscription.weeklyUsage": "주간 사용량",
"workspace.lite.subscription.weeklyQuota": "주간 할당량",
"workspace.lite.subscription.monthlyUsage": "월간 사용량",
"workspace.lite.subscription.monthlyQuota": "월간 할당량",
"workspace.lite.subscription.resetsIn": "초기화까지 남은 시간:",
"workspace.lite.subscription.showDetails": "상세 정보 보기",
"workspace.lite.subscription.hideDetails": "상세 정보 숨기기",
"workspace.lite.subscription.model": "모델",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "합계",
"workspace.lite.subscription.useBalance": "사용 한도 도달 후에는 보유 잔액 사용",
"workspace.lite.subscription.selectProvider":
'Go 모델을 사용하려면 opencode 설정에서 "OpenCode Go"를 공급자로 선택하세요.',
@@ -679,8 +685,8 @@ export const dict = {
"workspace.lite.other.message":
"이 워크스페이스의 다른 멤버가 이미 OpenCode Go를 구독 중입니다. 워크스페이스당 한 명의 멤버만 구독할 수 있습니다.",
"workspace.lite.promo.description":
"OpenCode Go는 {{price}}부터 시작하며, 이후 $10/월로 넉넉한 사용량 한도와 함께 인기 있는 오픈 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
"workspace.lite.promo.price": "첫 달 $5",
"OpenCode Go는 {{price}}로 넉넉한 사용량 한도와 함께 인기 있는 오픈 코딩 모델에 대한 안정적인 액세스를 제공합니다.",
"workspace.lite.promo.price": "$10/월",
"workspace.lite.promo.modelsTitle": "포함 내역",
"workspace.lite.promo.footer":
"이 플랜은 주로 해외 사용자를 위해 설계되었으며, 전 세계에서 안정적으로 이용할 수 있습니다. 초기 이용 현황과 피드백을 반영하는 과정에서 가격과 사용 한도가 변경될 수 있습니다.",
+20 -15
View File
@@ -256,7 +256,7 @@ export const dict = {
"go.title": "OpenCode Go | Rimelige kodemodeller for alle",
"go.banner.text": "Ox Alpha Free er tilgjengelig på Go i en begrenset periode",
"go.meta.description":
"Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.",
"Go koster $10/måned, med sjenerøse bruksgrenser og pålitelig tilgang til ledende kodemodeller.",
"go.hero.title": "Rimelige kodemodeller for alle",
"go.hero.body":
"Go bringer agent-koding til programmerere over hele verden. Med rause grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene, kan du bygge med kraftige agenter uten å bekymre deg for kostnader eller tilgjengelighet.",
@@ -265,9 +265,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Abonner på Go",
"go.cta.price": "$10/måned",
"go.cta.promo": "$5 første måned",
"go.pricing.body":
"Bruk med hvilken som helst agent. $5 første måned, deretter $10/måned. Fyll på kreditt ved behov. Avslutt når som helst.",
"go.pricing.body": "Bruk med hvilken som helst agent. $10/måned. Fyll på kreditt ved behov. Avslutt når som helst.",
"go.graph.free": "Gratis",
"go.graph.freePill": "Big Pickle og gratis modeller",
"go.graph.go": "Go",
@@ -300,21 +298,20 @@ export const dict = {
"go.testimonials.frank.quote": "Jeg skulle ønske jeg fortsatt var hos Nvidia.",
"go.problem.title": "Hvilket problem løser Go?",
"go.problem.body":
"Vi fokuserer på å bringe OpenCode-opplevelsen til så mange som mulig. OpenCode Go er et rimelig abonnement: $5 for den første måneden, deretter $10/måned. Det gir sjenerøse grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene.",
"Vi fokuserer på å bringe OpenCode-opplevelsen til så mange som mulig. OpenCode Go er et rimelig abonnement til $10/måned. Det gir sjenerøse grenser og pålitelig tilgang til de mest kapable åpen kildekode-modellene.",
"go.problem.subtitle": " ",
"go.problem.item1": "Rimelig abonnementspris",
"go.problem.item2": "Rause grenser og pålitelig tilgang",
"go.problem.item3": "Bygget for så mange programmerere som mulig",
"go.problem.item4": "Et kuratert modellutvalg testet for agent-koding",
"go.how.title": "Hvordan Go fungerer",
"go.how.body":
"Go starter på $5 for den første måneden, deretter $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.",
"go.how.body": "Go koster $10/måned. Du kan bruke det med OpenCode eller hvilken som helst agent.",
"go.how.step1.title": "Opprett en konto",
"go.how.step1.beforeLink": "følg",
"go.how.step1.link": "oppsettsinstruksjonene",
"go.how.step2.title": "Abonner på Go",
"go.how.step2.link": "$5 første måned",
"go.how.step2.afterLink": "deretter $10/måned med sjenerøse grenser",
"go.how.step2.link": "$10/måned",
"go.how.step2.afterLink": "med sjenerøse grenser",
"go.how.step3.title": "Begynn å kode",
"go.how.step3.body": "med pålitelig tilgang til åpen kildekode-modeller",
"go.privacy.title": "Personvernet ditt er viktig for oss",
@@ -331,11 +328,11 @@ export const dict = {
"go.faq.a2": "Go inkluderer modellene nedenfor, med høye grenser og pålitelig tilgang.",
"go.faq.q3": "Er Go det samme som Zen?",
"go.faq.a3":
"Nei. Zen er betaling etter bruk, mens Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse grenser og pålitelig tilgang til et kuratert modellutvalg.",
"Nei. Zen er betaling etter bruk, mens Go koster $10/måned, med sjenerøse grenser og pålitelig tilgang til et kuratert modellutvalg.",
"go.faq.q4": "Hva koster Go?",
"go.faq.a4.p1.beforePricing": "Go koster",
"go.faq.a4.p1.pricingLink": "$5 første måned",
"go.faq.a4.p1.afterPricing": "deretter $10/måned med sjenerøse grenser.",
"go.faq.a4.p1.pricingLink": "$10/måned",
"go.faq.a4.p1.afterPricing": "med sjenerøse grenser.",
"go.faq.a4.p2.beforeAccount": "Du kan administrere abonnementet ditt i din",
"go.faq.a4.p2.accountLink": "konto",
"go.faq.a4.p3": "Avslutt når som helst.",
@@ -673,10 +670,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "noen få sekunder",
"workspace.lite.subscription.message": "Du abonnerer på OpenCode Go.",
"workspace.lite.subscription.manage": "Administrer abonnement",
"workspace.lite.subscription.rollingUsage": "Løpende bruk",
"workspace.lite.subscription.rollingUsage": "5-timers bruk",
"workspace.lite.subscription.rollingQuota": "5-timers kvote",
"workspace.lite.subscription.weeklyUsage": "Ukentlig bruk",
"workspace.lite.subscription.weeklyQuota": "Ukentlig kvote",
"workspace.lite.subscription.monthlyUsage": "Månedlig bruk",
"workspace.lite.subscription.monthlyQuota": "Månedlig kvote",
"workspace.lite.subscription.resetsIn": "Nullstilles om",
"workspace.lite.subscription.showDetails": "Vis detaljer",
"workspace.lite.subscription.hideDetails": "Skjul detaljer",
"workspace.lite.subscription.model": "Modell",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Totalt",
"workspace.lite.subscription.useBalance": "Bruk din tilgjengelige saldo etter å ha nådd bruksgrensene",
"workspace.lite.subscription.selectProvider":
'Velg "OpenCode Go" som leverandør i opencode-konfigurasjonen din for å bruke Go-modeller.',
@@ -689,8 +694,8 @@ export const dict = {
"workspace.lite.other.message":
"Et annet medlem i dette arbeidsområdet abonnerer allerede på OpenCode Go. Kun ett medlem per arbeidsområde kan abonnere.",
"workspace.lite.promo.description":
"OpenCode Go starter på {{price}}, deretter $10/måned, og gir pålitelig tilgang til populære åpne kodingsmodeller med sjenerøse bruksgrenser.",
"workspace.lite.promo.price": "$5 for den første måneden",
"OpenCode Go koster {{price}} og gir pålitelig tilgang til populære åpne kodingsmodeller med sjenerøse bruksgrenser.",
"workspace.lite.promo.price": "$10/måned",
"workspace.lite.promo.modelsTitle": "Hva som er inkludert",
"workspace.lite.promo.footer":
"Planen er primært utviklet for internasjonale brukere og gir stabil global tilgang. Priser og bruksgrenser kan endres etter hvert som vi lærer av tidlig bruk og tilbakemeldinger.",
+20 -14
View File
@@ -257,7 +257,7 @@ export const dict = {
"go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego",
"go.banner.text": "Ox Alpha Free jest dostępny w Go przez ograniczony czas",
"go.meta.description":
"Go kosztuje $5 za pierwszy miesiąc, a następnie $10/miesiąc, oferując hojne limity użycia i niezawodny dostęp do wiodących modeli do kodowania.",
"Go kosztuje $10/miesiąc, oferując hojne limity użycia i niezawodny dostęp do wiodących modeli do kodowania.",
"go.hero.title": "Niskokosztowe modele do kodowania dla każdego",
"go.hero.body":
"Go udostępnia programowanie z agentami programistom na całym świecie. Oferuje hojne limity i niezawodny dostęp do najzdolniejszych modeli open source, dzięki czemu możesz budować za pomocą potężnych agentów, nie martwiąc się o koszty czy dostępność.",
@@ -266,9 +266,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Zasubskrybuj Go",
"go.cta.price": "$10/miesiąc",
"go.cta.promo": "$5 pierwszy miesiąc",
"go.pricing.body":
"Używaj z dowolnym agentem. $5 za pierwszy miesiąc, potem $10/miesiąc. Doładuj konto w razie potrzeby. Anuluj w dowolnym momencie.",
"Używaj z dowolnym agentem. $10/miesiąc. Doładuj konto w razie potrzeby. Anuluj w dowolnym momencie.",
"go.graph.free": "Darmowe",
"go.graph.freePill": "Big Pickle i darmowe modele",
"go.graph.go": "Go",
@@ -301,21 +300,20 @@ export const dict = {
"go.testimonials.frank.quote": "Chciałbym wciąż być w Nvidia.",
"go.problem.title": "Jaki problem rozwiązuje Go?",
"go.problem.body":
"Skupiamy się na udostępnieniu doświadczenia OpenCode jak największej liczbie osób. OpenCode Go to tania subskrypcja: $5 za pierwszy miesiąc, potem $10/miesiąc. Zapewnia hojne limity i niezawodny dostęp do najbardziej wydajnych modeli open source.",
"Skupiamy się na udostępnieniu doświadczenia OpenCode jak największej liczbie osób. OpenCode Go to tania subskrypcja za $10/miesiąc. Zapewnia hojne limity i niezawodny dostęp do najbardziej wydajnych modeli open source.",
"go.problem.subtitle": " ",
"go.problem.item1": "Niskokosztowa cena subskrypcji",
"go.problem.item2": "Hojne limity i niezawodny dostęp",
"go.problem.item3": "Stworzony dla jak największej liczby programistów",
"go.problem.item4": "Starannie dobrany zestaw modeli przetestowanych pod kątem kodowania z agentami",
"go.how.title": "Jak działa Go",
"go.how.body":
"Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.",
"go.how.body": "Go kosztuje $10/miesiąc. Możesz go używać z OpenCode lub dowolnym agentem.",
"go.how.step1.title": "Załóż konto",
"go.how.step1.beforeLink": "postępuj zgodnie z",
"go.how.step1.link": "instrukcją konfiguracji",
"go.how.step2.title": "Zasubskrybuj Go",
"go.how.step2.link": "$5 za pierwszy miesiąc",
"go.how.step2.afterLink": "potem $10/miesiąc z hojnymi limitami",
"go.how.step2.link": "$10/miesiąc",
"go.how.step2.afterLink": "z hojnymi limitami",
"go.how.step3.title": "Zacznij kodować",
"go.how.step3.body": "z niezawodnym dostępem do modeli open source",
"go.privacy.title": "Twoja prywatność jest dla nas ważna",
@@ -332,11 +330,11 @@ export const dict = {
"go.faq.a2": "Go obejmuje poniższe modele z wysokimi limitami i niezawodnym dostępem.",
"go.faq.q3": "Czy Go to to samo co Zen?",
"go.faq.a3":
"Nie. Zen działa w modelu płatności za użycie, natomiast Go kosztuje $5 za pierwszy miesiąc, a następnie $10/miesiąc, oferując hojne limity i niezawodny dostęp do starannie dobranego zestawu modeli.",
"Nie. Zen działa w modelu płatności za użycie, natomiast Go kosztuje $10/miesiąc, oferując hojne limity i niezawodny dostęp do starannie dobranego zestawu modeli.",
"go.faq.q4": "Ile kosztuje Go?",
"go.faq.a4.p1.beforePricing": "Go kosztuje",
"go.faq.a4.p1.pricingLink": "$5 za pierwszy miesiąc",
"go.faq.a4.p1.afterPricing": "potem $10/miesiąc z hojnymi limitami.",
"go.faq.a4.p1.pricingLink": "$10/miesiąc",
"go.faq.a4.p1.afterPricing": "z hojnymi limitami.",
"go.faq.a4.p2.beforeAccount": "Możesz zarządzać subskrypcją na swoim",
"go.faq.a4.p2.accountLink": "koncie",
"go.faq.a4.p3": "Anuluj w dowolnym momencie.",
@@ -673,10 +671,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "kilka sekund",
"workspace.lite.subscription.message": "Subskrybujesz OpenCode Go.",
"workspace.lite.subscription.manage": "Zarządzaj subskrypcją",
"workspace.lite.subscription.rollingUsage": "Użycie kroczące",
"workspace.lite.subscription.rollingUsage": "Użycie w ciągu 5 godzin",
"workspace.lite.subscription.rollingQuota": "Limit 5-godzinny",
"workspace.lite.subscription.weeklyUsage": "Użycie tygodniowe",
"workspace.lite.subscription.weeklyQuota": "Limit tygodniowy",
"workspace.lite.subscription.monthlyUsage": "Użycie miesięczne",
"workspace.lite.subscription.monthlyQuota": "Limit miesięczny",
"workspace.lite.subscription.resetsIn": "Resetuje się za",
"workspace.lite.subscription.showDetails": "Pokaż szczegóły",
"workspace.lite.subscription.hideDetails": "Ukryj szczegóły",
"workspace.lite.subscription.model": "Model",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Łącznie",
"workspace.lite.subscription.useBalance": "Użyj dostępnego salda po osiągnięciu limitów użycia",
"workspace.lite.subscription.selectProvider":
'Wybierz "OpenCode Go" jako dostawcę w konfiguracji opencode, aby używać modeli Go.',
@@ -689,8 +695,8 @@ export const dict = {
"workspace.lite.other.message":
"Inny członek tego obszaru roboczego już subskrybuje OpenCode Go. Tylko jeden członek na obszar roboczy może subskrybować.",
"workspace.lite.promo.description":
"OpenCode Go zaczyna się od {{price}}, potem $10/miesiąc, i zapewnia niezawodny dostęp do popularnych otwartych modeli kodowania z hojnymi limitami użycia.",
"workspace.lite.promo.price": "$5 za pierwszy miesiąc",
"OpenCode Go kosztuje {{price}} i zapewnia niezawodny dostęp do popularnych otwartych modeli kodowania z hojnymi limitami użycia.",
"workspace.lite.promo.price": "$10/miesiąc",
"workspace.lite.promo.modelsTitle": "Co zawiera",
"workspace.lite.promo.footer":
"Plan został opracowany przede wszystkim z myślą o użytkownikach z całego świata i zapewnia stabilny globalny dostęp. Ceny i limity użycia mogą ulec zmianie w miarę zdobywania doświadczeń na podstawie początkowego korzystania z usługi i otrzymywanych opinii.",
+20 -14
View File
@@ -260,7 +260,7 @@ export const dict = {
"go.title": "OpenCode Go | Недорогие модели для кодинга для всех",
"go.banner.text": "Ox Alpha Free доступна в Go в течение ограниченного времени",
"go.meta.description":
"Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.",
"Go стоит $10/месяц и предлагает щедрые лимиты использования и надежный доступ к ведущим моделям для кодинга.",
"go.hero.title": "Недорогие модели для кодинга для всех",
"go.hero.body":
"Go открывает доступ к агентам-программистам разработчикам по всему миру. Предлагая щедрые лимиты и надежный доступ к наиболее способным моделям с открытым исходным кодом, вы можете создавать проекты с мощными агентами, не беспокоясь о затратах или доступности.",
@@ -269,9 +269,8 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Подписаться на Go",
"go.cta.price": "$10/месяц",
"go.cta.promo": "$5 первый месяц",
"go.pricing.body":
"Используйте с любым агентом. $5 за первый месяц, затем $10/месяц. Пополняйте баланс при необходимости. Отменить можно в любое время.",
"Используйте с любым агентом. $10/месяц. Пополняйте баланс при необходимости. Отменить можно в любое время.",
"go.graph.free": "Бесплатно",
"go.graph.freePill": "Big Pickle и бесплатные модели",
"go.graph.go": "Go",
@@ -305,21 +304,20 @@ export const dict = {
"go.testimonials.frank.quote": "Жаль, что я больше не в Nvidia.",
"go.problem.title": "Какую проблему решает Go?",
"go.problem.body":
"Мы стремимся сделать OpenCode доступным для как можно большего числа людей. OpenCode Go - это недорогая подписка: $5 за первый месяц, затем $10/месяц. Она предоставляет щедрые лимиты и надежный доступ к самым мощным моделям с открытым исходным кодом.",
"Мы стремимся сделать OpenCode доступным для как можно большего числа людей. OpenCode Go - это недорогая подписка за $10/месяц. Она предоставляет щедрые лимиты и надежный доступ к самым мощным моделям с открытым исходным кодом.",
"go.problem.subtitle": " ",
"go.problem.item1": "Недорогая подписка",
"go.problem.item2": "Щедрые лимиты и надежный доступ",
"go.problem.item3": "Создан для максимального числа программистов",
"go.problem.item4": "Отобранные модели, протестированные для агентного программирования",
"go.how.title": "Как работает Go",
"go.how.body":
"Go начинается с $5 за первый месяц, затем $10/месяц. Вы можете использовать его с OpenCode или любым агентом.",
"go.how.body": "Go стоит $10/месяц. Вы можете использовать его с OpenCode или любым агентом.",
"go.how.step1.title": "Создайте аккаунт",
"go.how.step1.beforeLink": "следуйте",
"go.how.step1.link": "инструкциям по настройке",
"go.how.step2.title": "Подпишитесь на Go",
"go.how.step2.link": "$5 за первый месяц",
"go.how.step2.afterLink": "затем $10/месяц с щедрыми лимитами",
"go.how.step2.link": "$10/месяц",
"go.how.step2.afterLink": "с щедрыми лимитами",
"go.how.step3.title": "Начните кодить",
"go.how.step3.body": "с надежным доступом к open-source моделям",
"go.privacy.title": "Ваша приватность важна для нас",
@@ -336,11 +334,11 @@ export const dict = {
"go.faq.a2": "Go включает перечисленные ниже модели с щедрыми лимитами и надежным доступом.",
"go.faq.q3": "Go — это то же самое, что и Zen?",
"go.faq.a3":
"Нет. Zen оплачивается по мере использования, а Go стоит $5 за первый месяц, затем $10/месяц и предлагает щедрые лимиты и надежный доступ к отобранным моделям.",
"Нет. Zen оплачивается по мере использования, а Go стоит $10/месяц и предлагает щедрые лимиты и надежный доступ к отобранным моделям.",
"go.faq.q4": "Сколько стоит Go?",
"go.faq.a4.p1.beforePricing": "Go стоит",
"go.faq.a4.p1.pricingLink": "$5 за первый месяц",
"go.faq.a4.p1.afterPricing": "затем $10/месяц с щедрыми лимитами.",
"go.faq.a4.p1.pricingLink": "$10/месяц",
"go.faq.a4.p1.afterPricing": "с щедрыми лимитами.",
"go.faq.a4.p2.beforeAccount": "Вы можете управлять подпиской в своем",
"go.faq.a4.p2.accountLink": "аккаунте",
"go.faq.a4.p3": "Отмена в любое время.",
@@ -680,10 +678,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "несколько секунд",
"workspace.lite.subscription.message": "Вы подписаны на OpenCode Go.",
"workspace.lite.subscription.manage": "Управление подпиской",
"workspace.lite.subscription.rollingUsage": "Скользящее использование",
"workspace.lite.subscription.rollingUsage": "Использование за 5 часов",
"workspace.lite.subscription.rollingQuota": "Квота на 5 часов",
"workspace.lite.subscription.weeklyUsage": "Недельное использование",
"workspace.lite.subscription.weeklyQuota": "Недельная квота",
"workspace.lite.subscription.monthlyUsage": "Ежемесячное использование",
"workspace.lite.subscription.monthlyQuota": "Ежемесячная квота",
"workspace.lite.subscription.resetsIn": "Сброс через",
"workspace.lite.subscription.showDetails": "Показать подробности",
"workspace.lite.subscription.hideDetails": "Скрыть подробности",
"workspace.lite.subscription.model": "Модель",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Итого",
"workspace.lite.subscription.useBalance": "Использовать доступный баланс после достижения лимитов",
"workspace.lite.subscription.selectProvider":
'Выберите "OpenCode Go" в качестве провайдера в настройках opencode для использования моделей Go.',
@@ -696,8 +702,8 @@ export const dict = {
"workspace.lite.other.message":
"Другой участник в этом рабочем пространстве уже подписан на OpenCode Go. Только один участник в рабочем пространстве может оформить подписку.",
"workspace.lite.promo.description":
"OpenCode Go начинается с {{price}}, затем $10/месяц и предоставляет надежный доступ к популярным открытым моделям кодирования с щедрыми лимитами использования.",
"workspace.lite.promo.price": "$5 за первый месяц",
"OpenCode Go стоит {{price}} и предоставляет надежный доступ к популярным открытым моделям кодирования с щедрыми лимитами использования.",
"workspace.lite.promo.price": "$10/месяц",
"workspace.lite.promo.modelsTitle": "Что включено",
"workspace.lite.promo.footer":
"План предназначен в первую очередь для пользователей по всему миру и обеспечивает стабильный глобальный доступ. Цены и лимиты использования могут меняться по мере изучения первых результатов использования и отзывов.",
+20 -13
View File
@@ -255,7 +255,7 @@ export const dict = {
"go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
"go.banner.text": "Ox Alpha Free พร้อมใช้งานบน Go ในช่วงเวลาจำกัด",
"go.meta.description":
"Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้",
"Go มีราคา $10/เดือน พร้อมขีดจำกัดการใช้งานที่เอื้อเฟื้อและการเข้าถึงโมเดลเขียนโค้ดชั้นนำอย่างเชื่อถือได้",
"go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
"go.hero.body":
"Go นำการเขียนโค้ดแบบเอเจนต์มาสู่นักเขียนโปรแกรมทั่วโลก เสนอขีดจำกัดที่กว้างขวางและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดได้อย่างน่าเชื่อถือ เพื่อให้คุณสามารถสร้างสรรค์ด้วยเอเจนต์ที่ทรงพลังโดยไม่ต้องกังวลเรื่องค่าใช้จ่ายหรือความพร้อมใช้งาน",
@@ -264,8 +264,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "สมัครสมาชิก Go",
"go.cta.price": "$10/เดือน",
"go.cta.promo": "$5 เดือนแรก",
"go.pricing.body": "ใช้กับเอเจนต์ใดก็ได้ $5 ในเดือนแรก จากนั้น $10/เดือน เติมเครดิตหากจำเป็น ยกเลิกได้ตลอดเวลา",
"go.pricing.body": "ใช้กับเอเจนต์ใดก็ได้ $10/เดือน เติมเครดิตหากจำเป็น ยกเลิกได้ตลอดเวลา",
"go.graph.free": "ฟรี",
"go.graph.freePill": "Big Pickle และโมเดลฟรี",
"go.graph.go": "Go",
@@ -298,20 +297,20 @@ export const dict = {
"go.testimonials.frank.quote": "ผมหวังว่าผมจะยังอยู่ที่ Nvidia",
"go.problem.title": "Go แก้ปัญหาอะไร?",
"go.problem.body":
"เรามุ่งมั่นที่จะนำประสบการณ์ OpenCode ไปสู่ผู้คนให้ได้มากที่สุด OpenCode Go เป็นการสมัครสมาชิกราคาประหยัด: $5 สำหรับเดือนแรก จากนั้น $10/เดือน โดยมอบขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดอย่างเชื่อถือได้",
"เรามุ่งมั่นที่จะนำประสบการณ์ OpenCode ไปสู่ผู้คนให้ได้มากที่สุด OpenCode Go เป็นการสมัครสมาชิกราคาประหยัด $10/เดือน โดยมอบขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดอย่างเชื่อถือได้",
"go.problem.subtitle": " ",
"go.problem.item1": "ราคาการสมัครสมาชิกที่ต่ำ",
"go.problem.item2": "ขีดจำกัดที่กว้างขวางและการเข้าถึงที่เชื่อถือได้",
"go.problem.item3": "สร้างขึ้นเพื่อโปรแกรมเมอร์จำนวนมากที่สุดเท่าที่จะเป็นไปได้",
"go.problem.item4": "ชุดโมเดลที่คัดสรรและผ่านการทดสอบสำหรับการเขียนโค้ดแบบเอเจนต์",
"go.how.title": "Go ทำงานอย่างไร",
"go.how.body": "Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน คุณสามารถใช้กับ OpenCode หรือเอเจนต์ใดก็ได้",
"go.how.body": "Go มีราคา $10/เดือน คุณสามารถใช้กับ OpenCode หรือเอเจนต์ใดก็ได้",
"go.how.step1.title": "สร้างบัญชี",
"go.how.step1.beforeLink": "ทำตาม",
"go.how.step1.link": "คำแนะนำการตั้งค่า",
"go.how.step2.title": "สมัครสมาชิก Go",
"go.how.step2.link": "$5 เดือนแรก",
"go.how.step2.afterLink": "จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อ",
"go.how.step2.link": "$10/เดือน",
"go.how.step2.afterLink": "พร้อมขีดจำกัดที่เอื้อเฟื้อ",
"go.how.step3.title": "เริ่มเขียนโค้ด",
"go.how.step3.body": "ด้วยการเข้าถึงโมเดลโอเพนซอร์สที่เชื่อถือได้",
"go.privacy.title": "ความเป็นส่วนตัวของคุณสำคัญสำหรับเรา",
@@ -328,11 +327,11 @@ export const dict = {
"go.faq.a2": "Go รวมโมเดลด้านล่างนี้ พร้อมขีดจำกัดที่มากและการเข้าถึงที่เชื่อถือได้",
"go.faq.q3": "Go เหมือนกับ Zen หรือไม่?",
"go.faq.a3":
"ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ขณะที่ Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงชุดโมเดลที่คัดสรรอย่างเชื่อถือได้",
"ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ขณะที่ Go มีราคา $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงชุดโมเดลที่คัดสรรอย่างเชื่อถือได้",
"go.faq.q4": "Go ราคาเท่าไหร่?",
"go.faq.a4.p1.beforePricing": "Go ราคา",
"go.faq.a4.p1.pricingLink": "$5 เดือนแรก",
"go.faq.a4.p1.afterPricing": "จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อ",
"go.faq.a4.p1.pricingLink": "$10/เดือน",
"go.faq.a4.p1.afterPricing": "พร้อมขีดจำกัดที่เอื้อเฟื้อ",
"go.faq.a4.p2.beforeAccount": "คุณสามารถจัดการการสมัครสมาชิกของคุณได้ใน",
"go.faq.a4.p2.accountLink": "บัญชีของคุณ",
"go.faq.a4.p3": "ยกเลิกได้ตลอดเวลา",
@@ -668,10 +667,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "ไม่กี่วินาที",
"workspace.lite.subscription.message": "คุณได้สมัครสมาชิก OpenCode Go แล้ว",
"workspace.lite.subscription.manage": "จัดการการสมัครสมาชิก",
"workspace.lite.subscription.rollingUsage": "การใช้งานแบบหมุนเวียน",
"workspace.lite.subscription.rollingUsage": "การใช้งานใน 5 ชั่วโมง",
"workspace.lite.subscription.rollingQuota": "โควตา 5 ชั่วโมง",
"workspace.lite.subscription.weeklyUsage": "การใช้งานรายสัปดาห์",
"workspace.lite.subscription.weeklyQuota": "โควตารายสัปดาห์",
"workspace.lite.subscription.monthlyUsage": "การใช้งานรายเดือน",
"workspace.lite.subscription.monthlyQuota": "โควตารายเดือน",
"workspace.lite.subscription.resetsIn": "รีเซ็ตใน",
"workspace.lite.subscription.showDetails": "แสดงรายละเอียด",
"workspace.lite.subscription.hideDetails": "ซ่อนรายละเอียด",
"workspace.lite.subscription.model": "โมเดล",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "รวม",
"workspace.lite.subscription.useBalance": "ใช้ยอดคงเหลือของคุณหลังจากถึงขีดจำกัดการใช้งาน",
"workspace.lite.subscription.selectProvider":
'เลือก "OpenCode Go" เป็นผู้ให้บริการในการตั้งค่า opencode ของคุณเพื่อใช้โมเดล Go',
@@ -684,8 +691,8 @@ export const dict = {
"workspace.lite.other.message":
"สมาชิกคนอื่นใน Workspace นี้ได้สมัคร OpenCode Go แล้ว สามารถสมัครได้เพียงหนึ่งคนต่อหนึ่ง Workspace เท่านั้น",
"workspace.lite.promo.description":
"OpenCode Go เริ่มต้นที่ {{price}} จากนั้น $10/เดือน และมอบการเข้าถึงโมเดลการเขียนโค้ดแบบเปิดยอดนิยมอย่างเสถียรพร้อมขีดจำกัดการใช้งานที่ให้มาอย่างเหลือเฟือ",
"workspace.lite.promo.price": "$5 สำหรับเดือนแรก",
"OpenCode Go ราคา {{price}} และมอบการเข้าถึงโมเดลการเขียนโค้ดแบบเปิดยอดนิยมอย่างเสถียรพร้อมขีดจำกัดการใช้งานที่ให้มาอย่างเหลือเฟือ",
"workspace.lite.promo.price": "$10/เดือน",
"workspace.lite.promo.modelsTitle": "สิ่งที่รวมอยู่ด้วย",
"workspace.lite.promo.footer":
"แผนนี้ออกแบบมาสำหรับผู้ใช้งานต่างประเทศเป็นหลักและให้การเข้าถึงที่เสถียรทั่วโลก ราคาและขีดจำกัดการใช้งานอาจเปลี่ยนแปลงได้ตามสิ่งที่เราเรียนรู้จากการใช้งานและข้อเสนอแนะในช่วงแรก",
+20 -15
View File
@@ -258,7 +258,7 @@ export const dict = {
"go.title": "OpenCode Go | Herkes için düşük maliyetli kodlama modelleri",
"go.banner.text": "Ox Alpha Free sınırlı bir süre için Go'da kullanılabilir",
"go.meta.description":
"Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; cömert kullanım limitleri ve önde gelen kodlama modellerine güvenilir erişim sunar.",
"Go ayda 10$'dır; cömert kullanım limitleri ve önde gelen kodlama modellerine güvenilir erişim sunar.",
"go.hero.title": "Herkes için düşük maliyetli kodlama modelleri",
"go.hero.body":
"Go, dünya çapındaki programcılara ajan tabanlı kodlama getiriyor. En yetenekli açık kaynaklı modellere cömert limitler ve güvenilir erişim sunarak, maliyet veya erişilebilirlik konusunda endişelenmeden güçlü ajanlarla geliştirme yapmanızı sağlar.",
@@ -267,9 +267,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Go'ya abone ol",
"go.cta.price": "Ayda 10$",
"go.cta.promo": "İlk ay $5",
"go.pricing.body":
"Herhangi bir ajanla kullanın. İlk ay $5, sonrasında ayda 10$. Gerekirse kredi yükleyin. İstediğiniz zaman iptal edin.",
"go.pricing.body": "Herhangi bir ajanla kullanın. Ayda 10$. Gerekirse kredi yükleyin. İstediğiniz zaman iptal edin.",
"go.graph.free": "Ücretsiz",
"go.graph.freePill": "Big Pickle ve ücretsiz modeller",
"go.graph.go": "Go",
@@ -303,21 +301,20 @@ export const dict = {
"go.testimonials.frank.quote": "Keşke hala Nvidia'da olsaydım.",
"go.problem.title": "Go hangi sorunu çözüyor?",
"go.problem.body":
"OpenCode deneyimini mümkün olduğunca çok kişiye ulaştırmaya odaklandık. OpenCode Go düşük maliyetli bir aboneliktir: İlk ay $5, sonrasında ayda 10$. Cömert limitler ve en yetenekli açık kaynak modellere güvenilir erişim sağlar.",
"OpenCode deneyimini mümkün olduğunca çok kişiye ulaştırmaya odaklandık. OpenCode Go, ayda 10$ olan düşük maliyetli bir aboneliktir. Cömert limitler ve en yetenekli açık kaynak modellere güvenilir erişim sağlar.",
"go.problem.subtitle": " ",
"go.problem.item1": "Düşük maliyetli abonelik fiyatlandırması",
"go.problem.item2": "Cömert limitler ve güvenilir erişim",
"go.problem.item3": "Mümkün olduğunca çok programcı için geliştirildi",
"go.problem.item4": "Ajan tabanlı kodlama için test edilmiş, özenle seçilmiş model seçenekleri",
"go.how.title": "Go nasıl çalışır?",
"go.how.body":
"Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar. OpenCode veya herhangi bir ajanla kullanabilirsiniz.",
"go.how.body": "Go ayda 10$'dır. OpenCode veya herhangi bir ajanla kullanabilirsiniz.",
"go.how.step1.title": "Bir hesap oluşturun",
"go.how.step1.beforeLink": "takip edin",
"go.how.step1.link": "kurulum talimatları",
"go.how.step2.title": "Go'ya abone olun",
"go.how.step2.link": "İlk ay $5",
"go.how.step2.afterLink": "sonrasında cömert limitlerle ayda 10$",
"go.how.step2.link": "Ayda 10$",
"go.how.step2.afterLink": "cömert limitlerle",
"go.how.step3.title": "Kodlamaya başlayın",
"go.how.step3.body": "açık kaynaklı modellere güvenilir erişimle",
"go.privacy.title": "Gizliliğiniz bizim için önemlidir",
@@ -334,11 +331,11 @@ export const dict = {
"go.faq.a2": "Go, aşağıda listelenen modelleri cömert limitler ve güvenilir erişimle sunar.",
"go.faq.q3": "Go, Zen ile aynı mı?",
"go.faq.a3":
"Hayır. Zen kullandıkça öde modelidir; Go ise ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar ve özenle seçilmiş model seçeneklerine cömert limitlerle güvenilir erişim sunar.",
"Hayır. Zen kullandıkça öde modelidir; Go ise ayda 10$'dır ve özenle seçilmiş model seçeneklerine cömert limitlerle güvenilir erişim sunar.",
"go.faq.q4": "Go ne kadar?",
"go.faq.a4.p1.beforePricing": "Go'nun maliyeti",
"go.faq.a4.p1.pricingLink": "İlk ay $5",
"go.faq.a4.p1.afterPricing": "sonrasında cömert limitlerle ayda 10$.",
"go.faq.a4.p1.pricingLink": "ayda 10$",
"go.faq.a4.p1.afterPricing": "cömert limitlerle.",
"go.faq.a4.p2.beforeAccount": "Aboneliğinizi",
"go.faq.a4.p2.accountLink": "hesabınızdan",
"go.faq.a4.p3": "yönetebilirsiniz. İstediğiniz zaman iptal edin.",
@@ -676,10 +673,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "birkaç saniye",
"workspace.lite.subscription.message": "OpenCode Go abonesisiniz.",
"workspace.lite.subscription.manage": "Aboneliği Yönet",
"workspace.lite.subscription.rollingUsage": "Devam Eden Kullanım",
"workspace.lite.subscription.rollingUsage": "5 Saatlik Kullanım",
"workspace.lite.subscription.rollingQuota": "5 Saatlik Kota",
"workspace.lite.subscription.weeklyUsage": "Haftalık Kullanım",
"workspace.lite.subscription.weeklyQuota": "Haftalık Kota",
"workspace.lite.subscription.monthlyUsage": "Aylık Kullanım",
"workspace.lite.subscription.monthlyQuota": "Aylık Kota",
"workspace.lite.subscription.resetsIn": "Sıfırlama süresi",
"workspace.lite.subscription.showDetails": "Ayrıntıları göster",
"workspace.lite.subscription.hideDetails": "Ayrıntıları gizle",
"workspace.lite.subscription.model": "Model",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Toplam",
"workspace.lite.subscription.useBalance": "Kullanım limitlerine ulaştıktan sonra mevcut bakiyenizi kullanın",
"workspace.lite.subscription.selectProvider":
'Go modellerini kullanmak için opencode yapılandırmanızda "OpenCode Go"\'yu sağlayıcı olarak seçin.',
@@ -692,8 +697,8 @@ export const dict = {
"workspace.lite.other.message":
"Bu çalışma alanındaki başka bir üye zaten OpenCode Go abonesi. Çalışma alanı başına yalnızca bir üye abone olabilir.",
"workspace.lite.promo.description":
"OpenCode Go {{price}} fiyatından başlar, sonrasında ayda 10$ olur ve cömert kullanım limitleriyle popüler açık kodlama modellerine güvenilir erişim sağlar.",
"workspace.lite.promo.price": "İlk ay $5",
"OpenCode Go {{price}} fiyatıyla cömert kullanım limitleri ve popüler açık kodlama modellerine güvenilir erişim sağlar.",
"workspace.lite.promo.price": "Ayda 10$",
"workspace.lite.promo.modelsTitle": "Neler Dahil",
"workspace.lite.promo.footer":
"Plan öncelikle uluslararası kullanıcılar için tasarlanmıştır ve istikrarlı küresel erişim sağlar. Erken kullanım ve geri bildirimlerden öğrendiklerimiz doğrultusunda fiyatlandırma ve kullanım limitleri değişebilir.",
+20 -15
View File
@@ -257,7 +257,7 @@ export const dict = {
"go.title": "OpenCode Go | Недорогі моделі кодування для всіх",
"go.banner.text": "Ox Alpha Free доступна в Go протягом обмеженого часу",
"go.meta.description":
"Go починається від $5 за перший місяць, потім $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.",
"Go коштує $10/місяць, зі щедрими лімітами використання та надійним доступом до провідних моделей для кодування.",
"go.hero.title": "Недорогі моделі кодування для всіх",
"go.hero.body":
"Go надає агентне програмування програмістам у всьому світі, пропонуючи щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.",
@@ -266,9 +266,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "Підписатися на Go",
"go.cta.price": "$10/місяць",
"go.cta.promo": "$5 перший місяць",
"go.pricing.body":
"Використовуйте з будь-яким агентом. $5 перший місяць, потім $10/місяць. Поповнюйте за потреби. Скасуйте в будь-який час.",
"go.pricing.body": "Використовуйте з будь-яким агентом. $10/місяць. Поповнюйте за потреби. Скасуйте в будь-який час.",
"go.graph.free": "Безкоштовно",
"go.graph.freePill": "Big Pickle та безкоштовні моделі",
"go.graph.go": "Go",
@@ -301,21 +299,20 @@ export const dict = {
"go.testimonials.frank.quote": "Хотів би я досі бути в Nvidia.",
"go.problem.title": "Яку проблему вирішує Go?",
"go.problem.body":
"Ми зосереджені на тому, щоб зробити досвід OpenCode доступним для якомога більшої кількості людей. OpenCode Go — це недорога підписка: $5 за перший місяць, потім $10/місяць. Вона надає щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.",
"Ми зосереджені на тому, щоб зробити досвід OpenCode доступним для якомога більшої кількості людей. OpenCode Go — це недорога підписка за $10/місяць. Вона надає щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.",
"go.problem.subtitle": " ",
"go.problem.item1": "Недорога підписка",
"go.problem.item2": "Щедрі ліміти та надійний доступ",
"go.problem.item3": "Створено для якомога більшої кількості програмістів",
"go.problem.item4": "Добірка моделей, протестованих для агентного кодування",
"go.how.title": "Як працює Go",
"go.how.body":
"Go починається від $5 за перший місяць, потім $10/місяць. Використовуйте з OpenCode або будь-яким агентом.",
"go.how.body": "Go коштує $10/місяць. Використовуйте з OpenCode або будь-яким агентом.",
"go.how.step1.title": "Створіть обліковий запис",
"go.how.step1.beforeLink": "дотримуйтесь",
"go.how.step1.link": "інструкцій з налаштування",
"go.how.step2.title": "Підпишіться на Go",
"go.how.step2.link": "$5 перший місяць",
"go.how.step2.afterLink": "потім $10/місяць із щедрими лімітами",
"go.how.step2.link": "$10/місяць",
"go.how.step2.afterLink": "із щедрими лімітами",
"go.how.step3.title": "Почніть кодувати",
"go.how.step3.body": "з надійним доступом до моделей з відкритим кодом",
"go.privacy.title": "Ваша конфіденційність важлива для нас",
@@ -332,11 +329,11 @@ export const dict = {
"go.faq.a2": "Go включає моделі, перелічені нижче, із щедрими лімітами та надійним доступом.",
"go.faq.q3": "Чи Go те саме, що Zen?",
"go.faq.a3":
"Ні. Zen — це плата за використання, тоді як Go починається від $5 за перший місяць, потім $10/місяць, із щедрими лімітами та надійним доступом до добірки моделей.",
"Ні. Zen — це плата за використання, тоді як Go коштує $10/місяць, із щедрими лімітами та надійним доступом до добірки моделей.",
"go.faq.q4": "Скільки коштує Go?",
"go.faq.a4.p1.beforePricing": "Go коштує",
"go.faq.a4.p1.pricingLink": "$5 за перший місяць",
"go.faq.a4.p1.afterPricing": "потім $10/місяць із щедрими лімітами.",
"go.faq.a4.p1.pricingLink": "$10/місяць",
"go.faq.a4.p1.afterPricing": "із щедрими лімітами.",
"go.faq.a4.p2.beforeAccount": "Ви можете керувати підпискою в",
"go.faq.a4.p2.accountLink": "обліковому записі",
"go.faq.a4.p3": "Скасуйте в будь-який час.",
@@ -672,10 +669,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "кілька секунд",
"workspace.lite.subscription.message": "Ви підписані на OpenCode Go.",
"workspace.lite.subscription.manage": "Керувати підпискою",
"workspace.lite.subscription.rollingUsage": "Ковзне використання",
"workspace.lite.subscription.rollingUsage": "Використання за 5 годин",
"workspace.lite.subscription.rollingQuota": "Квота на 5 годин",
"workspace.lite.subscription.weeklyUsage": "Тижневе використання",
"workspace.lite.subscription.weeklyQuota": "Тижнева квота",
"workspace.lite.subscription.monthlyUsage": "Місячне використання",
"workspace.lite.subscription.monthlyQuota": "Місячна квота",
"workspace.lite.subscription.resetsIn": "Скидається через",
"workspace.lite.subscription.showDetails": "Показати подробиці",
"workspace.lite.subscription.hideDetails": "Приховати подробиці",
"workspace.lite.subscription.model": "Модель",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "Усього",
"workspace.lite.subscription.useBalance": "Використовуйте доступний баланс після досягнення лімітів",
"workspace.lite.subscription.selectProvider": 'Виберіть "OpenCode Go" як провайдера в конфігурації opencode.',
"workspace.lite.providers.title": "Провайдери",
@@ -685,8 +690,8 @@ export const dict = {
"workspace.lite.black.message":
"Ви вже підписані на OpenCode Black або в списку очікування. Спочатку скасуйте підписку, якщо хочете перейти на Go.",
"workspace.lite.other.message": "Інший учасник цього робочого простору вже підписаний на OpenCode Go.",
"workspace.lite.promo.description": "OpenCode Go починається від {{price}}, потім $10/місяць, із щедрими лімітами.",
"workspace.lite.promo.price": "$5 за перший місяць",
"workspace.lite.promo.description": "OpenCode Go коштує {{price}} і має щедрі ліміти.",
"workspace.lite.promo.price": "$10/місяць",
"workspace.lite.promo.modelsTitle": "Що включено",
"workspace.lite.promo.footer":
"План призначений насамперед для міжнародних користувачів і забезпечує стабільний глобальний доступ. Ціни та ліміти використання можуть змінюватися з урахуванням перших даних про використання та відгуків.",
+20 -13
View File
@@ -245,7 +245,7 @@ export const dict = {
"go.title": "OpenCode Go | 人人可用的低成本编程模型",
"go.banner.text": "Ox Alpha Free 限时加入 Go",
"go.meta.description": "Go 月 $5,之后 $10/月,提供充裕的使用限额,并可可靠访问领先的编程模型。",
"go.meta.description": "Go 月 $10,提供充裕的使用限额,并可可靠访问领先的编程模型。",
"go.hero.title": "人人可用的低成本编程模型",
"go.hero.body":
"Go 将代理编程带给全世界的程序员。提供充裕的限额和对最强大的开源模型的可靠访问,让您可以利用强大的代理进行构建,而无需担心成本或可用性。",
@@ -254,8 +254,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "订阅 Go",
"go.cta.price": "$10/月",
"go.cta.promo": "首月 $5",
"go.pricing.body": "可配合任何代理使用。首月 $5,之后 $10/月。如有需要可充值。随时取消。",
"go.pricing.body": "可配合任何代理使用。每月 $10。如有需要可充值。随时取消。",
"go.graph.free": "免费",
"go.graph.freePill": "Big Pickle 和免费模型",
"go.graph.go": "Go",
@@ -288,20 +287,20 @@ export const dict = {
"go.testimonials.frank.quote": "我希望我还在 Nvidia。",
"go.problem.title": "Go 解决了什么问题?",
"go.problem.body":
"我们致力于将 OpenCode 体验带给尽可能多的人。OpenCode Go 是一款低成本订阅服务:首月 $5,之后 $10/月。它提供充裕的额度,并让您能可靠地使用最强大的开源模型。",
"我们致力于将 OpenCode 体验带给尽可能多的人。OpenCode Go 是一款每月 $10 的低成本订阅服务。它提供充裕的额度,并让您能可靠地使用最强大的开源模型。",
"go.problem.subtitle": " ",
"go.problem.item1": "低成本订阅定价",
"go.problem.item2": "充裕的限额和可靠的访问",
"go.problem.item3": "为尽可能多的程序员打造",
"go.problem.item4": "经过代理编程测试的精选模型阵容",
"go.how.title": "Go 如何工作",
"go.how.body": "Go 起价为首月 $5,之后 $10/月。您可以将其与 OpenCode 或任何代理搭配使用。",
"go.how.body": "Go 每月 $10。您可以将其与 OpenCode 或任何代理搭配使用。",
"go.how.step1.title": "创建账户",
"go.how.step1.beforeLink": "遵循",
"go.how.step1.link": "设置说明",
"go.how.step2.title": "订阅 Go",
"go.how.step2.link": "月 $5",
"go.how.step2.afterLink": "之后 $10/月,额度充裕",
"go.how.step2.link": "月 $10",
"go.how.step2.afterLink": "额度充裕",
"go.how.step3.title": "开始编程",
"go.how.step3.body": "可靠访问开源模型",
"go.privacy.title": "您的隐私对我们很重要",
@@ -314,11 +313,11 @@ export const dict = {
"go.faq.q2": "Go 包含哪些模型?",
"go.faq.a2": "Go 包含下方列出的模型,提供充足的限额和可靠的访问。",
"go.faq.q3": "Go 和 Zen 一样吗?",
"go.faq.a3": "不。Zen 是按量付费,而 Go 月 $5,之后 $10/月,提供充裕的限额,并可可靠访问精选模型阵容。",
"go.faq.a3": "不。Zen 是按量付费,而 Go 月 $10,提供充裕的限额,并可可靠访问精选模型阵容。",
"go.faq.q4": "Go 多少钱?",
"go.faq.a4.p1.beforePricing": "Go 费用为",
"go.faq.a4.p1.pricingLink": "月 $5",
"go.faq.a4.p1.afterPricing": "之后 $10/月,额度充裕。",
"go.faq.a4.p1.pricingLink": "月 $10",
"go.faq.a4.p1.afterPricing": "额度充裕。",
"go.faq.a4.p2.beforeAccount": "您可以在您的",
"go.faq.a4.p2.accountLink": "账户",
"go.faq.a4.p3": "中管理订阅。随时取消。",
@@ -643,10 +642,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "几秒钟",
"workspace.lite.subscription.message": "您已订阅 OpenCode Go。",
"workspace.lite.subscription.manage": "管理订阅",
"workspace.lite.subscription.rollingUsage": "滚动用量",
"workspace.lite.subscription.rollingUsage": "5 小时用量",
"workspace.lite.subscription.rollingQuota": "5 小时配额",
"workspace.lite.subscription.weeklyUsage": "每周用量",
"workspace.lite.subscription.weeklyQuota": "每周配额",
"workspace.lite.subscription.monthlyUsage": "每月用量",
"workspace.lite.subscription.monthlyQuota": "每月配额",
"workspace.lite.subscription.resetsIn": "重置于",
"workspace.lite.subscription.showDetails": "显示详情",
"workspace.lite.subscription.hideDetails": "隐藏详情",
"workspace.lite.subscription.model": "模型",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "总计",
"workspace.lite.subscription.useBalance": "达到使用限额后使用您的可用余额",
"workspace.lite.subscription.selectProvider":
"在你的 opencode 配置中选择「OpenCode Go」作为提供商,即可使用 Go 模型。",
@@ -657,8 +664,8 @@ export const dict = {
"workspace.lite.black.message": "您当前已订阅 OpenCode Black 或在候补名单中。如需切换到 Go,请先取消订阅。",
"workspace.lite.other.message": "此工作区中的另一位成员已经订阅了 OpenCode Go。每个工作区只有一名成员可以订阅。",
"workspace.lite.promo.description":
"OpenCode Go 起价为 {{price}}之后 $10/月,并提供对流行开放编码模型的可靠访问,同时享有充裕的使用限额。",
"workspace.lite.promo.price": "首月 $5",
"OpenCode Go 每月 {{price}},并提供对流行开放编码模型的可靠访问,同时享有充裕的使用限额。",
"workspace.lite.promo.price": "$10/月",
"workspace.lite.promo.modelsTitle": "包含模型",
"workspace.lite.promo.footer":
"该计划主要面向国际用户,提供稳定的全球访问体验。随着我们持续了解早期使用情况并收集反馈,定价和使用限额可能会有所调整。",
+20 -13
View File
@@ -245,7 +245,7 @@ export const dict = {
"go.title": "OpenCode Go | 低成本全民編碼模型",
"go.banner.text": "Ox Alpha Free 限時加入 Go",
"go.meta.description": "Go 月 $5,之後 $10/月,提供充裕的使用限額,並可穩定存取領先的編碼模型。",
"go.meta.description": "Go 月 $10,提供充裕的使用限額,並可穩定存取領先的編碼模型。",
"go.hero.title": "低成本全民編碼模型",
"go.hero.body":
"Go 將代理編碼帶給全世界的程式設計師。提供寬裕的限額以及對最強大開源模型的穩定存取,讓你可以使用強大的代理進行構建,而無需擔心成本或可用性。",
@@ -254,8 +254,7 @@ export const dict = {
"go.cta.template": "{{text}} {{price}}",
"go.cta.text": "訂閱 Go",
"go.cta.price": "$10/月",
"go.cta.promo": "首月 $5",
"go.pricing.body": "可搭配任何代理使用。首月 $5,之後 $10/月。如有需要可儲值。隨時取消。",
"go.pricing.body": "可搭配任何代理使用。每月 $10。如有需要可儲值。隨時取消。",
"go.graph.free": "免費",
"go.graph.freePill": "Big Pickle 與免費模型",
"go.graph.go": "Go",
@@ -288,20 +287,20 @@ export const dict = {
"go.testimonials.frank.quote": "我希望我還在 Nvidia。",
"go.problem.title": "Go 正在解決什麼問題?",
"go.problem.body":
"我們致力於將 OpenCode 體驗帶給盡可能多的人。OpenCode Go 是一款低成本訂閱服務:首月 $5,之後 $10/月。它提供充裕的額度,並讓您能可靠地使用最強大的開源模型。",
"我們致力於將 OpenCode 體驗帶給盡可能多的人。OpenCode Go 是一款每月 $10 的低成本訂閱服務。它提供充裕的額度,並讓您能可靠地使用最強大的開源模型。",
"go.problem.subtitle": " ",
"go.problem.item1": "低成本訂閱定價",
"go.problem.item2": "寬裕的限額與穩定存取",
"go.problem.item3": "專為盡可能多的程式設計師打造",
"go.problem.item4": "針對代理編碼測試的精選模型陣容",
"go.how.title": "Go 如何運作",
"go.how.body": "Go 起價為首月 $5,之後 $10/月。您可以將其與 OpenCode 或任何代理搭配使用。",
"go.how.body": "Go 每月 $10。您可以將其與 OpenCode 或任何代理搭配使用。",
"go.how.step1.title": "建立帳號",
"go.how.step1.beforeLink": "遵循",
"go.how.step1.link": "設定說明",
"go.how.step2.title": "訂閱 Go",
"go.how.step2.link": "月 $5",
"go.how.step2.afterLink": "之後 $10/月,額度充裕",
"go.how.step2.link": "月 $10",
"go.how.step2.afterLink": "額度充裕",
"go.how.step3.title": "開始編碼",
"go.how.step3.body": "穩定存取開源模型",
"go.privacy.title": "你的隱私對我們很重要",
@@ -314,11 +313,11 @@ export const dict = {
"go.faq.q2": "Go 包含哪些模型?",
"go.faq.a2": "Go 包含下方列出的模型,提供充足的額度與穩定的存取。",
"go.faq.q3": "Go 與 Zen 一樣嗎?",
"go.faq.a3": "不。Zen 是按量付費,而 Go 月 $5,之後 $10/月,提供充裕的限額,並可穩定存取精選模型陣容。",
"go.faq.a3": "不。Zen 是按量付費,而 Go 月 $10,提供充裕的限額,並可穩定存取精選模型陣容。",
"go.faq.q4": "Go 費用是多少?",
"go.faq.a4.p1.beforePricing": "Go 費用為",
"go.faq.a4.p1.pricingLink": "月 $5",
"go.faq.a4.p1.afterPricing": "之後 $10/月,額度充裕。",
"go.faq.a4.p1.pricingLink": "月 $10",
"go.faq.a4.p1.afterPricing": "額度充裕。",
"go.faq.a4.p2.beforeAccount": "你可以在你的",
"go.faq.a4.p2.accountLink": "帳戶",
"go.faq.a4.p3": "中管理訂閱。隨時取消。",
@@ -643,10 +642,18 @@ export const dict = {
"workspace.lite.time.fewSeconds": "幾秒",
"workspace.lite.subscription.message": "您已訂閱 OpenCode Go。",
"workspace.lite.subscription.manage": "管理訂閱",
"workspace.lite.subscription.rollingUsage": "滾動使用量",
"workspace.lite.subscription.rollingUsage": "5 小時使用量",
"workspace.lite.subscription.rollingQuota": "5 小時配額",
"workspace.lite.subscription.weeklyUsage": "每週使用量",
"workspace.lite.subscription.weeklyQuota": "每週配額",
"workspace.lite.subscription.monthlyUsage": "每月使用量",
"workspace.lite.subscription.monthlyQuota": "每月配額",
"workspace.lite.subscription.resetsIn": "重置時間:",
"workspace.lite.subscription.showDetails": "顯示詳情",
"workspace.lite.subscription.hideDetails": "隱藏詳情",
"workspace.lite.subscription.model": "模型",
"workspace.lite.subscription.contribution": "%",
"workspace.lite.subscription.total": "總計",
"workspace.lite.subscription.useBalance": "達到使用限制後使用您的可用餘額",
"workspace.lite.subscription.selectProvider":
"在您的 opencode 設定中選擇「OpenCode Go」作為提供商,即可使用 Go 模型。",
@@ -657,8 +664,8 @@ export const dict = {
"workspace.lite.black.message": "您目前已訂閱 OpenCode Black 或在候補名單中。若要切換至 Go,請先取消訂閱。",
"workspace.lite.other.message": "此工作區中的另一位成員已訂閱 OpenCode Go。每個工作區只能有一位成員訂閱。",
"workspace.lite.promo.description":
"OpenCode Go 起價為 {{price}}之後 $10/月,並提供對熱門開放編碼模型的可靠存取,同時享有充裕的使用額度。",
"workspace.lite.promo.price": "首月 $5",
"OpenCode Go 每月 {{price}},並提供對熱門開放編碼模型的可靠存取,同時享有充裕的使用額度。",
"workspace.lite.promo.price": "$10/月",
"workspace.lite.promo.modelsTitle": "包含模型",
"workspace.lite.promo.footer":
"此方案主要為國際使用者設計,提供穩定的全球存取服務。隨著我們從初期使用情況和回饋中持續了解需求,價格和使用額度可能會有所調整。",
@@ -0,0 +1,60 @@
export type LiteUsageBreakdownSource = {
model: string
name: string
cost: number
quotaCost: number
multiplier?: number
estimated: boolean
}
export type LiteUsageBreakdownItem = {
model: string
name: string
cost?: number
multiplier?: number
quotaCost: number
contributionPercent: number
estimated: boolean
}
export function buildLiteUsageBreakdown(input: { usage: number; limit: number; sources: LiteUsageBreakdownSource[] }) {
const rows: LiteUsageBreakdownItem[] = input.sources
.filter((item) => item.cost !== 0 || item.quotaCost !== 0)
.sort((a, b) => b.quotaCost - a.quotaCost)
.map((item) => ({
...item,
contributionPercent: 0,
}))
const usagePercent = getUsagePercent(input.usage, input.limit)
const target = Math.max(0, Math.round(usagePercent * 10))
const totalQuota = rows.reduce((total, item) => total + Math.max(0, item.quotaCost), 0)
const units = rows.map((item) => {
const exact = totalQuota === 0 ? 0 : (Math.max(0, item.quotaCost) / totalQuota) * target
const value = Math.floor(exact)
return { item, exact, value }
})
const remaining = target - units.reduce((total, item) => total + item.value, 0)
const ranked = units.toSorted((a, b) => b.exact - b.value - (a.exact - a.value))
Array.from({ length: ranked.length === 0 ? 0 : remaining }).forEach((_, index) => {
ranked[index % ranked.length].value += 1
})
units.forEach((unit) => (unit.item.contributionPercent = unit.value / 10))
return {
usage: input.usage,
limit: input.limit,
usagePercent,
rows,
}
}
export function getModelQuotaLimit(limit: number, multiplier?: number) {
if (multiplier === undefined || multiplier <= 0) return
return limit / multiplier
}
export function getUsagePercent(amount: number, limit: number) {
if (limit === 0) return 0
return Math.round((amount / limit) * 1000) / 10
}
@@ -0,0 +1,21 @@
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({ workspaceID: z.string().startsWith("wrk_") })
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 Workspace.unblock(body.data)
.then(() => Response.json({ success: true, message: "Workspace unblocked" }))
.catch((error) => Response.json({ error: error instanceof Error ? error.message : String(error) }, { status: 400 }))
}
@@ -103,9 +103,6 @@ export default function Black() {
<button type="button" onClick={() => cancel()} data-slot="cancel">
{i18n.t("common.cancel")}
</button>
<a href={`/black/subscribe/${plan().id}`} data-slot="continue">
{i18n.t("black.action.continue")}
</a>
</div>
</div>
</div>
@@ -1,489 +0,0 @@
import { A, createAsync, query, redirect, useParams } from "@solidjs/router"
import { Title } from "@solidjs/meta"
import { createEffect, createSignal, For, Match, Show, Switch } from "solid-js"
import { type Stripe, type PaymentMethod, loadStripe } from "@stripe/stripe-js"
import { Elements, PaymentElement, useStripe, useElements, AddressElement } from "solid-stripe"
import { PlanID, plans } from "../common"
import { getActor, useAuthSession } from "~/context/auth"
import { withActor } from "~/context/auth.withActor"
import { Actor } from "@opencode-ai/console-core/actor.js"
import { and, Database, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
import { createList } from "solid-list"
import { Modal } from "~/component/modal"
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
import { Billing } from "@opencode-ai/console-core/billing.js"
import { useI18n } from "~/context/i18n"
import { useLanguage } from "~/context/language"
import { formError } from "~/lib/form-error"
import { Resource } from "@opencode-ai/console-resource"
const getEnabled = query(async () => {
"use server"
return Resource.App.stage !== "production"
}, "black.subscribe.enabled")
const plansMap = Object.fromEntries(plans.map((p) => [p.id, p])) as Record<PlanID, (typeof plans)[number]>
const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY!)
const getWorkspaces = query(async (plan: string) => {
"use server"
const actor = await getActor()
if (actor.type === "public") throw redirect("/auth/authorize?continue=/black/subscribe/" + plan)
return withActor(async () => {
return Database.use((tx) =>
tx
.select({
id: WorkspaceTable.id,
name: WorkspaceTable.name,
slug: WorkspaceTable.slug,
billing: {
customerID: BillingTable.customerID,
paymentMethodID: BillingTable.paymentMethodID,
paymentMethodType: BillingTable.paymentMethodType,
paymentMethodLast4: BillingTable.paymentMethodLast4,
subscriptionID: BillingTable.subscriptionID,
timeSubscriptionBooked: BillingTable.timeSubscriptionBooked,
},
})
.from(UserTable)
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
.innerJoin(BillingTable, eq(WorkspaceTable.id, BillingTable.workspaceID))
.where(
and(
eq(UserTable.accountID, Actor.account()),
isNull(WorkspaceTable.timeDeleted),
isNull(UserTable.timeDeleted),
),
),
)
})
}, "black.subscribe.workspaces")
const createSetupIntent = async (input: { plan: string; workspaceID: string }) => {
"use server"
const { plan, workspaceID } = input
if (!plan || !["20", "100", "200"].includes(plan)) return { error: formError.invalidPlan }
if (!workspaceID) return { error: formError.workspaceRequired }
return withActor(async () => {
const session = await useAuthSession()
const account = session.data.account?.[session.data.current ?? ""]
const email = account?.email
const customer = await Database.use((tx) =>
tx
.select({
customerID: BillingTable.customerID,
subscriptionID: BillingTable.subscriptionID,
})
.from(BillingTable)
.where(eq(BillingTable.workspaceID, workspaceID))
.then((rows) => rows[0]),
)
if (customer?.subscriptionID) {
return { error: formError.alreadySubscribed }
}
let customerID = customer?.customerID
if (!customerID) {
const customer = await Billing.stripe().customers.create({
email,
metadata: {
workspaceID,
},
})
customerID = customer.id
await Database.use((tx) =>
tx
.update(BillingTable)
.set({
customerID,
})
.where(eq(BillingTable.workspaceID, workspaceID)),
)
}
const intent = await Billing.stripe().setupIntents.create({
customer: customerID,
payment_method_types: ["card"],
metadata: {
workspaceID,
},
})
return { clientSecret: intent.client_secret ?? undefined }
}, workspaceID)
}
const bookSubscription = async (input: {
workspaceID: string
plan: PlanID
paymentMethodID: string
paymentMethodType: string
paymentMethodLast4?: string
}) => {
"use server"
return withActor(
() =>
Database.use((tx) =>
tx
.update(BillingTable)
.set({
paymentMethodID: input.paymentMethodID,
paymentMethodType: input.paymentMethodType,
paymentMethodLast4: input.paymentMethodLast4,
subscriptionPlan: input.plan,
timeSubscriptionBooked: new Date(),
})
.where(eq(BillingTable.workspaceID, input.workspaceID)),
),
input.workspaceID,
)
}
interface SuccessData {
plan: string
paymentMethodType: string
paymentMethodLast4?: string
}
function Failure(props: { message: string }) {
const i18n = useI18n()
return (
<div data-slot="failure">
<p data-slot="message">
{i18n.t("black.subscribe.failurePrefix")} {props.message}
</p>
</div>
)
}
function Success(props: SuccessData) {
const i18n = useI18n()
return (
<div data-slot="success">
<p data-slot="title">{i18n.t("black.subscribe.success.title")}</p>
<dl data-slot="details">
<div>
<dt>{i18n.t("black.subscribe.success.subscriptionPlan")}</dt>
<dd>{i18n.t("black.subscribe.success.planName", { plan: props.plan })}</dd>
</div>
<div>
<dt>{i18n.t("black.subscribe.success.amount")}</dt>
<dd>{i18n.t("black.subscribe.success.amountValue", { plan: props.plan })}</dd>
</div>
<div>
<dt>{i18n.t("black.subscribe.success.paymentMethod")}</dt>
<dd>
<Show when={props.paymentMethodLast4} fallback={<span>{props.paymentMethodType}</span>}>
<span>
{props.paymentMethodType} - {props.paymentMethodLast4}
</span>
</Show>
</dd>
</div>
<div>
<dt>{i18n.t("black.subscribe.success.dateJoined")}</dt>
<dd>{new Date().toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" })}</dd>
</div>
</dl>
<p data-slot="charge-notice">{i18n.t("black.subscribe.success.chargeNotice")}</p>
</div>
)
}
function IntentForm(props: { plan: PlanID; workspaceID: string; onSuccess: (data: SuccessData) => void }) {
const i18n = useI18n()
const stripe = useStripe()
const elements = useElements()
const [error, setError] = createSignal<string | undefined>(undefined)
const [loading, setLoading] = createSignal(false)
const handleSubmit = async (e: Event) => {
e.preventDefault()
if (!stripe() || !elements()) return
setLoading(true)
setError(undefined)
const result = await elements()!.submit()
if (result.error) {
setError(result.error.message ?? i18n.t("black.subscribe.error.generic"))
setLoading(false)
return
}
const { error: confirmError, setupIntent } = await stripe()!.confirmSetup({
elements: elements()!,
confirmParams: {
expand: ["payment_method"],
payment_method_data: {
allow_redisplay: "always",
},
},
redirect: "if_required",
})
if (confirmError) {
setError(confirmError.message ?? i18n.t("black.subscribe.error.generic"))
setLoading(false)
return
}
if (setupIntent?.status === "succeeded") {
const pm = setupIntent.payment_method as PaymentMethod
await bookSubscription({
workspaceID: props.workspaceID,
plan: props.plan,
paymentMethodID: pm.id,
paymentMethodType: pm.type,
paymentMethodLast4: pm.card?.last4,
})
props.onSuccess({
plan: props.plan,
paymentMethodType: pm.type,
paymentMethodLast4: pm.card?.last4,
})
}
setLoading(false)
}
return (
<form onSubmit={handleSubmit} data-slot="checkout-form">
<PaymentElement />
<AddressElement options={{ mode: "billing" }} />
<Show when={error()}>
<p data-slot="error">{error()}</p>
</Show>
<button type="submit" disabled={loading() || !stripe() || !elements()} data-slot="submit-button">
{loading() ? i18n.t("black.subscribe.processing") : i18n.t("black.subscribe.submit", { plan: props.plan })}
</button>
<p data-slot="charge-notice">{i18n.t("black.subscribe.form.chargeNotice")}</p>
</form>
)
}
export default function BlackSubscribe() {
const params = useParams()
const i18n = useI18n()
const language = useLanguage()
const enabled = createAsync(() => getEnabled())
const planData = plansMap[(params.plan as PlanID) ?? "20"] ?? plansMap["20"]
const plan = planData.id
const workspaces = createAsync(() => getWorkspaces(plan))
const [selectedWorkspace, setSelectedWorkspace] = createSignal<string | undefined>(undefined)
const [success, setSuccess] = createSignal<SuccessData | undefined>(undefined)
const [failure, setFailure] = createSignal<string | undefined>(undefined)
const [clientSecret, setClientSecret] = createSignal<string | undefined>(undefined)
const [stripe, setStripe] = createSignal<Stripe | undefined>(undefined)
const formatError = (error: string) => {
if (error === formError.invalidPlan) return i18n.t("black.subscribe.error.invalidPlan")
if (error === formError.workspaceRequired) return i18n.t("black.subscribe.error.workspaceRequired")
if (error === formError.alreadySubscribed) return i18n.t("black.subscribe.error.alreadySubscribed")
if (error === "Invalid plan") return i18n.t("black.subscribe.error.invalidPlan")
if (error === "Workspace ID is required") return i18n.t("black.subscribe.error.workspaceRequired")
if (error === "This workspace already has a subscription") return i18n.t("black.subscribe.error.alreadySubscribed")
return error
}
// Resolve stripe promise once
createEffect(() => {
void stripePromise.then((s) => {
if (s) setStripe(s)
})
})
// Auto-select if only one workspace
createEffect(() => {
const ws = workspaces()
if (ws?.length === 1 && !selectedWorkspace()) {
setSelectedWorkspace(ws[0].id)
}
})
// Fetch setup intent when workspace is selected (unless workspace already has payment method)
createEffect(async () => {
const id = selectedWorkspace()
if (!id) return
const ws = workspaces()?.find((w) => w.id === id)
if (ws?.billing?.subscriptionID) {
setFailure(i18n.t("black.subscribe.error.alreadySubscribed"))
return
}
if (ws?.billing?.paymentMethodID) {
if (!ws?.billing?.timeSubscriptionBooked) {
await bookSubscription({
workspaceID: id,
plan: planData.id,
paymentMethodID: ws.billing.paymentMethodID!,
paymentMethodType: ws.billing.paymentMethodType!,
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
})
}
setSuccess({
plan: planData.id,
paymentMethodType: ws.billing.paymentMethodType!,
paymentMethodLast4: ws.billing.paymentMethodLast4 ?? undefined,
})
return
}
const result = await createSetupIntent({ plan, workspaceID: id })
if (result.error) {
setFailure(formatError(result.error))
} else if ("clientSecret" in result) {
setClientSecret(result.clientSecret)
}
})
// Keyboard navigation for workspace picker
const { active, setActive, onKeyDown } = createList({
items: () => workspaces()?.map((w) => w.id) ?? [],
initialActive: null,
})
const handleSelectWorkspace = (id: string) => {
setSelectedWorkspace(id)
}
let listRef: HTMLUListElement | undefined
// Show workspace picker if multiple workspaces and none selected
const showWorkspacePicker = () => {
const ws = workspaces()
return ws && ws.length > 1 && !selectedWorkspace()
}
return (
<Show when={enabled()}>
<Title>{i18n.t("black.subscribe.title")}</Title>
<section data-slot="subscribe-form">
<div data-slot="form-card">
<Switch>
<Match when={success()}>{(data) => <Success {...data()} />}</Match>
<Match when={failure()}>{(data) => <Failure message={data()} />}</Match>
<Match when={true}>
<>
<div data-slot="plan-header">
<p data-slot="title">{i18n.t("black.subscribe.title")}</p>
<p data-slot="price">
<span data-slot="amount">${planData.id}</span>{" "}
<span data-slot="period">{i18n.t("black.price.perMonth")}</span>
<Show when={planData.multiplier}>
{(multiplier) => <span data-slot="multiplier">{i18n.t(multiplier())}</span>}
</Show>
</p>
</div>
<div data-slot="divider" />
<p data-slot="section-title">{i18n.t("black.subscribe.paymentMethod")}</p>
<Show
when={clientSecret() && selectedWorkspace() && stripe()}
fallback={
<div data-slot="loading">
<p>
{selectedWorkspace()
? i18n.t("black.subscribe.loadingPaymentForm")
: i18n.t("black.subscribe.selectWorkspaceToContinue")}
</p>
</div>
}
>
<Elements
stripe={stripe()!}
options={{
clientSecret: clientSecret()!,
appearance: {
theme: "night",
variables: {
colorPrimary: "#ffffff",
colorBackground: "#1a1a1a",
colorText: "#ffffff",
colorTextSecondary: "#999999",
colorDanger: "#ff6b6b",
fontFamily: "JetBrains Mono, monospace",
borderRadius: "4px",
spacingUnit: "4px",
},
rules: {
".Input": {
backgroundColor: "#1a1a1a",
border: "1px solid rgba(255, 255, 255, 0.17)",
color: "#ffffff",
},
".Input:focus": {
borderColor: "rgba(255, 255, 255, 0.35)",
boxShadow: "none",
},
".Label": {
color: "rgba(255, 255, 255, 0.59)",
fontSize: "14px",
marginBottom: "8px",
},
},
},
}}
>
<IntentForm plan={plan} workspaceID={selectedWorkspace()!} onSuccess={setSuccess} />
</Elements>
</Show>
</>
</Match>
</Switch>
</div>
{/* Workspace picker modal */}
<Modal
open={showWorkspacePicker() ?? false}
onClose={() => {}}
title={i18n.t("black.workspace.selectPlan")}
variant="black"
>
<div data-component="black-workspace-picker-modal" data-slot="workspace-picker">
<ul
ref={listRef}
data-slot="workspace-list"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === "Enter" && active()) {
handleSelectWorkspace(active()!)
} else {
onKeyDown(e)
}
}}
>
<For each={workspaces()}>
{(workspace) => (
<li
data-slot="workspace-item"
data-active={active() === workspace.id}
onMouseEnter={() => setActive(workspace.id)}
onClick={() => handleSelectWorkspace(workspace.id)}
>
<span data-slot="selected-icon">[*]</span>
<span>{workspace.name || workspace.slug}</span>
</li>
)}
</For>
</ul>
</div>
</Modal>
<p data-slot="fine-print">
{i18n.t("black.finePrint.beforeTerms")} ·{" "}
<A href={language.route("/legal/terms-of-service")}>{i18n.t("black.finePrint.terms")}</A>
</p>
</section>
</Show>
)
}
+6 -10
View File
@@ -23,7 +23,7 @@ const checkLoggedIn = query(async () => {
}, "checkLoggedIn.get")
const models = [
{ name: "Grok 4.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" },
{ name: "Grok 4.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" },
{ name: "GPT 5.6 Luna", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention30" },
{ name: "GLM-5.3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "GLM-5.2", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
@@ -31,6 +31,7 @@ const models = [
{ name: "Kimi K3", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "Kimi K2.7 Code", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "Kimi K2.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "LongCat-2.0", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "MiMo-V2.5-Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "MiMo-V2.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
{ name: "Qwen3.8 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
@@ -69,14 +70,14 @@ function LimitsGraph(props: { href: string }) {
const baseline = 100
const graph = [
{ id: "kimi-k3", name: "Kimi K3", req: 110, d: "50ms" },
{ id: "grok-4.5", name: "Grok 4.5", req: 120, d: "75ms" },
{ id: "qwen3.8-max", name: "Qwen3.8 Max", req: 160, d: "90ms" },
{ id: "grok-4.6", name: "Grok 4.6", req: 169, d: "75ms" },
{ id: "glm-5.2", name: "GLM-5.2", req: 880, d: "100ms" },
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 1050, d: "150ms" },
{ id: "gpt-5.6-luna", name: "GPT 5.6 Luna", req: 2050, d: "290ms" },
{ id: "minimax-m3", name: "MiniMax M3", req: 3200, d: "210ms" },
{ id: "qwen3.7-plus", name: "Qwen3.7 Plus", req: 4300, d: "300ms" },
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 7600, d: "330ms" },
{ id: "longcat-2.0", name: "LongCat-2.0", req: 11400, d: "335ms" },
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, d: "340ms" },
{ id: "hy3", name: "Hy3", req: 34400, baseReq: 4300, d: "320ms" },
{ id: "muse-spark-1.2-contributor", name: "Muse Spark 1.2 Contributor", req: 45300, edge: true, d: "360ms" },
@@ -375,12 +376,7 @@ export default function Home() {
{(part) => {
if (part === "{{text}}") return <span>{i18n.t("go.cta.text")}</span>
if (part === "{{price}}") {
return (
<span data-slot="cta-price">
<span data-slot="cta-price-old">{i18n.t("go.cta.price")}</span>
<span data-slot="cta-price-new">{i18n.t("go.cta.promo")}</span>
</span>
)
return <span data-slot="cta-price">{i18n.t("go.cta.price")}</span>
}
return part
}}
@@ -515,7 +511,7 @@ export default function Home() {
</div>
<div data-slot="faq-retention-notes">
<p>
<strong>Grok 4.5:</strong> {i18n.t("go.faq.a5.grokRetention")}{" "}
<strong>Grok 4.6:</strong> {i18n.t("go.faq.a5.grokRetention")}{" "}
<a href="https://docs.x.ai/developers/faq/security#what-is-zero-data-retention-zdr">
{i18n.t("go.faq.a5.learnMore")}
</a>
@@ -237,9 +237,8 @@ export default function PrivacyPolicy() {
</td>
<td>
<ul>
<li>Providing, Customizing and Improving the Services</li>
<li>Marketing the Services</li>
<li>Corresponding with You</li>
<li>Passing through to upstream provider to provide services</li>
<li>Not stored</li>
</ul>
</td>
<td>
@@ -21,7 +21,7 @@ export default function TermsOfService() {
<section data-component="brand-content">
<article data-component="terms-of-service">
<h1>Terms of Use</h1>
<p class="effective-date">Effective date: Mar 6, 2026</p>
<p class="effective-date">Effective date: Aug 15, 2026</p>
<p>
Welcome to OpenCode. Please read on to learn the rules and restrictions that govern your use of
@@ -154,6 +154,11 @@ export default function TermsOfService() {
is dangerous, harmful, fraudulent, deceptive, threatening, harassing, defamatory, obscene, or
otherwise objectionable;
</li>
<li>
creates, maintains, or uses accounts in bulk, or creates, maintains, or uses multiple accounts to
circumvent usage limits, access restrictions, billing obligations, promotions, suspensions, or any
other restriction or policy applicable to the Services;
</li>
<li>automatically or programmatically extracts data or Output (defined below);</li>
<li>Represent that the Output was human-generated when it was not;</li>
<li>
@@ -205,6 +205,13 @@ export async function POST(input: APIEvent) {
} else if (productID === BlackData.productID()) {
await Billing.unsubscribeBlack({ subscriptionID })
}
const latestInvoice = body.data.object.latest_invoice
const invoiceID = typeof latestInvoice === "string" ? latestInvoice : latestInvoice?.id
if (invoiceID) {
const invoice = await Billing.stripe().invoices.retrieve(invoiceID)
if (invoice.status === "open") await Billing.stripe().invoices.voidInvoice(invoiceID)
}
}
if (body.type === "invoice.payment_succeeded") {
if (
@@ -19,6 +19,7 @@
[data-slot="usage-item"] {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
@@ -60,6 +61,119 @@
color: var(--color-text-muted);
}
[data-slot="usage-details"] {
margin-top: var(--space-1);
}
[data-slot="usage-details-trigger"] {
display: inline-flex;
align-items: center;
gap: var(--space-2);
width: fit-content;
padding: 0;
border: 0;
background-color: transparent;
color: var(--color-text-secondary);
font-size: var(--font-size-sm);
cursor: pointer;
&:hover:not(:disabled) {
background-color: transparent;
border-color: transparent;
}
svg {
transition: transform 0.2s ease;
}
&[aria-expanded="true"] svg {
transform: rotate(180deg);
}
[data-slot="hide-details"] {
display: none;
}
&[aria-expanded="true"] [data-slot="show-details"] {
display: none;
}
&[aria-expanded="true"] [data-slot="hide-details"] {
display: inline;
}
}
[data-slot="usage-details-content"] {
width: 100%;
margin-top: var(--space-3);
}
[data-slot="usage-details-loading"] {
width: 100%;
margin-top: var(--space-3);
color: var(--color-text-muted);
font-size: var(--font-size-sm);
}
[data-slot="usage-details-empty"] {
margin: 0;
color: var(--color-text-muted);
font-size: var(--font-size-sm);
}
[data-slot="usage-details-table"] {
overflow-x: auto;
border: 1px solid var(--color-border-muted);
border-radius: var(--border-radius-sm);
table {
width: 100%;
min-width: 28rem;
table-layout: fixed;
border-collapse: collapse;
font-size: var(--font-size-sm);
white-space: nowrap;
}
th,
td {
width: 25%;
padding: var(--space-2) var(--space-3);
border-bottom: 1px solid var(--color-border-muted);
text-align: end;
font-variant-numeric: tabular-nums;
}
th {
color: var(--color-text-muted);
font-size: var(--font-size-xs);
font-weight: 500;
text-transform: uppercase;
}
th:first-child,
td:first-child {
text-align: start;
}
td:first-child {
max-width: 13rem;
overflow: hidden;
color: var(--color-text);
text-overflow: ellipsis;
}
tbody tr:last-child td {
border-bottom: 0;
}
[data-slot="usage-total"] td {
border-top: 1px solid var(--color-border);
color: var(--color-text);
font-weight: 600;
}
}
[data-slot="setting-row"] {
display: flex;
align-items: center;
@@ -1,15 +1,19 @@
import { action, useParams, useAction, useSubmission, json, query, createAsync } from "@solidjs/router"
import { createStore } from "solid-js/store"
import { createMemo, For, Show } from "solid-js"
import { createMemo, createSignal, For, Show } from "solid-js"
import { Modal } from "~/component/modal"
import { Billing } from "@opencode-ai/console-core/billing.js"
import { Database, eq, and, isNull } from "@opencode-ai/console-core/drizzle/index.js"
import { BillingTable, LiteTable } from "@opencode-ai/console-core/schema/billing.sql.js"
import { Database, eq, and, gte, isNull, sql } from "@opencode-ai/console-core/drizzle/index.js"
import { BillingTable, LiteTable, UsageTable } from "@opencode-ai/console-core/schema/billing.sql.js"
import { KeyTable } from "@opencode-ai/console-core/schema/key.sql.js"
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
import { Actor } from "@opencode-ai/console-core/actor.js"
import { Workspace } from "@opencode-ai/console-core/workspace.js"
import { Subscription } from "@opencode-ai/console-core/subscription.js"
import { LiteData } from "@opencode-ai/console-core/lite.js"
import { ZenData } from "@opencode-ai/console-core/model.js"
import { getMonthlyBounds, getWeekBounds } from "@opencode-ai/console-core/util/date.js"
import { centsToMicroCents } from "@opencode-ai/console-core/util/price.js"
import { withActor } from "~/context/auth.withActor"
import { queryBillingInfo } from "../../common"
import styles from "./lite-section.module.css"
@@ -20,8 +24,12 @@ import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time"
import { createReferralFromCookie } from "~/lib/referral-invite"
import { getRequestEvent } from "solid-js/web"
import { countryFromRequest } from "~/lib/request-country"
import { checkCheckoutRateLimit } from "~/routes/zen/util/redis"
import { IconAlipay, IconUpi } from "~/component/icon"
import { IconAlipay, IconChevron, IconUpi } from "~/component/icon"
import { buildLiteUsageBreakdown, getModelQuotaLimit, getUsagePercent } from "~/lib/lite-usage"
type LiteUsageWindow = "rolling" | "weekly" | "monthly"
export const queryLiteSubscription = query(async (workspaceID: string) => {
"use server"
@@ -51,6 +59,33 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
const limits = LiteData.getLimits()
const mine = row.userID === Actor.userID()
const now = new Date()
const rollingCutoff = new Date(now.getTime() - limits.rollingWindow * 3600 * 1000)
const week = getWeekBounds(now)
const month = getMonthlyBounds(now, row.timeCreated)
const rollingActive = !!row.timeRollingUpdated && row.timeRollingUpdated >= rollingCutoff
const weeklyActive = !!row.timeWeeklyUpdated && row.timeWeeklyUpdated >= week.start
const monthlyActive = !!row.timeMonthlyUpdated && row.timeMonthlyUpdated >= month.start
const rollingLimit = centsToMicroCents(limits.rollingLimit * 100)
const weeklyLimit = centsToMicroCents(limits.weeklyLimit * 100)
const monthlyLimit = centsToMicroCents(limits.monthlyLimit * 100)
const rollingUsage = Subscription.analyzeRollingUsage({
limit: limits.rollingLimit,
window: limits.rollingWindow,
usage: row.rollingUsage ?? 0,
timeUpdated: row.timeRollingUpdated ?? now,
})
const weeklyUsage = Subscription.analyzeWeeklyUsage({
limit: limits.weeklyLimit,
usage: row.weeklyUsage ?? 0,
timeUpdated: row.timeWeeklyUpdated ?? now,
})
const monthlyUsage = Subscription.analyzeMonthlyUsage({
limit: limits.monthlyLimit,
usage: row.monthlyUsage ?? 0,
timeUpdated: row.timeMonthlyUpdated ?? now,
timeSubscribed: row.timeCreated,
})
return {
mine,
@@ -58,27 +93,126 @@ export const queryLiteSubscription = query(async (workspaceID: string) => {
allowTraining: row.allowTraining ?? false,
region:
row.region ?? (await Workspace.setDefaultRegion({ country: countryFromRequest(getRequestEvent()?.request) })),
rollingUsage: Subscription.analyzeRollingUsage({
limit: limits.rollingLimit,
window: limits.rollingWindow,
usage: row.rollingUsage ?? 0,
timeUpdated: row.timeRollingUpdated ?? new Date(),
}),
weeklyUsage: Subscription.analyzeWeeklyUsage({
limit: limits.weeklyLimit,
usage: row.weeklyUsage ?? 0,
timeUpdated: row.timeWeeklyUpdated ?? new Date(),
}),
monthlyUsage: Subscription.analyzeMonthlyUsage({
limit: limits.monthlyLimit,
usage: row.monthlyUsage ?? 0,
timeUpdated: row.timeMonthlyUpdated ?? new Date(),
timeSubscribed: row.timeCreated,
}),
rollingUsage: {
...rollingUsage,
usage: rollingActive ? (row.rollingUsage ?? 0) : 0,
limit: rollingLimit,
usagePercent: getUsagePercent(rollingActive ? (row.rollingUsage ?? 0) : 0, rollingLimit),
},
weeklyUsage: {
...weeklyUsage,
usage: weeklyActive ? (row.weeklyUsage ?? 0) : 0,
limit: weeklyLimit,
usagePercent: getUsagePercent(weeklyActive ? (row.weeklyUsage ?? 0) : 0, weeklyLimit),
},
monthlyUsage: {
...monthlyUsage,
usage: monthlyActive ? (row.monthlyUsage ?? 0) : 0,
limit: monthlyLimit,
usagePercent: getUsagePercent(monthlyActive ? (row.monthlyUsage ?? 0) : 0, monthlyLimit),
},
}
}, workspaceID)
}, "lite.subscription.get")
export const queryLiteUsageDetails = query(async (workspaceID: string, window: LiteUsageWindow) => {
"use server"
return withActor(async () => {
if (window !== "rolling" && window !== "weekly" && window !== "monthly") return null
const row = await Database.use((tx) =>
tx
.select({
userID: LiteTable.userID,
rollingUsage: LiteTable.rollingUsage,
weeklyUsage: LiteTable.weeklyUsage,
monthlyUsage: LiteTable.monthlyUsage,
timeRollingUpdated: LiteTable.timeRollingUpdated,
timeWeeklyUpdated: LiteTable.timeWeeklyUpdated,
timeMonthlyUpdated: LiteTable.timeMonthlyUpdated,
timeCreated: LiteTable.timeCreated,
})
.from(LiteTable)
.where(and(eq(LiteTable.workspaceID, Actor.workspace()), isNull(LiteTable.timeDeleted)))
.then((result) => result[0]),
)
if (!row || row.userID !== Actor.userID()) return null
const limits = LiteData.getLimits()
const now = new Date()
const detail = (() => {
if (window === "rolling") {
const active =
!!row.timeRollingUpdated &&
row.timeRollingUpdated >= new Date(now.getTime() - limits.rollingWindow * 3600 * 1000)
return {
start: active ? row.timeRollingUpdated! : now,
usage: active ? (row.rollingUsage ?? 0) : 0,
limit: centsToMicroCents(limits.rollingLimit * 100),
}
}
if (window === "weekly") {
const start = getWeekBounds(now).start
return {
start,
usage: row.timeWeeklyUpdated && row.timeWeeklyUpdated >= start ? (row.weeklyUsage ?? 0) : 0,
limit: centsToMicroCents(limits.weeklyLimit * 100),
}
}
const start = getMonthlyBounds(now, row.timeCreated).start
return {
start,
usage: row.timeMonthlyUpdated && row.timeMonthlyUpdated >= start ? (row.monthlyUsage ?? 0) : 0,
limit: centsToMicroCents(limits.monthlyLimit * 100),
}
})()
const modelData = Object.fromEntries(
Object.entries(ZenData.list("lite").models).map(([id, value]) => {
const models = Array.isArray(value) ? value : [value]
const multipliers = new Set(models.map((model) => model.costMultiplier))
return [id, { name: models[0].name, multiplier: multipliers.size === 1 ? models[0].costMultiplier : undefined }]
}),
)
const usageRows = await Database.use((tx) =>
tx
.select({
model: UsageTable.model,
multiplier: sql<string | null>`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier'))`,
cost: sql<string>`SUM(${UsageTable.cost})`,
quotaCost: sql<string>`SUM(CASE WHEN JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier') IS NOT NULL THEN ROUND(${UsageTable.cost} * CAST(JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier')) AS DECIMAL(20, 8))) ELSE 0 END)`,
})
.from(UsageTable)
.innerJoin(KeyTable, and(eq(KeyTable.id, UsageTable.keyID), eq(KeyTable.workspaceID, UsageTable.workspaceID)))
.where(
and(
eq(UsageTable.workspaceID, Actor.workspace()),
eq(KeyTable.userID, row.userID),
gte(UsageTable.timeCreated, detail.start),
sql`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.plan')) = 'lite'`,
),
)
.groupBy(UsageTable.model, sql`JSON_UNQUOTE(JSON_EXTRACT(${UsageTable.enrichment}, '$.costMultiplier'))`),
)
return buildLiteUsageBreakdown({
usage: detail.usage,
limit: detail.limit,
sources: usageRows.map((usage) => {
const cost = Number(usage.cost)
const info = modelData[usage.model]
const multiplier = usage.multiplier === null ? info?.multiplier : Number(usage.multiplier)
return {
model: usage.model,
name: info?.name ?? usage.model,
cost,
quotaCost: usage.multiplier === null ? Math.round(cost * (multiplier ?? 1)) : Number(usage.quotaCost),
multiplier,
estimated: usage.multiplier === null,
}
}),
})
}, workspaceID)
}, "lite.subscription.usage")
type LiteSubscription = Awaited<ReturnType<typeof queryLiteSubscription>>
const createLiteCheckoutUrl = action(
@@ -86,6 +220,7 @@ const createLiteCheckoutUrl = action(
"use server"
return json(
await withActor(async () => {
await checkCheckoutRateLimit(Actor.account())
const data = await Billing.generateLiteCheckoutUrl({ successUrl, cancelUrl, method })
await createReferralFromCookie()
return { error: undefined, data }
@@ -174,7 +309,16 @@ const setGoAllowTraining = action(async (form: FormData) => {
)
}, "go.allowTraining.set")
function LiteUsageItem(props: { label: string; usage: { usagePercent: number; resetInSec: number } }) {
type LiteUsage = NonNullable<LiteSubscription>["rollingUsage"]
type LiteUsageDetailsData = NonNullable<Awaited<ReturnType<typeof queryLiteUsageDetails>>>
function LiteUsageItem(props: {
id: LiteUsageWindow
label: string
usage: LiteUsage
open: boolean
onToggle: () => void
}) {
const i18n = useI18n()
return (
@@ -183,17 +327,178 @@ function LiteUsageItem(props: { label: string; usage: { usagePercent: number; re
<span data-slot="usage-label">{props.label}</span>
<span data-slot="usage-value">{props.usage.usagePercent}%</span>
</div>
<div data-slot="progress">
<div data-slot="progress-bar" style={{ width: `${props.usage.usagePercent}%` }} />
<div
data-slot="progress"
role="progressbar"
aria-label={props.label}
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow={Math.min(100, props.usage.usagePercent)}
>
<div data-slot="progress-bar" style={{ width: `${Math.min(100, props.usage.usagePercent)}%` }} />
</div>
<span data-slot="reset-time">
{i18n.t("workspace.lite.subscription.resetsIn")}{" "}
{formatResetTime(props.usage.resetInSec, i18n, liteResetTimeKeys)}
</span>
<Show when={props.usage.usage > 0}>
<div data-slot="usage-details">
<button
type="button"
data-slot="usage-details-trigger"
aria-expanded={props.open}
aria-controls={`usage-details-${props.id}`}
onClick={props.onToggle}
>
<span data-slot="show-details">{i18n.t("workspace.lite.subscription.showDetails")}</span>
<span data-slot="hide-details">{i18n.t("workspace.lite.subscription.hideDetails")}</span>
<IconChevron />
</button>
</div>
</Show>
</div>
)
}
function LiteUsageDetails(props: {
id: LiteUsageWindow
label: string
quotaLabel: string
usage: LiteUsageDetailsData
}) {
const i18n = useI18n()
const language = useLanguage()
const money = (amount: number) =>
new Intl.NumberFormat(language.tag(language.locale()), {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 4,
}).format(amount / 100_000_000)
const totalPercentage = () =>
Number(props.usage.rows.reduce((total, row) => total + row.contributionPercent, 0).toFixed(1))
return (
<div id={`usage-details-${props.id}`} data-slot="usage-details-content" role="region" aria-label={props.label}>
<div data-slot="usage-details-table">
<table>
<thead>
<tr>
<th>{i18n.t("workspace.lite.subscription.model")}</th>
<th>{props.label}</th>
<th>{props.quotaLabel}</th>
<th>{i18n.t("workspace.lite.subscription.contribution")}</th>
</tr>
</thead>
<tbody>
<For each={props.usage.rows}>
{(row) => {
const quota = getModelQuotaLimit(props.usage.limit, row.multiplier)
return (
<tr>
<td>
<bdi dir="auto">{row.name}</bdi>
</td>
<td>{row.cost === undefined ? "-" : money(row.cost)}</td>
<td>{quota === undefined ? "-" : money(quota)}</td>
<td>{row.contributionPercent}%</td>
</tr>
)
}}
</For>
<tr data-slot="usage-total">
<td colSpan={3}>{i18n.t("workspace.lite.subscription.total")}</td>
<td>{totalPercentage()}%</td>
</tr>
</tbody>
</table>
</div>
</div>
)
}
function LiteUsageGroup(props: { lite: NonNullable<LiteSubscription> }) {
const params = useParams()
const i18n = useI18n()
const [open, setOpen] = createSignal<LiteUsageWindow>()
const [store, setStore] = createStore({
details: {} as Partial<Record<LiteUsageWindow, LiteUsageDetailsData | null>>,
loading: undefined as LiteUsageWindow | undefined,
})
const items = () =>
[
{
id: "rolling",
label: i18n.t("workspace.lite.subscription.rollingUsage"),
quotaLabel: i18n.t("workspace.lite.subscription.rollingQuota"),
usage: props.lite.rollingUsage,
},
{
id: "weekly",
label: i18n.t("workspace.lite.subscription.weeklyUsage"),
quotaLabel: i18n.t("workspace.lite.subscription.weeklyQuota"),
usage: props.lite.weeklyUsage,
},
{
id: "monthly",
label: i18n.t("workspace.lite.subscription.monthlyUsage"),
quotaLabel: i18n.t("workspace.lite.subscription.monthlyQuota"),
usage: props.lite.monthlyUsage,
},
] as const
const selected = createMemo(() => items().find((item) => item.id === open()))
async function toggle(id: LiteUsageWindow) {
if (open() === id) {
setOpen()
return
}
setOpen(id)
if (store.details[id] !== undefined) return
setStore("loading", id)
const details = await queryLiteUsageDetails(params.id!, id).catch(() => null)
setStore("details", id, details)
setStore("loading", (current) => (current === id ? undefined : current))
}
return (
<>
<div data-slot="usage">
<For each={items()}>
{(item) => (
<LiteUsageItem
id={item.id}
label={item.label}
usage={item.usage}
open={open() === item.id}
onToggle={() => toggle(item.id)}
/>
)}
</For>
</div>
<Show when={selected()}>
{(item) => {
const details = () => store.details[item().id]
return (
<Show
when={details()}
fallback={
<Show when={store.loading === item().id}>
<div data-slot="usage-details-loading">{i18n.t("workspace.lite.loading")}</div>
</Show>
}
>
{(usage) => (
<LiteUsageDetails id={item().id} label={item().label} quotaLabel={item().quotaLabel} usage={usage()} />
)}
</Show>
)
}}
</Show>
</>
)
}
export function LiteSection(props: { lite: LiteSubscription | undefined }) {
const params = useParams()
const i18n = useI18n()
@@ -261,11 +566,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
</a>
.
</div>
<div data-slot="usage">
<LiteUsageItem label={i18n.t("workspace.lite.subscription.rollingUsage")} usage={sub().rollingUsage} />
<LiteUsageItem label={i18n.t("workspace.lite.subscription.weeklyUsage")} usage={sub().weeklyUsage} />
<LiteUsageItem label={i18n.t("workspace.lite.subscription.monthlyUsage")} usage={sub().monthlyUsage} />
</div>
<LiteUsageGroup lite={sub()} />
<form action={setLiteUseBalance} method="post" data-slot="setting-row">
<p>{i18n.t("workspace.lite.subscription.useBalance")}</p>
<input type="hidden" name="workspaceID" value={params.id} />
@@ -339,7 +640,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
</p>
<h3 data-slot="promo-models-title">{i18n.t("workspace.lite.promo.modelsTitle")}</h3>
<ul data-slot="promo-models">
<li>Grok 4.5</li>
<li>Grok 4.6</li>
<li>GPT 5.6 Luna</li>
<li>GLM-5.3</li>
<li>GLM-5.2</li>
@@ -347,6 +648,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
<li>Kimi K3</li>
<li>Kimi K2.7 Code</li>
<li>Kimi K2.6</li>
<li>LongCat-2.0</li>
<li>MiniMax M3</li>
<li>MiniMax M2.7</li>
<li>Muse Spark 1.2 Contributor</li>
@@ -6,6 +6,7 @@ import { Billing } from "@opencode-ai/console-core/billing.js"
import { and, Database, desc, eq, isNull } from "@opencode-ai/console-core/drizzle/index.js"
import { WorkspaceTable } from "@opencode-ai/console-core/schema/workspace.sql.js"
import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
import { checkCheckoutRateLimit } from "~/routes/zen/util/redis"
export function formatDateForTable(date: Date) {
const options: Intl.DateTimeFormatOptions = {
@@ -77,7 +78,8 @@ export const createCheckoutUrl = action(
return json(
await withActor(
() =>
Billing.generateCheckoutUrl({ amount, successUrl, cancelUrl })
checkCheckoutRateLimit(Actor.account())
.then(() => Billing.generateCheckoutUrl({ amount, successUrl, cancelUrl }))
.then((data) => ({ error: undefined, data }))
.catch((e) => ({
error: e.message as string,
@@ -28,13 +28,7 @@ import {
GoUsageLimitError,
BlackUsageLimitError,
} from "./error"
import {
buildCostChunk,
createBodyConverter,
createStreamPartConverter,
createResponseConverter,
UsageInfo,
} from "./provider/provider"
import { buildCostChunk, createStreamPartConverter, createResponseConverter, UsageInfo } from "./provider/provider"
import { anthropicHelper } from "./provider/anthropic"
import { googleHelper } from "./provider/google"
import { openaiHelper } from "./provider/openai"
@@ -53,12 +47,11 @@ import { createProviderBudgetTracker } from "./providerBudgetTracker"
import { accumulateUsage, HOT_WORKSPACES } from "./usageBatcher"
import { Workspace } from "@opencode-ai/console-core/workspace.js"
import { countryFromRequest, isModelCountryRestricted } from "~/lib/request-country"
import { isPeakPricing } from "./pricing"
import { prepareRequestBody } from "./requestBody"
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
type RetryOptions = {
excludeProviders: string[]
retryCount: number
}
type PreparedBody = Awaited<ReturnType<typeof prepareRequestBody>>
type BillingSource = "anonymous" | "free" | "byok" | "subscription" | "lite" | "balance"
function resolve(text: string, params?: Record<string, string | number>) {
@@ -86,8 +79,6 @@ export async function handler(
type ProviderInfo = Awaited<ReturnType<typeof selectProvider>>
type CostInfo = ReturnType<typeof calculateCost>
const MAX_FAILOVER_RETRIES = 3
const MAX_RETRYABLE_STATUS_RETRIES = 3
const dict = i18n(localeFromRequest(input.request))
const t = (key: Key, params?: Record<string, string | number>) => resolve(dict[key], params)
const ADMIN_WORKSPACES = [
@@ -96,12 +87,14 @@ export async function handler(
"wrk_01KKZDKDWCS1VTJF8QTX62DD50", // contributors
]
let requestBody: PreparedBody | undefined
try {
const url = input.request.url
const body = await input.request.json()
const model = opts.parseModel(url, body)
const variant = opts.parseVariant(url, body)
const isStream = opts.parseIsStream(url, body)
const body = input.request.body
if (!body) throw new Error("Missing request body")
requestBody = opts.format === "google" ? undefined : await prepareRequestBody(body)
const model = opts.format === "google" ? opts.parseModel(url, undefined) : (requestBody?.model ?? "")
const googleStream = opts.format === "google" ? opts.parseIsStream(url, undefined) : undefined
const rawIp = input.request.headers.get("x-real-ip") ?? ""
const ip = rawIp.includes(":") ? rawIp.split(":").slice(0, 4).join(":") : rawIp
const rawZenApiKey = opts.parseApiKey(input.request.headers)
@@ -112,12 +105,10 @@ export async function handler(
const projectId = input.request.headers.get("x-opencode-project") ?? ""
const userAgent = input.request.headers.get("user-agent") ?? ""
logger.metric({
is_stream: isStream,
session: sessionId,
request: requestId,
client: ocClient,
user_agent: userAgent,
"model.variant": variant,
"model.tier": opts.modelList === "full" ? "zen" : "go",
})
const zenData = ZenData.list(opts.modelList)
@@ -175,7 +166,7 @@ export async function handler(
)
const providerBudget = await providerBudgetTracker?.check()
const retriableRequest = async (retry: RetryOptions = { excludeProviders: [], retryCount: 0 }) => {
const providerRequest = async () => {
const providerInfo = selectProvider(
model,
zenData,
@@ -183,7 +174,6 @@ export async function handler(
modelInfo,
stickyId,
trialProviders,
retry,
stickyProvider,
modelTpmLimits,
modelTpsLimits,
@@ -199,80 +189,73 @@ export async function handler(
})
const startTimestamp = Date.now()
const reqUrl = providerInfo.modifyUrl(providerInfo.api, isStream)
const reqBody = JSON.stringify(
providerInfo.modifyBody({
...createBodyConverter(opts.format, providerInfo.format)(body),
model: providerInfo.model,
...(() => {
const replacer = (obj: Record<string, any>): Record<string, any> =>
Object.fromEntries(
Object.entries(obj).flatMap(([k, v]) => {
if (Array.isArray(v)) return [[k, v]]
if (typeof v === "object") return [[k, replacer(v)]]
if (typeof v === "string") {
if (v === "$workspace") return authInfo?.workspaceID ? [[k, authInfo.workspaceID]] : []
if (v === "$org")
return authInfo?.workspaceID ? [[k, authInfo.workspaceID.replace("wrk_", "org_")]] : []
if (v === "$user") return stickyId ? [[k, stickyId]] : []
if (v.startsWith("$header.")) {
const headerValue = input.request.headers.get(v.slice(8))
return headerValue ? [[k, headerValue]] : []
}
}
return [[k, v]]
}),
)
return replacer(providerInfo.payloadModifier ?? {})
})(),
}),
)
const reqUrl = providerInfo.modifyUrl(providerInfo.api, googleStream ?? false)
const specialAnthropic =
providerInfo.format === "anthropic" &&
(providerInfo.model.startsWith("arn:aws:bedrock:") ||
providerInfo.model.startsWith("global.anthropic.") ||
providerInfo.model.startsWith("databricks-claude-"))
if (providerInfo.format !== opts.format) throw new Error("Zen provider format must match request format")
if (specialAnthropic) throw new Error("Anthropic provider body modifiers are incompatible with streaming")
const prepared = requestBody
const reqBody = (() => {
if (opts.format === "google") return body
if (!prepared) throw new Error("Missing prepared request body")
return prepared.stream(providerInfo.model, providerInfo.format === "oa-compat")
})()
logger.debug("REQUEST URL: " + reqUrl)
logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
logger.debug("REQUEST: " + (requestBody?.preview ?? "") + "...")
const isNewInference =
providerInfo.id.startsWith("console.") ||
providerInfo.id.startsWith("console-go.") ||
providerInfo.id.startsWith("inf.") ||
providerInfo.id.startsWith("inf-go.")
const res = await fetchWithRetryableStatus(
reqUrl,
{
method: "POST",
headers: (() => {
const headers = new Headers(input.request.headers)
providerInfo.modifyHeaders(headers, providerInfo.apiKey, stickyId)
Object.entries(providerInfo.headerModifier ?? {}).forEach(([k, v]) => {
if (v === "$ip") return headers.set(k, ip)
if (v === "$caller") return headers.set(k, stickyId)
if (v === "$session") return headers.set(k, sessionId)
if (v === "$model") return headers.set(k, model)
if (v === "$request") return headers.set(k, requestId)
if (v === "$project") return headers.set(k, projectId)
if (v === "$workspace") {
if (authInfo?.workspaceID) headers.set(k, authInfo.workspaceID)
return
}
if (v === "$org") {
if (authInfo?.workspaceID) headers.set(k, authInfo.workspaceID.replace("wrk_", "org_"))
return
}
headers.set(k, v)
})
headers.delete("host")
headers.delete("content-length")
headers.delete("x-opencode-request")
if (!isNewInference) headers.delete("x-opencode-session")
const res = await fetch(reqUrl, {
method: "POST",
headers: (() => {
const headers = new Headers(input.request.headers)
providerInfo.modifyHeaders(headers, providerInfo.apiKey, stickyId)
Object.entries(providerInfo.headerModifier ?? {}).forEach(([k, v]) => {
if (v === "$ip") return headers.set(k, ip)
if (v === "$caller") return headers.set(k, stickyId)
if (v === "$session") return headers.set(k, sessionId)
if (v === "$model") return headers.set(k, model)
if (v === "$request") return headers.set(k, requestId)
if (v === "$client") return headers.set(k, ocClient)
if (v === "$project") return headers.set(k, projectId)
if (v === "$workspace") {
if (authInfo?.workspaceID) headers.set(k, authInfo.workspaceID)
return
}
if (v === "$org") {
if (authInfo?.workspaceID) headers.set(k, authInfo.workspaceID.replace("wrk_", "org_"))
return
}
headers.set(k, v)
})
if (isNewInference) {
headers.set("x-zen-model", model)
}
headers.delete("host")
headers.delete("content-length")
if (!isNewInference) {
headers.delete("x-opencode-session")
headers.delete("x-opencode-project")
headers.delete("x-opencode-client")
return headers
})(),
body: reqBody,
// Propagate caller disconnects to the upstream provider request so
// abandoned Console requests do not leave orphaned inference work open.
signal: input.request.signal,
},
{ count: isNewInference ? MAX_RETRYABLE_STATUS_RETRIES : 0 },
)
headers.delete("x-opencode-request")
headers.delete("x-zen-model")
}
return headers
})(),
body: reqBody,
duplex: "half",
// Propagate caller disconnects to the upstream provider request so
// abandoned Console requests do not leave orphaned inference work open.
signal: input.request.signal,
} as RequestInit & { duplex: "half" })
const isStream = res.headers.get("content-type")?.toLowerCase().includes("text/event-stream") ?? false
logger.metric({ is_stream: isStream })
if (isNewInference) {
const resEndpointId = res.headers.get("x-opencode-endpoint-id")
@@ -291,29 +274,10 @@ export async function handler(
})
}
// Try another provider => stop retrying if using fallback provider
if (
//!isNewInference &&
res.status !== 200 &&
// ie. 400 error is usually provider error like malformed request
res.status !== 400 &&
// ie. openai 404 error: Item with id 'msg_0ead8b004a3b165d0069436a6b6834819896da85b63b196a3f' not found.
!(modelInfo.id.startsWith("gpt-") && res.status === 404) &&
// ie. cannot change codex model providers mid-session
modelInfo.stickyProvider !== "strict" &&
modelInfo.fallbackProvider &&
providerInfo.id !== modelInfo.fallbackProvider
) {
return retriableRequest({
excludeProviders: [...retry.excludeProviders, providerInfo.id],
retryCount: retry.retryCount + 1,
})
}
return { providerInfo, reqBody, res, startTimestamp }
return { providerInfo, res, startTimestamp, isStream }
}
const { providerInfo, reqBody, res, startTimestamp } = await retriableRequest()
const { providerInfo, res, startTimestamp, isStream } = await providerRequest()
// Store sticky provider
if (res.status === 200) await stickyTracker?.set(providerInfo.id)
@@ -469,6 +433,8 @@ export async function handler(
headers: resHeaders,
})
} catch (error: any) {
if (requestBody) void requestBody.cancel().catch(() => {})
else void input.request.body?.cancel().catch(() => {})
// The caller disconnected before we finished. Because the outbound provider
// request shares input.request.signal, an aborted caller surfaces here as an
// AbortError. There is no client left to receive a body, so skip the error
@@ -593,7 +559,6 @@ export async function handler(
modelInfo: ModelInfo,
stickyId: string,
trialProviders: string[] | undefined,
retry: RetryOptions,
stickyProviderId: string | undefined,
modelTpmLimits: Record<string, number> | undefined,
modelTpsLimits: Record<string, { qualify: number; unqualify: number }> | undefined,
@@ -620,14 +585,11 @@ export async function handler(
}))
}
// Use fallback provider if max retries reached
const fallbackProvider = allProviders.find((provider) => provider.id === modelInfo.fallbackProvider)
if (retry.retryCount === MAX_FAILOVER_RETRIES) return fallbackProvider
let topPriority = Infinity
const providers = allProviders
.filter((provider) => provider.weight !== 0)
.filter((provider) => !retry.excludeProviders.includes(provider.id))
.filter((provider) => {
if (provider.budgetPriority === undefined) return true
if (!providerBudget) return true
@@ -910,6 +872,8 @@ export async function handler(
// Validate lite subscription billing
if (opts.modelList === "lite" && authInfo.billing.lite && authInfo.lite) {
if (Object.values(modelInfo.cost).every((price) => price === 0)) return "lite"
try {
const consoleGoUrl = `https://opencode.ai/workspace/${authInfo.workspaceID}/go`
const sub = authInfo.lite
@@ -1035,22 +999,12 @@ export async function handler(
providerInfo.apiKey = authInfo.provider.credentials
}
async function fetchWithRetryableStatus(url: string, options: RequestInit, retry = { count: 0 }) {
const res = await fetch(url, options)
if ([429, 529].includes(res.status) && retry.count < MAX_RETRYABLE_STATUS_RETRIES) {
await new Promise((resolve) => setTimeout(resolve, Math.pow(2, retry.count) * 500))
return fetchWithRetryableStatus(url, options, { count: retry.count + 1 })
}
return res
}
function calculateCost(modelInfo: ModelInfo, usageInfo: UsageInfo) {
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
usageInfo
const hour = new Date().getUTCHours()
const modelCost =
modelInfo.costPeak && ((hour >= 1 && hour < 4) || (hour >= 6 && hour < 10))
modelInfo.costPeak && isPeakPricing(new Date())
? modelInfo.costPeak
: modelInfo.cost200K &&
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000
@@ -1160,7 +1114,7 @@ export async function handler(
enrichment: (() => {
if (billingSource === "subscription") return { plan: "sub" }
if (billingSource === "byok") return { plan: "byok" }
if (billingSource === "lite") return { plan: "lite" }
if (billingSource === "lite") return { plan: "lite", costMultiplier: modelInfo.costMultiplier }
return undefined
})(),
}),
@@ -0,0 +1,11 @@
export function isPeakPricing(date: Date) {
// DeepSeek peak pricing in China Standard Time (UTC+8):
// - Weekdays only
// - 9 AM to noon
// - 2 PM to 6 PM
const dateCN = new Date(date.getTime() + 8 * 3_600 * 1000)
const dayCN = dateCN.getUTCDay()
if (dayCN === 0 || dayCN === 6) return false
const hourCN = dateCN.getUTCHours()
return (hourCN >= 9 && hourCN < 12) || (hourCN >= 14 && hourCN < 18)
}
@@ -16,3 +16,11 @@ export function getRedis() {
export function buildRateLimitKey(kind: string, identifier: string, interval?: string) {
return `${Resource.App.stage}:ratelimit:${kind}:${identifier}${interval ? `:${interval}` : ""}`
}
export async function checkCheckoutRateLimit(accountID: string) {
const redis = getRedis()
const key = buildRateLimitKey("checkout", accountID)
const count = await redis.incr(key)
if (count === 1) await redis.expire(key, 60 * 60)
if (count > 5) throw new Error("Too many payment attempts. Please try again later.")
}
@@ -0,0 +1,237 @@
const TAIL_LIMIT = 4 * 1024
const encoder = new TextEncoder()
export async function prepareRequestBody(body: ReadableStream<Uint8Array>) {
const reader = body.getReader()
const chunks: Uint8Array[] = []
const decoder = new TextDecoder()
let text = ""
let done = false
let bom = 0
let index = 0
let depth = 0
let stringStart = -1
let escaped = false
let phase: "key" | "colon" | "value" | "comma" = "key"
let key = ""
let found: { model: string; start: number; end: number } | undefined
const scan = () => {
while (index < text.length && !found) {
const char = text[index]
if (stringStart >= 0) {
if (escaped) escaped = false
else if (char === "\\") escaped = true
else if (char === '"') {
if (depth === 1 && phase === "key") {
key = JSON.parse(text.slice(stringStart, index + 1))
phase = "colon"
} else if (depth === 1 && phase === "value") {
if (key === "model") {
const start = bom + utf8Length(text, stringStart + 1)
found = {
model: JSON.parse(text.slice(stringStart, index + 1)),
start,
end: bom + utf8Length(text, index),
}
}
phase = "comma"
}
stringStart = -1
}
index++
continue
}
if (char === '"') {
stringStart = index++
continue
}
if (char === "{" || char === "[") {
if (depth === 1 && phase === "value") phase = "comma"
depth++
index++
continue
}
if (char === "}" || char === "]") {
depth--
index++
continue
}
if (depth !== 1) {
index++
continue
}
if (char === ":" && phase === "colon") phase = "value"
else if (char === "," && phase === "comma") phase = "key"
else if (phase === "value" && !/\s/.test(char)) phase = "comma"
index++
}
}
while (!done && !found) {
const next = await reader.read()
done = next.done
if (!next.value) continue
if (!chunks.length && next.value[0] === 0xef && next.value[1] === 0xbb && next.value[2] === 0xbf) bom = 3
chunks.push(next.value)
text += decoder.decode(next.value, { stream: true })
scan()
}
if (done) {
text += decoder.decode()
scan()
}
const preview = text.substring(0, 300)
text = ""
let used = false
return {
model: found?.model ?? "",
preview,
cancel: () => reader.cancel(),
stream(providerModel: string, includeUsage: boolean) {
if (used) throw new Error("Request body stream already consumed")
if (!found) throw new Error("Missing model field")
used = true
const initial = replace(chunks, found.start, found.end, providerModel)
chunks.length = 0
const output = passthrough(initial, reader, done)
if (!includeUsage) return output
return appendUsage(output)
},
}
}
function utf8Length(value: string, end: number) {
let length = 0
for (let i = 0; i < end; i++) {
const code = value.charCodeAt(i)
if (code <= 0x7f) length++
else if (code <= 0x7ff) length += 2
else if (code >= 0xd800 && code <= 0xdbff && i + 1 < end && value.charCodeAt(i + 1) >= 0xdc00) {
length += 4
i++
} else length += 3
}
return length
}
function replace(chunks: Uint8Array[], start: number, end: number, value: string) {
let offset = 0
let inserted = false
return chunks.flatMap((chunk) => {
const chunkStart = offset
const chunkEnd = offset + chunk.length
offset = chunkEnd
if (chunkEnd <= start || chunkStart >= end) return [chunk]
const parts = [chunk.subarray(0, Math.max(0, start - chunkStart))]
if (!inserted) {
parts.push(encoder.encode(value))
inserted = true
}
parts.push(chunk.subarray(Math.min(chunk.length, end - chunkStart)))
return parts.filter((part) => part.length)
})
}
function passthrough(
initial: Array<Uint8Array | undefined>,
reader: ReadableStreamDefaultReader<Uint8Array>,
sourceDone: boolean,
) {
let done = sourceDone
let index = 0
return new ReadableStream<Uint8Array>({
async pull(controller) {
const chunk = initial[index]
if (chunk) {
initial[index++] = undefined
controller.enqueue(chunk)
return
}
initial.length = 0
if (done) {
controller.close()
return
}
const next = await reader.read()
done = next.done
if (next.value) controller.enqueue(next.value)
if (done) controller.close()
},
cancel(reason) {
initial.length = 0
return reader.cancel(reason)
},
})
}
function appendUsage(body: ReadableStream<Uint8Array>) {
const reader = body.getReader()
const decoder = new TextDecoder()
let tail = new Uint8Array()
let streamText = ""
let isStream = false
const inspect = (chunk?: Uint8Array) => {
streamText += chunk ? decoder.decode(chunk, { stream: true }) : decoder.decode()
for (const match of streamText.matchAll(/"stream"\s*:\s*(true|false)/g)) isStream = match[1] === "true"
streamText = streamText.slice(-64)
}
return new ReadableStream<Uint8Array>({
async pull(controller) {
while (true) {
const next = await reader.read()
if (next.done) {
inspect()
if (!isStream) {
if (tail.length) controller.enqueue(tail)
controller.close()
return
}
const close = tail.lastIndexOf(125)
if (close < 0) {
controller.error(new Error("Invalid JSON request body"))
return
}
if (close) controller.enqueue(tail.subarray(0, close))
controller.enqueue(encoder.encode(',"stream_options":{"include_usage":true}}'))
if (close + 1 < tail.length) controller.enqueue(tail.subarray(close + 1))
controller.close()
return
}
const chunk = next.value
inspect(chunk)
if (tail.length + chunk.length <= TAIL_LIMIT) {
const combined = new Uint8Array(tail.length + chunk.length)
combined.set(tail)
combined.set(chunk, tail.length)
tail = combined
continue
}
const emit = tail.length + chunk.length - TAIL_LIMIT
if (emit <= tail.length) {
controller.enqueue(tail.subarray(0, emit))
const combined = new Uint8Array(TAIL_LIMIT)
combined.set(tail.subarray(emit))
combined.set(chunk, tail.length - emit)
tail = combined
return
}
if (tail.length) controller.enqueue(tail)
controller.enqueue(chunk.subarray(0, emit - tail.length))
tail = chunk.slice(emit - tail.length)
return
}
},
cancel(reason) {
return reader.cancel(reason)
},
})
}
@@ -0,0 +1,75 @@
import { describe, expect, test } from "bun:test"
import { buildLiteUsageBreakdown, getModelQuotaLimit } from "../src/lib/lite-usage"
describe("Go usage breakdown", () => {
test("derives the model quota from the window limit and multiplier", () => {
expect(getModelQuotaLimit(30, 1)).toBe(30)
expect(getModelQuotaLimit(30, 2)).toBe(15)
expect(getModelQuotaLimit(30, 4)).toBe(7.5)
})
test("groups model quota usage into the percentage of the limit", () => {
const result = buildLiteUsageBreakdown({
usage: 416,
limit: 1_200,
sources: [
{ model: "glm", name: "GLM", cost: 200, quotaCost: 300, multiplier: 1.5, estimated: false },
{ model: "kimi", name: "Kimi", cost: 116, quotaCost: 116, multiplier: 1, estimated: false },
],
})
expect(result.usagePercent).toBe(34.7)
expect(result.rows[0]).toMatchObject({ name: "GLM", multiplier: 1.5, contributionPercent: 25 })
expect(result.rows.reduce((total, row) => total + row.contributionPercent, 0)).toBeCloseTo(result.usagePercent)
})
test("distributes credits across the model contributions", () => {
const result = buildLiteUsageBreakdown({
usage: 366,
limit: 1_200,
sources: [
{ model: "glm", name: "GLM", cost: 200, quotaCost: 300, multiplier: 1.5, estimated: false },
{ model: "kimi", name: "Kimi", cost: 116, quotaCost: 116, multiplier: 1, estimated: true },
],
})
expect(result.rows).toHaveLength(2)
expect(result.rows.every((row) => row.contributionPercent >= 0)).toBe(true)
expect(result.rows.reduce((total, row) => total + row.contributionPercent, 0)).toBeCloseTo(result.usagePercent)
})
test("does not synthesize a row when request history is unavailable", () => {
const result = buildLiteUsageBreakdown({ usage: 120, limit: 1_200, sources: [] })
expect(result.rows).toEqual([])
})
test("allocates rounded percentages without making positive rows negative", () => {
const sources = Array.from({ length: 20 }, (_, index) => ({
model: `model-${index}`,
name: `Model ${index}`,
cost: 4,
quotaCost: 4,
multiplier: 1,
estimated: false,
}))
const result = buildLiteUsageBreakdown({ usage: 80, limit: 10_000, sources })
expect(result.rows.every((row) => row.contributionPercent >= 0)).toBe(true)
expect(result.rows.reduce((total, row) => total + row.contributionPercent, 0)).toBeCloseTo(result.usagePercent)
})
test("keeps multiplier changes for the same model as separate rows", () => {
const result = buildLiteUsageBreakdown({
usage: 500,
limit: 1_000,
sources: [
{ model: "glm", name: "GLM", cost: 100, quotaCost: 100, multiplier: 1, estimated: false },
{ model: "glm", name: "GLM", cost: 200, quotaCost: 400, multiplier: 2, estimated: false },
],
})
expect(result.rows.map((row) => row.multiplier)).toEqual([2, 1])
expect(result.rows.map((row) => row.contributionPercent)).toEqual([40, 10])
})
})
+16
View File
@@ -0,0 +1,16 @@
import { describe, expect, test } from "bun:test"
import { isPeakPricing } from "../src/routes/zen/util/pricing"
describe("isPeakPricing", () => {
test.each([
["weekday 09:00 CN starts peak pricing", "2026-08-27T01:00:00.000Z", true],
["weekday 12:00 CN ends peak pricing", "2026-08-27T04:00:00.000Z", false],
["weekday 14:00 CN starts peak pricing", "2026-08-27T06:00:00.000Z", true],
["weekday 18:00 CN ends peak pricing", "2026-08-27T10:00:00.000Z", false],
["Saturday in Beijing", "2026-08-29T01:00:00.000Z", false],
["Sunday in Beijing", "2026-08-30T06:00:00.000Z", false],
["Monday in Beijing", "2026-08-31T01:00:00.000Z", true],
] as const)("handles %s", (_name, timestamp, expected) => {
expect(isPeakPricing(new Date(timestamp))).toBe(expected)
})
})
@@ -0,0 +1,137 @@
import { describe, expect, test } from "bun:test"
import { prepareRequestBody } from "../src/routes/zen/util/requestBody"
describe("Zen request body streaming", () => {
test("patches the leading model without buffering the remaining body", async () => {
let reads = 0
const body = new ReadableStream<Uint8Array>(
{
pull(controller) {
const chunks = [
'{"model":"client-model","stream":true,"messages":[',
JSON.stringify({ role: "user", content: "large payload" }),
"]}",
]
const chunk = chunks[reads++]
if (chunk) controller.enqueue(new TextEncoder().encode(chunk))
else controller.close()
},
},
{ highWaterMark: 0 },
)
const request = await prepareRequestBody(body)
expect(request.model).toBe("client-model")
expect(reads).toBe(1)
const output = await new Response(request.stream("provider-model", false)).text()
expect(JSON.parse(output)).toEqual({
model: "provider-model",
stream: true,
messages: [{ role: "user", content: "large payload" }],
})
})
test("ignores model fields nested before the root model", async () => {
const body = new Blob([
'{"metadata":{"model":"ox-alpha-free"},"model":"glm-5.3","messages":[],"stream":false}',
]).stream()
const request = await prepareRequestBody(body)
expect(request.model).toBe("glm-5.3")
expect(JSON.parse(await new Response(request.stream("provider-model", false)).text())).toEqual({
metadata: { model: "ox-alpha-free" },
model: "provider-model",
messages: [],
stream: false,
})
})
test("appends stream usage options at the end of the request", async () => {
const body = new Blob(['{"model":"client-model","stream":true,"messages":[]} ']).stream()
const request = await prepareRequestBody(body)
const output = await new Response(request.stream("provider-model", true)).text()
expect(JSON.parse(output)).toEqual({
model: "provider-model",
stream: true,
messages: [],
stream_options: { include_usage: true },
})
expect(output.endsWith(" ")).toBe(true)
})
test("detects streaming after a large message while forwarding", async () => {
const content = "x".repeat(128 * 1024)
let reads = 0
const chunks = [
'{"model":"client-model","messages":[',
JSON.stringify({ role: "user", content }),
'],"stream":true}',
]
const body = new ReadableStream<Uint8Array>(
{
pull(controller) {
const chunk = chunks[reads++]
if (chunk) controller.enqueue(new TextEncoder().encode(chunk))
else controller.close()
},
},
{ highWaterMark: 0 },
)
const request = await prepareRequestBody(body)
expect(reads).toBe(1)
const output = await new Response(request.stream("provider-model", true)).text()
expect(JSON.parse(output)).toEqual({
model: "provider-model",
messages: [{ role: "user", content }],
stream: true,
stream_options: { include_usage: true },
})
})
test("buffers through a late model field and then streams the rest", async () => {
const content = "こんにちは".repeat(32 * 1024)
let reads = 0
const chunks = [
'{"messages":[',
JSON.stringify({ role: "user", content }),
'],"model":"client-model","stream":true,"extra":"after-model"}',
]
const body = new ReadableStream<Uint8Array>(
{
pull(controller) {
const chunk = chunks[reads++]
if (chunk) controller.enqueue(new TextEncoder().encode(chunk))
else controller.close()
},
},
{ highWaterMark: 0 },
)
const request = await prepareRequestBody(body)
expect(request.model).toBe("client-model")
expect(reads).toBe(3)
expect(JSON.parse(await new Response(request.stream("provider-model", true)).text())).toEqual({
messages: [{ role: "user", content }],
model: "provider-model",
stream: true,
extra: "after-model",
stream_options: { include_usage: true },
})
})
test("preserves a UTF-8 BOM while patching the model", async () => {
const body = new Blob(['\uFEFF{"messages":[],"model":"client-model","stream":false}']).stream()
const request = await prepareRequestBody(body)
const output = new Uint8Array(await new Response(request.stream("provider-model", false)).arrayBuffer())
expect([...output.subarray(0, 3)]).toEqual([0xef, 0xbb, 0xbf])
expect(JSON.parse(new TextDecoder().decode(output))).toEqual({
messages: [],
model: "provider-model",
stream: false,
})
})
})
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/console-core",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
-1
View File
@@ -328,7 +328,6 @@ export namespace Billing {
return LiteData.threeMonths100Coupon
if (coupons.some((coupon) => coupon.type === "GOFREEMONTH" && !coupon.timeRedeemed))
return LiteData.firstMonth100Coupon
if (!coupons.some((coupon) => coupon.type === "GO1MONTH50")) return LiteData.firstMonth50Coupon
return undefined
})()
const createSession = () =>
@@ -129,6 +129,7 @@ export const UsageTable = mysqlTable(
sessionID: varchar("session_id", { length: 30 }),
enrichment: json("enrichment").$type<{
plan: "sub" | "byok" | "lite"
costMultiplier?: number
}>(),
},
(table) => [...workspaceIndexes(table), index("usage_time_created").on(table.workspaceID, table.timeCreated)],
+12
View File
@@ -97,6 +97,18 @@ export namespace Workspace {
},
)
export const unblock = fn(
z.object({
workspaceID: Identifier.schema("workspace"),
}),
async (input) => {
const result = await Database.use((tx) =>
tx.update(WorkspaceTable).set({ is_blocked: false }).where(eq(WorkspaceTable.id, input.workspaceID)),
)
if (result.rowsAffected === 0) throw new Error("Workspace not found")
},
)
export const remove = fn(z.void(), async () => {
await Database.use((tx) =>
tx
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-function",
"version": "1.18.21",
"version": "1.18.23",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-mail",
"version": "1.18.21",
"version": "1.18.23",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-support",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "@opencode-ai/core",
"type": "module",
"license": "MIT",
+33 -6
View File
@@ -54,12 +54,39 @@ export function applyOnly(db: Database, input: Migration[]) {
if (
yield* db.get(sql`SELECT name FROM sqlite_master WHERE type = 'table' AND name = ${"__drizzle_migrations"}`)
) {
yield* db.run(sql`
INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed)
SELECT name, ${Date.now()}
FROM ${sql.identifier("__drizzle_migrations")}
WHERE name IS NOT NULL
`)
const named = (yield* db.all<{ name: string }>(
sql`SELECT name FROM pragma_table_info('__drizzle_migrations')`,
)).some((column) => column.name === "name")
if (named) {
yield* db.run(sql`
INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed)
SELECT name, ${Date.now()}
FROM ${sql.identifier("__drizzle_migrations")}
WHERE name IS NOT NULL
`)
}
if (!named) {
const entries = yield* db.all<{ created_at: number; prefix: string | null }>(sql`
SELECT created_at, strftime('%Y%m%d%H%M%S', created_at / 1000, 'unixepoch') AS prefix
FROM ${sql.identifier("__drizzle_migrations")}
WHERE created_at IS NOT NULL
`)
for (const entry of entries) {
const migration = input.find((item) => item.id.startsWith(`${entry.prefix}_`))
if (!migration) {
return yield* Effect.die(
new Error(`Legacy migration timestamp ${entry.created_at} does not match any known migration`),
)
}
yield* db.run(sql`
INSERT OR IGNORE INTO ${sql.identifier("migration")} (id, time_completed)
VALUES (${migration.id}, ${Date.now()})
`)
}
}
completed = new Set(
(yield* db.all<{ id: string }>(sql`SELECT id FROM ${sql.identifier("migration")}`)).map((row) => row.id),
)
@@ -5,6 +5,9 @@ export default {
id: "20260410174513_workspace-name",
up(tx) {
return Effect.gen(function* () {
const columns = yield* tx.all<{ name: string }>(`PRAGMA table_info(\`workspace\`)`)
const name = columns.some((column) => column.name === "name") ? "`name`" : "''"
yield* tx.run(`PRAGMA foreign_keys=OFF;`)
yield* tx.run(`
CREATE TABLE \`__new_workspace\` (
@@ -19,7 +22,7 @@ export default {
);
`)
yield* tx.run(
`INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`,
`INSERT INTO \`__new_workspace\`(\`id\`, \`type\`, \`branch\`, \`name\`, \`directory\`, \`extra\`, \`project_id\`) SELECT \`id\`, \`type\`, \`branch\`, ${name}, \`directory\`, \`extra\`, \`project_id\` FROM \`workspace\`;`,
)
yield* tx.run(`DROP TABLE \`workspace\`;`)
yield* tx.run(`ALTER TABLE \`__new_workspace\` RENAME TO \`workspace\`;`)
@@ -8,6 +8,7 @@ import { Effect, Layer } from "effect"
import { eq, inArray, sql } from "drizzle-orm"
import { DatabaseMigration } from "@opencode-ai/core/database/migration"
import { migrations } from "@opencode-ai/core/database/migration.gen"
import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
import normalizeStoragePathsMigration from "@opencode-ai/core/database/migration/20260601010001_normalize_storage_paths"
import sessionMessageProjectionOrderMigration from "@opencode-ai/core/database/migration/20260603040000_session_message_projection_order"
@@ -38,6 +39,68 @@ const run = <A, E>(effect: Effect.Effect<A, E, SqlClientService>) =>
const makeDb = EffectDrizzleSqlite.makeWithDefaults()
describe("DatabaseMigration", () => {
test("defaults missing workspace names while preserving legacy workspace data", async () => {
await run(
Effect.gen(function* () {
const db = yield* makeDb
yield* db.run(sql`
CREATE TABLE workspace (
id text PRIMARY KEY,
type text NOT NULL,
branch text,
directory text,
extra text,
project_id text NOT NULL
)
`)
yield* db.run(sql`
INSERT INTO workspace (id, type, branch, directory, extra, project_id)
VALUES ('wrk_legacy', 'remote', 'main', '/repo', '{}', 'proj_legacy')
`)
yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
expect(yield* db.get(sql`SELECT id, name, branch, directory, extra FROM workspace`)).toEqual({
id: "wrk_legacy",
name: "",
branch: "main",
directory: "/repo",
extra: "{}",
})
}),
)
})
test("imports unnamed legacy Drizzle journal entries by their actual migration timestamps", async () => {
await run(
Effect.gen(function* () {
const db = yield* makeDb
yield* db.run(sql`CREATE TABLE __drizzle_migrations (id integer PRIMARY KEY, hash text, created_at integer)`)
yield* db.run(sql`
INSERT INTO __drizzle_migrations (hash, created_at)
VALUES ('', ${Date.UTC(2026, 3, 10, 17, 45, 13)})
`)
yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
expect(yield* db.all(sql`SELECT id FROM migration`)).toEqual([{ id: "20260410174513_workspace-name" }])
}),
)
})
test("rejects unknown legacy Drizzle journal timestamps instead of guessing completed migrations", async () => {
await expect(
run(
Effect.gen(function* () {
const db = yield* makeDb
yield* db.run(sql`CREATE TABLE __drizzle_migrations (id integer PRIMARY KEY, hash text, created_at integer)`)
yield* db.run(sql`INSERT INTO __drizzle_migrations (hash, created_at) VALUES ('', 1234567890000)`)
yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
}),
),
).rejects.toThrow("does not match any known migration")
})
test("serializes concurrent embedded initialization for one database path", async () => {
await using tmp = await tmpdir()
const filename = path.join(tmp.path, "embedded.sqlite")
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@opencode-ai/desktop",
"private": true,
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"homepage": "https://opencode.ai",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "@opencode-ai/effect-drizzle-sqlite",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "@opencode-ai/effect-sqlite-node",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/enterprise",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+4 -1
View File
@@ -1,10 +1,13 @@
{
"name": "@opencode-ai/function",
"version": "1.18.21",
"version": "1.18.23",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"license": "MIT",
"scripts": {
"test": "bun test"
},
"devDependencies": {
"@cloudflare/workers-types": "catalog:",
"@tsconfig/node22": "22.0.2",
+26 -26
View File
@@ -5,6 +5,7 @@ import { jwtVerify, createRemoteJWKSet } from "jose"
import { createAppAuth } from "@octokit/auth-app"
import { Octokit } from "@octokit/rest"
import { Resource } from "sst"
import { parseRepositoryClaim } from "./github"
type Env = {
SYNC_SERVER: DurableObjectNamespace<SyncServer>
@@ -269,42 +270,41 @@ export default new Hono<{ Bindings: Env }>()
// verify token
const JWKS = createRemoteJWKSet(new URL(JWKS_URL))
let owner, repo
let repository: ReturnType<typeof parseRepositoryClaim>
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: GITHUB_ISSUER,
audience: EXPECTED_AUDIENCE,
})
const sub = payload.sub // e.g. 'repo:my-org/my-repo:ref:refs/heads/main'
const parts = sub.split(":")[1].split("/")
owner = parts[0]
repo = parts[1]
repository = parseRepositoryClaim(payload)
} catch (err) {
console.error("Token verification failed:", err)
return c.json({ error: "Invalid or expired token" }, { status: 403 })
}
// Create app JWT token
const auth = createAppAuth({
appId: Resource.GITHUB_APP_ID.value,
privateKey: Resource.GITHUB_APP_PRIVATE_KEY.value,
})
const appAuth = await auth({ type: "app" })
// Lookup installation
const octokit = new Octokit({ auth: appAuth.token })
const { data: installation } = await octokit.apps.getRepoInstallation({
owner,
repo,
})
// Get installation token
const installationAuth = await auth({
type: "installation",
installationId: installation.id,
})
return c.json({ token: installationAuth.token })
try {
const auth = createAppAuth({
appId: Resource.GITHUB_APP_ID.value,
privateKey: Resource.GITHUB_APP_PRIVATE_KEY.value,
})
const appAuth = await auth({ type: "app" })
const octokit = new Octokit({ auth: appAuth.token })
const { data: installation } = await octokit.apps.getRepoInstallation({
owner: repository.owner,
repo: repository.repo,
})
const installationAuth = await auth({
type: "installation",
installationId: installation.id,
})
return c.json({ token: installationAuth.token })
} catch (error) {
console.error("GitHub App token exchange failed:", error)
return c.json(
{ error: `Failed to exchange GitHub App token for ${repository.owner}/${repository.repo}` },
{ status: 502 },
)
}
})
/**
* Used by the GitHub action to get GitHub installation access token given user PAT token (used when testing `opencode github run` locally)
+14
View File
@@ -0,0 +1,14 @@
import type { JWTPayload } from "jose"
export function parseRepositoryClaim(payload: JWTPayload) {
const claim = payload.repository
if (typeof claim !== "string") throw new Error("Repository claim is missing")
const parts = claim.split("/")
if (parts.length !== 2 || !parts[0] || !parts[1]) throw new Error("Repository claim is invalid")
return {
owner: parts[0],
repo: parts[1],
}
}
+39
View File
@@ -0,0 +1,39 @@
import { describe, expect, test } from "bun:test"
import { parseRepositoryClaim } from "../src/github"
describe("parseRepositoryClaim", () => {
test("reads repository identity with a legacy subject", () => {
expect(
parseRepositoryClaim({
repository: "octocat/my-repo",
sub: "repo:octocat/my-repo:ref:refs/heads/main",
}),
).toEqual({ owner: "octocat", repo: "my-repo" })
})
test("reads repository identity with an immutable subject", () => {
expect(
parseRepositoryClaim({
repository: "octocat/my-repo",
sub: "repo:octocat@123456/my-repo@456789:ref:refs/heads/main",
}),
).toEqual({ owner: "octocat", repo: "my-repo" })
})
test("does not depend on a repository path in a customized subject", () => {
expect(
parseRepositoryClaim({
repository: "octocat/my-repo",
sub: "repository_owner:octocat:repository_visibility:private",
}),
).toEqual({ owner: "octocat", repo: "my-repo" })
})
test("rejects a missing repository claim", () => {
expect(() => parseRepositoryClaim({})).toThrow("Repository claim is missing")
})
test("rejects an invalid repository claim", () => {
expect(() => parseRepositoryClaim({ repository: "octocat" })).toThrow("Repository claim is invalid")
})
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "@opencode-ai/http-recorder",
"description": "Record and replay Effect HTTP client traffic with deterministic cassettes",
"type": "module",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "@opencode-ai/llm",
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "1.18.21",
"version": "1.18.23",
"name": "opencode",
"type": "module",
"license": "MIT",
@@ -437,6 +437,7 @@ export const githubRun = Effect.fn("Cli.github.run")(function* (args: { event?:
let session: { id: SessionID; title: string; version: string }
let shareId: string | undefined
let exitCode = 0
let githubClientReady = false
type PromptFiles = Awaited<ReturnType<typeof getUserPrompt>>["promptFiles"]
const triggerCommentId = isCommentEvent
? (payload as IssueCommentEvent | PullRequestReviewCommentEvent).comment.id
@@ -485,6 +486,7 @@ export const githubRun = Effect.fn("Cli.github.run")(function* (args: { event?:
octoGraph = graphql.defaults({
headers: { authorization: `token ${appToken}` },
})
githubClientReady = true
const { userPrompt, promptFiles } = await getUserPrompt()
if (!useGithubToken) {
@@ -639,9 +641,13 @@ export const githubRun = Effect.fn("Cli.github.run")(function* (args: { event?:
} else if (e instanceof Error) {
msg = e.message
}
if (isUserEvent) {
await createComment(`${msg}${footer()}`)
await removeReaction(commentType)
if (isUserEvent && githubClientReady) {
try {
await createComment(`${msg}${footer()}`)
await removeReaction(commentType)
} catch (error) {
console.error("Failed to report error on GitHub:", error)
}
}
core.setFailed(msg)
// Also output the clean error message for the action to capture
@@ -1004,8 +1010,9 @@ export const githubRun = Effect.fn("Cli.github.run")(function* (args: { event?:
})
if (!response.ok) {
const responseJson = (await response.json()) as { error?: string }
throw new Error(`App token exchange failed: ${response.status} ${response.statusText} - ${responseJson.error}`)
throw new Error(
`App token exchange failed: ${response.status} ${response.statusText} - ${await response.text()}`,
)
}
const responseJson = (await response.json()) as { token: string }
+23 -3
View File
@@ -809,6 +809,7 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
const { createUnified } = yield* Effect.promise(() => import("ai-gateway-provider/providers/unified"))
const { createOpenAI } = yield* Effect.promise(() => import("ai-gateway-provider/providers/openai"))
const { createAnthropic } = yield* Effect.promise(() => import("ai-gateway-provider/providers/anthropic"))
const { createOpenAICompatible } = yield* Effect.promise(() => import("@ai-sdk/openai-compatible"))
const metadata = iife(() => {
if (input.options?.metadata) return input.options.metadata
@@ -844,15 +845,34 @@ function custom(dep: CustomDep): Record<string, CustomLoader> {
// The passthrough wrappers inject a CF_TEMP_TOKEN sentinel that the gateway strips before
// dispatch, so upstream billing stays on the gateway (Unified Billing / stored BYOK).
if (modelID.startsWith("openai/")) return aigateway(createOpenAI()(modelID.slice("openai/".length)))
if (modelID.startsWith("anthropic/")) return aigateway(createAnthropic()(modelID.slice("anthropic/".length)))
// models.dev lists Anthropic ids with dotted versions (claude-haiku-4.5); Anthropic's
// Messages API expects dashed native slugs (claude-haiku-4-5), so translate before passing.
// No native Anthropic slug contains a dot, so the blanket replacement is lossless here -
// unlike OpenAI above, whose native ids (e.g. gpt-4.1) keep their dots and must not be touched.
if (modelID.startsWith("anthropic/"))
return aigateway(createAnthropic()(modelID.slice("anthropic/".length).replaceAll(".", "-")))
// Workers AI is the only first-party provider whose upstream is Cloudflare itself, so it is
// the only one that should receive the Cloudflare token as its upstream Authorization header.
// The Unified API addresses Workers AI both with the explicit "workers-ai/" prefix and as
// bare "@cf/..." ids. Third-party providers must not receive the token; they rely on the
// gateway's stored/BYOK keys instead.
// Workers AI is Cloudflare's own upstream, so it rides the unified compat route with the
// Cloudflare token as its upstream Authorization header.
const isWorkersAi = modelID.startsWith("workers-ai/") || modelID.startsWith("@cf/")
const unified = createUnified(isWorkersAi ? { apiKey: apiToken } : {})
return aigateway(unified(modelID))
if (isWorkersAi) return aigateway(createUnified({ apiKey: apiToken })(modelID))
// Every other third-party provider (google, xai, alibaba, deepseek, moonshotai, …) is only
// served by Cloudflare's catalog-aware REST API. The universal/compat gateway route rejects
// them with "Invalid provider" (the gateway's compat endpoint doesn't front those upstreams),
// so point an OpenAI-compatible client at the REST endpoint and bind it to the gateway with
// cf-aig-gateway-id — that keeps requests gateway-routed (analytics/caching/BYOK), not a
// bypass. models.dev ids (provider/model, dotted) pass through unchanged.
return createOpenAICompatible({
name: "cloudflare-ai-gateway",
baseURL: `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/v1`,
apiKey: apiToken,
headers: { "cf-aig-gateway-id": gateway },
})(modelID)
},
options: {},
}
@@ -5,7 +5,8 @@ import { InstanceDisposed } from "@/server/event"
import "@opencode-ai/core/account"
import "@/server/event"
import { Schema } from "effect"
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
import { HttpApi, HttpApiEndpoint, HttpApiError, HttpApiGroup, OpenApi } from "effect/unstable/httpapi"
import semver from "semver"
import { described } from "./metadata"
const GlobalHealth = Schema.Struct({
@@ -48,7 +49,9 @@ const GlobalEventSchema = Schema.Struct({
}).annotate({ identifier: "GlobalEvent" })
export const GlobalUpgradeInput = Schema.Struct({
target: Schema.optional(Schema.String),
target: Schema.String.check(
Schema.makeFilter((value) => (semver.valid(value) === null ? "Expected a semantic version" : undefined)),
),
})
const GlobalUpgradeResult = Schema.Union([
@@ -121,14 +124,14 @@ export const GlobalApi = HttpApi.make("global").add(
}),
),
HttpApiEndpoint.post("upgrade", GlobalPaths.upgrade, {
payload: [HttpApiSchema.NoContent, GlobalUpgradeInput],
payload: GlobalUpgradeInput,
success: described(GlobalUpgradeResult, "Upgrade result"),
error: HttpApiError.BadRequest,
}).annotateMerge(
OpenApi.annotations({
identifier: "global.upgrade",
summary: "Upgrade opencode",
description: "Upgrade opencode to the specified version or latest if not specified.",
description: "Upgrade opencode to the specified version.",
}),
),
)
@@ -5,9 +5,9 @@ import { EventV2 } from "@opencode-ai/core/event"
import { Installation } from "@/installation"
import { disposeAllInstancesAndEmitGlobalDisposed } from "@/server/global-lifecycle"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { Effect, Queue, Schema } from "effect"
import { Effect, Queue } from "effect"
import * as Stream from "effect/Stream"
import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http"
import { HttpServerResponse } from "effect/unstable/http"
import { HttpApiBuilder } from "effect/unstable/httpapi"
import * as Sse from "effect/unstable/encoding/Sse"
import { RootHttpApi } from "../api"
@@ -22,14 +22,6 @@ function eventData(data: unknown): Sse.Event {
}
}
function parseBody(body: string) {
try {
return JSON.parse(body || "{}") as unknown
} catch {
return undefined
}
}
function eventResponse() {
return Effect.gen(function* () {
yield* Effect.logInfo("global event connected")
@@ -97,25 +89,22 @@ export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handl
const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
const method = yield* installation.method()
if (method === "unknown") {
return {
status: 400,
body: { success: false as const, error: "Unknown installation method" },
}
return HttpServerResponse.jsonUnsafe(
{ success: false as const, error: "Unknown installation method" },
{ status: 400 },
)
}
const target = ctx.payload.target || (yield* installation.latest(method))
const target = ctx.payload.target
const result = yield* installation.upgrade(method, target).pipe(
Effect.as({ status: 200, body: { success: true as const, version: target } }),
Effect.as({ success: true as const, version: target }),
Effect.catch((err) =>
Effect.succeed({
status: 500,
body: {
success: false as const,
error: err instanceof Error ? err.message : String(err),
},
success: false as const,
error: err instanceof Error ? err.message : String(err),
}),
),
)
if (!result.body.success) return result
if (!result.success) return HttpServerResponse.jsonUnsafe(result, { status: 500 })
GlobalBus.emit("event", {
directory: "global",
payload: {
@@ -123,26 +112,7 @@ export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handl
properties: { version: target },
},
})
return result
})
const upgradeRaw = Effect.fn("GlobalHttpApi.upgradeRaw")(function* (ctx: {
request: HttpServerRequest.HttpServerRequest
}) {
const body = yield* Effect.orDie(ctx.request.text)
const json = parseBody(body)
if (json === undefined) {
return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
}
const payload = yield* Schema.decodeUnknownEffect(GlobalUpgradeInput)(json).pipe(
Effect.map((payload) => ({ valid: true as const, payload })),
Effect.catch(() => Effect.succeed({ valid: false as const })),
)
if (!payload.valid) {
return HttpServerResponse.jsonUnsafe({ success: false, error: "Invalid request body" }, { status: 400 })
}
const result = yield* upgrade({ payload: payload.payload })
return HttpServerResponse.jsonUnsafe(result.body, { status: result.status })
return HttpServerResponse.jsonUnsafe(result)
})
return handlers
@@ -151,6 +121,6 @@ export const globalHandlers = HttpApiBuilder.group(RootHttpApi, "global", (handl
.handle("configGet", configGet)
.handle("configUpdate", configUpdate)
.handle("dispose", dispose)
.handleRaw("upgrade", upgradeRaw)
.handle("upgrade", upgrade)
}),
)
+1 -1
View File
@@ -196,9 +196,9 @@ export const prepare = Effect.fn("LLMRequestPrep.prepare")(function* (input: Pre
: {
"x-session-affinity": input.sessionID,
"X-Session-Id": input.sessionID,
...(input.parentSessionID ? { "x-parent-session-id": input.parentSessionID } : {}),
"User-Agent": USER_AGENT,
}),
...(input.parentSessionID ? { "x-parent-session-id": input.parentSessionID } : {}),
...input.model.headers,
...headers,
},
+1 -1
View File
@@ -103,7 +103,7 @@ export function retryable(error: Err, provider: string) {
reason: "free_tier_limit",
provider,
title: "Free limit reached",
message: "Subscribe to OpenCode Go for reliable access to the best open-source models, starting at $5/month.",
message: "Subscribe to OpenCode Go for reliable access to the best open-source models for $10/month.",
label: "subscribe",
link: GO_UPSELL_URL,
},
@@ -165,7 +165,8 @@ function extractUpstreamHeaders(body: unknown): Record<string, unknown> | undefi
function gatewayModel(apiId: string, gatewayToken = "test") {
const aigateway = createAiGateway({ accountId: "test", gateway: "test", apiKey: gatewayToken })
if (apiId.startsWith("openai/")) return aigateway(createOpenAI()(apiId.slice("openai/".length)))
if (apiId.startsWith("anthropic/")) return aigateway(createAnthropic()(apiId.slice("anthropic/".length)))
if (apiId.startsWith("anthropic/"))
return aigateway(createAnthropic()(apiId.slice("anthropic/".length).replaceAll(".", "-")))
const isWorkersAi = apiId.startsWith("workers-ai/") || apiId.startsWith("@cf/")
const unified = createUnified(isWorkersAi ? { apiKey: gatewayToken } : {})
return aigateway(unified(apiId))
@@ -195,6 +196,17 @@ describe("cf-ai-gateway routing", () => {
expect(upstream?.model).toBe("claude-sonnet-4-6")
})
test("anthropic/* with a dotted models.dev id reaches Anthropic as a dashed native slug", async () => {
// models.dev ids are dotted (claude-haiku-4.5); Anthropic's Messages API 404s unless the
// version is dashed (claude-haiku-4-5). Regression guard for the dotted-id translation.
await callThroughGateway("anthropic/claude-haiku-4.5", {})
const step = firstStep(captured?.outerBody)
expect(step?.provider).toBe("anthropic")
expect(step?.endpoint).toBe("v1/messages")
const upstream = extractUpstreamQuery(captured?.outerBody)
expect(upstream?.model).toBe("claude-haiku-4-5")
})
test("workers-ai models stay on the unified /compat route", async () => {
await callThroughGateway("workers-ai/@cf/moonshotai/kimi-k2.6", {})
const step = firstStep(captured?.outerBody)
@@ -43,24 +43,48 @@ const apiLayer = HttpRouter.serve(
const it = testEffect(apiLayer)
describe("global HttpApi", () => {
it.live("upgrades to latest when the request body is omitted", () =>
it.live("upgrades to the requested version", () =>
Effect.gen(function* () {
const response = yield* HttpClient.post(GlobalPaths.upgrade)
const response = yield* HttpClientRequest.post(GlobalPaths.upgrade).pipe(
HttpClientRequest.bodyJsonUnsafe({ target: "9.9.9" }),
HttpClient.execute,
)
expect(response.status).toBe(200)
expect(yield* response.json).toEqual({ success: true, version: "9.9.9" })
}),
)
it.live("rejects malformed upgrade payloads", () =>
it.live("rejects invalid upgrade payloads", () =>
Effect.gen(function* () {
const response = yield* HttpClientRequest.post(GlobalPaths.upgrade).pipe(
HttpClientRequest.setBody(HttpBody.text("{", "application/json")),
HttpClientRequest.bodyJsonUnsafe({ target: 1 }),
HttpClient.execute,
)
expect(response.status).toBe(400)
expect(yield* response.json).toEqual({ success: false, error: "Invalid request body" })
}),
)
it.live("rejects invalid upgrade target versions", () =>
Effect.gen(function* () {
const response = yield* HttpClientRequest.post(GlobalPaths.upgrade).pipe(
HttpClientRequest.bodyJsonUnsafe({ target: "latest" }),
HttpClient.execute,
)
expect(response.status).toBe(400)
}),
)
it.live("rejects unsupported upgrade content types", () =>
Effect.gen(function* () {
const response = yield* HttpClientRequest.post(GlobalPaths.upgrade).pipe(
HttpClientRequest.setBody(HttpBody.text('{"target":"1.0.0"}', "text/plain")),
HttpClient.execute,
)
expect(response.status).toBe(415)
}),
)
})
@@ -754,6 +754,75 @@ function createEventResponse(chunks: unknown[], includeDone = false) {
describe("session.llm.stream", () => {
const vivgridFixture = { providerID: "vivgrid", modelID: "gemini-3.1-pro-preview" }
const opencodeFixture = { providerID: "opencode-test", modelID: vivgridFixture.modelID }
it.instance(
"sends the parent session header for opencode providers",
() =>
Effect.gen(function* () {
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
const request = waitRequest(
"/chat/completions",
new Response(createChatStream("Hello"), {
status: 200,
headers: { "Content-Type": "text/event-stream" },
}),
)
const resolved = yield* Provider.use.getModel(
ProviderV2.ID.make(opencodeFixture.providerID),
ModelV2.ID.make(opencodeFixture.modelID),
)
const sessionID = SessionID.make("session-child")
const parentSessionID = SessionID.make("session-parent")
const agent = {
name: "test",
mode: "primary",
options: {},
permission: [{ permission: "*", pattern: "*", action: "allow" }],
} satisfies Agent.Info
const user = {
id: MessageID.make("msg_user-parent-header"),
sessionID,
role: "user",
time: { created: Date.now() },
agent: agent.name,
model: {
providerID: ProviderV2.ID.make(opencodeFixture.providerID),
modelID: resolved.id,
},
} satisfies SessionV1.User
yield* drain({
user,
sessionID,
parentSessionID,
model: resolved,
agent,
system: ["You are a helpful assistant."],
messages: [{ role: "user", content: "Hello" }],
tools: {},
})
expect((yield* Effect.promise(() => request)).headers.get("x-parent-session-id")).toBe(parentSessionID)
}),
{
config: () => {
const fixture = loadFixture(vivgridFixture.providerID, vivgridFixture.modelID)
return {
enabled_providers: [opencodeFixture.providerID],
provider: {
[opencodeFixture.providerID]: {
name: "OpenCode Test",
npm: "@ai-sdk/openai-compatible",
models: { [fixture.model.id]: configModel(fixture.model) as ConfigModel },
options: { apiKey: "test-key", baseURL: `${state.server!.url.origin}/v1` },
},
},
}
},
},
)
it.instance(
"sends temperature, tokens, and reasoning options for openai-compatible models",
() =>
+1 -1
View File
@@ -354,7 +354,7 @@ describe("session.retry.retryable", () => {
reason: "free_tier_limit",
provider: "opencode",
title: "Free limit reached",
message: "Subscribe to OpenCode Go for reliable access to the best open-source models, starting at $5/month.",
message: "Subscribe to OpenCode Go for reliable access to the best open-source models for $10/month.",
label: "subscribe",
link: SessionRetry.GO_UPSELL_URL,
},
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/plugin",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/sdk",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1355,7 +1355,7 @@ export class Global extends HeyApiClient {
/**
* Upgrade opencode
*
* Upgrade opencode to the specified version or latest if not specified.
* Upgrade opencode to the specified version.
*/
public upgrade<ThrowOnError extends boolean = false>(
parameters?: {
+1 -1
View File
@@ -7353,7 +7353,7 @@ export type GlobalDisposeResponse = GlobalDisposeResponses[keyof GlobalDisposeRe
export type GlobalUpgradeData = {
body?: {
target?: string
target: string
}
path?: never
query?: never
+2 -1
View File
@@ -548,7 +548,7 @@
}
}
},
"description": "Upgrade opencode to the specified version or latest if not specified.",
"description": "Upgrade opencode to the specified version.",
"summary": "Upgrade opencode",
"requestBody": {
"content": {
@@ -560,6 +560,7 @@
"type": "string"
}
},
"required": ["target"],
"additionalProperties": false
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/server",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/session-ui",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/slack",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"scripts": {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-app",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-core",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
@@ -4,6 +4,7 @@ import { modelAuthor, normalizeInferenceModel, statModel, statProvider } from ".
describe("inference stat normalization", () => {
test("normalizes model suffixes used by router/provider variants", () => {
expect(normalizeInferenceModel("GPT-5-Free")).toBe("gpt-5")
expect(normalizeInferenceModel("deepseek-v4-flash-free")).toBe("deepseek-v4-flash")
expect(normalizeInferenceModel("deepseek-v4-flash:global")).toBe("deepseek-v4-flash")
expect(normalizeInferenceModel("mimo-v2.5-free")).toBe("mimo-v2.5")
@@ -23,7 +23,7 @@ export const RETIRED_STAT_MODELS = ["big-pickle", ...Object.keys(MODEL_NAME_ALIA
export const RETIRED_STAT_PROVIDERS = ["opencode"]
export function normalizeInferenceModel(value: string | undefined) {
return (value || "unknown").replace(/(-free|:free|:global)+$/, "") || "unknown"
return (value || "unknown").toLowerCase().replace(/(-free|:free|:global)+$/, "") || "unknown"
}
export function modelAuthor(value: string | undefined) {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/stats-server",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/tui",
"version": "1.18.21",
"version": "1.18.23",
"private": true,
"type": "module",
"license": "MIT",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/ui",
"version": "1.18.21",
"version": "1.18.23",
"type": "module",
"license": "MIT",
"repository": {
+2 -2
View File
@@ -88,10 +88,10 @@ function init() {
setClosing = setClosingSignal
return (
<Kobalte
modal
modal={stack().at(-1)?.id === id}
open={!closing()}
onOpenChange={(open: boolean) => {
if (open) return
if (open || stack().at(-1)?.id !== id) return
close(id)
}}
>
+1 -1
View File
@@ -68,7 +68,7 @@ export const dict: Record<string, string> = {
"ui.sessionTurn.error.freeUsageExceeded": "ነፃ አጠቃቀም ታልፏል",
"ui.sessionTurn.error.addCredits": "ክሬዲት አክል",
"dialog.usageExceeded.freeTier.title": "ነፃ ገደብ ላይ ደርሷል",
"dialog.usageExceeded.freeTier.description": "ለOpenCode Go ለምርጥ ክፍት ምንጭ ሞዴሎች ታማኝ መዳረሻ ለማግኘት ይመዝገቡ፣ ከ$5 በወር ጀምሮ።",
"dialog.usageExceeded.freeTier.description": "ለምርጥ ክፍት ምንጭ ሞዴሎች ታማኝ መዳረሻ ለማግኘት በወር $10 ለOpenCode Go ይመዝገቡ።",
"dialog.usageExceeded.freeTier.actionLabel": "ለደንበኝነት ይመዝገቡ",
"dialog.usageExceeded.accountRateLimit.title": "የሂድ ገደብ ላይ ደርሷል",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -77,7 +77,7 @@ export const dict = {
"dialog.usageExceeded.freeTier.title": "تم الوصول إلى الحد المجاني",
"dialog.usageExceeded.freeTier.description":
"اشترك في OpenCode Go للحصول على وصول موثوق إلى أفضل النماذج مفتوحة المصدر، ابتداءً من $5/شهر.",
"اشترك في OpenCode Go مقابل $10/شهر للحصول على وصول موثوق إلى أفضل النماذج مفتوحة المصدر.",
"dialog.usageExceeded.freeTier.actionLabel": "اشترك",
"dialog.usageExceeded.accountRateLimit.title": "تم الوصول إلى حد Go",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -69,7 +69,7 @@ export const dict: Record<string, string> = {
"ui.sessionTurn.error.addCredits": "Kredit əlavə et",
"dialog.usageExceeded.freeTier.title": "Pulsuz limitə çatdınız",
"dialog.usageExceeded.freeTier.description":
"Ayda $5-dan başlayan OpenCode Go abunəliyi ilə ən yaxşı açıq mənbəli modellərə etibarlı giriş əldə edin.",
"Ayda $10 olan OpenCode Go abunəliyi ilə ən yaxşı açıq mənbəli modellərə etibarlı giriş əldə edin.",
"dialog.usageExceeded.freeTier.actionLabel": "Abunə ol",
"dialog.usageExceeded.accountRateLimit.title": "Go limitinə çatdınız",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -69,7 +69,7 @@ export const dict = {
"ui.sessionTurn.error.addCredits": "Добавете кредити",
"dialog.usageExceeded.freeTier.title": "Безплатният лимит е достигнат",
"dialog.usageExceeded.freeTier.description":
"Абонирайте се за OpenCode Go за надежден достъп до най-добрите модели с отворен код, започващи от $5/месец.",
"Абонирайте се за OpenCode Go за надежден достъп до най-добрите модели с отворен код за $10/месец.",
"dialog.usageExceeded.freeTier.actionLabel": "Абонирайте се",
"dialog.usageExceeded.accountRateLimit.title": "Лимитът за движение е достигнат",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -71,7 +71,7 @@ export const dict: Record<string, string> = {
"ui.sessionTurn.error.addCredits": "ক্রেডিট যোগ করুন",
"dialog.usageExceeded.freeTier.title": "বিনামূল্যের সীমা পৌঁছেছে",
"dialog.usageExceeded.freeTier.description":
"OpenCode-এ সদস্যতা নিন $5/মাস থেকে শুরু করে সেরা ওপেন-সোর্স মডেলগুলিতে নির্ভরযোগ্য অ্যাক্সেসের জন্য যান৷",
"সেরা ওপেন-সোর্স মডেলগুলিতে নির্ভরযোগ্য অ্যাক্সেসের জন্য $10/মাসে OpenCode Go-তে সদস্যতা নিন৷",
"dialog.usageExceeded.freeTier.actionLabel": "সদস্যতা",
"dialog.usageExceeded.accountRateLimit.title": "যাওয়ার সীমা পৌঁছে গেছে",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -74,7 +74,7 @@ export const dict = {
"dialog.usageExceeded.freeTier.title": "Limite gratuito atingido",
"dialog.usageExceeded.freeTier.description":
"Assine o OpenCode Go para ter acesso confiável aos melhores modelos de código aberto, a partir de $5/mês.",
"Assine o OpenCode Go por $10/mês para ter acesso confiável aos melhores modelos de código aberto.",
"dialog.usageExceeded.freeTier.actionLabel": "Assinar",
"dialog.usageExceeded.accountRateLimit.title": "Limite do Go atingido",
"dialog.usageExceeded.accountRateLimit.description":
+1 -1
View File
@@ -78,7 +78,7 @@ export const dict = {
"dialog.usageExceeded.freeTier.title": "Dostignut besplatan limit",
"dialog.usageExceeded.freeTier.description":
"Pretplati se na OpenCode Go za pouzdan pristup najboljim modelima otvorenog koda, počevši od $5/mjesec.",
"Pretplati se na OpenCode Go za $10/mjesec i ostvari pouzdan pristup najboljim modelima otvorenog koda.",
"dialog.usageExceeded.freeTier.actionLabel": "Pretplati se",
"dialog.usageExceeded.accountRateLimit.title": "Dostignut Go limit",
"dialog.usageExceeded.accountRateLimit.description":

Some files were not shown because too many files have changed in this diff Show More