Compare commits

..

215 Commits

Author SHA1 Message Date
Aiden Cline 258fe1546c fix(opencode): process prompts queued during loop shutdown 2026-06-02 21:08:32 -05:00
opencode-agent[bot] dc216e8b03 chore: update nix node_modules hashes 2026-06-02 23:19:59 +00:00
opencode-agent[bot] b12bc87548 chore: generate 2026-06-02 23:06:44 +00:00
Dax ca2acc4f8d refactor(opencode): remove JSON storage migration (#30461) 2026-06-02 19:05:14 -04:00
opencode-agent[bot] 113e7be5ac chore: update nix node_modules hashes 2026-06-02 20:27:24 +00:00
Simon Klee 30cff95158 run: enable interactive replay by default (#30465) 2026-06-02 22:20:16 +02:00
opencode-agent[bot] cb587b677b chore: generate 2026-06-02 20:11:21 +00:00
Dax 604a5f781f refactor(core): consolidate filesystem services (#30447) 2026-06-02 16:09:26 -04:00
opencode-agent[bot] b93963e462 chore: generate 2026-06-02 19:51:01 +00:00
Aiden Cline 70a2e846cb fix(opencode): patch empty Gemini replay messages (#30463) 2026-06-02 14:49:13 -05:00
opencode-agent[bot] 1acdc48a95 chore: generate 2026-06-02 19:22:06 +00:00
Dustin Deus 7dd2306dad refactor(opencode): improve startup time by 38% (#30453)
Co-authored-by: starptech <starptech@starptechs-MBP.fritz.box>
2026-06-02 21:20:25 +02:00
opencode-agent[bot] 83c12f334e chore: update nix node_modules hashes 2026-06-02 19:05:15 +00:00
Simon Klee 52ecc6d0f5 Revert "tui: revert OpenTUI upgrade to 0.2.16 (#30383)" (#30452) 2026-06-02 20:49:31 +02:00
Kit Langton 1f2bcc22e3 chore: bump effect beta to 74 (#30449) 2026-06-02 18:41:19 +00:00
Kit Langton 4002b85707 fix(effect-drizzle-sqlite): preserve transaction begin errors (#30448) 2026-06-02 14:36:48 -04:00
opencode-agent[bot] 5b92b173ca chore: generate 2026-06-02 18:34:14 +00:00
Adam 69345a29b0 feat(stats): improve cache ratio chart 2026-06-02 13:32:01 -05:00
opencode-agent[bot] e54f974840 chore: generate 2026-06-02 18:02:46 +00:00
Shoubhit Dash a639fe7a08 chore(opencode): remove scout agent (#30435) 2026-06-02 23:30:34 +05:30
Adam 18ba80f3ac fix(stats): remove leaderboard nav link 2026-06-02 12:16:01 -05:00
Aiden Cline 882d028ad8 fix(plugin): restore private git install fallback (#30430) 2026-06-02 11:14:41 -05:00
Aiden Cline 42a35385b0 test: widen provider header timeout margin (#30427) 2026-06-02 11:02:39 -05:00
James Long c466d32bdb fix(tui): scope diff viewer to session directory (#30426) 2026-06-02 11:43:21 -04:00
Frank f1af9c5ddf sync 2026-06-02 11:38:07 -04:00
opencode-agent[bot] 091ea86b31 chore: generate 2026-06-02 15:31:15 +00:00
Shoubhit Dash 380931a827 feat(core): expose project reference filesystem access (#30423) 2026-06-02 20:59:07 +05:30
Adam 8a17bc4de0 fix(stats): mention opencode go in top models copy 2026-06-02 09:55:28 -05:00
Adam 212ae3d25f fix(stats): clean retired provider rows during sync (#30420) 2026-06-02 14:46:07 +00:00
James Long b3da479a7c feat(core): support named migrations (#30418) 2026-06-02 14:36:28 +00:00
opencode-agent[bot] 35007094c4 chore: generate 2026-06-02 14:27:18 +00:00
Shoubhit Dash 5a8ef94998 feat(core): add flagged project references (#30414) 2026-06-02 19:55:11 +05:30
Adam d93ca9ff60 feat(stats): add cache ratio section 2026-06-02 08:52:45 -05:00
Frank 74052c7bb7 sync 2026-06-02 09:51:59 -04:00
opencode-agent[bot] 9c37286732 chore: generate 2026-06-02 13:44:07 +00:00
opencode-agent[bot] fcfd47602b chore: generate 2026-06-02 13:42:23 +00:00
Shoubhit Dash 371ee321e0 feat(core): add managed repository cache (#30408) 2026-06-02 19:10:09 +05:30
opencode-agent[bot] a78adb1b09 chore: update nix node_modules hashes 2026-06-02 11:44:02 +00:00
Simon Klee cd0fd9941d tui: revert OpenTUI upgrade to 0.2.16 (#30383) 2026-06-02 13:26:54 +02:00
Simon Klee 8936914e9d tui: show model context in run footer (#30380) 2026-06-02 13:09:03 +02:00
Brendan Allan d5a0ddb520 fix(app): tab title truncation and close button positioning (#30349) 2026-06-02 08:13:55 +00:00
Brendan Allan 787f170682 feat(app): inset new layout session panels (#30342) 2026-06-02 07:43:08 +00:00
Frank 8af7371c13 sync 2026-06-02 03:37:30 -04:00
Frank 90d85636e0 infra: stats 2026-06-02 02:41:33 -04:00
opencode-agent[bot] 6a5ef7f2ea chore: generate 2026-06-02 05:56:05 +00:00
Dax Raad 5937e606df feat(opencode): add filesystem read and list routes 2026-06-02 01:54:27 -04:00
opencode-agent[bot] 0136f03fa9 chore: generate 2026-06-02 05:40:53 +00:00
Dax Raad 4df3c98c6b feat(core): add dummy location filesystem layer 2026-06-02 01:39:04 -04:00
Dax Raad 98b7230c2a feat(core): add location filesystem contract 2026-06-02 01:39:04 -04:00
Dax Raad 8d03f6c5be refactor(core): simplify session pagination 2026-06-02 01:39:04 -04:00
Aiden Cline 1b85d55a0e fix(opencode): preserve websocket api errors (#30321) 2026-06-01 23:23:51 -05:00
opencode-agent[bot] 497ff36e91 chore: generate 2026-06-02 04:14:16 +00:00
Dax Raad c1c02f80dd feat(core): expose session location 2026-06-02 00:12:42 -04:00
opencode-agent[bot] bbec00fbd5 chore: generate 2026-06-02 03:28:04 +00:00
Aiden Cline 4668db8fa2 fix(opencode): remove sunsetted gpt-5.2 and gpt-5.3-codex from allowed models for codex subscriptions (#30316) 2026-06-01 22:20:44 -05:00
Luke Parker ab69f41067 fix(app): avoid suspending on pending child path (#30314) 2026-06-02 03:19:56 +00:00
opencode-agent[bot] 687c66248c chore: update nix node_modules hashes 2026-06-02 02:54:52 +00:00
Luke Parker 61b5046f88 fix: export v2 stylesheets and declare core node types (#30312) 2026-06-02 12:37:50 +10:00
Luke Parker 4e70eabbcc fix(app): restore deferred MCP status updates (#30220) 2026-06-02 12:11:11 +10:00
Kit Langton 1cae8f89c5 fix(tui): preserve live parts during session hydration (#30300) 2026-06-01 21:40:29 -04:00
opencode-agent[bot] 8dc2ffd48f chore: generate 2026-06-02 01:34:11 +00:00
Dax 9b815bcbd2 feat(core): add location-based permission service (#30287) 2026-06-02 01:32:50 +00:00
opencode-agent[bot] acd620f411 chore: generate 2026-06-02 00:56:03 +00:00
Luke Parker f0c7febb02 fix(opencode): enforce storage path invariants (#29666) 2026-06-02 00:54:41 +00:00
opencode-agent[bot] a821029258 chore: generate 2026-06-01 21:42:43 +00:00
Kit Langton 57d602d07a fix(tui): keep retry attempt before message (#30275) 2026-06-01 21:41:21 +00:00
Kit Langton 6072a68d6b fix(tui): keep background marker with subagent label (#30271) 2026-06-01 15:56:15 -04:00
Aiden Cline ae92f3158f feat(core): update Copilot for token-based billing (#30181) 2026-06-01 14:55:23 -05:00
James Long fa23fb5d38 fix(tui): handle events across workspaces (#30281) 2026-06-01 19:47:37 +00:00
Kit Langton 5f2b4eab28 fix(tui): clarify inline subagent rows (#30051) 2026-06-01 15:44:11 -04:00
opencode-agent[bot] 32d5058f8c chore: generate 2026-06-01 19:10:46 +00:00
Aarav Sareen 363d6d1a26 feat(app): v2 desktop UI improvements (#29689)
Co-authored-by: Brendan Allan <git@brendonovich.dev>
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
2026-06-01 19:09:09 +00:00
Adam 2bf85b8479 fix(stats): align big-pickle provider resolution (#30274) 2026-06-01 14:09:07 -05:00
Adam 1813256d8e fix(stats): stabilize top models hover 2026-06-01 12:52:11 -05:00
Adam d7ba8b1125 fix(stats): center top models dot grid 2026-06-01 12:45:58 -05:00
Adam 44350bced9 fix(stats): restore leaderboard spacing 2026-06-01 11:48:59 -05:00
opencode-agent[bot] 913c4ef0b1 chore: update nix node_modules hashes 2026-06-01 13:34:43 +00:00
opencode-agent[bot] 10095bfad4 chore: generate 2026-06-01 13:13:19 +00:00
Simon Klee 6a3b2f339a add run --replay mode (#30239) 2026-06-01 15:11:45 +02:00
Shoubhit Dash d85f8cd4d8 fix(core): contain lsp warmup defects (#30226) 2026-06-01 16:08:23 +05:30
opencode-agent[bot] 68676f2c5c chore: generate 2026-06-01 08:47:26 +00:00
Shoubhit Dash 2d2f587bfa fix(opencode): avoid nullable webfetch format schema (#30215) 2026-06-01 14:15:57 +05:30
Orca丶 bba76009a8 fix(tui): prevent prompt corruption when pasting near wide characters (#29710)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-06-01 08:42:14 +00:00
smagnuso 50b4ad89b3 fix(acp): honor session/cancel by aborting the running turn (#30145)
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-06-01 14:08:28 +05:30
opencode-agent[bot] fd2278eefc chore: generate 2026-06-01 07:27:27 +00:00
Simon Klee 7ccb7889af opencode(run): add queued prompt management (#30103)
Direct run mode previously made submitted follow-up prompts irrevocable while a response was still running. Let users edit or remove queued prompts before dispatch without interrupting the active turn.
2026-06-01 09:26:09 +02:00
Aiden Cline f9ba23ab62 refactor(session): align namespace imports and inline trivial helpers (#30180) 2026-06-01 00:00:40 -05:00
Aiden Cline b258a55a6c fix(core): preserve session metadata migration identity (#30176) 2026-06-01 04:50:43 +00:00
Michael Hart 2725eed992 fix(app): show project sessions before path sync resolves (#30167)
Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
2026-06-01 14:27:03 +10:00
Aiden Cline a9c115c220 refactor(opencode): simplify provider setup flow (#30173) 2026-05-31 23:17:57 -05:00
Aiden Cline 917a36abc3 refactor(opencode): inline local provider helpers (#30169) 2026-05-31 22:59:44 -05:00
Frank c57379833e go: minimax m3 2026-05-31 15:20:55 -04:00
opencode-agent[bot] abaabdcb73 fix(tui): remount session view on session switch (#30129)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2026-05-31 18:30:29 +00:00
Frank 8f2afba7a5 zen: deepseek flash 2026-05-31 14:01:12 -04:00
Dax Raad 31f94f205e refactor(worktree): move project out of repository 2026-05-31 14:00:01 -04:00
opencode-agent[bot] 542b082373 chore: generate 2026-05-31 17:35:31 +00:00
Dax 2f2fcc1654 fix(opencode): remove automatic full session diffs (#30127) 2026-05-31 17:34:14 +00:00
Dax Raad a69b70d5ca fix(core): allow skipping migration execution 2026-05-31 12:17:40 -04:00
Dax Raad 02edad83b2 test(tui): skip crashing keymap textarea renderer 2026-05-31 12:06:03 -04:00
Dax 5661af2034 feat(worktree): add managed workspace cloning (#30117) 2026-05-31 11:57:44 -04:00
Shoubhit Dash 331bed2469 fix(core): stabilize migration registry generation (#30105) 2026-05-31 19:28:09 +05:30
opencode-agent[bot] f401f01c05 chore: update nix node_modules hashes 2026-05-31 09:18:58 +00:00
opencode-agent[bot] 6f07f10dd2 chore: generate 2026-05-31 08:59:27 +00:00
Simon Klee e8dd8f7fe0 tui(run): use keymap instead of raw key events (#30077)
Co-authored-by: Sebastian Herrlinger <hasta84@gmail.com>
2026-05-31 10:58:12 +02:00
opencode-agent[bot] a291967206 chore: generate 2026-05-31 07:27:16 +00:00
Dax Raad a893ca857a sync 2026-05-31 03:25:47 -04:00
Dax Raad 25edeaf473 fix(sdk): preserve generated event contracts 2026-05-31 03:18:43 -04:00
opencode-agent[bot] 1afa9e32c9 chore: update nix node_modules hashes 2026-05-31 01:21:53 +00:00
opencode-agent[bot] 102c8353e0 chore: generate 2026-05-31 01:09:55 +00:00
Dax 7f571d36ea refactor(core): move database schema ownership (#29068)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-05-30 21:08:38 -04:00
opencode-agent[bot] 6bcb9cb9bb chore: generate 2026-05-31 00:04:25 +00:00
Dax Raad 0269d6f5de fix(core): isolate location layer instances 2026-05-30 20:02:14 -04:00
Dax Raad aa86a20b1d fix(core): provide agent service in location layer 2026-05-30 20:01:54 -04:00
opencode 74ce1a1edf sync release versions for v1.15.13 2026-05-30 23:40:56 +00:00
opencode-agent[bot] 9c7788bbf9 chore: generate 2026-05-30 23:31:35 +00:00
Jérôme Benoit f4f508e659 fix(opencode): support sap-ai-core anthropic opus 4.7+ adaptive reasoning (#29991) 2026-05-30 18:30:21 -05:00
opencode-agent[bot] 30f9780561 chore: update nix node_modules hashes 2026-05-30 23:13:09 +00:00
Aiden Cline 3070b0f4ae fix(opencode): default display summarized for gateway opus 4.7+ adaptive reasoning (#30027) 2026-05-30 18:07:06 -05:00
opencode-agent[bot] 52e288ea71 chore: generate 2026-05-30 22:59:16 +00:00
Dax Raad 9b915e70bd v2: default agents 2026-05-30 18:57:42 -04:00
Shantur Rathore ddc30cd151 feat(core): add session metadata support (#23068)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-05-30 16:58:11 -05:00
Frank ac8e686f33 zen: batch balance calculation 2026-05-30 10:53:35 -04:00
opencode-agent[bot] 69b2784b13 chore: generate 2026-05-30 08:56:10 +00:00
vimtor 6a2cd816b7 core: credit referral invites on first Lite checkout
Applies saved invites during Lite checkout so users who skip the referral page still credit the inviter. Avoids crediting already-active Lite workspaces so rewards stay tied to the first upgrade.
2026-05-30 10:52:21 +02:00
opencode-agent[bot] 04c46117b9 chore: generate 2026-05-30 05:13:21 +00:00
Frank 51a9b0baad zen: log ip prefix 2026-05-30 01:11:20 -04:00
opencode-agent[bot] bcc345ff82 chore: update nix node_modules hashes 2026-05-30 05:09:31 +00:00
opencode-agent[bot] 0d2508ffd5 chore: generate 2026-05-30 04:51:39 +00:00
Dax Raad f6062bd5b6 feat(core): add default agent plugin 2026-05-30 00:49:52 -04:00
Dax Raad 181e58f50a feat(cli): add effect cli scaffold 2026-05-30 00:49:52 -04:00
opencode-agent[bot] 1ceb5dbd56 chore: update nix node_modules hashes 2026-05-30 04:19:30 +00:00
opencode-agent[bot] e4d3b81b0e chore: generate 2026-05-30 04:07:26 +00:00
Dax 9583e08be4 feat(core): add location-scoped config loading (#29625) 2026-05-30 04:06:08 +00:00
Kit Langton 5fb85a6aa3 fix(tui): align wrapped inline tool rows (#28664) 2026-05-29 21:50:05 -04:00
opencode-agent[bot] b2a06351b5 chore: generate 2026-05-29 21:02:33 +00:00
James Murdza 9d5f3c1833 fix(workspaces): surface real error messages on failed workspace operations (#29167)
Signed-off-by: James Murdza <james@jamesmurdza.com>
2026-05-29 17:00:23 -04:00
Adam 7342e94094 fix(stats): scroll dense mobile charts 2026-05-29 15:11:21 -05:00
opencode-agent[bot] 8fe6cd9932 chore: generate 2026-05-29 20:06:03 +00:00
Shoubhit Dash 4cc166a400 feat(acp): promote next implementation (#29929) 2026-05-30 01:34:44 +05:30
Aiden Cline 0733c080c0 ignore: bug report template
Updated bug report template to discourage large AI-generated summaries.
2026-05-29 14:54:45 -05:00
opencode-agent[bot] 73c7bef1d0 chore: generate 2026-05-29 19:22:38 +00:00
Adam 9854c5d910 fix(stats): stabilize stacked chart bands 2026-05-29 14:20:23 -05:00
opencode-agent[bot] 494b55a2a7 chore: generate 2026-05-29 19:02:42 +00:00
Adam aa22cf841d fix(stats): reorder market share section 2026-05-29 14:00:08 -05:00
Adam c977cb7f31 Combine top models and leaderboard sections 2026-05-29 14:00:08 -05:00
James Long a4bc76d2a4 chore: remove improve codebase architecture skill (#29926) 2026-05-29 14:57:55 -04:00
opencode-agent[bot] 39cd14826a chore: generate 2026-05-29 18:34:40 +00:00
Adam 6b0e5499f5 fix(stats): tighten 2m chart gaps 2026-05-29 13:31:25 -05:00
opencode-agent[bot] a87b27a8be chore: generate 2026-05-29 18:24:08 +00:00
Adam ce4e0e8a4e fix(stats): rotate dense chart labels 2026-05-29 13:22:25 -05:00
Aiden Cline b956e9a06f fix(opencode): support vertex opus adaptive reasoning (#29911) 2026-05-29 11:35:26 -05:00
Adam 16cae9a323 fix(stats): stack largest segments at bottom 2026-05-29 09:30:32 -05:00
Adam 0a60dbdcee fix(stats): stabilize newsletter form reset 2026-05-29 09:03:16 -05:00
Adam bccebb8a36 fix(stats): refine newsletter modal 2026-05-29 08:08:57 -05:00
Adam 76dbe10ad7 feat(stats): match theme icons to figma (#29893) 2026-05-29 08:04:34 -05:00
Adam 46836beec9 fix(stats): add social unfurl metadata (#29892) 2026-05-29 08:02:00 -05:00
Adam e84988b2a3 fix(stats): ignore partial may 27 athena data (#29891) 2026-05-29 12:59:47 +00:00
vimtor 3f6cb631c3 fix: referral edge case 2026-05-29 14:50:16 +02:00
Adam e9966b651f feat(stats): improve mobile leaderboard layout 2026-05-29 07:46:10 -05:00
Adam dc28b82394 Add live GitHub star count to stats page 2026-05-29 07:46:09 -05:00
Adam fb6275e04a fix(stats): improve mobile charts 2026-05-29 07:46:09 -05:00
Jack 6102fb2e3f docs: update Go availability and Zen pricing (#29890) 2026-05-29 20:39:23 +08:00
Shoubhit Dash 7da2620078 fix(tui): pin dialog select footer to bottom (#29878) 2026-05-29 16:43:50 +05:30
vimtor 5764f19937 core: credit users for missed referral rewards 2026-05-29 13:04:53 +02:00
opencode-agent[bot] 41198186be chore: generate 2026-05-29 10:11:21 +00:00
Shoubhit Dash 8f8b161cae feat(tui): add session switcher plugin (#29861) 2026-05-29 15:39:34 +05:30
opencode-agent[bot] 710ed7cb33 chore: generate 2026-05-29 09:21:36 +00:00
Shoubhit Dash fa73ec4fa6 fix(opencode): serialize mcp auth mutations (#29852) 2026-05-29 14:50:15 +05:30
Aiden Cline c7e1fc5e42 fix(opencode): retry stalled SSE streams (#29837) 2026-05-29 00:19:54 -05:00
Aiden Cline a15d4f9f04 fix(openai): proxy websocket connections under bun (#29832) 2026-05-28 23:53:07 -05:00
opencode-agent[bot] 43f110a6cb chore: generate 2026-05-29 03:49:46 +00:00
Luke Parker 29d17b9055 fix(opencode): allow pid 0 in Pty.Info for Windows ConPTY (#29828) 2026-05-29 13:47:50 +10:00
opencode-agent[bot] 031f82adc8 chore: update nix node_modules hashes 2026-05-29 00:51:14 +00:00
Luke Parker 672b8c4762 fix(desktop): bump @lydell/node-pty to 1.2.0-beta.12 (#29803) 2026-05-29 10:35:20 +10:00
Adam 0dc44772df fix(stats): tune dark tooltip colors 2026-05-28 19:14:41 -05:00
Saurav M H 797c689ec2 fix(opencode): pass OAuth scopes to GoogleAuth for Vertex AI (#15110)
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-05-28 19:05:43 -05:00
opencode-agent[bot] acca8864ba chore: generate 2026-05-28 23:51:26 +00:00
Adam 7480b97630 fix(stats): proxy build assets under stats path 2026-05-28 18:49:03 -05:00
opencode-agent[bot] edc7a20cea chore: update nix node_modules hashes 2026-05-28 23:38:53 +00:00
Adam 58da1ea48e feat(stats): route stats app 2026-05-28 18:37:13 -05:00
Adam 0d67d659fc feat(stats): polish rankings page 2026-05-28 18:22:56 -05:00
opencode-agent[bot] 6a9087007c chore: generate 2026-05-28 22:44:48 +00:00
Adam c63d532cc2 refactor: simplify the changed workflow 2026-05-28 17:43:15 -05:00
Adam 629d5593eb feat(stats): split updated timestamp ticker 2026-05-28 17:43:14 -05:00
Adam ffbf9df18e Refine leaderboard layout and styling 2026-05-28 17:43:14 -05:00
Adam e3a6255c72 Fix touch interaction in top models chart 2026-05-28 17:43:14 -05:00
Frank 9f15715877 zen: sync 2026-05-28 18:41:17 -04:00
opencode-agent[bot] ecd645b023 chore: update nix node_modules hashes 2026-05-28 22:08:51 +00:00
NoisemakerJon 4d894d129e fix(app): prevent horizontal jitter on /go page (#29796) 2026-05-28 16:56:24 -05:00
opencode-agent[bot] 4759ae44b4 chore: generate 2026-05-28 21:54:17 +00:00
Vladimir Glafirov e00a62e461 chore: bump gitlab-ai-provider to 6.8.0 (#29792) 2026-05-28 16:52:26 -05:00
Luke Parker e16bfd745d fix(app): start MCP servers only for open directories (#28937) 2026-05-28 21:51:11 +00:00
Aiden Cline 7b56a1cea3 fix(tui): order connected provider models by release date (#29798) 2026-05-28 16:37:32 -05:00
Guilherme França 1f707f1b52 fix(dialog-provider): change url for opencode-go provider to correct… (#29780) 2026-05-28 15:49:24 -05:00
opencode-agent[bot] caf2451dae chore: generate 2026-05-28 20:01:06 +00:00
William Ricchiuti de7bb076a3 docs: add opencode-goal-plugin to ecosystem (#29788) 2026-05-28 14:59:24 -05:00
Vadim Safonov d924685939 docs: fix russian translation (#29742) 2026-05-28 14:59:05 -05:00
opencode 569230bfbc sync release versions for v1.15.12 2026-05-28 19:34:46 +00:00
Aiden Cline cbc4b062f7 fix(openai): preserve websocket upgrade error messages (#29774) 2026-05-28 14:34:28 -05:00
opencode-agent[bot] 824ecf2f4d chore: generate 2026-05-28 19:33:59 +00:00
Adam b623d86f10 Improve top models chart mobile axis 2026-05-28 14:32:09 -05:00
Adam 93ba2dd24a Remove hero borders from stats page 2026-05-28 14:32:08 -05:00
Adam f55b70b4e3 feat(stats): refine top models chart scaling 2026-05-28 14:32:08 -05:00
Adam 982b7d39e7 Adjust stats header layout 2026-05-28 14:32:08 -05:00
opencode-agent[bot] 2d15d89140 chore: update nix node_modules hashes 2026-05-28 18:05:19 +00:00
Frank 2eb0606998 zen: add opus 4.8 2026-05-28 13:48:05 -04:00
opencode-agent[bot] cc230feca1 chore: generate 2026-05-28 17:45:17 +00:00
Adam 3ce9b4be0d fix(stats): refine hero layout for responsive Figma match 2026-05-28 12:43:16 -05:00
Adam fff7781fa8 fix(stats): preload IBM Plex Mono weights 2026-05-28 12:43:15 -05:00
Adam 0a72298062 feat(stats): better hero 2026-05-28 12:43:15 -05:00
Adam 7c320fd463 feat(stats): better header 2026-05-28 12:43:15 -05:00
Aiden Cline 05e3c4ecee fix(opencode): enable adaptive reasoning for opus 4.7+ (#29769) 2026-05-28 12:19:34 -05:00
Filip 72d008bd5c fix(core): allow colons inside passwords (#29576)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
2026-05-28 13:17:35 -04:00
Avnish Uba 202ec12056 docs: fix grammar in config.mdx (#29683)
Co-authored-by: dnsnav <dnsnav@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-28 11:25:34 -05:00
Aiden Cline af30b315b2 fix(tui): restore thinking spinner (#29765) 2026-05-28 11:24:45 -05:00
935 changed files with 55760 additions and 33436 deletions
+2
View File
@@ -0,0 +1,2 @@
packages/core/migration/**/snapshot.json linguist-generated
packages/core/src/database/migration.gen.ts linguist-generated
+1 -1
View File
@@ -1,5 +1,5 @@
name: Bug report
description: Report an issue that should be fixed
description: Report an issue that should be fixed (avoid pasting giant AI generated summaries or your issue may be closed/ignored)
body:
- type: textarea
id: description
+3
View File
@@ -2,6 +2,9 @@
"$schema": "https://opencode.ai/config.json",
"provider": {},
"permission": {},
"reference": {
"effect": "github.com/Effect-TS/effect-smol",
},
"mcp": {},
"tools": {
"github-triage": false,
@@ -1,37 +0,0 @@
# Deepening
How to deepen a cluster of shallow modules safely, given its dependencies. Assumes the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**.
## Dependency categories
When assessing a candidate for deepening, classify its dependencies. The category determines how the deepened module is tested across its seam.
### 1. In-process
Pure computation, in-memory state, no I/O. Always deepenable — merge the modules and test through the new interface directly. No adapter needed.
### 2. Local-substitutable
Dependencies that have local test stand-ins (PGLite for Postgres, in-memory filesystem). Deepenable if the stand-in exists. The deepened module is tested with the stand-in running in the test suite. The seam is internal; no port at the module's external interface.
### 3. Remote but owned (Ports & Adapters)
Your own services across a network boundary (microservices, internal APIs). Define a **port** (interface) at the seam. The deep module owns the logic; the transport is injected as an **adapter**. Tests use an in-memory adapter. Production uses an HTTP/gRPC/queue adapter.
Recommendation shape: _"Define a port at the seam, implement an HTTP adapter for production and an in-memory adapter for testing, so the logic sits in one deep module even though it's deployed across a network."_
### 4. True external (Mock)
Third-party services (Stripe, Twilio, etc.) you don't control. The deepened module takes the external dependency as an injected port; tests provide a mock adapter.
## Seam discipline
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a port unless at least two adapters are justified (typically production + test). A single-adapter seam is just indirection.
- **Internal seams vs external seams.** A deep module can have internal seams (private to its implementation, used by its own tests) as well as the external seam at its interface. Don't expose internal seams through the interface just because tests use them.
## Testing strategy: replace, don't layer
- Old unit tests on shallow modules become waste once tests at the deepened module's interface exist — delete them.
- Write new tests at the deepened module's interface. The **interface is the test surface**.
- Tests assert on observable outcomes through the interface, not internal state.
- Tests should survive internal refactors — they describe behaviour, not implementation. If a test has to change when the implementation changes, it's testing past the interface.
@@ -1,44 +0,0 @@
# Interface Design
When the user wants to explore alternative interfaces for a chosen deepening candidate, use this parallel sub-agent pattern. Based on "Design It Twice" (Ousterhout) — your first idea is unlikely to be the best.
Uses the vocabulary in [LANGUAGE.md](LANGUAGE.md) — **module**, **interface**, **seam**, **adapter**, **leverage**.
## Process
### 1. Frame the problem space
Before spawning sub-agents, write a user-facing explanation of the problem space for the chosen candidate:
- The constraints any new interface would need to satisfy
- The dependencies it would rely on, and which category they fall into (see [DEEPENING.md](DEEPENING.md))
- A rough illustrative code sketch to ground the constraints — not a proposal, just a way to make the constraints concrete
Show this to the user, then immediately proceed to Step 2. The user reads and thinks while the sub-agents work in parallel.
### 2. Spawn sub-agents
Spawn 3+ sub-agents in parallel using the Agent tool. Each must produce a **radically different** interface for the deepened module.
Prompt each sub-agent with a separate technical brief (file paths, coupling details, dependency category from [DEEPENING.md](DEEPENING.md), what sits behind the seam). The brief is independent of the user-facing problem-space explanation in Step 1. Give each agent a different design constraint:
- Agent 1: "Minimize the interface — aim for 13 entry points max. Maximise leverage per entry point."
- Agent 2: "Maximise flexibility — support many use cases and extension."
- Agent 3: "Optimise for the most common caller — make the default case trivial."
- Agent 4 (if applicable): "Design around ports & adapters for cross-seam dependencies."
Include both [LANGUAGE.md](LANGUAGE.md) vocabulary and CONTEXT.md vocabulary in the brief so each sub-agent names things consistently with the architecture language and the project's domain language.
Each sub-agent outputs:
1. Interface (types, methods, params — plus invariants, ordering, error modes)
2. Usage example showing how callers use it
3. What the implementation hides behind the seam
4. Dependency strategy and adapters (see [DEEPENING.md](DEEPENING.md))
5. Trade-offs — where leverage is high, where it's thin
### 3. Present and compare
Present designs sequentially so the user can absorb each one, then compare them in prose. Contrast by **depth** (leverage at the interface), **locality** (where change concentrates), and **seam placement**.
After comparing, give your own recommendation: which design you think is strongest and why. If elements from different designs would combine well, propose a hybrid. Be opinionated — the user wants a strong read, not a menu.
@@ -1,53 +0,0 @@
# Language
Shared vocabulary for every suggestion this skill makes. Use these terms exactly — don't substitute "component," "service," "API," or "boundary." Consistent language is the whole point.
## Terms
**Module**
Anything with an interface and an implementation. Deliberately scale-agnostic — applies equally to a function, class, package, or tier-spanning slice.
_Avoid_: unit, component, service.
**Interface**
Everything a caller must know to use the module correctly. Includes the type signature, but also invariants, ordering constraints, error modes, required configuration, and performance characteristics.
_Avoid_: API, signature (too narrow — those refer only to the type-level surface).
**Implementation**
What's inside a module — its body of code. Distinct from **Adapter**: a thing can be a small adapter with a large implementation (a Postgres repo) or a large adapter with a small implementation (an in-memory fake). Reach for "adapter" when the seam is the topic; "implementation" otherwise.
**Depth**
Leverage at the interface — the amount of behaviour a caller (or test) can exercise per unit of interface they have to learn. A module is **deep** when a large amount of behaviour sits behind a small interface. A module is **shallow** when the interface is nearly as complex as the implementation.
**Seam** _(from Michael Feathers)_
A place where you can alter behaviour without editing in that place. The _location_ at which a module's interface lives. Choosing where to put the seam is its own design decision, distinct from what goes behind it.
_Avoid_: boundary (overloaded with DDD's bounded context).
**Adapter**
A concrete thing that satisfies an interface at a seam. Describes _role_ (what slot it fills), not substance (what's inside).
**Leverage**
What callers get from depth. More capability per unit of interface they have to learn. One implementation pays back across N call sites and M tests.
**Locality**
What maintainers get from depth. Change, bugs, knowledge, and verification concentrate at one place rather than spreading across callers. Fix once, fixed everywhere.
## Principles
- **Depth is a property of the interface, not the implementation.** A deep module can be internally composed of small, mockable, swappable parts — they just aren't part of the interface. A module can have **internal seams** (private to its implementation, used by its own tests) as well as the **external seam** at its interface.
- **The deletion test.** Imagine deleting the module. If complexity vanishes, the module wasn't hiding anything (it was a pass-through). If complexity reappears across N callers, the module was earning its keep.
- **The interface is the test surface.** Callers and tests cross the same seam. If you want to test _past_ the interface, the module is probably the wrong shape.
- **One adapter means a hypothetical seam. Two adapters means a real one.** Don't introduce a seam unless something actually varies across it.
## Relationships
- A **Module** has exactly one **Interface** (the surface it presents to callers and tests).
- **Depth** is a property of a **Module**, measured against its **Interface**.
- A **Seam** is where a **Module**'s **Interface** lives.
- An **Adapter** sits at a **Seam** and satisfies the **Interface**.
- **Depth** produces **Leverage** for callers and **Locality** for maintainers.
## Rejected framings
- **Depth as ratio of implementation-lines to interface-lines** (Ousterhout): rewards padding the implementation. We use depth-as-leverage instead.
- **"Interface" as the TypeScript `interface` keyword or a class's public methods**: too narrow — interface here includes every fact a caller must know.
- **"Boundary"**: overloaded with DDD's bounded context. Say **seam** or **interface**.
@@ -1,71 +0,0 @@
---
name: improve-codebase-architecture
description: Find deepening opportunities in a codebase, informed by the domain language in CONTEXT.md and the decisions in docs/adr/. Use when the user wants to improve architecture, find refactoring opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable.
---
# Improve Codebase Architecture
Surface architectural friction and propose **deepening opportunities** — refactors that turn shallow modules into deep ones. The aim is testability and AI-navigability.
## Glossary
Use these terms exactly in every suggestion. Consistent language is the point — don't drift into "component," "service," "API," or "boundary." Full definitions in [LANGUAGE.md](LANGUAGE.md).
- **Module** — anything with an interface and an implementation (function, class, package, slice).
- **Interface** — everything a caller must know to use the module: types, invariants, error modes, ordering, config. Not just the type signature.
- **Implementation** — the code inside.
- **Depth** — leverage at the interface: a lot of behaviour behind a small interface. **Deep** = high leverage. **Shallow** = interface nearly as complex as the implementation.
- **Seam** — where an interface lives; a place behaviour can be altered without editing in place. (Use this, not "boundary.")
- **Adapter** — a concrete thing satisfying an interface at a seam.
- **Leverage** — what callers get from depth.
- **Locality** — what maintainers get from depth: change, bugs, knowledge concentrated in one place.
Key principles (see [LANGUAGE.md](LANGUAGE.md) for the full list):
- **Deletion test**: imagine deleting the module. If complexity vanishes, it was a pass-through. If complexity reappears across N callers, it was earning its keep.
- **The interface is the test surface.**
- **One adapter = hypothetical seam. Two adapters = real seam.**
This skill is _informed_ by the project's domain model. The domain language gives names to good seams; ADRs record decisions the skill should not re-litigate.
## Process
### 1. Explore
Read the project's domain glossary and any ADRs in the area you're touching first.
Then use the Agent tool with `subagent_type=Explore` to walk the codebase. Don't follow rigid heuristics — explore organically and note where you experience friction:
- Where does understanding one concept require bouncing between many small modules?
- Where are modules **shallow** — interface nearly as complex as the implementation?
- Where have pure functions been extracted just for testability, but the real bugs hide in how they're called (no **locality**)?
- Where do tightly-coupled modules leak across their seams?
- Which parts of the codebase are untested, or hard to test through their current interface?
Apply the **deletion test** to anything you suspect is shallow: would deleting it concentrate complexity, or just move it? A "yes, concentrates" is the signal you want.
### 2. Present candidates
Present a numbered list of deepening opportunities. For each candidate:
- **Files** — which files/modules are involved
- **Problem** — why the current architecture is causing friction
- **Solution** — plain English description of what would change
- **Benefits** — explained in terms of locality and leverage, and also in how tests would improve
**Use CONTEXT.md vocabulary for the domain, and [LANGUAGE.md](LANGUAGE.md) vocabulary for the architecture.** If `CONTEXT.md` defines "Order," talk about "the Order intake module" — not "the FooBarHandler," and not "the Order service."
**ADR conflicts**: if a candidate contradicts an existing ADR, only surface it when the friction is real enough to warrant revisiting the ADR. Mark it clearly (e.g. _"contradicts ADR-0007 — but worth reopening because…"_). Don't list every theoretical refactor an ADR forbids.
Do NOT propose interfaces yet. Ask the user: "Which of these would you like to explore?"
### 3. Grilling loop
Once the user picks a candidate, drop into a grilling conversation. Walk the design tree with them — constraints, dependencies, the shape of the deepened module, what sits behind the seam, what tests survive.
Side effects happen inline as decisions crystallize:
- **Naming a deepened module after a concept not in `CONTEXT.md`?** Add the term to `CONTEXT.md` — same discipline as `/grill-with-docs` (see [CONTEXT-FORMAT.md](../grill-with-docs/CONTEXT-FORMAT.md)). Create the file lazily if it doesn't exist.
- **Sharpening a fuzzy term during the conversation?** Update `CONTEXT.md` right there.
- **User rejects the candidate with a load-bearing reason?** Offer an ADR, framed as: _"Want me to record this as an ADR so future architecture reviews don't re-suggest it?"_ Only offer when the reason would actually be needed by a future explorer to avoid re-suggesting the same thing — skip ephemeral reasons ("not worth it right now") and self-evident ones. See [ADR-FORMAT.md](../grill-with-docs/ADR-FORMAT.md).
- **Want to explore alternative interfaces for the deepened module?** See [INTERFACE-DESIGN.md](INTERFACE-DESIGN.md).
+7
View File
@@ -47,6 +47,13 @@ obj.b
const { a, b } = obj
```
### Imports
- Never alias imports. Do not use `import { foo as bar } from "..."` or renamed imports like `resolve as pathResolve`.
- Never use star imports. Do not use `import * as Foo from "..."` or `import type * as Foo from "..."`.
- If a namespace-style value is needed, import the module's own exported namespace by name, for example `import { Project } from "@opencode-ai/core/project"`, then reference `Project.ID`.
- Prefer dynamic imports for heavy modules that are only needed in selected code paths, especially in startup-sensitive entrypoints. Destructure dynamic import bindings near the top of the narrowest scope that needs them so they read like normal imports. Avoid inline chains such as `await import("./module").then((mod) => mod.value())` or `(await import("./module")).value()`. Keep branch-specific imports inside the branch that needs them to preserve lazy loading.
### Variables
Prefer `const` over `let`. Use ternaries or early returns instead of reassignment.
+190 -92
View File
@@ -29,7 +29,7 @@
},
"packages/app": {
"name": "@opencode-ai/app",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -83,9 +83,26 @@
"vite-plugin-solid": "catalog:",
},
},
"packages/cli": {
"name": "@opencode-ai/cli",
"version": "1.15.13",
"bin": {
"opencode": "./src/index.ts",
},
"dependencies": {
"@effect/platform-node": "catalog:",
"@opencode-ai/core": "workspace:*",
"effect": "catalog:",
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:",
},
},
"packages/console/app": {
"name": "@opencode-ai/console-app",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1",
@@ -121,7 +138,7 @@
},
"packages/console/core": {
"name": "@opencode-ai/console-core",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1",
@@ -148,7 +165,7 @@
},
"packages/console/function": {
"name": "@opencode-ai/console-function",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@ai-sdk/anthropic": "3.0.64",
"@ai-sdk/openai": "3.0.48",
@@ -170,7 +187,7 @@
},
"packages/console/mail": {
"name": "@opencode-ai/console-mail",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3",
@@ -194,7 +211,7 @@
},
"packages/console/support": {
"name": "@opencode-ai/console-support",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@cloudflare/vite-plugin": "1.15.2",
"@opencode-ai/console-core": "workspace:*",
@@ -214,7 +231,7 @@
},
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.15.11",
"version": "1.15.13",
"bin": {
"opencode": "./bin/opencode",
},
@@ -242,43 +259,62 @@
"@aws-sdk/credential-providers": "3.993.0",
"@effect/opentelemetry": "catalog:",
"@effect/platform-node": "catalog:",
"@effect/sql-sqlite-bun": "catalog:",
"@npmcli/arborist": "9.4.0",
"@npmcli/config": "10.8.1",
"@opencode-ai/effect-drizzle-sqlite": "workspace:*",
"@opencode-ai/effect-sqlite-node": "workspace:*",
"@openrouter/ai-sdk-provider": "2.8.1",
"@opentelemetry/api": "1.9.0",
"@opentelemetry/context-async-hooks": "2.6.1",
"@opentelemetry/exporter-trace-otlp-http": "0.214.0",
"@opentelemetry/sdk-trace-base": "2.6.1",
"@parcel/watcher": "2.5.1",
"ai-gateway-provider": "3.1.2",
"cross-spawn": "catalog:",
"drizzle-orm": "catalog:",
"effect": "catalog:",
"gitlab-ai-provider": "6.7.0",
"fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.8.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"ignore": "7.0.5",
"immer": "11.1.4",
"jsonc-parser": "3.3.1",
"mime-types": "3.0.2",
"minimatch": "10.2.5",
"npm-package-arg": "13.0.2",
"semver": "^7.6.3",
"venice-ai-sdk-provider": "2.0.2",
"which": "6.0.1",
"xdg-basedir": "5.1.0",
"zod": "catalog:",
},
"devDependencies": {
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/cross-spawn": "catalog:",
"@types/node": "catalog:",
"@types/npm-package-arg": "6.1.4",
"@types/npmcli__arborist": "6.3.3",
"@types/semver": "catalog:",
"@types/which": "3.0.4",
"drizzle-kit": "catalog:",
},
},
"packages/desktop": {
"name": "@opencode-ai/desktop",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@zip.js/zip.js": "2.7.62",
"drizzle-orm": "catalog:",
"effect": "catalog:",
"electron-context-menu": "4.1.2",
"electron-log": "^5",
@@ -312,12 +348,12 @@
"zod-openapi": "5.4.6",
},
"optionalDependencies": {
"@lydell/node-pty-darwin-arm64": "1.2.0-beta.10",
"@lydell/node-pty-darwin-x64": "1.2.0-beta.10",
"@lydell/node-pty-linux-arm64": "1.2.0-beta.10",
"@lydell/node-pty-linux-x64": "1.2.0-beta.10",
"@lydell/node-pty-win32-arm64": "1.2.0-beta.10",
"@lydell/node-pty-win32-x64": "1.2.0-beta.10",
"@lydell/node-pty-darwin-arm64": "1.2.0-beta.12",
"@lydell/node-pty-darwin-x64": "1.2.0-beta.12",
"@lydell/node-pty-linux-arm64": "1.2.0-beta.12",
"@lydell/node-pty-linux-x64": "1.2.0-beta.12",
"@lydell/node-pty-win32-arm64": "1.2.0-beta.12",
"@lydell/node-pty-win32-x64": "1.2.0-beta.12",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
@@ -330,7 +366,7 @@
},
"packages/effect-drizzle-sqlite": {
"name": "@opencode-ai/effect-drizzle-sqlite",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:",
@@ -342,9 +378,21 @@
"@typescript/native-preview": "catalog:",
},
},
"packages/effect-sqlite-node": {
"name": "@opencode-ai/effect-sqlite-node",
"version": "1.15.10",
"dependencies": {
"effect": "catalog:",
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/node": "catalog:",
"@typescript/native-preview": "catalog:",
},
},
"packages/enterprise": {
"name": "@opencode-ai/enterprise",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@opencode-ai/core": "workspace:*",
"@opencode-ai/ui": "workspace:*",
@@ -374,7 +422,7 @@
},
"packages/function": {
"name": "@opencode-ai/function",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:",
@@ -390,7 +438,7 @@
},
"packages/http-recorder": {
"name": "@opencode-ai/http-recorder",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@effect/platform-node": "catalog:",
"effect": "catalog:",
@@ -403,7 +451,7 @@
},
"packages/llm": {
"name": "@opencode-ai/llm",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@smithy/eventstream-codec": "4.2.14",
"@smithy/util-utf8": "4.2.2",
@@ -421,7 +469,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "1.15.11",
"version": "1.15.13",
"bin": {
"opencode": "./bin/opencode",
},
@@ -492,12 +540,11 @@
"drizzle-orm": "catalog:",
"effect": "catalog:",
"fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.7.0",
"gitlab-ai-provider": "6.8.0",
"glob": "13.0.5",
"google-auth-library": "10.5.0",
"gray-matter": "4.0.3",
"htmlparser2": "8.0.2",
"ignore": "7.0.5",
"immer": "11.1.4",
"jsonc-parser": "3.3.1",
"mime-types": "3.0.2",
@@ -519,7 +566,6 @@
"venice-ai-sdk-provider": "2.0.2",
"vscode-jsonrpc": "8.2.1",
"web-tree-sitter": "0.25.10",
"which": "6.0.1",
"ws": "8.21.0",
"xdg-basedir": "5.1.0",
"yargs": "18.0.0",
@@ -531,14 +577,6 @@
"@opencode-ai/core": "workspace:*",
"@opencode-ai/http-recorder": "workspace:*",
"@opencode-ai/script": "workspace:*",
"@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1",
"@parcel/watcher-linux-arm64-musl": "2.5.1",
"@parcel/watcher-linux-x64-glibc": "2.5.1",
"@parcel/watcher-linux-x64-musl": "2.5.1",
"@parcel/watcher-win32-arm64": "2.5.1",
"@parcel/watcher-win32-x64": "2.5.1",
"@standard-schema/spec": "1.0.0",
"@tsconfig/bun": "catalog:",
"@types/babel__core": "7.20.5",
@@ -548,10 +586,8 @@
"@types/npm-package-arg": "6.1.4",
"@types/semver": "^7.5.8",
"@types/turndown": "5.0.5",
"@types/which": "3.0.4",
"@types/yargs": "17.0.33",
"@typescript/native-preview": "catalog:",
"drizzle-kit": "catalog:",
"drizzle-orm": "catalog:",
"prettier": "3.6.2",
"typescript": "catalog:",
@@ -561,7 +597,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"effect": "catalog:",
@@ -577,9 +613,9 @@
"typescript": "catalog:",
},
"peerDependencies": {
"@opentui/core": ">=0.2.16",
"@opentui/keymap": ">=0.2.16",
"@opentui/solid": ">=0.2.16",
"@opentui/core": ">=0.3.1",
"@opentui/keymap": ">=0.3.1",
"@opentui/solid": ">=0.3.1",
},
"optionalPeers": [
"@opentui/core",
@@ -599,7 +635,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"cross-spawn": "catalog:",
},
@@ -614,7 +650,7 @@
},
"packages/slack": {
"name": "@opencode-ai/slack",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1",
@@ -627,8 +663,9 @@
},
"packages/stats/app": {
"name": "@opencode-ai/stats-app",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@ibm/plex": "6.4.1",
"@opencode-ai/stats-core": "workspace:*",
"@opencode-ai/ui": "workspace:*",
"@solidjs/meta": "catalog:",
@@ -638,6 +675,7 @@
"effect": "catalog:",
"nitro": "3.0.1-alpha.1",
"solid-js": "catalog:",
"sst": "catalog:",
"vite": "catalog:",
},
"devDependencies": {
@@ -650,7 +688,7 @@
},
"packages/stats/core": {
"name": "@opencode-ai/stats-core",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@aws-sdk/client-athena": "3.933.0",
"@planetscale/database": "1.19.0",
@@ -669,7 +707,7 @@
},
"packages/stats/server": {
"name": "@opencode-ai/stats-server",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@aws-sdk/client-firehose": "3.933.0",
"@effect/platform-node": "catalog:",
@@ -709,7 +747,7 @@
},
"packages/ui": {
"name": "@opencode-ai/ui",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
@@ -758,7 +796,7 @@
},
"packages/web": {
"name": "@opencode-ai/web",
"version": "1.15.11",
"version": "1.15.13",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -803,8 +841,10 @@
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch",
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"pacote@21.5.0": "patches/pacote@21.5.0.patch",
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch",
},
@@ -817,18 +857,18 @@
},
"catalog": {
"@cloudflare/workers-types": "4.20251008.0",
"@effect/opentelemetry": "4.0.0-beta.66",
"@effect/platform-node": "4.0.0-beta.66",
"@effect/sql-sqlite-bun": "4.0.0-beta.66",
"@effect/opentelemetry": "4.0.0-beta.74",
"@effect/platform-node": "4.0.0-beta.74",
"@effect/sql-sqlite-bun": "4.0.0-beta.74",
"@hono/zod-validator": "0.4.2",
"@kobalte/core": "0.13.11",
"@lydell/node-pty": "1.2.0-beta.10",
"@lydell/node-pty": "1.2.0-beta.12",
"@npmcli/arborist": "9.4.0",
"@octokit/rest": "22.0.0",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@opentui/core": "0.2.16",
"@opentui/keymap": "0.2.16",
"@opentui/solid": "0.2.16",
"@opentui/core": "0.3.1",
"@opentui/keymap": "0.3.1",
"@opentui/solid": "0.3.1",
"@pierre/diffs": "1.1.0-beta.18",
"@playwright/test": "1.59.1",
"@sentry/solid": "10.36.0",
@@ -852,7 +892,7 @@
"dompurify": "3.3.1",
"drizzle-kit": "1.0.0-rc.2",
"drizzle-orm": "1.0.0-rc.2",
"effect": "4.0.0-beta.66",
"effect": "4.0.0-beta.74",
"fuzzysort": "3.1.0",
"hono": "4.10.7",
"hono-openapi": "1.1.2",
@@ -1220,13 +1260,13 @@
"@drizzle-team/brocli": ["@drizzle-team/brocli@0.11.0", "", {}, "sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg=="],
"@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.66", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.66" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-LU3ejAzJS+4P+Qtfn9ULnsGcIPmx1tUUB2ZswFRL+EolD8US7zMljHTwGuQRUBJOjDwt7wFCMN5AR512vdY8FQ=="],
"@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.74", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@opentelemetry/api-logs": ">=0.203.0 <0.300.0", "@opentelemetry/resources": "^2.0.0", "@opentelemetry/sdk-logs": ">=0.203.0 <0.300.0", "@opentelemetry/sdk-metrics": "^2.0.0", "@opentelemetry/sdk-trace-base": "^2.0.0", "@opentelemetry/sdk-trace-node": "^2.0.0", "@opentelemetry/sdk-trace-web": "^2.0.0", "@opentelemetry/semantic-conventions": "^1.33.0", "effect": "^4.0.0-beta.74" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/api-logs", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-flpyqLPyr+THSe6ZCGRZl6hi+FqxbIXNSkslKGiRJAjbPabam9mSp7R3aC8biIMt6xE4Fd0LNfo4p2GplUkm2Q=="],
"@effect/platform-node": ["@effect/platform-node@4.0.0-beta.66", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.66", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.66", "ioredis": "^5.7.0" } }, "sha512-s/0RgaQFuszzdorRnX1PwEQNnSOi+JgMJo3zEe9O2NR3sosMhTr0Uk+1AF6bUOI9uJ2CPT3KpTIIU7q5/TpOkg=="],
"@effect/platform-node": ["@effect/platform-node@4.0.0-beta.74", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.74", "mime": "^4.1.0", "undici": "^8.2.0" }, "peerDependencies": { "effect": "^4.0.0-beta.74", "ioredis": "^5.7.0" } }, "sha512-/W16mKqxvhWINLjufzc0log1sl57exXQfwd+em398/zKCbmU3S7snXTDMN6w0ju2TtgK35qrsoGBXEochij6Sg=="],
"@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.66", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.66" } }, "sha512-+ymrhBnESv/hmn5SKTe2//IY9Ox/hGPeoogEWhW47ZGyhFI5eMYFxdEUBa+3IAV05rrBzrxON9lynu68n0DM7w=="],
"@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.74", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.74" } }, "sha512-C6C2hXixNcZXLaFF2u7B/FtOsqpdY7luaPuiGFBJza0P7EnYDkwaT3kB6lv7l/qctmkADc24qOsSCWIKRbC4jg=="],
"@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.66", "", { "peerDependencies": { "effect": "^4.0.0-beta.66" } }, "sha512-UYsrAb/5T0ZRypeN9Kmv3/ZInibGCjM6dtoiAWtfG+xKyuq8N05wmuVCXB0+XgVmUBxDWjw/S1fu4ivS0vZVuw=="],
"@effect/sql-sqlite-bun": ["@effect/sql-sqlite-bun@4.0.0-beta.74", "", { "peerDependencies": { "effect": "^4.0.0-beta.74" } }, "sha512-RVMRVY7NhSoAp9cAAyy4TT6dt6NNZjOpWeqticoho9HNBukxQSUcu/kjcz4Iq9eoQfXadmepu8kZqtdZULM/fg=="],
"@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="],
@@ -1506,19 +1546,19 @@
"@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="],
"@lydell/node-pty": ["@lydell/node-pty@1.2.0-beta.10", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.10", "@lydell/node-pty-darwin-x64": "1.2.0-beta.10", "@lydell/node-pty-linux-arm64": "1.2.0-beta.10", "@lydell/node-pty-linux-x64": "1.2.0-beta.10", "@lydell/node-pty-win32-arm64": "1.2.0-beta.10", "@lydell/node-pty-win32-x64": "1.2.0-beta.10" } }, "sha512-Fv+A3+MZVA8qhkBIZsM1E6dCdHNMyXXz22mAYiMWd03LlyK///F3OH6CKPX9mj4id7LUlxpr45yPzyBVy9aDPw=="],
"@lydell/node-pty": ["@lydell/node-pty@1.2.0-beta.12", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", "@lydell/node-pty-linux-x64": "1.2.0-beta.12", "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", "@lydell/node-pty-win32-x64": "1.2.0-beta.12" } }, "sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g=="],
"@lydell/node-pty-darwin-arm64": ["@lydell/node-pty-darwin-arm64@1.2.0-beta.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-C+eqDyRNHRYvx7RaHj6VVCx6nCpRBPuuxhTcc3JH3GuBMoxTsYeY4GkWH2XOktrgbAq1BG8e/Y8bu/wNQreCEw=="],
"@lydell/node-pty-darwin-arm64": ["@lydell/node-pty-darwin-arm64@1.2.0-beta.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-tqaifcY9Cr41SblO1+FLzh8oxxtkNhuW9Dhl22lKme9BreYvKvxEZcdPIXTuqkJc5tagOEC4QHShKmJjLyLXLQ=="],
"@lydell/node-pty-darwin-x64": ["@lydell/node-pty-darwin-x64@1.2.0-beta.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-aZoIK6HtJO5BiT4ELm683U4dyHtt8b7wNgq3NJqYAQwSXrcPv576Z8vY3BIulVxfcFkht/SPLKou9TtdFXdNpg=="],
"@lydell/node-pty-darwin-x64": ["@lydell/node-pty-darwin-x64@1.2.0-beta.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-4LrS5pCJwqHKDVf1zS2gyNV0m4hKAXch+XZNhbZ6LY8uwVL8BhchzQBO40Os5anuRxRCWzHpw4Sp64Ie8q7E4Q=="],
"@lydell/node-pty-linux-arm64": ["@lydell/node-pty-linux-arm64@1.2.0-beta.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-0cKX2iMyXFNBE4fGtGK6B7IkdXcDMZajyEDoGMOgQQs/DDtoI5tSPcBcqNY9VitVrsRQA8+gFt6eKYU9Ye/lUA=="],
"@lydell/node-pty-linux-arm64": ["@lydell/node-pty-linux-arm64@1.2.0-beta.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-Sx+A71x5BDGHt9ansfrtGxwq2VFVDWvJUAdlUL0Hv0qeiJUfts+hgopx+CgT4PSwahKjdEgtu0+FAfY9rICKRw=="],
"@lydell/node-pty-linux-x64": ["@lydell/node-pty-linux-x64@1.2.0-beta.10", "", { "os": "linux", "cpu": "x64" }, "sha512-J9HnxvSzEeMH748+Ul1VrmCLWMo7iCVJy9EGijRR62+YO/Yk5GaCydUTZ+KzlH0/X5aTrgt5cfiof4vx45tRRg=="],
"@lydell/node-pty-linux-x64": ["@lydell/node-pty-linux-x64@1.2.0-beta.12", "", { "os": "linux", "cpu": "x64" }, "sha512-bJzs94njofYhGg/UDqW1nj0dtvvu+2OvxMY+RlLS1T17VgcktKoIR6PuenTwE5HJ/D6StCPADmXcT0nNsCKmIQ=="],
"@lydell/node-pty-win32-arm64": ["@lydell/node-pty-win32-arm64@1.2.0-beta.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-PlDJpJX/pnKyy6OmADKzhf+INZDDnzTBGaI0LT4laVNc6NblZNqUSkCMjLFWbeakeuQp0VG37M49WQSN9FDfeA=="],
"@lydell/node-pty-win32-arm64": ["@lydell/node-pty-win32-arm64@1.2.0-beta.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-p7POgjVEiFaBC3/y+AKuV1FzePCsJ6HmZDv2XK+jBZSfwP8+uBAw181ZiKYN1YuRa/XpmBGaWezcI8hZkbW++g=="],
"@lydell/node-pty-win32-x64": ["@lydell/node-pty-win32-x64@1.2.0-beta.10", "", { "os": "win32", "cpu": "x64" }, "sha512-ExFgWrzyldNAMi45U9PLIOu+g/RatP+f0c/dZxaooifME6yLW32BoHveH26/TtoAjZyJrc2iL0u48pgnR1fzmg=="],
"@lydell/node-pty-win32-x64": ["@lydell/node-pty-win32-x64@1.2.0-beta.12", "", { "os": "win32", "cpu": "x64" }, "sha512-IDFa00g7qUDGUYgByrUBJtC+mOjYVt/8KYyWivCg5JjGOHbBUACUQZLl0jTWmnr+tld/UyTpX90a2PY6oTVtRw=="],
"@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="],
@@ -1544,17 +1584,17 @@
"@motionone/utils": ["@motionone/utils@10.18.0", "", { "dependencies": { "@motionone/types": "^10.17.1", "hey-listen": "^1.0.8", "tslib": "^2.3.1" } }, "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw=="],
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="],
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w=="],
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw=="],
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw=="],
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ=="],
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="],
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="],
@@ -1644,6 +1684,8 @@
"@opencode-ai/app": ["@opencode-ai/app@workspace:packages/app"],
"@opencode-ai/cli": ["@opencode-ai/cli@workspace:packages/cli"],
"@opencode-ai/console-app": ["@opencode-ai/console-app@workspace:packages/console/app"],
"@opencode-ai/console-core": ["@opencode-ai/console-core@workspace:packages/console/core"],
@@ -1662,6 +1704,8 @@
"@opencode-ai/effect-drizzle-sqlite": ["@opencode-ai/effect-drizzle-sqlite@workspace:packages/effect-drizzle-sqlite"],
"@opencode-ai/effect-sqlite-node": ["@opencode-ai/effect-sqlite-node@workspace:packages/effect-sqlite-node"],
"@opencode-ai/enterprise": ["@opencode-ai/enterprise@workspace:packages/enterprise"],
"@opencode-ai/function": ["@opencode-ai/function@workspace:packages/function"],
@@ -1718,23 +1762,23 @@
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
"@opentui/core": ["@opentui/core@0.2.16", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.2.16", "@opentui/core-darwin-x64": "0.2.16", "@opentui/core-linux-arm64": "0.2.16", "@opentui/core-linux-x64": "0.2.16", "@opentui/core-win32-arm64": "0.2.16", "@opentui/core-win32-x64": "0.2.16" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-4vWN15Zc3nsXJlOiHhhpqkBXD+wrNFKxCPtiTiillZYDRre+XsZogVTOOGUDwaBIC23OSxq7imezLmmtShVBEA=="],
"@opentui/core": ["@opentui/core@0.3.1", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.3.1", "@opentui/core-darwin-x64": "0.3.1", "@opentui/core-linux-arm64": "0.3.1", "@opentui/core-linux-x64": "0.3.1", "@opentui/core-win32-arm64": "0.3.1", "@opentui/core-win32-x64": "0.3.1" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-kQFSsSCgtlasSqTigCgKmM67xaquGvTg+vwimDnFSZtcBEt4E3dz7qLrbeh5FVvTA+RMbwe+Bozq03PW+SgjXw=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-aFb2Yp+oqDu3h6VCWi7xpQ9yjpKSQcROzGGfHgqC6Nd3U+uiLfPJBkmiI87iK0opCggCFj5TkKI004050DmGjg=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-krvVfiBpeBY+727R8yogdqIcxkK3RUVcI97bqjl8jTeDMcWOkFFfHezssRMPmbR5x++1tX669Fz3fuxoe7XUIg=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-KimiHE0j7EsTB5P8doW0lr1eH5iZKLPKWQO+tmy1VcdYr/TzqhdHSvGuJXrZvfTFi9/rV57Eq0d7964Ri9O0vQ=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-D/6ec5H8SPpSBMr01/sqgSddIl1Qc1QMKsDl/wV5MpbxYc7Qvie9qlNvvoSsWNfAXAbafLRb1jQBzouk41cp1w=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-4fCwRCfTtUgS/5QcSEkSuBjgQymSOUWXgrXG2ycrf3Swi0QhKDA/pVjwLrUJ6eF+/8mQyQSEV72T8MxMO3M2qg=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-E/FFBoAsWJyS/EO/cF7h7DuEENYa9nAdSv1W/TIyKXpBisN6K3U1Xgbk528TkfWjrwJjhGs+9OMYdXuAHd5LTw=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.16", "", { "os": "linux", "cpu": "x64" }, "sha512-KgQBGjiucw4e7gM+R8qOzHWBFhjCY1IfCrGjW3Wzxv2hKUlL+mPhelaeJwnEqtNxMUdVTYjlwlu3IHxslXMJWQ=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-Btb7Q4BOC55Aj2qCs0VoxGuj87DNfUEaSx0z89oeU4npTN+6SpJApyGZTCNNeSe2sdmOGeh/8eAR4X96ORjcKg=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-C6WqEI3VkXatXraMgSFXZjEXq0pzURGjRpFAJZYmuVDmpqE57o7E80Np2UkdZ6m5kpJDt4mRyu3krc/P825iNQ=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-+lt24u3KwEPG69oXDOLz9N484wPcAHvrPbDNU77OT6DvWew+StAjh40eY+Zeu0TkTNDWfj7qnQKV0GKWtFA3cw=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.16", "", { "os": "win32", "cpu": "x64" }, "sha512-kCX3CMTns6DMCFDNTDV4sjmBKyA/iEvzaVhl/jYi4JRIVT2zcy1lo+lhXT5mPgYHmJZu8Uye6j3Zi3c7Z2Me5A=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-eVkKMYirYgpn92lI0YT/GKru4J+UiXjzwyzNRFX+P59OHXvL3GFdqJMcJmX4/zvyjg4c8HDnU79YLnyG+TlXLw=="],
"@opentui/keymap": ["@opentui/keymap@0.2.16", "", { "dependencies": { "@opentui/core": "0.2.16" }, "peerDependencies": { "@opentui/react": "0.2.16", "@opentui/solid": "0.2.16", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-YBLQfNLbU2kx49bjEY9rrFoNlvIoi5qNJfRcOt6frvnR3C6MLl0/8hZY+vMQ2PEQWeEiNejFnl1lQw+z4Nk2FQ=="],
"@opentui/keymap": ["@opentui/keymap@0.3.1", "", { "dependencies": { "@opentui/core": "0.3.1" }, "peerDependencies": { "@opentui/react": "0.3.1", "@opentui/solid": "0.3.1", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-BTj+ggsarO2uyvd6CWzvgfsekA8c4aEclbAPKPZGVjBI3Fo5+KAHUrXvteFO5qpGMANfEJTtVHoRu5cic1Nlaw=="],
"@opentui/solid": ["@opentui/solid@0.2.16", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.16", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-2Q+v1PPpXXr+sALi9Aj6I5Jvo7xDfbmstYjRLL7lW3Hghh9i7ONQKpt/gyDDRbhSsYrhxKYTNenF9OxgoXkTHg=="],
"@opentui/solid": ["@opentui/solid@0.3.1", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.3.1", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-2R6wEijfMub9COTBCm8IKVj2y7+Sc4fZZjJawxk8sE6+++mzeUaokKNJTlYhZXpMju4LKMv6j9CjWkG8JYfbcg=="],
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
@@ -3134,7 +3178,7 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"effect": ["effect@4.0.0-beta.66", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-4arEr62cziFa8BBVDUwJCJJmaVepXf/kRg7KtC0h8+bufngscrHbwWFhr9c+HonwOF+31U3iD3xUJmw9KzX7Dw=="],
"effect": ["effect@4.0.0-beta.74", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.8.0", "find-my-way-ts": "^0.1.6", "ini": "^7.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^2.0.1", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^14.0.0", "yaml": "^2.9.0" } }, "sha512-Yx+Kh12U+i2FmjwEfKs+ePFmpMd43RPD1oGqc/VraSS9bYzvF0Ff3PojwEFEVEewp8xc92Uxu28gTspU4qyvHA=="],
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
@@ -3288,7 +3332,7 @@
"extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="],
"fast-check": ["fast-check@4.6.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA=="],
"fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="],
"fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="],
@@ -3418,7 +3462,7 @@
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.7.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-J7apROAmDwDJcLPcbQ9YQiPhyFN1Vvt/f3ugsePaUnzh3sedfHCHGETgG0Mm0dJCB2rC7DYwmV/sgxLQcQ/sjw=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.8.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-KwHASXkHtDcgrzTXZVp9Dyx6t8m9nK0R2fCm47MWcxxQ1kOBt3f2LZugtu1kOby8i4Sbd+kvBSYM66PGkDclng=="],
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
@@ -3580,7 +3624,7 @@
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"ini": ["ini@7.0.0", "", {}, "sha512-ifK0CgjALofS5bkrcTy4RaQ9Vx2Knf/eLeIO+NaswQEpH1UblrtTSCIvN71qQDMq0PeQ/SSPojvEJp9vvvfr+w=="],
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
@@ -4068,9 +4112,9 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"msgpackr": ["msgpackr@1.11.9", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw=="],
"msgpackr": ["msgpackr@2.0.2", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.4" } }, "sha512-c5hYOXFbP79Slh6Dzd2wzk+jnV7mX1UxfMYtilnY1NmalXPqG8DGb5cYCMBrW4AsH3zekBBZd4QrKz9NhtvYLQ=="],
"msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
"msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="],
"mssql": ["mssql@11.0.1", "", { "dependencies": { "@tediousjs/connection-string": "^0.5.0", "commander": "^11.0.0", "debug": "^4.3.3", "rfdc": "^1.3.0", "tarn": "^3.0.2", "tedious": "^18.2.1" }, "bin": { "mssql": "bin/mssql" } }, "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w=="],
@@ -4954,7 +4998,7 @@
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
"undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
"undici": ["undici@8.3.0", "", {}, "sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
@@ -5024,7 +5068,7 @@
"utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="],
"uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"uuid": ["uuid@14.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg=="],
"valibot": ["valibot@1.3.1", "", { "peerDependencies": { "typescript": ">=5" }, "optionalPeers": ["typescript"] }, "sha512-sfdRir/QFM0JaF22hqTroPc5xy4DimuGQVKFrzF1YfGwaS1nJot3Y8VqMdLO2Lg27fMzat2yD3pY5PbAYO39Gg=="],
@@ -5506,10 +5550,6 @@
"@dot/log/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"@effect/platform-node/undici": ["undici@8.1.0", "", {}, "sha512-E9MkTS4xXLnRPYqxH2e6Hr2/49e7WFDKczKcCaFH4VaZs2iNvHMqeIkyUAD9vM8kujy9TjVrRlQ5KkdEJxB2pw=="],
"@effect/platform-node-shared/ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="],
"@electron/asar/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="],
"@electron/asar/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="],
@@ -5574,6 +5614,10 @@
"@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"@npmcli/config/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"@npmcli/git/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"@npmcli/query/postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="],
"@octokit/auth-app/@octokit/request": ["@octokit/request@10.0.8", "", { "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" } }, "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw=="],
@@ -5642,6 +5686,8 @@
"@opencode-ai/core/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@opencode-ai/core/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"@opencode-ai/desktop/@actions/artifact": ["@actions/artifact@4.0.0", "", { "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^6.0.1", "@actions/http-client": "^2.1.0", "@azure/core-http": "^3.0.5", "@azure/storage-blob": "^12.15.0", "@octokit/core": "^5.2.1", "@octokit/plugin-request-log": "^1.0.4", "@octokit/plugin-retry": "^3.0.9", "@octokit/request": "^8.4.1", "@octokit/request-error": "^5.1.1", "@protobuf-ts/plugin": "^2.2.3-alpha.1", "archiver": "^7.0.1", "jwt-decode": "^3.1.2", "unzip-stream": "^0.3.1" } }, "sha512-HCc2jMJRAfviGFAh0FsOR/jNfWhirxl7W6z8zDtttt0GltwxBLdEIjLiweOPFl9WbyJRW1VWnPUSAixJqcWUMQ=="],
"@opencode-ai/desktop/marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
@@ -5652,6 +5698,8 @@
"@opencode-ai/llm/@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="],
"@opencode-ai/script/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"@opencode-ai/ui/@solid-primitives/resize-observer": ["@solid-primitives/resize-observer@2.1.3", "", { "dependencies": { "@solid-primitives/event-listener": "^2.4.3", "@solid-primitives/rootless": "^1.5.2", "@solid-primitives/static-store": "^0.1.2", "@solid-primitives/utils": "^6.3.2" }, "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ=="],
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
@@ -5874,6 +5922,8 @@
"effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"effect/yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
"electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"electron-builder/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
@@ -5978,6 +6028,8 @@
"nitro/h3": ["h3@2.0.1-rc.5", "", { "dependencies": { "rou3": "^0.7.9", "srvx": "^0.9.1" }, "peerDependencies": { "crossws": "^0.4.1" }, "optionalPeers": ["crossws"] }, "sha512-qkohAzCab0nLzXNm78tBjZDvtKMTmtygS8BJLT3VPczAQofdqlFXDPkXdLMJN4r05+xqneG8snZJ0HgkERCZTg=="],
"nitro/undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="],
"node-gyp-build-optional-packages/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
@@ -5996,6 +6048,8 @@
"opencode/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="],
"opencode/semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
"opencode-gitlab-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"opencode-poe-auth/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
@@ -6682,8 +6736,24 @@
"@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-json/effect/fast-check": ["fast-check@4.6.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA=="],
"@standard-community/standard-json/effect/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"@standard-community/standard-json/effect/msgpackr": ["msgpackr@1.11.9", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw=="],
"@standard-community/standard-json/effect/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-openapi/effect/fast-check": ["fast-check@4.6.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-h7H6Dm0Fy+H4ciQYFxFjXnXkzR2kr9Fb22c0UBpHnm59K2zpr2t13aPTHlltFiNT6zuxp6HMPAVVvgur4BLdpA=="],
"@standard-community/standard-openapi/effect/ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
"@standard-community/standard-openapi/effect/msgpackr": ["msgpackr@1.11.9", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-FkoAAyyA6HM8wL882EcEyFZ9s7hVADSwG9xrVx3dxxNQAtgADTrJoEWivID82Iv1zWDsv/OtbrrcZAzGzOMdNw=="],
"@standard-community/standard-openapi/effect/uuid": ["uuid@13.0.0", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
"@storybook/csf-plugin/unplugin/webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
@@ -7080,6 +7150,10 @@
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="],
"ai-gateway-provider/@ai-sdk/google-vertex/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"ai-gateway-provider/@ai-sdk/google/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
@@ -7222,6 +7296,30 @@
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es/regex-recursion": ["regex-recursion@5.1.1", "", { "dependencies": { "regex": "^5.1.1", "regex-utilities": "^2.3.0" } }, "sha512-ae7SBCbzVNrIjgSbh7wMznPcQel1DNlDtzensnFxpiNpXt1U2ju/bHugH422r+4LAVS1FpW1YCwilmnNsjum9w=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
"@standard-community/standard-json/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3", "", { "os": "linux", "cpu": "arm" }, "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg=="],
"@standard-community/standard-openapi/effect/msgpackr/msgpackr-extract/@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3", "", { "os": "win32", "cpu": "x64" }, "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ=="],
"archiver-utils/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
"archiver-utils/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
+1 -1
View File
@@ -2,7 +2,7 @@
exact = true
# Only install newly resolved package versions published at least 3 days ago.
minimumReleaseAge = 259200
minimumReleaseAgeExcludes = ["@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-x64", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid"]
minimumReleaseAgeExcludes = ["@opentui/core", "@opentui/core-darwin-arm64", "@opentui/core-darwin-x64", "@opentui/core-linux-arm64", "@opentui/core-linux-x64", "@opentui/core-win32-arm64", "@opentui/core-win32-x64", "@opentui/keymap", "@opentui/solid", "gitlab-ai-provider"]
[test]
root = "./do-not-run-tests-from-root"
+5
View File
@@ -198,6 +198,11 @@ export const lakeCatalog = $interpolate`${glueCatalogName}/${tableBucket.name}`
export const lakeAthenaWorkgroup = athenaWorkgroup
const ingestSecret = new random.RandomPassword("LakeIngestSecret", { length: 32 })
export const ingestSecretSsm = new aws.ssm.Parameter("LakeIngestSecretSsm", {
name: $interpolate`/${$app.name}/${$app.stage}/lake/ingest/secret`,
type: "SecureString",
value: ingestSecret.result,
})
const ingestConfig = new sst.Linkable("LakeIngestConfig", {
properties: {
+11 -15
View File
@@ -1,10 +1,6 @@
import { lakeAthenaWorkgroup, lakeCatalog, lakeCluster, lakeQueryPermissions, lakeRegion, tableBucket } from "./lake"
const domain = (() => {
if ($app.stage === "production") return "stats.opencode.ai"
if ($app.stage === "dev") return "stats.dev.opencode.ai"
return `stats.${$app.stage}.dev.opencode.ai`
})()
import { EMAILOCTOPUS_API_KEY } from "./app"
import { domain } from "./stage"
////////////////
// LAKE
@@ -163,15 +159,15 @@ new sst.x.DevCommand("StatsStudio", {
// APP
////////////////
// export const app = new sst.cloudflare.x.SolidStart("Stats", {
// path: "packages/stats/app",
// buildCommand: "bun run build",
// domain,
// link: [database],
// environment: {
// PUBLIC_URL: `https://${domain}`,
// },
// })
export const app = new sst.cloudflare.x.SolidStart("Stats", {
path: "packages/stats/app",
buildCommand: "bun run build",
domain: `stats.${domain}`,
link: [database, EMAILOCTOPUS_API_KEY],
environment: {
PUBLIC_URL: `https://stats.${domain}/stats`,
},
})
////////////////
// SERVICES
+4 -4
View File
@@ -1,8 +1,8 @@
{
"nodeModules": {
"x86_64-linux": "sha256-KFVkxs0jtN1EXvwuxFMOlWg6BDr8L6K/wJZ/N73p8cI=",
"aarch64-linux": "sha256-7OLglRBBt5prwb9e0I0iMTXi3MrDfDI+8xcDrxtq/JI=",
"aarch64-darwin": "sha256-Q7iE6xJyomDnvqUgsHh+FlCnJFI6vK0zJSfI9BBYitQ=",
"x86_64-darwin": "sha256-9lVsq2Bw74Vgphg6GSo/HxCtDXUyabxcS/vE1Um/Hfw="
"x86_64-linux": "sha256-fzplLKjzYefVCRhAgfCWWIa3me2JiI2d9kDBACnz4fA=",
"aarch64-linux": "sha256-gccipj2CAau/lZ7KTQhdeU8n6fcXDuUt4X4XbNufCko=",
"aarch64-darwin": "sha256-r0QEetxKUf6HrtMOozyvb4jjEJjVtp+DE8aiKKhRrVI=",
"x86_64-darwin": "sha256-bU+5WudkiIflFgoqgLSf30iqeNNLuEbScI5oMFPu9eM="
}
}
+12 -10
View File
@@ -10,7 +10,7 @@
"dev:desktop": "bun --cwd packages/desktop dev",
"dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev:stats": "bun sst shell --stage=dev -- bun run --cwd packages/stats/app dev",
"dev:stats": "bun sst shell --stage=production -- bun run --cwd packages/stats/app dev",
"dev:storybook": "bun --cwd packages/storybook storybook",
"lint": "oxlint",
"typecheck": "bun turbo typecheck",
@@ -30,17 +30,17 @@
"packages/slack"
],
"catalog": {
"@effect/opentelemetry": "4.0.0-beta.66",
"@effect/platform-node": "4.0.0-beta.66",
"@effect/sql-sqlite-bun": "4.0.0-beta.66",
"@effect/opentelemetry": "4.0.0-beta.74",
"@effect/platform-node": "4.0.0-beta.74",
"@effect/sql-sqlite-bun": "4.0.0-beta.74",
"@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.13",
"@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2",
"@opentui/core": "0.2.16",
"@opentui/keymap": "0.2.16",
"@opentui/solid": "0.2.16",
"@opentui/core": "0.3.1",
"@opentui/keymap": "0.3.1",
"@opentui/solid": "0.3.1",
"ulid": "3.0.1",
"@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1",
@@ -58,7 +58,7 @@
"dompurify": "3.3.1",
"drizzle-kit": "1.0.0-rc.2",
"drizzle-orm": "1.0.0-rc.2",
"effect": "4.0.0-beta.66",
"effect": "4.0.0-beta.74",
"ai": "6.0.168",
"cross-spawn": "7.0.6",
"hono": "4.10.7",
@@ -87,7 +87,7 @@
"@sentry/vite-plugin": "4.6.0",
"solid-js": "1.9.10",
"vite-plugin-solid": "2.11.10",
"@lydell/node-pty": "1.2.0-beta.10"
"@lydell/node-pty": "1.2.0-beta.12"
}
},
"devDependencies": {
@@ -145,6 +145,8 @@
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"virtua@0.49.1": "patches/virtua@0.49.1.patch",
"@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch",
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch"
"gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch",
"pacote@21.5.0": "patches/pacote@21.5.0.patch",
"@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch"
}
}
@@ -0,0 +1,40 @@
import { expect, test } from "@playwright/test"
import { fixture, pageMessages } from "../smoke/session-timeline.fixture"
import { mockOpenCodeServer } from "../utils/mock-server"
test("shows loaded sessions before the directory path request resolves", async ({ page }) => {
await mockOpenCodeServer(page, {
sessions: fixture.sessions,
provider: fixture.provider,
directory: fixture.directory,
project: fixture.project,
pageMessages,
})
let releasePath!: () => void
const pathBlocked = new Promise<void>((resolve) => {
releasePath = resolve
})
await page.route("**/path?*", async (route) => {
if (!new URL(route.request().url()).searchParams.has("directory")) return route.fallback()
await pathBlocked
return route.fallback()
})
await page.addInitScript((directory) => {
localStorage.setItem(
"opencode.global.dat:server",
JSON.stringify({
projects: { local: [{ worktree: directory, expanded: true }] },
lastProject: { local: directory },
}),
)
}, fixture.directory)
await page.goto("/")
try {
await expect(page.getByText(fixture.expected.sourceTitle).first()).toBeVisible({ timeout: 5_000 })
} finally {
releasePath()
}
})
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/app",
"version": "1.15.11",
"version": "1.15.13",
"description": "",
"type": "module",
"exports": {
@@ -8,7 +8,7 @@ import { List, type ListRef } from "@opencode-ai/ui/list"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Spinner } from "@opencode-ai/ui/spinner"
import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { createEffect, createMemo, createResource, Match, onCleanup, onMount, Switch } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { Link } from "@/components/link"
@@ -5,7 +5,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { useMutation } from "@tanstack/solid-query"
import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { batch, For } from "solid-js"
import { createStore, produce } from "solid-js/store"
import { Link } from "@/components/link"
+1 -1
View File
@@ -6,7 +6,7 @@ import { usePrompt } from "@/context/prompt"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { extractPromptFromParts } from "@/utils/prompt"
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/core/util/encode"
@@ -7,7 +7,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { List } from "@opencode-ai/ui/list"
import { TextField } from "@opencode-ai/ui/text-field"
import { useMutation } from "@tanstack/solid-query"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { useNavigate } from "@solidjs/router"
import { createEffect, createMemo, createResource, onCleanup, Show } from "solid-js"
import { createStore, reconcile } from "solid-js/store"
@@ -1,6 +1,6 @@
import { onMount } from "solid-js"
import { makeEventListener } from "@solid-primitives/event-listener"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { usePrompt, type ContentPart, type ImageAttachmentPart } from "@/context/prompt"
import { useLanguage } from "@/context/language"
import { uuid } from "@/utils/uuid"
@@ -1,5 +1,5 @@
import type { Message, Session } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { Binary } from "@opencode-ai/core/util/binary"
import { useNavigate, useParams } from "@solidjs/router"
@@ -5,7 +5,7 @@ import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Keybind } from "@opencode-ai/ui/keybind"
import { Spinner } from "@opencode-ai/ui/spinner"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/core/util/path"
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
@@ -25,8 +25,8 @@ import { messageAgentColor } from "@/utils/agent"
import { decode64 } from "@/utils/base64"
import { Persist, persisted } from "@/utils/persist"
import { StatusPopover, StatusPopoverV2 } from "../status-popover"
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
const OPEN_APPS = [
"vscode",
@@ -530,7 +530,7 @@ type SessionHeaderV2ActionsState = {
function SessionHeaderV2Actions(props: { state: SessionHeaderV2ActionsState }) {
return (
<div class="flex items-center gap-0">
<div class="flex items-center gap-2">
<Show when={props.state.statusVisible}>
<Tooltip placement="bottom" value={props.state.statusLabel}>
<StatusPopoverV2 />
@@ -1,11 +1,12 @@
import type { JSX } from "solid-js"
import { WordmarkV2 } from "@opencode-ai/ui/v2/components/wordmark-v2.jsx"
import { WordmarkV2 } from "@opencode-ai/ui/v2/wordmark-v2"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"
export function NewSessionDesignView(props: { children: JSX.Element }) {
return (
<div data-component="session-new-design" class="relative size-full overflow-hidden bg-v2-background-bg-deep">
<div class="absolute inset-x-0 top-[25.375%] flex justify-center px-6">
<div class="w-full max-w-[720px]">
<div class={NEW_SESSION_CONTENT_WIDTH}>
<WordmarkV2 class="h-auto w-full text-v2-icon-icon-base" />
<div class="mt-8">{props.children}</div>
</div>
@@ -7,7 +7,8 @@ import { Switch } from "@opencode-ai/ui/switch"
import { TextField } from "@opencode-ai/ui/text-field"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
import { showToast } from "@opencode-ai/ui/toast"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { showToast } from "@/utils/toast"
import { useParams } from "@solidjs/router"
import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission"
@@ -86,6 +87,7 @@ export const SettingsGeneral: Component = () => {
const language = useLanguage()
const permission = usePermission()
const platform = usePlatform()
const dialog = useDialog()
const params = useParams()
const settings = useSettings()
@@ -407,7 +409,13 @@ export const SettingsGeneral: Component = () => {
<div data-action="settings-new-layout-designs">
<Switch
checked={settings.general.newLayoutDesigns()}
onChange={(checked) => settings.general.setNewLayoutDesigns(checked)}
onChange={(checked) => {
settings.general.setNewLayoutDesigns(checked)
if (!checked) return
void import("@/components/settings-v2").then((module) => {
dialog.show(() => <module.DialogSettings />)
})
}}
/>
</div>
</SettingsRow>
+176 -71
View File
@@ -1,17 +1,29 @@
import { Component, For, Show, createMemo, onCleanup, onMount } from "solid-js"
import { Component, For, Show, createMemo, lazy, onCleanup, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { makeEventListener } from "@solid-primitives/event-listener"
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { TextField } from "@opencode-ai/ui/text-field"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import fuzzysort from "fuzzysort"
import { formatKeybind, parseKeybind, useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings"
import { SettingsList } from "./settings-list"
const ButtonV2 = lazy(() => import("@opencode-ai/ui/v2/button-v2").then((module) => ({ default: module.ButtonV2 })))
const IconV2 = lazy(() => import("@opencode-ai/ui/v2/icon").then((module) => ({ default: module.Icon })))
const IconButtonV2 = lazy(() =>
import("@opencode-ai/ui/v2/icon-button-v2").then((module) => ({ default: module.IconButtonV2 })),
)
const TextInputV2 = lazy(() =>
import("@opencode-ai/ui/v2/text-input-v2").then((module) => ({ default: module.TextInputV2 })),
)
const SettingsListV2 = lazy(() =>
import("./settings-v2/parts/list").then((module) => ({ default: module.SettingsListV2 })),
)
const IS_MAC = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform)
const PALETTE_ID = "command.palette"
const DEFAULT_PALETTE_KEYBIND = "mod+shift+p"
@@ -257,7 +269,7 @@ function useKeyCapture(input: {
})
}
export const SettingsKeybinds: Component = () => {
export const SettingsKeybinds: Component<{ v2?: boolean }> = (props) => {
const command = useCommand()
const language = useLanguage()
const settings = useSettings()
@@ -371,85 +383,178 @@ export const SettingsKeybinds: Component = () => {
if (store.active) command.keybinds(true)
})
return (
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
<div class="flex items-center justify-between gap-4">
<h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
<Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
{language.t("settings.shortcuts.reset.button")}
</Button>
</div>
const emptyResults = (
<Show when={store.filter && !hasResults()}>
<div
classList={{
"flex flex-col items-center justify-center py-12 text-center": !props.v2,
"settings-v2-shortcuts-status": props.v2,
}}
>
<span
classList={{
"text-14-regular text-text-weak": !props.v2,
}}
>
{language.t("settings.shortcuts.search.empty")}
</span>
<Show when={store.filter}>
<span
classList={{
"text-14-regular text-text-strong mt-1": !props.v2,
"settings-v2-shortcuts-status-filter": props.v2,
}}
>
&quot;{store.filter}&quot;
</span>
</Show>
</div>
</Show>
)
<div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base">
<Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" />
<TextField
variant="ghost"
type="text"
const List = props.v2 ? SettingsListV2 : SettingsList
const groups = (
<div
classList={{
"settings-v2-shortcuts flex flex-col gap-8": props.v2,
"flex flex-col gap-8 max-w-[720px]": !props.v2,
}}
>
<For each={GROUPS}>
{(group) => (
<Show when={(filtered().get(group) ?? []).length > 0}>
<div
classList={{
"settings-v2-section": props.v2,
"flex flex-col gap-1": !props.v2,
}}
>
<h3
classList={{
"settings-v2-section-title": props.v2,
"text-14-medium text-text-strong pb-2": !props.v2,
}}
>
{language.t(groupKey[group])}
</h3>
<List>
<For each={filtered().get(group) ?? []}>
{(id) => (
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
<span
classList={{
"text-14-regular text-text-strong": !props.v2,
}}
>
{title(id)}
</span>
<button
type="button"
data-keybind-id={id}
classList={{
"settings-v2-keybind-button": props.v2,
"settings-v2-keybind-button--active": props.v2 && store.active === id,
"h-8 px-3 rounded-md text-12-regular": !props.v2,
"bg-surface-base text-text-subtle hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active":
!props.v2 && store.active !== id,
"border border-border-weak-base bg-surface-inset-base text-text-weak":
!props.v2 && store.active === id,
}}
onClick={() => start(id)}
>
<Show
when={store.active === id}
fallback={command.keybind(id) || language.t("settings.shortcuts.unassigned")}
>
{language.t("settings.shortcuts.pressKeys")}
</Show>
</button>
</div>
)}
</For>
</List>
</div>
</Show>
)}
</For>
{emptyResults}
</div>
)
return (
<Show
when={props.v2}
fallback={
<div class="flex flex-col h-full overflow-y-auto no-scrollbar px-4 pb-10 sm:px-10 sm:pb-10">
<div class="sticky top-0 z-10 bg-[linear-gradient(to_bottom,var(--surface-stronger-non-alpha)_calc(100%_-_24px),transparent)]">
<div class="flex flex-col gap-4 pt-6 pb-6 max-w-[720px]">
<div class="flex items-center justify-between gap-4">
<h2 class="text-16-medium text-text-strong">{language.t("settings.shortcuts.title")}</h2>
<Button size="small" variant="secondary" onClick={resetAll} disabled={!hasOverrides()}>
{language.t("settings.shortcuts.reset.button")}
</Button>
</div>
<div class="flex items-center gap-2 px-3 h-9 rounded-lg bg-surface-base">
<Icon name="magnifying-glass" class="text-icon-weak-base flex-shrink-0" />
<TextField
variant="ghost"
type="text"
value={store.filter}
onChange={(v) => setStore("filter", v)}
placeholder={language.t("settings.shortcuts.search.placeholder")}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
class="flex-1"
/>
<Show when={store.filter}>
<IconButton icon="circle-x" variant="ghost" onClick={() => setStore("filter", "")} />
</Show>
</div>
</div>
</div>
{groups}
</div>
}
>
<>
<div class="settings-v2-tab-header settings-v2-tab-header--stacked">
<div class="settings-v2-tab-header-row">
<h2 class="settings-v2-tab-title">{language.t("settings.shortcuts.title")}</h2>
<ButtonV2 variant="ghost" onClick={resetAll} disabled={!hasOverrides()}>
{language.t("settings.shortcuts.reset.button")}
</ButtonV2>
</div>
<div class="settings-v2-tab-search">
<TextInputV2
type="search"
appearance="base"
value={store.filter}
onChange={(v) => setStore("filter", v)}
onInput={(event) => setStore("filter", event.currentTarget.value)}
placeholder={language.t("settings.shortcuts.search.placeholder")}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
class="flex-1"
aria-label={language.t("settings.shortcuts.search.placeholder")}
/>
<Show when={store.filter}>
<IconButton icon="circle-x" variant="ghost" onClick={() => setStore("filter", "")} />
<IconButtonV2
type="button"
variant="ghost-muted"
size="small"
class="settings-v2-tab-search-clear"
icon={<IconV2 name="close" size="large" class="text-v2-icon-icon-muted" />}
onClick={() => setStore("filter", "")}
/>
</Show>
</div>
</div>
</div>
<div class="flex flex-col gap-8 max-w-[720px]">
<For each={GROUPS}>
{(group) => (
<Show when={(filtered().get(group) ?? []).length > 0}>
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t(groupKey[group])}</h3>
<SettingsList>
<For each={filtered().get(group) ?? []}>
{(id) => (
<div class="flex items-center justify-between gap-4 py-3 border-b border-border-weak-base last:border-none">
<span class="text-14-regular text-text-strong">{title(id)}</span>
<button
type="button"
data-keybind-id={id}
classList={{
"h-8 px-3 rounded-md text-12-regular": true,
"bg-surface-base text-text-subtle hover:bg-surface-raised-base-hover active:bg-surface-raised-base-active":
store.active !== id,
"border border-border-weak-base bg-surface-inset-base text-text-weak": store.active === id,
}}
onClick={() => start(id)}
>
<Show
when={store.active === id}
fallback={command.keybind(id) || language.t("settings.shortcuts.unassigned")}
>
{language.t("settings.shortcuts.pressKeys")}
</Show>
</button>
</div>
)}
</For>
</SettingsList>
</div>
</Show>
)}
</For>
<Show when={store.filter && !hasResults()}>
<div class="flex flex-col items-center justify-center py-12 text-center">
<span class="text-14-regular text-text-weak">{language.t("settings.shortcuts.search.empty")}</span>
<Show when={store.filter}>
<span class="text-14-regular text-text-strong mt-1">"{store.filter}"</span>
</Show>
</div>
</Show>
</div>
</div>
<div class="settings-v2-tab-body">{groups}</div>
</>
</Show>
)
}
@@ -2,7 +2,7 @@ import { Button } from "@opencode-ai/ui/button"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Tag } from "@opencode-ai/ui/tag"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { createMemo, type Component, For, Show } from "solid-js"
import { useLanguage } from "@/context/language"
@@ -0,0 +1,80 @@
import { Component } from "solid-js"
import { Dialog } from "@opencode-ai/ui/v2/dialog-v2"
import { TabsV2 } from "@opencode-ai/ui/v2/tabs-v2"
import { Icon } from "@opencode-ai/ui/icon"
import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform"
import { SettingsGeneralV2 } from "./general"
import { SettingsKeybinds } from "../settings-keybinds"
import { SettingsProvidersV2 } from "./providers"
import { SettingsModelsV2 } from "./models"
import "./settings-v2.css"
export const DialogSettings: Component = () => {
const language = useLanguage()
const platform = usePlatform()
return (
<Dialog size="x-large" class="settings-v2-dialog" data-component="settings-v2-dialog">
<TabsV2
orientation="vertical"
variant="settings"
defaultValue="general"
class="settings-v2"
data-component="settings-v2"
>
<TabsV2.List>
<div class="flex flex-col justify-between h-full w-full">
<div class="flex flex-col gap-3 w-full">
<div class="flex flex-col gap-3">
<div class="flex flex-col gap-1.5">
<TabsV2.SectionTitle>{language.t("settings.section.desktop")}</TabsV2.SectionTitle>
<div class="flex flex-col gap-1.5 w-full">
<TabsV2.Trigger value="general">
<Icon name="sliders" />
{language.t("settings.tab.general")}
</TabsV2.Trigger>
<TabsV2.Trigger value="shortcuts">
<Icon name="keyboard" />
{language.t("settings.tab.shortcuts")}
</TabsV2.Trigger>
</div>
</div>
<div class="flex flex-col gap-1.5">
<TabsV2.SectionTitle>{language.t("settings.section.server")}</TabsV2.SectionTitle>
<div class="flex flex-col gap-1.5 w-full">
<TabsV2.Trigger value="providers">
<Icon name="providers" />
{language.t("settings.providers.title")}
</TabsV2.Trigger>
<TabsV2.Trigger value="models">
<Icon name="models" />
{language.t("settings.models.title")}
</TabsV2.Trigger>
</div>
</div>
</div>
</div>
<div class="settings-v2-nav-footer">
<span>{language.t("app.name.desktop")}</span>
<span>v{platform.version}</span>
</div>
</div>
</TabsV2.List>
<TabsV2.Content value="general" class="settings-v2-panel">
<SettingsGeneralV2 />
</TabsV2.Content>
<TabsV2.Content value="shortcuts" class="settings-v2-panel">
<SettingsKeybinds v2 />
</TabsV2.Content>
<TabsV2.Content value="providers" class="settings-v2-panel">
<SettingsProvidersV2 />
</TabsV2.Content>
<TabsV2.Content value="models" class="settings-v2-panel">
<SettingsModelsV2 />
</TabsV2.Content>
</TabsV2>
</Dialog>
)
}
@@ -0,0 +1,825 @@
import { Component, Show, createMemo, createResource, onMount } from "solid-js"
import { createStore } from "solid-js/store"
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
import { Icon } from "@opencode-ai/ui/icon"
import { SelectV2 } from "@opencode-ai/ui/select-v2"
import { Switch } from "@opencode-ai/ui/v2/switch-v2"
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
import { Tooltip } from "@opencode-ai/ui/tooltip"
import { useTheme, type ColorScheme } from "@opencode-ai/ui/theme/context"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { showToast } from "@/utils/toast"
import { useParams } from "@solidjs/router"
import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission"
import { usePlatform, type DisplayBackend } from "@/context/platform"
import { useServerSync } from "@/context/server-sync"
import { useServerSDK } from "@/context/server-sdk"
import {
monoDefault,
monoFontFamily,
monoInput,
sansDefault,
sansFontFamily,
sansInput,
terminalDefault,
terminalFontFamily,
terminalInput,
useSettings,
} from "@/context/settings"
import { decode64 } from "@/utils/base64"
import { playSoundById, SOUND_OPTIONS } from "@/utils/sound"
import { Link } from "../link"
import { SettingsListV2 } from "./parts/list"
import { SettingsRowV2 } from "./parts/row"
import "./settings-v2.css"
let demoSoundState = {
cleanup: undefined as (() => void) | undefined,
timeout: undefined as NodeJS.Timeout | undefined,
run: 0,
}
type ThemeOption = {
id: string
name: string
}
type ShellOption = {
path: string
name: string
acceptable: boolean
}
type ShellSelectOption = {
id: string
value: string
label: string
}
// To prevent audio from overlapping/playing very quickly when navigating the settings menus,
// delay the playback by 100ms during quick selection changes and pause existing sounds.
const stopDemoSound = () => {
demoSoundState.run += 1
if (demoSoundState.cleanup) {
demoSoundState.cleanup()
}
clearTimeout(demoSoundState.timeout)
demoSoundState.cleanup = undefined
}
const playDemoSound = (id: string | undefined) => {
stopDemoSound()
if (!id) return
const run = ++demoSoundState.run
demoSoundState.timeout = setTimeout(() => {
void playSoundById(id).then((cleanup) => {
if (demoSoundState.run !== run) {
cleanup?.()
return
}
demoSoundState.cleanup = cleanup
})
}, 100)
}
export const SettingsGeneralV2: Component = () => {
const theme = useTheme()
const language = useLanguage()
const permission = usePermission()
const platform = usePlatform()
const dialog = useDialog()
const params = useParams()
const settings = useSettings()
const [store, setStore] = createStore({
checking: false,
})
const linux = createMemo(() => platform.platform === "desktop" && platform.os === "linux")
const dir = createMemo(() => decode64(params.dir))
const accepting = createMemo(() => {
const value = dir()
if (!value) return false
if (!params.id) return permission.isAutoAcceptingDirectory(value)
return permission.isAutoAccepting(params.id, value)
})
const toggleAccept = (checked: boolean) => {
const value = dir()
if (!value) return
if (!params.id) {
if (permission.isAutoAcceptingDirectory(value) === checked) return
permission.toggleAutoAcceptDirectory(value)
return
}
if (checked) {
permission.enableAutoAccept(params.id, value)
return
}
permission.disableAutoAccept(params.id, value)
}
const desktop = createMemo(() => platform.platform === "desktop")
const check = () => {
if (!platform.checkUpdate) return
setStore("checking", true)
void platform
.checkUpdate()
.then((result) => {
if (!result.updateAvailable) {
showToast({
variant: "success",
icon: "circle-check",
title: language.t("settings.updates.toast.latest.title"),
description: language.t("settings.updates.toast.latest.description", { version: platform.version ?? "" }),
})
return
}
const actions = platform.updateAndRestart
? [
{
label: language.t("toast.update.action.installRestart"),
onClick: async () => {
await platform.updateAndRestart!()
},
},
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]
: [
{
label: language.t("toast.update.action.notYet"),
onClick: "dismiss" as const,
},
]
showToast({
persistent: true,
icon: "download",
title: language.t("toast.update.title"),
description: language.t("toast.update.description", { version: result.version ?? "" }),
actions,
})
})
.catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err)
showToast({ title: language.t("common.requestFailed"), description: message })
})
.finally(() => setStore("checking", false))
}
const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
const globalSync = useServerSync()
const globalSdk = useServerSDK()
const [shells] = createResource(
() =>
globalSdk.client.pty
.shells()
.then((res) => res.data ?? [])
.catch(() => [] as ShellOption[]),
{ initialValue: [] as ShellOption[] },
)
const [displayBackend, { refetch: refetchDisplayBackend }] = createResource(
() => (linux() && platform.getDisplayBackend ? true : false),
() => Promise.resolve(platform.getDisplayBackend?.() ?? null).catch(() => null as DisplayBackend | null),
{ initialValue: null as DisplayBackend | null },
)
const [pinchZoom, { mutate: setPinchZoom }] = createResource(
() => (desktop() && platform.getPinchZoomEnabled ? true : false),
() => Promise.resolve(platform.getPinchZoomEnabled?.() ?? false).catch(() => false),
{ initialValue: false },
)
onMount(() => {
void theme.loadThemes()
})
const autoOption = { id: "auto", value: "", label: language.t("settings.general.row.shell.autoDefault") }
const currentShell = createMemo(() => globalSync.data.config.shell ?? "")
const shellOptions = createMemo<ShellSelectOption[]>(() => {
const list = shells.latest
const current = globalSync.data.config.shell
const nameCounts = new Map<string, number>()
for (const s of list) {
nameCounts.set(s.name, (nameCounts.get(s.name) || 0) + 1)
}
const options = [
autoOption,
...list.map((s) => {
const ambiguousName = (nameCounts.get(s.name) || 0) > 1
const text = ambiguousName ? s.path : s.name
const label = s.acceptable ? text : `${text} (${language.t("settings.general.row.shell.terminalOnly")})`
return {
id: s.path,
// Prefer name over path - "bash" is much cleaner than the explicit full route even when it may change due to PATH.
value: ambiguousName ? s.path : s.name,
label,
}
}),
]
if (current && !options.some((o) => o.value === current)) {
options.push({ id: current, value: current, label: current })
}
return options
})
const onDisplayBackendChange = (checked: boolean) => {
const update = platform.setDisplayBackend?.(checked ? "wayland" : "auto")
if (!update) return
void update.finally(() => {
void refetchDisplayBackend()
})
}
const onPinchZoomChange = (checked: boolean) => {
setPinchZoom(checked)
const update = platform.setPinchZoomEnabled?.(checked)
if (!update) return
void update.catch(() => setPinchZoom(!checked))
}
const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
{ value: "system", label: language.t("theme.scheme.system") },
{ value: "light", label: language.t("theme.scheme.light") },
{ value: "dark", label: language.t("theme.scheme.dark") },
])
const languageOptions = createMemo(() =>
language.locales.map((locale) => ({
value: locale,
label: language.label(locale),
})),
)
const noneSound = { id: "none", label: "sound.option.none" } as const
const soundOptions = [noneSound, ...SOUND_OPTIONS]
const mono = () => monoInput(settings.appearance.font())
const sans = () => sansInput(settings.appearance.uiFont())
const terminal = () => terminalInput(settings.appearance.terminalFont())
const soundSelectProps = (
enabled: () => boolean,
current: () => string,
setEnabled: (value: boolean) => void,
set: (id: string) => void,
) => ({
options: soundOptions,
current: enabled() ? (soundOptions.find((o) => o.id === current()) ?? noneSound) : noneSound,
value: (o: (typeof soundOptions)[number]) => o.id,
label: (o: (typeof soundOptions)[number]) => language.t(o.label),
onHighlight: (option: (typeof soundOptions)[number] | undefined) => {
if (!option) return
playDemoSound(option.id === "none" ? undefined : option.id)
},
onSelect: (option: (typeof soundOptions)[number] | undefined) => {
if (!option) return
if (option.id === "none") {
setEnabled(false)
stopDemoSound()
return
}
setEnabled(true)
set(option.id)
playDemoSound(option.id)
},
})
const GeneralSection = () => (
<div class="settings-v2-section">
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.row.language.title")}
description={language.t("settings.general.row.language.description")}
>
<SelectV2
data-action="settings-language"
options={languageOptions()}
current={languageOptions().find((o) => o.value === language.locale())}
value={(o) => o.value}
label={(o) => o.label}
onSelect={(option) => option && language.setLocale(option.value)}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("command.permissions.autoaccept.enable")}
description={language.t("toast.permissions.autoaccept.on.description")}
>
<div data-action="settings-auto-accept-permissions">
<Switch checked={accepting()} disabled={!dir()} onChange={toggleAccept} />
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.shell.title")}
description={language.t("settings.general.row.shell.description")}
>
<SelectV2
data-action="settings-shell"
options={shellOptions()}
current={shellOptions().find((o) => o.value === currentShell()) ?? autoOption}
value={(o) => o.id}
label={(o) => o.label}
onSelect={(option) => {
if (!option) return
if (option.value === currentShell()) return
globalSync.updateConfig({ shell: option.value })
}}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.reasoningSummaries.title")}
description={language.t("settings.general.row.reasoningSummaries.description")}
>
<div data-action="settings-feed-reasoning-summaries">
<Switch
checked={settings.general.showReasoningSummaries()}
onChange={(checked) => settings.general.setShowReasoningSummaries(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.shellToolPartsExpanded.title")}
description={language.t("settings.general.row.shellToolPartsExpanded.description")}
>
<div data-action="settings-feed-shell-tool-parts-expanded">
<Switch
checked={settings.general.shellToolPartsExpanded()}
onChange={(checked) => settings.general.setShellToolPartsExpanded(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.editToolPartsExpanded.title")}
description={language.t("settings.general.row.editToolPartsExpanded.description")}
>
<div data-action="settings-feed-edit-tool-parts-expanded">
<Switch
checked={settings.general.editToolPartsExpanded()}
onChange={(checked) => settings.general.setEditToolPartsExpanded(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showSessionProgressBar.title")}
description={language.t("settings.general.row.showSessionProgressBar.description")}
>
<div data-action="settings-show-session-progress-bar">
<Switch
checked={settings.general.showSessionProgressBar()}
onChange={(checked) => settings.general.setShowSessionProgressBar(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.newLayoutDesigns.title")}
description={language.t("settings.general.row.newLayoutDesigns.description")}
>
<div data-action="settings-new-layout-designs">
<Switch
checked={settings.general.newLayoutDesigns()}
onChange={(checked) => {
settings.general.setNewLayoutDesigns(checked)
if (checked) return
void import("@/components/dialog-settings").then((module) => {
dialog.show(() => <module.DialogSettings />)
})
}}
/>
</div>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const AdvancedSection = () => (
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.advanced")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.row.showFileTree.title")}
description={language.t("settings.general.row.showFileTree.description")}
>
<div data-action="settings-show-file-tree">
<Switch
checked={settings.general.showFileTree()}
onChange={(checked) => settings.general.setShowFileTree(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showNavigation.title")}
description={language.t("settings.general.row.showNavigation.description")}
>
<div data-action="settings-show-navigation">
<Switch
checked={settings.general.showNavigation()}
onChange={(checked) => settings.general.setShowNavigation(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showSearch.title")}
description={language.t("settings.general.row.showSearch.description")}
>
<div data-action="settings-show-search">
<Switch
checked={settings.general.showSearch()}
onChange={(checked) => settings.general.setShowSearch(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showTerminal.title")}
description={language.t("settings.general.row.showTerminal.description")}
>
<div data-action="settings-show-terminal">
<Switch
checked={settings.general.showTerminal()}
onChange={(checked) => settings.general.setShowTerminal(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showStatus.title")}
description={language.t("settings.general.row.showStatus.description")}
>
<div data-action="settings-show-status">
<Switch
checked={settings.general.showStatus()}
onChange={(checked) => settings.general.setShowStatus(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.showCustomAgents.title")}
description={language.t("settings.general.row.showCustomAgents.description")}
>
<div data-action="settings-show-custom-agents">
<Switch
checked={settings.general.showCustomAgents()}
onChange={(checked) => settings.general.setShowCustomAgents(checked)}
/>
</div>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const AppearanceSection = () => (
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.appearance")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.row.colorScheme.title")}
description={language.t("settings.general.row.colorScheme.description")}
>
<SelectV2
data-action="settings-color-scheme"
options={colorSchemeOptions()}
current={colorSchemeOptions().find((o) => o.value === theme.colorScheme())}
value={(o) => o.value}
label={(o) => o.label}
onSelect={(option) => option && theme.setColorScheme(option.value)}
onHighlight={(option) => {
if (!option) return
theme.previewColorScheme(option.value)
return () => theme.cancelPreview()
}}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.theme.title")}
description={
<>
{language.t("settings.general.row.theme.description")}{" "}
<Link class="settings-v2-link" href="https://opencode.ai/docs/themes/">
{language.t("common.learnMore")}
</Link>
</>
}
>
<SelectV2
data-action="settings-theme"
options={themeOptions()}
current={themeOptions().find((o) => o.id === theme.themeId())}
value={(o) => o.id}
label={(o) => o.name}
onSelect={(option) => {
if (!option) return
theme.setTheme(option.id)
}}
onHighlight={(option) => {
if (!option) return
theme.previewTheme(option.id)
return () => theme.cancelPreview()
}}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.uiFont.title")}
description={language.t("settings.general.row.uiFont.description")}
>
<div class="w-full sm:w-[220px]">
<TextInputV2
data-action="settings-ui-font"
type="text"
appearance="base"
value={sans()}
onInput={(event) => settings.appearance.setUIFont(event.currentTarget.value)}
placeholder={sansDefault}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
aria-label={language.t("settings.general.row.uiFont.title")}
style={{ "font-family": sansFontFamily(settings.appearance.uiFont()) }}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.font.title")}
description={language.t("settings.general.row.font.description")}
>
<div class="w-full sm:w-[220px]">
<TextInputV2
data-action="settings-code-font"
type="text"
appearance="base"
value={mono()}
onInput={(event) => settings.appearance.setFont(event.currentTarget.value)}
placeholder={monoDefault}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
aria-label={language.t("settings.general.row.font.title")}
style={{ "font-family": monoFontFamily(settings.appearance.font()) }}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.terminalFont.title")}
description={language.t("settings.general.row.terminalFont.description")}
>
<div class="w-full sm:w-[220px]">
<TextInputV2
data-action="settings-terminal-font"
type="text"
appearance="base"
value={terminal()}
onInput={(event) => settings.appearance.setTerminalFont(event.currentTarget.value)}
placeholder={terminalDefault}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
aria-label={language.t("settings.general.row.terminalFont.title")}
style={{ "font-family": terminalFontFamily(settings.appearance.terminalFont()) }}
/>
</div>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const NotificationsSection = () => (
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.notifications")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.notifications.agent.title")}
description={language.t("settings.general.notifications.agent.description")}
>
<div data-action="settings-notifications-agent">
<Switch
checked={settings.notifications.agent()}
onChange={(checked) => settings.notifications.setAgent(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.notifications.permissions.title")}
description={language.t("settings.general.notifications.permissions.description")}
>
<div data-action="settings-notifications-permissions">
<Switch
checked={settings.notifications.permissions()}
onChange={(checked) => settings.notifications.setPermissions(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.notifications.errors.title")}
description={language.t("settings.general.notifications.errors.description")}
>
<div data-action="settings-notifications-errors">
<Switch
checked={settings.notifications.errors()}
onChange={(checked) => settings.notifications.setErrors(checked)}
/>
</div>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const SoundsSection = () => (
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.sounds")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.sounds.agent.title")}
description={language.t("settings.general.sounds.agent.description")}
>
<SelectV2
data-action="settings-sounds-agent"
{...soundSelectProps(
() => settings.sounds.agentEnabled(),
() => settings.sounds.agent(),
(value) => settings.sounds.setAgentEnabled(value),
(id) => settings.sounds.setAgent(id),
)}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.sounds.permissions.title")}
description={language.t("settings.general.sounds.permissions.description")}
>
<SelectV2
data-action="settings-sounds-permissions"
{...soundSelectProps(
() => settings.sounds.permissionsEnabled(),
() => settings.sounds.permissions(),
(value) => settings.sounds.setPermissionsEnabled(value),
(id) => settings.sounds.setPermissions(id),
)}
/>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.sounds.errors.title")}
description={language.t("settings.general.sounds.errors.description")}
>
<SelectV2
data-action="settings-sounds-errors"
{...soundSelectProps(
() => settings.sounds.errorsEnabled(),
() => settings.sounds.errors(),
(value) => settings.sounds.setErrorsEnabled(value),
(id) => settings.sounds.setErrors(id),
)}
/>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const UpdatesSection = () => (
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.updates")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.updates.row.startup.title")}
description={language.t("settings.updates.row.startup.description")}
>
<div data-action="settings-updates-startup">
<Switch
checked={settings.updates.startup()}
disabled={!platform.checkUpdate}
onChange={(checked) => settings.updates.setStartup(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.general.row.releaseNotes.title")}
description={language.t("settings.general.row.releaseNotes.description")}
>
<div data-action="settings-release-notes">
<Switch
checked={settings.general.releaseNotes()}
onChange={(checked) => settings.general.setReleaseNotes(checked)}
/>
</div>
</SettingsRowV2>
<SettingsRowV2
title={language.t("settings.updates.row.check.title")}
description={language.t("settings.updates.row.check.description")}
>
<ButtonV2 size="normal" variant="neutral" disabled={store.checking || !platform.checkUpdate} onClick={check}>
{store.checking
? language.t("settings.updates.action.checking")
: language.t("settings.updates.action.checkNow")}
</ButtonV2>
</SettingsRowV2>
</SettingsListV2>
</div>
)
const DisplaySection = () => (
<Show when={desktop()}>
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.general.section.display")}</h3>
<SettingsListV2>
<SettingsRowV2
title={language.t("settings.general.row.pinchZoom.title")}
description={language.t("settings.general.row.pinchZoom.description")}
>
<div data-action="settings-pinch-zoom">
<Switch checked={pinchZoom.latest} onChange={onPinchZoomChange} />
</div>
</SettingsRowV2>
<Show when={linux()}>
<SettingsRowV2
title={
<div class="flex items-center gap-2">
<span>{language.t("settings.general.row.wayland.title")}</span>
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<Icon name="help" size="small" />
</span>
</Tooltip>
</div>
}
description={language.t("settings.general.row.wayland.description")}
>
<div data-action="settings-wayland">
<Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</div>
</SettingsRowV2>
</Show>
</SettingsListV2>
</div>
</Show>
)
return (
<>
<div class="settings-v2-tab-header">
<h2 class="settings-v2-tab-title">{language.t("settings.tab.general")}</h2>
</div>
<div class="settings-v2-tab-body">
<GeneralSection />
<AppearanceSection />
<NotificationsSection />
<SoundsSection />
<UpdatesSection />
<DisplaySection />
<Show when={desktop()}>
<AdvancedSection />
</Show>
</div>
</>
)
}
@@ -0,0 +1 @@
export { DialogSettings } from "./dialog-settings-v2"
@@ -0,0 +1,138 @@
import { useFilteredList } from "@opencode-ai/ui/hooks"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { Switch } from "@opencode-ai/ui/v2/switch-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { TextInputV2 } from "@opencode-ai/ui/v2/text-input-v2"
import { type Component, For, Show } from "solid-js"
import { useLanguage } from "@/context/language"
import { useModels } from "@/context/models"
import { popularProviders } from "@/hooks/use-providers"
import { SettingsListV2 } from "./parts/list"
import { SettingsRowV2 } from "./parts/row"
import "./settings-v2.css"
type ModelItem = ReturnType<ReturnType<typeof useModels>["list"]>[number]
const PROVIDER_ICON_SIZE = 16
export const SettingsModelsV2: Component = () => {
const language = useLanguage()
const models = useModels()
const list = useFilteredList<ModelItem>({
items: (_filter) => models.list(),
key: (x) => `${x.provider.id}:${x.id}`,
filterKeys: ["provider.name", "name", "id"],
sortBy: (a, b) => a.name.localeCompare(b.name),
groupBy: (x) => x.provider.id,
sortGroupsBy: (a, b) => {
const aIndex = popularProviders.indexOf(a.category)
const bIndex = popularProviders.indexOf(b.category)
const aPopular = aIndex >= 0
const bPopular = bIndex >= 0
if (aPopular && !bPopular) return -1
if (!aPopular && bPopular) return 1
if (aPopular && bPopular) return aIndex - bIndex
const aName = a.items[0].provider.name
const bName = b.items[0].provider.name
return aName.localeCompare(bName)
},
})
return (
<>
<div class="settings-v2-tab-header settings-v2-tab-header--stacked">
<h2 class="settings-v2-tab-title">{language.t("settings.models.title")}</h2>
<div class="settings-v2-tab-search">
<TextInputV2
type="search"
appearance="base"
value={list.filter()}
onInput={(event) => list.onInput(event.currentTarget.value)}
placeholder={language.t("dialog.model.search.placeholder")}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
aria-label={language.t("dialog.model.search.placeholder")}
/>
<Show when={list.filter()}>
<IconButtonV2
type="button"
variant="ghost-muted"
size="small"
class="settings-v2-tab-search-clear"
icon={<IconV2 name="close" size="large" class="text-v2-icon-icon-muted" />}
onClick={() => list.clear()}
/>
</Show>
</div>
</div>
<div class="settings-v2-tab-body settings-v2-models">
<Show
when={!list.grouped.loading}
fallback={
<div class="settings-v2-models-status">
{language.t("common.loading")}
{language.t("common.loading.ellipsis")}
</div>
}
>
<Show
when={list.flat().length > 0}
fallback={
<div class="settings-v2-models-status">
<span>{language.t("dialog.model.empty")}</span>
<Show when={list.filter()}>
<span class="settings-v2-models-status-filter">&quot;{list.filter()}&quot;</span>
</Show>
</div>
}
>
<For each={list.grouped.latest}>
{(group) => (
<div class="settings-v2-section" data-component="settings-models-provider">
<div class="settings-v2-models-group-header">
<ProviderIcon
id={group.category}
width={PROVIDER_ICON_SIZE}
height={PROVIDER_ICON_SIZE}
class="settings-v2-models-provider-icon shrink-0"
/>
<h3 class="settings-v2-section-title">{group.items[0].provider.name}</h3>
</div>
<SettingsListV2>
<For each={group.items}>
{(item) => {
const key = { providerID: item.provider.id, modelID: item.id }
return (
<SettingsRowV2 title={item.name} description="">
<div>
<Switch
checked={models.visible(key)}
onChange={(checked) => {
models.setVisibility(key, checked)
}}
hideLabel
>
{item.name}
</Switch>
</div>
</SettingsRowV2>
)
}}
</For>
</SettingsListV2>
</div>
)}
</For>
</Show>
</Show>
</div>
</>
)
}
@@ -0,0 +1,6 @@
import type { Component, JSX } from "solid-js"
import "../settings-v2.css"
export const SettingsListV2: Component<{ children: JSX.Element }> = (props) => {
return <div data-component="settings-v2-list">{props.children}</div>
}
@@ -0,0 +1,20 @@
import type { Component, JSX } from "solid-js"
import "../settings-v2.css"
export interface SettingsRowV2Props {
title: string | JSX.Element
description: string | JSX.Element
children: JSX.Element
}
export const SettingsRowV2: Component<SettingsRowV2Props> = (props) => {
return (
<div data-component="settings-v2-row">
<div data-slot="settings-v2-row-copy">
<div data-slot="settings-v2-row-title">{props.title}</div>
<div data-slot="settings-v2-row-description">{props.description}</div>
</div>
<div data-slot="settings-v2-row-control">{props.children}</div>
</div>
)
}
@@ -0,0 +1,263 @@
import { ButtonV2 } from "@opencode-ai/ui/v2/button-v2"
import { Tag } from "@opencode-ai/ui/v2/badge-v2"
import { useDialog } from "@opencode-ai/ui/context/dialog"
import { ProviderIcon } from "@opencode-ai/ui/provider-icon"
import { showToast } from "@/utils/toast"
import { popularProviders, useProviders } from "@/hooks/use-providers"
import { createMemo, type Component, For, Show } from "solid-js"
import { useLanguage } from "@/context/language"
import { useServerSDK } from "@/context/server-sdk"
import { useServerSync } from "@/context/server-sync"
import { DialogConnectProvider } from "../dialog-connect-provider"
import { DialogSelectProvider } from "../dialog-select-provider"
import { DialogCustomProvider } from "../dialog-custom-provider"
import { SettingsListV2 } from "./parts/list"
import "./settings-v2.css"
type ProviderSource = "env" | "api" | "config" | "custom"
type ProviderItem = ReturnType<ReturnType<typeof useProviders>["connected"]>[number]
const PROVIDER_NOTES = [
{ match: (id: string) => id === "opencode", key: "dialog.provider.opencode.note" },
{ match: (id: string) => id === "opencode-go", key: "dialog.provider.opencodeGo.tagline" },
{ match: (id: string) => id === "anthropic", key: "dialog.provider.anthropic.note" },
{ match: (id: string) => id.startsWith("github-copilot"), key: "dialog.provider.copilot.note" },
{ match: (id: string) => id === "openai", key: "dialog.provider.openai.note" },
{ match: (id: string) => id === "google", key: "dialog.provider.google.note" },
{ match: (id: string) => id === "openrouter", key: "dialog.provider.openrouter.note" },
{ match: (id: string) => id === "vercel", key: "dialog.provider.vercel.note" },
] as const
const PROVIDER_ICON_SIZE = 16
export const SettingsProvidersV2: Component = () => {
const dialog = useDialog()
const language = useLanguage()
const globalSDK = useServerSDK()
const globalSync = useServerSync()
const providers = useProviders()
const connected = createMemo(() => {
return providers
.connected()
.filter((p) => p.id !== "opencode" || Object.values(p.models).find((m) => m.cost?.input))
})
const popular = createMemo(() => {
const connectedIDs = new Set(connected().map((p) => p.id))
const items = providers
.popular()
.filter((p) => !connectedIDs.has(p.id))
.slice()
items.sort((a, b) => popularProviders.indexOf(a.id) - popularProviders.indexOf(b.id))
return items
})
const source = (item: ProviderItem): ProviderSource | undefined => {
if (!("source" in item)) return
const value = item.source
if (value === "env" || value === "api" || value === "config" || value === "custom") return value
return
}
const type = (item: ProviderItem) => {
const current = source(item)
if (current === "env") return language.t("settings.providers.tag.environment")
if (current === "api") return language.t("provider.connect.method.apiKey")
if (current === "config") {
if (isConfigCustom(item.id)) return language.t("settings.providers.tag.custom")
return language.t("settings.providers.tag.config")
}
if (current === "custom") return language.t("settings.providers.tag.custom")
return language.t("settings.providers.tag.other")
}
const canDisconnect = (item: ProviderItem) => source(item) !== "env"
const note = (id: string) => PROVIDER_NOTES.find((item) => item.match(id))?.key
const isConfigCustom = (providerID: string) => {
const provider = globalSync.data.config.provider?.[providerID]
if (!provider) return false
if (provider.npm !== "@ai-sdk/openai-compatible") return false
if (!provider.models || Object.keys(provider.models).length === 0) return false
return true
}
const disableProvider = async (providerID: string, name: string) => {
const before = globalSync.data.config.disabled_providers ?? []
const next = before.includes(providerID) ? before : [...before, providerID]
globalSync.set("config", "disabled_providers", next)
await globalSync
.updateConfig({ disabled_providers: next })
.then(() => {
showToast({
variant: "success",
icon: "circle-check",
title: language.t("provider.disconnect.toast.disconnected.title", { provider: name }),
description: language.t("provider.disconnect.toast.disconnected.description", { provider: name }),
})
})
.catch((err: unknown) => {
globalSync.set("config", "disabled_providers", before)
const message = err instanceof Error ? err.message : String(err)
showToast({ title: language.t("common.requestFailed"), description: message })
})
}
const disconnect = async (providerID: string, name: string) => {
if (isConfigCustom(providerID)) {
await globalSDK.client.auth.remove({ providerID }).catch(() => undefined)
await disableProvider(providerID, name)
return
}
await globalSDK.client.auth
.remove({ providerID })
.then(async () => {
await globalSDK.client.global.dispose()
showToast({
variant: "success",
icon: "circle-check",
title: language.t("provider.disconnect.toast.disconnected.title", { provider: name }),
description: language.t("provider.disconnect.toast.disconnected.description", { provider: name }),
})
})
.catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err)
showToast({ title: language.t("common.requestFailed"), description: message })
})
}
return (
<>
<div class="settings-v2-tab-header">
<h2 class="settings-v2-tab-title">{language.t("settings.providers.title")}</h2>
</div>
<div class="settings-v2-tab-body settings-v2-providers">
<div class="settings-v2-section" data-component="connected-providers-section">
<h3 class="settings-v2-section-title">{language.t("settings.providers.section.connected")}</h3>
<SettingsListV2>
<Show
when={connected().length > 0}
fallback={
<div class="settings-v2-provider-empty">{language.t("settings.providers.connected.empty")}</div>
}
>
<For each={connected()}>
{(item) => (
<div class="settings-v2-provider-row group">
<div class="settings-v2-provider-lead">
<ProviderIcon
id={item.id}
width={PROVIDER_ICON_SIZE}
height={PROVIDER_ICON_SIZE}
class="settings-v2-provider-icon shrink-0"
/>
<div class="settings-v2-provider-main">
<span class="settings-v2-provider-name truncate">{item.name}</span>
<Tag>{type(item)}</Tag>
</div>
</div>
<Show
when={canDisconnect(item)}
fallback={
<span class="settings-v2-provider-env-hint">
{language.t("settings.providers.connected.environmentDescription")}
</span>
}
>
<ButtonV2 size="normal" variant="ghost-muted" onClick={() => void disconnect(item.id, item.name)}>
{language.t("common.disconnect")}
</ButtonV2>
</Show>
</div>
)}
</For>
</Show>
</SettingsListV2>
</div>
<div class="settings-v2-section">
<h3 class="settings-v2-section-title">{language.t("settings.providers.section.popular")}</h3>
<SettingsListV2>
<For each={popular()}>
{(item) => (
<div class="settings-v2-provider-row">
<div class="settings-v2-provider-lead">
<ProviderIcon
id={item.id}
width={PROVIDER_ICON_SIZE}
height={PROVIDER_ICON_SIZE}
class="settings-v2-provider-icon shrink-0"
/>
<div class="settings-v2-provider-copy">
<div class="settings-v2-provider-main">
<span class="settings-v2-provider-name">{item.name}</span>
<Show when={item.id === "opencode" || item.id === "opencode-go"}>
<Tag>{language.t("dialog.provider.tag.recommended")}</Tag>
</Show>
</div>
<Show when={note(item.id)}>
{(key) => <p class="settings-v2-provider-description">{language.t(key())}</p>}
</Show>
</div>
</div>
<ButtonV2
size="normal"
variant="neutral"
icon="plus"
onClick={() => {
dialog.show(() => <DialogConnectProvider provider={item.id} />)
}}
>
{language.t("common.connect")}
</ButtonV2>
</div>
)}
</For>
<div class="settings-v2-provider-row" data-component="custom-provider-section">
<div class="settings-v2-provider-lead">
<ProviderIcon
id="synthetic"
width={PROVIDER_ICON_SIZE}
height={PROVIDER_ICON_SIZE}
class="settings-v2-provider-icon shrink-0"
/>
<div class="settings-v2-provider-copy">
<div class="settings-v2-provider-main">
<span class="settings-v2-provider-name">{language.t("provider.custom.title")}</span>
<Tag>{language.t("settings.providers.tag.custom")}</Tag>
</div>
<p class="settings-v2-provider-description">{language.t("settings.providers.custom.description")}</p>
</div>
</div>
<ButtonV2
size="normal"
variant="neutral"
icon="plus"
onClick={() => {
dialog.show(() => <DialogCustomProvider back="close" />)
}}
>
{language.t("common.connect")}
</ButtonV2>
</div>
</SettingsListV2>
<button
type="button"
class="settings-v2-providers-view-all"
onClick={() => {
dialog.show(() => <DialogSelectProvider />)
}}
>
{language.t("dialog.provider.viewAll")}
</button>
</div>
</div>
</>
)
}
@@ -0,0 +1,515 @@
@import "@opencode-ai/ui/v2/text-input-v2.css";
@import "@opencode-ai/ui/v2/button-v2.css";
[data-component="settings-v2"] {
height: 100%;
}
[data-component="settings-v2-dialog"] [data-slot="dialog-body"] {
padding: 0;
overflow: hidden;
}
.settings-v2-panel {
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
scrollbar-width: none;
}
.settings-v2-panel::-webkit-scrollbar {
display: none;
}
.settings-v2-tab-header {
position: sticky;
top: 0;
z-index: 10;
padding: 40px 40px 32px;
background: linear-gradient(to bottom, var(--v2-background-bg-base) calc(100% - 24px), transparent);
}
.settings-v2-tab-title {
font-size: 15px;
font-weight: 640;
line-height: 1;
color: var(--v2-text-text-base);
}
.settings-v2-tab-body {
display: flex;
flex-direction: column;
gap: 36px;
width: 100%;
padding: 0 40px 40px;
}
[data-slot="settings-v2-row-description"] a.settings-v2-link {
color: var(--v2-text-text-accent);
cursor: pointer;
text-decoration: none;
}
[data-slot="settings-v2-row-description"] a.settings-v2-link:hover {
color: var(--v2-text-text-accent-hover);
}
.settings-v2-section {
display: flex;
flex-direction: column;
gap: 16px;
}
.settings-v2-section-title {
padding-bottom: 8px;
font-size: 15px;
font-weight: 640;
line-height: 1;
color: var(--v2-text-text-base);
}
.settings-v2-section-title + [data-component="settings-v2-list"] {
margin-top: -4px;
margin-bottom: 0;
}
[data-component="settings-v2-list"] {
border-radius: 8px;
background-color: var(--v2-background-bg-layer-01);
padding-inline: 20px;
box-shadow: inset 0 0 0 0.5px var(--v2-border-border-muted);
}
[data-slot="dialog-container"]:has(.settings-v2-dialog) {
box-shadow: var(--v2-elevation-overlay);
}
[data-component="settings-v2-row"] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 16px;
padding-block: 20px;
border-bottom: 0.5px solid var(--v2-border-border-base);
}
[data-component="settings-v2-row"]:last-child {
border-bottom: none;
}
@media (min-width: 640px) {
[data-component="settings-v2-row"] {
flex-wrap: nowrap;
}
}
[data-slot="settings-v2-row-copy"] {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 8px;
}
[data-slot="settings-v2-row-title"] {
font-style: normal;
font-size: 13px;
font-weight: 530;
line-height: 1;
letter-spacing: -0.04px;
color: var(--v2-text-text-base);
font-variation-settings: "slnt" 0;
}
[data-slot="settings-v2-row-description"] {
font-size: 11px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
}
[data-slot="settings-v2-row-control"] {
display: flex;
width: 100%;
justify-content: flex-end;
}
[data-slot="settings-v2-row-control"] > div:has([data-component="switch"]),
[data-slot="settings-v2-row-control"] > [data-component="switch"] {
display: inline-flex;
align-items: center;
padding: 4px;
}
@media (min-width: 640px) {
[data-slot="settings-v2-row-control"] {
width: auto;
flex-shrink: 0;
}
}
[data-slot="settings-v2-row-control"] [data-component="text-input-v2"] {
width: 100%;
}
[data-component="settings-v2-dialog"] [data-component="select"][data-trigger-style="settings-v2"] {
width: fit-content;
max-width: 100%;
}
[data-component="settings-v2-dialog"]
[data-component="select"][data-trigger-style="settings-v2"]
[data-component="button-v2"] {
width: fit-content;
max-width: 100%;
}
[data-component="tabs-v2"][data-variant="settings"][data-orientation="vertical"] [data-slot="tabs-v2-list"] {
background-color: var(--v2-background-bg-layer-01);
}
.settings-v2-nav-footer {
display: flex;
flex-direction: column;
gap: 8px;
padding: 4px 0 4px 4px;
}
.settings-v2-nav-footer > span {
font-size: 11px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-faint);
}
.settings-v2-legacy-panel {
height: 100%;
overflow: hidden;
}
.settings-v2-legacy-panel [data-component="dialog"] {
display: contents;
}
.settings-v2-provider-row {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-block: 20px;
border-bottom: 0.5px solid var(--v2-border-border-base);
}
.settings-v2-provider-row:last-child {
border-bottom: none;
}
@media (min-width: 640px) {
.settings-v2-provider-row {
flex-wrap: nowrap;
}
}
.settings-v2-providers [data-component="provider-icon"] {
color: var(--v2-icon-icon-base);
}
.settings-v2-provider-lead {
display: flex;
min-width: 0;
flex: 1;
align-items: flex-start;
gap: 10px;
}
.settings-v2-provider-lead:not(:has(.settings-v2-provider-copy)) {
align-items: center;
}
.settings-v2-provider-copy {
display: flex;
min-width: 0;
flex: 1;
flex-direction: column;
gap: 6px;
}
.settings-v2-provider-main {
display: flex;
min-width: 0;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
.settings-v2-provider-name {
font-size: 13px;
font-weight: 530;
line-height: 16px;
color: var(--v2-text-text-base);
}
.settings-v2-provider-description {
margin: 0;
font-size: 13px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
}
.settings-v2-provider-empty {
padding-block: 20px;
font-size: 13px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
}
.settings-v2-provider-env-hint {
padding-right: 12px;
font-size: 13px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
opacity: 0;
transition: opacity 200ms ease;
cursor: default;
}
.group:hover .settings-v2-provider-env-hint {
opacity: 1;
}
.settings-v2-providers-view-all {
margin-top: 20px;
padding: 0;
border: 0;
background: transparent;
font-size: 13px;
font-weight: 530;
line-height: 1;
color: var(--v2-text-text-accent);
cursor: pointer;
text-align: left;
}
.settings-v2-providers-view-all:hover {
color: var(--v2-text-text-accent-hover);
}
.settings-v2-tab-body.settings-v2-providers {
gap: 32px;
}
.settings-v2-tab-header:has(+ .settings-v2-tab-body.settings-v2-providers) {
padding-bottom: 32px;
}
.settings-v2-providers .settings-v2-section-title {
padding-bottom: 0;
font-size: 13px;
font-weight: 530;
line-height: 1;
}
.settings-v2-providers .settings-v2-section-title + [data-component="settings-v2-list"] {
margin-top: 16px;
}
.settings-v2-tab-header.settings-v2-tab-header--stacked {
display: flex;
flex-direction: column;
gap: 32px;
padding-bottom: 32px;
}
.settings-v2-tab-header--stacked > .settings-v2-tab-header-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
}
.settings-v2-tab-search {
position: relative;
width: 100%;
}
.settings-v2-tab-search [data-component="text-input-v2"] {
width: 100%;
}
.settings-v2-tab-search [data-slot="text-input-v2-input"] {
padding-right: 28px;
}
.settings-v2-tab-search-clear {
position: absolute;
top: 50%;
right: 6px;
z-index: 1;
transform: translateY(-50%);
}
.settings-v2-tab-body.settings-v2-models {
gap: 24px;
}
.settings-v2-models-group-header {
display: flex;
align-items: center;
gap: 8px;
padding-bottom: 8px;
}
.settings-v2-models .settings-v2-section-title {
padding-bottom: 0;
font-size: 13px;
font-weight: 530;
line-height: 16px;
}
.settings-v2-models [data-component="provider-icon"] {
color: var(--v2-icon-icon-base);
}
.settings-v2-models .settings-v2-section-title + [data-component="settings-v2-list"] {
margin-top: 0;
}
.settings-v2-models [data-slot="settings-v2-row-description"]:empty {
display: none;
}
.settings-v2-models [data-slot="settings-v2-row-copy"] {
gap: 0;
}
.settings-v2-models [data-slot="settings-v2-row-title"] {
min-width: 0;
overflow: hidden;
font-size: 13px;
font-weight: 440;
line-height: 1;
text-overflow: ellipsis;
white-space: nowrap;
}
.settings-v2-models-status {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
padding-block: 48px;
font-size: 13px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
text-align: center;
}
.settings-v2-models-status-filter {
color: var(--v2-text-text-base);
}
.settings-v2-shortcuts .settings-v2-section {
gap: 16px;
}
.settings-v2-shortcuts .settings-v2-section-title {
padding-bottom: 0;
font-size: 13px;
font-weight: 530;
line-height: 1;
}
.settings-v2-shortcuts [data-component="settings-v2-list"] {
display: flex;
flex-direction: column;
gap: 0;
padding: 20px;
border-radius: 6px;
}
.settings-v2-shortcuts [data-component="settings-v2-list"] > div {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding-top: 0;
padding-bottom: 0;
border-bottom: none;
}
.settings-v2-shortcuts [data-component="settings-v2-list"] > div:not(:last-child) {
padding-bottom: 16px;
margin-bottom: 16px;
border-bottom: 0.5px solid var(--v2-border-border-base);
}
.settings-v2-shortcuts [data-component="settings-v2-list"] > div > span {
font-weight: 440;
font-size: 13px;
line-height: 1;
letter-spacing: -0.04px;
color: var(--v2-text-text-base);
font-variation-settings: "slnt" 0;
}
.settings-v2-keybind-button {
box-sizing: border-box;
flex-shrink: 0;
padding: 6px 8px;
margin: -6px -8px;
border: 0;
border-radius: 2px;
background: transparent;
cursor: pointer;
font-style: normal;
font-weight: 530;
font-size: 11px;
line-height: 1;
letter-spacing: 0.05px;
font-variant-numeric: tabular-nums;
font-feature-settings:
"tnum" on,
"lnum" on;
font-variation-settings: "slnt" 0;
color: var(--v2-text-text-faint);
}
.settings-v2-keybind-button:hover {
background-color: var(--v2-background-bg-layer-02);
}
.settings-v2-keybind-button:focus-visible {
outline: 2px solid var(--v2-border-border-focus);
outline-offset: 2px;
}
.settings-v2-keybind-button--active {
color: var(--v2-text-text-faint);
border-radius: 2px;
background-color: var(--v2-background-bg-layer-02);
}
.settings-v2-shortcuts-status {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
padding-block: 48px;
font-size: 13px;
font-weight: 440;
line-height: 1;
color: var(--v2-text-text-muted);
text-align: center;
}
.settings-v2-shortcuts-status-filter {
color: var(--v2-text-text-base);
}
@@ -4,7 +4,7 @@ import { Icon } from "@opencode-ai/ui/icon"
import { Switch } from "@opencode-ai/ui/switch"
import { Tabs } from "@opencode-ai/ui/tabs"
import { useMutation, useQueryClient } from "@tanstack/solid-query"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { useNavigate } from "@solidjs/router"
import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
import { createStore } from "solid-js/store"
@@ -1,7 +1,7 @@
import { Button } from "@opencode-ai/ui/button"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { Popover } from "@opencode-ai/ui/popover"
import { Suspense, createMemo, createSignal, lazy, Show, type JSX } from "solid-js"
import { useLanguage } from "@/context/language"
+1 -1
View File
@@ -2,7 +2,7 @@ import { withAlpha } from "@opencode-ai/ui/theme/color"
import { useTheme } from "@opencode-ai/ui/theme/context"
import { resolveThemeVariant } from "@opencode-ai/ui/theme/resolve"
import type { HexColor } from "@opencode-ai/ui/theme/types"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import type { FitAddon, Ghostty, Terminal as Term } from "ghostty-web"
import { type ComponentProps, createEffect, createMemo, onCleanup, onMount, splitProps } from "solid-js"
import { SerializeAddon } from "@/addons/serialize"
+66 -46
View File
@@ -6,10 +6,10 @@ import { Icon } from "@opencode-ai/ui/icon"
import { Button } from "@opencode-ai/ui/button"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { useTheme } from "@opencode-ai/ui/theme/context"
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { getAvatarColors, useLayout, type LocalProject } from "@/context/layout"
import { getProjectAvatarVariant, useLayout, type LocalProject } from "@/context/layout"
import { usePlatform } from "@/context/platform"
import { useCommand } from "@/context/command"
import { useLanguage } from "@/context/language"
@@ -20,10 +20,10 @@ import { useServerSync } from "@/context/server-sync"
import { decodeDirectory } from "@/pages/directory-layout"
import { iife } from "@opencode-ai/core/util/iife"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { Avatar as AvatarV2 } from "@opencode-ai/ui/v2/components/avatar-v2.jsx"
import { ProjectAvatar } from "@opencode-ai/ui/v2/project-avatar-v2"
import { displayName, getProjectAvatarSource, projectForSession } from "@/pages/layout/helpers"
import { useSessionTabAvatarState } from "@/pages/layout/project-avatar-state"
import { makeEventListener } from "@solid-primitives/event-listener"
import { StatusPopoverV2 } from "@/components/status-popover"
import {
readSessionTabsRemovedDetail,
SESSION_TABS_REMOVED_EVENT,
@@ -52,7 +52,7 @@ const tauriApi = () => (window as unknown as { __TAURI__?: TauriApi }).__TAURI__
const currentDesktopWindow = () => tauriApi()?.window?.getCurrentWindow?.()
const currentThemeWindow = () => tauriApi()?.webviewWindow?.getCurrentWebviewWindow?.()
const legacyTitlebarHeight = 40
const v2TitlebarHeight = 44
const v2TitlebarHeight = 36
const minTitlebarZoom = 0.25
const windowsControlsBaseWidth = 138 // 3 native Windows caption buttons at 46px each.
@@ -133,8 +133,6 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
})
const v2RightState = createMemo<TitlebarV2RightState>(() => ({
update: updateState(),
statusVisible: !params.dir && settings.general.showStatus(),
statusLabel: language.t("status.popover.trigger"),
}))
const back = () => {
@@ -222,7 +220,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
<header
classList={{
"shrink-0 relative overflow-hidden flex flex-row": true,
"h-11 bg-v2-background-bg-deep": useV2Titlebar(),
"h-9 bg-v2-background-bg-deep": useV2Titlebar(),
"h-10 bg-background-base": !useV2Titlebar(),
}}
style={{
@@ -370,22 +368,28 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
return true
}
makeEventListener(
document,
"keydown",
(event) => {
if (!event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return
if (event.key.toLowerCase() !== "w") return
if (!(closeCurrentSessionTab() || closeNewSessionTab())) return
const openNewTab = () => navigate(newSessionHref())
event.preventDefault()
event.stopPropagation()
},
{ capture: true },
)
const closeActiveTab = () => closeCurrentSessionTab() || closeNewSessionTab()
command.register(() => {
const commands = [
{
id: "tab.new",
category: "tab",
title: language.t("command.session.new"),
keybind: "mod+t",
hidden: true,
onSelect: openNewTab,
},
{
id: "tab.close",
category: "tab",
title: language.t("command.tab.close"),
keybind: "mod+w",
hidden: true,
onSelect: closeActiveTab,
},
{
id: `tab.prev`,
category: "tab",
@@ -456,7 +460,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
return (
<div
class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3 py-2"
class="h-full flex-1 flex flex-row items-center gap-1.5 pr-3 pt-2"
classList={{
"pl-2": mac(),
"pl-4": !mac(),
@@ -489,6 +493,7 @@ export function Titlebar(props: { update?: TitlebarUpdate }) {
title={tab.info.title}
project={projectForSession(tab.info, projects(), projectByID())}
directory={tab.dir}
sessionId={tab.info.id}
onClose={() => tabsStoreActions.removeTab(tab.href)}
/>
</>
@@ -696,19 +701,12 @@ type TitlebarUpdatePillState = {
type TitlebarV2RightState = {
update: TitlebarUpdatePillState
statusVisible: boolean
statusLabel: string
}
function TitlebarV2Right(props: { state: TitlebarV2RightState }) {
return (
<div class="flex shrink-0 items-center justify-end gap-0">
<TitlebarUpdatePill state={props.state.update} />
<Show when={props.state.statusVisible}>
<Tooltip placement="bottom" value={props.state.statusLabel}>
<StatusPopoverV2 scope="server" />
</Tooltip>
</Show>
<div id="opencode-titlebar-right" class="flex shrink-0 items-center justify-end gap-0" />
</div>
)
@@ -736,26 +734,39 @@ function TabNavItem(props: {
title: string
project?: LocalProject
directory: string
sessionId: string
hideClose?: boolean
onClose: () => void
}) {
const match = useMatch(() => props.href)
const isActive = () => !!match()
const closeTab = (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
props.onClose()
}
return (
<div
class="group relative flex h-7 min-w-24 max-w-60 flex-row items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-[6px] bg-[var(--tab-bg)] px-1.5 [--tab-bg:var(--v2-background-bg-deep)] hover:[--tab-bg:var(--v2-background-bg-layer-02)] data-[active='true']:[--tab-bg:var(--v2-background-bg-layer-02)]"
data-active={isActive()}
onMouseDown={(event) => {
if (event.button !== 1) return
closeTab(event)
}}
>
<a
href={props.href}
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 overflow-hidden text-[13px] font-medium leading-5 text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
class="flex h-full min-w-0 flex-1 flex-row items-center gap-1.5 text-[13px] font-medium text-v2-text-text-faint group-data-[active='true']:text-v2-text-text-base"
>
<ProjectTabAvatar project={props.project} directory={props.directory} />
<span class="text-clip leading-5">{props.title}</span>
<span data-slot="project-avatar-slot">
<ProjectTabAvatar project={props.project} directory={props.directory} sessionId={props.sessionId} />
</span>
<span class="min-w-0 flex-1">{props.title}</span>
</a>
<div class="absolute not-group-hover:not-group-data-[active=true]:left-52 group-hover:right-0 group-data-[active=true]:right-0 inset-y-0 flex flex-row items-center pr-1 py-1 w-8 pl-2">
<div
class="absolute inset-0 bg-(image:--inactive-bg) group-hover:bg-(image:--active-bg) group-data-[active=true]:bg-(image:--active-bg)"
class="absolute inset-0 rounded-r-[6px] bg-(image:--inactive-bg) group-hover:bg-(image:--active-bg) group-data-[active=true]:bg-(image:--active-bg)"
style={{
"--inactive-bg": "linear-gradient(to right, transparent 0%, var(--tab-bg) 80%)",
"--active-bg": "linear-gradient(90deg, transparent 0%, var(--tab-bg) 25%)",
@@ -765,7 +776,7 @@ function TabNavItem(props: {
size="small"
variant="ghost-muted"
class="opacity-0 group-hover:opacity-100 group-data-[active='true']:opacity-100 z-10"
onClick={props.onClose}
onClick={closeTab}
icon={<IconV2 name="xmark-small" />}
/>
</div>
@@ -773,22 +784,35 @@ function TabNavItem(props: {
)
}
function ProjectTabAvatar(props: { project?: LocalProject; directory: string }) {
function ProjectTabAvatar(props: { project?: LocalProject; directory: string; sessionId: string }) {
const directory = () => props.directory
const sessionId = () => props.sessionId
const state = useSessionTabAvatarState(directory, sessionId)
return (
<AvatarV2
<ProjectAvatar
fallback={displayName(props.project ?? { worktree: props.directory })}
src={getProjectAvatarSource(props.project?.id, props.project?.icon)}
kind="org"
size="small"
{...getAvatarColors(props.project?.icon?.color)}
class="size-4 rounded"
variant={getProjectAvatarVariant(props.project?.icon?.color)}
unread={state.unread()}
loading={state.loading()}
/>
)
}
function NewSessionTabItem(props: { href: string; title: string; onClose: () => void }) {
const closeTab = (event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
props.onClose()
}
return (
<div class="group relative flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]">
<div
class="group relative flex h-7 max-w-60 flex-row items-center gap-1.5 overflow-hidden rounded-[6px] bg-[var(--v2-overlay-simple-overlay-pressed)] pl-1.5 pr-8 whitespace-nowrap focus-within:outline focus-within:outline-2 focus-within:outline-offset-2 focus-within:outline-[var(--v2-border-border-focus)]"
onMouseDown={(event) => {
if (event.button !== 1) return
closeTab(event)
}}
>
<a
href={props.href}
aria-current="page"
@@ -807,11 +831,7 @@ function NewSessionTabItem(props: { href: string; title: string; onClose: () =>
event.preventDefault()
event.stopPropagation()
}}
onClick={(event) => {
event.preventDefault()
event.stopPropagation()
props.onClose()
}}
onClick={closeTab}
icon={<IconV2 name="xmark-small" />}
aria-label="Close tab"
/>
@@ -2,8 +2,8 @@ import { Show, type JSX } from "solid-js"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { IconButtonV2 } from "@opencode-ai/ui/v2/components/icon-button-v2.jsx"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/components/icon.jsx"
import { IconButtonV2 } from "@opencode-ai/ui/v2/icon-button-v2"
import { Icon as IconV2 } from "@opencode-ai/ui/v2/icon"
import { useCommand } from "@/context/command"
import { DESKTOP_MENU, desktopMenuVisible, type DesktopMenuAction, type DesktopMenuEntry } from "@/desktop-menu"
+1 -1
View File
@@ -178,7 +178,7 @@ export const createDirSyncContext = (directory: string, serverSync: ReturnType<t
type Child = ReturnType<(typeof serverSync)["child"]>
type Setter = Child[1]
const current = createMemo(() => serverSync.child(directory))
const current = createMemo(() => serverSync.child(directory, { mcp: true }))
const target = (directory?: string) => {
if (!directory || directory === directory) return current()
return serverSync.child(directory)
+1 -1
View File
@@ -1,7 +1,7 @@
import { batch, createEffect, createMemo, onCleanup } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { useParams } from "@solidjs/router"
import { getFilename } from "@opencode-ai/core/util/path"
import { useSDK } from "./sdk"
@@ -10,6 +10,7 @@ const provider = { all: new Map(), connected: [], default: {} } satisfies Normal
describe("bootstrapDirectory", () => {
test("marks a loading directory partial during bootstrap and complete after success", async () => {
const mcpReads: string[] = []
const [store, setStore] = createStore<State>({
status: "loading",
agent: [],
@@ -44,6 +45,7 @@ describe("bootstrapDirectory", () => {
await bootstrapDirectory({
directory: "/project",
mcp: false,
global: {
config: {} satisfies Config,
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
@@ -55,10 +57,20 @@ describe("bootstrapDirectory", () => {
config: { get: async () => ({ data: {} }) },
session: { status: async () => ({ data: {} }) },
vcs: { get: async () => ({ data: undefined }) },
command: { list: async () => ({ data: [] }) },
command: {
list: async () => {
mcpReads.push("command")
return { data: [] }
},
},
permission: { list: async () => ({ data: [] }) },
question: { list: async () => ({ data: [] }) },
mcp: { status: async () => ({ data: {} }) },
mcp: {
status: async () => {
mcpReads.push("status")
return { data: {} }
},
},
provider: { list: async () => ({ data: { all: [], connected: [], default: {} } }) },
} as unknown as OpencodeClient,
store,
@@ -74,5 +86,6 @@ describe("bootstrapDirectory", () => {
await new Promise((resolve) => setTimeout(resolve, 80))
expect(store.status).toBe("complete")
expect(mcpReads).toEqual([])
})
})
@@ -9,7 +9,7 @@ import type {
Session,
Todo,
} from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { getFilename } from "@opencode-ai/core/util/path"
import { retry } from "@opencode-ai/core/util/retry"
import { batch } from "solid-js"
@@ -198,6 +198,7 @@ export const loadPathQuery = (directory: string | null, sdk: OpencodeClient) =>
export async function bootstrapDirectory(input: {
directory: string
mcp: boolean
sdk: OpencodeClient
store: Store<State>
setStore: SetStoreFunction<State>
@@ -250,7 +251,7 @@ export async function bootstrapDirectory(input: {
if (next) input.vcsCache.setStore("value", next)
}),
),
() => retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? []))),
input.mcp && (() => retry(() => input.sdk.command.list().then((x) => input.setStore("command", x.data ?? [])))),
() =>
retry(() =>
input.sdk.permission.list().then((x) => {
@@ -304,7 +305,7 @@ export async function bootstrapDirectory(input: {
}),
),
() => Promise.resolve(input.loadSessions(input.directory)),
() => input.queryClient.fetchQuery(loadMcpQuery(input.directory, input.sdk)),
input.mcp && (() => input.queryClient.fetchQuery(loadMcpQuery(input.directory, input.sdk))),
() =>
input.queryClient.fetchQuery(loadProvidersQuery(input.directory, input.sdk)).catch((err) => {
const project = getFilename(input.directory)
@@ -6,6 +6,7 @@ import type { State } from "./types"
import type { QueryOptionsApi } from "../server-sync"
let createChildStoreManager: typeof import("./child-store").createChildStoreManager
const querySingles: Array<() => { queryKey?: unknown[]; enabled?: boolean }> = []
const child = () => createStore({} as State)
const provider = { all: new Map(), connected: [], default: {} } satisfies NormalizedProviderListResponse
@@ -48,12 +49,21 @@ beforeAll(async () => {
persisted: (_target: string, store: unknown[]) => [store[0], store[1], null, () => true],
}))
mock.module("@tanstack/solid-query", () => ({
useQueries: () => [
{ isLoading: false, data: { state: "", config: "", worktree: "", directory: "", home: "" } },
{ isLoading: false, data: {} },
{ isLoading: false, data: [] },
{ isLoading: false, data: provider },
],
useQuery: (options: () => { queryKey?: unknown[]; enabled?: boolean }) => {
querySingles.push(options)
return {
get isLoading() {
return options().queryKey?.[1] === "path"
},
get data() {
if (options().queryKey?.[1] === "path") throw new Error("pending path data read")
if (options().queryKey?.[1] === "mcp") return options().enabled ? { demo: { status: "disabled" } } : undefined
if (options().queryKey?.[1] === "lsp") return []
if (options().queryKey?.[1] === "providers") return provider
return undefined
},
}
},
}))
createChildStoreManager = (await import("./child-store")).createChildStoreManager
@@ -73,6 +83,7 @@ describe("createChildStoreManager", () => {
isBooting: () => false,
isLoadingSessions: () => false,
onBootstrap() {},
onMcp() {},
onDispose() {},
translate: (key) => key,
queryOptions: queryOptionsApi,
@@ -103,6 +114,7 @@ describe("createChildStoreManager", () => {
onBootstrap(directory) {
bootstraps.push(directory)
},
onMcp() {},
onDispose() {},
translate: (key) => key,
queryOptions: queryOptionsApi,
@@ -121,4 +133,76 @@ describe("createChildStoreManager", () => {
dispose()
}
})
test("provides the requested directory while the path query is pending", () => {
let manager: ReturnType<typeof createChildStoreManager> | undefined
const dispose = createOwner((owner) => {
manager = createChildStoreManager({
owner,
isBooting: () => false,
isLoadingSessions: () => false,
onBootstrap() {},
onMcp() {},
onDispose() {},
translate: (key) => key,
queryOptions: queryOptionsApi,
global: { provider },
})
})
try {
if (!manager) throw new Error("manager required")
const [store] = manager.child("/project", { bootstrap: false })
expect(store.path.directory).toBe("/project")
expect(store.path.worktree).toBe("")
} finally {
dispose()
}
})
test("enables MCP only when requested for the directory", () => {
let manager: ReturnType<typeof createChildStoreManager> | undefined
const offset = querySingles.length
const mcpLoads: string[] = []
const dispose = createOwner((owner) => {
manager = createChildStoreManager({
owner,
isBooting: () => false,
isLoadingSessions: () => false,
onBootstrap() {},
onMcp(directory) {
mcpLoads.push(directory)
},
onDispose() {},
translate: (key) => key,
queryOptions: queryOptionsApi,
global: { provider },
})
})
try {
if (!manager) throw new Error("manager required")
const [store, setStore] = manager.child("/project", { bootstrap: false })
expect(querySingles.length - offset).toBe(4)
const query = querySingles[offset + 1]
if (!query) throw new Error("query required")
expect(query().enabled).toBe(false)
setStore("status", "complete")
manager.child("/project", { bootstrap: false, mcp: true })
expect(query().enabled).toBe(true)
expect(store.mcp).toEqual({ demo: { status: "disabled" } })
expect(mcpLoads).toEqual(["/project"])
manager.disableMcp("/project")
expect(query().enabled).toBe(false)
expect(manager.mcp("/project")).toBe(false)
} finally {
dispose()
}
})
})
@@ -1,4 +1,4 @@
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
import { createRoot, createSignal, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist"
import type { VcsInfo } from "@opencode-ai/sdk/v2/client"
@@ -14,7 +14,7 @@ import {
type VcsCache,
} from "./types"
import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
import { useQueries } from "@tanstack/solid-query"
import { useQuery } from "@tanstack/solid-query"
import { QueryOptionsApi } from "../server-sync"
import { directoryKey, type DirectoryKey } from "./utils"
import { NormalizedProviderListResponse } from "@opencode-ai/ui/context"
@@ -24,6 +24,7 @@ export function createChildStoreManager(input: {
isBooting: (directory: string) => boolean
isLoadingSessions: (directory: string) => boolean
onBootstrap: (directory: string) => void
onMcp: (directory: string, setStore: SetStoreFunction<State>) => void
onDispose: (directory: string) => void
translate: (key: string, vars?: Record<string, string | number>) => string
queryOptions: QueryOptionsApi
@@ -39,6 +40,8 @@ export function createChildStoreManager(input: {
const pins = new Map<string, number>()
const ownerPins = new WeakMap<object, Set<string>>()
const disposers = new Map<string, () => void>()
const mcpDirectories = new Set<string>()
const mcpToggles = new Map<string, (enabled: boolean) => void>()
const markKey = (key: DirectoryKey) => {
if (!key) return
@@ -110,6 +113,8 @@ export function createChildStoreManager(input: {
metaCache.delete(key)
iconCache.delete(key)
lifecycle.delete(key)
mcpDirectories.delete(key)
mcpToggles.delete(key)
const dispose = disposers.get(key)
if (dispose) {
dispose()
@@ -173,15 +178,12 @@ export function createChildStoreManager(input: {
createRoot((dispose) => {
const initialMeta = meta[0].value
const initialIcon = icon[0].value
const [mcpEnabled, setMcpEnabled] = createSignal(false)
const [pathQuery, mcpQuery, lspQuery, providerQuery] = useQueries(() => ({
queries: [
input.queryOptions.path(key),
input.queryOptions.mcp(key),
input.queryOptions.lsp(key),
input.queryOptions.providers(key),
],
}))
const pathQuery = useQuery(() => input.queryOptions.path(key))
const mcpQuery = useQuery(() => ({ ...input.queryOptions.mcp(key), enabled: mcpEnabled() }))
const lspQuery = useQuery(() => input.queryOptions.lsp(key))
const providerQuery = useQuery(() => input.queryOptions.providers(key))
const child = createStore<State>({
project: "",
@@ -198,9 +200,9 @@ export function createChildStoreManager(input: {
},
config: {},
get path() {
if (pathQuery.isLoading || !pathQuery.data)
return { state: "", config: "", worktree: "", directory: "", home: "" }
return pathQuery.data
const EMPTY = { state: "", config: "", worktree: "", directory, home: "" }
if (pathQuery.isLoading) return EMPTY
return pathQuery.data ?? EMPTY
},
status: "loading" as const,
agent: [],
@@ -236,6 +238,7 @@ export function createChildStoreManager(input: {
})
children[key] = child
disposers.set(key, dispose)
mcpToggles.set(key, setMcpEnabled)
const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => {
if (!(init instanceof Promise)) return
@@ -274,6 +277,7 @@ export function createChildStoreManager(input: {
const key = directoryKey(directory)
const childStore = ensureChild(directory)
pinForOwner(key)
if (options.mcp) enableMcp(directory, key, childStore)
const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") {
input.onBootstrap(directory)
@@ -284,6 +288,7 @@ export function createChildStoreManager(input: {
function peek(directory: string, options: ChildOptions = {}) {
const key = directoryKey(directory)
const childStore = ensureChild(directory)
if (options.mcp) enableMcp(directory, key, childStore)
const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") {
input.onBootstrap(directory)
@@ -291,6 +296,19 @@ export function createChildStoreManager(input: {
return childStore
}
function enableMcp(directory: string, key: DirectoryKey, childStore: [Store<State>, SetStoreFunction<State>]) {
if (mcpDirectories.has(key)) return
mcpDirectories.add(key)
mcpToggles.get(key)?.(true)
if (childStore[0].status !== "loading") input.onMcp(directory, childStore[1])
}
function disableMcp(directory: string) {
const key = directoryKey(directory)
if (!mcpDirectories.delete(key)) return
mcpToggles.get(key)?.(false)
}
function projectMeta(directory: string, patch: ProjectMeta) {
const key = directoryKey(directory)
const [store, setStore] = ensureChild(directory)
@@ -330,6 +348,8 @@ export function createChildStoreManager(input: {
pin,
unpin,
pinned,
mcp: (directory: string) => mcpDirectories.has(directoryKey(directory)),
disableMcp,
disposeDirectory,
runEviction,
vcsCache,
@@ -98,6 +98,7 @@ export type IconCache = {
export type ChildOptions = {
bootstrap?: boolean
mcp?: boolean
}
export type DirState = {
+13
View File
@@ -12,6 +12,9 @@ import { decode64 } from "@/utils/base64"
import { same } from "@/utils/same"
import { createScrollPersistence, type SessionScroll } from "./layout-scroll"
import { createPathHelpers } from "./file/path"
import type { ProjectAvatarVariant } from "@opencode-ai/ui/v2/project-avatar-v2"
export type { ProjectAvatarVariant }
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
const DEFAULT_SIDEBAR_WIDTH = 344
@@ -33,6 +36,16 @@ export function getAvatarColors(key?: string) {
}
}
export function getProjectAvatarVariant(key?: string): ProjectAvatarVariant {
if (key === "orange") return "orange"
if (key === "pink") return "pink"
if (key === "cyan") return "cyan"
if (key === "purple") return "purple"
if (key === "mint") return "cyan"
if (key === "lime") return "green"
return "gray"
}
type SessionTabs = {
active?: string
all: string[]
+22 -2
View File
@@ -1,5 +1,5 @@
import type { Config, OpencodeClient, Path, Project, ProviderAuthResponse, Todo } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { getFilename } from "@opencode-ai/core/util/path"
import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store"
@@ -31,6 +31,7 @@ import { PathKey } from "@/utils/path-key"
import { createDirSyncContext } from "./directory-sync"
import { createSimpleContext, NormalizedProviderListResponse } from "@opencode-ai/ui/context"
import { createRefCountMap } from "@/utils/refcount"
import { retry } from "@opencode-ai/core/util/retry"
type GlobalStore = {
ready: boolean
@@ -205,6 +206,19 @@ export function createServerSyncContext() {
onBootstrap: (directory) => {
void bootstrapInstance(directory)
},
onMcp: (directory, setStore) => {
void retry(() =>
sdkFor(directory)
.command.list()
.then((x) => setStore("command", x.data ?? [])),
).catch((err) => {
showToast({
variant: "error",
title: language.t("toast.project.reloadFailed.title", { project: getFilename(directory) }),
description: formatServerError(err, language.t),
})
})
},
onDispose: (directory) => {
const key = directoryKey(directory)
queue.clear(key)
@@ -311,6 +325,7 @@ export function createServerSyncContext() {
const sdk = sdkFor(directory)
await bootstrapDirectory({
directory,
mcp: children.mcp(key),
global: {
config: globalStore.config,
path: globalStore.path,
@@ -437,6 +452,7 @@ export function createServerSyncContext() {
},
child: children.child,
peek: children.peek,
disableMcp: children.disableMcp,
queryOptions: queryOptionsApi,
// bootstrap,
updateConfig: updateConfigMutation.mutateAsync,
@@ -454,7 +470,11 @@ export const { use: useServerSync, provider: ServerSyncProvider } = createSimple
return {
...sync,
createDirSyncContext: createRefCountMap((dir) => createDirSyncContext(dir, sync)),
createDirSyncContext: createRefCountMap(
(dir) => createDirSyncContext(dir, sync),
(dir) => sync.disableMcp(dir),
directoryKey,
),
}
},
})
+2
View File
@@ -531,6 +531,8 @@ export const dict = {
"home.projects": "Projects",
"home.project.add": "Add project",
"home.sessions.search.placeholder": "Search sessions",
"home.sessions.search.sessions": "Sessions",
"home.sessions.search.noResults": "No sessions found for {{query}}",
"home.sessions.empty": "No sessions found",
"home.sessions.empty.description": "Start a new session for this project",
"home.sessions.group.today": "Today",
+2
View File
@@ -502,6 +502,8 @@ export const dict = {
"home.projects": "项目",
"home.project.add": "添加项目",
"home.sessions.search.placeholder": "搜索会话",
"home.sessions.search.sessions": "会话",
"home.sessions.search.noResults": "未找到与 {{query}} 相关的会话",
"home.sessions.empty": "未找到会话",
"home.sessions.group.today": "今天",
"home.sessions.group.yesterday": "昨天",
+1 -1
View File
@@ -1,5 +1,5 @@
import { DataProvider } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { base64Encode } from "@opencode-ai/core/util/encode"
import { useLocation, useNavigate, useParams } from "@solidjs/router"
import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -34,7 +34,8 @@ import { createStore, produce, reconcile } from "solid-js/store"
import { DragDropProvider, DragDropSensors, DragOverlay, SortableProvider, closestCenter } from "@thisbeyond/solid-dnd"
import type { DragEvent } from "@thisbeyond/solid-dnd"
import { useProviders } from "@/hooks/use-providers"
import { showToast, Toast, toaster } from "@opencode-ai/ui/toast"
import { toaster } from "@opencode-ai/ui/toast"
import { setV2Toast, showToast, ToastRegion } from "@/utils/toast"
import { useServerSDK } from "@/context/server-sdk"
import { clearWorkspaceTerminals, getTerminalServerScope } from "@/context/terminal"
import { dropSessionCaches, pickSessionCacheEvictions } from "@/context/global-sync/session-cache"
@@ -128,6 +129,7 @@ export default function Layout(props: ParentProps) {
const theme = useTheme()
const language = useLanguage()
const newDesign = createMemo(() => settings.general.newLayoutDesigns())
createEffect(() => setV2Toast(newDesign()))
const initialDirectory = decode64(params.dir)
const location = useLocation()
const route = createMemo(() => {
@@ -1227,7 +1229,10 @@ export default function Layout(props: ParentProps) {
function openSettings() {
const run = ++dialogRun
void import("@/components/dialog-settings").then((x) => {
const module = settings.general.newLayoutDesigns()
? import("@/components/settings-v2")
: import("@/components/dialog-settings")
void module.then((x) => {
if (dialogDead || dialogRun !== run) return
dialog.show(() => <x.DialogSettings />)
})
@@ -1639,7 +1644,7 @@ export default function Layout(props: ParentProps) {
})
onMount(() => {
serverSDK.client.file
serverSDK.client.vcs
.status({ directory: props.directory })
.then((x) => {
const files = x.data ?? []
@@ -1707,7 +1712,7 @@ export default function Layout(props: ParentProps) {
}
onMount(() => {
serverSDK.client.file
serverSDK.client.vcs
.status({ directory: props.directory })
.then((x) => {
const files = x.data ?? []
@@ -2369,18 +2374,13 @@ export default function Layout(props: ParentProps) {
<div class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text">
{autoselecting() ?? ""}
<Titlebar update={titlebarUpdate} />
<main
class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict bg-v2-background-bg-base"
classList={{
"m-2 mt-0 rounded-[10px] shadow-[var(--v2-elevation-raised)] overflow-hidden": !!params.id || !params.dir,
}}
>
<main class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-strict">
<Show when={!autoselecting.loading} fallback={<div class="size-full" />}>
{props.children}
</Show>
</main>
{import.meta.env.DEV && <DebugBar />}
<Toast.Region />
<ToastRegion v2={newDesign()} />
</div>
}
>
@@ -2533,7 +2533,7 @@ export default function Layout(props: ParentProps) {
</div>
{import.meta.env.DEV && <DebugBar />}
</div>
<Toast.Region />
<ToastRegion v2={newDesign()} />
</div>
</Show>
)
@@ -0,0 +1,24 @@
import { createMemo, type Accessor } from "solid-js"
import { useServerSync } from "@/context/server-sync"
import { useNotification } from "@/context/notification"
import { usePermission } from "@/context/permission"
import { sessionPermissionRequest } from "@/pages/session/composer/session-request-tree"
export function useSessionTabAvatarState(directory: Accessor<string>, sessionId: Accessor<string>) {
const globalSync = useServerSync()
const notification = useNotification()
const permission = usePermission()
const hasPermissions = createMemo(() => {
const [store] = globalSync.child(directory(), { bootstrap: false })
return !!sessionPermissionRequest(store.session, store.permission, sessionId(), (item) => {
return !permission.autoResponds(item, directory())
})
})
const unread = createMemo(() => hasPermissions() || notification.session.unseenCount(sessionId()) > 0)
const loading = createMemo(() => {
if (hasPermissions()) return false
const [store] = globalSync.child(directory(), { bootstrap: false })
return store.session_working(sessionId())
})
return { unread, loading }
}
+18 -4
View File
@@ -28,7 +28,7 @@ import { Tabs } from "@opencode-ai/ui/tabs"
import { createAutoScroll } from "@opencode-ai/ui/hooks"
import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
import { Button } from "@opencode-ai/ui/button"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { checksum } from "@opencode-ai/core/util/encode"
import { useLocation, useSearchParams } from "@solidjs/router"
import { NewSessionDesignView, NewSessionView, SessionHeader } from "@/components/session"
@@ -1707,10 +1707,15 @@ export default function Page() {
)
return (
<div class="relative bg-background-base size-full overflow-hidden flex flex-col">
<div class="relative size-full overflow-hidden flex flex-col">
{sessionSync() ?? ""}
<SessionHeader />
<div class="flex-1 min-h-0 flex flex-col md:flex-row">
<div
class="flex-1 min-h-0 flex flex-col md:flex-row "
classList={{
"gap-2 p-2": settings.general.newLayoutDesigns(),
}}
>
<Show when={!isDesktop() && !!params.id}>
<Tabs value={store.mobileTab} class="h-auto">
<Tabs.List>
@@ -1742,12 +1747,18 @@ export default function Page() {
"duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!size.active() && !ui.reviewSnap,
"transition-[width]": !isV2NewSessionPage(),
"rounded-[10px] shadow-[var(--v2-elevation-raised)]": settings.general.newLayoutDesigns(),
}}
style={{
width: sessionPanelWidth(),
}}
>
<div class="flex-1 min-h-0 overflow-hidden">
<div
class="flex-1 min-h-0 overflow-hidden"
classList={{
"rounded-[10px]": settings.general.newLayoutDesigns(),
}}
>
<Switch>
<Match when={params.id && mobileChanges()}>
<div class="relative h-full overflow-hidden">
@@ -1810,6 +1821,9 @@ export default function Page() {
<Show when={desktopReviewOpen()}>
<div onPointerDown={() => size.start()}>
<ResizeHandle
classList={{
"-right-1": settings.general.newLayoutDesigns(),
}}
direction="horizontal"
size={layout.session.width()}
min={450}
@@ -17,6 +17,7 @@ import type { SessionComposerState } from "@/pages/session/composer/session-comp
import { SessionTodoDock } from "@/pages/session/composer/session-todo-dock"
import type { FollowupDraft } from "@/components/prompt-input/submit"
import { createResizeObserver } from "@solid-primitives/resize-observer"
import { NEW_SESSION_CONTENT_WIDTH } from "@/pages/session/new-session-layout"
export function SessionComposerRegion(props: {
state: SessionComposerState
@@ -150,8 +151,9 @@ export function SessionComposerRegion(props: {
>
<div
classList={{
"w-full px-3 pointer-events-auto": true,
"max-w-[720px] px-0": props.placement === "inline",
"w-full pointer-events-auto": true,
"px-3": props.placement !== "inline",
[NEW_SESSION_CONTENT_WIDTH]: props.placement === "inline",
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
}}
>
@@ -2,7 +2,7 @@ import { createEffect, createMemo, on, onCleanup } from "solid-js"
import { createStore } from "solid-js/store"
import type { PermissionRequest, QuestionRequest, Todo } from "@opencode-ai/sdk/v2"
import { useParams } from "@solidjs/router"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { useServerSync } from "@/context/server-sync"
import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission"
@@ -4,7 +4,7 @@ import { useMutation } from "@tanstack/solid-query"
import { Button } from "@opencode-ai/ui/button"
import { DockPrompt } from "@opencode-ai/ui/dock-prompt"
import { Icon } from "@opencode-ai/ui/icon"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import type { QuestionAnswer, QuestionRequest } from "@opencode-ai/sdk/v2"
import { useLanguage } from "@/context/language"
import { useSDK } from "@/context/sdk"
+1 -1
View File
@@ -11,7 +11,7 @@ import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tabs } from "@opencode-ai/ui/tabs"
import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { selectionFromLines, useFile, type FileSelection, type SelectedLineRange } from "@/context/file"
import { useComments } from "@/context/comments"
import { useLanguage } from "@/context/language"
@@ -48,7 +48,7 @@ import type {
ToolPart,
UserMessage,
} from "@opencode-ai/sdk/v2"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { Binary } from "@opencode-ai/core/util/binary"
import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import { Popover as KobaltePopover } from "@kobalte/core/popover"
@@ -1,3 +1,6 @@
/** Inline new-session content width — keep in sync with session composer `placement === "inline"`. */
export const NEW_SESSION_CONTENT_WIDTH = "w-full max-w-[720px] px-0"
export function shouldUseV2NewSessionPage(input: { newLayoutDesigns: boolean; sessionID?: string }) {
return input.newLayoutDesigns && !input.sessionID
}
@@ -67,7 +67,7 @@ export function SessionSidePanel(props: {
const reviewTab = createMemo(() => isDesktop())
const panelWidth = createMemo(() => {
if (!open()) return "0px"
if (reviewOpen()) return `calc(100% - ${layout.session.width()}px)`
if (reviewOpen()) return "auto"
return `${layout.fileTree.width()}px`
})
const treeWidth = createMemo(() => (fileOpen() ? `${layout.fileTree.width()}px` : "0px"))
@@ -214,11 +214,18 @@ export function SessionSidePanel(props: {
"pointer-events-none": !open(),
"transition-[width] duration-[240ms] ease-[cubic-bezier(0.22,1,0.36,1)] will-change-[width] motion-reduce:transition-none":
!props.size.active() && !props.reviewSnap,
"rounded-[10px] shadow-[var(--v2-elevation-raised)] overflow-hidden": settings.general.newLayoutDesigns(),
"flex-1": reviewOpen(),
}}
style={{ width: panelWidth() }}
>
<Show when={open()}>
<div class="size-full flex border-l border-border-weaker-base">
<div
class="size-full flex"
classList={{
"border-l border-border-weaker-base": !settings.general.newLayoutDesigns(),
}}
>
<div
aria-hidden={!reviewOpen()}
inert={!reviewOpen()}
@@ -13,7 +13,7 @@ import { useSDK } from "@/context/sdk"
import { useSettings } from "@/context/settings"
import { useSync } from "@/context/sync"
import { useTerminal } from "@/context/terminal"
import { showToast } from "@opencode-ai/ui/toast"
import { showToast } from "@/utils/toast"
import { findLast } from "@opencode-ai/core/util/array"
import { createSessionTabs } from "@/pages/session/helpers"
import { extractPromptFromParts } from "@/utils/prompt"
+49
View File
@@ -0,0 +1,49 @@
import { describe, expect, test } from "bun:test"
import { createRoot } from "solid-js"
import { createRefCountMap } from "./refcount"
import { pathKey } from "./path-key"
describe("createRefCountMap", () => {
test("removes an item after its last owner is disposed", () => {
const removed: string[] = []
const map = createRefCountMap(
(key) => key,
(key) => removed.push(key),
)
const first = createRoot((dispose) => {
map("/project")
return dispose
})
const second = createRoot((dispose) => {
map("/project")
return dispose
})
first()
expect(removed).toEqual([])
second()
expect(removed).toEqual(["/project"])
})
test("keeps equivalent path consumers until the last owner is disposed", () => {
const removed: string[] = []
const map = createRefCountMap(
(key) => key,
(key) => removed.push(key),
pathKey,
)
const first = createRoot((dispose) => {
map("C:\\repo")
return dispose
})
const second = createRoot((dispose) => {
map("C:/repo/")
return dispose
})
first()
expect(removed).toEqual([])
second()
expect(removed).toEqual(["C:/repo"])
})
})
+15 -9
View File
@@ -1,26 +1,32 @@
import { onCleanup } from "solid-js"
export function createRefCountMap<T>(create: (key: string) => T) {
export function createRefCountMap<T>(
create: (key: string) => T,
remove?: (key: string) => void,
identity: (key: string) => string = (key) => key,
) {
const items = new Map<string, T>()
const refCounts = new Map<string, number>()
return (key: string) => {
const id = identity(key)
onCleanup(() => {
refCounts.set(key, (refCounts.get(key) ?? 0) - 1)
if (refCounts.get(key) === 0) {
items.delete(key)
refCounts.delete(key)
refCounts.set(id, (refCounts.get(id) ?? 0) - 1)
if (refCounts.get(id) === 0) {
remove?.(id)
items.delete(id)
refCounts.delete(id)
}
})
const cached = items.get(key)
const cached = items.get(id)
if (cached) {
refCounts.set(key, (refCounts.get(key) ?? 0) + 1)
refCounts.set(id, (refCounts.get(id) ?? 0) + 1)
return cached
}
const item = create(key)
items.set(key, item)
refCounts.set(key, 1)
items.set(id, item)
refCounts.set(id, 1)
return item
}
}
+34
View File
@@ -0,0 +1,34 @@
import { Icon, type IconProps } from "@opencode-ai/ui/icon"
import { Toast, showToast as showLegacyToast, type ToastOptions, type ToastVariant } from "@opencode-ai/ui/toast"
import { ToastV2, showToastV2 } from "@opencode-ai/ui/v2/toast-v2"
let v2 = false
export function setV2Toast(value: boolean) {
v2 = value
}
export function ToastRegion(props: { v2: boolean }) {
if (props.v2) return <ToastV2.Region />
return <Toast.Region />
}
export function showToast(options: ToastOptions | string) {
if (!v2) return showLegacyToast(options)
if (typeof options === "string") return showToastV2(options)
return showToastV2({
...options,
icon: resolveIcon(options.icon, options.variant),
actions: options.actions?.map((action) => ({
...action,
variant: action.onClick === "dismiss" ? "secondary" : "primary",
})),
})
}
function resolveIcon(icon: IconProps["name"] | undefined, variant: ToastVariant | undefined) {
const name = icon ?? (variant === "success" ? "check" : undefined)
if (!name) return
return <Icon name={name} />
}
+26
View File
@@ -0,0 +1,26 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode-ai/cli",
"version": "1.15.13",
"private": true,
"type": "module",
"license": "MIT",
"bin": {
"opencode": "./src/index.ts"
},
"scripts": {
"build": "bun run script/build.ts",
"dev": "bun run src/index.ts",
"typecheck": "tsgo --noEmit"
},
"dependencies": {
"@effect/platform-node": "catalog:",
"@opencode-ai/core": "workspace:*",
"effect": "catalog:"
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@typescript/native-preview": "catalog:"
}
}
+31
View File
@@ -0,0 +1,31 @@
import { EOL } from "os"
import { AgentV2 } from "@opencode-ai/core/agent"
import { PluginBoot } from "@opencode-ai/core/plugin/boot"
import * as Effect from "effect/Effect"
import * as Command from "effect/unstable/cli/Command"
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
import { AbsolutePath } from "@opencode-ai/core/schema"
export const AgentsCommand = Command.make("agents", {}, () =>
Effect.gen(function* () {
const svc = {
plugin: yield* PluginBoot.Service,
agent: yield* AgentV2.Service,
}
yield* svc.plugin.wait()
const agents = yield* svc.agent.all()
process.stdout.write(
JSON.stringify(
agents.sort((a, b) => a.id.localeCompare(b.id)),
null,
2,
) + EOL,
)
}).pipe(
Effect.provide(
LocationServiceMap.get({
directory: AbsolutePath.make(process.cwd()),
}),
),
),
).pipe(Command.withDescription("List all agents"))
+7
View File
@@ -0,0 +1,7 @@
import * as Command from "effect/unstable/cli/Command"
import { AgentsCommand } from "./agents"
export const DebugCommand = Command.make("debug").pipe(
Command.withDescription("Debugging and troubleshooting tools"),
Command.withSubcommands([AgentsCommand]),
)
+18
View File
@@ -0,0 +1,18 @@
#!/usr/bin/env bun
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
import * as NodeServices from "@effect/platform-node/NodeServices"
import * as Effect from "effect/Effect"
import * as Layer from "effect/Layer"
import * as Command from "effect/unstable/cli/Command"
import { DebugCommand } from "./debug"
import { LocationServiceMap } from "@opencode-ai/core/location-layer"
const cli = Command.make("opencode", {}, () => Effect.void).pipe(
Command.withDescription("OpenCode command line interface"),
Command.withSubcommands([DebugCommand]),
)
const layer = Layer.mergeAll(LocationServiceMap.layer, NodeServices.layer)
Command.run(cli, { version: "local" }).pipe(Effect.provide(layer), Effect.scoped, NodeRuntime.runMain)
+10
View File
@@ -0,0 +1,10 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/* biome-ignore-all lint: auto-generated */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+7
View File
@@ -0,0 +1,7 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"noUncheckedIndexedAccess": false
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode-ai/console-app",
"version": "1.15.11",
"version": "1.15.13",
"type": "module",
"license": "MIT",
"scripts": {
@@ -2,7 +2,6 @@ import { action, json, query, useAction, useSubmission } from "@solidjs/router"
import { createEffect, createMemo, createSignal, For, onCleanup, Show } from "solid-js"
import { getRequestEvent } from "solid-js/web"
import { Referral } from "@opencode-ai/console-core/referral.js"
import { Actor } from "@opencode-ai/console-core/actor.js"
import { withActor } from "~/context/auth.withActor"
import { Modal } from "~/component/modal"
import { IconCheck, IconCopy } from "~/component/icon"
@@ -10,7 +9,7 @@ import { useI18n } from "~/context/i18n"
import { useLanguage } from "~/context/language"
import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time"
import { queryLiteSubscription } from "~/routes/workspace/[id]/go/lite-section"
import { clearReferralCookie, referralCodeFromCookieHeader } from "~/lib/referral-invite"
import { createReferralFromCookie } from "~/lib/referral-invite"
import "./go-referral.css"
type GoReferralSummary = Awaited<ReturnType<typeof Referral.summary>>
@@ -28,18 +27,7 @@ const emptyUsagePreview = {
export const queryGoReferral = query(async (workspaceID: string) => {
"use server"
return withActor(async () => {
const event = getRequestEvent()
const referralCode = referralCodeFromCookieHeader(event?.request.headers.get("cookie") ?? null)
if (referralCode) {
await Referral.createFromAccount({
accountID: Actor.account(),
referralCode,
}).catch((error) => {
console.error("Referral create failed", error)
})
event?.response.headers.append("set-cookie", clearReferralCookie())
}
await createReferralFromCookie()
return Referral.summary()
}, workspaceID)
}, "go.referral.get")
+4 -4
View File
@@ -249,7 +249,7 @@ export const dict = {
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
"go.meta.description":
"يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وQwen3.5 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وMiniMax M3 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
"go.hero.body":
"يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.",
@@ -298,7 +298,7 @@ export const dict = {
"go.problem.item2": "حدود سخية ووصول موثوق",
"go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين",
"go.problem.item4":
"يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وQwen3.5 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash",
"يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وMiniMax M3 وDeepSeek V4 Pro وDeepSeek V4 Flash",
"go.how.title": "كيف يعمل Go",
"go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
"go.how.step1.title": "أنشئ حسابًا",
@@ -322,7 +322,7 @@ export const dict = {
"go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.",
"go.faq.q3": "هل Go هو نفسه Zen؟",
"go.faq.a3":
"لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وQwen3.5 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وMiniMax M3 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"go.faq.q4": "كم تكلفة Go؟",
"go.faq.a4.p1.beforePricing": "تكلفة Go",
"go.faq.a4.p1.pricingLink": "$5 للشهر الأول",
@@ -345,7 +345,7 @@ export const dict = {
"go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟",
"go.faq.a9":
"تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وQwen3.5 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).",
"تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.7 Max وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وMiniMax M3 وDeepSeek V4 Pro وDeepSeek V4 Flash مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).",
"zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.",
"zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم",
+4 -4
View File
@@ -253,7 +253,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
"go.meta.description":
"O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de solicitação de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"O Go começa em $5 no primeiro mês, depois $10/mês, com limites generosos de solicitação de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"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.",
@@ -303,7 +303,7 @@ export const dict = {
"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":
"Inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash",
"Inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash",
"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.",
@@ -329,7 +329,7 @@ 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 aos modelos open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"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 aos modelos open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"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",
@@ -353,7 +353,7 @@ export const dict = {
"go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?",
"go.faq.a9":
"Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a $12 por 5 horas, $30 por semana e $60 por mês (as contagens reais de requisições variam de acordo com o modelo e o uso).",
"Os modelos gratuitos incluem Big Pickle e modelos promocionais disponíveis no momento, com uma cota de 200 requisições/dia. O Go inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash com cotas de requisição mais altas aplicadas em janelas móveis (5 horas, semanal e mensal), aproximadamente equivalentes a $12 por 5 horas, $30 por semana e $60 por mês (as contagens reais de requisições variam de acordo com o modelo e o uso).",
"zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.",
"zen.api.error.modelNotSupported": "Modelo {{model}} não suportado",
+4 -4
View File
@@ -251,7 +251,7 @@ export const dict = {
"go.title": "OpenCode Go | Kodningsmodeller til lav pris for alle",
"go.meta.description":
"Go starter ved $5 for den første måned, derefter $10/måned, med generøse 5-timers anmodningsgrænser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"Go starter ved $5 for den første måned, derefter $10/måned, med generøse 5-timers anmodningsgrænser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"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.",
@@ -300,7 +300,7 @@ export const dict = {
"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":
"Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash",
"Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash",
"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.",
@@ -326,7 +326,7 @@ 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 open source-modellerne GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"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 open source-modellerne GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"go.faq.q4": "Hvad koster Go?",
"go.faq.a4.p1.beforePricing": "Go koster",
"go.faq.a4.p1.pricingLink": "$5 første måned",
@@ -349,7 +349,7 @@ export const dict = {
"go.faq.q9": "Hvad er forskellen på gratis modeller og Go?",
"go.faq.a9":
"Gratis modeller inkluderer Big Pickle plus salgsfremmende modeller tilgængelige på det tidspunkt, med en kvote på 200 forespørgsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash med højere anmodningskvoter håndhævet over rullende vinduer (5-timers, ugentlig og månedlig), nogenlunde svarende til $12 pr. 5 timer, $30 pr. uge og $60 pr. måned (faktiske anmodningstal varierer efter model og brug).",
"Gratis modeller inkluderer Big Pickle plus salgsfremmende modeller tilgængelige på det tidspunkt, med en kvote på 200 forespørgsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash med højere anmodningskvoter håndhævet over rullende vinduer (5-timers, ugentlig og månedlig), nogenlunde svarende til $12 pr. 5 timer, $30 pr. uge og $60 pr. måned (faktiske anmodningstal varierer efter model og brug).",
"zen.api.error.rateLimitExceeded": "Hastighedsgrænse overskredet. Prøv venligst igen senere.",
"zen.api.error.modelNotSupported": "Model {{model}} understøttes ikke",
+4 -4
View File
@@ -253,7 +253,7 @@ export const dict = {
"go.title": "OpenCode Go | Kostengünstige Coding-Modelle für alle",
"go.meta.description":
"Go beginnt bei $5 für den ersten Monat, danach $10/Monat, mit großzügigen 5-Stunden-Anfragelimits für GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash.",
"Go beginnt bei $5 für den ersten Monat, danach $10/Monat, mit großzügigen 5-Stunden-Anfragelimits für GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro und DeepSeek V4 Flash.",
"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.",
@@ -302,7 +302,7 @@ export const dict = {
"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":
"Beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash",
"Beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro und DeepSeek V4 Flash",
"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.",
@@ -328,7 +328,7 @@ 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 den ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu den Open-Source-Modellen GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash.",
"Nein. Zen ist Pay-as-you-go, während Go bei $5 für den ersten Monat beginnt, danach $10/Monat, mit großzügigen Limits und zuverlässigem Zugang zu den Open-Source-Modellen GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro und DeepSeek V4 Flash.",
"go.faq.q4": "Wie viel kostet Go?",
"go.faq.a4.p1.beforePricing": "Go kostet",
"go.faq.a4.p1.pricingLink": "$5 im ersten Monat",
@@ -352,7 +352,7 @@ export const dict = {
"go.faq.q9": "Was ist der Unterschied zwischen kostenlosen Modellen und Go?",
"go.faq.a9":
"Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro und DeepSeek V4 Flash mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).",
"Kostenlose Modelle beinhalten Big Pickle sowie Werbemodelle, die zum jeweiligen Zeitpunkt verfügbar sind, mit einem Kontingent von 200 Anfragen/Tag. Go beinhaltet GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro und DeepSeek V4 Flash mit höheren Anfragekontingenten, die über rollierende Zeitfenster (5 Stunden, wöchentlich und monatlich) durchgesetzt werden, grob äquivalent zu $12 pro 5 Stunden, $30 pro Woche und $60 pro Monat (tatsächliche Anfragezahlen variieren je nach Modell und Nutzung).",
"zen.api.error.rateLimitExceeded": "Ratenlimit überschritten. Bitte versuche es später erneut.",
"zen.api.error.modelNotSupported": "Modell {{model}} wird nicht unterstützt",
+4 -4
View File
@@ -248,7 +248,7 @@ export const dict = {
"go.title": "OpenCode Go | Low cost coding models for everyone",
"go.meta.description":
"Go starts at $5 for your first month, then $10/month, with generous 5-hour request limits for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
"Go starts at $5 for your first month, then $10/month, with generous 5-hour request limits for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
"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.",
@@ -296,7 +296,7 @@ export const dict = {
"go.problem.item2": "Generous limits and reliable access",
"go.problem.item3": "Built for as many programmers as possible",
"go.problem.item4":
"Includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash",
"Includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, and DeepSeek V4 Flash",
"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.step1.title": "Create an account",
@@ -321,7 +321,7 @@ 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 open-source models GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
"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 open-source models GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, and DeepSeek V4 Flash.",
"go.faq.q4": "How much does Go cost?",
"go.faq.a4.p1.beforePricing": "Go costs",
"go.faq.a4.p1.pricingLink": "$5 first month",
@@ -345,7 +345,7 @@ export const dict = {
"go.faq.q9": "What is the difference between free models and Go?",
"go.faq.a9":
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
"Free models include Big Pickle plus promotional models available at the time, with a quota of 200 requests/day. Go includes GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, and DeepSeek V4 Flash with higher request quotas enforced across rolling windows (5-hour, weekly, and monthly), roughly equivalent to $12 per 5 hours, $30 per week, and $60 per month (actual request counts vary by model and usage).",
"zen.api.error.rateLimitExceeded": "Rate limit exceeded. Please try again later.",
"zen.api.error.modelNotSupported": "Model {{model}} is not supported",
+4 -4
View File
@@ -254,7 +254,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelos de programación de bajo coste para todos",
"go.meta.description":
"Go comienza en $5 el primer mes, luego 10 $/mes, con generosos límites de solicitudes de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash.",
"Go comienza en $5 el primer mes, luego 10 $/mes, con generosos límites de solicitudes de 5 horas para GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro y DeepSeek V4 Flash.",
"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.",
@@ -304,7 +304,7 @@ export const dict = {
"go.problem.item2": "Límites generosos y acceso fiable",
"go.problem.item3": "Creado para tantos programadores como sea posible",
"go.problem.item4":
"Incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash",
"Incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro y DeepSeek V4 Flash",
"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.step1.title": "Crear una cuenta",
@@ -329,7 +329,7 @@ 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 pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a los modelos de código abierto GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash.",
"No. Zen es pago por uso, mientras que Go comienza en $5 el primer mes, luego 10 $/mes, con límites generosos y acceso fiable a los modelos de código abierto GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro y DeepSeek V4 Flash.",
"go.faq.q4": "¿Cuánto cuesta Go?",
"go.faq.a4.p1.beforePricing": "Go cuesta",
"go.faq.a4.p1.pricingLink": "$5 el primer mes",
@@ -353,7 +353,7 @@ export const dict = {
"go.faq.q9": "¿Cuál es la diferencia entre los modelos gratuitos y Go?",
"go.faq.a9":
"Los modelos gratuitos incluyen Big Pickle más modelos promocionales disponibles en el momento, con una cuota de 200 solicitudes/día. Go incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro y DeepSeek V4 Flash con cuotas de solicitud más altas aplicadas a través de ventanas móviles (5 horas, semanal y mensual), aproximadamente equivalente a 12 $ por 5 horas, 30 $ por semana y 60 $ por mes (los recuentos reales de solicitudes varían según el modelo y el uso).",
"Los modelos gratuitos incluyen Big Pickle más modelos promocionales disponibles en el momento, con una cuota de 200 solicitudes/día. Go incluye GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro y DeepSeek V4 Flash con cuotas de solicitud más altas aplicadas a través de ventanas móviles (5 horas, semanal y mensual), aproximadamente equivalente a 12 $ por 5 horas, 30 $ por semana y 60 $ por mes (los recuentos reales de solicitudes varían según el modelo y el uso).",
"zen.api.error.rateLimitExceeded": "Límite de tasa excedido. Por favor, inténtalo de nuevo más tarde.",
"zen.api.error.modelNotSupported": "Modelo {{model}} no soportado",
+4 -4
View File
@@ -255,7 +255,7 @@ export const dict = {
"go.title": "OpenCode Go | Modèles de code à faible coût pour tous",
"go.meta.description":
"Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites de requêtes généreuses sur 5 heures pour GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash.",
"Go commence à $5 pour le premier mois, puis 10 $/mois, avec des limites de requêtes généreuses sur 5 heures pour GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro et DeepSeek V4 Flash.",
"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é.",
@@ -304,7 +304,7 @@ export const dict = {
"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":
"Inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash",
"Inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro et DeepSeek V4 Flash",
"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.",
@@ -330,7 +330,7 @@ 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 aux modèles open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash.",
"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 aux modèles open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro et DeepSeek V4 Flash.",
"go.faq.q4": "Combien coûte Go ?",
"go.faq.a4.p1.beforePricing": "Go coûte",
"go.faq.a4.p1.pricingLink": "$5 le premier mois",
@@ -353,7 +353,7 @@ export const dict = {
"Oui, vous pouvez utiliser Go avec n'importe quel agent. Suivez les instructions de configuration dans votre agent de code préféré.",
"go.faq.q9": "Quelle est la différence entre les modèles gratuits et Go ?",
"go.faq.a9":
"Les modèles gratuits incluent Big Pickle ainsi que des modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro et DeepSeek V4 Flash avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalent à 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois (le nombre réel de requêtes varie selon le modèle et l'utilisation).",
"Les modèles gratuits incluent Big Pickle ainsi que des modèles promotionnels disponibles à ce moment-là, avec un quota de 200 requêtes/jour. Go inclut GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro et DeepSeek V4 Flash avec des quotas de requêtes plus élevés appliqués sur des fenêtres glissantes (5 heures, hebdomadaire et mensuelle), à peu près équivalent à 12 $ par 5 heures, 30 $ par semaine et 60 $ par mois (le nombre réel de requêtes varie selon le modèle et l'utilisation).",
"zen.api.error.rateLimitExceeded": "Limite de débit dépassée. Veuillez réessayer plus tard.",
"zen.api.error.modelNotSupported": "Modèle {{model}} non pris en charge",
+4 -4
View File
@@ -251,7 +251,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelli di coding a basso costo per tutti",
"go.meta.description":
"Go inizia a $5 per il primo mese, poi $10/mese, con generosi limiti di richiesta di 5 ore per GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"Go inizia a $5 per il primo mese, poi $10/mese, con generosi limiti di richiesta di 5 ore per GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"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à.",
@@ -300,7 +300,7 @@ export const dict = {
"go.problem.item2": "Limiti generosi e accesso affidabile",
"go.problem.item3": "Costruito per il maggior numero possibile di programmatori",
"go.problem.item4":
"Include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash",
"Include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash",
"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.step1.title": "Crea un account",
@@ -325,7 +325,7 @@ 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 accesso affidabile ai modelli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"No. Zen è a consumo, mentre Go inizia a $5 per il primo mese, poi $10/mese, con limiti generosi e accesso affidabile ai modelli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"go.faq.q4": "Quanto costa Go?",
"go.faq.a4.p1.beforePricing": "Go costa",
"go.faq.a4.p1.pricingLink": "$5 il primo mese",
@@ -349,7 +349,7 @@ export const dict = {
"go.faq.q9": "Qual è la differenza tra i modelli gratuiti e Go?",
"go.faq.a9":
"I modelli gratuiti includono Big Pickle più modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a $12 ogni 5 ore, $30 a settimana e $60 al mese (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).",
"I modelli gratuiti includono Big Pickle più modelli promozionali disponibili al momento, con una quota di 200 richieste/giorno. Go include GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro e DeepSeek V4 Flash con quote di richiesta più elevate applicate su finestre mobili (5 ore, settimanale e mensile), approssimativamente equivalenti a $12 ogni 5 ore, $30 a settimana e $60 al mese (il conteggio effettivo delle richieste varia in base al modello e all'utilizzo).",
"zen.api.error.rateLimitExceeded": "Limite di richieste superato. Riprova più tardi.",
"zen.api.error.modelNotSupported": "Modello {{model}} non supportato",
+4 -4
View File
@@ -250,7 +250,7 @@ export const dict = {
"go.title": "OpenCode Go | すべての人のための低価格なコーディングモデル",
"go.meta.description":
"Goは最初の月$5、その後$10/月で、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashに対して5時間のゆとりあるリクエスト上限があります。",
"Goは最初の月$5、その後$10/月で、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro、DeepSeek V4 Flashに対して5時間のゆとりあるリクエスト上限があります。",
"go.hero.title": "すべての人のための低価格なコーディングモデル",
"go.hero.body":
"Goは、世界中のプログラマーにエージェント型コーディングをもたらします。最も高性能なオープンソースモデルへの十分な制限と安定したアクセスを提供し、コストや可用性を気にすることなく強力なエージェントで構築できます。",
@@ -300,7 +300,7 @@ export const dict = {
"go.problem.item2": "十分な制限と安定したアクセス",
"go.problem.item3": "できるだけ多くのプログラマーのために構築",
"go.problem.item4":
"GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashを含む",
"GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro、DeepSeek V4 Flashを含む",
"go.how.title": "Goの仕組み",
"go.how.body": "Goは最初の月$5、その後$10/月で始まります。OpenCodeまたは任意のエージェントで使えます。",
"go.how.step1.title": "アカウントを作成",
@@ -325,7 +325,7 @@ export const dict = {
"go.faq.a2": "Go には、十分な利用上限と安定したアクセスを備えた、以下のモデルが含まれます。",
"go.faq.q3": "GoはZenと同じですか?",
"go.faq.a3":
"いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で始まり、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashのオープンソースモデルに対して、ゆとりある上限と信頼できるアクセスを提供します。",
"いいえ。Zenは従量課金制ですが、Goは最初の月$5、その後$10/月で始まり、GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro、DeepSeek V4 Flashのオープンソースモデルに対して、ゆとりある上限と信頼できるアクセスを提供します。",
"go.faq.q4": "Goの料金は?",
"go.faq.a4.p1.beforePricing": "Goは",
"go.faq.a4.p1.pricingLink": "最初の月$5",
@@ -349,7 +349,7 @@ export const dict = {
"go.faq.q9": "無料モデルとGoの違いは何ですか?",
"go.faq.a9":
"無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。GoにはGLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro、DeepSeek V4 Flashが含まれ、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。これは概算で5時間あたり$12、週間$30、月間$60相当です(実際のリクエスト数はモデルと使用状況により異なります)。",
"無料モデルにはBig Pickleと、その時点で利用可能なプロモーションモデルが含まれ、1日200リクエストの制限があります。GoにはGLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro、DeepSeek V4 Flashが含まれ、ローリングウィンドウ(5時間、週間、月間)全体でより高いリクエスト制限が適用されます。これは概算で5時間あたり$12、週間$30、月間$60相当です(実際のリクエスト数はモデルと使用状況により異なります)。",
"zen.api.error.rateLimitExceeded": "レート制限を超えました。後でもう一度お試しください。",
"zen.api.error.modelNotSupported": "モデル {{model}} はサポートされていません",
+4 -4
View File
@@ -247,7 +247,7 @@ export const dict = {
"go.title": "OpenCode Go | 모두를 위한 저비용 코딩 모델",
"go.meta.description":
"Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash에 대해 넉넉한 5시간 요청 한도를 제공합니다.",
"Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, DeepSeek V4 Flash에 대해 넉넉한 5시간 요청 한도를 제공합니다.",
"go.hero.title": "모두를 위한 저비용 코딩 모델",
"go.hero.body":
"Go는 전 세계 프로그래머들에게 에이전트 코딩을 제공합니다. 가장 유능한 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공하므로, 비용이나 가용성 걱정 없이 강력한 에이전트로 빌드할 수 있습니다.",
@@ -297,7 +297,7 @@ export const dict = {
"go.problem.item2": "넉넉한 한도와 안정적인 액세스",
"go.problem.item3": "가능한 한 많은 프로그래머를 위해 제작됨",
"go.problem.item4":
"GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash 포함",
"GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, DeepSeek V4 Flash 포함",
"go.how.title": "Go 작동 방식",
"go.how.body": "Go는 첫 달 $5, 이후 $10/월로 시작합니다. OpenCode 또는 어떤 에이전트와도 함께 사용할 수 있습니다.",
"go.how.step1.title": "계정 생성",
@@ -321,7 +321,7 @@ export const dict = {
"go.faq.a2": "Go에는 넉넉한 한도와 안정적인 액세스를 제공하는 아래 모델이 포함됩니다.",
"go.faq.q3": "Go는 Zen과 같은가요?",
"go.faq.a3":
"아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
"아니요. Zen은 종량제인 반면, Go는 첫 달 $5, 이후 $10/월로 시작하며, GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, DeepSeek V4 Flash 오픈 소스 모델에 대한 넉넉한 한도와 안정적인 액세스를 제공합니다.",
"go.faq.q4": "Go 비용은 얼마인가요?",
"go.faq.a4.p1.beforePricing": "Go 비용은",
"go.faq.a4.p1.pricingLink": "첫 달 $5",
@@ -344,7 +344,7 @@ export const dict = {
"go.faq.q9": "무료 모델과 Go의 차이점은 무엇인가요?",
"go.faq.a9":
"무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro, DeepSeek V4 Flash를 포함하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 이는 대략 5시간당 $12, 주당 $30, 월 $60에 해당합니다(실제 요청 수는 모델 및 사용량에 따라 다름).",
"무료 모델에는 Big Pickle과 당시 사용 가능한 프로모션 모델이 포함되며, 하루 200회 요청 할당량이 적용됩니다. Go는 GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro, DeepSeek V4 Flash를 포함하며, 롤링 윈도우(5시간, 주간, 월간)에 걸쳐 더 높은 요청 할당량을 적용합니다. 이는 대략 5시간당 $12, 주당 $30, 월 $60에 해당합니다(실제 요청 수는 모델 및 사용량에 따라 다름).",
"zen.api.error.rateLimitExceeded": "속도 제한을 초과했습니다. 나중에 다시 시도해 주세요.",
"zen.api.error.modelNotSupported": "{{model}} 모델은 지원되지 않습니다",
+4 -4
View File
@@ -251,7 +251,7 @@ export const dict = {
"go.title": "OpenCode Go | Rimelige kodemodeller for alle",
"go.meta.description":
"Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse 5-timers forespørselsgrenser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"Go starter på $5 for den første måneden, deretter $10/måned, med sjenerøse 5-timers forespørselsgrenser for GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"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.",
@@ -300,7 +300,7 @@ export const dict = {
"go.problem.item2": "Rause grenser og pålitelig tilgang",
"go.problem.item3": "Bygget for så mange programmerere som mulig",
"go.problem.item4":
"Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash",
"Inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash",
"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.",
@@ -326,7 +326,7 @@ 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 åpen kildekode-modellene GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"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 åpen kildekode-modellene GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash.",
"go.faq.q4": "Hva koster Go?",
"go.faq.a4.p1.beforePricing": "Go koster",
"go.faq.a4.p1.pricingLink": "$5 første måned",
@@ -350,7 +350,7 @@ export const dict = {
"go.faq.q9": "Hva er forskjellen mellom gratis modeller og Go?",
"go.faq.a9":
"Gratis modeller inkluderer Big Pickle pluss kampanjemodeller tilgjengelig på det tidspunktet, med en kvote på 200 forespørsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro og DeepSeek V4 Flash med høyere kvoter håndhevet over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende $12 per 5 timer, $30 per uke og $60 per måned (faktiske forespørselsantall varierer etter modell og bruk).",
"Gratis modeller inkluderer Big Pickle pluss kampanjemodeller tilgjengelig på det tidspunktet, med en kvote på 200 forespørsler/dag. Go inkluderer GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro og DeepSeek V4 Flash med høyere kvoter håndhevet over rullerende vinduer (5 timer, ukentlig og månedlig), omtrent tilsvarende $12 per 5 timer, $30 per uke og $60 per måned (faktiske forespørselsantall varierer etter modell og bruk).",
"zen.api.error.rateLimitExceeded": "Rate limit overskredet. Vennligst prøv igjen senere.",
"zen.api.error.modelNotSupported": "Modell {{model}} støttes ikke",
+4 -4
View File
@@ -252,7 +252,7 @@ export const dict = {
"go.title": "OpenCode Go | Niskokosztowe modele do kodowania dla każdego",
"go.meta.description":
"Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi 5-godzinnymi limitami zapytań dla GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash.",
"Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi 5-godzinnymi limitami zapytań dla GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro i DeepSeek V4 Flash.",
"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ść.",
@@ -301,7 +301,7 @@ export const dict = {
"go.problem.item2": "Hojne limity i niezawodny dostęp",
"go.problem.item3": "Stworzony dla jak największej liczby programistów",
"go.problem.item4":
"Zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash",
"Zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro i DeepSeek V4 Flash",
"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.",
@@ -327,7 +327,7 @@ 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 to model płatności za użycie, podczas gdy Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi limitami i niezawodnym dostępem do modeli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash.",
"Nie. Zen to model płatności za użycie, podczas gdy Go zaczyna się od $5 za pierwszy miesiąc, potem $10/miesiąc, z hojnymi limitami i niezawodnym dostępem do modeli open source GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro i DeepSeek V4 Flash.",
"go.faq.q4": "Ile kosztuje Go?",
"go.faq.a4.p1.beforePricing": "Go kosztuje",
"go.faq.a4.p1.pricingLink": "$5 za pierwszy miesiąc",
@@ -351,7 +351,7 @@ export const dict = {
"go.faq.q9": "Jaka jest różnica między darmowymi modelami a Go?",
"go.faq.a9":
"Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro i DeepSeek V4 Flash z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), w przybliżeniu równoważnymi $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie (rzeczywista liczba zapytań zależy od modelu i użycia).",
"Darmowe modele obejmują Big Pickle oraz modele promocyjne dostępne w danym momencie, z limitem 200 zapytań/dzień. Go zawiera GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro i DeepSeek V4 Flash z wyższymi limitami zapytań egzekwowanymi w oknach kroczących (5-godzinnych, tygodniowych i miesięcznych), w przybliżeniu równoważnymi $12 na 5 godzin, $30 tygodniowo i $60 miesięcznie (rzeczywista liczba zapytań zależy od modelu i użycia).",
"zen.api.error.rateLimitExceeded": "Przekroczono limit zapytań. Spróbuj ponownie później.",
"zen.api.error.modelNotSupported": "Model {{model}} nie jest obsługiwany",
+4 -4
View File
@@ -255,7 +255,7 @@ export const dict = {
"go.title": "OpenCode Go | Недорогие модели для кодинга для всех",
"go.meta.description":
"Go начинается с $5 за первый месяц, затем $10/месяц, с щедрыми лимитами запросов за 5 часов для GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro и DeepSeek V4 Flash.",
"Go начинается с $5 за первый месяц, затем $10/месяц, с щедрыми лимитами запросов за 5 часов для GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro и DeepSeek V4 Flash.",
"go.hero.title": "Недорогие модели для кодинга для всех",
"go.hero.body":
"Go открывает доступ к агентам-программистам разработчикам по всему миру. Предлагая щедрые лимиты и надежный доступ к наиболее способным моделям с открытым исходным кодом, вы можете создавать проекты с мощными агентами, не беспокоясь о затратах или доступности.",
@@ -305,7 +305,7 @@ export const dict = {
"go.problem.item2": "Щедрые лимиты и надежный доступ",
"go.problem.item3": "Создан для максимального числа программистов",
"go.problem.item4":
"Включает GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro и DeepSeek V4 Flash",
"Включает GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro и DeepSeek V4 Flash",
"go.how.title": "Как работает Go",
"go.how.body":
"Go начинается с $5 за первый месяц, затем $10/месяц. Вы можете использовать его с OpenCode или любым агентом.",
@@ -331,7 +331,7 @@ export const dict = {
"go.faq.a2": "Go включает перечисленные ниже модели с щедрыми лимитами и надежным доступом.",
"go.faq.q3": "Go — это то же самое, что и Zen?",
"go.faq.a3":
"Нет. Zen - это оплата по мере использования, в то время как Go начинается с $5 за первый месяц, затем $10/месяц, с щедрыми лимитами и надежным доступом к моделям с открытым исходным кодом GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro и DeepSeek V4 Flash.",
"Нет. Zen - это оплата по мере использования, в то время как Go начинается с $5 за первый месяц, затем $10/месяц, с щедрыми лимитами и надежным доступом к моделям с открытым исходным кодом GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro и DeepSeek V4 Flash.",
"go.faq.q4": "Сколько стоит Go?",
"go.faq.a4.p1.beforePricing": "Go стоит",
"go.faq.a4.p1.pricingLink": "$5 за первый месяц",
@@ -355,7 +355,7 @@ export const dict = {
"go.faq.q9": "В чем разница между бесплатными моделями и Go?",
"go.faq.a9":
"Бесплатные модели включают Big Pickle плюс промо-модели, доступные на данный момент, с квотой 200 запросов/день. Go включает GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro и DeepSeek V4 Flash с более высокими квотами запросов, применяемыми в скользящих окнах (5 часов, неделя и месяц), что примерно эквивалентно $12 за 5 часов, $30 в неделю и $60 в месяц (фактическое количество запросов зависит от модели и использования).",
"Бесплатные модели включают Big Pickle плюс промо-модели, доступные на данный момент, с квотой 200 запросов/день. Go включает GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro и DeepSeek V4 Flash с более высокими квотами запросов, применяемыми в скользящих окнах (5 часов, неделя и месяц), что примерно эквивалентно $12 за 5 часов, $30 в неделю и $60 в месяц (фактическое количество запросов зависит от модели и использования).",
"zen.api.error.rateLimitExceeded": "Превышен лимит запросов. Пожалуйста, попробуйте позже.",
"zen.api.error.modelNotSupported": "Модель {{model}} не поддерживается",
+4 -4
View File
@@ -250,7 +250,7 @@ export const dict = {
"go.title": "OpenCode Go | โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
"go.meta.description":
"Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดคำขอ 5 ชั่วโมงที่เอื้อเฟื้อสำหรับ GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro และ DeepSeek V4 Flash",
"Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดคำขอ 5 ชั่วโมงที่เอื้อเฟื้อสำหรับ GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro และ DeepSeek V4 Flash",
"go.hero.title": "โมเดลเขียนโค้ดราคาประหยัดสำหรับทุกคน",
"go.hero.body":
"Go นำการเขียนโค้ดแบบเอเจนต์มาสู่นักเขียนโปรแกรมทั่วโลก เสนอขีดจำกัดที่กว้างขวางและการเข้าถึงโมเดลโอเพนซอร์สที่มีความสามารถสูงสุดได้อย่างน่าเชื่อถือ เพื่อให้คุณสามารถสร้างสรรค์ด้วยเอเจนต์ที่ทรงพลังโดยไม่ต้องกังวลเรื่องค่าใช้จ่ายหรือความพร้อมใช้งาน",
@@ -298,7 +298,7 @@ export const dict = {
"go.problem.item2": "ขีดจำกัดที่กว้างขวางและการเข้าถึงที่เชื่อถือได้",
"go.problem.item3": "สร้างขึ้นเพื่อโปรแกรมเมอร์จำนวนมากที่สุดเท่าที่จะเป็นไปได้",
"go.problem.item4":
"รวมถึง GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro และ DeepSeek V4 Flash",
"รวมถึง GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro และ DeepSeek V4 Flash",
"go.how.title": "Go ทำงานอย่างไร",
"go.how.body": "Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน คุณสามารถใช้กับ OpenCode หรือเอเจนต์ใดก็ได้",
"go.how.step1.title": "สร้างบัญชี",
@@ -323,7 +323,7 @@ export const dict = {
"go.faq.a2": "Go รวมโมเดลด้านล่างนี้ พร้อมขีดจำกัดที่มากและการเข้าถึงที่เชื่อถือได้",
"go.faq.q3": "Go เหมือนกับ Zen หรือไม่?",
"go.faq.a3":
"ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ในขณะที่ Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์ส GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro และ DeepSeek V4 Flash อย่างเชื่อถือได้",
"ไม่ Zen เป็นแบบจ่ายตามการใช้งาน ในขณะที่ Go เริ่มต้นที่ $5 สำหรับเดือนแรก จากนั้น $10/เดือน พร้อมขีดจำกัดที่เอื้อเฟื้อและการเข้าถึงโมเดลโอเพนซอร์ส GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro และ DeepSeek V4 Flash อย่างเชื่อถือได้",
"go.faq.q4": "Go ราคาเท่าไหร่?",
"go.faq.a4.p1.beforePricing": "Go ราคา",
"go.faq.a4.p1.pricingLink": "$5 เดือนแรก",
@@ -346,7 +346,7 @@ export const dict = {
"go.faq.q9": "ความแตกต่างระหว่างโมเดลฟรีและ Go คืออะไร?",
"go.faq.a9":
"โมเดลฟรีรวมถึง Big Pickle บวกกับโมเดลโปรโมชั่นที่มีให้ในขณะนั้น ด้วยโควต้า 200 คำขอ/วัน Go รวมถึง GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro และ DeepSeek V4 Flash ที่มีโควต้าคำขอสูงกว่า ซึ่งบังคับใช้ผ่านช่วงเวลาหมุนเวียน (5 ชั่วโมง, รายสัปดาห์ และรายเดือน) เทียบเท่าประมาณ $12 ต่อ 5 ชั่วโมง, $30 ต่อสัปดาห์ และ $60 ต่อเดือน (จำนวนคำขอจริงจะแตกต่างกันไปตามโมเดลและการใช้งาน)",
"โมเดลฟรีรวมถึง Big Pickle บวกกับโมเดลโปรโมชั่นที่มีให้ในขณะนั้น ด้วยโควต้า 200 คำขอ/วัน Go รวมถึง GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro และ DeepSeek V4 Flash ที่มีโควต้าคำขอสูงกว่า ซึ่งบังคับใช้ผ่านช่วงเวลาหมุนเวียน (5 ชั่วโมง, รายสัปดาห์ และรายเดือน) เทียบเท่าประมาณ $12 ต่อ 5 ชั่วโมง, $30 ต่อสัปดาห์ และ $60 ต่อเดือน (จำนวนคำขอจริงจะแตกต่างกันไปตามโมเดลและการใช้งาน)",
"zen.api.error.rateLimitExceeded": "เกินขีดจำกัดอัตราการใช้งาน กรุณาลองใหม่ในภายหลัง",
"zen.api.error.modelNotSupported": "ไม่รองรับโมเดล {{model}}",
+4 -4
View File
@@ -253,7 +253,7 @@ export const dict = {
"go.title": "OpenCode Go | Herkes için düşük maliyetli kodlama modelleri",
"go.meta.description":
"Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro ve DeepSeek V4 Flash için cömert 5 saatlik istek limitleri sunar.",
"Go ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro ve DeepSeek V4 Flash için cömert 5 saatlik istek limitleri 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.",
@@ -303,7 +303,7 @@ export const dict = {
"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":
"GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro ve DeepSeek V4 Flash içerir",
"GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro ve DeepSeek V4 Flash içerir",
"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.",
@@ -329,7 +329,7 @@ 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; GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro ve DeepSeek V4 Flash açık kaynak modellerine cömert limitler ve güvenilir erişim sunar.",
"Hayır. Zen kullandıkça öde modelidir, Go ise ilk ay $5, sonrasında ayda 10$ fiyatıyla başlar; GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro ve DeepSeek V4 Flash açık kaynak modellerine cömert limitler ve 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",
@@ -353,7 +353,7 @@ export const dict = {
"go.faq.q9": "Ücretsiz modeller ve Go arasındaki fark nedir?",
"go.faq.a9":
"Ücretsiz modeller, günlük 200 istek kotası ile Big Pickle ve o sırada mevcut olan promosyonel modelleri içerir. Go ise GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro ve DeepSeek V4 Flash modellerini; yuvarlanan pencereler (5 saatlik, haftalık ve aylık) üzerinden uygulanan daha yüksek istek kotalarıyla içerir. Bu kotalar kabaca her 5 saatte 12$, haftada 30$ ve ayda 60$ değerine eşdeğerdir (gerçek istek sayıları modele ve kullanıma göre değişir).",
"Ücretsiz modeller, günlük 200 istek kotası ile Big Pickle ve o sırada mevcut olan promosyonel modelleri içerir. Go ise GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro ve DeepSeek V4 Flash modellerini; yuvarlanan pencereler (5 saatlik, haftalık ve aylık) üzerinden uygulanan daha yüksek istek kotalarıyla içerir. Bu kotalar kabaca her 5 saatte 12$, haftada 30$ ve ayda 60$ değerine eşdeğerdir (gerçek istek sayıları modele ve kullanıma göre değişir).",
"zen.api.error.rateLimitExceeded": "İstek limiti aşıldı. Lütfen daha sonra tekrar deneyin.",
"zen.api.error.modelNotSupported": "{{model}} modeli desteklenmiyor",
+4 -4
View File
@@ -252,7 +252,7 @@ export const dict = {
"go.title": "OpenCode Go | Недорогі моделі кодування для всіх",
"go.meta.description":
"Go починається від $5 за перший місяць, потім $10/місяць, з generous 5-годинними лімітами запитів для GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro та DeepSeek V4 Flash.",
"Go починається від $5 за перший місяць, потім $10/місяць, з generous 5-годинними лімітами запитів для GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro та DeepSeek V4 Flash.",
"go.hero.title": "Недорогі моделі кодування для всіх",
"go.hero.body":
"Go надає агентне програмування програмістам у всьому світі, пропонуючи щедрі ліміти та надійний доступ до найкращих моделей з відкритим кодом.",
@@ -301,7 +301,7 @@ export const dict = {
"go.problem.item2": "Щедрі ліміти та надійний доступ",
"go.problem.item3": "Створено для якомога більшої кількості програмістів",
"go.problem.item4":
"Включає GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro та DeepSeek V4 Flash",
"Включає GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro та DeepSeek V4 Flash",
"go.how.title": "Як працює Go",
"go.how.body":
"Go починається від $5 за перший місяць, потім $10/місяць. Використовуйте з OpenCode або будь-яким агентом.",
@@ -327,7 +327,7 @@ export const dict = {
"go.faq.a2": "Go включає моделі, перелічені нижче, із щедрими лімітами та надійним доступом.",
"go.faq.q3": "Чи Go те саме, що Zen?",
"go.faq.a3":
"Ні. Zen — це плата за використання, тоді як Go починається від $5 за перший місяць, потім $10/місяць, із щедрими лімітами та надійним доступом до моделей з відкритим кодом.",
"Ні. Zen — це плата за використання, тоді як Go починається від $5 за перший місяць, потім $10/місяць, із щедрими лімітами та надійним доступом до моделей з відкритим кодом GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro та DeepSeek V4 Flash.",
"go.faq.q4": "Скільки коштує Go?",
"go.faq.a4.p1.beforePricing": "Go коштує",
"go.faq.a4.p1.pricingLink": "$5 за перший місяць",
@@ -350,7 +350,7 @@ export const dict = {
"go.faq.q9": "Яка різниця між безкоштовними моделями та Go?",
"go.faq.a9":
"Безкоштовні моделі включають Big Pickle та акційні моделі з лімітом 200 запитів/день. Go включає GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro та DeepSeek V4 Flash із вищими лімітами.",
"Безкоштовні моделі включають Big Pickle та акційні моделі з лімітом 200 запитів/день. Go включає GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, MiniMax M3, DeepSeek V4 Pro та DeepSeek V4 Flash із вищими лімітами.",
"zen.api.error.rateLimitExceeded": "Перевищено ліміт запитів. Спробуйте пізніше.",
"zen.api.error.modelNotSupported": "Модель {{model}} не підтримується",
+4 -4
View File
@@ -241,7 +241,7 @@ export const dict = {
"go.title": "OpenCode Go | 人人可用的低成本编程模型",
"go.meta.description":
"Go 首月 $5,之后 $10/月,提供对 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash 的 5 小时充裕请求额度。",
"Go 首月 $5,之后 $10/月,提供对 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash 的 5 小时充裕请求额度。",
"go.hero.title": "人人可用的低成本编程模型",
"go.hero.body":
"Go 将代理编程带给全世界的程序员。提供充裕的限额和对最强大的开源模型的可靠访问,让您可以利用强大的代理进行构建,而无需担心成本或可用性。",
@@ -289,7 +289,7 @@ export const dict = {
"go.problem.item2": "充裕的限额和可靠的访问",
"go.problem.item3": "为尽可能多的程序员打造",
"go.problem.item4":
"包含 GLM-5.1, GLM-5, Kimi K2.5、Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash",
"包含 GLM-5.1, GLM-5, Kimi K2.5、Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash",
"go.how.title": "Go 如何工作",
"go.how.body": "Go 起价为首月 $5,之后 $10/月。您可以将其与 OpenCode 或任何代理搭配使用。",
"go.how.step1.title": "创建账户",
@@ -311,7 +311,7 @@ export const dict = {
"go.faq.a2": "Go 包含下方列出的模型,提供充足的限额和可靠的访问。",
"go.faq.q3": "Go 和 Zen 一样吗?",
"go.faq.a3":
"不。Zen 是按量付费,而 Go 首月 $5,之后 $10/月,提供充裕的额度,并可可靠地访问 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash 等开源模型。",
"不。Zen 是按量付费,而 Go 首月 $5,之后 $10/月,提供充裕的额度,并可可靠地访问 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash 等开源模型。",
"go.faq.q4": "Go 多少钱?",
"go.faq.a4.p1.beforePricing": "Go 费用为",
"go.faq.a4.p1.pricingLink": "首月 $5",
@@ -333,7 +333,7 @@ export const dict = {
"go.faq.q9": "免费模型和 Go 之间的区别是什么?",
"go.faq.a9":
"免费模型包含 Big Pickle 加上当时可用的促销模型,每天有 200 次请求的配额。Go 包含 GLM-5.1, GLM-5, Kimi K2.5、Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, Qwen3.5 Plus, MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash,并在滚动窗口(5 小时、每周和每月)内执行更高的请求配额,大致相当于每 5 小时 $12、每周 $30 和每月 $60(实际请求计数因模型和使用情况而异)。",
"免费模型包含 Big Pickle 加上当时可用的促销模型,每天有 200 次请求的配额。Go 包含 GLM-5.1, GLM-5, Kimi K2.5、Kimi K2.6, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.7 Max, Qwen3.6 Plus, MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash,并在滚动窗口(5 小时、每周和每月)内执行更高的请求配额,大致相当于每 5 小时 $12、每周 $30 和每月 $60(实际请求计数因模型和使用情况而异)。",
"zen.api.error.rateLimitExceeded": "超出速率限制。请稍后重试。",
"zen.api.error.modelNotSupported": "不支持模型 {{model}}",
+4 -4
View File
@@ -241,7 +241,7 @@ export const dict = {
"go.title": "OpenCode Go | 低成本全民編碼模型",
"go.meta.description":
"Go 首月 $5,之後 $10/月,提供對 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash 的 5 小時充裕請求額度。",
"Go 首月 $5,之後 $10/月,提供對 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash 的 5 小時充裕請求額度。",
"go.hero.title": "低成本全民編碼模型",
"go.hero.body":
"Go 將代理編碼帶給全世界的程式設計師。提供寬裕的限額以及對最強大開源模型的穩定存取,讓你可以使用強大的代理進行構建,而無需擔心成本或可用性。",
@@ -289,7 +289,7 @@ export const dict = {
"go.problem.item2": "寬裕的限額與穩定存取",
"go.problem.item3": "專為盡可能多的程式設計師打造",
"go.problem.item4":
"包含 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 與 DeepSeek V4 Flash",
"包含 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 與 DeepSeek V4 Flash",
"go.how.title": "Go 如何運作",
"go.how.body": "Go 起價為首月 $5,之後 $10/月。您可以將其與 OpenCode 或任何代理搭配使用。",
"go.how.step1.title": "建立帳號",
@@ -311,7 +311,7 @@ export const dict = {
"go.faq.a2": "Go 包含下方列出的模型,提供充足的額度與穩定的存取。",
"go.faq.q3": "Go 與 Zen 一樣嗎?",
"go.faq.a3":
"不。Zen 是按量付費,而 Go 首月 $5,之後 $10/月,提供充裕的額度,並可可靠地存取 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 和 DeepSeek V4 Flash 等開源模型。",
"不。Zen 是按量付費,而 Go 首月 $5,之後 $10/月,提供充裕的額度,並可可靠地存取 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 和 DeepSeek V4 Flash 等開源模型。",
"go.faq.q4": "Go 費用是多少?",
"go.faq.a4.p1.beforePricing": "Go 費用為",
"go.faq.a4.p1.pricingLink": "首月 $5",
@@ -333,7 +333,7 @@ export const dict = {
"go.faq.q9": "免費模型與 Go 有什麼區別?",
"go.faq.a9":
"免費模型包括 Big Pickle 以及當時可用的促銷模型,配額為 200 次請求/天。Go 包括 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、Qwen3.5 Plus、MiniMax M2.5、MiniMax M2.7、DeepSeek V4 Pro 與 DeepSeek V4 Flash,並在滾動視窗(5 小時、每週和每月)內執行更高的請求配額,大約相當於每 5 小時 $12、每週 $30 和每月 $60(實際請求數因模型和使用情況而異)。",
"免費模型包括 Big Pickle 以及當時可用的促銷模型,配額為 200 次請求/天。Go 包括 GLM-5.1、GLM-5、Kimi K2.5、Kimi K2.6、MiMo-V2.5-Pro、MiMo-V2.5、Qwen3.7 Max、Qwen3.6 Plus、MiniMax M2.5、MiniMax M2.7、MiniMax M3、DeepSeek V4 Pro 與 DeepSeek V4 Flash,並在滾動視窗(5 小時、每週和每月)內執行更高的請求配額,大約相當於每 5 小時 $12、每週 $30 和每月 $60(實際請求數因模型和使用情況而異)。",
"zen.api.error.rateLimitExceeded": "超出頻率限制。請稍後再試。",
"zen.api.error.modelNotSupported": "不支援模型 {{model}}",
@@ -1,4 +1,6 @@
import { Actor } from "@opencode-ai/console-core/actor.js"
import { Referral } from "@opencode-ai/console-core/referral.js"
import { getRequestEvent } from "solid-js/web"
const REFERRAL_COOKIE = "oc_referral"
const REFERRAL_MAX_AGE = 60 * 60 * 24 * 30
@@ -26,3 +28,17 @@ export function referralCodeFromCookieHeader(header: string | null) {
?.slice(`${REFERRAL_COOKIE}=`.length),
)
}
export async function createReferralFromCookie() {
const event = getRequestEvent()
const referralCode = referralCodeFromCookieHeader(event?.request.headers.get("cookie") ?? null)
if (!referralCode) return
await Referral.createFromAccount({
accountID: Actor.account(),
referralCode,
}).catch((error) => {
console.error("Referral create failed", error)
})
event?.response.headers.append("set-cookie", clearReferralCookie())
}
@@ -0,0 +1,37 @@
import type { APIEvent } from "@solidjs/start/server"
import { Resource } from "@opencode-ai/console-resource"
export async function statsProxy(evt: APIEvent) {
const req = evt.request.clone()
const targetUrl = new URL(req.url)
targetUrl.protocol = "https:"
targetUrl.hostname = Resource.App.stage === "production" ? "stats.opencode.ai" : "stats.dev.opencode.ai"
targetUrl.port = ""
if (targetUrl.pathname.startsWith("/stats/_build/")) {
targetUrl.pathname = targetUrl.pathname.slice("/stats".length)
}
const response = await fetch(targetUrl, {
method: req.method,
headers: req.headers,
body: req.body,
})
if (!response.headers.get("content-type")?.includes("text/html")) return response
const headers = new Headers(response.headers)
headers.delete("content-encoding")
headers.delete("content-length")
headers.delete("etag")
return new Response(rewriteStatsHtml(await response.text()), {
status: response.status,
statusText: response.statusText,
headers,
})
}
function rewriteStatsHtml(html: string) {
return html.replaceAll('"/_build/', '"/stats/_build/').replaceAll("'/_build/", "'/stats/_build/")
}
@@ -90,6 +90,7 @@ body {
[data-page="go"] {
background: var(--color-background);
overflow-x: clip;
--padding: 5rem;
--vertical-padding: 4rem;
+7 -7
View File
@@ -31,7 +31,7 @@ const models = [
{ name: "MiMo-V2.5", provider: "Xiaomi MiMo" },
{ name: "Qwen3.7 Max", provider: "Alibaba Cloud Model Studio" },
{ name: "Qwen3.6 Plus", provider: "Alibaba Cloud Model Studio" },
{ name: "Qwen3.5 Plus", provider: "Alibaba Cloud Model Studio" },
{ name: "MiniMax M3", provider: "MiniMax" },
{ name: "MiniMax M2.7", provider: "MiniMax" },
{ name: "MiniMax M2.5", provider: "MiniMax" },
{ name: "DeepSeek V4 Pro", provider: "DeepSeek" },
@@ -62,13 +62,13 @@ function LimitsGraph(props: { href: string }) {
const free = 200
const graph = [
{ id: "glm-5.1", name: "GLM-5.1", req: 880, d: "100ms" },
{ id: "qwen3.7-max", name: "Qwen3.7 Max", req: 950, d: "280ms" },
{ id: "qwen3.7-max", name: "Qwen3.7 Max", req: 950, d: "110ms" },
{ id: "kimi-k2.6", name: "Kimi K2.6", req: 1150, d: "150ms" },
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", req: 3250, d: "150ms" },
{ id: "qwen3.6-plus", name: "Qwen3.6 Plus", req: 3300, d: "280ms" },
{ id: "minimax-m2.7", name: "MiniMax M2.7", req: 3400, d: "300ms" },
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 3450, d: "200ms" },
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus", req: 10200, d: "360ms" },
{ id: "minimax-m3", name: "MiniMax M3", req: 1400, d: "200ms" },
{ id: "mimo-v2.5-pro", name: "MiMo-V2.5-Pro", req: 3250, d: "210ms" },
{ id: "qwen3.6-plus", name: "Qwen3.6 Plus", req: 3300, d: "220ms" },
{ id: "minimax-m2.7", name: "MiniMax M2.7", req: 3400, d: "230ms" },
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 3450, d: "240ms" },
{ id: "mimo-v2.5", name: "MiMo-V2.5", req: 30100, d: "340ms" },
{ id: "deepseek-v4-flash", name: "DeepSeek V4 Flash", req: 31650, d: "340ms" },
]
@@ -0,0 +1,8 @@
import { statsProxy } from "~/lib/stats-proxy"
export const GET = statsProxy
export const POST = statsProxy
export const PUT = statsProxy
export const DELETE = statsProxy
export const OPTIONS = statsProxy
export const PATCH = statsProxy
@@ -0,0 +1,8 @@
import { statsProxy } from "~/lib/stats-proxy"
export const GET = statsProxy
export const POST = statsProxy
export const PUT = statsProxy
export const DELETE = statsProxy
export const OPTIONS = statsProxy
export const PATCH = statsProxy
@@ -15,6 +15,7 @@ import { useI18n } from "~/context/i18n"
import { useLanguage } from "~/context/language"
import { formError } from "~/lib/form-error"
import { formatResetTime, liteResetTimeKeys } from "~/lib/format-reset-time"
import { createReferralFromCookie } from "~/lib/referral-invite"
import { IconAlipay, IconUpi } from "~/component/icon"
@@ -74,16 +75,14 @@ const createLiteCheckoutUrl = action(
async (workspaceID: string, successUrl: string, cancelUrl: string, method?: "alipay" | "upi") => {
"use server"
return json(
await withActor(
() =>
Billing.generateLiteCheckoutUrl({ successUrl, cancelUrl, method })
.then((data) => ({ error: undefined, data }))
.catch((e) => ({
error: e.message as string,
data: undefined,
})),
workspaceID,
),
await withActor(async () => {
const data = await Billing.generateLiteCheckoutUrl({ successUrl, cancelUrl, method })
await createReferralFromCookie()
return { error: undefined, data }
}, workspaceID).catch((e) => ({
error: e.message as string,
data: undefined,
})),
{ revalidate: [queryBillingInfo.key, queryLiteSubscription.key] },
)
},
@@ -266,7 +265,7 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
<li>MiMo-V2.5</li>
<li>MiniMax M2.5</li>
<li>MiniMax M2.7</li>
<li>Qwen3.5 Plus</li>
<li>MiniMax M3</li>
<li>Qwen3.6 Plus</li>
<li>Qwen3.7 Max</li>
<li>DeepSeek V4 Pro</li>

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