Compare commits

...
Author SHA1 Message Date
Kit Langton e8562cad22 refactor(llm): drop approval bridge 2026-04-30 23:05:10 -04:00
opencode-agent[bot] 8b56d77ea1 chore: generate 2026-05-01 03:02:15 +00:00
Kit LangtonandGitHub dd3aa96730 test(httpapi): cover more safe GET parity (#25217) 2026-04-30 23:01:11 -04:00
Kit LangtonandGitHub 8b56d1712f refactor(session): pass project to list (#25215) 2026-04-30 23:00:59 -04:00
Kit LangtonandGitHub 3c24d22d42 fix(httpapi): omit absent optional response fields (#25214) 2026-05-01 02:38:32 +00:00
Kit LangtonandGitHub 4c70ea28d2 fix(tui): scope Zed editor context to containing workspaces (#25211) 2026-04-30 22:33:39 -04:00
Kit LangtonandGitHub 5ba68a28c0 refactor(httpapi): scope async prompt fiber (#25213) 2026-04-30 22:33:02 -04:00
opencode-agent[bot] bce4def2db chore: generate 2026-05-01 02:26:56 +00:00
Kit LangtonandGitHub 3544ea0244 refactor(httpapi): drop session prompt bridge (#25210) 2026-04-30 22:25:52 -04:00
opencode-agent[bot] 6434918794 chore: generate 2026-05-01 01:46:55 +00:00
Kit LangtonandGitHub 5984d917dc refactor(session): yield instance context in system prompt (#25207) 2026-04-30 21:45:48 -04:00
Kit LangtonandGitHub c2a97a7a6c refactor(file): yield instance context in watcher (#25205) 2026-04-30 21:45:21 -04:00
Kit LangtonandGitHub a083c88e87 refactor(sync): capture instance context for publish (#25206) 2026-04-30 21:45:02 -04:00
Kit LangtonandGitHub ce3b0988c4 refactor(project): yield instance context in bootstrap (#25204) 2026-04-30 21:44:52 -04:00
Kit LangtonandGitHub e8a194a2bb test(effect): stabilize runner active shell check (#25203) 2026-04-30 21:36:19 -04:00
Kit LangtonandGitHub 8aa8798e07 refactor(session): yield instance context in llm (#25200) 2026-04-30 21:29:28 -04:00
opencode-agent[bot] 6d4629b566 chore: generate 2026-05-01 01:28:37 +00:00
OpeOginniandGitHub a9d399699e fix(desktop): Prevent Model response Interruption when opening settings dialog (#25114) 2026-05-01 01:27:38 +00:00
Kit LangtonandGitHub bc805b3001 Pass CORS options to HttpApi backend (#25201) 2026-04-30 21:26:32 -04:00
Kit LangtonandGitHub 668d77bb4e refactor(tool): yield InstanceState context (#25199) 2026-04-30 21:01:06 -04:00
Kit LangtonandGitHub 5c2e06f353 Document HttpApi route patterns (#25188) 2026-04-30 20:48:14 -04:00
Kit LangtonandGitHub a499fe2b17 refactor(tool/read): yield InstanceState.context instead of reading ALS (#25183) 2026-04-30 20:33:04 -04:00
Kit LangtonandGitHub 451650b584 refactor(httpapi): preserve typed errors in session prompt handlers (#25181) 2026-04-30 20:04:00 -04:00
opencode-agent[bot] 1b76bec0e2 chore: generate 2026-05-01 00:03:55 +00:00
Kit LangtonandGitHub 96f4da1e1d Serve instance events through HttpApiBuilder (#25182) 2026-04-30 20:02:46 -04:00
opencode-agent[bot] 96a0dd6b04 chore: generate 2026-04-30 23:37:58 +00:00
Kit LangtonandGitHub 2dd1f2d453 Avoid request-time HttpApi layer provisioning (#25179) 2026-04-30 19:36:57 -04:00
opencode-agent[bot] 510f01674a chore: generate 2026-04-30 23:29:58 +00:00
Kit LangtonandGitHub e3134a2a99 refactor(session): align prompt input types with their schemas (#25178) 2026-04-30 19:28:46 -04:00
opencode-agent[bot] 8805104b8d chore: generate 2026-04-30 23:25:10 +00:00
Kit LangtonandGitHub fc155e9fc5 Build HttpApi UI route from services (#25177) 2026-04-30 19:24:10 -04:00
opencode-agent[bot] 3aaac0098e chore: generate 2026-04-30 23:06:57 +00:00
Sewer.andGitHub a12333310f fix(provider): split providerOptions key on dot for openai-compatible, openai, and anthropic providers (#25145) 2026-04-30 18:05:56 -05:00
opencode-agent[bot] 247284b9af chore: generate 2026-04-30 22:51:09 +00:00
Kit LangtonandGitHub e0305e47f3 Protect HttpApi web UI fallback with auth (#25169) 2026-04-30 18:49:54 -04:00
Kit LangtonandGitHub 76a0f0f619 docs(httpapi): update migration spec to current state (#25173) 2026-04-30 18:41:27 -04:00
Aiden ClineandGitHub 560baae15d fix: ensure user config takes precendence over plugin hooks for model resolution (#25167) 2026-04-30 17:15:56 -05:00
Kit LangtonandGitHub 5518ecaefe Fix HttpApi web UI fallback (#25163) 2026-04-30 17:43:18 -04:00
opencode-agent[bot] 924ba97055 chore: generate 2026-04-30 21:04:24 +00:00
Aiden ClineandGitHub b80f52f8ad tweak: adjust codex plugin to use the models hook (#25157) 2026-04-30 16:03:07 -05:00
Kit LangtonandGitHub feb275d08b Remove covered workspace websocket todo (#25161) 2026-04-30 20:58:08 +00:00
Kit LangtonandGitHub fbcbd24063 Add SyncEvent service (#25158) 2026-04-30 16:45:26 -04:00
Kit LangtonandGitHub 3250b814ce Fix HttpApi raw route authorization (#25154) 2026-04-30 19:55:20 +00:00
Kit LangtonandGitHub 0e9d9282c6 Refactor workspace service boundaries (#25152) 2026-04-30 15:34:37 -04:00
Kit LangtonandGitHub b315a70773 test: use Effect test helper for agent colors (#25051) 2026-04-30 15:14:25 -04:00
Kit LangtonandGitHub cedff6fb89 Isolate TUI thread cwd resolution test (#25147) 2026-04-30 15:10:30 -04:00
Kit LangtonandGitHub 87cd9446d8 test: use testEffect for plugin triggers (#25053) 2026-04-30 14:24:53 -04:00
Kit LangtonandGitHub f4ce240a2e Use PTY service directly in HTTP routes (#25138) 2026-04-30 14:24:43 -04:00
Kit LangtonandGitHub 320527a3e4 Support multiple Zed selections in TUI context (#25140) 2026-04-30 14:15:50 -04:00
Kit LangtonandGitHub 19271fca2d Use workspace service in HTTP routes (#25139) 2026-04-30 13:57:25 -04:00
Kit LangtonandGitHub feeebbe7d4 Preserve workspace context in session HTTP routes (#25136) 2026-04-30 13:53:26 -04:00
Kit LangtonandGitHub f384675c01 test: use Effect test helper for run-service (#25048) 2026-04-30 17:05:10 +00:00
Kit LangtonandGitHub ec3ab4a00c test: use testEffect for retry policy (#25050) 2026-04-30 12:55:33 -04:00
Kit LangtonandGitHub e4ac936eb9 test: use testEffect for plugin workspace adaptor (#25052) 2026-04-30 12:54:53 -04:00
Kit LangtonandGitHub 79e23b7eb9 test: use testEffect for instance state (#25115) 2026-04-30 12:53:13 -04:00
Kit LangtonandGitHub 92e80b4660 test: use Effect test helper for app runtime logger (#25049) 2026-04-30 12:52:29 -04:00
Kit LangtonandGitHub ce63ca4d7a test: use testEffect for system prompt test (#25047) 2026-04-30 12:51:32 -04:00
Kit LangtonandGitHub fef7981942 test: use Effect runtime in runner deadlock case (#25045) 2026-04-30 12:45:30 -04:00
Aiden ClineandGitHub ffe0314c47 fix: ensure disabling OPENCODE_DISABLE_CLAUDE_CODE_SKILLS doesnt disable external skills too (#25123) 2026-04-30 11:15:53 -05:00
opencode-agent[bot] 375444a149 chore: update nix node_modules hashes 2026-04-30 15:48:28 +00:00
Kit LangtonandGitHub 65c15afe9f test: use testEffect for instruction tests (#25046) 2026-04-30 11:48:13 -04:00
opencode-agent[bot] 8f57a2a462 chore: generate 2026-04-30 15:46:04 +00:00
James LongandGitHub 53e9cac383 refactor(core): convert control-plane workspace to Effect (#25018) 2026-04-30 11:44:58 -04:00
SebastianandGitHub fe0c182747 upgrade opentui to 0.2.0 (#24810) 2026-04-30 17:33:54 +02:00
opencode-agent[bot] 29b1060c67 chore: generate 2026-04-30 15:08:03 +00:00
Kit LangtonandGitHub dddfcbf0d8 test: port instance HttpApi path/vcs read coverage to Effect 2026-04-30 11:07:00 -04:00
OpeOginniandGitHub 62e1335388 fix(opencode): allow oc://renderer origin in cors middleware (#25099) 2026-04-30 12:11:42 +00:00
Brendan AllanandGitHub 908e28175f fix: invert *_ready getters to fix server status indicator (#25077) 2026-04-30 15:10:39 +08:00
Brendan AllanandGitHub 3398fd7719 feat(httpapi): add CORS middleware to instance routes (#25074) 2026-04-30 07:06:17 +00:00
Luke ParkerandGitHub 9bddf7f3ef fix app crash restoring messages without model (#25062) 2026-04-30 14:44:53 +10:00
Dax Raad 8ba374fefa ci: enable sourcemaps for beta releases
Generate linked sourcemaps when building beta releases to help users
debug issues with readable stack traces.
2026-04-30 00:42:22 -04:00
Aiden ClineandGitHub 3ef0aaf768 tweak: make azure onboarding ux a bit better (#25057) 2026-04-29 23:35:59 -05:00
Tommy D. RossiandGitHub d7701dbfb6 fix(opencode): preserve external_dir and deny parent permissions in task child sessions (#23290) 2026-04-29 22:06:29 -05:00
Kit LangtonandGitHub c49bf0b402 test: cover ConfigService helper (#25042) 2026-04-30 02:41:59 +00:00
Kit LangtonandGitHub cee9610d26 refactor: use Effect config for HttpApi authorization (#25035) 2026-04-29 22:22:32 -04:00
Kit LangtonandGitHub 38adc13295 test: cover HttpApi authorization middleware (#25033) 2026-04-29 21:34:52 -04:00
Kit LangtonandGitHub 4fe14abb8c test: cover HttpApi instance context middleware (#25032) 2026-04-29 21:24:45 -04:00
Kit LangtonandGitHub 9052e8a1ba test: cover HttpApi workspace routing middleware (#25027) 2026-04-29 21:08:03 -04:00
github-actions[bot] de78dedceb Update VOUCHED list
https://github.com/anomalyco/opencode/issues/23890#issuecomment-4348703527
2026-04-30 00:58:42 +00:00
Kit LangtonandGitHub 6f508d574e test: deflake runner cancel test (#25021) 2026-04-29 20:19:52 -04:00
Kit LangtonandGitHub 61dfae31e7 test: cover HttpApi websocket proxy (#25017) 2026-04-29 19:37:50 -04:00
opencode ac6aa43e3b sync release versions for v1.14.30 2026-04-29 23:33:39 +00:00
Luke ParkerandGitHub ea89925042 fix: handle invalid mcp urls (#25019) 2026-04-30 09:32:26 +10:00
opencode-agent[bot] 12cbfe5b64 chore: generate 2026-04-29 22:40:26 +00:00
Luke ParkerandGitHub d7b7be1909 fix(desktop): Path mismatches cause sessions missing + strong ID + existing data fix (#25013) 2026-04-29 22:39:19 +00:00
Aiden ClineandGitHub a740d2c667 fix: adjust azure defaults to closer match openai to prevent Item .. of type 'reasoning' was provided without its required following item (#25007) 2026-04-29 16:49:28 -05:00
Aiden ClineandGitHub 588261076a fix: make deepseek string check a bit looser (#25012) 2026-04-29 16:47:45 -05:00
Ruben De SmetandGitHub 639e27c3ce feat: add Mistral Medium 3.5 with reasoning support (#24996) 2026-04-29 16:26:24 -05:00
opencode-agent[bot] 1124ae17b4 chore: generate 2026-04-29 20:52:19 +00:00
Kit LangtonandGitHub 9db5890ce5 Refactor HttpApi workspace routing and proxy boundaries (#25006) 2026-04-29 16:50:54 -04:00
James LongandGitHub 293877cb7e fix(core): reconnect editor context for session directory (#24984) 2026-04-29 15:11:44 -04:00
opencode-agent[bot] c480006554 chore: generate 2026-04-29 18:17:10 +00:00
Aiden ClineandGitHub 6aa8e894b1 chore: rm broken codesearch tool (#24992) 2026-04-29 13:15:44 -05:00
Aiden ClineandGitHub 00bb9836a6 tweak: adjust order of system prompt instructions: Global, Project, Skills (#24974) 2026-04-29 10:55:53 -05:00
github-actions[bot] 71f9189607 Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24964#issuecomment-4345349260
2026-04-29 15:54:34 +00:00
opencode-agent[bot] a3f7ea2555 chore: generate 2026-04-29 13:47:48 +00:00
Kit Langton d3df8e1180 test(httpapi): clean up SDK parity tests 2026-04-29 09:46:17 -04:00
opencode-agent[bot] df147b65fd chore: generate 2026-04-29 13:36:05 +00:00
Kit LangtonandGitHub 6015084fa2 Prepare Effect HttpApi backend parity (#24853) 2026-04-29 09:34:50 -04:00
Brendan AllanandGitHub 65ba1f6c13 fix(download): update beta asset names from electron to desktop (#24908) 2026-04-29 14:50:06 +08:00
Brendan AllanandGitHub d37e5af57d Disable Windows update code signature verification (#24905) 2026-04-29 14:40:37 +08:00
d71b827d8c fix(session): remap compaction tail_start_id when forking (#24898)
Co-authored-by: spark4862 <spark4862@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-04-29 00:23:56 -05:00
Aiden ClineandGitHub 504ca3d3d8 feat: make it easier to toggle on/off paste summary in the tui (#24869) 2026-04-28 23:55:14 -05:00
MikeandGitHub a8c74c04de docs: add Atomic Chat provider section (#23069) 2026-04-28 23:10:41 -05:00
Brendan AllanandGitHub f6b4f54216 refactor(app): convert getProjectAvatarSource to early returns (#24896) 2026-04-29 12:00:13 +08:00
Dax Raad fc0e3c65b3 ignore 2026-04-28 23:07:46 -04:00
Dax Raad 23b8ed788e ignore 2026-04-28 23:03:48 -04:00
Dax Raad 3bd890f46b ignore: ideas 2026-04-28 23:00:07 -04:00
Luke ParkerandGitHub 9fbeafb63e fix: clear timeout after promise rejection (#24864) 2026-04-28 23:37:12 +00:00
opencode-agent[bot] 91bd295209 chore: generate 2026-04-28 23:11:59 +00:00
d4bf70be06 fix(bash): memory leak - release parsed syntax trees (#24861)
Co-authored-by: jiwenshang <jiwenshang@xiaohongshu.com>
2026-04-28 23:10:48 +00:00
opencode-agent[bot] ae8904c4ff chore: generate 2026-04-28 20:50:56 +00:00
James LongandGitHub 9209c04370 feat(core): filter sessions by path and add setting to disable (#24849) 2026-04-28 16:49:13 -04:00
Kit LangtonandGitHub 379e7f3f20 test(httpapi): cover sdk effect routes (#24836) 2026-04-28 16:34:06 -04:00
opencode-agent[bot] 366d11e1f8 chore: generate 2026-04-28 20:33:13 +00:00
Kit LangtonandGitHub 58836e75f0 fix(httpapi): wire global and control handlers (#24835) 2026-04-28 16:31:45 -04:00
Aiden ClineGitHubgithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
0acac216ae fix(copilot): ensure available variants sync from api (#24734)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-04-28 14:58:51 -05:00
opencode 276d162044 sync release versions for v1.14.29 2026-04-28 18:34:48 +00:00
opencode-agent[bot] 1b0ed983c5 chore: generate 2026-04-28 18:25:23 +00:00
Kit LangtonandGitHub 2e8d690ab1 fix(httpapi): finish sdk openapi parity (#24827) 2026-04-28 14:24:10 -04:00
Kit LangtonandGitHub 1ff8d289af fix(tui): handle Zed selection byte offsets (#24825) 2026-04-28 14:09:39 -04:00
DaxandGitHub d54ffbda1c tui: ignore invalid custom themes to prevent startup crashes (#24645) 2026-04-28 13:58:55 -04:00
Kit LangtonandGitHub c00058ed7a fix(httpapi): align request body openapi shape (#24811) 2026-04-28 12:55:37 -04:00
James LongandGitHub 2c2fc3499b feat(core): store relative path for sessions (#24704) 2026-04-28 11:51:24 -04:00
Kit LangtonandGitHub ea3c6c3481 fix(httpapi): document instance query parameters (#24809) 2026-04-28 11:10:00 -04:00
opencode-agent[bot] 9b68b7195a chore: generate 2026-04-28 15:05:37 +00:00
Kit LangtonandGitHub 7739cc53b4 refactor(httpapi): fork server startup by flag (#24799) 2026-04-28 11:02:35 -04:00
David HillandGitHub 3fa78a8b01 docs: bump GitHub stars count to 150K (#24792) 2026-04-28 09:24:46 -04:00
Kit LangtonandGitHub e57d0c2fee fix(httpapi): document tui bad request responses
Document legacy 400 bad-request responses on TUI Effect HttpApi payload-validation endpoints and cover them with OpenAPI parity tests.
2026-04-28 09:23:54 -04:00
Kit LangtonandGitHub 2a4f2bf527 fix(httpapi): align sync seq validation
Reject negative and fractional sync sequence values in Effect HttpApi schemas so replay/history validation matches the legacy Hono routes.
2026-04-28 09:22:49 -04:00
aa07f38b07 fix(app): preserve per-workspace icon override from localStorage (#24738)
Co-authored-by: Andrew Avsenin <snatvb@ya.ru>
2026-04-28 19:37:40 +08:00
Brendan AllanandGitHub 9d1f17d836 fix(ui): remove redundant flex overrides in tool components (#24749) 2026-04-28 15:51:18 +08:00
github-actions[bot] b420952e59 Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24732#issuecomment-4332442679
2026-04-28 04:53:58 +00:00
opencode-agent[bot] bb9e445257 chore: generate 2026-04-28 04:45:24 +00:00
Aiden ClineandGitHub 528fb1d404 fix: sanitize tools for moonshot (#24730) 2026-04-27 23:44:21 -05:00
Brendan AllanandGitHub c8d9f7aa89 refactor(app): load sync state through TanStack Query (#23792) 2026-04-28 04:24:30 +00:00
opencode-agent[bot] cd7ec93cdf chore: generate 2026-04-28 02:34:43 +00:00
Kit LangtonandGitHub 796b652d2b fix(httpapi): preserve mcp oauth error parity (#24706) 2026-04-27 22:33:21 -04:00
Kit LangtonandGitHub 4d74849c1a fix(tui): keep Zed context polling responsive (#24711) 2026-04-27 22:24:04 -04:00
opencode-agent[bot] 937a7c48a5 chore: update nix node_modules hashes 2026-04-28 02:22:33 +00:00
Kit LangtonandGitHub 704eb00de4 chore: bump effect beta (#24705) 2026-04-27 21:58:11 -04:00
opencode-agent[bot] bad4599bf9 chore: generate 2026-04-28 01:50:04 +00:00
Kit LangtonandGitHub 892fd85ba7 fix(httpapi): preserve provider oauth authorize parity (#24703) 2026-04-27 21:48:50 -04:00
opencode-agent[bot] 0eaa47d857 chore: generate 2026-04-28 00:54:28 +00:00
Kit LangtonandGitHub faca24d487 fix(httpapi): align session boolean query parsing (#24693) 2026-04-27 20:53:27 -04:00
Kit LangtonandGitHub c103202ad5 test(httpapi): cover session json parity (#24682) 2026-04-27 19:48:57 -04:00
Kit LangtonandGitHub ce78a4265d fix(session): remove compaction summary dividers (#24677) 2026-04-27 18:15:11 -04:00
Kit LangtonandGitHub c4a2353ac3 fix(session): omit undefined optional fields (#24676) 2026-04-27 17:50:09 -04:00
Kit LangtonandGitHub 576efed196 fix(httpapi): preserve optional session fields (#24671) 2026-04-27 21:38:28 +00:00
opencode-agent[bot] dfc0075f90 chore: generate 2026-04-27 20:52:42 +00:00
Kit LangtonandGitHub acd15dcc8a test(httpapi): cover full OpenAPI route inventory (#24667) 2026-04-27 16:51:24 -04:00
Kit LangtonandGitHub 139c4fd555 fix(session): harden shell cancellation (#24553) 2026-04-27 20:47:18 +00:00
CasandGitHub e0f3df8252 fix(tui): consume Enter in dialog useKeyboard handlers (#23390) 2026-04-27 15:31:49 -05:00
opencode-agent[bot] 9cd2e3a1c3 chore: generate 2026-04-27 20:31:05 +00:00
Kit LangtonandGitHub f584f80219 test(httpapi): verify reflected route mounts (#24663) 2026-04-27 16:29:58 -04:00
Kit LangtonandGitHub 45eac589f8 fix(tui): preserve Zed context on terminal focus (#24662) 2026-04-27 16:25:37 -04:00
James LongandGitHub fab1768826 feat(core): file context improvements and option to disable (#24661) 2026-04-27 16:10:13 -04:00
Kit LangtonandGitHub 51fc10e407 fix(httpapi): enforce instance route parity (#24660) 2026-04-27 16:07:31 -04:00
opencode-agent[bot] 7a1c8465f5 chore: generate 2026-04-27 19:38:33 +00:00
Kit LangtonandGitHub 5290e9ca7e fix(tui): stabilize Zed editor context polling (#24656) 2026-04-27 15:37:18 -04:00
Aiden ClineandGitHub c361c2953f fix: ensure toolStreaming is set to off by default when using non anthropic models with anthropic sdk (#24642) 2026-04-27 14:16:00 -05:00
opencode-agent[bot] ccb7669736 chore: generate 2026-04-27 18:34:44 +00:00
DaxandGitHub f25f1485d5 refactor: remove module barrels (#24554) 2026-04-27 14:33:33 -04:00
Kit LangtonandGitHub 55ecb06748 fix(httpapi): accept empty session create body (#24640) 2026-04-27 17:17:11 +00:00
Kit LangtonandGitHub dc6991e5a8 fix(httpapi): mount workspace bridge routes (#24626) 2026-04-27 12:52:48 -04:00
Aiden ClineandGitHub 738b3065dc tweak: make interleaved reasoning_content default to true for openai compat deepseek setups (#24630) 2026-04-27 10:17:38 -05:00
opencode-agent[bot] 26cc537cb1 chore: generate 2026-04-27 14:46:00 +00:00
ede354b0e6 docs: fix duplicated word in CLI env var table (#24614)
Co-authored-by: Seashore <ss@SeashoredeMac-mini.local>
2026-04-27 09:44:53 -05:00
JackandGitHub 61eabfc60c update Go DeepSeek flash limits for cache pricing drop (#24592) 2026-04-27 17:02:27 +08:00
opencode-agent[bot] 2789b770aa chore: generate 2026-04-27 05:40:37 +00:00
Luke ParkerandGitHub 8718b98ee1 fix: pass workspace symbol query to experimental LSP tool (#24576) 2026-04-27 05:39:36 +00:00
opencode-agent[bot] c8b2f987f9 chore: generate 2026-04-27 05:39:13 +00:00
Frank 52b55b826f Merge branch 'fix/usage-chart' into dev 2026-04-27 01:37:52 -04:00
Frank e8c20235b8 zen: coupons 2026-04-27 01:36:28 -04:00
opencode-agent[bot] 17701628bd chore: generate 2026-04-27 05:18:33 +00:00
0efc6163f1 fix(opencode): agent create generates permissions field with deny ins… (#24482)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-04-27 00:17:08 -05:00
JackandGitHub 1e191ba815 update Go DeepSeek request estimates for cache pricing changes (#24575) 2026-04-27 13:06:51 +08:00
Aiden ClineandGitHub f19d863689 ignore: split up reasoning transforms (#24574) 2026-04-26 23:57:32 -05:00
Frank 4a1ef327ca sync 2026-04-27 00:52:54 -04:00
Aiden ClineandGitHub 025a6392ce fix: default tool call streaming to false for google vertex (#24573) 2026-04-26 23:42:23 -05:00
opencode e578c442be sync release versions for v1.14.28 2026-04-27 04:23:44 +00:00
opencode-agent[bot] 0cecb1bff2 chore: generate 2026-04-27 04:05:59 +00:00
Frank 5d8971c1ed go: add deepseek icon 2026-04-27 00:04:44 -04:00
DaxandGitHub a9b62d67df Refactor npm config handling (#24565) 2026-04-27 03:54:59 +00:00
Luke ParkerandGitHub 3525e61906 fix: ignore GitHub Actions changelog contributor (#24567) 2026-04-27 03:47:04 +00:00
github-actions[bot] 059e6c46db Update VOUCHED list
https://github.com/anomalyco/opencode/issues/24563#issuecomment-4323944984
2026-04-27 03:30:38 +00:00
Frank 5cf195e0af go: models endpoint 2026-04-26 23:02:18 -04:00
opencode 244d1debe4 sync release versions for v1.14.27 2026-04-27 02:09:07 +00:00
opencode-agent[bot] 35734b42fe chore: update nix node_modules hashes 2026-04-27 01:55:51 +00:00
SebastianandGitHub a3128e32c5 upgrade opentui to 0.1.105 (#24555) 2026-04-26 21:39:40 -04:00
DaxandGitHub 5f8a72bfc4 fix(tui): hide provider checks before onboarding (#24551) 2026-04-27 01:18:26 +00:00
Kit LangtonandGitHub 418a1cf5f3 feat(httpapi): bridge tui routes (#24548) 2026-04-27 01:17:48 +00:00
Dax Raad 60ebd074ac core: refactor Installation service to use a single consolidated result object
Reorganizes the Installation service implementation by grouping info, method, latest, and upgrade methods into a single result object. This improves code locality and makes the service interface more maintainable. Also adds a clarifying comment explaining why the package manager's resolver is used for version lookups (to ensure registries, mirrors, auth, proxies, and dist-tags match upgrade behavior).
2026-04-26 21:05:42 -04:00
Kit LangtonandGitHub 216dd363e8 feat(httpapi): bridge pty routes (#24547) 2026-04-26 21:05:16 -04:00
Luke ParkerandGitHub 141f33d24b feat: configurable shell selection + desktop settings UI (#20602) 2026-04-27 00:54:55 +00:00
opencode-agent[bot] c4d8a8183e chore: generate 2026-04-26 23:56:15 +00:00
Kit LangtonandGitHub 58244eb687 feat(httpapi): bridge event stream (#24518) 2026-04-26 19:55:13 -04:00
Dax Raad e9071b0a80 tui: remove excessive debug logging from workspace creation flow to reduce terminal output noise 2026-04-26 19:33:40 -04:00
c68907ece2 fix(tui): update toast duration handling to use default value (#23395)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-26 17:27:02 -05:00
opencode af3998c8a6 sync release versions for v1.14.26 2026-04-26 21:01:16 +00:00
opencode-agent[bot] fad2618757 chore: update nix node_modules hashes 2026-04-26 20:45:18 +00:00
SebastianandGitHub 21e01dbe04 upgrade opentui to 0.1.104 (#24531) 2026-04-26 22:31:33 +02:00
3beadeebff feat(go): add Go model listing endpoint (#24304)
Co-authored-by: Frank <frank@anoma.ly>
2026-04-26 14:46:49 -04:00
Jermiah JosephandGitHub dcee1c3642 fix(editor): reject lock files with no workspace match for cwd (#24323) 2026-04-26 14:21:29 -04:00
Aiden Cline 00d1a7e090 chore: rm empty file 2026-04-26 12:32:41 -05:00
opencode-agent[bot] bbb56c2a88 chore: generate 2026-04-26 17:14:24 +00:00
Frank 5186c6964b sync 2026-04-26 13:11:09 -04:00
Frank 79c66e353f sync 2026-04-26 12:50:03 -04:00
opencode-agent[bot] 41f5e8a861 chore: generate 2026-04-26 16:25:19 +00:00
Kit LangtonandGitHub c5b67927af feat(httpapi): bridge remaining session routes (#24510) 2026-04-26 12:24:19 -04:00
opencode-agent[bot] 301ecb185e chore: generate 2026-04-26 16:01:07 +00:00
Kit LangtonandGitHub 151df05eeb feat(httpapi): bridge session message mutations (#24487) 2026-04-26 12:00:02 -04:00
opencode-agent[bot] 55adcdfd07 chore: generate 2026-04-26 15:51:31 +00:00
Kit LangtonandGitHub daaa2e5911 feat(httpapi): bridge session lifecycle routes (#24486) 2026-04-26 11:50:26 -04:00
opencode-agent[bot] daff119fe4 chore: generate 2026-04-26 15:50:09 +00:00
Kit LangtonandGitHub e0d1ff42c0 feat(httpapi): bridge session read routes (#24485) 2026-04-26 11:49:11 -04:00
opencode-agent[bot] de413c56ae chore: generate 2026-04-26 15:32:42 +00:00
Kit LangtonandGitHub da61b0290a feat(httpapi): bridge sync routes (#24484) 2026-04-26 11:31:46 -04:00
Dax Raad d03e6cedde ci: update team assignments in github-triage
Update team member assignments in the triage tool:
- Remove thdxr from tui and core teams
- Add simonklee to tui team
- Add kitlangton to core team
2026-04-26 11:18:59 -04:00
JackandGitHub 7feb6ab962 fix(docs): correct OpenCode Go DeepSeek endpoints (#24500) 2026-04-26 23:15:55 +08:00
opencode-agent[bot] 854d4b7a53 chore: generate 2026-04-26 15:13:18 +00:00
Kit LangtonandGitHub aa5999b188 feat(httpapi): bridge workspace mutations (#24483) 2026-04-26 11:12:04 -04:00
opencode-agent[bot] 37c5eab6f8 chore: generate 2026-04-26 14:48:31 +00:00
Kit LangtonandGitHub 6daa2b9aeb feat(httpapi): bridge experimental session list (#24478) 2026-04-26 10:47:24 -04:00
Ariane EmoryandGitHub 5a5a2e5fa0 fix: correct typo in comment (#24420) 2026-04-26 00:26:48 -05:00
opencode-agent[bot] 95c43fc675 chore: update nix node_modules hashes 2026-04-26 05:19:08 +00:00
Aiden ClineandGitHub e7053c41f4 fix: bump openrouter sdk version to resolve deepseek reasoning issue (bug was in sdk pkg) (#24435) 2026-04-26 00:05:16 -05:00
Dax Raad fc6d4b4010 core: Add User-Agent header to identify client version in HTTP requests 2026-04-25 23:51:23 -04:00
Frank 2893588016 sync 2026-04-25 23:23:48 -04:00
Kit LangtonandGitHub f2d4d816fb test(provider): avoid plugin dependency install timeout (#24416) 2026-04-26 02:11:25 +00:00
opencode-agent[bot] 097d930668 chore: generate 2026-04-26 01:55:57 +00:00
Kit LangtonandGitHub 7cab6824d1 feat(httpapi): bridge experimental tool routes (#24407) 2026-04-25 21:54:58 -04:00
opencode-agent[bot] f77277a69e chore: generate 2026-04-25 23:28:25 +00:00
Kit LangtonandGitHub 450128f9be feat(httpapi): bridge mcp oauth endpoints (#24405) 2026-04-25 19:27:11 -04:00
opencode-agent[bot] 3e35c974a4 chore: generate 2026-04-25 23:17:18 +00:00
Kit LangtonandGitHub a14c22d4e9 feat(httpapi): bridge mcp control endpoints (#24403) 2026-04-25 19:16:19 -04:00
Kit LangtonandGitHub 58c65874ba feat(httpapi): bridge project update endpoint (#24398) 2026-04-25 18:55:49 -04:00
opencode-agent[bot] 27b0877714 chore: generate 2026-04-25 22:43:13 +00:00
Kit LangtonandGitHub 5904f599a9 feat(httpapi): bridge project git init endpoint (#24394) 2026-04-25 18:42:02 -04:00
Kit LangtonandGitHub df9e1d9854 feat(httpapi): bridge config update endpoint (#24387) 2026-04-25 17:52:34 -04:00
opencode-agent[bot] 75a22f82bd chore: generate 2026-04-25 19:36:15 +00:00
Kit LangtonandGitHub a369130226 feat(httpapi): bridge worktree mutations (#24371) 2026-04-25 15:35:15 -04:00
opencode-agent[bot] 474024f9e6 chore: generate 2026-04-25 19:25:41 +00:00
Kit LangtonandGitHub b4f4134e81 feat(httpapi): bridge instance dispose endpoint (#24368) 2026-04-25 15:24:07 -04:00
Kit LangtonandGitHub cd64b67038 feat(tui): show /connect tip when user has no models configured (#24014) 2026-04-25 15:01:41 -04:00
opencode-agent[bot] 9af46df535 chore: generate 2026-04-25 18:56:31 +00:00
Kit LangtonandGitHub b749866f0b feat(httpapi): bridge worktree read endpoint (#24366) 2026-04-25 14:55:29 -04:00
opencode-agent[bot] 60fa708f0b chore: update nix node_modules hashes 2026-04-25 18:49:27 +00:00
opencode-agent[bot] 3b74077437 chore: generate 2026-04-25 18:47:06 +00:00
Kit LangtonandGitHub 95d4bb2130 feat(httpapi): bridge experimental read endpoints (#24365) 2026-04-25 14:46:06 -04:00
Dax Raad f5dce6d960 core: move npm service to core package for shared dependency management 2026-04-25 14:36:15 -04:00
Dax Raad 1e98167b0e core: move cross-spawn-spawner to root and remove unused types
The cross-spawn-spawner module has been moved from src/effect/ to src/
to simplify the core package structure. The src/types.d.ts file which
contained unused type declarations has also been removed. All imports
throughout the codebase have been updated to reflect the new location.

This change reduces the package's internal complexity by flattening the
module hierarchy and removing dead code, making future maintenance easier.
2026-04-25 14:30:16 -04:00
Dax Raad 3eee2f6afa core: move cross-spawn-spawner from opencode to core package
Moved the cross-spawn-spawner module from packages/opencode to packages/core
to enable code sharing across the monorepo. This consolidates the process
spawning infrastructure into the core package so other packages can use
cross-platform child process spawning without duplicating the implementation.

Updated all import statements across the codebase to reference the new
location (@opencode-ai/core/effect/cross-spawn-spawner). Removed the
local copy from the opencode package along with its tests.
2026-04-25 14:23:17 -04:00
opencode-agent[bot] ff4b60e1f3 chore: generate 2026-04-25 18:14:26 +00:00
Aiden Cline f91b73b938 ci: fix model name 2026-04-25 14:13:19 -04:00
Kit LangtonandGitHub 05661c60ff feat(httpapi): bridge file search endpoints (#24356) 2026-04-25 14:12:54 -04:00
Kit LangtonandGitHub 625aca49de feat(tui): read Zed editor context from state db (#24352) 2026-04-25 18:10:58 +00:00
opencode-agent[bot] 3bc0c36ace chore: generate 2026-04-25 18:01:35 +00:00
Kit LangtonandGitHub eb0219988b feat(httpapi): bridge catalog read endpoints (#24353) 2026-04-25 14:00:30 -04:00
Dax Raad 705f792e87 core: move Global module to @opencode-ai/core for centralized path management
Move the Global module from packages/opencode/src/global to packages/core/src/global
to provide a unified location for managing XDG directories and application paths.
This eliminates duplicate path definitions across packages and ensures consistent
access to data, config, cache, state, log, and bin directories throughout the codebase.
2026-04-25 13:52:32 -04:00
Aiden ClineandGitHub 716cf74190 ci: adjust review flow (#24355) 2026-04-25 13:52:19 -04:00
opencode-agent[bot] fc8dae2422 chore: update nix node_modules hashes 2026-04-25 17:50:26 +00:00
opencode-agent[bot] 27353df0cc chore: generate 2026-04-25 17:31:57 +00:00
Dax Raad 1a734adb4d core: consolidate shared infrastructure into core package
Moves effect logging, observability, runtime utilities, flags, installation
version info, and process utilities from opencode to core package. This
enables better code sharing across packages and establishes core as the
single source of truth for foundational utilities.

All internal imports updated to use @opencode-ai/core paths for consistency.
2026-04-25 13:30:37 -04:00
Kit LangtonandGitHub a9740b9133 fix(config): preserve permission order with Effect decode (#24308) 2026-04-25 13:30:12 -04:00
opencode-agent[bot] 62651c7114 chore: update nix node_modules hashes 2026-04-25 15:16:42 +00:00
DaxandGitHub 1d728fc627 feat: add startup debug command (#24310) 2026-04-25 15:08:19 +00:00
DaxandGitHub 62ef2a2207 refactor: rename shared package to core (#24309) 2026-04-25 10:59:17 -04:00
DaxandGitHub 37aa8442dc refactor: remove lazy cross-spawn runtime (#24305) 2026-04-25 14:46:16 +00:00
opencode-agent[bot] 5b0e828c10 chore: generate 2026-04-25 14:43:27 +00:00
Kit LangtonandGitHub d5bfaef53d feat(httpapi): bridge instance read endpoints (#24258) 2026-04-25 10:42:31 -04:00
opencode bad732c26a sync release versions for v1.14.25 2026-04-25 14:37:01 +00:00
Dax Raad 1b92c95425 core: permission config schema now provides full IntelliSense for all tool permission keys
The permission configuration previously used a generic record type that didn't offer editor completions. Updated the schema to explicitly list all tool permission keys (read, edit, glob, grep, list, bash, task, external_directory, lsp, skill, todowrite, question, webfetch, websearch, codesearch, doom_loop) with proper types, enabling autocomplete when editing permission files.
2026-04-25 09:48:09 -04:00
Dax Raad d748c71845 ci: centralize opentui dependencies in workspace catalog
Use catalog references for @opentui/core, @opentui/solid, and opentui-spinner
across packages to ensure consistent versions and simplify updates.
2026-04-25 09:41:30 -04:00
opencode-agent[bot] fc88ed1262 chore: generate 2026-04-25 13:19:42 +00:00
DaxandGitHub 66f93035b0 fix permission config order (#24222) 2026-04-25 13:18:42 +00:00
Simon KleeandGitHub 9ff999cc2b tool/lsp: include request details in permission metadata (#24139) 2026-04-25 14:21:35 +02:00
Kit LangtonandGitHub 4877eccc0d Fix shell cwd after login startup (#24215) 2026-04-25 01:14:52 -04:00
Aiden ClineandGitHub f7d527cd28 ci: adjust auto close issue script to use not planned instead of completed (#24253) 2026-04-25 00:47:36 -04:00
opencode-agent[bot] e29058c346 chore: update nix node_modules hashes 2026-04-25 03:04:44 +00:00
Maddison HellstromandGitHub 49894330d9 fix(build): add prettier to devDependencies (#23255) 2026-04-24 22:47:05 -04:00
Luke ParkerandGitHub cdc7d5f2ea chore: group beta PR logs (#24236) 2026-04-25 10:42:33 +10:00
opencode-agent[bot] ec201623fb chore: generate 2026-04-25 00:34:02 +00:00
Luke ParkerandGitHub 386091b79a fix: validate beta before pushing (#24230) 2026-04-25 10:32:41 +10:00
Luke ParkerandGitHub 1e4b7b5451 Add Roslyn support for Razor and C# scripts (#24228) 2026-04-25 10:25:57 +10:00
opencode-agent[bot] 5cd178ba70 chore: generate 2026-04-24 22:05:23 +00:00
Kit LangtonandGitHub 97eb9fdee8 test(httpapi): cover hono bridge middleware (#24216) 2026-04-24 18:03:51 -04:00
Kit LangtonandGitHub 5a04de231e refactor(ripgrep): migrate result schemas to effect (#24213) 2026-04-24 17:42:52 -04:00
Kyle AltendorfGitHubAiden ClineLuke ParkerBrendan Allanopencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>Shoubhit Dash
bb3509b5ff fix(opencode): clarify git amend condition to require verifying commit landed (#19937)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Luke Parker <10430890+Hona@users.noreply.github.com>
Co-authored-by: Brendan Allan <14191578+Brendonovich@users.noreply.github.com>
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: Shoubhit Dash <shoubhit2005@gmail.com>
2026-04-24 17:42:10 -04:00
Aiden ClineandGitHub 4a67905266 fix: ensure gpt-5.5 compacts at correct context size when using openai oauth (#24212) 2026-04-24 17:39:06 -04:00
opencode-agent[bot] c4e33d3168 chore: generate 2026-04-24 21:38:31 +00:00
Aiden ClineandGitHub 872cdff2ab ignore: denounce ai spammer 2026-04-24 17:37:28 -04:00
Kit LangtonandGitHub 361d7001d0 Clarify HttpApi migration plan (#24211) 2026-04-24 17:36:49 -04:00
opencode-agent[bot] 7a02eee0f4 chore: generate 2026-04-24 21:13:00 +00:00
Kit LangtonandGitHub cf45a8d807 refactor(schema): decode effect schemas directly (#24169) 2026-04-24 17:11:52 -04:00
Kit LangtonandGitHub 435becbea0 Refactor HttpApi auth middleware wiring (#24168) 2026-04-24 17:11:07 -04:00
Frank 0405bc74e9 zen: gpt-5.5 2026-04-24 14:57:23 -04:00
Frank 4dab2a8555 zen: gpt-5.5 2026-04-24 14:43:04 -04:00
Frank 3776d85e15 zen: gpt-5.5 2026-04-24 14:39:24 -04:00
Frank d01ad4c499 zen: gpt-5.5 2026-04-24 14:05:29 -04:00
opencode 28025a0f36 sync release versions for v1.14.24 2026-04-24 15:53:03 +00:00
opencode-agent[bot] 1220f784fe chore: generate 2026-04-24 15:48:08 +00:00
Frank 28f7d31e72 zen: deepseek v4 pro 2026-04-24 11:46:01 -04:00
opencode-agent[bot] 66936b0fff chore: update nix node_modules hashes 2026-04-24 15:45:10 +00:00
SebastianandGitHub 3a5507de95 Use OpenTUI theme detection for initial TUI mode, again (#23846) 2026-04-24 17:28:07 +02:00
Aiden ClineandGitHub 86715fecc4 fix: ensure assistant messages always have reasoning on them for deepseek (#24180) 2026-04-24 11:10:47 -04:00
07akioniandGitHub 3062d3e070 fix: use existingModel as fallback for interleaved field (#24172) 2026-04-24 10:41:41 -04:00
opencode-agent[bot] d89bfc32ac chore: generate 2026-04-24 13:20:23 +00:00
Kit LangtonandGitHub 011c23761b feat(httpapi): bridge mcp status endpoint (#24100) 2026-04-24 09:18:57 -04:00
opencode a8c8d2dd79 sync release versions for v1.14.23 2026-04-24 13:17:13 +00:00
Kit LangtonandGitHub 9f7ecd65e5 feat(httpapi): bridge file read endpoints (#24098) 2026-04-24 09:12:05 -04:00
Aiden ClineandGitHub f8e939d96f fix: support max for deepseek (#24163) 2026-04-24 08:48:52 -04:00
923af96d26 fix: preserve empty reasoning_content for DeepSeek V4 thinking mode (#24146)
Co-authored-by: Simon Klee <hello@simonklee.dk>
2026-04-24 08:42:57 -04:00
Aiden ClineandGitHub a882e958b3 fix: deepseek variants (#24157) 2026-04-24 08:34:48 -04:00
Simon KleeandGitHub 2cda629c8d test(prompt): align shell placeholder expectation (#24147) 2026-04-24 13:00:11 +02:00
Brendan AllanandGitHub f033d2d8fb fix(app): conditionally show model variant selector (#24115) 2026-04-24 15:20:53 +08:00
Frank a4bd88ab97 zen: deepseek v4 pro 2026-04-24 02:09:14 -04:00
Frank f4616c8268 sync 2026-04-24 02:05:22 -04:00
Brendan AllanandGitHub 4712f0f3c1 feat(prompt): add shell mode UI with cancel button, custom icon, and example placeholder (#24105) 2026-04-24 14:04:55 +08:00
opencode-agent[bot] 6c1268f3b1 chore: generate 2026-04-24 05:28:43 +00:00
Brendan AllanandGitHub 2e156b8990 fix(desktop): avoid relaunching without installing updates (#23806) 2026-04-24 13:27:36 +08:00
Brendan AllanandGitHub 3bfe6a1ef6 ci: add platform-specific bun install flags (#23822) 2026-04-24 04:50:34 +00:00
opencode-agent[bot] 5c5069b622 chore: generate 2026-04-23 21:44:44 +00:00
f8c6ddd4cb feat(truncate): allow configuring tool output truncation limits (#23770)
Co-authored-by: rgs_ramp <rgs@ramp.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-23 17:43:33 -04:00
Kit LangtonandGitHub e50a688ca3 feat(httpapi): bridge workspace read endpoints (#24062) 2026-04-23 17:32:02 -04:00
Aiden ClineandGitHub 334ab4707c fix: account for additional openai retry case (#24063) 2026-04-23 17:31:43 -04:00
Aiden ClineandGitHub 87321942fe chore: update copilot readme to symlink to an agents md to prevent dumbass agents from touching these files (#24057) 2026-04-23 17:08:46 -04:00
opencode-agent[bot] a771859362 chore: generate 2026-04-23 20:50:19 +00:00
Kit LangtonandGitHub 31d01d404a refactor(control-plane): migrate workspace DTO schemas (#24056) 2026-04-23 16:48:56 -04:00
Kit LangtonandGitHub 814e83ffec docs: update effect schema migration tracker (#24054) 2026-04-23 16:23:45 -04:00
opencode-agent[bot] 4c3e65c877 chore: generate 2026-04-23 20:20:26 +00:00
James LongandGitHub 98ea5b6e7e feat(tui): support builtin protocol for handling context from editors (#24034) 2026-04-23 16:19:19 -04:00
opencode-agent[bot] 3f8c659056 chore: generate 2026-04-23 20:10:56 +00:00
Kit LangtonandGitHub 3910a6e527 refactor(tool): migrate tool framework + all 18 built-in tools to Effect Schema (#23244) 2026-04-23 16:09:34 -04:00
opencode-agent[bot] 24892559ae chore: generate 2026-04-23 19:38:54 +00:00
Kit LangtonandGitHub cd93533b1f refactor(bus): migrate BusEvent to Effect Schema (#24040) 2026-04-23 15:37:44 -04:00
Kit LangtonandGitHub 0590452456 refactor(schema): use Schema.Int and consolidate PositiveInt/NonNegativeInt (#24029) 2026-04-23 13:22:48 -04:00
Kit LangtonandGitHub 93940a1859 refactor(provider): migrate provider domain to Effect Schema (#24027) 2026-04-23 13:17:33 -04:00
Frank 1e439b8226 sync 2026-04-23 13:13:29 -04:00
Kit LangtonandGitHub 8b2f8355b2 docs(schema): mark sync/index.ts migrated with compat-bridge note (#24024) 2026-04-23 12:54:46 -04:00
opencode-agent[bot] aed03078f8 chore: generate 2026-04-23 16:44:32 +00:00
Kit LangtonandGitHub c50d65b4d6 refactor(sync): make session events schema-first (#24019) 2026-04-23 12:43:08 -04:00
opencode-agent[bot] 353532b1c1 chore: generate 2026-04-23 16:00:01 +00:00
Shoubhit DashandGitHub 9df7c78ebe fix(npm): respect npmrc for version lookups (#24016) 2026-04-23 21:28:54 +05:30
opencode eb7555d3c6 sync release versions for v1.14.22 2026-04-23 15:56:49 +00:00
opencode-agent[bot] 2cd89d64e9 chore: generate 2026-04-23 15:31:16 +00:00
Kit LangtonandGitHub 0517ab4695 refactor(session): migrate session domain to Effect Schema (#24005) 2026-04-23 11:30:02 -04:00
James LongandGitHub bbf67d0fff fix(tui): render all non-synthetic text parts of a user message (#24009) 2026-04-23 11:24:40 -04:00
Shoubhit DashandGitHub 38deb0f3ee fix(npm): respect npmrc config (#24001) 2026-04-23 19:54:01 +05:30
Simon KleeandGitHub 9b6db08d21 chore: add to TEAM_MEMBERS (#23975) 2026-04-23 13:02:40 +02:00
opencode-agent[bot] 3ae74cb047 chore: generate 2026-04-23 10:58:14 +00:00
Brendan AllanandGitHub 6002500bc0 feat(project): add icon_url_override field to projects (#23955) 2026-04-23 18:57:04 +08:00
Brendan AllanandGitHub 785f3589ab fix: add keyed prop to Show components for proper reactivity (#23935) 2026-04-23 17:32:01 +08:00
Frank a419f1c50f zen: hy3 preview 2026-04-23 02:56:41 -04:00
opencode 871789ce64 sync release versions for v1.14.21 2026-04-23 05:45:21 +00:00
Brendan AllanandGitHub df27baa00d refactor: remove redundant pending check from working memo (#23929) 2026-04-23 13:15:02 +08:00
Aiden ClineandGitHub 9730008543 tweak: codex model logic (#23925) 2026-04-23 00:29:56 -04:00
Luke ParkerandAiden Cline ac26394fcb fix(beta): PR resolvers/smoke check should typecheck all pacakges (#23913) 2026-04-23 00:25:41 -04:00
Luke ParkerandAiden Cline 6387b35a2d log session sdk errors (#23652) 2026-04-23 00:25:41 -04:00
opencode-agent[bot]andAiden Cline 1cd4c92242 chore: generate 2026-04-23 00:25:40 -04:00
Luke ParkerandAiden Cline e383df4b17 feat: support pull diagnostics in the LSP client (C#, Kotlin, etc) (#23771) 2026-04-23 00:25:40 -04:00
Caleb NortonandAiden Cline 58db41b4b9 chore: update nix bun version (#23881) 2026-04-23 00:25:40 -04:00
opencode-agent[bot]andAiden Cline 5d133f2785 chore: generate 2026-04-23 00:25:39 -04:00
JackandAiden Cline e9b1d3b940 docs: add MiMo V2.5 to Go pages (#23876) 2026-04-23 00:25:39 -04:00
Steven T. CramerandAiden Cline 3a082a0efd fix(project): use git common dir for bare repo project cache (#19054) 2026-04-23 00:25:39 -04:00
opencode-agent[bot]andAiden Cline 504fd1b373 chore: generate 2026-04-23 00:25:39 -04:00
Shoubhit DashandAiden Cline 574b2c2170 fix(session): improve session compaction (#23870) 2026-04-23 00:25:38 -04:00
opencode-agent[bot]andAiden Cline fa8b7bc4d2 chore: generate 2026-04-23 00:25:38 -04:00
Shoubhit DashandAiden Cline 6196b81e0a fix(tui): fail fast on invalid session startup (#23837) 2026-04-23 00:25:38 -04:00
Brendan AllanandAiden Cline d884ab73d5 fix: consolidate project avatar source logic (#23819) 2026-04-23 00:25:37 -04:00
opencode-agent[bot]andAiden Cline 71d196d3cd chore: generate 2026-04-23 00:25:37 -04:00
Luke ParkerandAiden Cline 20756e0ee4 test: fix cross-spawn stderr race on Windows CI (#23808) 2026-04-23 00:25:37 -04:00
opencode-agent[bot]andAiden Cline 894e638914 chore: generate 2026-04-23 00:25:37 -04:00
Luke ParkerandAiden Cline 8113a4360e fix: preserve BOM in text tool round-trips (#23797) 2026-04-23 00:25:36 -04:00
opencode-agent[bot]andAiden Cline c819804638 chore: update nix node_modules hashes 2026-04-23 00:25:36 -04:00
Brendan AllanandAiden Cline 06066dbb7b fix(app): improve icon override handling in project edit dialog (#23768) 2026-04-23 00:25:36 -04:00
Luke ParkerandAiden Cline 69b8ea0d66 chore: bump Bun to 1.3.13 (#23791) 2026-04-23 00:25:36 -04:00
opencode-agent[bot] b0455583aa chore: generate 2026-04-22 03:41:42 +00:00
Kit LangtonandGitHub ed802fd121 refactor(core): migrate MessageV2 errors to Schema-backed named errors (#23764) 2026-04-21 23:40:32 -04:00
Kit LangtonandGitHub 1593c3ed16 refactor(core): migrate MessageV2 internal Cursor to Effect Schema (#23763) 2026-04-21 23:28:33 -04:00
Kit LangtonandGitHub e89543811c refactor(core): migrate MessageV2 message DTOs (User/Assistant/Part/Info/WithParts) to Effect Schema (#23757) 2026-04-21 23:26:12 -04:00
opencode-agent[bot] 1a76799fd8 chore: generate 2026-04-22 03:18:25 +00:00
Kit LangtonandGitHub fa623964a2 refactor(core): migrate MessageV2 part leaves + ToolPart to Effect Schema (#23756) 2026-04-21 23:17:23 -04:00
Frank 628102ad04 zen: handle alibaba format 2026-04-21 23:13:44 -04:00
Kit LangtonandGitHub ad7ae7353f refactor(core): derive all schema.ts leaves' .zod via effect-zod walker (#23754) 2026-04-21 22:51:18 -04:00
NN708andGitHub 8043cfa68d fix(desktop): update desktop file and MetaInfo file (#14933) 2026-04-22 07:19:04 +08:00
opencode-agent[bot] d2181e9273 chore: generate 2026-04-21 22:04:16 +00:00
5e9fb3cc90 feat: replace csharp-ls with roslyn-language-server (#14463)
Co-authored-by: Mathews <Mathews.Bryan@cincpro.com>
2026-04-21 22:03:09 +00:00
Kit LangtonandGitHub 2da6d860e0 refactor(core): derive provider schema .zod via effect-zod walker (#23753) 2026-04-21 17:49:24 -04:00
Kit LangtonandGitHub df0c1f649c refactor(core): migrate MessageV2 tool state schemas to Effect Schema (#23752) 2026-04-21 17:47:50 -04:00
Kit LangtonandGitHub d6dea3f3e0 chore(core): clean up after ConfigPermission Effect Schema migration (#23749) 2026-04-21 17:40:54 -04:00
Kit LangtonandGitHub 0bcf734a67 migrate Snapshot schemas to Effect Schema (#23747) 2026-04-21 17:37:27 -04:00
opencode-agent[bot] b1c3095edd chore: generate 2026-04-21 21:34:17 +00:00
Kit LangtonandGitHub b0f565b74a refactor(core): migrate ConfigPermission.Info to Effect Schema canonical (#23740) 2026-04-21 17:33:13 -04:00
Kit LangtonandGitHub 2ae64f426b refactor(core): migrate MessageV2.Format to Effect Schema (#23744) 2026-04-21 17:30:08 -04:00
Kit LangtonandGitHub 7933657135 migrate LSP data schemas to Effect Schema (#23745) 2026-04-21 17:26:50 -04:00
Frank caaddf0964 zen: ling 2.6 free 2026-04-21 17:06:31 -04:00
Ruben De SmetandGitHub 1a20703469 feat: add Mistral Small reasoning variant support (issue #19479) (#23735) 2026-04-21 16:45:06 -04:00
github-actions[bot] 8751f48a75 Update VOUCHED list
https://github.com/anomalyco/opencode/issues/23735#issuecomment-4291498854
2026-04-21 20:17:13 +00:00
Aiden ClineandGitHub 58232d896e fix: dont show variants for kimi models that dont support them (#23696) 2026-04-21 15:33:35 -04:00
cd6415f332 fix(tui): don't check for version upgrades if it's disabled by the user (#20089)
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-21 15:30:15 -04:00
opencode c9fb8d0ce7 sync release versions for v1.14.20 2026-04-21 19:07:36 +00:00
opencode-agent[bot] 1e1a500603 chore: generate 2026-04-21 18:08:04 +00:00
Kit LangtonandGitHub ecc06a3d8f refactor(core): make Config.Info canonical Effect Schema (#23716) 2026-04-21 14:06:47 -04:00
opencode-agent[bot] 3205f122eb chore: update nix node_modules hashes 2026-04-21 16:57:27 +00:00
Aiden ClineandGitHub e95474df05 fix: revert parts of a824064c4 which caused system theme regression (#23714) 2026-04-21 12:08:12 -04:00
Kit LangtonandGitHub 96a534d8c6 feat(core): bridge GET /config through experimental HttpApi (#23712) 2026-04-21 16:05:23 +00:00
Kit LangtonandGitHub 9579429276 test(opencode): consolidate session prompt tests into Effect style (#23710) 2026-04-21 15:54:40 +00:00
Aiden ClineandGitHub 2486621ca1 chore: kill unused tool (#23701) 2026-04-21 11:31:20 -04:00
James LongandGitHub b5acc2203c fix(core): fix permissions routing when using remote workspace (#23593) 2026-04-21 14:42:54 +00:00
Brendan AllanandGitHub 8cc2c81d57 fix(app): prevent prompt input animations from rerunning on every render (#23676) 2026-04-21 19:12:32 +08:00
opencode-agent[bot] 8d2d12d9c6 chore: generate 2026-04-21 11:11:54 +00:00
Brendan AllanandGitHub 811a7e9a8b feat(app): allow disabling progress bar in settings (#23674) 2026-04-21 19:10:50 +08:00
Brendan AllanandGitHub febadc5589 fix(ui): correct diff render condition logic (#23670) 2026-04-21 18:49:04 +08:00
Luke ParkerandGitHub 92c005866b fix(core): use file:// URLs for local dynamic import() on Windows+Node (#23639) 2026-04-21 07:54:53 +00:00
OpeOginniandGitHub 224548d87d fix(desktop): adjust layout properties in DialogSelectServer component (#23589) 2026-04-21 14:38:56 +08:00
Frank 8a7bb7c6a9 zen: tpm routing 2026-04-21 02:36:40 -04:00
Brendan AllanandGitHub 22d33c57af fix(app): properly wrap produce calls in setProjects (#23638) 2026-04-21 14:11:23 +08:00
1e0137f624 go: promote kimi k2.6 usage limits (#23634)
Co-authored-by: Frank <frank@anoma.ly>
2026-04-21 02:01:52 -04:00
Brendan AllanandGitHub 38e2f4cdda fix(desktop-electron): add CORS headers to main window webRequest (#23633) 2026-04-21 13:32:31 +08:00
Frank bd54b68c12 zen: m2.7 & k2.6 2026-04-21 01:15:07 -04:00
opencode-agent[bot] a08aa21cb3 chore: generate 2026-04-21 04:40:02 +00:00
Brendan AllanandGitHub eb9906420f refactor(desktop-electron): enable contextIsolation and sandbox (#23523) 2026-04-21 12:38:59 +08:00
opencode-agent[bot] 4964ce480c chore: generate 2026-04-21 04:37:57 +00:00
Brendan AllanandGitHub e5687d646c electron: use custom oc:// protocol for renderer windows (#23516) 2026-04-21 12:36:56 +08:00
opencode-agent[bot] a38d53fe2f chore: generate 2026-04-21 02:42:45 +00:00
Frank 6278ce51ce zen: tpm routing 2026-04-20 22:41:36 -04:00
opencode-agent[bot] 53b0084ce2 chore: generate 2026-04-21 02:22:12 +00:00
Frank f74a255ca9 zen: tpm routing 2026-04-20 22:21:06 -04:00
Frank 3e8abac625 sync 2026-04-20 17:01:27 -04:00
opencode-agent[bot] 65e99fcc2e chore: generate 2026-04-20 20:06:22 +00:00
Frank bad025eba9 sync 2026-04-20 16:05:04 -04:00
opencode-agent[bot] 06dde3afd3 chore: generate 2026-04-20 19:04:24 +00:00
Frank ad65af28e7 zen: tpm routing 2026-04-20 15:02:54 -04:00
opencode-agent[bot] bd1bdc4f04 chore: generate 2026-04-20 18:29:48 +00:00
James LongandGitHub debcff2b6b feat(core): add debug workspace server (#23590) 2026-04-20 18:27:58 +00:00
opencode-agent[bot] 8b3323708d chore: generate 2026-04-20 17:43:32 +00:00
James MurdzaandGitHub 3406f18746 fix(plugin): add env parameter to WorkspaceAdaptor.create type (#23235) 2026-04-20 13:41:38 -04:00
opencode-agent[bot] 7e576eea41 chore: generate 2026-04-20 15:59:40 +00:00
JackandGitHub d68ebee555 docs(go): add Kimi K2.6 to Go and Zen content (#23558) 2026-04-20 23:58:32 +08:00
Frank ae7a3518f7 zen: tpm based routing 2026-04-20 09:56:26 -04:00
Chris YangandGitHub 16caaa2229 fix(app): fall back to icon.url in sidebar avatar (#18747) 2026-04-20 19:32:54 +08:00
黑墨水鱼andGitHub 91468fe455 fix(ui): use parentID matching instead of positional scan for assistant messages (#23093) 2026-04-20 07:35:06 +00:00
opencode 7c6948cf6f sync release versions for v1.14.19 2026-04-20 07:21:46 +00:00
Luke ParkerandGitHub 7a568a457f fix: defer MessageV2.Assistant.shape access to break circular dep in compiled binary (#23495) 2026-04-20 06:39:13 +00:00
opencode-agent[bot] 3ddc69ec2b chore: update nix node_modules hashes 2026-04-20 06:36:01 +00:00
opencode-agent[bot] f3d5a71620 chore: generate 2026-04-20 06:07:28 +00:00
Aiden ClineandGitHub c6c56ac2cf tweak: rename tail_tokens -> preserve_recent_tokens (#23491) 2026-04-20 01:06:29 -05:00
Aiden ClineandGitHub e539efe2b9 fix: patch arborist to get around bun bug (#23460) 2026-04-20 00:49:46 -05:00
Brendan AllanandGitHub 687b758882 app: better loading (#23489) 2026-04-20 05:17:37 +00:00
opencode-agent[bot] 84e322b0fd chore: generate 2026-04-20 05:15:29 +00:00
Aiden ClineandGitHub 8bc4f91fd9 fix: parallel edits sometimes would override each other (#23483) 2026-04-20 00:14:21 -05:00
Brendan AllanandGitHub 93e633fb7d refactor(app): move QueryProvider to AppInterface (#23484) 2026-04-20 12:51:34 +08:00
opencode-agent[bot] cbe702c09d chore: generate 2026-04-20 04:40:12 +00:00
Luke ParkerandGitHub a7a85c94b8 fix(core): fix Windows managed install and bump ripgrep to 15.1.0 for ARM64 support (#23477) 2026-04-20 14:39:15 +10:00
6e0178655b feat(provider): add NVIDIA to popular providers, docs, and attribution headers (#22927)
Co-authored-by: Kit Langton <kit.langton@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-19 19:57:49 -05:00
Dax Raad e4be557928 ci: skip beta smoke fixes for now 2026-04-19 20:01:30 -04:00
Dax Raad b9640fc7e4 core: fix session compaction test to properly enable prune config option 2026-04-19 19:53:43 -04:00
opencode-agent[bot] 29f05cb1ee chore: generate 2026-04-19 23:48:44 +00:00
Dax Raad 48acab48ad ci: skip Docker builds during preview releases to save time 2026-04-19 19:47:29 -04:00
Dax Raad 5ae74aa881 Merge branch 'nxl/improve-compaction-strategy' into dev 2026-04-19 19:38:46 -04:00
Dax Raad 6eddf08244 flip toolcall prune defaults 2026-04-19 19:34:44 -04:00
opencode-agent[bot] 9c7e52b8a1 chore: update nix node_modules hashes 2026-04-19 22:52:42 +00:00
SebastianandGitHub a824064c4c stabilize TUI theme persistence and KV writes (#23188) 2026-04-20 00:10:31 +02:00
opencode-agent[bot] 33b2795cc8 chore: generate 2026-04-19 09:47:36 +00:00
Luke ParkerandGitHub 10bd044c55 feat: add terminal font settings and built-in Nerd Font (#23391) 2026-04-19 19:46:34 +10:00
opencode c09bcfe531 sync release versions for v1.14.18 2026-04-19 09:36:56 +00:00
Brendan AllanandGitHub 83227be0ca fix(version): remove --target flag from beta release creation (#23403) 2026-04-19 17:05:03 +08:00
opencode-agent[bot] 8ee47a0533 chore: update nix node_modules hashes 2026-04-19 08:29:51 +00:00
Brendan AllanandGitHub a546e88f37 fix(desktop-electron): run JSON migration before spawning sidecar (#23396) 2026-04-19 15:53:47 +08:00
opencode-agent[bot] e998c9e9cb chore: update nix node_modules hashes 2026-04-19 07:35:27 +00:00
889087c966 fix(ripgrep): restore native rg backend (#22773)
Co-authored-by: LukeParkerDev <10430890+Hona@users.noreply.github.com>
2026-04-19 06:58:15 +00:00
opencode-agent[bot] 7f3b64c7c4 chore: update nix node_modules hashes 2026-04-19 06:38:10 +00:00
DaxandGitHub e60a6e3a82 fix: change Free download button text to Download (#23388) 2026-04-19 02:19:40 -04:00
opencode-agent[bot] 135c8f0e99 chore: generate 2026-04-19 05:59:31 +00:00
opencode-agent[bot] f02504bb80 chore: generate 2026-04-19 05:58:31 +00:00
Dax Raad 40834fdf2f core: allow users with credits but no payment method to access zen mode 2026-04-19 01:57:16 -04:00
Aiden ClineandGitHub fc0588954b fix (#23385) 2026-04-19 00:45:44 -05:00
opencode-agent[bot] 75960e3bf3 chore: generate 2026-04-19 04:25:23 +00:00
Ariane EmoryandGitHub f14ac472a3 docs: document --dangerously-skip-permissions CLI flag (#23371) 2026-04-18 23:24:23 -05:00
opencode-agent[bot] 9ed93715ef chore: update nix node_modules hashes 2026-04-19 03:40:53 +00:00
Luke ParkerandGitHub b34ca44abe fix incorrect config directory by lazily loading electron-store (#23373) 2026-04-19 13:06:07 +10:00
opencode 40ba8f3570 sync release versions for v1.14.17 2026-04-19 03:02:14 +00:00
Luke ParkerandGitHub e543acf923 chore: bump electron and fix taskbar icon (#23368) 2026-04-19 02:35:02 +00:00
Dax Raad d183568644 core: ensure executable permissions are set before Docker builds
Fixes an issue where GitHub artifact downloads could strip executable bits
from binaries, causing Docker builds to fail when using unpacked dist files
directly rather than published tarballs. The chmod now runs before the
publish check to guarantee binaries are executable.
2026-04-18 22:32:53 -04:00
Dax Raad f27eb8f09e fix plugins reinstalling too often 2026-04-18 20:02:24 -04:00
Dax Raad ad0545335a ci 2026-04-18 19:29:21 -04:00
Dax Raad cfbbae7323 ci 2026-04-18 18:59:44 -04:00
Dax Raad 940f971ca0 ci: fix 2026-04-18 18:56:14 -04:00
Aiden ClineandGitHub 78ca49a1bc test: fix bedrock test (#23351) 2026-04-18 17:46:15 -05:00
1d54b0e540 Stefan/enterprise forms waitlist (#23158)
Co-authored-by: Ryan Vogel <me@ryan.ceo>
2026-04-18 18:30:28 -04:00
opencode-agent[bot] 7e971d8302 chore: generate 2026-04-18 21:37:45 +00:00
Frank 54b3b3fe05 zen: redeem go 2026-04-18 17:33:28 -04:00
Frank 9d012b0621 zen: redeem credit 2026-04-18 17:33:28 -04:00
opencode-agent[bot] fbb0a93e12 chore: update nix node_modules hashes 2026-04-18 21:32:47 +00:00
Aiden ClineandGitHub e2e7a8d722 fix: ensure display: summarized is sent by default for bedrock (#23343) 2026-04-18 16:04:00 -05:00
Aiden ClineandGitHub ce7923adaf chore: bump @ai-sdk/amazon-bedrock (#23341) 2026-04-18 16:00:46 -05:00
DaxandGitHub a26d53151b tui: allow full-session forks from the session dialog (#23339) 2026-04-18 20:20:23 +00:00
Dax Raad 5eaef6b758 release: avoid package.json drift during publish 2026-04-18 12:32:23 -04:00
opencode-agent[bot] c5c38cad9c chore: generate 2026-04-18 16:00:01 +00:00
Kit LangtonandGitHub 9918f389e7 fix: detect attachment mime from file contents (#23291) 2026-04-18 11:59:08 -04:00
opencode-agent[bot] dd8c424806 chore: generate 2026-04-18 15:21:48 +00:00
Dax Raad 078d8a07cf core: support OTEL_RESOURCE_ATTRIBUTES environment variable for custom telemetry attributes
Users can now pass custom OpenTelemetry resource attributes via the OTEL_RESOURCE_ATTRIBUTES environment variable (comma-separated key=value format). These attributes are automatically included in all telemetry data sent from both the main process and workspace environments, enabling better observability integration with existing monitoring systems that rely on custom resource tags.
2026-04-18 11:20:29 -04:00
Dax Raad 1ee712e549 core: fix early return when node_modules is missing during package install 2026-04-18 10:42:33 -04:00
Dax Raad 55315bdffa tui: fix sync loading indicator to properly show loading state on startup 2026-04-18 10:39:10 -04:00
Dax Raad 882b8e1e75 core: track retry attempts with detailed error context on assistant entries
users can now see when transient failures occur during assistant responses,
such as rate limits or provider overloads, giving visibility into what
issues were encountered and automatically resolved before the final response
2026-04-18 10:38:35 -04:00
opencode-agent[bot] 95edbc0ae6 chore: generate 2026-04-18 05:49:37 +00:00
Dax Raad 11cd4fb639 core: extract session entry stepping logic into dedicated module
Move the step function from session-entry.ts to session-entry-stepper.ts and remove immer dependency. Add static fromEvent factory methods to Synthetic, Assistant, and Compaction classes for cleaner event-to-entry conversion.
2026-04-18 01:48:21 -04:00
Aiden ClineandGitHub 9c16bd1e30 fix: make skills logic more token efficient (#23253) 2026-04-17 23:51:16 -05:00
opencode-agent[bot] 5e9d5c734e chore: generate 2026-04-18 03:52:28 +00:00
Kit LangtonandGitHub b382d1a467 docs(effect): track schema migration progress with concrete file checklists (#23242) 2026-04-18 03:51:30 +00:00
Kit LangtonandGitHub 23f31475e7 refactor(config): migrate config.ts root Info to Effect Schema (#23241) 2026-04-18 03:44:35 +00:00
OpeOginniandGitHub c0eab9e442 fix(desktop): adjust ui tool diff sticky header offset (#23149) 2026-04-18 11:31:38 +08:00
opencode-agent[bot] 8a1e85d0c8 chore: generate 2026-04-18 03:17:28 +00:00
Kit LangtonandGitHub 2793502db2 refactor(config): migrate agent.ts Info to Effect Schema (#23237) 2026-04-18 03:16:24 +00:00
opencode-agent[bot] 9f7bd0246c chore: generate 2026-04-18 03:05:59 +00:00
Kit LangtonandGitHub a6a4350d10 refactor(config): migrate permission.ts Info to Effect Schema (#23231) 2026-04-18 03:05:06 +00:00
Kit LangtonandGitHub 471b9f4dc4 refactor: use InstanceState context in worktree cleanup paths (#23019) 2026-04-17 23:04:16 -04:00
Kit LangtonandGitHub 24fb9b1296 fix: stop rewriting dev during release publish (#22982) 2026-04-18 02:53:19 +00:00
Kit LangtonandGitHub 3573019916 fix(generate): make openapi output deterministic by formatting in-place (#23228) 2026-04-17 22:31:21 -04:00
Kit LangtonandGitHub fc5b353144 refactor(config): migrate keybinds.ts to Effect Schema (#23227) 2026-04-18 02:28:45 +00:00
Kit LangtonandGitHub 1dd257b76a refactor: use instance state in small services (#23022) 2026-04-18 02:16:15 +00:00
Kit LangtonandGitHub 5fa1673341 refactor: use InstanceState context in File service (#23015) 2026-04-17 22:08:57 -04:00
opencode-agent[bot] daaa1c7e26 chore: generate 2026-04-18 02:03:30 +00:00
Kit LangtonandGitHub 1fae784b81 feat(effect-zod): add ZodPreprocess annotation for pre-parse transforms (#23222) 2026-04-18 02:02:37 +00:00
Aiden ClineandGitHub 81b7b58a5e fix: gh copilot issue w/ haiku (eager_input_streaming not supported) (#23223) 2026-04-17 20:57:48 -05:00
opencode-agent[bot] 866188a643 chore: generate 2026-04-18 01:48:50 +00:00
Kit LangtonandGitHub e6fd57165e refactor: remove ambient instance reads from lsp (#23023) 2026-04-17 21:47:59 -04:00
Kit LangtonandGitHub a5d99e7a3c refactor: pass formatter instance context explicitly (#23020) 2026-04-18 01:22:36 +00:00
opencode-agent[bot] a92c75e5f4 chore: generate 2026-04-18 01:21:01 +00:00
Kit LangtonandGitHub 826fd3350c refactor(config): migrate Server + Layout to Effect Schema (#23216) 2026-04-18 01:20:06 +00:00
Kit LangtonandGitHub 23a2d01282 fix(observability): standardize session telemetry attrs (#23213) 2026-04-17 21:14:23 -04:00
Kit LangtonandGitHub 5181f9b4e1 refactor(config): drop ZodOverride from PositiveInt in provider.ts (#23215) 2026-04-18 01:04:40 +00:00
opencode-agent[bot] f52ae28432 chore: generate 2026-04-18 00:56:33 +00:00
Kit LangtonandGitHub 36119ff173 feat(effect-zod): translate Schema.withDecodingDefault into zod .default() (#23207) 2026-04-17 20:55:38 -04:00
Kit LangtonandGitHub bb90f3bbf9 feat(effect-zod): translate well-known filters into native Zod methods (#23209) 2026-04-17 20:50:36 -04:00
Kit Langtonandopencode 05cdb7c107 refactor(v2): tag session unions and exhaustively match events (#23201) 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode b493dabfe6 docs(effect): refresh migration status specs (#23206) 2026-04-18 00:29:26 +00:00
opencode-agent[bot]andopencode c4816f944e chore: generate 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode 211136e3a8 feat(effect-zod): transform support + walk memoization + flattened checks (#23203) 2026-04-18 00:29:26 +00:00
opencode-agent[bot]andopencode cf0a53c501 chore: generate 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode 2899984819 refactor(config): migrate provider (Model + Info) to Effect Schema (#23197) 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode eafbe5c57c refactor(server): align route-span attrs with OTel semantic conventions (#23198) 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode 7b98f544ff feat(effect-zod): add catchall (StructWithRest) support to the walker (#23186) 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode b5aba5807c feat(tui): show session ID in sidebar on non-prod channels (#23185) 2026-04-18 00:29:26 +00:00
Kit Langtonandopencode d5c4c26b4b feat(server): auto-tag route spans with route params (session.id, message.id, …) (#23189) 2026-04-18 00:29:26 +00:00
opencode a35b8a95c2 release: v1.4.11 2026-04-18 00:29:16 +00:00
DaxandGitHub cded68a2e2 refactor(npm): use object-based package spec for install API (#23181) 2026-04-17 17:30:50 -04:00
Aiden ClineandGitHub 0068ccec35 fix: ensure copilot model list filters out disabled models (#23176) 2026-04-17 16:27:32 -05:00
opencode-agent[bot] 89e8994fd1 chore: generate 2026-04-17 21:08:00 +00:00
Kit LangtonandGitHub 5980b0a5ee feat(effect-zod): add tuple support; migrate config/plugin to Effect Schema (#23178) 2026-04-17 21:06:55 +00:00
opencode-agent[bot] 89029a20ef chore: generate 2026-04-17 21:00:20 +00:00
Kit LangtonandGitHub ce69bd97b9 refactor(config): migrate model-id and command to Effect Schema (#23175) 2026-04-17 20:59:24 +00:00
Kit LangtonandGitHub 999d8651aa feat(server): wrap remaining route handlers in request spans (#23169) 2026-04-17 16:52:40 -04:00
opencode-agent[bot] ed0f022502 chore: generate 2026-04-17 20:50:37 +00:00
Kit LangtonandGitHub b1307d5c2a refactor(config): migrate skills, formatter, console-state to Effect Schema (#23162) 2026-04-17 20:49:36 +00:00
opencode-agent[bot] dc16013b4f chore: generate 2026-04-17 20:47:05 +00:00
Kit LangtonandGitHub e7686dbd64 feat(effect-zod): translate Schema.check filters into zod .superRefine + promote LSP refinement to Effect layer (#23173) 2026-04-17 20:46:05 +00:00
James LongandGitHub 47f553f9ba fix(core): more explicit routing to fix workspace instance issue (#23171) 2026-04-17 16:39:34 -04:00
Kit LangtonandGitHub d11268ece7 refactor(config): migrate permission Action/Object/Rule leaves to Effect Schema (#23168) 2026-04-17 20:35:42 +00:00
Kit LangtonandGitHub 650a13a690 refactor(config): migrate lsp schemas to Effect Schema (#23167) 2026-04-17 20:34:47 +00:00
opencode-agent[bot] 54435325b6 chore: generate 2026-04-17 20:26:43 +00:00
Kit LangtonandGitHub 11fa257549 refactor(config): migrate mcp schemas to Effect Schema.Class (#23163) 2026-04-17 20:25:49 +00:00
Kit LangtonandGitHub 6af8ab0df2 docs(http-api): refresh bridge inventory and clarify Schema.Class vs Struct (#23164) 2026-04-17 16:20:57 -04:00
Kit LangtonandGitHub 984f5ed6eb fix(opencode): skip share sync for unshared sessions (#23159) 2026-04-17 20:15:24 +00:00
opencode-agent[bot]andopencode c2061c6bbf chore: generate 2026-04-17 20:13:34 +00:00
Daxandopencode b708e8431e docs(opencode): annotate plugin loader flow (#23160) 2026-04-17 20:13:34 +00:00
opencode 9b6c397171 release: v1.4.10 2026-04-17 20:13:25 +00:00
opencode-agent[bot] 9b0659d4f9 chore: generate 2026-04-17 19:30:28 +00:00
Kit LangtonandGitHub f83cecaaf6 fix(opencode): untrace streaming event hot paths (#23156) 2026-04-17 15:29:32 -04:00
James LongandGitHub aa05b9abe5 fix(core): pass OTEL config to workspace env (#23154) 2026-04-17 15:25:58 -04:00
Kit LangtonandGitHub 68834cfcc3 fix(opencode): normalize provider metadata and tag otel runs (#23140) 2026-04-17 15:22:08 -04:00
James LongandGitHub 5621373bc2 fix(core): move instance middleware after control plane routes (#23150) 2026-04-17 15:20:11 -04:00
opencode-agent[bot] 88582566bf chore: update nix node_modules hashes 2026-04-17 19:18:55 +00:00
opencode-agent[bot] d6e1362fee chore: generate 2026-04-17 19:15:07 +00:00
James LongandGitHub b275b8580d feat(tui): minor UX improvements for workspaces (#23146) 2026-04-17 15:14:05 -04:00
DaxandGitHub 467be08e67 refactor: consolidate npm exports and trace flock acquisition (#23151) 2026-04-17 18:58:37 +00:00
Aiden ClineandGitHub bbb422d125 chore: bump ai to 6.0.168 and @ai-sdk/gateway to 3.0.104 (#23145) 2026-04-17 13:47:22 -05:00
Dax Raad b1f076558c test: align plugin loader npm mocks
- switch plugin loader tests to the effect npm module
- return Option.none() for mocked npm entrypoints
- keep test fixtures aligned with the current Npm.add contract
2026-04-17 14:33:02 -04:00
Dax Raad 992435aaf8 do not flock until reify 2026-04-17 14:18:48 -04:00
Dax Raad 2f73e73e9d trace npm fully 2026-04-17 14:08:45 -04:00
James LongandGitHub 4c30a78cd9 fix: revert sdk generation script change (#23133) 2026-04-17 13:33:11 -04:00
James LongandGitHub a8c78fc005 fix(core): add historical sync on workspace connect (#23121) 2026-04-17 13:30:09 -04:00
opencode-agent[bot] fcb473ff64 chore: update nix node_modules hashes 2026-04-17 17:25:44 +00:00
James LongandGitHub 797953c88d when generating sdk only format sdk, much faster (#23122) 2026-04-17 13:01:22 -04:00
opencode-agent[bot] ce0cfb0ea5 chore: generate 2026-04-17 16:46:34 +00:00
Kit LangtonandGitHub 13dfe569ef tui: fix agent cycling and prompt metadata polish (#23115) 2026-04-17 12:45:29 -04:00
Aiden ClineandGitHub c491161c0c chore: bump @ai-sdk/anthropic to 3.0.71 and dependents (#23120) 2026-04-17 11:40:24 -05:00
fde3d9133b fix(opencode): pass EXA_API_KEY to websearch tool to avoid rate limits (#16362)
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
2026-04-17 11:28:23 -05:00
Vladimir GlafirovandGitHub 0d582f9d3f chore: bump gitlab-ai-provider to 6.6.0 (#23057) 2026-04-17 11:22:43 -05:00
Dax Raadandopencode 1a59133168 Improve light mode dark mode copy 2026-04-17 16:19:57 +00:00
opencode 803d9eb7ad release: v1.4.9 2026-04-17 16:19:46 +00:00
Dax Raad a27d3c1623 tui: fix session resumption with --session-id flag to navigate after app initialization
Previously when passing a session ID directly, the route was set during initial
render which could cause navigation issues before the router was fully ready.
Now the session navigation happens after initialization completes, ensuring
the TUI properly loads the requested session when users resume with --session-id.
2026-04-17 11:41:24 -04:00
Dax Raad 551216a452 fix incorrect light mode in ghostty 2026-04-17 11:32:17 -04:00
opencode-agent[bot] 38cd3979f2 chore: update nix node_modules hashes 2026-04-17 15:31:50 +00:00
3fe602cda3 feat: add LLM Gateway provider (#7847)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
2026-04-17 10:29:31 -05:00
opencode-agent[bot] 3a4b49095c chore: generate 2026-04-17 15:26:45 +00:00
Jen PersonandGitHub ac5b395c5d docs: adding Mistral to docs as a provider (it is already a provider, just docs update) #23070 (#23072) 2026-04-17 10:25:42 -05:00
OpeOginniandGitHub 8fbbca5f4b fix(opencode): rescrict github copilot opus 4.7 variants to "medium" (#23097) 2026-04-17 10:25:12 -05:00
Brendan AllanandGitHub 2415820ecd fix: conditionally show file tree in beta channel (#23099) 2026-04-17 15:13:59 +00:00
Frank 20103eb97b sync 2026-04-17 10:53:45 -04:00
Dax Raad 10c4ab9a3d roll back opentui 2026-04-17 10:51:02 -04:00
Dax Raad 7e39c9b950 back to opentui 0.1.99 2026-04-17 10:43:17 -04:00
opencode-agent[bot] cc063d4c32 chore: generate 2026-04-17 13:56:17 +00:00
Frank 3707e4a49c zen: routing logic 2026-04-17 09:54:47 -04:00
opencode-agent[bot] cb425ac927 chore: generate 2026-04-17 13:53:11 +00:00
James LongandGitHub 0f80c827ed feat(core): exponential backoff of workspace reconnect (#23083) 2026-04-17 09:52:10 -04:00
Dax Raad fffc496f41 remove log 2026-04-17 09:46:35 -04:00
Brendan Allan a4882290aa Merge branch 'dev' into nxl/improve-compaction-strategy 2026-04-17 11:53:17 +08:00
Shoubhit Dash 42771c1db3 fix(compaction): budget retained tail with media 2026-04-16 17:30:29 +05:30
Shoubhit Dash 2e18a603f0 merge dev 2026-04-16 17:30:14 +05:30
Aiden Cline 9819eb0461 tweak: disable 2026-04-09 23:11:09 -05:00
Shoubhit Dash aa86fb75ad refactor compaction tail selection 2026-04-10 09:36:39 +05:30
Shoubhit Dash 6f5a3d30fd keep recent turns during session compaction 2026-04-10 09:34:01 +05:30
941 changed files with 66288 additions and 18663 deletions
+1
View File
@@ -13,3 +13,4 @@ R44VC0RP
rekram1-node rekram1-node
RhysSullivan RhysSullivan
thdxr thdxr
simonklee
+6
View File
@@ -12,9 +12,13 @@ adamdotdevin
ariane-emory ariane-emory
-atharvau AI review spamming literally every PR -atharvau AI review spamming literally every PR
-borealbytes -borealbytes
-carycooper777
-danieljoshuanazareth -danieljoshuanazareth
-danieljoshuanazareth -danieljoshuanazareth
-davidbernat looks to be a clawdbot that spams team and sends super weird emails, doesnt appear to be a real person
dmtrkovalenko
edemaine edemaine
fahreddinozcan
-florianleibert -florianleibert
fwang fwang
iamdavidhill iamdavidhill
@@ -27,8 +31,10 @@ r44vc0rp
rekram1-node rekram1-node
-ricardo-m-l -ricardo-m-l
-robinmordasiewicz -robinmordasiewicz
rubdos
shantur shantur
simonklee simonklee
-spider-yamet clawdbot/llm psychosis, spam pinging the team -spider-yamet clawdbot/llm psychosis, spam pinging the team
-terisuke
thdxr thdxr
-toastythebot -toastythebot
+7 -2
View File
@@ -1,5 +1,10 @@
name: "Setup Bun" name: "Setup Bun"
description: "Setup Bun with caching and install dependencies" description: "Setup Bun with caching and install dependencies"
inputs:
install-flags:
description: "Additional flags to pass to 'bun install'"
required: false
default: ""
runs: runs:
using: "composite" using: "composite"
steps: steps:
@@ -46,8 +51,8 @@ runs:
# e.g. ./patches/ for standard-openapi # e.g. ./patches/ for standard-openapi
# https://github.com/oven-sh/bun/issues/28147 # https://github.com/oven-sh/bun/issues/28147
if [ "$RUNNER_OS" = "Windows" ]; then if [ "$RUNNER_OS" = "Windows" ]; then
bun install --linker hoisted bun install --linker hoisted ${{ inputs.install-flags }}
else else
bun install bun install ${{ inputs.install-flags }}
fi fi
shell: bash shell: bash
+7 -3
View File
@@ -88,7 +88,7 @@ jobs:
- name: Build - name: Build
id: build id: build
run: | run: |
./packages/opencode/script/build.ts ./packages/opencode/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }}
env: env:
OPENCODE_VERSION: ${{ needs.version.outputs.version }} OPENCODE_VERSION: ${{ needs.version.outputs.version }}
OPENCODE_RELEASE: ${{ needs.version.outputs.release }} OPENCODE_RELEASE: ${{ needs.version.outputs.release }}
@@ -402,12 +402,14 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
settings: settings:
- host: macos-latest - host: macos-26-intel
target: x86_64-apple-darwin target: x86_64-apple-darwin
platform_flag: --mac --x64 platform_flag: --mac --x64
- host: macos-latest bun_install_flags: --os=darwin --cpu=x64
- host: macos-26
target: aarch64-apple-darwin target: aarch64-apple-darwin
platform_flag: --mac --arm64 platform_flag: --mac --arm64
bun_install_flags: --os=darwin --cpu=arm64
# github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain # github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain
- host: "windows-2025" - host: "windows-2025"
target: aarch64-pc-windows-msvc target: aarch64-pc-windows-msvc
@@ -437,6 +439,8 @@ jobs:
run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8 run: echo "${{ secrets.APPLE_API_KEY_PATH }}" > $RUNNER_TEMP/apple-api-key.p8
- uses: ./.github/actions/setup-bun - uses: ./.github/actions/setup-bun
with:
install-flags: ${{ matrix.settings.bun_install_flags }}
- name: Azure login - name: Azure login
if: runner.os == 'Windows' if: runner.os == 'Windows'
+2 -2
View File
@@ -45,13 +45,13 @@ jobs:
- name: Check PR guidelines compliance - name: Check PR guidelines compliance
env: env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }' OPENCODE_PERMISSION: '{ "bash": { "*": "deny", "gh*": "allow", "gh pr review*": "deny" } }'
PR_TITLE: ${{ steps.pr-details.outputs.title }} PR_TITLE: ${{ steps.pr-details.outputs.title }}
run: | run: |
PR_BODY=$(jq -r .body pr_data.json) PR_BODY=$(jq -r .body pr_data.json)
opencode run -m anthropic/claude-opus-4-5 "A new pull request has been created: '${PR_TITLE}' opencode run -m opencode/gpt-5.5 --variant medium "A new pull request has been created: '${PR_TITLE}'
<pr-number> <pr-number>
${{ steps.pr-number.outputs.number }} ${{ steps.pr-number.outputs.number }}
-899
View File
@@ -1,899 +0,0 @@
---
description: Translate content for a specified locale while preserving technical terms
mode: subagent
model: opencode/gpt-5.4
---
You are a professional translator and localization specialist.
Translate the user's content into the requested target locale (language + region, e.g. fr-FR, de-DE).
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.
- Output ONLY the translation (no commentary).
If the target locale is missing, ask the user to provide it.
If no locale-specific glossary exists, use the global glossary only.
---
# Locale-Specific Glossaries
When a locale glossary exists, use it to:
- Apply preferred wording for recurring UI/docs terms in that locale
- Preserve locale-specific do-not-translate terms and casing decisions
- Prefer natural phrasing over literal translation when the locale file calls it out
- If the repo uses a locale alias slug, apply that file too (for example, `pt-BR` maps to `br.md` in this repo)
Locale guidance does not override code/command preservation rules or the global Do-Not-Translate glossary below.
---
# Do-Not-Translate Terms (OpenCode Docs)
Generated from: `packages/web/src/content/docs/*.mdx` (default English docs)
Generated on: 2026-02-10
Use this as a translation QA checklist / glossary. Preserve listed terms exactly (spelling, casing, punctuation).
General rules (verbatim, even if not listed below):
- Anything inside inline code (single backticks) or fenced code blocks (triple backticks)
- MDX/JS code in docs: `import ... from "..."`, component tags, identifiers
- CLI commands, flags, config keys/values, file paths, URLs/domains, and env vars
## Proper nouns and product names
Additional (not reliably captured via link text):
```text
Astro
Bun
Chocolatey
Cursor
Docker
Git
GitHub Actions
GitLab CI
GNOME Terminal
Homebrew
Mise
Neovim
Node.js
npm
Obsidian
opencode
opencode-ai
Paru
pnpm
ripgrep
Scoop
SST
Starlight
Visual Studio Code
VS Code
VSCodium
Windsurf
Windows Terminal
Yarn
Zellij
Zed
anomalyco
```
Extracted from link labels in the English docs (review and prune as desired):
```text
@openspoon/subtask2
302.AI console
ACP progress report
Agent Client Protocol
Agent Skills
Agentic
AGENTS.md
AI SDK
Alacritty
Anthropic
Anthropic's Data Policies
Atom One
Avante.nvim
Ayu
Azure AI Foundry
Azure portal
Baseten
built-in GITHUB_TOKEN
Bun.$
Catppuccin
Cerebras console
ChatGPT Plus or Pro
Cloudflare dashboard
CodeCompanion.nvim
CodeNomad
Configuring Adapters: Environment Variables
Context7 MCP server
Cortecs console
Deep Infra dashboard
DeepSeek console
Duo Agent Platform
Everforest
Fireworks AI console
Firmware dashboard
Ghostty
GitLab CLI agents docs
GitLab docs
GitLab User Settings > Access Tokens
Granular Rules (Object Syntax)
Grep by Vercel
Groq console
Gruvbox
Helicone
Helicone documentation
Helicone Header Directory
Helicone's Model Directory
Hugging Face Inference Providers
Hugging Face settings
install WSL
IO.NET console
JetBrains IDE
Kanagawa
Kitty
MiniMax API Console
Models.dev
Moonshot AI console
Nebius Token Factory console
Nord
OAuth
Ollama integration docs
OpenAI's Data Policies
OpenChamber
OpenCode
OpenCode config
OpenCode Config
OpenCode TUI with the opencode theme
OpenCode Web - Active Session
OpenCode Web - New Session
OpenCode Web - See Servers
OpenCode Zen
OpenCode-Obsidian
OpenRouter dashboard
OpenWork
OVHcloud panel
Pro+ subscription
SAP BTP Cockpit
Scaleway Console IAM settings
Scaleway Generative APIs
SDK documentation
Sentry MCP server
shell API
Together AI console
Tokyonight
Unified Billing
Venice AI console
Vercel dashboard
WezTerm
Windows Subsystem for Linux (WSL)
WSL
WSL (Windows Subsystem for Linux)
WSL extension
xAI console
Z.AI API console
Zed
ZenMux dashboard
Zod
```
## Acronyms and initialisms
```text
ACP
AGENTS
AI
AI21
ANSI
API
AST
AWS
BTP
CD
CDN
CI
CLI
CMD
CORS
DEBUG
EKS
ERROR
FAQ
GLM
GNOME
GPT
HTML
HTTP
HTTPS
IAM
ID
IDE
INFO
IO
IP
IRSA
JS
JSON
JSONC
K2
LLM
LM
LSP
M2
MCP
MR
NET
NPM
NTLM
OIDC
OS
PAT
PATH
PHP
PR
PTY
README
RFC
RPC
SAP
SDK
SKILL
SSE
SSO
TS
TTY
TUI
UI
URL
US
UX
VCS
VPC
VPN
VS
WARN
WSL
X11
YAML
```
## Code identifiers used in prose (CamelCase, mixedCase)
```text
apiKey
AppleScript
AssistantMessage
baseURL
BurntSushi
ChatGPT
ClangFormat
CodeCompanion
CodeNomad
DeepSeek
DefaultV2
FileContent
FileDiff
FileNode
fineGrained
FormatterStatus
GitHub
GitLab
iTerm2
JavaScript
JetBrains
macOS
mDNS
MiniMax
NeuralNomadsAI
NickvanDyke
NoeFabris
OpenAI
OpenAPI
OpenChamber
OpenCode
OpenRouter
OpenTUI
OpenWork
ownUserPermissions
PowerShell
ProviderAuthAuthorization
ProviderAuthMethod
ProviderInitError
SessionStatus
TabItem
tokenType
ToolIDs
ToolList
TypeScript
typesUrl
UserMessage
VcsInfo
WebView2
WezTerm
xAI
ZenMux
```
## OpenCode CLI commands (as shown in docs)
```text
opencode
opencode [project]
opencode /path/to/project
opencode acp
opencode agent [command]
opencode agent create
opencode agent list
opencode attach [url]
opencode attach http://10.20.30.40:4096
opencode attach http://localhost:4096
opencode auth [command]
opencode auth list
opencode auth login
opencode auth logout
opencode auth ls
opencode export [sessionID]
opencode github [command]
opencode github install
opencode github run
opencode import <file>
opencode import https://opncd.ai/s/abc123
opencode import session.json
opencode mcp [command]
opencode mcp add
opencode mcp auth [name]
opencode mcp auth list
opencode mcp auth ls
opencode mcp auth my-oauth-server
opencode mcp auth sentry
opencode mcp debug <name>
opencode mcp debug my-oauth-server
opencode mcp list
opencode mcp logout [name]
opencode mcp logout my-oauth-server
opencode mcp ls
opencode models --refresh
opencode models [provider]
opencode models anthropic
opencode run [message..]
opencode run Explain the use of context in Go
opencode serve
opencode serve --cors http://localhost:5173 --cors https://app.example.com
opencode serve --hostname 0.0.0.0 --port 4096
opencode serve [--port <number>] [--hostname <string>] [--cors <origin>]
opencode session [command]
opencode session list
opencode session delete <sessionID>
opencode stats
opencode uninstall
opencode upgrade
opencode upgrade [target]
opencode upgrade v0.1.48
opencode web
opencode web --cors https://example.com
opencode web --hostname 0.0.0.0
opencode web --mdns
opencode web --mdns --mdns-domain myproject.local
opencode web --port 4096
opencode web --port 4096 --hostname 0.0.0.0
opencode.server.close()
```
## Slash commands and routes
```text
/agent
/auth/:id
/clear
/command
/config
/config/providers
/connect
/continue
/doc
/editor
/event
/experimental/tool?provider=<p>&model=<m>
/experimental/tool/ids
/export
/file?path=<path>
/file/content?path=<p>
/file/status
/find?pattern=<pat>
/find/file
/find/file?query=<q>
/find/symbol?query=<q>
/formatter
/global/event
/global/health
/help
/init
/instance/dispose
/log
/lsp
/mcp
/mnt/
/mnt/c/
/mnt/d/
/models
/oc
/opencode
/path
/project
/project/current
/provider
/provider/{id}/oauth/authorize
/provider/{id}/oauth/callback
/provider/auth
/q
/quit
/redo
/resume
/session
/session/:id
/session/:id/abort
/session/:id/children
/session/:id/command
/session/:id/diff
/session/:id/fork
/session/:id/init
/session/:id/message
/session/:id/message/:messageID
/session/:id/permissions/:permissionID
/session/:id/prompt_async
/session/:id/revert
/session/:id/share
/session/:id/shell
/session/:id/summarize
/session/:id/todo
/session/:id/unrevert
/session/status
/share
/summarize
/theme
/tui
/tui/append-prompt
/tui/clear-prompt
/tui/control/next
/tui/control/response
/tui/execute-command
/tui/open-help
/tui/open-models
/tui/open-sessions
/tui/open-themes
/tui/show-toast
/tui/submit-prompt
/undo
/Users/username
/Users/username/projects/*
/vcs
```
## CLI flags and short options
```text
--agent
--attach
--command
--continue
--cors
--cwd
--days
--dir
--dry-run
--event
--file
--force
--fork
--format
--help
--hostname
--hostname 0.0.0.0
--keep-config
--keep-data
--log-level
--max-count
--mdns
--mdns-domain
--method
--model
--models
--port
--print-logs
--project
--prompt
--refresh
--session
--share
--title
--token
--tools
--verbose
--version
--wait
-c
-d
-f
-h
-m
-n
-s
-v
```
## Environment variables
```text
AI_API_URL
AI_FLOW_CONTEXT
AI_FLOW_EVENT
AI_FLOW_INPUT
AICORE_DEPLOYMENT_ID
AICORE_RESOURCE_GROUP
AICORE_SERVICE_KEY
ANTHROPIC_API_KEY
AWS_ACCESS_KEY_ID
AWS_BEARER_TOKEN_BEDROCK
AWS_PROFILE
AWS_REGION
AWS_ROLE_ARN
AWS_SECRET_ACCESS_KEY
AWS_WEB_IDENTITY_TOKEN_FILE
AZURE_COGNITIVE_SERVICES_RESOURCE_NAME
AZURE_RESOURCE_NAME
CI_PROJECT_DIR
CI_SERVER_FQDN
CI_WORKLOAD_REF
CLOUDFLARE_ACCOUNT_ID
CLOUDFLARE_API_TOKEN
CLOUDFLARE_GATEWAY_ID
CONTEXT7_API_KEY
GITHUB_TOKEN
GITLAB_AI_GATEWAY_URL
GITLAB_HOST
GITLAB_INSTANCE_URL
GITLAB_OAUTH_CLIENT_ID
GITLAB_TOKEN
GITLAB_TOKEN_OPENCODE
GOOGLE_APPLICATION_CREDENTIALS
GOOGLE_CLOUD_PROJECT
HTTP_PROXY
HTTPS_PROXY
K2_
MY_API_KEY
MY_ENV_VAR
MY_MCP_CLIENT_ID
MY_MCP_CLIENT_SECRET
NO_PROXY
NODE_ENV
NODE_EXTRA_CA_CERTS
NPM_AUTH_TOKEN
OC_ALLOW_WAYLAND
OPENCODE_API_KEY
OPENCODE_AUTH_JSON
OPENCODE_AUTO_SHARE
OPENCODE_CLIENT
OPENCODE_CONFIG
OPENCODE_CONFIG_CONTENT
OPENCODE_CONFIG_DIR
OPENCODE_DISABLE_AUTOCOMPACT
OPENCODE_DISABLE_AUTOUPDATE
OPENCODE_DISABLE_CLAUDE_CODE
OPENCODE_DISABLE_CLAUDE_CODE_PROMPT
OPENCODE_DISABLE_CLAUDE_CODE_SKILLS
OPENCODE_DISABLE_DEFAULT_PLUGINS
OPENCODE_DISABLE_LSP_DOWNLOAD
OPENCODE_DISABLE_MODELS_FETCH
OPENCODE_DISABLE_PRUNE
OPENCODE_DISABLE_TERMINAL_TITLE
OPENCODE_ENABLE_EXA
OPENCODE_ENABLE_EXPERIMENTAL_MODELS
OPENCODE_EXPERIMENTAL
OPENCODE_EXPERIMENTAL_BASH_DEFAULT_TIMEOUT_MS
OPENCODE_EXPERIMENTAL_DISABLE_COPY_ON_SELECT
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER
OPENCODE_EXPERIMENTAL_EXA
OPENCODE_EXPERIMENTAL_FILEWATCHER
OPENCODE_EXPERIMENTAL_ICON_DISCOVERY
OPENCODE_EXPERIMENTAL_LSP_TOOL
OPENCODE_EXPERIMENTAL_LSP_TY
OPENCODE_EXPERIMENTAL_MARKDOWN
OPENCODE_EXPERIMENTAL_OUTPUT_TOKEN_MAX
OPENCODE_EXPERIMENTAL_OXFMT
OPENCODE_EXPERIMENTAL_PLAN_MODE
OPENCODE_ENABLE_QUESTION_TOOL
OPENCODE_FAKE_VCS
OPENCODE_GIT_BASH_PATH
OPENCODE_MODEL
OPENCODE_MODELS_URL
OPENCODE_PERMISSION
OPENCODE_PORT
OPENCODE_SERVER_PASSWORD
OPENCODE_SERVER_USERNAME
PROJECT_ROOT
RESOURCE_NAME
RUST_LOG
VARIABLE_NAME
VERTEX_LOCATION
XDG_CONFIG_HOME
```
## Package/module identifiers
```text
../../../config.mjs
@astrojs/starlight/components
@opencode-ai/plugin
@opencode-ai/sdk
path
shescape
zod
@
@ai-sdk/anthropic
@ai-sdk/cerebras
@ai-sdk/google
@ai-sdk/openai
@ai-sdk/openai-compatible
@File#L37-42
@modelcontextprotocol/server-everything
@opencode
```
## GitHub owner/repo slugs referenced in docs
```text
24601/opencode-zellij-namer
angristan/opencode-wakatime
anomalyco/opencode
apps/opencode-agent
athal7/opencode-devcontainers
awesome-opencode/awesome-opencode
backnotprop/plannotator
ben-vargas/ai-sdk-provider-opencode-sdk
btriapitsyn/openchamber
BurntSushi/ripgrep
Cluster444/agentic
code-yeongyu/oh-my-opencode
darrenhinde/opencode-agents
different-ai/opencode-scheduler
different-ai/openwork
features/copilot
folke/tokyonight.nvim
franlol/opencode-md-table-formatter
ggml-org/llama.cpp
ghoulr/opencode-websearch-cited.git
H2Shami/opencode-helicone-session
hosenur/portal
jamesmurdza/daytona
jenslys/opencode-gemini-auth
JRedeker/opencode-morph-fast-apply
JRedeker/opencode-shell-strategy
kdcokenny/ocx
kdcokenny/opencode-background-agents
kdcokenny/opencode-notify
kdcokenny/opencode-workspace
kdcokenny/opencode-worktree
login/device
mohak34/opencode-notifier
morhetz/gruvbox
mtymek/opencode-obsidian
NeuralNomadsAI/CodeNomad
nick-vi/opencode-type-inject
NickvanDyke/opencode.nvim
NoeFabris/opencode-antigravity-auth
nordtheme/nord
numman-ali/opencode-openai-codex-auth
olimorris/codecompanion.nvim
panta82/opencode-notificator
rebelot/kanagawa.nvim
remorses/kimaki
sainnhe/everforest
shekohex/opencode-google-antigravity-auth
shekohex/opencode-pty.git
spoons-and-mirrors/subtask2
sudo-tee/opencode.nvim
supermemoryai/opencode-supermemory
Tarquinen/opencode-dynamic-context-pruning
Th3Whit3Wolf/one-nvim
upstash/context7
vtemian/micode
vtemian/octto
yetone/avante.nvim
zenobi-us/opencode-plugin-template
zenobi-us/opencode-skillful
```
## Paths, filenames, globs, and URLs
```text
./.opencode/themes/*.json
./<project-slug>/storage/
./config/#custom-directory
./global/storage/
.agents/skills/*/SKILL.md
.agents/skills/<name>/SKILL.md
.clang-format
.claude
.claude/skills
.claude/skills/*/SKILL.md
.claude/skills/<name>/SKILL.md
.env
.github/workflows/opencode.yml
.gitignore
.gitlab-ci.yml
.ignore
.NET SDK
.npmrc
.ocamlformat
.opencode
.opencode/
.opencode/agents/
.opencode/commands/
.opencode/commands/test.md
.opencode/modes/
.opencode/plans/*.md
.opencode/plugins/
.opencode/skills/<name>/SKILL.md
.opencode/skills/git-release/SKILL.md
.opencode/tools/
.well-known/opencode
{ type: "raw" \| "patch", content: string }
{file:path/to/file}
**/*.js
%USERPROFILE%/intelephense/license.txt
%USERPROFILE%\.cache\opencode
%USERPROFILE%\.config\opencode\opencode.jsonc
%USERPROFILE%\.config\opencode\plugins
%USERPROFILE%\.local\share\opencode
%USERPROFILE%\.local\share\opencode\log
<project-root>/.opencode/themes/*.json
<providerId>/<modelId>
<your-project>/.opencode/plugins/
~
~/...
~/.agents/skills/*/SKILL.md
~/.agents/skills/<name>/SKILL.md
~/.aws/credentials
~/.bashrc
~/.cache/opencode
~/.cache/opencode/node_modules/
~/.claude/CLAUDE.md
~/.claude/skills/
~/.claude/skills/*/SKILL.md
~/.claude/skills/<name>/SKILL.md
~/.config/opencode
~/.config/opencode/AGENTS.md
~/.config/opencode/agents/
~/.config/opencode/commands/
~/.config/opencode/modes/
~/.config/opencode/opencode.json
~/.config/opencode/opencode.jsonc
~/.config/opencode/plugins/
~/.config/opencode/skills/*/SKILL.md
~/.config/opencode/skills/<name>/SKILL.md
~/.config/opencode/themes/*.json
~/.config/opencode/tools/
~/.config/zed/settings.json
~/.local/share
~/.local/share/opencode/
~/.local/share/opencode/auth.json
~/.local/share/opencode/log/
~/.local/share/opencode/mcp-auth.json
~/.local/share/opencode/opencode.jsonc
~/.npmrc
~/.zshrc
~/code/
~/Library/Application Support
~/projects/*
~/projects/personal/
${config.github}/blob/dev/packages/sdk/js/src/gen/types.gen.ts
$HOME/intelephense/license.txt
$HOME/projects/*
$XDG_CONFIG_HOME/opencode/themes/*.json
agent/
agents/
build/
commands/
dist/
http://<wsl-ip>:4096
http://127.0.0.1:8080/callback
http://localhost:<port>
http://localhost:4096
http://localhost:4096/doc
https://app.example.com
https://AZURE_COGNITIVE_SERVICES_RESOURCE_NAME.cognitiveservices.azure.com/
https://opencode.ai/zen/v1/chat/completions
https://opencode.ai/zen/v1/messages
https://opencode.ai/zen/v1/models/gemini-3-flash
https://opencode.ai/zen/v1/models/gemini-3-pro
https://opencode.ai/zen/v1/responses
https://RESOURCE_NAME.openai.azure.com/
laravel/pint
log/
model: "anthropic/claude-sonnet-4-5"
modes/
node_modules/
openai/gpt-4.1
opencode.ai/config.json
opencode/<model-id>
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
opncd.ai/s/<share-id>
packages/*/AGENTS.md
plugins/
project/
provider_id/model_id
provider/model
provider/model-id
rm -rf ~/.cache/opencode
skills/
skills/*/SKILL.md
src/**/*.ts
themes/
tools/
```
## Keybind strings
```text
alt+b
Alt+Ctrl+K
alt+d
alt+f
Cmd+Esc
Cmd+Option+K
Cmd+Shift+Esc
Cmd+Shift+G
Cmd+Shift+P
ctrl+a
ctrl+b
ctrl+d
ctrl+e
Ctrl+Esc
ctrl+f
ctrl+g
ctrl+k
Ctrl+Shift+Esc
Ctrl+Shift+P
ctrl+t
ctrl+u
ctrl+w
ctrl+x
DELETE
Shift+Enter
WIN+R
```
## Model ID strings referenced
```text
{env:OPENCODE_MODEL}
anthropic/claude-3-5-sonnet-20241022
anthropic/claude-haiku-4-20250514
anthropic/claude-haiku-4-5
anthropic/claude-sonnet-4-20250514
anthropic/claude-sonnet-4-5
gitlab/duo-chat-haiku-4-5
lmstudio/google/gemma-3n-e4b
openai/gpt-4.1
openai/gpt-5
opencode/gpt-5.1-codex
opencode/gpt-5.2-codex
opencode/kimi-k2
openrouter/google/gemini-2.5-flash
```
+14
View File
@@ -0,0 +1,14 @@
---
description: translate English to other languages
model: opencode/claude-opus-4-7
---
run git diff and translate changed english doc and UI copy files to other international languages. Translate all languages in parallel to save time.
Requirements:
- Preserve meaning, intent, tone, and formatting (including Markdown/MDX structure).
- Preserve all technical terms and artifacts exactly: product/company names, API names, identifiers, code, commands/flags, file paths, URLs, versions, error messages, config keys/values, and anything inside inline code or code blocks.
- Also preserve every term listed in the Do-Not-Translate glossary below.
- Also apply locale-specific guidance from `.opencode/glossary/<locale>.md` when available (for example, `zh-cn.md`).
- Do not modify fenced code blocks.
+1 -5
View File
@@ -1,10 +1,6 @@
{ {
"$schema": "https://opencode.ai/config.json", "$schema": "https://opencode.ai/config.json",
"provider": { "provider": {},
"opencode": {
"options": {},
},
},
"permission": { "permission": {
"edit": { "edit": {
"packages/opencode/migration/*": "deny", "packages/opencode/migration/*": "deny",
+27 -10
View File
@@ -1,21 +1,38 @@
--- ---
name: effect name: effect
description: Answer questions about the Effect framework description: Work with Effect v4 / effect-smol TypeScript code in this repo
--- ---
# Effect # Effect
This codebase uses Effect, a framework for writing typescript. This codebase uses Effect for typed, composable TypeScript services, schemas, and workflows.
## How to Answer Effect Questions ## Source Of Truth
1. Clone the Effect repository: `https://github.com/Effect-TS/effect-smol` to Use the current Effect v4 / effect-smol source, not memory or older Effect v2/v3 examples.
`.opencode/references/effect-smol` in this project NOT the skill folder.
2. Use the explore agent to search the codebase for answers about Effect patterns, APIs, and concepts 1. If `.opencode/references/effect-smol` is missing, clone `https://github.com/Effect-TS/effect-smol` there. Do this in the project, not in the skill folder.
3. Provide responses based on the actual Effect source code and documentation 2. Search `.opencode/references/effect-smol` for exact APIs, examples, tests, and naming patterns before answering or implementing Effect-specific code.
3. Also inspect existing repo code for local house style before introducing new patterns.
4. Prefer answers and implementations backed by specific source files or nearby repo examples.
## Guidelines ## Guidelines
- Always use the explore agent with the cloned repository when answering Effect-related questions - Prefer current Effect v4 APIs and project-local patterns over old blog posts, examples, or package-memory guesses.
- Reference specific files and patterns found in the Effect codebase - Use `Effect.gen(function* () { ... })` for multi-step workflows.
- Do not answer from memory - always verify against the source - Use `Effect.fn("Name")` or `Effect.fnUntraced(...)` for named effects when adding reusable service methods or important workflows.
- Prefer Effect `Schema` for API and domain data shapes. Use branded schemas for IDs and `Schema.TaggedErrorClass` for typed domain errors when modeling new error surfaces.
- Keep HTTP handlers thin: decode input, read request context, call services, and map transport errors. Put business rules in services.
- In Effect service code, prefer Effect-aware platform abstractions and dependencies over ad hoc promises where the surrounding code already does so.
- Keep layer composition explicit. Avoid broad hidden provisioning that makes missing dependencies hard to see.
- In tests, prefer the repo's existing Effect test helpers and live tests for filesystem, git, child process, locks, or timing behavior.
- Do not introduce `any`, non-null assertions, unchecked casts, or older Effect APIs just to satisfy types.
- Do not answer from memory. Verify against `.opencode/references/effect-smol` or nearby code first.
## Testing Patterns
- Use `testEffect(...)` from `packages/opencode/test/lib/effect.ts` for tests that exercise Effect services, layers, runtime context, scoped resources, or platform integrations.
- Use `it.live(...)` for filesystem, git repositories, HTTP servers, sockets, child processes, locks, real time, and other live platform behavior.
- Run tests from package directories such as `packages/opencode`; never run package tests from the repo root.
- Prefer explicit test layers over ad hoc managed runtimes. Keep dependency provisioning visible in the test file.
- Use scoped fixtures and finalizers for resources that must be cleaned up, including temporary directories, flags, databases, fibers, servers, and global state.
+2 -2
View File
@@ -3,8 +3,8 @@ import { tool } from "@opencode-ai/plugin"
const TEAM = { const TEAM = {
desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"], desktop: ["adamdotdevin", "iamdavidhill", "Brendonovich", "nexxeln"],
zen: ["fwang", "MrMushrooooom"], zen: ["fwang", "MrMushrooooom"],
tui: ["thdxr", "kommander", "rekram1-node"], tui: ["kommander", "rekram1-node", "simonklee"],
core: ["thdxr", "rekram1-node", "jlongster"], core: ["kitlangton", "rekram1-node", "jlongster"],
docs: ["R44VC0RP"], docs: ["R44VC0RP"],
windows: ["Hona"], windows: ["Hona"],
} as const } as const
+158 -117
View File
@@ -29,11 +29,11 @@
}, },
"packages/app": { "packages/app": {
"name": "@opencode-ai/app", "name": "@opencode-ai/app",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/shared": "workspace:*",
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
"@shikijs/transformers": "3.9.2", "@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3", "@solid-primitives/active-element": "2.1.3",
@@ -83,7 +83,7 @@
}, },
"packages/console/app": { "packages/console/app": {
"name": "@opencode-ai/console-app", "name": "@opencode-ai/console-app",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@cloudflare/vite-plugin": "1.15.2", "@cloudflare/vite-plugin": "1.15.2",
"@ibm/plex": "6.4.1", "@ibm/plex": "6.4.1",
@@ -117,7 +117,7 @@
}, },
"packages/console/core": { "packages/console/core": {
"name": "@opencode-ai/console-core", "name": "@opencode-ai/console-core",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@aws-sdk/client-sts": "3.782.0", "@aws-sdk/client-sts": "3.782.0",
"@jsx-email/render": "1.1.1", "@jsx-email/render": "1.1.1",
@@ -144,7 +144,7 @@
}, },
"packages/console/function": { "packages/console/function": {
"name": "@opencode-ai/console-function", "name": "@opencode-ai/console-function",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@ai-sdk/anthropic": "3.0.64", "@ai-sdk/anthropic": "3.0.64",
"@ai-sdk/openai": "3.0.48", "@ai-sdk/openai": "3.0.48",
@@ -168,7 +168,7 @@
}, },
"packages/console/mail": { "packages/console/mail": {
"name": "@opencode-ai/console-mail", "name": "@opencode-ai/console-mail",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@jsx-email/all": "2.2.3", "@jsx-email/all": "2.2.3",
"@jsx-email/cli": "1.4.3", "@jsx-email/cli": "1.4.3",
@@ -190,9 +190,43 @@
"cloudflare": "5.2.0", "cloudflare": "5.2.0",
}, },
}, },
"packages/core": {
"name": "@opencode-ai/core",
"version": "1.14.30",
"bin": {
"opencode": "./bin/opencode",
},
"dependencies": {
"@effect/opentelemetry": "catalog:",
"@effect/platform-node": "catalog:",
"@npmcli/arborist": "9.4.0",
"@npmcli/config": "10.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",
"cross-spawn": "catalog:",
"effect": "catalog:",
"glob": "13.0.5",
"mime-types": "3.0.2",
"minimatch": "10.2.5",
"npm-package-arg": "13.0.2",
"semver": "^7.6.3",
"xdg-basedir": "5.1.0",
"zod": "catalog:",
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/cross-spawn": "catalog:",
"@types/npm-package-arg": "6.1.4",
"@types/npmcli__arborist": "6.3.3",
"@types/semver": "catalog:",
},
},
"packages/desktop": { "packages/desktop": {
"name": "@opencode-ai/desktop", "name": "@opencode-ai/desktop",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@opencode-ai/app": "workspace:*", "@opencode-ai/app": "workspace:*",
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
@@ -225,8 +259,9 @@
}, },
"packages/desktop-electron": { "packages/desktop-electron": {
"name": "@opencode-ai/desktop-electron", "name": "@opencode-ai/desktop-electron",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"drizzle-orm": "catalog:",
"effect": "catalog:", "effect": "catalog:",
"electron-context-menu": "4.1.2", "electron-context-menu": "4.1.2",
"electron-log": "^5", "electron-log": "^5",
@@ -248,7 +283,7 @@
"@types/node": "catalog:", "@types/node": "catalog:",
"@typescript/native-preview": "catalog:", "@typescript/native-preview": "catalog:",
"@valibot/to-json-schema": "1.6.0", "@valibot/to-json-schema": "1.6.0",
"electron": "40.4.1", "electron": "41.2.1",
"electron-builder": "^26", "electron-builder": "^26",
"electron-vite": "^5", "electron-vite": "^5",
"solid-js": "catalog:", "solid-js": "catalog:",
@@ -268,9 +303,9 @@
}, },
"packages/enterprise": { "packages/enterprise": {
"name": "@opencode-ai/enterprise", "name": "@opencode-ai/enterprise",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@opencode-ai/shared": "workspace:*", "@opencode-ai/core": "workspace:*",
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
"@pierre/diffs": "catalog:", "@pierre/diffs": "catalog:",
"@solidjs/meta": "catalog:", "@solidjs/meta": "catalog:",
@@ -297,7 +332,7 @@
}, },
"packages/function": { "packages/function": {
"name": "@opencode-ai/function", "name": "@opencode-ai/function",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@octokit/auth-app": "8.0.1", "@octokit/auth-app": "8.0.1",
"@octokit/rest": "catalog:", "@octokit/rest": "catalog:",
@@ -313,7 +348,7 @@
}, },
"packages/opencode": { "packages/opencode": {
"name": "opencode", "name": "opencode",
"version": "1.4.8", "version": "1.14.30",
"bin": { "bin": {
"opencode": "./bin/opencode", "opencode": "./bin/opencode",
}, },
@@ -322,15 +357,15 @@
"@actions/github": "6.0.1", "@actions/github": "6.0.1",
"@agentclientprotocol/sdk": "0.16.1", "@agentclientprotocol/sdk": "0.16.1",
"@ai-sdk/alibaba": "1.0.17", "@ai-sdk/alibaba": "1.0.17",
"@ai-sdk/amazon-bedrock": "4.0.94", "@ai-sdk/amazon-bedrock": "4.0.96",
"@ai-sdk/anthropic": "3.0.70", "@ai-sdk/anthropic": "3.0.71",
"@ai-sdk/azure": "3.0.49", "@ai-sdk/azure": "3.0.49",
"@ai-sdk/cerebras": "2.0.41", "@ai-sdk/cerebras": "2.0.41",
"@ai-sdk/cohere": "3.0.27", "@ai-sdk/cohere": "3.0.27",
"@ai-sdk/deepinfra": "2.0.41", "@ai-sdk/deepinfra": "2.0.41",
"@ai-sdk/gateway": "3.0.102", "@ai-sdk/gateway": "3.0.104",
"@ai-sdk/google": "3.0.63", "@ai-sdk/google": "3.0.63",
"@ai-sdk/google-vertex": "4.0.111", "@ai-sdk/google-vertex": "4.0.112",
"@ai-sdk/groq": "3.0.31", "@ai-sdk/groq": "3.0.31",
"@ai-sdk/mistral": "3.0.27", "@ai-sdk/mistral": "3.0.27",
"@ai-sdk/openai": "3.0.53", "@ai-sdk/openai": "3.0.53",
@@ -352,21 +387,20 @@
"@hono/zod-validator": "catalog:", "@hono/zod-validator": "catalog:",
"@lydell/node-pty": "catalog:", "@lydell/node-pty": "catalog:",
"@modelcontextprotocol/sdk": "1.27.1", "@modelcontextprotocol/sdk": "1.27.1",
"@npmcli/arborist": "9.4.0",
"@octokit/graphql": "9.0.2", "@octokit/graphql": "9.0.2",
"@octokit/rest": "catalog:", "@octokit/rest": "catalog:",
"@openauthjs/openauth": "catalog:", "@openauthjs/openauth": "catalog:",
"@opencode-ai/plugin": "workspace:*", "@opencode-ai/plugin": "workspace:*",
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@openrouter/ai-sdk-provider": "2.5.1", "@openrouter/ai-sdk-provider": "2.8.1",
"@opentelemetry/api": "1.9.0", "@opentelemetry/api": "1.9.0",
"@opentelemetry/context-async-hooks": "2.6.1", "@opentelemetry/context-async-hooks": "2.6.1",
"@opentelemetry/exporter-trace-otlp-http": "0.214.0", "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
"@opentelemetry/sdk-trace-base": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1",
"@opentelemetry/sdk-trace-node": "2.6.1", "@opentelemetry/sdk-trace-node": "2.6.1",
"@opentui/core": "0.1.100", "@opentui/core": "catalog:",
"@opentui/solid": "0.1.100", "@opentui/solid": "catalog:",
"@parcel/watcher": "2.5.1", "@parcel/watcher": "2.5.1",
"@pierre/diffs": "catalog:", "@pierre/diffs": "catalog:",
"@solid-primitives/event-bus": "1.1.2", "@solid-primitives/event-bus": "1.1.2",
@@ -386,7 +420,7 @@
"drizzle-orm": "catalog:", "drizzle-orm": "catalog:",
"effect": "catalog:", "effect": "catalog:",
"fuzzysort": "3.1.0", "fuzzysort": "3.1.0",
"gitlab-ai-provider": "6.4.2", "gitlab-ai-provider": "6.6.0",
"glob": "13.0.5", "glob": "13.0.5",
"google-auth-library": "10.5.0", "google-auth-library": "10.5.0",
"gray-matter": "4.0.3", "gray-matter": "4.0.3",
@@ -401,10 +435,9 @@
"open": "10.1.2", "open": "10.1.2",
"opencode-gitlab-auth": "2.0.1", "opencode-gitlab-auth": "2.0.1",
"opencode-poe-auth": "0.0.1", "opencode-poe-auth": "0.0.1",
"opentui-spinner": "0.0.6", "opentui-spinner": "catalog:",
"partial-json": "0.1.7", "partial-json": "0.1.7",
"remeda": "catalog:", "remeda": "catalog:",
"ripgrep": "0.3.1",
"semver": "^7.6.3", "semver": "^7.6.3",
"solid-js": "catalog:", "solid-js": "catalog:",
"strip-ansi": "7.1.2", "strip-ansi": "7.1.2",
@@ -425,8 +458,8 @@
"@babel/core": "7.28.4", "@babel/core": "7.28.4",
"@effect/language-service": "0.84.2", "@effect/language-service": "0.84.2",
"@octokit/webhooks-types": "7.6.1", "@octokit/webhooks-types": "7.6.1",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/script": "workspace:*", "@opencode-ai/script": "workspace:*",
"@opencode-ai/shared": "workspace:*",
"@parcel/watcher-darwin-arm64": "2.5.1", "@parcel/watcher-darwin-arm64": "2.5.1",
"@parcel/watcher-darwin-x64": "2.5.1", "@parcel/watcher-darwin-x64": "2.5.1",
"@parcel/watcher-linux-arm64-glibc": "2.5.1", "@parcel/watcher-linux-arm64-glibc": "2.5.1",
@@ -442,7 +475,6 @@
"@types/cross-spawn": "catalog:", "@types/cross-spawn": "catalog:",
"@types/mime-types": "3.0.1", "@types/mime-types": "3.0.1",
"@types/npm-package-arg": "6.1.4", "@types/npm-package-arg": "6.1.4",
"@types/npmcli__arborist": "6.3.3",
"@types/semver": "^7.5.8", "@types/semver": "^7.5.8",
"@types/turndown": "5.0.5", "@types/turndown": "5.0.5",
"@types/which": "3.0.4", "@types/which": "3.0.4",
@@ -450,6 +482,7 @@
"@typescript/native-preview": "catalog:", "@typescript/native-preview": "catalog:",
"drizzle-kit": "catalog:", "drizzle-kit": "catalog:",
"drizzle-orm": "catalog:", "drizzle-orm": "catalog:",
"prettier": "3.6.2",
"typescript": "catalog:", "typescript": "catalog:",
"vscode-languageserver-types": "3.17.5", "vscode-languageserver-types": "3.17.5",
"why-is-node-running": "3.2.2", "why-is-node-running": "3.2.2",
@@ -458,23 +491,23 @@
}, },
"packages/plugin": { "packages/plugin": {
"name": "@opencode-ai/plugin", "name": "@opencode-ai/plugin",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"effect": "catalog:", "effect": "catalog:",
"zod": "catalog:", "zod": "catalog:",
}, },
"devDependencies": { "devDependencies": {
"@opentui/core": "0.1.100", "@opentui/core": "catalog:",
"@opentui/solid": "0.1.100", "@opentui/solid": "catalog:",
"@tsconfig/node22": "catalog:", "@tsconfig/node22": "catalog:",
"@types/node": "catalog:", "@types/node": "catalog:",
"@typescript/native-preview": "catalog:", "@typescript/native-preview": "catalog:",
"typescript": "catalog:", "typescript": "catalog:",
}, },
"peerDependencies": { "peerDependencies": {
"@opentui/core": ">=0.1.100", "@opentui/core": ">=0.2.0",
"@opentui/solid": ">=0.1.100", "@opentui/solid": ">=0.2.0",
}, },
"optionalPeers": [ "optionalPeers": [
"@opentui/core", "@opentui/core",
@@ -493,7 +526,7 @@
}, },
"packages/sdk/js": { "packages/sdk/js": {
"name": "@opencode-ai/sdk", "name": "@opencode-ai/sdk",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"cross-spawn": "catalog:", "cross-spawn": "catalog:",
}, },
@@ -506,33 +539,9 @@
"typescript": "catalog:", "typescript": "catalog:",
}, },
}, },
"packages/shared": {
"name": "@opencode-ai/shared",
"version": "1.4.8",
"bin": {
"opencode": "./bin/opencode",
},
"dependencies": {
"@effect/platform-node": "catalog:",
"@npmcli/arborist": "catalog:",
"effect": "catalog:",
"glob": "13.0.5",
"mime-types": "3.0.2",
"minimatch": "10.2.5",
"semver": "catalog:",
"xdg-basedir": "5.1.0",
"zod": "catalog:",
},
"devDependencies": {
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
"@types/npmcli__arborist": "6.3.3",
"@types/semver": "catalog:",
},
},
"packages/slack": { "packages/slack": {
"name": "@opencode-ai/slack", "name": "@opencode-ai/slack",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@slack/bolt": "^3.17.1", "@slack/bolt": "^3.17.1",
@@ -567,11 +576,11 @@
}, },
"packages/ui": { "packages/ui": {
"name": "@opencode-ai/ui", "name": "@opencode-ai/ui",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/core": "workspace:*",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/shared": "workspace:*",
"@pierre/diffs": "catalog:", "@pierre/diffs": "catalog:",
"@shikijs/transformers": "3.9.2", "@shikijs/transformers": "3.9.2",
"@solid-primitives/bounds": "0.1.3", "@solid-primitives/bounds": "0.1.3",
@@ -616,7 +625,7 @@
}, },
"packages/web": { "packages/web": {
"name": "@opencode-ai/web", "name": "@opencode-ai/web",
"version": "1.4.8", "version": "1.14.30",
"dependencies": { "dependencies": {
"@astrojs/cloudflare": "12.6.3", "@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1", "@astrojs/markdown-remark": "6.3.1",
@@ -660,6 +669,7 @@
"patchedDependencies": { "patchedDependencies": {
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
}, },
"overrides": { "overrides": {
"@types/bun": "catalog:", "@types/bun": "catalog:",
@@ -667,14 +677,16 @@
}, },
"catalog": { "catalog": {
"@cloudflare/workers-types": "4.20251008.0", "@cloudflare/workers-types": "4.20251008.0",
"@effect/opentelemetry": "4.0.0-beta.48", "@effect/opentelemetry": "4.0.0-beta.57",
"@effect/platform-node": "4.0.0-beta.48", "@effect/platform-node": "4.0.0-beta.57",
"@hono/zod-validator": "0.4.2", "@hono/zod-validator": "0.4.2",
"@kobalte/core": "0.13.11", "@kobalte/core": "0.13.11",
"@lydell/node-pty": "1.2.0-beta.10", "@lydell/node-pty": "1.2.0-beta.10",
"@npmcli/arborist": "9.4.0", "@npmcli/arborist": "9.4.0",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
"@openauthjs/openauth": "0.0.0-20250322224806", "@openauthjs/openauth": "0.0.0-20250322224806",
"@opentui/core": "0.2.0",
"@opentui/solid": "0.2.0",
"@pierre/diffs": "1.1.0-beta.18", "@pierre/diffs": "1.1.0-beta.18",
"@playwright/test": "1.59.1", "@playwright/test": "1.59.1",
"@solid-primitives/storage": "4.3.3", "@solid-primitives/storage": "4.3.3",
@@ -684,25 +696,26 @@
"@tailwindcss/vite": "4.1.11", "@tailwindcss/vite": "4.1.11",
"@tsconfig/bun": "1.0.9", "@tsconfig/bun": "1.0.9",
"@tsconfig/node22": "22.0.2", "@tsconfig/node22": "22.0.2",
"@types/bun": "1.3.11", "@types/bun": "1.3.12",
"@types/cross-spawn": "6.0.6", "@types/cross-spawn": "6.0.6",
"@types/luxon": "3.7.1", "@types/luxon": "3.7.1",
"@types/node": "22.13.9", "@types/node": "22.13.9",
"@types/semver": "7.7.1", "@types/semver": "7.7.1",
"@typescript/native-preview": "7.0.0-dev.20251207.1", "@typescript/native-preview": "7.0.0-dev.20251207.1",
"ai": "6.0.158", "ai": "6.0.168",
"cross-spawn": "7.0.6", "cross-spawn": "7.0.6",
"diff": "8.0.2", "diff": "8.0.2",
"dompurify": "3.3.1", "dompurify": "3.3.1",
"drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.48", "effect": "4.0.0-beta.57",
"fuzzysort": "3.1.0", "fuzzysort": "3.1.0",
"hono": "4.10.7", "hono": "4.10.7",
"hono-openapi": "1.1.2", "hono-openapi": "1.1.2",
"luxon": "3.6.1", "luxon": "3.6.1",
"marked": "17.0.1", "marked": "17.0.1",
"marked-shiki": "1.2.1", "marked-shiki": "1.2.1",
"opentui-spinner": "0.0.6",
"remeda": "2.26.0", "remeda": "2.26.0",
"remend": "1.3.0", "remend": "1.3.0",
"semver": "7.7.4", "semver": "7.7.4",
@@ -738,7 +751,7 @@
"@ai-sdk/alibaba": ["@ai-sdk/alibaba@1.0.17", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZbE+U5bWz2JBc5DERLowx5+TKbjGBE93LqKZAWvuEn7HOSQMraxFMZuc0ST335QZJAyfBOzh7m1mPQ+y7EaaoA=="], "@ai-sdk/alibaba": ["@ai-sdk/alibaba@1.0.17", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZbE+U5bWz2JBc5DERLowx5+TKbjGBE93LqKZAWvuEn7HOSQMraxFMZuc0ST335QZJAyfBOzh7m1mPQ+y7EaaoA=="],
"@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.94", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.70", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XKE7wAjXejsIfNQvn3onvGUByhGHVM6W+xlL+1DAQLmjEb+ue4sOJIRehJ96rEvTXVVHRVyA6bSXx7ayxXfn5A=="], "@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.96", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.71", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Mc4Ias2jRMD1jOB6xWtKNPdhECeuCZyIlbr9EAGfBnyBt++sS13ziZh9qv9TdyMCAZJ7xoQcpbchoRJcKwPdpA=="],
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rwLi/Rsuj2pYniQXIrvClHvXDzgM4UQHHnvHTWEF14efnlKclG/1ghpNC+adsRujAbCTr6gRsSbDE2vEqriV7g=="], "@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-rwLi/Rsuj2pYniQXIrvClHvXDzgM4UQHHnvHTWEF14efnlKclG/1ghpNC+adsRujAbCTr6gRsSbDE2vEqriV7g=="],
@@ -758,11 +771,11 @@
"@ai-sdk/fireworks": ["@ai-sdk/fireworks@2.0.46", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XRKR0zgRyegdmtK5CDUEjlyRp0Fo+XVCdoG+301U1SGtgRIAYG3ObVtgzVJBVpJdHFSLHuYeLTnNiQoUxD7+FQ=="], "@ai-sdk/fireworks": ["@ai-sdk/fireworks@2.0.46", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XRKR0zgRyegdmtK5CDUEjlyRp0Fo+XVCdoG+301U1SGtgRIAYG3ObVtgzVJBVpJdHFSLHuYeLTnNiQoUxD7+FQ=="],
"@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.102", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-GrwDpaYJiVafrsA1MTbZtXPcQUI67g5AXiJo7Y1F8b+w+SiYHLk3ZIn1YmpQVoVAh2bjvxjj+Vo0AvfskuGH4g=="], "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="],
"@ai-sdk/google": ["@ai-sdk/google@3.0.63", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RfOZWVMYSPu2sPRfGajrauWAZ9BSaRopSn+AszkKWQ1MFj8nhaXvCqRHB5pBQUaHTfZKagvOmMpNfa/s3gPLgQ=="], "@ai-sdk/google": ["@ai-sdk/google@3.0.63", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-RfOZWVMYSPu2sPRfGajrauWAZ9BSaRopSn+AszkKWQ1MFj8nhaXvCqRHB5pBQUaHTfZKagvOmMpNfa/s3gPLgQ=="],
"@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.111", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.70", "@ai-sdk/google": "3.0.64", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-5gILpAWWI5idfal/MfoH3tlQeSnOJ9jfL8JB8m2fdc3ue/9xoXkYDpXpDL/nyJImFjMCi6eR0Fpvlo/IKEWDIg=="], "@ai-sdk/google-vertex": ["@ai-sdk/google-vertex@4.0.112", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.71", "@ai-sdk/google": "3.0.64", "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "google-auth-library": "^10.5.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cSfHCkM+9ZrFtQWIN1WlV93JPD+isGSdFxKj7u1L9m2aLVZajlXdcE41GL9hMt7ld7bZYE4NnZ+4VLxBAHE+Eg=="],
"@ai-sdk/groq": ["@ai-sdk/groq@3.0.31", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XbbugpnFmXGu2TlXiq8KUJskP6/VVbuFcnFIGDzDIB/Chg6XHsNnqrTF80Zxkh0Pd3+NvbM+2Uqrtsndk6bDAg=="], "@ai-sdk/groq": ["@ai-sdk/groq@3.0.31", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-XbbugpnFmXGu2TlXiq8KUJskP6/VVbuFcnFIGDzDIB/Chg6XHsNnqrTF80Zxkh0Pd3+NvbM+2Uqrtsndk6bDAg=="],
@@ -1058,11 +1071,11 @@
"@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="], "@effect/language-service": ["@effect/language-service@0.84.2", "", { "bin": { "effect-language-service": "cli.js" } }, "sha512-l04qNxpiA8rY5yXWckRPJ7Mk5MNerXuNymSFf+IdflfI5i8jgL1bpBNLuP6ijg7wgjdHc/KmTnCj2kT0SCntuA=="],
"@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.48", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@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.48" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-vHk/X1vgDrviGcOTHQqzm2D81TtyPE/C7Qdksg5eAdbGpnqL4Dm4lk6PzTReQ0pO1/avIvWqpxy315IURV0Ldw=="], "@effect/opentelemetry": ["@effect/opentelemetry@4.0.0-beta.57", "", { "peerDependencies": { "@opentelemetry/api": "^1.9", "@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.57" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/resources", "@opentelemetry/sdk-logs", "@opentelemetry/sdk-metrics", "@opentelemetry/sdk-trace-base", "@opentelemetry/sdk-trace-node", "@opentelemetry/sdk-trace-web"] }, "sha512-gdjZPEP0QQg4qmI1vd+443kheeQZKytrjJIzCJncy6ZEpyk/SfrqeStLqLXdTRcms3IB0ls0vOV7KNq7YmBRVA=="],
"@effect/platform-node": ["@effect/platform-node@4.0.0-beta.48", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.48", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.48", "ioredis": "^5.7.0" } }, "sha512-8J6H0k9rtbp9O1QvKOyOPRcCTJ8WrR7IzZLJtYFTZ4bXVEEMCTo84h0CRpi7ccpA9t7DLqotip0NeFgiBosNKQ=="], "@effect/platform-node": ["@effect/platform-node@4.0.0-beta.57", "", { "dependencies": { "@effect/platform-node-shared": "^4.0.0-beta.57", "mime": "^4.1.0", "undici": "^8.0.2" }, "peerDependencies": { "effect": "^4.0.0-beta.57", "ioredis": "^5.7.0" } }, "sha512-la0xxPSAYOsY0d+uVxEBxok3jYB31iPQmIaZZRUj2SNWqcGGHJc6KorKtI8guqSLuv9FGZ255kBWXRbG6hMeeg=="],
"@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.48", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.48" } }, "sha512-wlhcdDHyacydCgiWdM8JwtQkViQhZsC8uJZ9wMoZXYxlCTvqfdzLeWw4A1UVMoq7sS6/KR1aZVeFkUjrqonncQ=="], "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.57", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.57" } }, "sha512-C976X6f+qHUtLSqcqImuCrjhAHnJV17NC2RvvybsAuDfkyIWU4MyiO2XwgiBeijeNupyr1M/KPKnyjtkNxV9Hw=="],
"@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=="], "@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=="],
@@ -1466,6 +1479,8 @@
"@npmcli/arborist": ["@npmcli/arborist@9.4.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bin": { "arborist": "bin/index.js" } }, "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA=="], "@npmcli/arborist": ["@npmcli/arborist@9.4.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/fs": "^5.0.0", "@npmcli/installed-package-contents": "^4.0.0", "@npmcli/map-workspaces": "^5.0.0", "@npmcli/metavuln-calculator": "^9.0.2", "@npmcli/name-from-folder": "^4.0.0", "@npmcli/node-gyp": "^5.0.0", "@npmcli/package-json": "^7.0.0", "@npmcli/query": "^5.0.0", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.0", "bin-links": "^6.0.0", "cacache": "^20.0.1", "common-ancestor-path": "^2.0.0", "hosted-git-info": "^9.0.0", "json-stringify-nice": "^1.1.4", "lru-cache": "^11.2.1", "minimatch": "^10.0.3", "nopt": "^9.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.0", "npm-pick-manifest": "^11.0.1", "npm-registry-fetch": "^19.0.0", "pacote": "^21.0.2", "parse-conflict-json": "^5.0.1", "proc-log": "^6.0.0", "proggy": "^4.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^3.0.1", "semver": "^7.3.7", "ssri": "^13.0.0", "treeverse": "^3.0.0", "walk-up-path": "^4.0.0" }, "bin": { "arborist": "bin/index.js" } }, "sha512-4Bm8hNixJG/sii1PMnag0V9i/sGOX9VRzFrUiZMSBJpGlLR38f+Btl85d07G9GL56xO0l0OZjvrGNYsDYp0xKA=="],
"@npmcli/config": ["@npmcli/config@10.8.1", "", { "dependencies": { "@npmcli/map-workspaces": "^5.0.0", "@npmcli/package-json": "^7.0.0", "ci-info": "^4.0.0", "ini": "^6.0.0", "nopt": "^9.0.0", "proc-log": "^6.0.0", "semver": "^7.3.5", "walk-up-path": "^4.0.0" } }, "sha512-MAYk9IlIGiyC0c9fnjdBSQfIFPZT0g1MfeSiD1UXTq2zJOLX55jS9/sETJHqw/7LN18JjITrhYfgCfapbmZHiQ=="],
"@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="], "@npmcli/fs": ["@npmcli/fs@5.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og=="],
"@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="], "@npmcli/git": ["@npmcli/git@7.0.2", "", { "dependencies": { "@gar/promise-retry": "^1.0.0", "@npmcli/promise-spawn": "^9.0.0", "ini": "^6.0.0", "lru-cache": "^11.2.1", "npm-pick-manifest": "^11.0.1", "proc-log": "^6.0.0", "semver": "^7.3.5", "which": "^6.0.0" } }, "sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg=="],
@@ -1546,6 +1561,8 @@
"@opencode-ai/console-resource": ["@opencode-ai/console-resource@workspace:packages/console/resource"], "@opencode-ai/console-resource": ["@opencode-ai/console-resource@workspace:packages/console/resource"],
"@opencode-ai/core": ["@opencode-ai/core@workspace:packages/core"],
"@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"], "@opencode-ai/desktop": ["@opencode-ai/desktop@workspace:packages/desktop"],
"@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"], "@opencode-ai/desktop-electron": ["@opencode-ai/desktop-electron@workspace:packages/desktop-electron"],
@@ -1560,8 +1577,6 @@
"@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"], "@opencode-ai/sdk": ["@opencode-ai/sdk@workspace:packages/sdk/js"],
"@opencode-ai/shared": ["@opencode-ai/shared@workspace:packages/shared"],
"@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"], "@opencode-ai/slack": ["@opencode-ai/slack@workspace:packages/slack"],
"@opencode-ai/storybook": ["@opencode-ai/storybook@workspace:packages/storybook"], "@opencode-ai/storybook": ["@opencode-ai/storybook@workspace:packages/storybook"],
@@ -1570,7 +1585,7 @@
"@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"], "@opencode-ai/web": ["@opencode-ai/web@workspace:packages/web"],
"@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="], "@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
@@ -1586,7 +1601,7 @@
"@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-logs": "0.214.0", "@opentelemetry/sdk-metrics": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w=="], "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/sdk-logs": "0.214.0", "@opentelemetry/sdk-metrics": "2.6.1", "@opentelemetry/sdk-trace-base": "2.6.1", "protobufjs": "^7.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w=="],
"@opentelemetry/resources": ["@opentelemetry/resources@2.2.0", "", { "dependencies": { "@opentelemetry/core": "2.2.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A=="], "@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="], "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.214.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.214.0", "@opentelemetry/core": "2.6.1", "@opentelemetry/resources": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA=="],
@@ -1598,21 +1613,21 @@
"@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="],
"@opentui/core": ["@opentui/core@0.1.100", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.100", "@opentui/core-darwin-x64": "0.1.100", "@opentui/core-linux-arm64": "0.1.100", "@opentui/core-linux-x64": "0.1.100", "@opentui/core-win32-arm64": "0.1.100", "@opentui/core-win32-x64": "0.1.100", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-g6Ft3CcOVpytMzq2AZrnHHeMrmvYeLVCsy/8LqZ30VnPv0zfJ+f1TVi/EFrcl4m0GRPdy6yBOVOMcIAWHSZvtg=="], "@opentui/core": ["@opentui/core@0.2.0", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.2.0", "@opentui/core-darwin-x64": "0.2.0", "@opentui/core-linux-arm64": "0.2.0", "@opentui/core-linux-x64": "0.2.0", "@opentui/core-win32-arm64": "0.2.0", "@opentui/core-win32-x64": "0.2.0", "bun-webgpu": "0.1.7", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-7YOEqPUQmsgrOb9nmLEBlX8RVHPFy4HquK1C489DwfvvPTiws8nTbZ+webNQDWha7shgnYQK4Zo1EcOlpQ5+1Q=="],
"@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.100", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cY70nNjkh53tys4iQ0FDST3CQfN4Rp8zOrT6EW0dH/51KZq2Rg3EhxDpc+qu4zASadR5uuU5i61g8lYyVGeGgw=="], "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VVmKwth3hzsQPjAZ7WGJxmzuzx0uCtynd79JJDg26D7QRM9V5beVGbKwwU5SKsDlK74EyQoY85Mv9xFY5E4jrA=="],
"@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.100", "", { "os": "darwin", "cpu": "x64" }, "sha512-RUJa4MPX5BWwXuc5nE+Zc+md8+ITYp5X0ourM4+ReaIIW9pTxSDcIPBba9pkJb3PIADyulUPMmmy5OX+umDJhQ=="], "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-eX+WNdbSNr7Bozdq/MH6p1vXIALGt0SqBHR4YtWyTh6X7KDz9FTtJT3ylxMPqiVRUGBNAiWOxoqKGXW7JLQ0TA=="],
"@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.100", "", { "os": "linux", "cpu": "arm64" }, "sha512-Hznr39cSXg+2sQd+WcTsk67BjyqrZvuTK6f94Uu1ULYZJYCE4KdyNU2NzJSK6ooyosSWXPDsb4kwetTlfypMZg=="], "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ARZa+ywbN/OV7esT5ZdJMlQW3a4Pr56qLlEI/X65ik88C2sgmDze4Kf2FmqtvJ1hbv1YsMfLHH9MfhLl5twyHQ=="],
"@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.100", "", { "os": "linux", "cpu": "x64" }, "sha512-mssQIwH2DU1aMGSUnTJgahMeAEfF82n2WKeTGabTRkPrcxD/6ML+JFiV9l8/8YA880fBD2Kh1XXGEhN2ZGB5UA=="], "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZjNxrD45P51cdbABoivVQLBakVYwDqAridJbHhkK6T/+EU7YsTrmAu9ae19N9ZGnrlKzLViQF8GOavNUNjAbhw=="],
"@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.100", "", { "os": "win32", "cpu": "arm64" }, "sha512-EuLA6+kiIyW/hbo7k56QTc9/QGxg2bYHQ+XPn1UWPf6tmjzbDUwzhw+y52CNJQZpTXtAQSNtxfiYIbigGBd4TQ=="], "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.2.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-ImMjFPOWE8wcZQ2lUz1D418xonS/5EwnItUF1g5dbp1q9+A0vv2P3bxTenLwMqcYvG4wjO6gKT3n2QLnRd6qKg=="],
"@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.100", "", { "os": "win32", "cpu": "x64" }, "sha512-RyiqbKQ15olR8hK4VsPKL3gHrOIIpqEXhUP0jzXJdQQNusmQKlxHyk5EY3R6hi52IgqjjGxwG+ocVeRb4/VTRg=="], "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.2.0", "", { "os": "win32", "cpu": "x64" }, "sha512-6yfYHTtJ4yzbl8kXCW3Pc4eWbZDYVw21GumwdNgkjJJ2JqQAQ861em0riEoucYAa5qPYYTiMUEw7X4Fv8lGwuQ=="],
"@opentui/solid": ["@opentui/solid@0.1.100", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.100", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-7ADpXOIXtdNzp/r0eQdUuO3kBof7YdcJhQBj/Fv1ckNKoq1XScMuxV2kIK5IzU1Jr3PC4mDxRbek9bMvLJEVjw=="], "@opentui/solid": ["@opentui/solid@0.2.0", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.2.0", "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-kZR9i0FPAcVtomrPsKuSb+D9smooplo9zggFfU2vnnguNuQjGNbEmuJtxhCacy7ig9g3GomdNtQAzD4LiAY+3w=="],
"@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="],
@@ -2296,7 +2311,7 @@
"@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="], "@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], "@types/bun": ["@types/bun@1.3.12", "", { "dependencies": { "bun-types": "1.3.12" } }, "sha512-DBv81elK+/VSwXHDlnH3Qduw+KxkTIWi7TXkAeh24zpi5l0B2kUg9Ga3tb4nJaPcOFswflgi/yAvMVBPrxMB+A=="],
"@types/cacache": ["@types/cacache@20.0.1", "", { "dependencies": { "@types/node": "*", "minipass": "*" } }, "sha512-QlKW3AFoFr/hvPHwFHMIVUH/ZCYeetBNou3PCmxu5LaNDvrtBlPJtIA6uhmU9JRt9oxj7IYoqoLcpxtzpPiTcw=="], "@types/cacache": ["@types/cacache@20.0.1", "", { "dependencies": { "@types/node": "*", "minipass": "*" } }, "sha512-QlKW3AFoFr/hvPHwFHMIVUH/ZCYeetBNou3PCmxu5LaNDvrtBlPJtIA6uhmU9JRt9oxj7IYoqoLcpxtzpPiTcw=="],
@@ -2454,7 +2469,7 @@
"@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="], "@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="],
"@vercel/oidc": ["@vercel/oidc@3.1.0", "", {}, "sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w=="], "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="],
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
@@ -2514,7 +2529,7 @@
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"ai": ["ai@6.0.158", "", { "dependencies": { "@ai-sdk/gateway": "3.0.95", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-gLTp1UXFtMqKUi3XHs33K7UFglbvojkxF/aq337TxnLGOhHIW9+GyP2jwW4hYX87f1es+wId3VQoPRRu9zEStQ=="], "ai": ["ai@6.0.168", "", { "dependencies": { "@ai-sdk/gateway": "3.0.104", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2HqCJuO+1V2aV7vfYs5LFEUfxbkGX+5oa54q/gCCTL7KLTdbxcCu5D7TdLA5kwsrs3Szgjah9q6D9tpjHM3hUQ=="],
"ai-gateway-provider": ["ai-gateway-provider@3.1.2", "", { "optionalDependencies": { "@ai-sdk/amazon-bedrock": "^4.0.62", "@ai-sdk/anthropic": "^3.0.46", "@ai-sdk/azure": "^3.0.31", "@ai-sdk/cerebras": "^2.0.34", "@ai-sdk/cohere": "^3.0.21", "@ai-sdk/deepgram": "^2.0.20", "@ai-sdk/deepseek": "^2.0.20", "@ai-sdk/elevenlabs": "^2.0.20", "@ai-sdk/fireworks": "^2.0.34", "@ai-sdk/google": "^3.0.30", "@ai-sdk/google-vertex": "^4.0.61", "@ai-sdk/groq": "^3.0.24", "@ai-sdk/mistral": "^3.0.20", "@ai-sdk/openai": "^3.0.30", "@ai-sdk/perplexity": "^3.0.19", "@ai-sdk/xai": "^3.0.57", "@openrouter/ai-sdk-provider": "^2.2.3" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^2.0.0", "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.0", "ai": "^6.0.0" } }, "sha512-krGNnJSoO/gJ7Hbe5nQDlsBpDUGIBGtMQTRUaW7s1MylsfvLduba0TLWzQaGtOmNRkP0pGhtGlwsnS6FNQMlyw=="], "ai-gateway-provider": ["ai-gateway-provider@3.1.2", "", { "optionalDependencies": { "@ai-sdk/amazon-bedrock": "^4.0.62", "@ai-sdk/anthropic": "^3.0.46", "@ai-sdk/azure": "^3.0.31", "@ai-sdk/cerebras": "^2.0.34", "@ai-sdk/cohere": "^3.0.21", "@ai-sdk/deepgram": "^2.0.20", "@ai-sdk/deepseek": "^2.0.20", "@ai-sdk/elevenlabs": "^2.0.20", "@ai-sdk/fireworks": "^2.0.34", "@ai-sdk/google": "^3.0.30", "@ai-sdk/google-vertex": "^4.0.61", "@ai-sdk/groq": "^3.0.24", "@ai-sdk/mistral": "^3.0.20", "@ai-sdk/openai": "^3.0.30", "@ai-sdk/perplexity": "^3.0.19", "@ai-sdk/xai": "^3.0.57", "@openrouter/ai-sdk-provider": "^2.2.3" }, "peerDependencies": { "@ai-sdk/openai-compatible": "^2.0.0", "@ai-sdk/provider": "^3.0.0", "@ai-sdk/provider-utils": "^4.0.0", "ai": "^6.0.0" } }, "sha512-krGNnJSoO/gJ7Hbe5nQDlsBpDUGIBGtMQTRUaW7s1MylsfvLduba0TLWzQaGtOmNRkP0pGhtGlwsnS6FNQMlyw=="],
@@ -2714,17 +2729,17 @@
"bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="], "bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], "bun-types": ["bun-types@1.3.12", "", { "dependencies": { "@types/node": "*" } }, "sha512-HqOLj5PoFajAQciOMRiIZGNoKxDJSr6qigAttOX40vJuSp6DN/CxWp9s3C1Xwm4oH7ybueITwiaOcWXoYVoRkA=="],
"bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="], "bun-webgpu": ["bun-webgpu@0.1.7", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.7", "bun-webgpu-darwin-x64": "^0.1.7", "bun-webgpu-linux-x64": "^0.1.7", "bun-webgpu-win32-x64": "^0.1.7" } }, "sha512-KUxUp+oQIf7pPBMD4Hv1TUu7DWaOZ4ciKulTk9to9+Uc8yHoYrMW7L2SJCJ4FHHkywgf/7aLRgRx0b7i6DvGIQ=="],
"bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ=="], "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mRrFFyHzPWjsTRidAZBRcu808CPQBOUL0P6b4nxLhp+XHcV/mbUHERZMgW9s58tsojQfSdzschiQa8q+JCgRWA=="],
"bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng=="], "bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-g0NXGNgvaVCSH/jCWWlfdiquOHkbUN6vP4zqzSkIxWKQeLnqm3oADcok7SO3yIgI7v5mKpRc/ks7NDEKNH+jNQ=="],
"bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA=="], "bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.7", "", { "os": "linux", "cpu": "x64" }, "sha512-UEP7UZdEhx9otvkZczjsszL8ZVlrODANQvgl+C88/bNVmxDoFi7w1fWzGi1sZyakiETjmtFDq2/xCLhbSZxjqw=="],
"bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg=="], "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.7", "", { "os": "win32", "cpu": "x64" }, "sha512-KZktiFkBz6sN7PEm1NVdeaLP5Q5X/PlSHZqefY4nNuWtf0LNvh54NhZe7yVv/Plz/nGbv92b0KHMBY3ki/pp6g=="],
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
@@ -3020,11 +3035,11 @@
"ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="],
"effect": ["effect@4.0.0-beta.48", "", { "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-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="], "effect": ["effect@4.0.0-beta.57", "", { "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-rg32VgXnLKaPRs9tbRDaZ5jxmzNY7ojXt85gSHGUTwdlbWH5Ik+OCUY2q14TXliygPGoHwCAvNWS4bQJOqf00g=="],
"ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="],
"electron": ["electron@40.4.1", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-N1ZXybQZL8kYemO8vAeh9nrk4mSvqlAO8xs0QCHkXIvRnuB/7VGwEehjvQbsU5/f4bmTKpG+2GQERe/zmKpudQ=="], "electron": ["electron@41.2.1", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-teeRThiYGTPKf/2yOW7zZA1bhb91KEQ4yLBPOg7GxpmnkLFLugKgQaAKOrCgdzwsXh/5mFIfmkm+4+wACJKwaA=="],
"electron-builder": ["electron-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.8.1", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw=="], "electron-builder": ["electron-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.8.1", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw=="],
@@ -3304,7 +3319,7 @@
"get-tsconfig": ["get-tsconfig@4.13.8", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-J87BxkLXykmisLQ+KA4x2+O6rVf+PJrtFUO8lGyiRg4lyxJLJ8/v0sRAKdVZQOy6tR6lMRAF1NqzCf9BQijm0w=="], "get-tsconfig": ["get-tsconfig@4.13.8", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-J87BxkLXykmisLQ+KA4x2+O6rVf+PJrtFUO8lGyiRg4lyxJLJ8/v0sRAKdVZQOy6tR6lMRAF1NqzCf9BQijm0w=="],
"ghostty-web": ["ghostty-web@github:anomalyco/ghostty-web#4af877d", {}, "anomalyco-ghostty-web-4af877d", "sha512-fbEK8mtr7ar4ySsF+JUGjhaZrane7dKphanN+SxHt5XXI6yLMAh/Hpf6sNCOyyVa2UlGCd7YpXG/T2v2RUAX+A=="], "ghostty-web": ["ghostty-web@github:anomalyco/ghostty-web#20bd361", {}, "anomalyco-ghostty-web-20bd361", "sha512-dW0nwaiBBcun9y5WJSvm3HxDLe5o9V0xLCndQvWonRVubU8CS1PHxZpLffyPt1YujPWC13ez03aWxcuKBPYYGQ=="],
"gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="], "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
@@ -3312,7 +3327,7 @@
"github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
"gitlab-ai-provider": ["gitlab-ai-provider@6.4.2", "", { "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-Wyw6uslCuipBOr/NYwAtpgXEUJj68iJY5aekad2DjePN99JetKVQBqkLgAy9PZp2EA4OuscfRQu9qKIBN/evNw=="], "gitlab-ai-provider": ["gitlab-ai-provider@6.6.0", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=3.0.0", "@ai-sdk/provider-utils": ">=4.0.0" } }, "sha512-jUxYnKA4XQaPc3wxACDZ8bPDXO0Mzx7cZaBDxbT2uGgLqtGZmSi+9tVNIg7louSS+s/ioVra3SoUz3iOFVhKPA=="],
"glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="], "glob": ["glob@13.0.5", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
@@ -4480,8 +4495,6 @@
"rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], "rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="],
"ripgrep": ["ripgrep@0.3.1", "", { "bin": { "rg": "lib/rg.mjs", "ripgrep": "lib/rg.mjs" } }, "sha512-6bDtNIBh1qPviVIU685/4uv0Ap5t8eS4wiJhy/tR2LdIeIey9CVasENlGS+ul3HnTmGANIp7AjnfsztsRmALfQ=="],
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
"rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="], "rollup": ["rollup@4.60.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.1", "@rollup/rollup-android-arm64": "4.60.1", "@rollup/rollup-darwin-arm64": "4.60.1", "@rollup/rollup-darwin-x64": "4.60.1", "@rollup/rollup-freebsd-arm64": "4.60.1", "@rollup/rollup-freebsd-x64": "4.60.1", "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", "@rollup/rollup-linux-arm-musleabihf": "4.60.1", "@rollup/rollup-linux-arm64-gnu": "4.60.1", "@rollup/rollup-linux-arm64-musl": "4.60.1", "@rollup/rollup-linux-loong64-gnu": "4.60.1", "@rollup/rollup-linux-loong64-musl": "4.60.1", "@rollup/rollup-linux-ppc64-gnu": "4.60.1", "@rollup/rollup-linux-ppc64-musl": "4.60.1", "@rollup/rollup-linux-riscv64-gnu": "4.60.1", "@rollup/rollup-linux-riscv64-musl": "4.60.1", "@rollup/rollup-linux-s390x-gnu": "4.60.1", "@rollup/rollup-linux-x64-gnu": "4.60.1", "@rollup/rollup-linux-x64-musl": "4.60.1", "@rollup/rollup-openbsd-x64": "4.60.1", "@rollup/rollup-openharmony-arm64": "4.60.1", "@rollup/rollup-win32-arm64-msvc": "4.60.1", "@rollup/rollup-win32-ia32-msvc": "4.60.1", "@rollup/rollup-win32-x64-gnu": "4.60.1", "@rollup/rollup-win32-x64-msvc": "4.60.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w=="],
@@ -5152,7 +5165,7 @@
"@ai-sdk/alibaba/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], "@ai-sdk/alibaba/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.70", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-hubTFcfnG3NbrlcDW0tU2fsZhRy/7dF5GCymu4DzBQUYliy2lb7tCeeMhDtFBaYa01qSBHRjkwGnsAdUtDPCwA=="], "@ai-sdk/amazon-bedrock/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.71", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bUWOzrzR0gJKJO/PLGMR4uH2dqEgqGhrsCV+sSpk4KtOEnUQlfjZI/F7BFlqSvVpFbjdgYRRLysAeEZpJ6S1lg=="],
"@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.13", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.0", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-vYahwBAtRaAcFbOmE9aLr12z7RiHYDSLcnogSdxfm7kKfsNa3wH+NU5r7vTeB5rKvLsWyPjVX8iH94brP7umiQ=="], "@ai-sdk/amazon-bedrock/@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.13", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.0", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-vYahwBAtRaAcFbOmE9aLr12z7RiHYDSLcnogSdxfm7kKfsNa3wH+NU5r7vTeB5rKvLsWyPjVX8iH94brP7umiQ=="],
@@ -5170,7 +5183,7 @@
"@ai-sdk/fireworks/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], "@ai-sdk/fireworks/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="],
"@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.70", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-hubTFcfnG3NbrlcDW0tU2fsZhRy/7dF5GCymu4DzBQUYliy2lb7tCeeMhDtFBaYa01qSBHRjkwGnsAdUtDPCwA=="], "@ai-sdk/google-vertex/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.71", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bUWOzrzR0gJKJO/PLGMR4uH2dqEgqGhrsCV+sSpk4KtOEnUQlfjZI/F7BFlqSvVpFbjdgYRRLysAeEZpJ6S1lg=="],
"@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA=="], "@ai-sdk/google-vertex/@ai-sdk/google": ["@ai-sdk/google@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA=="],
@@ -5582,22 +5595,8 @@
"@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="], "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
"@opentelemetry/exporter-trace-otlp-http/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentelemetry/otlp-transformer/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.2.0", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw=="],
"@opentelemetry/sdk-logs/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentelemetry/sdk-metrics/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@2.6.1", "", { "dependencies": { "@opentelemetry/core": "2.6.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA=="],
"@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="], "@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
"@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="], "@oslojs/jwt/@oslojs/encoding": ["@oslojs/encoding@0.4.1", "", {}, "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q=="],
"@pierre/diffs/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="], "@pierre/diffs/@shikijs/transformers": ["@shikijs/transformers@3.20.0", "", { "dependencies": { "@shikijs/core": "3.20.0", "@shikijs/types": "3.20.0" } }, "sha512-PrHHMRr3Q5W1qB/42kJW6laqFyWdhrPF2hNR9qjOm1xcSiAO3hAHo7HaVyHE6pMyevmy3i51O8kuGGXC78uK3g=="],
@@ -5662,6 +5661,10 @@
"@solidjs/start/vite-plugin-solid": ["vite-plugin-solid@2.11.12", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA=="], "@solidjs/start/vite-plugin-solid": ["vite-plugin-solid@2.11.12", "", { "dependencies": { "@babel/core": "^7.23.3", "@types/babel__core": "^7.20.4", "babel-preset-solid": "^1.8.4", "merge-anything": "^5.1.7", "solid-refresh": "^0.6.3", "vitefu": "^1.0.4" }, "peerDependencies": { "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", "solid-js": "^1.7.2", "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@testing-library/jest-dom"] }, "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA=="],
"@standard-community/standard-json/effect": ["effect@4.0.0-beta.48", "", { "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-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
"@standard-community/standard-openapi/effect": ["effect@4.0.0-beta.48", "", { "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-MMAM/ZabuNdNmgXiin+BAanQXK7qM8mlt7nfXDoJ/Gn9V8i89JlCq+2N0AiWmqFLXjGLA0u3FjiOjSOYQk5uMw=="],
"@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "@tailwindcss/oxide/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
@@ -5700,8 +5703,6 @@
"accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], "accepts/negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
"ai/@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.95", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.1.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZmUNNbZl3V42xwQzPaNUi+s8eqR2lnrxf0bvB6YbLXpLjHYv0k2Y78t12cNOfY0bxGeuVVTLyk856uLuQIuXEQ=="],
"ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.93", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.69", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-hcXDU8QDwpAzLVTuY932TQVlIij9+iaVTxc5mPGY6yb//JMAAC5hMVhg93IrxlrxWLvMgjezNgoZGwquR+SGnw=="], "ai-gateway-provider/@ai-sdk/amazon-bedrock": ["@ai-sdk/amazon-bedrock@4.0.93", "", { "dependencies": { "@ai-sdk/anthropic": "3.0.69", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@smithy/eventstream-codec": "^4.0.1", "@smithy/util-utf8": "^4.0.0", "aws4fetch": "^1.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-hcXDU8QDwpAzLVTuY932TQVlIij9+iaVTxc5mPGY6yb//JMAAC5hMVhg93IrxlrxWLvMgjezNgoZGwquR+SGnw=="],
"ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="], "ai-gateway-provider/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.69", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-LshR7X3pFugY0o41G2VKTmg1XoGpSl7uoYWfzk6zjVZLhCfeFiwgpOga+eTV4XY1VVpZwKVqRnkDbIL7K2eH5g=="],
@@ -5712,6 +5713,8 @@
"ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="], "ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.75", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-V8UKK4fNpI9cnrtsZBvUp9O9J6Y9fTKBRoSLyEaNGPirACewixmLDbXsSgAeownPVWiWpK34bFysd+XouI5Ywg=="],
"ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.5.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-r1fJL1Cb3gQDa2MpWH/sfx1BsEW0uzlRriJM6eihaKqbtKDmZoBisF32VcVaQYassighX7NGCkF68EsrZA43uQ=="],
"ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "ajv-keywords/ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="],
"ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -5920,7 +5923,7 @@
"nypm/tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], "nypm/tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="],
"opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.70", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-hubTFcfnG3NbrlcDW0tU2fsZhRy/7dF5GCymu4DzBQUYliy2lb7tCeeMhDtFBaYa01qSBHRjkwGnsAdUtDPCwA=="], "opencode/@ai-sdk/anthropic": ["@ai-sdk/anthropic@3.0.71", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bUWOzrzR0gJKJO/PLGMR4uH2dqEgqGhrsCV+sSpk4KtOEnUQlfjZI/F7BFlqSvVpFbjdgYRRLysAeEZpJ6S1lg=="],
"opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="], "opencode/@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="],
@@ -5946,6 +5949,10 @@
"openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], "openid-client/lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"opentui-spinner/@opentui/core": ["@opentui/core@0.1.105", "", { "dependencies": { "bun-ffi-structs": "0.1.2", "diff": "8.0.2", "jimp": "1.6.0", "marked": "17.0.1", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@dimforge/rapier2d-simd-compat": "^0.17.3", "@opentui/core-darwin-arm64": "0.1.105", "@opentui/core-darwin-x64": "0.1.105", "@opentui/core-linux-arm64": "0.1.105", "@opentui/core-linux-x64": "0.1.105", "@opentui/core-win32-arm64": "0.1.105", "@opentui/core-win32-x64": "0.1.105", "bun-webgpu": "0.1.5", "planck": "^1.4.2", "three": "0.177.0" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-vllSOOCW6VIThV/96GRLJ1IxIBuR+ci6FDvnPIAG4s7SJ/FW6zAkqDn1xrtBwwk/lM3QWjLqy8BZc+zwWvveJA=="],
"opentui-spinner/@opentui/solid": ["@opentui/solid@0.1.105", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.1.105", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.10", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.11" } }, "sha512-uxnaMP802sCI487pv/Hk9xdFdIj9mkg3eNliAqbqR0Shmd4phcjKEZvPRpijjmI99j4s9nul71jzF3h1oz31Nw=="],
"ora/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], "ora/bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
"ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "ora/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -6580,6 +6587,10 @@
"@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="], "@solidjs/start/shiki/@shikijs/types": ["@shikijs/types@1.29.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-VJjK0eIijTZf0QSTODEXCqinjBn0joAHQ+aPSBzrv4O2d/QSbsMw+ZeSRx03kV34Hy7NzUvV/7NqfYGRLrASmw=="],
"@standard-community/standard-json/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@standard-community/standard-openapi/effect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
"@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=="], "@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=="],
"@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
@@ -6702,6 +6713,24 @@
"opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], "opencontrol/@modelcontextprotocol/sdk/zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
"opentui-spinner/@opentui/core/@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.1.105", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1pIL7aer9amwj8EpYoMNtvavKetIe+nX8uBRmYsMQb+KvJoUAZUqENfRW+qHE5WrsOyxx8/QoyXTHw15GG5iLQ=="],
"opentui-spinner/@opentui/core/@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.1.105", "", { "os": "darwin", "cpu": "x64" }, "sha512-hLIRSWlK3gY2NRXJGWiTBiMYSmRDjOYFZF6WtUVXhY2SL3sp08dhmr/6dmAVH+3pKCsCipLEsrrcQX6SAihCTA=="],
"opentui-spinner/@opentui/core/@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.1.105", "", { "os": "linux", "cpu": "arm64" }, "sha512-jlRKfPkozTZEkHEePuCWYcTIUtPm+ieInAwGVqGmjbvqjxdVv1/W/Dt6LEZ/9jpRiOPd+FjXAfLe6wa/XWHr+w=="],
"opentui-spinner/@opentui/core/@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.1.105", "", { "os": "linux", "cpu": "x64" }, "sha512-kfWS1WMg6qHShmxZX9s1tZc/8JcXw6uyy2UtyTbJdRFExtXGH37oKHi8QK8iPL2ExCx4z7zqVnVJfO3X/Wh7lA=="],
"opentui-spinner/@opentui/core/@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.1.105", "", { "os": "win32", "cpu": "arm64" }, "sha512-UFx6A8OpBVbGWK6OAw4GqAqKZgIITJfSOd35pG9yDVKQouHN2OGc2HeeXrH2A4h42p40Xl6IfcqqfllkpC13Dg=="],
"opentui-spinner/@opentui/core/@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.1.105", "", { "os": "win32", "cpu": "x64" }, "sha512-f9FqqUmxehwhF+cgyazm0YT0v0BYTTCPzd6eztqhl74N3x/kC+jOOz2rdJDC/tTBo1JVsF64KupOnhIs6/Cogg=="],
"opentui-spinner/@opentui/core/bun-webgpu": ["bun-webgpu@0.1.5", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.5", "bun-webgpu-darwin-x64": "^0.1.5", "bun-webgpu-linux-x64": "^0.1.5", "bun-webgpu-win32-x64": "^0.1.5" } }, "sha512-91/K6S5whZKX7CWAm9AylhyKrLGRz6BUiiPiM/kXadSnD4rffljCD/q9cNFftm5YXhx4MvLqw33yEilxogJvwA=="],
"opentui-spinner/@opentui/solid/@babel/core": ["@babel/core@7.28.0", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ=="],
"opentui-spinner/@opentui/solid/babel-preset-solid": ["babel-preset-solid@1.9.10", "", { "dependencies": { "babel-plugin-jsx-dom-expressions": "^0.40.3" }, "peerDependencies": { "@babel/core": "^7.0.0", "solid-js": "^1.9.10" }, "optionalPeers": ["solid-js"] }, "sha512-HCelrgua/Y+kqO8RyL04JBWS/cVdrtUv/h45GntgQY+cJl4eBcKkCDV3TdMjtKx1nXwRaR9QXslM/Npm1dxdZQ=="],
"ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "ora/bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
"ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], "ora/bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
@@ -7044,6 +7073,18 @@
"opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], "opencontrol/@modelcontextprotocol/sdk/express/type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="],
"opentui-spinner/@opentui/core/bun-webgpu/@webgpu/types": ["@webgpu/types@0.1.69", "", {}, "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ=="],
"opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lIsDkPzJzPl6yrB5CUOINJFPnTRv6fF/Q8J1mAr43ogSp86WZEg9XZKaT6f3EUJ+9ETogGoMnoj1q0AwHUTbAQ=="],
"opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-darwin-x64": ["bun-webgpu-darwin-x64@0.1.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-uEddf5U7GvKIkM/BV18rUKtYHL6d0KeqBjNHwfqDH9QgEo9KVSKvJXS5I/sMefk5V5pIYE+8tQhtrREevhocng=="],
"opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-linux-x64": ["bun-webgpu-linux-x64@0.1.6", "", { "os": "linux", "cpu": "x64" }, "sha512-Y/f15j9r8ba0xUz+3lATtS74OE+PPzQXO7Do/1eCluJcuOlfa77kMjvBK/ShWnem3Y9xqi59pebTPOGRB+CaJA=="],
"opentui-spinner/@opentui/core/bun-webgpu/bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-MHSFAKqizISb+C5NfDrFe3g0Al5Njnu0j/A+oO2Q+bIWX+fUYjBSowiYE1ZXJx65KuryuB+tiM7Qh6cQbVvkEg=="],
"opentui-spinner/@opentui/solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], "ora/bl/buffer/ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
"pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="], "pkg-up/find-up/locate-path/p-locate": ["p-locate@3.0.0", "", { "dependencies": { "p-limit": "^2.0.0" } }, "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="],
Generated
+3 -3
View File
@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1773909469, "lastModified": 1776683584,
"narHash": "sha256-vglVrLfHjFIzIdV9A27Ugul6rh3I1qHbbitGW7dk420=", "narHash": "sha256-NuTLMrr10Tng72hurYG8jYQ4XKK8wnpJmOGcPiis96g=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "7149c06513f335be57f26fcbbbe34afda923882b", "rev": "9dd5558b06dbdacbf635a3dd36dce1b1a7ee3a89",
"type": "github" "type": "github"
}, },
"original": { "original": {
+24 -1
View File
@@ -115,6 +115,27 @@ const zenLiteCouponFirstMonth100 = new stripe.Coupon("ZenLiteCouponFirstMonth100
appliesToProducts: [zenLiteProduct.id], appliesToProducts: [zenLiteProduct.id],
duration: "once", duration: "once",
}) })
const zenLiteCouponThreeMonths100 = new stripe.Coupon("ZenLiteCoupon3Months100", {
name: "3 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 3,
})
const zenLiteCouponSixMonths100 = new stripe.Coupon("ZenLiteCoupon6Months100", {
name: "6 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 6,
})
const zenLiteCouponTwelveMonths100 = new stripe.Coupon("ZenLiteCoupon12Months100", {
name: "12 months 100% off",
percentOff: 100,
appliesToProducts: [zenLiteProduct.id],
duration: "repeating",
durationInMonths: 12,
})
const zenLitePrice = new stripe.Price("ZenLitePrice", { const zenLitePrice = new stripe.Price("ZenLitePrice", {
product: zenLiteProduct.id, product: zenLiteProduct.id,
currency: "usd", currency: "usd",
@@ -131,6 +152,9 @@ const ZEN_LITE_PRICE = new sst.Linkable("ZEN_LITE_PRICE", {
priceInr: 92900, priceInr: 92900,
firstMonth50Coupon: zenLiteCouponFirstMonth50.id, firstMonth50Coupon: zenLiteCouponFirstMonth50.id,
firstMonth100Coupon: zenLiteCouponFirstMonth100.id, firstMonth100Coupon: zenLiteCouponFirstMonth100.id,
threeMonths100Coupon: zenLiteCouponThreeMonths100.id,
sixMonths100Coupon: zenLiteCouponSixMonths100.id,
twelveMonths100Coupon: zenLiteCouponTwelveMonths100.id,
}, },
}) })
@@ -236,7 +260,6 @@ new sst.cloudflare.x.SolidStart("Console", {
SALESFORCE_INSTANCE_URL, SALESFORCE_INSTANCE_URL,
ZEN_BLACK_PRICE, ZEN_BLACK_PRICE,
ZEN_LITE_PRICE, ZEN_LITE_PRICE,
new sst.Secret("ZEN_LITE_COUPON_FIRST_MONTH_100_INVITEES"),
new sst.Secret("ZEN_LIMITS"), new sst.Secret("ZEN_LIMITS"),
new sst.Secret("ZEN_SESSION_SECRET"), new sst.Secret("ZEN_SESSION_SECRET"),
...ZEN_MODELS, ...ZEN_MODELS,
+4 -4
View File
@@ -1,8 +1,8 @@
{ {
"nodeModules": { "nodeModules": {
"x86_64-linux": "sha256-yGb+EPlFNDptIi4yFdJ0z7fhAyfOCRXu0GpNxrOnLVA=", "x86_64-linux": "sha256-cBfg4pJ4mjsfS4MFFASBaZZykArgIoeo/3woOcSGy1U=",
"aarch64-linux": "sha256-h8oBwLB6LnFN4xGB0/ocvxRbBMwewrEck91ADihTUCk=", "aarch64-linux": "sha256-Q6cqUwfqbscdrPW0uHcfshhQINjJi0HiyURMSdOOCf4=",
"aarch64-darwin": "sha256-/0IOgoihi4OR2AhDDfstLn2DcY/261v/KRQV4Pwhbmk=", "aarch64-darwin": "sha256-1AtfsD1D9YxWSEsecPJF9XsvsxsWTtVtkP5l6UW43og=",
"x86_64-darwin": "sha256-rPkqF+led93s1plBbhFpszrnzF2H+EUz8QlfbUkSHvM=" "x86_64-darwin": "sha256-YS5/8YTf9LymAUbjXVrGDfxtKVJrpZbPnnCtsGHSHoU="
} }
} }
+18 -17
View File
@@ -7,6 +7,7 @@
sysctl, sysctl,
makeBinaryWrapper, makeBinaryWrapper,
models-dev, models-dev,
ripgrep,
installShellFiles, installShellFiles,
versionCheckHook, versionCheckHook,
writableTmpDirAsHomeHook, writableTmpDirAsHomeHook,
@@ -51,25 +52,25 @@ stdenvNoCC.mkDerivation (finalAttrs: {
runHook postBuild runHook postBuild
''; '';
installPhase = installPhase = ''
'' runHook preInstall
runHook preInstall
install -Dm755 dist/opencode-*/bin/opencode $out/bin/opencode install -Dm755 dist/opencode-*/bin/opencode $out/bin/opencode
install -Dm644 schema.json $out/share/opencode/schema.json install -Dm644 schema.json $out/share/opencode/schema.json
''
# bun runs sysctl to detect if dunning on rosetta2 wrapProgram $out/bin/opencode \
+ lib.optionalString stdenvNoCC.hostPlatform.isDarwin '' --prefix PATH : ${
wrapProgram $out/bin/opencode \ lib.makeBinPath (
--prefix PATH : ${ [
lib.makeBinPath [ ripgrep
sysctl
] ]
} # bun runs sysctl to detect if running on rosetta2
'' ++ lib.optional stdenvNoCC.hostPlatform.isDarwin sysctl
+ '' )
runHook postInstall }
'';
runHook postInstall
'';
postInstall = lib.optionalString (stdenvNoCC.buildPlatform.canExecute stdenvNoCC.hostPlatform) '' postInstall = lib.optionalString (stdenvNoCC.buildPlatform.canExecute stdenvNoCC.hostPlatform) ''
# trick yargs into also generating zsh completions # trick yargs into also generating zsh completions
+11 -7
View File
@@ -4,10 +4,10 @@
"description": "AI-powered development tool", "description": "AI-powered development tool",
"private": true, "private": true,
"type": "module", "type": "module",
"packageManager": "bun@1.3.11", "packageManager": "bun@1.3.13",
"scripts": { "scripts": {
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts", "dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
"dev:desktop": "bun --cwd packages/desktop tauri dev", "dev:desktop": "bun --cwd packages/desktop-electron dev",
"dev:web": "bun --cwd packages/app dev", "dev:web": "bun --cwd packages/app dev",
"dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev", "dev:console": "ulimit -n 10240 2>/dev/null; bun run --cwd packages/console/app dev",
"dev:storybook": "bun --cwd packages/storybook storybook", "dev:storybook": "bun --cwd packages/storybook storybook",
@@ -27,13 +27,15 @@
"packages/slack" "packages/slack"
], ],
"catalog": { "catalog": {
"@effect/opentelemetry": "4.0.0-beta.48", "@effect/opentelemetry": "4.0.0-beta.57",
"@effect/platform-node": "4.0.0-beta.48", "@effect/platform-node": "4.0.0-beta.57",
"@npmcli/arborist": "9.4.0", "@npmcli/arborist": "9.4.0",
"@types/bun": "1.3.11", "@types/bun": "1.3.12",
"@types/cross-spawn": "6.0.6", "@types/cross-spawn": "6.0.6",
"@octokit/rest": "22.0.0", "@octokit/rest": "22.0.0",
"@hono/zod-validator": "0.4.2", "@hono/zod-validator": "0.4.2",
"@opentui/core": "0.2.0",
"@opentui/solid": "0.2.0",
"ulid": "3.0.1", "ulid": "3.0.1",
"@kobalte/core": "0.13.11", "@kobalte/core": "0.13.11",
"@types/luxon": "3.7.1", "@types/luxon": "3.7.1",
@@ -44,14 +46,15 @@
"@cloudflare/workers-types": "4.20251008.0", "@cloudflare/workers-types": "4.20251008.0",
"@openauthjs/openauth": "0.0.0-20250322224806", "@openauthjs/openauth": "0.0.0-20250322224806",
"@pierre/diffs": "1.1.0-beta.18", "@pierre/diffs": "1.1.0-beta.18",
"opentui-spinner": "0.0.6",
"@solid-primitives/storage": "4.3.3", "@solid-primitives/storage": "4.3.3",
"@tailwindcss/vite": "4.1.11", "@tailwindcss/vite": "4.1.11",
"diff": "8.0.2", "diff": "8.0.2",
"dompurify": "3.3.1", "dompurify": "3.3.1",
"drizzle-kit": "1.0.0-beta.19-d95b7a4", "drizzle-kit": "1.0.0-beta.19-d95b7a4",
"drizzle-orm": "1.0.0-beta.19-d95b7a4", "drizzle-orm": "1.0.0-beta.19-d95b7a4",
"effect": "4.0.0-beta.48", "effect": "4.0.0-beta.57",
"ai": "6.0.158", "ai": "6.0.168",
"cross-spawn": "7.0.6", "cross-spawn": "7.0.6",
"hono": "4.10.7", "hono": "4.10.7",
"hono-openapi": "1.1.2", "hono-openapi": "1.1.2",
@@ -125,6 +128,7 @@
"@types/node": "catalog:" "@types/node": "catalog:"
}, },
"patchedDependencies": { "patchedDependencies": {
"@npmcli/agent@4.0.0": "patches/@npmcli%2Fagent@4.0.0.patch",
"@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch",
"solid-js@1.9.10": "patches/solid-js@1.9.10.patch" "solid-js@1.9.10": "patches/solid-js@1.9.10.patch"
} }
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/app", "name": "@opencode-ai/app",
"version": "1.4.8", "version": "1.14.30",
"description": "", "description": "",
"type": "module", "type": "module",
"exports": { "exports": {
@@ -42,7 +42,7 @@
"@kobalte/core": "catalog:", "@kobalte/core": "catalog:",
"@opencode-ai/sdk": "workspace:*", "@opencode-ai/sdk": "workspace:*",
"@opencode-ai/ui": "workspace:*", "@opencode-ai/ui": "workspace:*",
"@opencode-ai/shared": "workspace:*", "@opencode-ai/core": "workspace:*",
"@shikijs/transformers": "3.9.2", "@shikijs/transformers": "3.9.2",
"@solid-primitives/active-element": "2.1.3", "@solid-primitives/active-element": "2.1.3",
"@solid-primitives/audio": "1.4.2", "@solid-primitives/audio": "1.4.2",
+30 -22
View File
@@ -82,7 +82,15 @@ declare global {
} }
function QueryProvider(props: ParentProps) { function QueryProvider(props: ParentProps) {
const client = new QueryClient() const client = new QueryClient({
defaultOptions: {
queries: {
refetchOnReconnect: false,
refetchOnMount: false,
refetchOnWindowFocus: false,
},
},
})
return <QueryClientProvider client={client}>{props.children}</QueryClientProvider> return <QueryClientProvider client={client}>{props.children}</QueryClientProvider>
} }
@@ -141,13 +149,11 @@ export function AppBaseProviders(props: ParentProps<{ locale?: Locale }>) {
<LanguageProvider locale={props.locale}> <LanguageProvider locale={props.locale}>
<UiI18nBridge> <UiI18nBridge>
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}> <ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
<QueryProvider> <DialogProvider>
<DialogProvider> <MarkedProvider>
<MarkedProvider> <FileComponentProvider component={File}>{props.children}</FileComponentProvider>
<FileComponentProvider component={File}>{props.children}</FileComponentProvider> </MarkedProvider>
</MarkedProvider> </DialogProvider>
</DialogProvider>
</QueryProvider>
</ErrorBoundary> </ErrorBoundary>
</UiI18nBridge> </UiI18nBridge>
</LanguageProvider> </LanguageProvider>
@@ -293,20 +299,22 @@ export function AppInterface(props: {
> >
<ConnectionGate disableHealthCheck={props.disableHealthCheck}> <ConnectionGate disableHealthCheck={props.disableHealthCheck}>
<ServerKey> <ServerKey>
<GlobalSDKProvider> <QueryProvider>
<GlobalSyncProvider> <GlobalSDKProvider>
<Dynamic <GlobalSyncProvider>
component={props.router ?? Router} <Dynamic
root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>} component={props.router ?? Router}
> root={(routerProps) => <RouterRoot appChildren={props.children}>{routerProps.children}</RouterRoot>}
<Route path="/" component={HomeRoute} /> >
<Route path="/:dir" component={DirectoryLayout}> <Route path="/" component={HomeRoute} />
<Route path="/" component={SessionIndexRoute} /> <Route path="/:dir" component={DirectoryLayout}>
<Route path="/session/:id?" component={SessionRoute} /> <Route path="/" component={SessionIndexRoute} />
</Route> <Route path="/session/:id?" component={SessionRoute} />
</Dynamic> </Route>
</GlobalSyncProvider> </Dynamic>
</GlobalSDKProvider> </GlobalSyncProvider>
</GlobalSDKProvider>
</QueryProvider>
</ServerKey> </ServerKey>
</ConnectionGate> </ConnectionGate>
</ServerProvider> </ServerProvider>
@@ -9,9 +9,10 @@ import { createStore } from "solid-js/store"
import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSDK } from "@/context/global-sdk"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
import { type LocalProject, getAvatarColors } from "@/context/layout" import { type LocalProject, getAvatarColors } from "@/context/layout"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Avatar } from "@opencode-ai/ui/avatar" import { Avatar } from "@opencode-ai/ui/avatar"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { getProjectAvatarSource } from "@/pages/layout/sidebar-items"
const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const const AVATAR_COLOR_KEYS = ["pink", "mint", "orange", "purple", "cyan", "lime"] as const
@@ -26,8 +27,8 @@ export function DialogEditProject(props: { project: LocalProject }) {
const [store, setStore] = createStore({ const [store, setStore] = createStore({
name: defaultName(), name: defaultName(),
color: props.project.icon?.color || "pink", color: props.project.icon?.color,
iconUrl: props.project.icon?.override || "", iconOverride: props.project.icon?.override,
startup: props.project.commands?.start ?? "", startup: props.project.commands?.start ?? "",
dragOver: false, dragOver: false,
iconHover: false, iconHover: false,
@@ -39,7 +40,7 @@ export function DialogEditProject(props: { project: LocalProject }) {
if (!file.type.startsWith("image/")) return if (!file.type.startsWith("image/")) return
const reader = new FileReader() const reader = new FileReader()
reader.onload = (e) => { reader.onload = (e) => {
setStore("iconUrl", e.target?.result as string) setStore("iconOverride", e.target?.result as string)
setStore("iconHover", false) setStore("iconHover", false)
} }
reader.readAsDataURL(file) reader.readAsDataURL(file)
@@ -68,7 +69,7 @@ export function DialogEditProject(props: { project: LocalProject }) {
} }
function clearIcon() { function clearIcon() {
setStore("iconUrl", "") setStore("iconOverride", "")
} }
const saveMutation = useMutation(() => ({ const saveMutation = useMutation(() => ({
@@ -81,17 +82,17 @@ export function DialogEditProject(props: { project: LocalProject }) {
projectID: props.project.id, projectID: props.project.id,
directory: props.project.worktree, directory: props.project.worktree,
name, name,
icon: { color: store.color, override: store.iconUrl }, icon: { color: store.color || "", override: store.iconOverride || "" },
commands: { start }, commands: { start },
}) })
globalSync.project.icon(props.project.worktree, store.iconUrl || undefined) globalSync.project.icon(props.project.worktree, store.iconOverride || undefined)
dialog.close() dialog.close()
return return
} }
globalSync.project.meta(props.project.worktree, { globalSync.project.meta(props.project.worktree, {
name, name,
icon: { color: store.color, override: store.iconUrl || undefined }, icon: { color: store.color || undefined, override: store.iconOverride || undefined },
commands: { start: start || undefined }, commands: { start: start || undefined },
}) })
dialog.close() dialog.close()
@@ -130,13 +131,13 @@ export function DialogEditProject(props: { project: LocalProject }) {
classList={{ classList={{
"border-text-interactive-base bg-surface-info-base/20": store.dragOver, "border-text-interactive-base bg-surface-info-base/20": store.dragOver,
"border-border-base hover:border-border-strong": !store.dragOver, "border-border-base hover:border-border-strong": !store.dragOver,
"overflow-hidden": !!store.iconUrl, "overflow-hidden": !!store.iconOverride,
}} }}
onDrop={handleDrop} onDrop={handleDrop}
onDragOver={handleDragOver} onDragOver={handleDragOver}
onDragLeave={handleDragLeave} onDragLeave={handleDragLeave}
onClick={() => { onClick={() => {
if (store.iconUrl && store.iconHover) { if (store.iconOverride && store.iconHover) {
clearIcon() clearIcon()
} else { } else {
iconInput?.click() iconInput?.click()
@@ -144,7 +145,11 @@ export function DialogEditProject(props: { project: LocalProject }) {
}} }}
> >
<Show <Show
when={store.iconUrl} when={getProjectAvatarSource(props.project.id, {
color: store.color,
url: props.project.icon?.url,
override: store.iconOverride,
})}
fallback={ fallback={
<div class="size-full flex items-center justify-center"> <div class="size-full flex items-center justify-center">
<Avatar <Avatar
@@ -155,18 +160,20 @@ export function DialogEditProject(props: { project: LocalProject }) {
</div> </div>
} }
> >
<img {(src) => (
src={store.iconUrl} <img
alt={language.t("dialog.project.edit.icon.alt")} src={src()}
class="size-full object-cover" alt={language.t("dialog.project.edit.icon.alt")}
/> class="size-full object-cover"
/>
)}
</Show> </Show>
</div> </div>
<div <div
class="absolute inset-0 size-16 bg-surface-raised-stronger-non-alpha/90 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity" class="absolute inset-0 size-16 bg-surface-raised-stronger-non-alpha/90 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity"
classList={{ classList={{
"opacity-100": store.iconHover && !store.iconUrl, "opacity-100": store.iconHover && !store.iconOverride,
"opacity-0": !(store.iconHover && !store.iconUrl), "opacity-0": !(store.iconHover && !store.iconOverride),
}} }}
> >
<Icon name="cloud-upload" size="large" class="text-icon-on-interactive-base drop-shadow-sm" /> <Icon name="cloud-upload" size="large" class="text-icon-on-interactive-base drop-shadow-sm" />
@@ -174,8 +181,8 @@ export function DialogEditProject(props: { project: LocalProject }) {
<div <div
class="absolute inset-0 size-16 bg-surface-raised-stronger-non-alpha/90 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity" class="absolute inset-0 size-16 bg-surface-raised-stronger-non-alpha/90 rounded-[6px] z-10 pointer-events-none flex items-center justify-center transition-opacity"
classList={{ classList={{
"opacity-100": store.iconHover && !!store.iconUrl, "opacity-100": store.iconHover && !!store.iconOverride,
"opacity-0": !(store.iconHover && !!store.iconUrl), "opacity-0": !(store.iconHover && !!store.iconOverride),
}} }}
> >
<Icon name="trash" size="large" class="text-icon-on-interactive-base drop-shadow-sm" /> <Icon name="trash" size="large" class="text-icon-on-interactive-base drop-shadow-sm" />
@@ -198,7 +205,7 @@ export function DialogEditProject(props: { project: LocalProject }) {
</div> </div>
</div> </div>
<Show when={!store.iconUrl}> <Show when={!store.iconOverride}>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<label class="text-12-medium text-text-weak">{language.t("dialog.project.edit.color")}</label> <label class="text-12-medium text-text-weak">{language.t("dialog.project.edit.color")}</label>
<div class="flex gap-1.5"> <div class="flex gap-1.5">
@@ -215,7 +222,10 @@ export function DialogEditProject(props: { project: LocalProject }) {
"bg-transparent border border-transparent hover:bg-surface-base-hover hover:border-border-weak-base": "bg-transparent border border-transparent hover:bg-surface-base-hover hover:border-border-weak-base":
store.color !== color, store.color !== color,
}} }}
onClick={() => setStore("color", color)} onClick={() => {
if (store.color === color && !props.project.icon?.url) return
setStore("color", store.color === color ? undefined : color)
}}
> >
<Avatar <Avatar
fallback={store.name || defaultName()} fallback={store.name || defaultName()}
+1 -1
View File
@@ -9,7 +9,7 @@ import { List } from "@opencode-ai/ui/list"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { extractPromptFromParts } from "@/utils/prompt" import { extractPromptFromParts } from "@/utils/prompt"
import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
interface ForkableMessage { interface ForkableMessage {
@@ -3,7 +3,7 @@ import { Dialog } from "@opencode-ai/ui/dialog"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import type { ListRef } from "@opencode-ai/ui/list" import type { ListRef } from "@opencode-ai/ui/list"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import fuzzysort from "fuzzysort" import fuzzysort from "fuzzysort"
import { createMemo, createResource, createSignal } from "solid-js" import { createMemo, createResource, createSignal } from "solid-js"
import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSDK } from "@/context/global-sdk"
@@ -4,8 +4,8 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Keybind } from "@opencode-ai/ui/keybind" import { Keybind } from "@opencode-ai/ui/keybind"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js" import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js"
import { formatKeybind, useCommand, type CommandOption } from "@/context/command" import { formatKeybind, useCommand, type CommandOption } from "@/context/command"
@@ -1,13 +1,12 @@
import { useMutation } from "@tanstack/solid-query" import { useMutation, useQueryClient } from "@tanstack/solid-query"
import { Component, createEffect, createMemo, on, Show } from "solid-js" import { Component, createMemo, Show } from "solid-js"
import { createStore } from "solid-js/store"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useSDK } from "@/context/sdk" import { useSDK } from "@/context/sdk"
import { Dialog } from "@opencode-ai/ui/dialog" import { Dialog } from "@opencode-ai/ui/dialog"
import { List } from "@opencode-ai/ui/list" import { List } from "@opencode-ai/ui/list"
import { Switch } from "@opencode-ai/ui/switch" import { Switch } from "@opencode-ai/ui/switch"
import { showToast } from "@opencode-ai/ui/toast"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { loadMcpQuery } from "@/context/global-sync"
const statusLabels = { const statusLabels = {
connected: "mcp.status.connected", connected: "mcp.status.connected",
@@ -20,48 +19,7 @@ export const DialogSelectMcp: Component = () => {
const sync = useSync() const sync = useSync()
const sdk = useSDK() const sdk = useSDK()
const language = useLanguage() const language = useLanguage()
const [state, setState] = createStore({ const queryClient = useQueryClient()
done: false,
loading: false,
})
createEffect(
on(
() => sync.data.mcp_ready,
(ready, prev) => {
if (!ready && prev) setState("done", false)
},
{ defer: true },
),
)
createEffect(() => {
if (state.done || state.loading) return
if (sync.data.mcp_ready) {
setState("done", true)
return
}
setState("loading", true)
void sdk.client.mcp
.status()
.then((result) => {
sync.set("mcp", result.data ?? {})
sync.set("mcp_ready", true)
setState("done", true)
})
.catch((err) => {
setState("done", true)
showToast({
variant: "error",
title: language.t("common.requestFailed"),
description: err instanceof Error ? err.message : String(err),
})
})
.finally(() => {
setState("loading", false)
})
})
const items = createMemo(() => const items = createMemo(() =>
Object.entries(sync.data.mcp ?? {}) Object.entries(sync.data.mcp ?? {})
@@ -71,16 +29,10 @@ export const DialogSelectMcp: Component = () => {
const toggle = useMutation(() => ({ const toggle = useMutation(() => ({
mutationFn: async (name: string) => { mutationFn: async (name: string) => {
const status = sync.data.mcp[name] if (sync.data.mcp[name]?.status === "connected") await sdk.client.mcp.disconnect({ name })
if (status?.status === "connected") { else await sdk.client.mcp.connect({ name })
await sdk.client.mcp.disconnect({ name })
} else {
await sdk.client.mcp.connect({ name })
}
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
}, },
onSuccess: () => queryClient.refetchQueries({ queryKey: loadMcpQuery(sync.directory).queryKey }),
})) }))
const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length) const enabledCount = createMemo(() => items().filter((i) => i.status === "connected").length)
@@ -504,7 +504,7 @@ export function DialogSelectServer() {
return ( return (
<Dialog title={formTitle()}> <Dialog title={formTitle()}>
<div class="flex flex-col gap-2"> <div class="flex flex-1 min-h-0 flex-col gap-2">
<Show <Show
when={!isFormMode()} when={!isFormMode()}
fallback={ fallback={
@@ -539,7 +539,7 @@ export function DialogSelectServer() {
if (x) void select(x) if (x) void select(x)
}} }}
divider={true} divider={true}
class="px-5 [&_[data-slot=list-search-wrapper]]:w-full [&_[data-slot=list-scroll]]h-[300px] [&_[data-slot=list-scroll]]:overflow-y-auto [&_[data-slot=list-items]]:bg-surface-base [&_[data-slot=list-items]]:rounded-md [&_[data-slot=list-item]]:min-h-14 [&_[data-slot=list-item]]:p-3 [&_[data-slot=list-item]]:!bg-transparent" class="flex-1 min-h-0 px-5 [&_[data-slot=list-search-wrapper]]:w-full [&_[data-slot=list-scroll]]:flex-1 [&_[data-slot=list-scroll]]:overflow-y-auto [&_[data-slot=list-items]]:bg-surface-base [&_[data-slot=list-items]]:rounded-md [&_[data-slot=list-item]]:min-h-14 [&_[data-slot=list-item]]:p-3 [&_[data-slot=list-item]]:!bg-transparent"
> >
{(i) => { {(i) => {
const key = ServerConnection.key(i) const key = ServerConnection.key(i)
@@ -619,7 +619,7 @@ export function DialogSelectServer() {
</List> </List>
</Show> </Show>
<div class="px-5 pb-5"> <div class="shrink-0 px-5 pb-5">
<Show <Show
when={isFormMode()} when={isFormMode()}
fallback={ fallback={
+75 -50
View File
@@ -1,6 +1,6 @@
import { useFilteredList } from "@opencode-ai/ui/hooks" import { useFilteredList } from "@opencode-ai/ui/hooks"
import { useSpring } from "@opencode-ai/ui/motion-spring" import { useSpring } from "@opencode-ai/ui/motion-spring"
import { createEffect, on, Component, Show, onCleanup, createMemo, createSignal } from "solid-js" import { createEffect, on, Component, Show, onCleanup, createMemo, createSignal, createResource } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { useLocal } from "@/context/local" import { useLocal } from "@/context/local"
import { selectionFromLines, type SelectedLineRange, useFile } from "@/context/file" import { selectionFromLines, type SelectedLineRange, useFile } from "@/context/file"
@@ -54,7 +54,7 @@ import { PromptImageAttachments } from "./prompt-input/image-attachments"
import { PromptDragOverlay } from "./prompt-input/drag-overlay" import { PromptDragOverlay } from "./prompt-input/drag-overlay"
import { promptPlaceholder } from "./prompt-input/placeholder" import { promptPlaceholder } from "./prompt-input/placeholder"
import { ImagePreview } from "@opencode-ai/ui/image-preview" import { ImagePreview } from "@opencode-ai/ui/image-preview"
import { useQuery } from "@tanstack/solid-query" import { useQueries } from "@tanstack/solid-query"
import { loadAgentsQuery, loadProvidersQuery } from "@/context/global-sync/bootstrap" import { loadAgentsQuery, loadProvidersQuery } from "@/context/global-sync/bootstrap"
interface PromptInputProps { interface PromptInputProps {
@@ -270,7 +270,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 }) const buttonsSpring = useSpring(() => (store.mode === "normal" ? 1 : 0), { visualDuration: 0.2, bounce: 0 })
const motion = (value: number) => ({ const motion = (value: number) => ({
opacity: value, opacity: value,
transform: `scale(${0.95 + value * 0.05})`, transform: `scale(${0.98 + value * 0.02})`,
filter: `blur(${(1 - value) * 2}px)`, filter: `blur(${(1 - value) * 2}px)`,
"pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const), "pointer-events": value > 0.5 ? ("auto" as const) : ("none" as const),
}) })
@@ -345,7 +345,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
promptPlaceholder({ promptPlaceholder({
mode: store.mode, mode: store.mode,
commentCount: commentCount(), commentCount: commentCount(),
example: suggest() ? language.t(EXAMPLES[store.placeholder]) : "", example: suggest() ? (store.mode === "shell" ? "git status" : language.t(EXAMPLES[store.placeholder])) : "",
suggest: suggest(), suggest: suggest(),
t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never), t: (key, params) => language.t(key as Parameters<typeof language.t>[0], params as never),
}), }),
@@ -1252,16 +1252,23 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
} }
} }
const agentsQuery = useQuery(() => loadAgentsQuery(sdk.directory)) const [agentsQuery, globalProvidersQuery, providersQuery] = useQueries(() => ({
queries: [loadAgentsQuery(sdk.directory), loadProvidersQuery(null), loadProvidersQuery(sdk.directory)],
}))
const agentsLoading = () => agentsQuery.isLoading const agentsLoading = () => agentsQuery.isLoading
const agentsShouldFadeIn = createMemo((prev) => prev ?? agentsLoading())
const globalProvidersQuery = useQuery(() => loadProvidersQuery(null))
const providersQuery = useQuery(() => loadProvidersQuery(sdk.directory))
const providersLoading = () => agentsLoading() || providersQuery.isLoading || globalProvidersQuery.isLoading const providersLoading = () => agentsLoading() || providersQuery.isLoading || globalProvidersQuery.isLoading
const providersShouldFadeIn = createMemo((prev) => prev ?? providersLoading())
const [promptReady] = createResource(
() => prompt.ready().promise,
(p) => p,
)
return ( return (
<div class="relative size-full _max-h-[320px] flex flex-col gap-0"> <div class="relative size-full _max-h-[320px] flex flex-col gap-0">
{(promptReady(), null)}
<PromptPopover <PromptPopover
popover={store.popover} popover={store.popover}
setSlashPopoverRef={(el) => (slashPopoverRef = el)} setSlashPopoverRef={(el) => (slashPopoverRef = el)}
@@ -1358,15 +1365,13 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
}} }}
style={{ "padding-bottom": space }} style={{ "padding-bottom": space }}
/> />
<Show when={!prompt.dirty()}> <div
<div class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate"
class="absolute top-0 inset-x-0 pl-3 pr-2 pt-2 text-14-regular text-text-weak pointer-events-none whitespace-nowrap truncate" classList={{ "font-mono!": store.mode === "shell" }}
classList={{ "font-mono!": store.mode === "shell" }} style={{ "padding-bottom": space, display: prompt.dirty() ? "none" : undefined }}
style={{ "padding-bottom": space }} >
> {placeholder()}
{placeholder()} </div>
</div>
</Show>
</div> </div>
<div <div
@@ -1398,12 +1403,11 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<IconButton <IconButton
data-action="prompt-submit" data-action="prompt-submit"
type="submit" type="submit"
disabled={store.mode !== "normal" || (!working() && blank())} disabled={!working() && blank()}
tabIndex={store.mode === "normal" ? undefined : -1} tabIndex={store.mode === "normal" ? undefined : -1}
icon={stopping() ? "stop" : "arrow-up"} icon={stopping() ? "stop" : store.mode === "shell" ? "arrow-undo-down" : "arrow-up"}
variant="primary" variant="primary"
class="size-8" class="size-8"
style={buttons()}
aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")} aria-label={stopping() ? language.t("prompt.action.stop") : language.t("prompt.action.send")}
/> />
</Tooltip> </Tooltip>
@@ -1446,18 +1450,31 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
<div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0"> <div class="px-1.75 pt-5.5 pb-2 flex items-center gap-2 min-w-0">
<div class="flex items-center gap-1.5 min-w-0 flex-1 relative"> <div class="flex items-center gap-1.5 min-w-0 flex-1 relative">
<div <div
class="h-7 flex items-center gap-1.5 max-w-[160px] min-w-0 absolute inset-y-0 left-0" class="h-7 flex items-center gap-1.5 min-w-0 absolute inset-0"
style={{ style={{
padding: "0 4px 0 8px", padding: "0 0px 0 8px",
...shell(), ...shell(),
}} }}
> >
<span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span> <Icon name="console" />
<div class="size-4 shrink-0" /> <span class="truncate text-13-medium text-text-base">{language.t("prompt.mode.shell")}</span>
<div class="flex-1" />
<Button
variant="ghost"
class="text-text-base"
onClick={() => {
setStore("mode", "normal")
}}
>
{language.t("common.cancel")}
</Button>
</div> </div>
<div class="flex items-center gap-1.5 min-w-0 flex-1 h-7"> <div class="flex items-center gap-1.5 min-w-0 flex-1 h-7">
<Show when={!agentsLoading()}> <Show when={!agentsLoading()}>
<div data-component="prompt-agent-control"> <div
data-component="prompt-agent-control"
style={agentsShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
>
<TooltipKeybind <TooltipKeybind
placement="top" placement="top"
gutter={4} gutter={4}
@@ -1483,7 +1500,10 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</Show> </Show>
<Show when={!providersLoading()}> <Show when={!providersLoading()}>
<Show when={store.mode !== "shell"}> <Show when={store.mode !== "shell"}>
<div data-component="prompt-model-control"> <div
data-component="prompt-model-control"
style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
>
<Show <Show
when={providers.paid().length > 0} when={providers.paid().length > 0}
fallback={ fallback={
@@ -1554,30 +1574,35 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
</TooltipKeybind> </TooltipKeybind>
</Show> </Show>
</div> </div>
<div data-component="prompt-variant-control"> <Show when={variants().length > 2}>
<TooltipKeybind <div
placement="top" data-component="prompt-variant-control"
gutter={4} style={providersShouldFadeIn() ? { animation: "fade-in 0.3s" } : undefined}
title={language.t("command.model.variant.cycle")}
keybind={command.keybind("model.variant.cycle")}
> >
<Select <TooltipKeybind
size="normal" placement="top"
options={variants()} gutter={4}
current={local.model.variant.current() ?? "default"} title={language.t("command.model.variant.cycle")}
label={(x) => (x === "default" ? language.t("common.default") : x)} keybind={command.keybind("model.variant.cycle")}
onSelect={(value) => { >
local.model.variant.set(value === "default" ? undefined : value) <Select
restoreFocus() size="normal"
}} options={variants()}
class="capitalize max-w-[160px] text-text-base" current={local.model.variant.current() ?? "default"}
valueClass="truncate text-13-regular text-text-base" label={(x) => (x === "default" ? language.t("common.default") : x)}
triggerStyle={control()} onSelect={(value) => {
triggerProps={{ "data-action": "prompt-model-variant" }} local.model.variant.set(value === "default" ? undefined : value)
variant="ghost" restoreFocus()
/> }}
</TooltipKeybind> class="capitalize max-w-[160px] text-text-base"
</div> valueClass="truncate text-13-regular text-text-base"
triggerStyle={control()}
triggerProps={{ "data-action": "prompt-model-variant" }}
variant="ghost"
/>
</TooltipKeybind>
</div>
</Show>
</Show> </Show>
</Show> </Show>
</div> </div>
@@ -1,4 +1,4 @@
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client" import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client"
import type { FileSelection } from "@/context/file" import type { FileSelection } from "@/context/file"
import { encodeFilePath } from "@/context/file/path" import { encodeFilePath } from "@/context/file/path"
@@ -2,7 +2,7 @@ import { Component, For, Show } from "solid-js"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/core/util/path"
import type { ContextItem } from "@/context/prompt" import type { ContextItem } from "@/context/prompt"
type PromptContextItem = ContextItem & { key: string } type PromptContextItem = ContextItem & { key: string }
@@ -12,7 +12,7 @@ describe("promptPlaceholder", () => {
suggest: true, suggest: true,
t, t,
}) })
expect(value).toBe("prompt.placeholder.shell") expect(value).toBe("prompt.placeholder.shell:example")
}) })
test("returns summarize placeholders for comment context", () => { test("returns summarize placeholders for comment context", () => {
@@ -7,7 +7,7 @@ type PromptPlaceholderInput = {
} }
export function promptPlaceholder(input: PromptPlaceholderInput) { export function promptPlaceholder(input: PromptPlaceholderInput) {
if (input.mode === "shell") return input.t("prompt.placeholder.shell") if (input.mode === "shell") return input.t("prompt.placeholder.shell", { example: input.example })
if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments") if (input.commentCount > 1) return input.t("prompt.placeholder.summarizeComments")
if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment") if (input.commentCount === 1) return input.t("prompt.placeholder.summarizeComment")
if (!input.suggest) return input.t("prompt.placeholder.simple") if (!input.suggest) return input.t("prompt.placeholder.simple")
@@ -1,7 +1,7 @@
import { Component, For, Match, Show, Switch } from "solid-js" import { Component, For, Match, Show, Switch } from "solid-js"
import { FileIcon } from "@opencode-ai/ui/file-icon" import { FileIcon } from "@opencode-ai/ui/file-icon"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
export type AtOption = export type AtOption =
| { type: "agent"; name: string; display: string } | { type: "agent"; name: string; display: string }
@@ -74,7 +74,7 @@ beforeAll(async () => {
showToast: () => 0, showToast: () => 0,
})) }))
mock.module("@opencode-ai/shared/util/encode", () => ({ mock.module("@opencode-ai/core/util/encode", () => ({
base64Encode: (value: string) => value, base64Encode: (value: string) => value,
})) }))
@@ -1,7 +1,7 @@
import type { Message, Session } from "@opencode-ai/sdk/v2/client" import type { Message, Session } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { useNavigate, useParams } from "@solidjs/router" import { useNavigate, useParams } from "@solidjs/router"
import { batch, type Accessor } from "solid-js" import { batch, type Accessor } from "solid-js"
import type { FileSelection } from "@/context/file" import type { FileSelection } from "@/context/file"
@@ -1,8 +1,8 @@
import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js" import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js"
import type { JSX } from "solid-js" import type { JSX } from "solid-js"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { findLast } from "@opencode-ai/shared/util/array" import { findLast } from "@opencode-ai/core/util/array"
import { same } from "@/utils/same" import { same } from "@/utils/same"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Accordion } from "@opencode-ai/ui/accordion" import { Accordion } from "@opencode-ai/ui/accordion"
@@ -7,7 +7,7 @@ import { Keybind } from "@opencode-ai/ui/keybind"
import { Spinner } from "@opencode-ai/ui/spinner" import { Spinner } from "@opencode-ai/ui/spinner"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js" import { createEffect, createMemo, createSignal, For, onMount, Show } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { Portal } from "solid-js/web" import { Portal } from "solid-js/web"
@@ -5,7 +5,7 @@ import { useSDK } from "@/context/sdk"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Mark } from "@opencode-ai/ui/logo" import { Mark } from "@opencode-ai/ui/logo"
import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { getDirectory, getFilename } from "@opencode-ai/core/util/path"
const MAIN_WORKTREE = "main" const MAIN_WORKTREE = "main"
const CREATE_WORKTREE = "create" const CREATE_WORKTREE = "create"
@@ -5,7 +5,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { TooltipKeybind } from "@opencode-ai/ui/tooltip"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { useFile } from "@/context/file" import { useFile } from "@/context/file"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { useCommand } from "@/context/command" import { useCommand } from "@/context/command"
+180 -85
View File
@@ -11,7 +11,9 @@ import { showToast } from "@opencode-ai/ui/toast"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { usePermission } from "@/context/permission" import { usePermission } from "@/context/permission"
import { usePlatform } from "@/context/platform" import { usePlatform, type DisplayBackend } from "@/context/platform"
import { useGlobalSync } from "@/context/global-sync"
import { useGlobalSDK } from "@/context/global-sdk"
import { import {
monoDefault, monoDefault,
monoFontFamily, monoFontFamily,
@@ -19,6 +21,9 @@ import {
sansDefault, sansDefault,
sansFontFamily, sansFontFamily,
sansInput, sansInput,
terminalDefault,
terminalFontFamily,
terminalInput,
useSettings, useSettings,
} from "@/context/settings" } from "@/context/settings"
import { decode64 } from "@/utils/base64" import { decode64 } from "@/utils/base64"
@@ -37,6 +42,18 @@ type ThemeOption = {
name: 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, // 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. // delay the playback by 100ms during quick selection changes and pause existing sounds.
const stopDemoSound = () => { const stopDemoSound = () => {
@@ -72,10 +89,6 @@ export const SettingsGeneral: Component = () => {
const params = useParams() const params = useParams()
const settings = useSettings() const settings = useSettings()
onMount(() => {
void theme.loadThemes()
})
const [store, setStore] = createStore({ const [store, setStore] = createStore({
checking: false, checking: false,
}) })
@@ -125,27 +138,25 @@ export const SettingsGeneral: Component = () => {
return return
} }
const actions = const actions = platform.updateAndRestart
platform.update && platform.restart ? [
? [ {
{ label: language.t("toast.update.action.installRestart"),
label: language.t("toast.update.action.installRestart"), onClick: async () => {
onClick: async () => { await platform.updateAndRestart!()
await platform.update!()
await platform.restart!()
},
}, },
{ },
label: language.t("toast.update.action.notYet"), {
onClick: "dismiss" as const, label: language.t("toast.update.action.notYet"),
}, onClick: "dismiss" as const,
] },
: [ ]
{ : [
label: language.t("toast.update.action.notYet"), {
onClick: "dismiss" as const, label: language.t("toast.update.action.notYet"),
}, onClick: "dismiss" as const,
] },
]
showToast({ showToast({
persistent: true, persistent: true,
@@ -164,6 +175,70 @@ export const SettingsGeneral: Component = () => {
const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) }))) const themeOptions = createMemo<ThemeOption[]>(() => theme.ids().map((id) => ({ id, name: theme.name(id) })))
const globalSync = useGlobalSync()
const globalSdk = useGlobalSDK()
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 },
)
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 colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [ const colorSchemeOptions = createMemo((): { value: ColorScheme; label: string }[] => [
{ value: "system", label: language.t("theme.scheme.system") }, { value: "system", label: language.t("theme.scheme.system") },
{ value: "light", label: language.t("theme.scheme.light") }, { value: "light", label: language.t("theme.scheme.light") },
@@ -181,6 +256,7 @@ export const SettingsGeneral: Component = () => {
const soundOptions = [noneSound, ...SOUND_OPTIONS] const soundOptions = [noneSound, ...SOUND_OPTIONS]
const mono = () => monoInput(settings.appearance.font()) const mono = () => monoInput(settings.appearance.font())
const sans = () => sansInput(settings.appearance.uiFont()) const sans = () => sansInput(settings.appearance.uiFont())
const terminal = () => terminalInput(settings.appearance.terminalFont())
const soundSelectProps = ( const soundSelectProps = (
enabled: () => boolean, enabled: () => boolean,
@@ -241,6 +317,28 @@ export const SettingsGeneral: Component = () => {
</div> </div>
</SettingsRow> </SettingsRow>
<SettingsRow
title={language.t("settings.general.row.shell.title")}
description={language.t("settings.general.row.shell.description")}
>
<Select
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 })
}}
variant="secondary"
size="small"
triggerVariant="settings"
triggerStyle={{ "min-width": "180px" }}
/>
</SettingsRow>
<SettingsRow <SettingsRow
title={language.t("settings.general.row.reasoningSummaries.title")} title={language.t("settings.general.row.reasoningSummaries.title")}
description={language.t("settings.general.row.reasoningSummaries.description")} description={language.t("settings.general.row.reasoningSummaries.description")}
@@ -276,6 +374,18 @@ export const SettingsGeneral: Component = () => {
/> />
</div> </div>
</SettingsRow> </SettingsRow>
<SettingsRow
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>
</SettingsRow>
</SettingsList> </SettingsList>
</div> </div>
) )
@@ -451,6 +561,29 @@ export const SettingsGeneral: Component = () => {
/> />
</div> </div>
</SettingsRow> </SettingsRow>
<SettingsRow
title={language.t("settings.general.row.terminalFont.title")}
description={language.t("settings.general.row.terminalFont.description")}
>
<div class="w-full sm:w-[220px]">
<TextField
data-action="settings-terminal-font"
label={language.t("settings.general.row.terminalFont.title")}
hideLabel
type="text"
value={terminal()}
onChange={(value) => settings.appearance.setTerminalFont(value)}
placeholder={terminalDefault}
spellcheck={false}
autocorrect="off"
autocomplete="off"
autocapitalize="off"
class="text-12-regular"
style={{ "font-family": terminalFontFamily(settings.appearance.terminalFont()) }}
/>
</div>
</SettingsRow>
</SettingsList> </SettingsList>
</div> </div>
) )
@@ -614,70 +747,32 @@ export const SettingsGeneral: Component = () => {
<SoundsSection /> <SoundsSection />
{/*<Show when={platform.platform === "desktop" && platform.os === "windows" && platform.getWslEnabled}>
{(_) => {
const [enabledResource, actions] = createResource(() => platform.getWslEnabled?.())
const enabled = () => (enabledResource.state === "pending" ? undefined : enabledResource.latest)
return (
<div class="flex flex-col gap-1">
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.desktop.section.wsl")}</h3>
<SettingsList>
<SettingsRow
title={language.t("settings.desktop.wsl.title")}
description={language.t("settings.desktop.wsl.description")}
>
<div data-action="settings-wsl">
<Switch
checked={enabled() ?? false}
disabled={enabledResource.state === "pending"}
onChange={(checked) => platform.setWslEnabled?.(checked)?.finally(() => actions.refetch())}
/>
</div>
</SettingsRow>
</SettingsList>
</div>
)
}}
</Show>*/}
<UpdatesSection /> <UpdatesSection />
<Show when={linux()}> <Show when={linux()}>
{(_) => { <div class="flex flex-col gap-1">
const [valueResource, actions] = createResource(() => platform.getDisplayBackend?.()) <h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3>
const value = () => (valueResource.state === "pending" ? undefined : valueResource.latest)
const onChange = (checked: boolean) => <SettingsList>
platform.setDisplayBackend?.(checked ? "wayland" : "auto").finally(() => actions.refetch()) <SettingsRow
title={
return ( <div class="flex items-center gap-2">
<div class="flex flex-col gap-1"> <span>{language.t("settings.general.row.wayland.title")}</span>
<h3 class="text-14-medium text-text-strong pb-2">{language.t("settings.general.section.display")}</h3> <Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top">
<span class="text-text-weak">
<SettingsList> <Icon name="help" size="small" />
<SettingsRow </span>
title={ </Tooltip>
<div class="flex items-center gap-2"> </div>
<span>{language.t("settings.general.row.wayland.title")}</span> }
<Tooltip value={language.t("settings.general.row.wayland.tooltip")} placement="top"> description={language.t("settings.general.row.wayland.description")}
<span class="text-text-weak"> >
<Icon name="help" size="small" /> <div data-action="settings-wayland">
</span> <Switch checked={displayBackend.latest === "wayland"} onChange={onDisplayBackendChange} />
</Tooltip> </div>
</div> </SettingsRow>
} </SettingsList>
description={language.t("settings.general.row.wayland.description")} </div>
>
<div data-action="settings-wayland">
<Switch checked={value() === "wayland"} onChange={onChange} />
</div>
</SettingsRow>
</SettingsList>
</div>
)
}}
</Show> </Show>
<Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}> <Show when={desktop() && import.meta.env.VITE_OPENCODE_CHANNEL === "beta"}>
@@ -3,7 +3,7 @@ import { useDialog } from "@opencode-ai/ui/context/dialog"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { Switch } from "@opencode-ai/ui/switch" import { Switch } from "@opencode-ai/ui/switch"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
import { useMutation } from "@tanstack/solid-query" import { useMutation, useQueryClient } from "@tanstack/solid-query"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js" import { type Accessor, createEffect, createMemo, For, type JSXElement, onCleanup, Show } from "solid-js"
@@ -15,6 +15,7 @@ import { useSDK } from "@/context/sdk"
import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server" import { normalizeServerUrl, ServerConnection, useServer } from "@/context/server"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health" import { useCheckServerHealth, type ServerHealth } from "@/utils/server-health"
import { loadMcpQuery } from "@/context/global-sync"
const pollMs = 10_000 const pollMs = 10_000
@@ -137,14 +138,14 @@ const useMcpToggleMutation = () => {
const sync = useSync() const sync = useSync()
const sdk = useSDK() const sdk = useSDK()
const language = useLanguage() const language = useLanguage()
const queryClient = useQueryClient()
return useMutation(() => ({ return useMutation(() => ({
mutationFn: async (name: string) => { mutationFn: async (name: string) => {
const status = sync.data.mcp[name] const status = sync.data.mcp[name]
await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name })) await (status?.status === "connected" ? sdk.client.mcp.disconnect({ name }) : sdk.client.mcp.connect({ name }))
const result = await sdk.client.mcp.status()
if (result.data) sync.set("mcp", result.data)
}, },
onSuccess: () => queryClient.refetchQueries({ queryKey: loadMcpQuery(sync.directory).queryKey }),
onError: (err) => { onError: (err) => {
showToast({ showToast({
variant: "error", variant: "error",
@@ -162,14 +163,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
const dialog = useDialog() const dialog = useDialog()
const language = useLanguage() const language = useLanguage()
const navigate = useNavigate() const navigate = useNavigate()
const sdk = useSDK()
const [load, setLoad] = createStore({
lspDone: false,
lspLoading: false,
mcpDone: false,
mcpLoading: false,
})
const fail = (err: unknown) => { const fail = (err: unknown) => {
showToast({ showToast({
@@ -181,40 +174,6 @@ export function StatusPopoverBody(props: { shown: Accessor<boolean> }) {
createEffect(() => { createEffect(() => {
if (!props.shown()) return if (!props.shown()) return
if (!sync.data.mcp_ready && !load.mcpDone && !load.mcpLoading) {
setLoad("mcpLoading", true)
void sdk.client.mcp
.status()
.then((result) => {
sync.set("mcp", result.data ?? {})
sync.set("mcp_ready", true)
})
.catch((err) => {
setLoad("mcpDone", true)
fail(err)
})
.finally(() => {
setLoad("mcpLoading", false)
})
}
if (!sync.data.lsp_ready && !load.lspDone && !load.lspLoading) {
setLoad("lspLoading", true)
void sdk.client.lsp
.status()
.then((result) => {
sync.set("lsp", result.data ?? [])
sync.set("lsp_ready", true)
})
.catch((err) => {
setLoad("lspDone", true)
fail(err)
})
.finally(() => {
setLoad("lspLoading", false)
})
}
}) })
let dialogRun = 0 let dialogRun = 0
+3 -3
View File
@@ -11,7 +11,7 @@ import { useLanguage } from "@/context/language"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useSDK } from "@/context/sdk" import { useSDK } from "@/context/sdk"
import { useServer } from "@/context/server" import { useServer } from "@/context/server"
import { monoFontFamily, useSettings } from "@/context/settings" import { terminalFontFamily, useSettings } from "@/context/settings"
import type { LocalPTY } from "@/context/terminal" import type { LocalPTY } from "@/context/terminal"
import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters" import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters"
import { terminalWriter } from "@/utils/terminal-writer" import { terminalWriter } from "@/utils/terminal-writer"
@@ -300,7 +300,7 @@ export const Terminal = (props: TerminalProps) => {
}) })
createEffect(() => { createEffect(() => {
const font = monoFontFamily(settings.appearance.font()) const font = terminalFontFamily(settings.appearance.terminalFont())
if (!term) return if (!term) return
setOptionIfSupported(term, "fontFamily", font) setOptionIfSupported(term, "fontFamily", font)
scheduleFit() scheduleFit()
@@ -360,7 +360,7 @@ export const Terminal = (props: TerminalProps) => {
cols: restoreSize?.cols, cols: restoreSize?.cols,
rows: restoreSize?.rows, rows: restoreSize?.rows,
fontSize: 14, fontSize: 14,
fontFamily: monoFontFamily(settings.appearance.font()), fontFamily: terminalFontFamily(settings.appearance.terminalFont()),
allowTransparency: false, allowTransparency: false,
convertEol: false, convertEol: false,
theme: terminalColors(), theme: terminalColors(),
+1 -1
View File
@@ -3,7 +3,7 @@ import { createStore, produce, reconcile } from "solid-js/store"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { useSDK } from "./sdk" import { useSDK } from "./sdk"
import { useSync } from "./sync" import { useSync } from "./sync"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
+132 -137
View File
@@ -8,25 +8,32 @@ import type {
Todo, Todo,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js" import { batch, createContext, getOwner, onCleanup, onMount, type ParentProps, untrack, useContext } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { Persist, persisted } from "@/utils/persist"
import type { InitError } from "../pages/error" import type { InitError } from "../pages/error"
import { useGlobalSDK } from "./global-sdk" import { useGlobalSDK } from "./global-sdk"
import { bootstrapDirectory, bootstrapGlobal, clearProviderRev } from "./global-sync/bootstrap" import {
bootstrapDirectory,
bootstrapGlobal,
clearProviderRev,
loadGlobalConfigQuery,
loadPathQuery,
loadProjectsQuery,
loadProvidersQuery,
} from "./global-sync/bootstrap"
import { createChildStoreManager } from "./global-sync/child-store" import { createChildStoreManager } from "./global-sync/child-store"
import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer" import { applyDirectoryEvent, applyGlobalEvent, cleanupDroppedSessionCaches } from "./global-sync/event-reducer"
import { createRefreshQueue } from "./global-sync/queue"
import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch" import { clearSessionPrefetchDirectory } from "./global-sync/session-prefetch"
import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load" import { estimateRootSessionTotal, loadRootSessionsWithFallback } from "./global-sync/session-load"
import { trimSessions } from "./global-sync/session-trim" import { trimSessions } from "./global-sync/session-trim"
import type { ProjectMeta } from "./global-sync/types" import type { ProjectMeta } from "./global-sync/types"
import { SESSION_RECENT_LIMIT } from "./global-sync/types" import { SESSION_RECENT_LIMIT } from "./global-sync/types"
import { sanitizeProject } from "./global-sync/utils"
import { formatServerError } from "@/utils/server-errors" import { formatServerError } from "@/utils/server-errors"
import { queryOptions, skipToken, useQueryClient } from "@tanstack/solid-query" import { queryOptions, skipToken, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/solid-query"
import { createRefreshQueue } from "./global-sync/queue"
import { directoryKey } from "./global-sync/utils"
type GlobalStore = { type GlobalStore = {
ready: boolean ready: boolean
@@ -45,6 +52,18 @@ type GlobalStore = {
export const loadSessionsQuery = (directory: string) => export const loadSessionsQuery = (directory: string) =>
queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken }) queryOptions<null>({ queryKey: [directory, "loadSessions"], queryFn: skipToken })
export const loadMcpQuery = (directory: string, sdk?: OpencodeClient) =>
queryOptions({
queryKey: [directory, "mcp"],
queryFn: sdk ? () => sdk.mcp.status().then((r) => r.data ?? {}) : skipToken,
})
export const loadLspQuery = (directory: string, sdk?: OpencodeClient) =>
queryOptions({
queryKey: [directory, "lsp"],
queryFn: sdk ? () => sdk.lsp.status().then((r) => r.data ?? []) : skipToken,
})
function createGlobalSync() { function createGlobalSync() {
const globalSDK = useGlobalSDK() const globalSDK = useGlobalSDK()
const language = useLanguage() const language = useLanguage()
@@ -56,54 +75,49 @@ function createGlobalSync() {
const sessionLoads = new Map<string, Promise<void>>() const sessionLoads = new Map<string, Promise<void>>()
const sessionMeta = new Map<string, { limit: number }>() const sessionMeta = new Map<string, { limit: number }>()
const [projectCache, setProjectCache, projectInit] = persisted( const [configQuery, providerQuery, pathQuery] = useQueries(() => ({
Persist.global("globalSync.project", ["globalSync.project.v1"]), queries: [loadGlobalConfigQuery(), loadProvidersQuery(null), loadPathQuery(null), loadProjectsQuery()],
createStore({ value: [] as Project[] }), }))
)
const [globalStore, setGlobalStore] = createStore<GlobalStore>({ const [globalStore, setGlobalStore] = createStore<GlobalStore>({
ready: false, get ready() {
path: { state: "", config: "", worktree: "", directory: "", home: "" }, return bootstrap.isPending
project: projectCache.value, },
project: [],
session_todo: {}, session_todo: {},
provider: { all: [], connected: [], default: {} },
provider_auth: {}, provider_auth: {},
config: {}, get path() {
reload: undefined, const EMPTY = { state: "", config: "", worktree: "", directory: "", home: "" }
if (pathQuery.isLoading) return EMPTY
return pathQuery.data ?? EMPTY
},
get provider() {
const EMPTY = { all: [], connected: [], default: {} }
if (providerQuery.isLoading) return EMPTY
return providerQuery.data ?? EMPTY
},
get config() {
if (configQuery.isLoading) return {}
return configQuery.data ?? {}
},
get reload() {
return updateConfigMutation.isPending ? "pending" : undefined
},
}) })
const queryClient = useQueryClient() const queryClient = useQueryClient()
let active = true
let projectWritten = false
let bootedAt = 0 let bootedAt = 0
let bootingRoot = false let bootingRoot = false
let eventFrame: number | undefined let eventFrame: number | undefined
let eventTimer: ReturnType<typeof setTimeout> | undefined let eventTimer: ReturnType<typeof setTimeout> | undefined
onCleanup(() => {
active = false
})
onCleanup(() => { onCleanup(() => {
if (eventFrame !== undefined) cancelAnimationFrame(eventFrame) if (eventFrame !== undefined) cancelAnimationFrame(eventFrame)
if (eventTimer !== undefined) clearTimeout(eventTimer) if (eventTimer !== undefined) clearTimeout(eventTimer)
}) })
const cacheProjects = () => { const setProjects = (next: Project[] | ((draft: Project[]) => Project[])) => {
setProjectCache(
"value",
untrack(() => globalStore.project.map(sanitizeProject)),
)
}
const setProjects = (next: Project[] | ((draft: Project[]) => void)) => {
projectWritten = true
if (typeof next === "function") {
setGlobalStore("project", produce(next))
cacheProjects()
return
}
setGlobalStore("project", next) setGlobalStore("project", next)
cacheProjects()
} }
const setBootStore = ((...input: unknown[]) => { const setBootStore = ((...input: unknown[]) => {
@@ -114,24 +128,30 @@ function createGlobalSync() {
return (setGlobalStore as (...args: unknown[]) => unknown)(...input) return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
}) as typeof setGlobalStore }) as typeof setGlobalStore
const bootstrap = useQuery(() => ({
queryKey: ["bootstrap"],
queryFn: async () => {
await bootstrapGlobal({
globalSDK: globalSDK.client,
requestFailedTitle: language.t("common.requestFailed"),
translate: language.t,
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore: setBootStore,
queryClient,
})
bootedAt = Date.now()
return bootedAt
},
}))
const set = ((...input: unknown[]) => { const set = ((...input: unknown[]) => {
if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) { if (input[0] === "project" && (Array.isArray(input[1]) || typeof input[1] === "function")) {
setProjects(input[1] as Project[] | ((draft: Project[]) => void)) setProjects(input[1] as Project[] | ((draft: Project[]) => Project[]))
return input[1] return input[1]
} }
return (setGlobalStore as (...args: unknown[]) => unknown)(...input) return (setGlobalStore as (...args: unknown[]) => unknown)(...input)
}) as typeof setGlobalStore }) as typeof setGlobalStore
if (projectInit instanceof Promise) {
void projectInit.then(() => {
if (!active) return
if (projectWritten) return
const cached = projectCache.value
if (cached.length === 0) return
setGlobalStore("project", cached)
})
}
const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => { const setSessionTodo = (sessionID: string, todos: Todo[] | undefined) => {
if (!sessionID) return if (!sessionID) return
if (!todos) { if (!todos) {
@@ -150,10 +170,23 @@ function createGlobalSync() {
const queue = createRefreshQueue({ const queue = createRefreshQueue({
paused, paused,
bootstrap, key: directoryKey,
bootstrap: () => queryClient.fetchQuery({ queryKey: ["bootstrap"] }),
bootstrapInstance, bootstrapInstance,
}) })
const sdkFor = (directory: string) => {
const key = directoryKey(directory)
const cached = sdkCache.get(key)
if (cached) return cached
const sdk = globalSDK.createClient({
directory,
throwOnError: true,
})
sdkCache.set(key, sdk)
return sdk
}
const children = createChildStoreManager({ const children = createChildStoreManager({
owner, owner,
isBooting: (directory) => booting.has(directory), isBooting: (directory) => booting.has(directory),
@@ -162,33 +195,25 @@ function createGlobalSync() {
void bootstrapInstance(directory) void bootstrapInstance(directory)
}, },
onDispose: (directory) => { onDispose: (directory) => {
queue.clear(directory) const key = directoryKey(directory)
sessionMeta.delete(directory) queue.clear(key)
sdkCache.delete(directory) sessionMeta.delete(key)
clearProviderRev(directory) sdkCache.delete(key)
clearSessionPrefetchDirectory(directory) clearProviderRev(key)
clearSessionPrefetchDirectory(key)
}, },
translate: language.t, translate: language.t,
getSdk: sdkFor,
}) })
const sdkFor = (directory: string) => {
const cached = sdkCache.get(directory)
if (cached) return cached
const sdk = globalSDK.createClient({
directory,
throwOnError: true,
})
sdkCache.set(directory, sdk)
return sdk
}
async function loadSessions(directory: string) { async function loadSessions(directory: string) {
const pending = sessionLoads.get(directory) const key = directoryKey(directory)
const pending = sessionLoads.get(key)
if (pending) return pending if (pending) return pending
children.pin(directory) children.pin(key)
const [store, setStore] = children.child(directory, { bootstrap: false }) const [store, setStore] = children.child(directory, { bootstrap: false })
const meta = sessionMeta.get(directory) const meta = sessionMeta.get(key)
if (meta && meta.limit >= store.limit) { if (meta && meta.limit >= store.limit) {
const next = trimSessions(store.session, { const next = trimSessions(store.session, {
limit: store.limit, limit: store.limit,
@@ -198,14 +223,14 @@ function createGlobalSync() {
setStore("session", reconcile(next, { key: "id" })) setStore("session", reconcile(next, { key: "id" }))
cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo) cleanupDroppedSessionCaches(store, setStore, next, setSessionTodo)
} }
children.unpin(directory) children.unpin(key)
return return
} }
const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT) const limit = Math.max(store.limit + SESSION_RECENT_LIMIT, SESSION_RECENT_LIMIT)
const promise = queryClient const promise = queryClient
.fetchQuery({ .fetchQuery({
...loadSessionsQuery(directory), ...loadSessionsQuery(key),
queryFn: () => queryFn: () =>
loadRootSessionsWithFallback({ loadRootSessionsWithFallback({
directory, directory,
@@ -223,17 +248,19 @@ function createGlobalSync() {
limit, limit,
permission: store.permission, permission: store.permission,
}) })
setStore( batch(() => {
"sessionTotal", setStore(
estimateRootSessionTotal({ "sessionTotal",
count: nonArchived.length, estimateRootSessionTotal({
limit: x.limit, count: nonArchived.length,
limited: x.limited, limit: x.limit,
}), limited: x.limited,
) }),
setStore("session", reconcile(sessions, { key: "id" })) )
cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo) setStore("session", reconcile(sessions, { key: "id" }))
sessionMeta.set(directory, { limit }) cleanupDroppedSessionCaches(store, setStore, sessions, setSessionTodo)
})
sessionMeta.set(key, { limit })
}) })
.catch((err) => { .catch((err) => {
console.error("Failed to load sessions", err) console.error("Failed to load sessions", err)
@@ -248,23 +275,24 @@ function createGlobalSync() {
}) })
.then(() => {}) .then(() => {})
sessionLoads.set(directory, promise) sessionLoads.set(key, promise)
void promise.finally(() => { void promise.finally(() => {
sessionLoads.delete(directory) sessionLoads.delete(key)
children.unpin(directory) children.unpin(key)
}) })
return promise return promise
} }
async function bootstrapInstance(directory: string) { async function bootstrapInstance(directory: string) {
if (!directory) return const key = directoryKey(directory)
const pending = booting.get(directory) if (!key) return
const pending = booting.get(key)
if (pending) return pending if (pending) return pending
children.pin(directory) children.pin(key)
const promise = Promise.resolve().then(async () => { const promise = Promise.resolve().then(async () => {
const child = children.ensureChild(directory) const child = children.ensureChild(directory)
const cache = children.vcsCache.get(directory) const cache = children.vcsCache.get(key)
if (!cache) return if (!cache) return
const sdk = sdkFor(directory) const sdk = sdkFor(directory)
await bootstrapDirectory({ await bootstrapDirectory({
@@ -285,16 +313,17 @@ function createGlobalSync() {
}) })
}) })
booting.set(directory, promise) booting.set(key, promise)
void promise.finally(() => { void promise.finally(() => {
booting.delete(directory) booting.delete(key)
children.unpin(directory) children.unpin(key)
}) })
return promise return promise
} }
const unsub = globalSDK.event.listen((e) => { const unsub = globalSDK.event.listen((e) => {
const directory = e.name const directory = e.name
const key = directoryKey(directory)
const event = e.details const event = e.details
const recent = bootingRoot || Date.now() - bootedAt < 1500 const recent = bootingRoot || Date.now() - bootedAt < 1500
@@ -304,7 +333,7 @@ function createGlobalSync() {
project: globalStore.project, project: globalStore.project,
refresh: () => { refresh: () => {
if (recent) return if (recent) return
queue.refresh() bootstrap.refetch()
}, },
setGlobalProject: setProjects, setGlobalProject: setProjects,
}) })
@@ -317,9 +346,9 @@ function createGlobalSync() {
return return
} }
const existing = children.children[directory] const existing = children.children[key]
if (!existing) return if (!existing) return
children.mark(directory) children.mark(key)
const [store, setStore] = existing const [store, setStore] = existing
applyDirectoryEvent({ applyDirectoryEvent({
event, event,
@@ -328,14 +357,9 @@ function createGlobalSync() {
setStore, setStore,
push: queue.push, push: queue.push,
setSessionTodo, setSessionTodo,
vcsCache: children.vcsCache.get(directory), vcsCache: children.vcsCache.get(key),
loadLsp: () => { loadLsp: () => {
void sdkFor(directory) void queryClient.fetchQuery(loadLspQuery(key, sdkFor(directory)))
.lsp.status()
.then((x) => {
setStore("lsp", x.data ?? [])
setStore("lsp_ready", true)
})
}, },
}) })
}) })
@@ -346,27 +370,10 @@ function createGlobalSync() {
}) })
onCleanup(() => { onCleanup(() => {
for (const directory of Object.keys(children.children)) { for (const directory of Object.keys(children.children)) {
children.disposeDirectory(directory) children.disposeDirectory(directoryKey(directory))
} }
}) })
async function bootstrap() {
bootingRoot = true
try {
await bootstrapGlobal({
globalSDK: globalSDK.client,
requestFailedTitle: language.t("common.requestFailed"),
translate: language.t,
formatMoreCount: (count) => language.t("common.moreCountSuffix", { count }),
setGlobalStore: setBootStore,
queryClient,
})
bootedAt = Date.now()
} finally {
bootingRoot = false
}
}
onMount(() => { onMount(() => {
if (typeof requestAnimationFrame === "function") { if (typeof requestAnimationFrame === "function") {
eventFrame = requestAnimationFrame(() => { eventFrame = requestAnimationFrame(() => {
@@ -382,7 +389,6 @@ function createGlobalSync() {
void globalSDK.event.start() void globalSDK.event.start()
}, 0) }, 0)
} }
void bootstrap()
}) })
const projectApi = { const projectApi = {
@@ -395,21 +401,10 @@ function createGlobalSync() {
}, },
} }
const updateConfig = async (config: Config) => { const updateConfigMutation = useMutation(() => ({
setGlobalStore("reload", "pending") mutationFn: (config: Config) => globalSDK.client.global.config.update({ config }),
return globalSDK.client.global.config onSuccess: () => bootstrap.refetch(),
.update({ config }) }))
.then(bootstrap)
.then(() => {
queue.refresh()
setGlobalStore("reload", undefined)
queue.refresh()
})
.catch((error) => {
setGlobalStore("reload", undefined)
throw error
})
}
return { return {
data: globalStore, data: globalStore,
@@ -422,8 +417,8 @@ function createGlobalSync() {
}, },
child: children.child, child: children.child,
peek: children.peek, peek: children.peek,
bootstrap, // bootstrap,
updateConfig, updateConfig: updateConfigMutation.mutateAsync,
project: projectApi, project: projectApi,
todo: { todo: {
set: setSessionTodo, set: setSessionTodo,
+134 -125
View File
@@ -11,15 +11,15 @@ import type {
Todo, Todo,
} from "@opencode-ai/sdk/v2/client" } from "@opencode-ai/sdk/v2/client"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { batch } from "solid-js" import { batch } from "solid-js"
import { reconcile, type SetStoreFunction, type Store } from "solid-js/store" import { reconcile, type SetStoreFunction, type Store } from "solid-js/store"
import type { State, VcsCache } from "./types" import type { State, VcsCache } from "./types"
import { cmp, normalizeAgentList, normalizeProviderList } from "./utils" import { cmp, normalizeAgentList, normalizeProviderList } from "./utils"
import { formatServerError } from "@/utils/server-errors" import { formatServerError } from "@/utils/server-errors"
import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query" import { QueryClient, queryOptions, skipToken } from "@tanstack/solid-query"
import { loadSessionsQuery } from "../global-sync" import { loadMcpQuery } from "../global-sync"
type GlobalStore = { type GlobalStore = {
ready: boolean ready: boolean
@@ -67,6 +67,62 @@ function runAll(list: Array<() => Promise<unknown>>) {
return Promise.allSettled(list.map((item) => item())) return Promise.allSettled(list.map((item) => item()))
} }
function showErrors(input: {
errors: unknown[]
title: string
translate: (key: string, vars?: Record<string, string | number>) => string
formatMoreCount: (count: number) => string
}) {
if (input.errors.length === 0) return
const message = formatServerError(input.errors[0], input.translate)
const more = input.errors.length > 1 ? input.formatMoreCount(input.errors.length - 1) : ""
showToast({
variant: "error",
title: input.title,
description: message + more,
})
}
export const loadGlobalConfigQuery = (
sdk?: OpencodeClient,
transform?: (x: Awaited<ReturnType<OpencodeClient["global"]["config"]["get"]>>) => void,
) =>
queryOptions({
queryKey: ["config"],
queryFn: sdk
? () =>
retry(() =>
sdk.global.config.get().then((x) => {
transform?.(x)
return x.data!
}),
)
: skipToken,
})
export const loadProjectsQuery = (
sdk?: OpencodeClient,
transform?: (x: Awaited<ReturnType<OpencodeClient["project"]["list"]>>["data"]) => void,
) =>
queryOptions({
queryKey: ["project"],
queryFn: sdk
? () =>
retry(() =>
sdk.project
.list()
.then((x) => {
return (x.data ?? [])
.filter((p) => !!p?.id)
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
.slice()
.sort((a, b) => cmp(a.id, b.id))
})
.then(transform),
)
: skipToken,
})
export async function bootstrapGlobal(input: { export async function bootstrapGlobal(input: {
globalSDK: OpencodeClient globalSDK: OpencodeClient
requestFailedTitle: string requestFailedTitle: string
@@ -75,53 +131,15 @@ export async function bootstrapGlobal(input: {
setGlobalStore: SetStoreFunction<GlobalStore> setGlobalStore: SetStoreFunction<GlobalStore>
queryClient: QueryClient queryClient: QueryClient
}) { }) {
const fast = [
() =>
retry(() =>
input.globalSDK.global.config.get().then((x) => {
input.setGlobalStore("config", x.data!)
}),
),
() =>
input.queryClient.fetchQuery({
...loadProvidersQuery(null),
queryFn: () =>
retry(() =>
input.globalSDK.provider.list().then((x) => {
input.setGlobalStore("provider", normalizeProviderList(x.data!))
return null
}),
),
}),
]
const slow = [ const slow = [
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.globalSDK)),
() => input.queryClient.fetchQuery(loadProvidersQuery(null, input.globalSDK)),
() => input.queryClient.fetchQuery(loadPathQuery(null, input.globalSDK)),
() => () =>
retry(() => input.queryClient.fetchQuery(
input.globalSDK.path.get().then((x) => { loadProjectsQuery(input.globalSDK, (data) => input.setGlobalStore("project", data ?? [])),
input.setGlobalStore("path", x.data!)
}),
),
() =>
retry(() =>
input.globalSDK.project.list().then((x) => {
const projects = (x.data ?? [])
.filter((p) => !!p?.id)
.filter((p) => !!p.worktree && !p.worktree.includes("opencode-test"))
.slice()
.sort((a, b) => cmp(a.id, b.id))
input.setGlobalStore("project", projects)
}),
), ),
] ]
await runAll(fast)
// showErrors({
// errors: errors(await runAll(fast)),
// title: input.requestFailedTitle,
// translate: input.translate,
// formatMoreCount: input.formatMoreCount,
// })
await waitForPaint()
await runAll(slow) await runAll(slow)
// showErrors({ // showErrors({
// errors: errors(), // errors: errors(),
@@ -129,7 +147,6 @@ export async function bootstrapGlobal(input: {
// translate: input.translate, // translate: input.translate,
// formatMoreCount: input.formatMoreCount, // formatMoreCount: input.formatMoreCount,
// }) // })
input.setGlobalStore("ready", true)
} }
function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) { function groupBySession<T extends { id: string; sessionID: string }>(input: T[]) {
@@ -180,11 +197,47 @@ function warmSessions(input: {
).then(() => undefined) ).then(() => undefined)
} }
export const loadProvidersQuery = (directory: string | null) => export const loadProvidersQuery = (directory: string | null, sdk?: OpencodeClient) =>
queryOptions<null>({ queryKey: [directory, "providers"], queryFn: skipToken }) queryOptions({
queryKey: [directory, "providers"],
queryFn: sdk ? () => retry(() => sdk.provider.list().then((x) => normalizeProviderList(x.data!))) : skipToken,
})
export const loadAgentsQuery = (directory: string | null) => export const loadAgentsQuery = (
queryOptions<null>({ queryKey: [directory, "agents"], queryFn: skipToken }) directory: string | null,
sdk?: OpencodeClient,
transform?: (x: Awaited<ReturnType<OpencodeClient["app"]["agents"]>>) => void,
) =>
queryOptions({
queryKey: [directory, "agents"],
queryFn: sdk
? () =>
retry(() =>
sdk.app.agents().then((x) => {
transform?.(x)
return x.data!
}),
)
: skipToken,
})
export const loadPathQuery = (
directory: string | null,
sdk?: OpencodeClient,
transform?: (x: Awaited<ReturnType<OpencodeClient["path"]["get"]>>) => void,
) =>
queryOptions<Path>({
queryKey: [directory, "path"],
queryFn: sdk
? () =>
retry(() =>
sdk.path.get().then(async (x) => {
transform?.(x)
return x.data!
}),
)
: skipToken,
})
export async function bootstrapDirectory(input: { export async function bootstrapDirectory(input: {
directory: string directory: string
@@ -211,56 +264,32 @@ export async function bootstrapDirectory(input: {
input.setStore("provider", input.global.provider) input.setStore("provider", input.global.provider)
} }
if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) { if (Object.keys(input.store.config).length === 0 && Object.keys(input.global.config).length > 0) {
input.setStore("config", input.global.config) input.setStore("config", reconcile(input.global.config, { merge: false }))
} }
if (loading || input.store.provider.all.length === 0) {
input.setStore("provider_ready", false)
}
input.setStore("mcp_ready", false)
input.setStore("mcp", {})
input.setStore("lsp_ready", false)
input.setStore("lsp", [])
if (loading) input.setStore("status", "partial") if (loading) input.setStore("status", "partial")
const fast = [() => Promise.resolve(input.loadSessions(input.directory))] const rev = (providerRev.get(input.directory) ?? 0) + 1
providerRev.set(input.directory, rev)
const errs = errors(await runAll(fast))
if (errs.length > 0) {
console.error("Failed to bootstrap instance", errs[0])
const project = getFilename(input.directory)
showToast({
variant: "error",
title: input.translate("toast.project.reloadFailed.title", { project }),
description: formatServerError(errs[0], input.translate),
})
}
;(async () => { ;(async () => {
const slow = [ const slow = [
() => Promise.resolve(input.loadSessions(input.directory)),
() => () =>
input.queryClient.ensureQueryData({ input.queryClient.ensureQueryData(
...loadAgentsQuery(input.directory), loadAgentsQuery(input.directory, input.sdk, (x) => input.setStore("agent", normalizeAgentList(x.data))),
queryFn: () => ),
retry(() => input.sdk.app.agents().then((x) => input.setStore("agent", normalizeAgentList(x.data)))).then( () =>
() => null, retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
),
}),
() => retry(() => input.sdk.config.get().then((x) => input.setStore("config", x.data!))),
() => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))), () => retry(() => input.sdk.session.status().then((x) => input.setStore("session_status", x.data!))),
() => !seededProject &&
seededProject (() => retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id))),
? Promise.resolve() !seededPath &&
: retry(() => input.sdk.project.current()).then((x) => input.setStore("project", x.data!.id)), (() =>
() => input.queryClient.ensureQueryData(
seededPath loadPathQuery(input.directory, input.sdk, (x) => {
? Promise.resolve() const next = projectID(x.data?.directory ?? input.directory, input.global.project)
: retry(() => if (next) input.setStore("project", next)
input.sdk.path.get().then((x) => { }),
input.setStore("path", x.data!) )),
const next = projectID(x.data?.directory ?? input.directory, input.global.project)
if (next) input.setStore("project", next)
}),
),
() => () =>
retry(() => retry(() =>
input.sdk.vcs.get().then((x) => { input.sdk.vcs.get().then((x) => {
@@ -323,14 +352,17 @@ export async function bootstrapDirectory(input: {
}), }),
), ),
() => Promise.resolve(input.loadSessions(input.directory)), () => Promise.resolve(input.loadSessions(input.directory)),
() => input.queryClient.fetchQuery(loadMcpQuery(input.directory, input.sdk)),
() => () =>
retry(() => input.queryClient.fetchQuery(loadProvidersQuery(input.directory, input.sdk)).catch((err) => {
input.sdk.mcp.status().then((x) => { const project = getFilename(input.directory)
input.setStore("mcp", x.data!) showToast({
input.setStore("mcp_ready", true) variant: "error",
}), title: input.translate("toast.project.reloadFailed.title", { project }),
), description: formatServerError(err, input.translate),
] })
}),
].filter(Boolean) as (() => Promise<any>)[]
await waitForPaint() await waitForPaint()
const slowErrs = errors(await runAll(slow)) const slowErrs = errors(await runAll(slow))
@@ -344,29 +376,6 @@ export async function bootstrapDirectory(input: {
}) })
} }
if (loading && errs.length === 0 && slowErrs.length === 0) input.setStore("status", "complete") if (loading && slowErrs.length === 0) input.setStore("status", "complete")
const rev = (providerRev.get(input.directory) ?? 0) + 1
providerRev.set(input.directory, rev)
void input.queryClient.ensureQueryData({
...loadSessionsQuery(input.directory),
queryFn: () =>
retry(() => input.sdk.provider.list())
.then((x) => {
if (providerRev.get(input.directory) !== rev) return
input.setStore("provider", normalizeProviderList(x.data!))
input.setStore("provider_ready", true)
})
.catch((err) => {
if (providerRev.get(input.directory) !== rev) console.error("Failed to refresh provider list", err)
const project = getFilename(input.directory)
showToast({
variant: "error",
title: input.translate("toast.project.reloadFailed.title", { project }),
description: formatServerError(err, input.translate),
})
})
.then(() => null),
})
})() })()
} }
@@ -22,6 +22,7 @@ describe("createChildStoreManager", () => {
onBootstrap() {}, onBootstrap() {},
onDispose() {}, onDispose() {},
translate: (key) => key, translate: (key) => key,
getSdk: () => null!,
}) })
Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => { Array.from({ length: 30 }, (_, index) => `/pinned-${index}`).forEach((directory) => {
@@ -1,7 +1,7 @@
import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js" import { createRoot, getOwner, onCleanup, runWithOwner, type Owner } from "solid-js"
import { createStore, type SetStoreFunction, type Store } from "solid-js/store" import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"
import type { VcsInfo } from "@opencode-ai/sdk/v2/client" import type { OpencodeClient, VcsInfo } from "@opencode-ai/sdk/v2/client"
import { import {
DIR_IDLE_TTL_MS, DIR_IDLE_TTL_MS,
MAX_DIR_STORES, MAX_DIR_STORES,
@@ -14,6 +14,10 @@ import {
type VcsCache, type VcsCache,
} from "./types" } from "./types"
import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction" import { canDisposeDirectory, pickDirectoriesToEvict } from "./eviction"
import { useQueries } from "@tanstack/solid-query"
import { loadPathQuery, loadProvidersQuery } from "./bootstrap"
import { loadLspQuery, loadMcpQuery } from "../global-sync"
import { directoryKey, type DirectoryKey } from "./utils"
export function createChildStoreManager(input: { export function createChildStoreManager(input: {
owner: Owner owner: Owner
@@ -22,6 +26,7 @@ export function createChildStoreManager(input: {
onBootstrap: (directory: string) => void onBootstrap: (directory: string) => void
onDispose: (directory: string) => void onDispose: (directory: string) => void
translate: (key: string, vars?: Record<string, string | number>) => string translate: (key: string, vars?: Record<string, string | number>) => string
getSdk: (directory: string) => OpencodeClient
}) { }) {
const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {} const children: Record<string, [Store<State>, SetStoreFunction<State>]> = {}
const vcsCache = new Map<string, VcsCache>() const vcsCache = new Map<string, VcsCache>()
@@ -32,30 +37,37 @@ export function createChildStoreManager(input: {
const ownerPins = new WeakMap<object, Set<string>>() const ownerPins = new WeakMap<object, Set<string>>()
const disposers = new Map<string, () => void>() const disposers = new Map<string, () => void>()
const markKey = (key: DirectoryKey) => {
if (!key) return
lifecycle.set(key, { lastAccessAt: Date.now() })
runEviction(key)
}
const mark = (directory: string) => { const mark = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
lifecycle.set(directory, { lastAccessAt: Date.now() }) markKey(key)
runEviction(directory)
} }
const pin = (directory: string) => { const pin = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
pins.set(directory, (pins.get(directory) ?? 0) + 1) if (!key) return
mark(directory) pins.set(key, (pins.get(key) ?? 0) + 1)
markKey(key)
} }
const unpin = (directory: string) => { const unpin = (directory: string) => {
if (!directory) return const key = directoryKey(directory)
const next = (pins.get(directory) ?? 0) - 1 if (!key) return
const next = (pins.get(key) ?? 0) - 1
if (next > 0) { if (next > 0) {
pins.set(directory, next) pins.set(key, next)
return return
} }
pins.delete(directory) pins.delete(key)
runEviction() runEviction()
} }
const pinned = (directory: string) => (pins.get(directory) ?? 0) > 0 const pinned = (directory: string) => (pins.get(directoryKey(directory)) ?? 0) > 0
const pinForOwner = (directory: string) => { const pinForOwner = (directory: string) => {
const current = getOwner() const current = getOwner()
@@ -77,30 +89,31 @@ export function createChildStoreManager(input: {
}) })
} }
function disposeDirectory(directory: string) { function disposeDirectory(directory: DirectoryKey) {
const key = directory
if ( if (
!canDisposeDirectory({ !canDisposeDirectory({
directory, directory: key,
hasStore: !!children[directory], hasStore: !!children[key],
pinned: pinned(directory), pinned: pinned(key),
booting: input.isBooting(directory), booting: input.isBooting(key),
loadingSessions: input.isLoadingSessions(directory), loadingSessions: input.isLoadingSessions(key),
}) })
) { ) {
return false return false
} }
vcsCache.delete(directory) vcsCache.delete(key)
metaCache.delete(directory) metaCache.delete(key)
iconCache.delete(directory) iconCache.delete(key)
lifecycle.delete(directory) lifecycle.delete(key)
const dispose = disposers.get(directory) const dispose = disposers.get(key)
if (dispose) { if (dispose) {
dispose() dispose()
disposers.delete(directory) disposers.delete(key)
} }
delete children[directory] delete children[key]
input.onDispose(directory) input.onDispose(key)
return true return true
} }
@@ -117,13 +130,14 @@ export function createChildStoreManager(input: {
}).filter((directory) => directory !== skip) }).filter((directory) => directory !== skip)
if (list.length === 0) return if (list.length === 0) return
for (const directory of list) { for (const directory of list) {
if (!disposeDirectory(directory)) continue if (!disposeDirectory(directoryKey(directory))) continue
} }
} }
function ensureChild(directory: string) { function ensureChild(directory: string) {
if (!directory) console.error("No directory provided") const key = directoryKey(directory)
if (!children[directory]) { if (!key) console.error("No directory provided")
if (!children[key]) {
const vcs = runWithOwner(input.owner, () => const vcs = runWithOwner(input.owner, () =>
persisted( persisted(
Persist.workspace(directory, "vcs", ["vcs.v1"]), Persist.workspace(directory, "vcs", ["vcs.v1"]),
@@ -132,7 +146,7 @@ export function createChildStoreManager(input: {
) )
if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed")) if (!vcs) throw new Error(input.translate("error.childStore.persistedCacheCreateFailed"))
const vcsStore = vcs[0] const vcsStore = vcs[0]
vcsCache.set(directory, { store: vcsStore, setStore: vcs[1], ready: vcs[3] }) vcsCache.set(key, { store: vcsStore, setStore: vcs[1], ready: vcs[3] })
const meta = runWithOwner(input.owner, () => const meta = runWithOwner(input.owner, () =>
persisted( persisted(
@@ -141,7 +155,7 @@ export function createChildStoreManager(input: {
), ),
) )
if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed")) if (!meta) throw new Error(input.translate("error.childStore.persistedProjectMetadataCreateFailed"))
metaCache.set(directory, { store: meta[0], setStore: meta[1], ready: meta[3] }) metaCache.set(key, { store: meta[0], setStore: meta[1], ready: meta[3] })
const icon = runWithOwner(input.owner, () => const icon = runWithOwner(input.owner, () =>
persisted( persisted(
@@ -150,20 +164,38 @@ export function createChildStoreManager(input: {
), ),
) )
if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed")) if (!icon) throw new Error(input.translate("error.childStore.persistedProjectIconCreateFailed"))
iconCache.set(directory, { store: icon[0], setStore: icon[1], ready: icon[3] }) iconCache.set(key, { store: icon[0], setStore: icon[1], ready: icon[3] })
const init = () => const init = () =>
createRoot((dispose) => { createRoot((dispose) => {
const sdk = input.getSdk(directory)
const initialMeta = meta[0].value const initialMeta = meta[0].value
const initialIcon = icon[0].value const initialIcon = icon[0].value
const [pathQuery, mcpQuery, lspQuery, providerQuery] = useQueries(() => ({
queries: [
loadPathQuery(key, sdk),
loadMcpQuery(key, sdk),
loadLspQuery(key, sdk),
loadProvidersQuery(key, sdk),
],
}))
const child = createStore<State>({ const child = createStore<State>({
project: "", project: "",
projectMeta: initialMeta, projectMeta: initialMeta,
icon: initialIcon, icon: initialIcon,
provider_ready: false, get provider_ready() {
return !providerQuery.isLoading
},
provider: { all: [], connected: [], default: {} }, provider: { all: [], connected: [], default: {} },
config: {}, config: {},
path: { state: "", config: "", worktree: "", directory: "", home: "" }, get path() {
if (pathQuery.isLoading || !pathQuery.data)
return { state: "", config: "", worktree: "", directory: "", home: "" }
return pathQuery.data
},
status: "loading" as const, status: "loading" as const,
agent: [], agent: [],
command: [], command: [],
@@ -174,22 +206,30 @@ export function createChildStoreManager(input: {
todo: {}, todo: {},
permission: {}, permission: {},
question: {}, question: {},
mcp_ready: false, get mcp_ready() {
mcp: {}, return !mcpQuery.isLoading
lsp_ready: false, },
lsp: [], get mcp() {
return mcpQuery.isLoading ? {} : (mcpQuery.data ?? {})
},
get lsp_ready() {
return !lspQuery.isLoading
},
get lsp() {
return lspQuery.isLoading ? [] : (lspQuery.data ?? [])
},
vcs: vcsStore.value, vcs: vcsStore.value,
limit: 5, limit: 5,
message: {}, message: {},
part: {}, part: {},
}) })
children[directory] = child children[key] = child
disposers.set(directory, dispose) disposers.set(key, dispose)
const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => { const onPersistedInit = (init: Promise<string> | string | null, run: () => void) => {
if (!(init instanceof Promise)) return if (!(init instanceof Promise)) return
void init.then(() => { void init.then(() => {
if (children[directory] !== child) return if (children[key] !== child) return
run() run()
}) })
} }
@@ -213,15 +253,16 @@ export function createChildStoreManager(input: {
runWithOwner(input.owner, init) runWithOwner(input.owner, init)
} }
mark(directory) markKey(key)
const childStore = children[directory] const childStore = children[key]
if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed")) if (!childStore) throw new Error(input.translate("error.childStore.storeCreateFailed"))
return childStore return childStore
} }
function child(directory: string, options: ChildOptions = {}) { function child(directory: string, options: ChildOptions = {}) {
const key = directoryKey(directory)
const childStore = ensureChild(directory) const childStore = ensureChild(directory)
pinForOwner(directory) pinForOwner(key)
const shouldBootstrap = options.bootstrap ?? true const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") { if (shouldBootstrap && childStore[0].status === "loading") {
input.onBootstrap(directory) input.onBootstrap(directory)
@@ -230,6 +271,7 @@ export function createChildStoreManager(input: {
} }
function peek(directory: string, options: ChildOptions = {}) { function peek(directory: string, options: ChildOptions = {}) {
const key = directoryKey(directory)
const childStore = ensureChild(directory) const childStore = ensureChild(directory)
const shouldBootstrap = options.bootstrap ?? true const shouldBootstrap = options.bootstrap ?? true
if (shouldBootstrap && childStore[0].status === "loading") { if (shouldBootstrap && childStore[0].status === "loading") {
@@ -239,8 +281,9 @@ export function createChildStoreManager(input: {
} }
function projectMeta(directory: string, patch: ProjectMeta) { function projectMeta(directory: string, patch: ProjectMeta) {
const key = directoryKey(directory)
const [store, setStore] = ensureChild(directory) const [store, setStore] = ensureChild(directory)
const cached = metaCache.get(directory) const cached = metaCache.get(key)
if (!cached) return if (!cached) return
const previous = store.projectMeta ?? {} const previous = store.projectMeta ?? {}
const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon const icon = patch.icon ? { ...previous.icon, ...patch.icon } : previous.icon
@@ -256,8 +299,9 @@ export function createChildStoreManager(input: {
} }
function projectIcon(directory: string, value: string | undefined) { function projectIcon(directory: string, value: string | undefined) {
const key = directoryKey(directory)
const [store, setStore] = ensureChild(directory) const [store, setStore] = ensureChild(directory)
const cached = iconCache.get(directory) const cached = iconCache.get(key)
if (!cached) return if (!cached) return
if (store.icon === value) return if (store.icon === value) return
cached.setStore("value", value) cached.setStore("value", value)
@@ -1,4 +1,4 @@
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store" import { produce, reconcile, type SetStoreFunction, type Store } from "solid-js/store"
import type { import type {
Message, Message,
@@ -21,7 +21,7 @@ const SKIP_PARTS = new Set(["patch", "step-start", "step-finish"])
export function applyGlobalEvent(input: { export function applyGlobalEvent(input: {
event: { type: string; properties?: unknown } event: { type: string; properties?: unknown }
project: Project[] project: Project[]
setGlobalProject: (next: Project[] | ((draft: Project[]) => void)) => void setGlobalProject: (next: Project[] | ((draft: Project[]) => Project[])) => void
refresh: () => void refresh: () => void
}) { }) {
if (input.event.type === "global.disposed" || input.event.type === "server.connected") { if (input.event.type === "global.disposed" || input.event.type === "server.connected") {
@@ -33,14 +33,18 @@ export function applyGlobalEvent(input: {
const properties = input.event.properties as Project const properties = input.event.properties as Project
const result = Binary.search(input.project, properties.id, (s) => s.id) const result = Binary.search(input.project, properties.id, (s) => s.id)
if (result.found) { if (result.found) {
input.setGlobalProject((draft) => { input.setGlobalProject(
draft[result.index] = { ...draft[result.index], ...properties } produce((draft) => {
}) draft[result.index] = { ...draft[result.index], ...properties }
}),
)
return return
} }
input.setGlobalProject((draft) => { input.setGlobalProject(
draft.splice(result.index, 0, properties) produce((draft) => {
}) draft.splice(result.index, 0, properties)
}),
)
} }
function cleanupSessionCaches( function cleanupSessionCaches(
@@ -0,0 +1,46 @@
import { describe, expect, test } from "bun:test"
import { createRefreshQueue } from "./queue"
import { directoryKey } from "./utils"
const tick = () => new Promise((resolve) => setTimeout(resolve, 10))
describe("createRefreshQueue", () => {
test("clears queued directories by normalized key", async () => {
const calls: string[] = []
const queue = createRefreshQueue({
paused: () => false,
key: directoryKey,
bootstrap: async () => {},
bootstrapInstance: (directory) => {
calls.push(directory)
},
})
queue.push("C:\\tmp\\demo")
queue.clear("C:/tmp/demo")
await tick()
expect(calls).toEqual([])
queue.dispose()
})
test("passes the original directory to bootstrapInstance", async () => {
const calls: string[] = []
const queue = createRefreshQueue({
paused: () => false,
key: directoryKey,
bootstrap: async () => {},
bootstrapInstance: (directory) => {
calls.push(directory)
},
})
queue.push("C:\\tmp\\demo")
await tick()
expect(calls).toEqual(["C:\\tmp\\demo"])
queue.dispose()
})
})
@@ -2,22 +2,25 @@ type QueueInput = {
paused: () => boolean paused: () => boolean
bootstrap: () => Promise<void> bootstrap: () => Promise<void>
bootstrapInstance: (directory: string) => Promise<void> | void bootstrapInstance: (directory: string) => Promise<void> | void
key?: (directory: string) => string
} }
export function createRefreshQueue(input: QueueInput) { export function createRefreshQueue(input: QueueInput) {
const queued = new Set<string>() const queued = new Map<string, string>()
let root = false let root = false
let running = false let running = false
let timer: ReturnType<typeof setTimeout> | undefined let timer: ReturnType<typeof setTimeout> | undefined
const key = input.key ?? ((directory: string) => directory)
const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0)) const tick = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
const take = (count: number) => { const take = (count: number) => {
if (queued.size === 0) return [] as string[] if (queued.size === 0) return [] as string[]
const items: string[] = [] const items: string[] = []
for (const item of queued) { for (const [id, directory] of queued) {
queued.delete(item) queued.delete(id)
items.push(item) items.push(directory)
if (items.length >= count) break if (items.length >= count) break
} }
return items return items
@@ -33,7 +36,7 @@ export function createRefreshQueue(input: QueueInput) {
const push = (directory: string) => { const push = (directory: string) => {
if (!directory) return if (!directory) return
queued.add(directory) queued.set(key(directory), directory)
if (input.paused()) return if (input.paused()) return
schedule() schedule()
} }
@@ -73,7 +76,7 @@ export function createRefreshQueue(input: QueueInput) {
push, push,
refresh, refresh,
clear(directory: string) { clear(directory: string) {
queued.delete(directory) queued.delete(key(directory))
}, },
dispose() { dispose() {
if (!timer) return if (!timer) return
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import type { Agent } from "@opencode-ai/sdk/v2/client" import type { Agent } from "@opencode-ai/sdk/v2/client"
import { normalizeAgentList } from "./utils" import { directoryKey, normalizeAgentList } from "./utils"
const agent = (name = "build") => const agent = (name = "build") =>
({ ({
@@ -33,3 +33,20 @@ describe("normalizeAgentList", () => {
expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")]) expect(normalizeAgentList([{ name: "build" }, agent("docs")])).toEqual([agent("docs")])
}) })
}) })
describe("directoryKey", () => {
test("normalizes slashes", () => {
expect(String(directoryKey("C:\\Repos\\sst\\opencode"))).toBe("C:/Repos/sst/opencode")
expect(String(directoryKey("C:/Repos/sst/opencode"))).toBe("C:/Repos/sst/opencode")
})
test("preserves backslashes in posix paths", () => {
expect(String(directoryKey("/tmp/foo\\bar"))).toBe("/tmp/foo\\bar")
})
test("trims trailing slashes without breaking roots", () => {
expect(String(directoryKey("C:/Repos/sst/opencode/"))).toBe("C:/Repos/sst/opencode")
expect(String(directoryKey("C:/"))).toBe("C:/")
expect(String(directoryKey("/"))).toBe("/")
})
})
@@ -1,4 +1,5 @@
import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client" import type { Agent, Project, ProviderListResponse } from "@opencode-ai/sdk/v2/client"
export { pathKey as directoryKey, type PathKey as DirectoryKey } from "@/utils/path-key"
export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0) export const cmp = (a: string, b: string) => (a < b ? -1 : a > b ? 1 : 0)
+8 -31
View File
@@ -391,37 +391,14 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
? globalSync.data.project.find((x) => x.id === projectID) ? globalSync.data.project.find((x) => x.id === projectID)
: globalSync.data.project.find((x) => x.worktree === project.worktree) : globalSync.data.project.find((x) => x.worktree === project.worktree)
const local = childStore.projectMeta // Preserve local icon override from per-workspace localStorage cache (childStore.icon).
const localOverride = // Without this, different subdirectories of the same git repo would share the same
local?.name !== undefined || // icon from the database instead of using their individual overrides.
local?.commands?.start !== undefined || const base = { ...metadata, ...project }
local?.icon?.override !== undefined || if (childStore.icon) {
local?.icon?.color !== undefined return { ...base, icon: { ...base.icon, override: childStore.icon } }
const base = {
...metadata,
...project,
icon: {
url: metadata?.icon?.url,
override: metadata?.icon?.override ?? childStore.icon,
color: metadata?.icon?.color,
},
}
const isGlobal = projectID === "global" || (metadata?.id === undefined && localOverride)
if (!isGlobal) return base
return {
...base,
id: base.id ?? "global",
name: local?.name,
commands: local?.commands,
icon: {
url: base.icon?.url,
override: local?.icon?.override,
color: local?.icon?.color,
},
} }
return base
} }
const roots = createMemo(() => { const roots = createMemo(() => {
@@ -516,7 +493,7 @@ export const { use: useLayout, provider: LayoutProvider } = createSimpleContext(
} }
for (const project of projects) { for (const project of projects) {
if (project.icon?.color) continue if (project.icon?.color || project.icon?.override || project.icon?.url) continue
const worktree = project.worktree const worktree = project.worktree
const existing = colors[worktree] const existing = colors[worktree]
const color = existing ?? pickAvailableColor(used) const color = existing ?? pickAvailableColor(used)
+2 -2
View File
@@ -1,5 +1,5 @@
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { batch, createEffect, createMemo } from "solid-js" import { batch, createEffect, createMemo } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
@@ -382,7 +382,7 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
setSaved("session", session, { setSaved("session", session, {
agent: msg.agent, agent: msg.agent,
model: msg.model, model: msg.model,
variant: msg.model.variant ?? null, variant: msg.model?.variant ?? null,
}) })
}, },
}, },
+2 -2
View File
@@ -7,8 +7,8 @@ import { useGlobalSync } from "./global-sync"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { useSettings } from "@/context/settings" import { useSettings } from "@/context/settings"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { decode64 } from "@/utils/base64" import { decode64 } from "@/utils/base64"
import { EventSessionError } from "@opencode-ai/sdk/v2" import { EventSessionError } from "@opencode-ai/sdk/v2"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test" import { describe, expect, test } from "bun:test"
import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client" import type { PermissionRequest, Session } from "@opencode-ai/sdk/v2/client"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond" import { autoRespondsPermission, isDirectoryAutoAccepting } from "./permission-auto-respond"
const session = (input: { id: string; parentID?: string }) => const session = (input: { id: string; parentID?: string }) =>
@@ -1,4 +1,4 @@
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
export function acceptKey(sessionID: string, directory?: string) { export function acceptKey(sessionID: string, directory?: string) {
if (!directory) return sessionID if (!directory) return sessionID
+3 -3
View File
@@ -49,11 +49,11 @@ export type Platform = {
/** Storage mechanism, defaults to localStorage */ /** Storage mechanism, defaults to localStorage */
storage?: (name?: string) => SyncStorage | AsyncStorage storage?: (name?: string) => SyncStorage | AsyncStorage
/** Check for updates (Tauri only) */ /** Check for a downloadable desktop update */
checkUpdate?(): Promise<UpdateInfo> checkUpdate?(): Promise<UpdateInfo>
/** Install updates (Tauri only) */ /** Install the downloaded update using the platform restart flow */
update?(): Promise<void> updateAndRestart?(): Promise<void>
/** Fetch override */ /** Fetch override */
fetch?: typeof fetch fetch?: typeof fetch
+4 -4
View File
@@ -1,5 +1,5 @@
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { useParams } from "@solidjs/router" import { useParams } from "@solidjs/router"
import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js" import { batch, createMemo, createRoot, getOwner, onCleanup } from "solid-js"
import { createStore, type SetStoreFunction } from "solid-js/store" import { createStore, type SetStoreFunction } from "solid-js/store"
@@ -185,9 +185,9 @@ function createPromptSession(dir: string, id: string | undefined) {
return { return {
ready, ready,
current: createMemo(() => store.prompt), current: () => store.prompt,
cursor: createMemo(() => store.cursor), cursor: createMemo(() => store.cursor),
dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)), dirty: () => !isPromptEqual(store.prompt, DEFAULT_PROMPT),
context: { context: {
items: createMemo(() => store.context.items), items: createMemo(() => store.context.items),
add(item: ContextItem) { add(item: ContextItem) {
@@ -277,7 +277,7 @@ export const { use: usePrompt, provider: PromptProvider } = createSimpleContext(
const pick = (scope?: Scope) => (scope ? load(scope.dir, scope.id) : session()) const pick = (scope?: Scope) => (scope ? load(scope.dir, scope.id) : session())
return { return {
ready: () => session().ready(), ready: () => session().ready,
current: () => session().current(), current: () => session().current(),
cursor: () => session().cursor(), cursor: () => session().cursor(),
dirty: () => session().dirty(), dirty: () => session().dirty(),
+27
View File
@@ -31,6 +31,7 @@ export interface Settings {
showReasoningSummaries: boolean showReasoningSummaries: boolean
shellToolPartsExpanded: boolean shellToolPartsExpanded: boolean
editToolPartsExpanded: boolean editToolPartsExpanded: boolean
showSessionProgressBar: boolean
} }
updates: { updates: {
startup: boolean startup: boolean
@@ -39,6 +40,7 @@ export interface Settings {
fontSize: number fontSize: number
mono: string mono: string
sans: string sans: string
terminal: string
} }
keybinds: Record<string, string> keybinds: Record<string, string>
permissions: { permissions: {
@@ -50,13 +52,17 @@ export interface Settings {
export const monoDefault = "System Mono" export const monoDefault = "System Mono"
export const sansDefault = "System Sans" export const sansDefault = "System Sans"
export const terminalDefault = "JetBrainsMono Nerd Font Mono"
const monoFallback = const monoFallback =
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace' 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
const sansFallback = 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif' const sansFallback = 'ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
const terminalFallback =
'"JetBrainsMono Nerd Font Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
const monoBase = monoFallback const monoBase = monoFallback
const sansBase = sansFallback const sansBase = sansFallback
const terminalBase = terminalFallback
function input(font: string | undefined) { function input(font: string | undefined) {
return font ?? "" return font ?? ""
@@ -89,6 +95,14 @@ export function sansFontFamily(font: string | undefined) {
return stack(font, sansBase) return stack(font, sansBase)
} }
export function terminalInput(font: string | undefined) {
return input(font)
}
export function terminalFontFamily(font: string | undefined) {
return stack(font, terminalBase)
}
const defaultSettings: Settings = { const defaultSettings: Settings = {
general: { general: {
autoSave: true, autoSave: true,
@@ -102,6 +116,7 @@ const defaultSettings: Settings = {
showReasoningSummaries: false, showReasoningSummaries: false,
shellToolPartsExpanded: false, shellToolPartsExpanded: false,
editToolPartsExpanded: false, editToolPartsExpanded: false,
showSessionProgressBar: true,
}, },
updates: { updates: {
startup: true, startup: true,
@@ -110,6 +125,7 @@ const defaultSettings: Settings = {
fontSize: 14, fontSize: 14,
mono: "", mono: "",
sans: "", sans: "",
terminal: "",
}, },
keybinds: {}, keybinds: {},
permissions: { permissions: {
@@ -213,6 +229,13 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
setEditToolPartsExpanded(value: boolean) { setEditToolPartsExpanded(value: boolean) {
setStore("general", "editToolPartsExpanded", value) setStore("general", "editToolPartsExpanded", value)
}, },
showSessionProgressBar: withFallback(
() => store.general?.showSessionProgressBar,
defaultSettings.general.showSessionProgressBar,
),
setShowSessionProgressBar(value: boolean) {
setStore("general", "showSessionProgressBar", value)
},
}, },
updates: { updates: {
startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup), startup: withFallback(() => store.updates?.startup, defaultSettings.updates.startup),
@@ -233,6 +256,10 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont
setUIFont(value: string) { setUIFont(value: string) {
setStore("appearance", "sans", value.trim() ? value : "") setStore("appearance", "sans", value.trim() ? value : "")
}, },
terminalFont: withFallback(() => store.appearance?.terminal, defaultSettings.appearance.terminal),
setTerminalFont(value: string) {
setStore("appearance", "terminal", value.trim() ? value : "")
},
}, },
keybinds: { keybinds: {
get: (action: string) => store.keybinds?.[action], get: (action: string) => store.keybinds?.[action],
+2 -2
View File
@@ -1,7 +1,7 @@
import { batch, createMemo } from "solid-js" import { batch, createMemo } from "solid-js"
import { createStore, produce, reconcile } from "solid-js/store" import { createStore, produce, reconcile } from "solid-js/store"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { createSimpleContext } from "@opencode-ai/ui/context" import { createSimpleContext } from "@opencode-ai/ui/context"
import { import {
clearSessionPrefetch, clearSessionPrefetch,
+6 -4
View File
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "جارٍ الحفظ...", "common.saving": "جارٍ الحفظ...",
"common.default": "افتراضي", "common.default": "افتراضي",
"common.attachment": "مرفق", "common.attachment": "مرفق",
"prompt.placeholder.shell": "أدخل أمر shell...", "prompt.placeholder.shell": "أدخل أمر shell... {{example}}",
"prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"', "prompt.placeholder.normal": 'اسأل أي شيء... "{{example}}"',
"prompt.placeholder.simple": "اسأل أي شيء...", "prompt.placeholder.simple": "اسأل أي شيء...",
"prompt.placeholder.summarizeComments": "لخّص التعليقات…", "prompt.placeholder.summarizeComments": "لخّص التعليقات…",
@@ -565,7 +565,9 @@ export const dict = {
"settings.general.row.theme.title": "السمة", "settings.general.row.theme.title": "السمة",
"settings.general.row.theme.description": "تخصيص سمة OpenCode.", "settings.general.row.theme.description": "تخصيص سمة OpenCode.",
"settings.general.row.font.title": "خط الكود", "settings.general.row.font.title": "خط الكود",
"settings.general.row.font.description": "خصّص الخط المستخدم في كتل التعليمات البرمجية والطرفيات", "settings.general.row.font.description": "خصّص الخط المستخدم في كتل التعليمات البرمجية",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "خط الواجهة", "settings.general.row.uiFont.title": "خط الواجهة",
"settings.general.row.uiFont.description": "خصّص الخط المستخدم في الواجهة بأكملها", "settings.general.row.uiFont.description": "خصّص الخط المستخدم في الواجهة بأكملها",
"settings.general.row.followup.title": "سلوك المتابعة", "settings.general.row.followup.title": "سلوك المتابعة",
@@ -580,6 +582,8 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "توسيع أجزاء أداة edit", "settings.general.row.editToolPartsExpanded.title": "توسيع أجزاء أداة edit",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"إظهار أجزاء أدوات edit و write و patch موسعة بشكل افتراضي في الشريط الزمني", "إظهار أجزاء أدوات edit و write و patch موسعة بشكل افتراضي في الشريط الزمني",
"settings.general.row.showSessionProgressBar.title": "إظهار شريط تقدم الجلسة",
"settings.general.row.showSessionProgressBar.description": "عرض شريط التقدم المتحرك أعلى الجلسة أثناء عمل الوكيل",
"settings.general.row.wayland.title": "استخدام Wayland الأصلي", "settings.general.row.wayland.title": "استخدام Wayland الأصلي",
"settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.", "settings.general.row.wayland.description": "تعطيل التراجع إلى X11 على Wayland. يتطلب إعادة التشغيل.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -717,8 +721,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL", "settings.permissions.tool.webfetch.description": "جلب محتوى من عنوان URL",
"settings.permissions.tool.websearch.title": "بحث الويب", "settings.permissions.tool.websearch.title": "بحث الويب",
"settings.permissions.tool.websearch.description": "البحث في الويب", "settings.permissions.tool.websearch.description": "البحث في الويب",
"settings.permissions.tool.codesearch.title": "بحث الكود",
"settings.permissions.tool.codesearch.description": "البحث عن كود على الويب",
"settings.permissions.tool.external_directory.title": "دليل خارجي", "settings.permissions.tool.external_directory.title": "دليل خارجي",
"settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع", "settings.permissions.tool.external_directory.description": "الوصول إلى الملفات خارج دليل المشروع",
"settings.permissions.tool.doom_loop.title": "حلقة الموت", "settings.permissions.tool.doom_loop.title": "حلقة الموت",
+7 -4
View File
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Salvando...", "common.saving": "Salvando...",
"common.default": "Padrão", "common.default": "Padrão",
"common.attachment": "anexo", "common.attachment": "anexo",
"prompt.placeholder.shell": "Digite comando do shell...", "prompt.placeholder.shell": "Digite comando do shell... {{example}}",
"prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"', "prompt.placeholder.normal": 'Pergunte qualquer coisa... "{{example}}"',
"prompt.placeholder.simple": "Pergunte qualquer coisa...", "prompt.placeholder.simple": "Pergunte qualquer coisa...",
"prompt.placeholder.summarizeComments": "Resumir comentários…", "prompt.placeholder.summarizeComments": "Resumir comentários…",
@@ -572,7 +572,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.", "settings.general.row.theme.description": "Personalize como o OpenCode é tematizado.",
"settings.general.row.font.title": "Fonte de código", "settings.general.row.font.title": "Fonte de código",
"settings.general.row.font.description": "Personalize a fonte usada em blocos de código e terminais", "settings.general.row.font.description": "Personalize a fonte usada em blocos de código",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Fonte da interface", "settings.general.row.uiFont.title": "Fonte da interface",
"settings.general.row.uiFont.description": "Personalize a fonte usada em toda a interface", "settings.general.row.uiFont.description": "Personalize a fonte usada em toda a interface",
"settings.general.row.followup.title": "Comportamento de acompanhamento", "settings.general.row.followup.title": "Comportamento de acompanhamento",
@@ -588,6 +590,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Expandir partes da ferramenta de edição", "settings.general.row.editToolPartsExpanded.title": "Expandir partes da ferramenta de edição",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Mostrar partes das ferramentas de edição, escrita e patch expandidas por padrão na linha do tempo", "Mostrar partes das ferramentas de edição, escrita e patch expandidas por padrão na linha do tempo",
"settings.general.row.showSessionProgressBar.title": "Mostrar barra de progresso da sessão",
"settings.general.row.showSessionProgressBar.description":
"Exibir a barra de progresso animada no topo da sessão quando o agente estiver trabalhando",
"settings.general.row.wayland.title": "Usar Wayland nativo", "settings.general.row.wayland.title": "Usar Wayland nativo",
"settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.", "settings.general.row.wayland.description": "Desabilitar fallback X11 no Wayland. Requer reinicialização.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -727,8 +732,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL", "settings.permissions.tool.webfetch.description": "Buscar conteúdo de uma URL",
"settings.permissions.tool.websearch.title": "Pesquisa Web", "settings.permissions.tool.websearch.title": "Pesquisa Web",
"settings.permissions.tool.websearch.description": "Pesquisar na web", "settings.permissions.tool.websearch.description": "Pesquisar na web",
"settings.permissions.tool.codesearch.title": "Pesquisa de Código",
"settings.permissions.tool.codesearch.description": "Pesquisar código na web",
"settings.permissions.tool.external_directory.title": "Diretório Externo", "settings.permissions.tool.external_directory.title": "Diretório Externo",
"settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto", "settings.permissions.tool.external_directory.description": "Acessar arquivos fora do diretório do projeto",
"settings.permissions.tool.doom_loop.title": "Loop Infinito", "settings.permissions.tool.doom_loop.title": "Loop Infinito",
+7 -4
View File
@@ -228,7 +228,7 @@ export const dict = {
"common.default": "Podrazumijevano", "common.default": "Podrazumijevano",
"common.attachment": "prilog", "common.attachment": "prilog",
"prompt.placeholder.shell": "Unesi shell naredbu...", "prompt.placeholder.shell": "Unesi shell naredbu... {{example}}",
"prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"', "prompt.placeholder.normal": 'Pitaj bilo šta... "{{example}}"',
"prompt.placeholder.simple": "Pitaj bilo šta...", "prompt.placeholder.simple": "Pitaj bilo šta...",
"prompt.placeholder.summarizeComments": "Sažmi komentare…", "prompt.placeholder.summarizeComments": "Sažmi komentare…",
@@ -637,7 +637,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "Prilagodi temu OpenCode-a.", "settings.general.row.theme.description": "Prilagodi temu OpenCode-a.",
"settings.general.row.font.title": "Font za kod", "settings.general.row.font.title": "Font za kod",
"settings.general.row.font.description": "Prilagodi font koji se koristi u blokovima koda i terminalima", "settings.general.row.font.description": "Prilagodi font koji se koristi u blokovima koda",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI font", "settings.general.row.uiFont.title": "UI font",
"settings.general.row.uiFont.description": "Prilagodi font koji se koristi u cijelom interfejsu", "settings.general.row.uiFont.description": "Prilagodi font koji se koristi u cijelom interfejsu",
"settings.general.row.followup.title": "Ponašanje nadovezivanja", "settings.general.row.followup.title": "Ponašanje nadovezivanja",
@@ -653,6 +655,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Proširi dijelove alata za uređivanje", "settings.general.row.editToolPartsExpanded.title": "Proširi dijelove alata za uređivanje",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Prikaži dijelove alata za uređivanje, pisanje i patch podrazumijevano proširene na vremenskoj traci", "Prikaži dijelove alata za uređivanje, pisanje i patch podrazumijevano proširene na vremenskoj traci",
"settings.general.row.showSessionProgressBar.title": "Prikaži traku napretka sesije",
"settings.general.row.showSessionProgressBar.description":
"Prikaži animiranu traku napretka na vrhu sesije kada agent radi",
"settings.general.row.wayland.title": "Koristi nativni Wayland", "settings.general.row.wayland.title": "Koristi nativni Wayland",
"settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.", "settings.general.row.wayland.description": "Onemogući X11 fallback na Waylandu. Zahtijeva restart.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -801,8 +806,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a", "settings.permissions.tool.webfetch.description": "Preuzmi sadržaj sa URL-a",
"settings.permissions.tool.websearch.title": "Web pretraga", "settings.permissions.tool.websearch.title": "Web pretraga",
"settings.permissions.tool.websearch.description": "Pretražuj web", "settings.permissions.tool.websearch.description": "Pretražuj web",
"settings.permissions.tool.codesearch.title": "Pretraga koda",
"settings.permissions.tool.codesearch.description": "Pretraži kod na webu",
"settings.permissions.tool.external_directory.title": "Vanjski direktorij", "settings.permissions.tool.external_directory.title": "Vanjski direktorij",
"settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta", "settings.permissions.tool.external_directory.description": "Pristup datotekama izvan direktorija projekta",
"settings.permissions.tool.doom_loop.title": "Beskonačna petlja", "settings.permissions.tool.doom_loop.title": "Beskonačna petlja",
+7 -4
View File
@@ -226,7 +226,7 @@ export const dict = {
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "vedhæftning", "common.attachment": "vedhæftning",
"prompt.placeholder.shell": "Indtast shell-kommando...", "prompt.placeholder.shell": "Indtast shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"', "prompt.placeholder.normal": 'Spørg om hvad som helst... "{{example}}"',
"prompt.placeholder.simple": "Spørg om hvad som helst...", "prompt.placeholder.simple": "Spørg om hvad som helst...",
"prompt.placeholder.summarizeComments": "Opsummér kommentarer…", "prompt.placeholder.summarizeComments": "Opsummér kommentarer…",
@@ -632,7 +632,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.", "settings.general.row.theme.description": "Tilpas hvordan OpenCode er temabestemt.",
"settings.general.row.font.title": "Kode-skrifttype", "settings.general.row.font.title": "Kode-skrifttype",
"settings.general.row.font.description": "Tilpas skrifttypen, der bruges i kodeblokke og terminaler", "settings.general.row.font.description": "Tilpas skrifttypen, der bruges i kodeblokke",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI-skrifttype", "settings.general.row.uiFont.title": "UI-skrifttype",
"settings.general.row.uiFont.description": "Tilpas skrifttypen, der bruges i hele brugerfladen", "settings.general.row.uiFont.description": "Tilpas skrifttypen, der bruges i hele brugerfladen",
"settings.general.row.followup.title": "Opfølgningsadfærd", "settings.general.row.followup.title": "Opfølgningsadfærd",
@@ -647,6 +649,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Udvid edit-værktøjsdele", "settings.general.row.editToolPartsExpanded.title": "Udvid edit-værktøjsdele",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Vis edit-, write- og patch-værktøjsdele udvidet som standard i tidslinjen", "Vis edit-, write- og patch-værktøjsdele udvidet som standard i tidslinjen",
"settings.general.row.showSessionProgressBar.title": "Vis sessionens fremdriftslinje",
"settings.general.row.showSessionProgressBar.description":
"Vis den animerede fremdriftslinje øverst i sessionen, når agenten arbejder",
"settings.general.row.wayland.title": "Brug native Wayland", "settings.general.row.wayland.title": "Brug native Wayland",
"settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.", "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Kræver genstart.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -795,8 +800,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Hent indhold fra en URL", "settings.permissions.tool.webfetch.description": "Hent indhold fra en URL",
"settings.permissions.tool.websearch.title": "Websøgning", "settings.permissions.tool.websearch.title": "Websøgning",
"settings.permissions.tool.websearch.description": "Søg på nettet", "settings.permissions.tool.websearch.description": "Søg på nettet",
"settings.permissions.tool.codesearch.title": "Kodesøgning",
"settings.permissions.tool.codesearch.description": "Søg kode på nettet",
"settings.permissions.tool.external_directory.title": "Ekstern mappe", "settings.permissions.tool.external_directory.title": "Ekstern mappe",
"settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen", "settings.permissions.tool.external_directory.description": "Få adgang til filer uden for projektmappen",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+7 -4
View File
@@ -215,7 +215,7 @@ export const dict = {
"common.saving": "Speichert...", "common.saving": "Speichert...",
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "Anhang", "common.attachment": "Anhang",
"prompt.placeholder.shell": "Shell-Befehl eingeben...", "prompt.placeholder.shell": "Shell-Befehl eingeben... {{example}}",
"prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"', "prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
"prompt.placeholder.simple": "Fragen Sie alles...", "prompt.placeholder.simple": "Fragen Sie alles...",
"prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…", "prompt.placeholder.summarizeComments": "Kommentare zusammenfassen…",
@@ -582,7 +582,9 @@ export const dict = {
"settings.general.row.theme.title": "Thema", "settings.general.row.theme.title": "Thema",
"settings.general.row.theme.description": "Das Thema von OpenCode anpassen.", "settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
"settings.general.row.font.title": "Code-Schriftart", "settings.general.row.font.title": "Code-Schriftart",
"settings.general.row.font.description": "Die in Codeblöcken und Terminals verwendete Schriftart anpassen", "settings.general.row.font.description": "Die in Codeblöcken verwendete Schriftart anpassen",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI-Schriftart", "settings.general.row.uiFont.title": "UI-Schriftart",
"settings.general.row.uiFont.description": "Die im gesamten Interface verwendete Schriftart anpassen", "settings.general.row.uiFont.description": "Die im gesamten Interface verwendete Schriftart anpassen",
"settings.general.row.followup.title": "Verhalten bei Folgefragen", "settings.general.row.followup.title": "Verhalten bei Folgefragen",
@@ -599,6 +601,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Edit-Tool-Abschnitte ausklappen", "settings.general.row.editToolPartsExpanded.title": "Edit-Tool-Abschnitte ausklappen",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Edit-, Write- und Patch-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen", "Edit-, Write- und Patch-Tool-Abschnitte standardmäßig in der Timeline ausgeklappt anzeigen",
"settings.general.row.showSessionProgressBar.title": "Sitzungsfortschrittsleiste anzeigen",
"settings.general.row.showSessionProgressBar.description":
"Die animierte Fortschrittsleiste oben in der Sitzung anzeigen, wenn der Agent arbeitet",
"settings.general.row.wayland.title": "Natives Wayland verwenden", "settings.general.row.wayland.title": "Natives Wayland verwenden",
"settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.", "settings.general.row.wayland.description": "X11-Fallback unter Wayland deaktivieren. Erfordert Neustart.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -738,8 +743,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen", "settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
"settings.permissions.tool.websearch.title": "Web-Suche", "settings.permissions.tool.websearch.title": "Web-Suche",
"settings.permissions.tool.websearch.description": "Das Web durchsuchen", "settings.permissions.tool.websearch.description": "Das Web durchsuchen",
"settings.permissions.tool.codesearch.title": "Code-Suche",
"settings.permissions.tool.codesearch.description": "Code im Web durchsuchen",
"settings.permissions.tool.external_directory.title": "Externes Verzeichnis", "settings.permissions.tool.external_directory.title": "Externes Verzeichnis",
"settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses", "settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+12 -4
View File
@@ -230,7 +230,7 @@ export const dict = {
"common.default": "Default", "common.default": "Default",
"common.attachment": "attachment", "common.attachment": "attachment",
"prompt.placeholder.shell": "Enter shell command...", "prompt.placeholder.shell": "Enter shell command... {{example}}",
"prompt.placeholder.normal": 'Ask anything... "{{example}}"', "prompt.placeholder.normal": 'Ask anything... "{{example}}"',
"prompt.placeholder.simple": "Ask anything...", "prompt.placeholder.simple": "Ask anything...",
"prompt.placeholder.summarizeComments": "Summarize comments…", "prompt.placeholder.summarizeComments": "Summarize comments…",
@@ -728,6 +728,11 @@ export const dict = {
"settings.general.row.language.title": "Language", "settings.general.row.language.title": "Language",
"settings.general.row.language.description": "Change the display language for OpenCode", "settings.general.row.language.description": "Change the display language for OpenCode",
"settings.general.row.shell.title": "Terminal Shell",
"settings.general.row.shell.description":
"Choose the shell used for your terminal. Compatible shells are also used for agent tool calls.",
"settings.general.row.shell.autoDefault": "Auto (Default)",
"settings.general.row.shell.terminalOnly": "terminal only",
"settings.general.row.appearance.title": "Appearance", "settings.general.row.appearance.title": "Appearance",
"settings.general.row.appearance.description": "Customise how OpenCode looks on your device", "settings.general.row.appearance.description": "Customise how OpenCode looks on your device",
"settings.general.row.colorScheme.title": "Color scheme", "settings.general.row.colorScheme.title": "Color scheme",
@@ -735,7 +740,9 @@ export const dict = {
"settings.general.row.theme.title": "Theme", "settings.general.row.theme.title": "Theme",
"settings.general.row.theme.description": "Customise how OpenCode is themed.", "settings.general.row.theme.description": "Customise how OpenCode is themed.",
"settings.general.row.font.title": "Code Font", "settings.general.row.font.title": "Code Font",
"settings.general.row.font.description": "Customise the font used in code blocks and terminals", "settings.general.row.font.description": "Customise the font used in code blocks",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI Font", "settings.general.row.uiFont.title": "UI Font",
"settings.general.row.uiFont.description": "Customise the font used throughout the interface", "settings.general.row.uiFont.description": "Customise the font used throughout the interface",
"settings.general.row.followup.title": "Follow-up behavior", "settings.general.row.followup.title": "Follow-up behavior",
@@ -760,6 +767,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Expand edit tool parts", "settings.general.row.editToolPartsExpanded.title": "Expand edit tool parts",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Show edit, write, and patch tool parts expanded by default in the timeline", "Show edit, write, and patch tool parts expanded by default in the timeline",
"settings.general.row.showSessionProgressBar.title": "Show session progress bar",
"settings.general.row.showSessionProgressBar.description":
"Display the animated progress bar at the top of the session when the agent is working",
"settings.general.row.wayland.title": "Use native Wayland", "settings.general.row.wayland.title": "Use native Wayland",
"settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.", "settings.general.row.wayland.description": "Disable X11 fallback on Wayland. Requires restart.",
@@ -910,8 +920,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Fetch content from a URL", "settings.permissions.tool.webfetch.description": "Fetch content from a URL",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "Search the web", "settings.permissions.tool.websearch.description": "Search the web",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "Search code on the web",
"settings.permissions.tool.external_directory.title": "External Directory", "settings.permissions.tool.external_directory.title": "External Directory",
"settings.permissions.tool.external_directory.description": "Access files outside the project directory", "settings.permissions.tool.external_directory.description": "Access files outside the project directory",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+7 -4
View File
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "Predeterminado", "common.default": "Predeterminado",
"common.attachment": "adjunto", "common.attachment": "adjunto",
"prompt.placeholder.shell": "Introduce comando de shell...", "prompt.placeholder.shell": "Introduce comando de shell... {{example}}",
"prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"', "prompt.placeholder.normal": 'Pregunta cualquier cosa... "{{example}}"',
"prompt.placeholder.simple": "Pregunta cualquier cosa...", "prompt.placeholder.simple": "Pregunta cualquier cosa...",
"prompt.placeholder.summarizeComments": "Resumir comentarios…", "prompt.placeholder.summarizeComments": "Resumir comentarios…",
@@ -640,7 +640,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "Personaliza el tema de OpenCode.", "settings.general.row.theme.description": "Personaliza el tema de OpenCode.",
"settings.general.row.font.title": "Fuente de código", "settings.general.row.font.title": "Fuente de código",
"settings.general.row.font.description": "Personaliza la fuente usada en bloques de código y terminales", "settings.general.row.font.description": "Personaliza la fuente usada en bloques de código",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Fuente de la interfaz", "settings.general.row.uiFont.title": "Fuente de la interfaz",
"settings.general.row.uiFont.description": "Personaliza la fuente usada en toda la interfaz", "settings.general.row.uiFont.description": "Personaliza la fuente usada en toda la interfaz",
"settings.general.row.followup.title": "Comportamiento de seguimiento", "settings.general.row.followup.title": "Comportamiento de seguimiento",
@@ -657,6 +659,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Expandir partes de la herramienta de edición", "settings.general.row.editToolPartsExpanded.title": "Expandir partes de la herramienta de edición",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Mostrar las partes de las herramientas de edición, escritura y parcheado expandidas por defecto en la línea de tiempo", "Mostrar las partes de las herramientas de edición, escritura y parcheado expandidas por defecto en la línea de tiempo",
"settings.general.row.showSessionProgressBar.title": "Mostrar barra de progreso de la sesión",
"settings.general.row.showSessionProgressBar.description":
"Mostrar la barra de progreso animada en la parte superior de la sesión cuando el agente esté trabajando",
"settings.general.row.wayland.title": "Usar Wayland nativo", "settings.general.row.wayland.title": "Usar Wayland nativo",
"settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.", "settings.general.row.wayland.description": "Deshabilitar fallback a X11 en Wayland. Requiere reinicio.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -808,8 +813,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Obtener contenido de una URL", "settings.permissions.tool.webfetch.description": "Obtener contenido de una URL",
"settings.permissions.tool.websearch.title": "Búsqueda Web", "settings.permissions.tool.websearch.title": "Búsqueda Web",
"settings.permissions.tool.websearch.description": "Buscar en la web", "settings.permissions.tool.websearch.description": "Buscar en la web",
"settings.permissions.tool.codesearch.title": "Búsqueda de Código",
"settings.permissions.tool.codesearch.description": "Buscar código en la web",
"settings.permissions.tool.external_directory.title": "Directorio Externo", "settings.permissions.tool.external_directory.title": "Directorio Externo",
"settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto", "settings.permissions.tool.external_directory.description": "Acceder a archivos fuera del directorio del proyecto",
"settings.permissions.tool.doom_loop.title": "Bucle Infinito", "settings.permissions.tool.doom_loop.title": "Bucle Infinito",
+7 -4
View File
@@ -210,7 +210,7 @@ export const dict = {
"common.saving": "Enregistrement...", "common.saving": "Enregistrement...",
"common.default": "Défaut", "common.default": "Défaut",
"common.attachment": "pièce jointe", "common.attachment": "pièce jointe",
"prompt.placeholder.shell": "Entrez une commande shell...", "prompt.placeholder.shell": "Entrez une commande shell... {{example}}",
"prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"', "prompt.placeholder.normal": 'Demandez n\'importe quoi... "{{example}}"',
"prompt.placeholder.simple": "Demandez n'importe quoi...", "prompt.placeholder.simple": "Demandez n'importe quoi...",
"prompt.placeholder.summarizeComments": "Résumer les commentaires…", "prompt.placeholder.summarizeComments": "Résumer les commentaires…",
@@ -579,7 +579,9 @@ export const dict = {
"settings.general.row.theme.title": "Thème", "settings.general.row.theme.title": "Thème",
"settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.", "settings.general.row.theme.description": "Personnaliser le thème d'OpenCode.",
"settings.general.row.font.title": "Police de code", "settings.general.row.font.title": "Police de code",
"settings.general.row.font.description": "Personnaliser la police utilisée dans les blocs de code et les terminaux", "settings.general.row.font.description": "Personnaliser la police utilisée dans les blocs de code",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Police de l'interface", "settings.general.row.uiFont.title": "Police de l'interface",
"settings.general.row.uiFont.description": "Personnaliser la police utilisée dans toute l'interface", "settings.general.row.uiFont.description": "Personnaliser la police utilisée dans toute l'interface",
"settings.general.row.followup.title": "Comportement de suivi", "settings.general.row.followup.title": "Comportement de suivi",
@@ -596,6 +598,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Développer les parties de l'outil edit", "settings.general.row.editToolPartsExpanded.title": "Développer les parties de l'outil edit",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Afficher les parties des outils edit, write et patch développées par défaut dans la chronologie", "Afficher les parties des outils edit, write et patch développées par défaut dans la chronologie",
"settings.general.row.showSessionProgressBar.title": "Afficher la barre de progression de la session",
"settings.general.row.showSessionProgressBar.description":
"Afficher la barre de progression animée en haut de la session lorsque l'agent travaille",
"settings.general.row.wayland.title": "Utiliser Wayland natif", "settings.general.row.wayland.title": "Utiliser Wayland natif",
"settings.general.row.wayland.description": "Désactiver le repli X11 sur Wayland. Nécessite un redémarrage.", "settings.general.row.wayland.description": "Désactiver le repli X11 sur Wayland. Nécessite un redémarrage.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -736,8 +741,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL", "settings.permissions.tool.webfetch.description": "Récupérer le contenu d'une URL",
"settings.permissions.tool.websearch.title": "Recherche Web", "settings.permissions.tool.websearch.title": "Recherche Web",
"settings.permissions.tool.websearch.description": "Rechercher sur le web", "settings.permissions.tool.websearch.description": "Rechercher sur le web",
"settings.permissions.tool.codesearch.title": "Recherche de code",
"settings.permissions.tool.codesearch.description": "Rechercher du code sur le web",
"settings.permissions.tool.external_directory.title": "Répertoire externe", "settings.permissions.tool.external_directory.title": "Répertoire externe",
"settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet", "settings.permissions.tool.external_directory.description": "Accéder aux fichiers en dehors du répertoire du projet",
"settings.permissions.tool.doom_loop.title": "Boucle infernale", "settings.permissions.tool.doom_loop.title": "Boucle infernale",
+7 -4
View File
@@ -209,7 +209,7 @@ export const dict = {
"common.saving": "保存中...", "common.saving": "保存中...",
"common.default": "デフォルト", "common.default": "デフォルト",
"common.attachment": "添付ファイル", "common.attachment": "添付ファイル",
"prompt.placeholder.shell": "シェルコマンドを入力...", "prompt.placeholder.shell": "シェルコマンドを入力... {{example}}",
"prompt.placeholder.normal": '何でも聞いてください... "{{example}}"', "prompt.placeholder.normal": '何でも聞いてください... "{{example}}"',
"prompt.placeholder.simple": "何でも聞いてください...", "prompt.placeholder.simple": "何でも聞いてください...",
"prompt.placeholder.summarizeComments": "コメントを要約…", "prompt.placeholder.summarizeComments": "コメントを要約…",
@@ -569,7 +569,9 @@ export const dict = {
"settings.general.row.theme.title": "テーマ", "settings.general.row.theme.title": "テーマ",
"settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。", "settings.general.row.theme.description": "OpenCodeのテーマをカスタマイズします。",
"settings.general.row.font.title": "コードフォント", "settings.general.row.font.title": "コードフォント",
"settings.general.row.font.description": "コードブロックとターミナルで使用するフォントをカスタマイズします", "settings.general.row.font.description": "コードブロックで使用するフォントをカスタマイズします",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UIフォント", "settings.general.row.uiFont.title": "UIフォント",
"settings.general.row.uiFont.description": "インターフェース全体で使用するフォントをカスタマイズします", "settings.general.row.uiFont.description": "インターフェース全体で使用するフォントをカスタマイズします",
"settings.general.row.followup.title": "フォローアップの動作", "settings.general.row.followup.title": "フォローアップの動作",
@@ -585,6 +587,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "edit ツールパーツを展開", "settings.general.row.editToolPartsExpanded.title": "edit ツールパーツを展開",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"タイムラインで edit、write、patch ツールパーツをデフォルトで展開して表示します", "タイムラインで edit、write、patch ツールパーツをデフォルトで展開して表示します",
"settings.general.row.showSessionProgressBar.title": "セッション進行状況バーを表示",
"settings.general.row.showSessionProgressBar.description":
"エージェントの作業中に、セッション上部にアニメーション付きの進行状況バーを表示します",
"settings.general.row.wayland.title": "ネイティブWaylandを使用", "settings.general.row.wayland.title": "ネイティブWaylandを使用",
"settings.general.row.wayland.description": "WaylandでのX11フォールバックを無効にします。再起動が必要です。", "settings.general.row.wayland.description": "WaylandでのX11フォールバックを無効にします。再起動が必要です。",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -722,8 +727,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "URLからコンテンツを取得", "settings.permissions.tool.webfetch.description": "URLからコンテンツを取得",
"settings.permissions.tool.websearch.title": "Web検索", "settings.permissions.tool.websearch.title": "Web検索",
"settings.permissions.tool.websearch.description": "ウェブを検索", "settings.permissions.tool.websearch.description": "ウェブを検索",
"settings.permissions.tool.codesearch.title": "コード検索",
"settings.permissions.tool.codesearch.description": "ウェブ上のコードを検索",
"settings.permissions.tool.external_directory.title": "外部ディレクトリ", "settings.permissions.tool.external_directory.title": "外部ディレクトリ",
"settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス", "settings.permissions.tool.external_directory.description": "プロジェクトディレクトリ外のファイルへのアクセス",
"settings.permissions.tool.doom_loop.title": "無限ループ", "settings.permissions.tool.doom_loop.title": "無限ループ",
+7 -4
View File
@@ -209,7 +209,7 @@ export const dict = {
"common.saving": "저장 중...", "common.saving": "저장 중...",
"common.default": "기본값", "common.default": "기본값",
"common.attachment": "첨부 파일", "common.attachment": "첨부 파일",
"prompt.placeholder.shell": "셸 명령어 입력...", "prompt.placeholder.shell": "셸 명령어 입력... {{example}}",
"prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"', "prompt.placeholder.normal": '무엇이든 물어보세요... "{{example}}"',
"prompt.placeholder.simple": "무엇이든 물어보세요...", "prompt.placeholder.simple": "무엇이든 물어보세요...",
"prompt.placeholder.summarizeComments": "댓글 요약…", "prompt.placeholder.summarizeComments": "댓글 요약…",
@@ -566,7 +566,9 @@ export const dict = {
"settings.general.row.theme.title": "테마", "settings.general.row.theme.title": "테마",
"settings.general.row.theme.description": "OpenCode 테마 사용자 지정", "settings.general.row.theme.description": "OpenCode 테마 사용자 지정",
"settings.general.row.font.title": "코드 글꼴", "settings.general.row.font.title": "코드 글꼴",
"settings.general.row.font.description": "코드 블록과 터미널에 사용되는 글꼴을 사용자 지정", "settings.general.row.font.description": "코드 블록에 사용되는 글꼴을 사용자 지정",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI 글꼴", "settings.general.row.uiFont.title": "UI 글꼴",
"settings.general.row.uiFont.description": "인터페이스 전반에 사용되는 글꼴을 사용자 지정", "settings.general.row.uiFont.description": "인터페이스 전반에 사용되는 글꼴을 사용자 지정",
"settings.general.row.followup.title": "후속 조치 동작", "settings.general.row.followup.title": "후속 조치 동작",
@@ -581,6 +583,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "edit 도구 파트 펼치기", "settings.general.row.editToolPartsExpanded.title": "edit 도구 파트 펼치기",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"타임라인에서 기본적으로 edit, write, patch 도구 파트를 펼친 상태로 표시합니다", "타임라인에서 기본적으로 edit, write, patch 도구 파트를 펼친 상태로 표시합니다",
"settings.general.row.showSessionProgressBar.title": "세션 진행 표시줄 표시",
"settings.general.row.showSessionProgressBar.description":
"에이전트가 작업 중일 때 세션 상단에 애니메이션 진행 표시줄을 표시합니다",
"settings.general.row.wayland.title": "네이티브 Wayland 사용", "settings.general.row.wayland.title": "네이티브 Wayland 사용",
"settings.general.row.wayland.description": "Wayland에서 X11 폴백을 비활성화합니다. 다시 시작해야 합니다.", "settings.general.row.wayland.description": "Wayland에서 X11 폴백을 비활성화합니다. 다시 시작해야 합니다.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -717,8 +722,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기", "settings.permissions.tool.webfetch.description": "URL에서 콘텐츠 가져오기",
"settings.permissions.tool.websearch.title": "웹 검색", "settings.permissions.tool.websearch.title": "웹 검색",
"settings.permissions.tool.websearch.description": "웹 검색", "settings.permissions.tool.websearch.description": "웹 검색",
"settings.permissions.tool.codesearch.title": "코드 검색",
"settings.permissions.tool.codesearch.description": "웹에서 코드 검색",
"settings.permissions.tool.external_directory.title": "외부 디렉터리", "settings.permissions.tool.external_directory.title": "외부 디렉터리",
"settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스", "settings.permissions.tool.external_directory.description": "프로젝트 디렉터리 외부의 파일에 액세스",
"settings.permissions.tool.doom_loop.title": "무한 반복", "settings.permissions.tool.doom_loop.title": "무한 반복",
+7 -4
View File
@@ -230,7 +230,7 @@ export const dict = {
"common.default": "Standard", "common.default": "Standard",
"common.attachment": "vedlegg", "common.attachment": "vedlegg",
"prompt.placeholder.shell": "Skriv inn shell-kommando...", "prompt.placeholder.shell": "Skriv inn shell-kommando... {{example}}",
"prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"', "prompt.placeholder.normal": 'Spør om hva som helst... "{{example}}"',
"prompt.placeholder.simple": "Spør om hva som helst...", "prompt.placeholder.simple": "Spør om hva som helst...",
"prompt.placeholder.summarizeComments": "Oppsummer kommentarer…", "prompt.placeholder.summarizeComments": "Oppsummer kommentarer…",
@@ -640,7 +640,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.", "settings.general.row.theme.description": "Tilpass hvordan OpenCode er tematisert.",
"settings.general.row.font.title": "Kodefont", "settings.general.row.font.title": "Kodefont",
"settings.general.row.font.description": "Tilpass skrifttypen som brukes i kodeblokker og terminaler", "settings.general.row.font.description": "Tilpass skrifttypen som brukes i kodeblokker",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "UI-skrift", "settings.general.row.uiFont.title": "UI-skrift",
"settings.general.row.uiFont.description": "Tilpass skrifttypen som brukes i hele grensesnittet", "settings.general.row.uiFont.description": "Tilpass skrifttypen som brukes i hele grensesnittet",
"settings.general.row.followup.title": "Oppfølgingsadferd", "settings.general.row.followup.title": "Oppfølgingsadferd",
@@ -654,6 +656,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Utvid edit-verktøydeler", "settings.general.row.editToolPartsExpanded.title": "Utvid edit-verktøydeler",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Vis edit-, write- og patch-verktøydeler utvidet som standard i tidslinjen", "Vis edit-, write- og patch-verktøydeler utvidet som standard i tidslinjen",
"settings.general.row.showSessionProgressBar.title": "Vis fremdriftslinje for sesjonen",
"settings.general.row.showSessionProgressBar.description":
"Vis den animerte fremdriftslinjen øverst i sesjonen når agenten jobber",
"settings.general.row.wayland.title": "Bruk innebygd Wayland", "settings.general.row.wayland.title": "Bruk innebygd Wayland",
"settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Krever omstart.", "settings.general.row.wayland.description": "Deaktiver X11-fallback på Wayland. Krever omstart.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -802,8 +807,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Hent innhold fra en URL", "settings.permissions.tool.webfetch.description": "Hent innhold fra en URL",
"settings.permissions.tool.websearch.title": "Websøk", "settings.permissions.tool.websearch.title": "Websøk",
"settings.permissions.tool.websearch.description": "Søk på nettet", "settings.permissions.tool.websearch.description": "Søk på nettet",
"settings.permissions.tool.codesearch.title": "Kodesøk",
"settings.permissions.tool.codesearch.description": "Søk etter kode på nettet",
"settings.permissions.tool.external_directory.title": "Ekstern mappe", "settings.permissions.tool.external_directory.title": "Ekstern mappe",
"settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen", "settings.permissions.tool.external_directory.description": "Få tilgang til filer utenfor prosjektmappen",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+7 -4
View File
@@ -211,7 +211,7 @@ export const dict = {
"common.saving": "Zapisywanie...", "common.saving": "Zapisywanie...",
"common.default": "Domyślny", "common.default": "Domyślny",
"common.attachment": "załącznik", "common.attachment": "załącznik",
"prompt.placeholder.shell": "Wpisz polecenie terminala...", "prompt.placeholder.shell": "Wpisz polecenie terminala... {{example}}",
"prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"', "prompt.placeholder.normal": 'Zapytaj o cokolwiek... "{{example}}"',
"prompt.placeholder.simple": "Zapytaj o cokolwiek...", "prompt.placeholder.simple": "Zapytaj o cokolwiek...",
"prompt.placeholder.summarizeComments": "Podsumuj komentarze…", "prompt.placeholder.summarizeComments": "Podsumuj komentarze…",
@@ -571,7 +571,9 @@ export const dict = {
"settings.general.row.theme.title": "Motyw", "settings.general.row.theme.title": "Motyw",
"settings.general.row.theme.description": "Dostosuj motyw OpenCode.", "settings.general.row.theme.description": "Dostosuj motyw OpenCode.",
"settings.general.row.font.title": "Czcionka kodu", "settings.general.row.font.title": "Czcionka kodu",
"settings.general.row.font.description": "Dostosuj czcionkę używaną w blokach kodu i terminalach", "settings.general.row.font.description": "Dostosuj czcionkę używaną w blokach kodu",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Czcionka interfejsu", "settings.general.row.uiFont.title": "Czcionka interfejsu",
"settings.general.row.uiFont.description": "Dostosuj czcionkę używaną w całym interfejsie", "settings.general.row.uiFont.description": "Dostosuj czcionkę używaną w całym interfejsie",
"settings.general.row.followup.title": "Zachowanie kontynuacji", "settings.general.row.followup.title": "Zachowanie kontynuacji",
@@ -586,6 +588,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Rozwijaj elementy narzędzia edit", "settings.general.row.editToolPartsExpanded.title": "Rozwijaj elementy narzędzia edit",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Domyślnie pokazuj rozwinięte elementy narzędzi edit, write i patch na osi czasu", "Domyślnie pokazuj rozwinięte elementy narzędzi edit, write i patch na osi czasu",
"settings.general.row.showSessionProgressBar.title": "Pokazuj pasek postępu sesji",
"settings.general.row.showSessionProgressBar.description":
"Wyświetlaj animowany pasek postępu u góry sesji, gdy agent pracuje",
"settings.general.row.wayland.title": "Użyj natywnego Wayland", "settings.general.row.wayland.title": "Użyj natywnego Wayland",
"settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.", "settings.general.row.wayland.description": "Wyłącz fallback X11 na Wayland. Wymaga restartu.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -724,8 +729,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL", "settings.permissions.tool.webfetch.description": "Pobieranie zawartości z adresu URL",
"settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci", "settings.permissions.tool.websearch.title": "Wyszukiwanie w sieci",
"settings.permissions.tool.websearch.description": "Przeszukiwanie sieci", "settings.permissions.tool.websearch.description": "Przeszukiwanie sieci",
"settings.permissions.tool.codesearch.title": "Wyszukiwanie kodu",
"settings.permissions.tool.codesearch.description": "Przeszukiwanie kodu w sieci",
"settings.permissions.tool.external_directory.title": "Katalog zewnętrzny", "settings.permissions.tool.external_directory.title": "Katalog zewnętrzny",
"settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu", "settings.permissions.tool.external_directory.description": "Dostęp do plików poza katalogiem projektu",
"settings.permissions.tool.doom_loop.title": "Zapętlenie", "settings.permissions.tool.doom_loop.title": "Zapętlenie",
+7 -4
View File
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "По умолчанию", "common.default": "По умолчанию",
"common.attachment": "вложение", "common.attachment": "вложение",
"prompt.placeholder.shell": "Введите команду оболочки...", "prompt.placeholder.shell": "Введите команду оболочки... {{example}}",
"prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"', "prompt.placeholder.normal": 'Спросите что угодно... "{{example}}"',
"prompt.placeholder.simple": "Спросите что угодно...", "prompt.placeholder.simple": "Спросите что угодно...",
"prompt.placeholder.summarizeComments": "Суммировать комментарии…", "prompt.placeholder.summarizeComments": "Суммировать комментарии…",
@@ -637,7 +637,9 @@ export const dict = {
"settings.general.row.theme.title": "Тема", "settings.general.row.theme.title": "Тема",
"settings.general.row.theme.description": "Настройте оформление OpenCode.", "settings.general.row.theme.description": "Настройте оформление OpenCode.",
"settings.general.row.font.title": "Шрифт кода", "settings.general.row.font.title": "Шрифт кода",
"settings.general.row.font.description": "Настройте шрифт, используемый в блоках кода и терминалах", "settings.general.row.font.description": "Настройте шрифт, используемый в блоках кода",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Шрифт интерфейса", "settings.general.row.uiFont.title": "Шрифт интерфейса",
"settings.general.row.uiFont.description": "Настройте шрифт, используемый во всем интерфейсе", "settings.general.row.uiFont.description": "Настройте шрифт, используемый во всем интерфейсе",
"settings.general.row.followup.title": "Поведение уточняющих вопросов", "settings.general.row.followup.title": "Поведение уточняющих вопросов",
@@ -654,6 +656,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "Разворачивать элементы инструмента edit", "settings.general.row.editToolPartsExpanded.title": "Разворачивать элементы инструмента edit",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Показывать элементы инструментов edit, write и patch в ленте развернутыми по умолчанию", "Показывать элементы инструментов edit, write и patch в ленте развернутыми по умолчанию",
"settings.general.row.showSessionProgressBar.title": "Показывать индикатор прогресса сессии",
"settings.general.row.showSessionProgressBar.description":
"Показывать анимированный индикатор прогресса вверху сессии, когда агент работает",
"settings.general.row.wayland.title": "Использовать нативный Wayland", "settings.general.row.wayland.title": "Использовать нативный Wayland",
"settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.", "settings.general.row.wayland.description": "Отключить X11 fallback на Wayland. Требуется перезапуск.",
"settings.general.row.wayland.tooltip": "settings.general.row.wayland.tooltip":
@@ -803,8 +808,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Получение контента по URL", "settings.permissions.tool.webfetch.description": "Получение контента по URL",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "Поиск в интернете", "settings.permissions.tool.websearch.description": "Поиск в интернете",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "Поиск кода в интернете",
"settings.permissions.tool.external_directory.title": "Внешняя директория", "settings.permissions.tool.external_directory.title": "Внешняя директория",
"settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта", "settings.permissions.tool.external_directory.description": "Доступ к файлам вне директории проекта",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+7 -4
View File
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "ค่าเริ่มต้น", "common.default": "ค่าเริ่มต้น",
"common.attachment": "ไฟล์แนบ", "common.attachment": "ไฟล์แนบ",
"prompt.placeholder.shell": "ป้อนคำสั่งเชลล์...", "prompt.placeholder.shell": "ป้อนคำสั่งเชลล์... {{example}}",
"prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"', "prompt.placeholder.normal": 'ถามอะไรก็ได้... "{{example}}"',
"prompt.placeholder.simple": "ถามอะไรก็ได้...", "prompt.placeholder.simple": "ถามอะไรก็ได้...",
"prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…", "prompt.placeholder.summarizeComments": "สรุปความคิดเห็น…",
@@ -631,7 +631,9 @@ export const dict = {
"settings.general.row.theme.title": "ธีม", "settings.general.row.theme.title": "ธีม",
"settings.general.row.theme.description": "ปรับแต่งวิธีการที่ OpenCode มีธีม", "settings.general.row.theme.description": "ปรับแต่งวิธีการที่ OpenCode มีธีม",
"settings.general.row.font.title": "ฟอนต์โค้ด", "settings.general.row.font.title": "ฟอนต์โค้ด",
"settings.general.row.font.description": "ปรับแต่งฟอนต์ที่ใช้ในบล็อกโค้ดและเทอร์มินัล", "settings.general.row.font.description": "ปรับแต่งฟอนต์ที่ใช้ในบล็อกโค้ด",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "ฟอนต์ UI", "settings.general.row.uiFont.title": "ฟอนต์ UI",
"settings.general.row.uiFont.description": "ปรับแต่งฟอนต์ที่ใช้ทั่วทั้งอินเทอร์เฟซ", "settings.general.row.uiFont.description": "ปรับแต่งฟอนต์ที่ใช้ทั่วทั้งอินเทอร์เฟซ",
"settings.general.row.followup.title": "พฤติกรรมการติดตามผล", "settings.general.row.followup.title": "พฤติกรรมการติดตามผล",
@@ -645,6 +647,9 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.title": "ขยายส่วนเครื่องมือ edit", "settings.general.row.editToolPartsExpanded.title": "ขยายส่วนเครื่องมือ edit",
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"แสดงส่วนเครื่องมือ edit, write และ patch แบบขยายตามค่าเริ่มต้นในไทม์ไลน์", "แสดงส่วนเครื่องมือ edit, write และ patch แบบขยายตามค่าเริ่มต้นในไทม์ไลน์",
"settings.general.row.showSessionProgressBar.title": "แสดงแถบความคืบหน้าของเซสชัน",
"settings.general.row.showSessionProgressBar.description":
"แสดงแถบความคืบหน้าแบบเคลื่อนไหวที่ด้านบนของเซสชันเมื่อเอเจนต์กำลังทำงาน",
"settings.general.row.wayland.title": "ใช้ Wayland แบบเนทีฟ", "settings.general.row.wayland.title": "ใช้ Wayland แบบเนทีฟ",
"settings.general.row.wayland.description": "ปิดใช้งาน X11 fallback บน Wayland ต้องรีสตาร์ท", "settings.general.row.wayland.description": "ปิดใช้งาน X11 fallback บน Wayland ต้องรีสตาร์ท",
"settings.general.row.wayland.tooltip": "บน Linux ที่มีจอภาพรีเฟรชเรตแบบผสม Wayland แบบเนทีฟอาจเสถียรกว่า", "settings.general.row.wayland.tooltip": "บน Linux ที่มีจอภาพรีเฟรชเรตแบบผสม Wayland แบบเนทีฟอาจเสถียรกว่า",
@@ -791,8 +796,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL", "settings.permissions.tool.webfetch.description": "ดึงเนื้อหาจาก URL",
"settings.permissions.tool.websearch.title": "ค้นหาเว็บ", "settings.permissions.tool.websearch.title": "ค้นหาเว็บ",
"settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ", "settings.permissions.tool.websearch.description": "ค้นหาบนเว็บ",
"settings.permissions.tool.codesearch.title": "ค้นหาโค้ด",
"settings.permissions.tool.codesearch.description": "ค้นหาโค้ดบนเว็บ",
"settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก", "settings.permissions.tool.external_directory.title": "ไดเรกทอรีภายนอก",
"settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์", "settings.permissions.tool.external_directory.description": "เข้าถึงไฟล์นอกไดเรกทอรีโปรเจกต์",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+8 -4
View File
@@ -232,7 +232,7 @@ export const dict = {
"common.default": "Varsayılan", "common.default": "Varsayılan",
"common.attachment": "ek", "common.attachment": "ek",
"prompt.placeholder.shell": "Kabuk komutu girin...", "prompt.placeholder.shell": "Kabuk komutu girin... {{example}}",
"prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"', "prompt.placeholder.normal": 'Bir şeyler sorun... "{{example}}"',
"prompt.placeholder.simple": "Bir şeyler sorun...", "prompt.placeholder.simple": "Bir şeyler sorun...",
"prompt.placeholder.summarizeComments": "Yorumları özetle…", "prompt.placeholder.summarizeComments": "Yorumları özetle…",
@@ -644,7 +644,9 @@ export const dict = {
"settings.general.row.theme.title": "Tema", "settings.general.row.theme.title": "Tema",
"settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.", "settings.general.row.theme.description": "OpenCode'un temasını özelleştirin.",
"settings.general.row.font.title": "Kod Yazı Tipi", "settings.general.row.font.title": "Kod Yazı Tipi",
"settings.general.row.font.description": "Kod bloklarında ve terminallerde kullanılan yazı tipini özelleştirin", "settings.general.row.font.description": "Kod bloklarında kullanılan yazı tipini özelleştirin",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "Arayüz Yazı Tipi", "settings.general.row.uiFont.title": "Arayüz Yazı Tipi",
"settings.general.row.uiFont.description": "Arayüz genelinde kullanılan yazı tipini özelleştirin", "settings.general.row.uiFont.description": "Arayüz genelinde kullanılan yazı tipini özelleştirin",
"settings.general.row.followup.title": "Takip davranışı", "settings.general.row.followup.title": "Takip davranışı",
@@ -661,6 +663,10 @@ export const dict = {
"settings.general.row.editToolPartsExpanded.description": "settings.general.row.editToolPartsExpanded.description":
"Zaman çizelgesinde düzenleme, yazma ve yama araç bileşenlerini varsayılan olarak genişletilmiş göster", "Zaman çizelgesinde düzenleme, yazma ve yama araç bileşenlerini varsayılan olarak genişletilmiş göster",
"settings.general.row.showSessionProgressBar.title": "Oturum ilerleme çubuğunu göster",
"settings.general.row.showSessionProgressBar.description":
"Ajan çalışırken oturumun üst kısmında animasyonlu ilerleme çubuğunu göster",
"settings.general.row.wayland.title": "Yerel Wayland kullan", "settings.general.row.wayland.title": "Yerel Wayland kullan",
"settings.general.row.wayland.description": "settings.general.row.wayland.description":
"Wayland'da X11 geri dönüşünü devre dışı bırak. Yeniden başlatma gerektirir.", "Wayland'da X11 geri dönüşünü devre dışı bırak. Yeniden başlatma gerektirir.",
@@ -810,8 +816,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir", "settings.permissions.tool.webfetch.description": "Bir URL'den içerik getir",
"settings.permissions.tool.websearch.title": "Web Ara", "settings.permissions.tool.websearch.title": "Web Ara",
"settings.permissions.tool.websearch.description": "Web'de ara", "settings.permissions.tool.websearch.description": "Web'de ara",
"settings.permissions.tool.codesearch.title": "Kod Ara",
"settings.permissions.tool.codesearch.description": "Web'de kod ara",
"settings.permissions.tool.external_directory.title": "Harici Dizin", "settings.permissions.tool.external_directory.title": "Harici Dizin",
"settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş", "settings.permissions.tool.external_directory.description": "Proje dizini dışındaki dosyalara eriş",
"settings.permissions.tool.doom_loop.title": "Sonsuz Döngü", "settings.permissions.tool.doom_loop.title": "Sonsuz Döngü",
+6 -4
View File
@@ -249,7 +249,7 @@ export const dict = {
"common.default": "默认", "common.default": "默认",
"common.attachment": "附件", "common.attachment": "附件",
"prompt.placeholder.shell": "输入 shell 命令...", "prompt.placeholder.shell": "输入 shell 命令... {{example}}",
"prompt.placeholder.normal": '随便问点什么... "{{example}}"', "prompt.placeholder.normal": '随便问点什么... "{{example}}"',
"prompt.placeholder.simple": "随便问点什么...", "prompt.placeholder.simple": "随便问点什么...",
"prompt.placeholder.summarizeComments": "总结评论…", "prompt.placeholder.summarizeComments": "总结评论…",
@@ -631,7 +631,9 @@ export const dict = {
"settings.general.row.theme.title": "主题", "settings.general.row.theme.title": "主题",
"settings.general.row.theme.description": "自定义 OpenCode 的主题。", "settings.general.row.theme.description": "自定义 OpenCode 的主题。",
"settings.general.row.font.title": "代码字体", "settings.general.row.font.title": "代码字体",
"settings.general.row.font.description": "自定义代码块和终端使用的字体", "settings.general.row.font.description": "自定义代码块使用的字体",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "界面字体", "settings.general.row.uiFont.title": "界面字体",
"settings.general.row.uiFont.description": "自定义整个界面使用的字体", "settings.general.row.uiFont.description": "自定义整个界面使用的字体",
"settings.general.row.followup.title": "跟进消息行为", "settings.general.row.followup.title": "跟进消息行为",
@@ -644,6 +646,8 @@ export const dict = {
"settings.general.row.shellToolPartsExpanded.description": "默认在时间线中展开 shell 工具部分", "settings.general.row.shellToolPartsExpanded.description": "默认在时间线中展开 shell 工具部分",
"settings.general.row.editToolPartsExpanded.title": "展开编辑工具部分", "settings.general.row.editToolPartsExpanded.title": "展开编辑工具部分",
"settings.general.row.editToolPartsExpanded.description": "默认在时间线中展开 edit、write 和 patch 工具部分", "settings.general.row.editToolPartsExpanded.description": "默认在时间线中展开 edit、write 和 patch 工具部分",
"settings.general.row.showSessionProgressBar.title": "显示会话进度条",
"settings.general.row.showSessionProgressBar.description": "当智能体正在工作时,在会话顶部显示动画进度条",
"settings.general.row.wayland.title": "使用原生 Wayland", "settings.general.row.wayland.title": "使用原生 Wayland",
"settings.general.row.wayland.description": "在 Wayland 上禁用 X11 回退。需要重启。", "settings.general.row.wayland.description": "在 Wayland 上禁用 X11 回退。需要重启。",
"settings.general.row.wayland.tooltip": "在混合刷新率显示器的 Linux 系统上,原生 Wayland 可能更稳定。", "settings.general.row.wayland.tooltip": "在混合刷新率显示器的 Linux 系统上,原生 Wayland 可能更稳定。",
@@ -789,8 +793,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "从 URL 获取内容", "settings.permissions.tool.webfetch.description": "从 URL 获取内容",
"settings.permissions.tool.websearch.title": "网页搜索", "settings.permissions.tool.websearch.title": "网页搜索",
"settings.permissions.tool.websearch.description": "搜索网页", "settings.permissions.tool.websearch.description": "搜索网页",
"settings.permissions.tool.codesearch.title": "代码搜索",
"settings.permissions.tool.codesearch.description": "在网上搜索代码",
"settings.permissions.tool.external_directory.title": "外部目录", "settings.permissions.tool.external_directory.title": "外部目录",
"settings.permissions.tool.external_directory.description": "访问项目目录之外的文件", "settings.permissions.tool.external_directory.description": "访问项目目录之外的文件",
"settings.permissions.tool.doom_loop.title": "死循环", "settings.permissions.tool.doom_loop.title": "死循环",
+6 -4
View File
@@ -227,7 +227,7 @@ export const dict = {
"common.default": "預設", "common.default": "預設",
"common.attachment": "附件", "common.attachment": "附件",
"prompt.placeholder.shell": "輸入 shell 命令...", "prompt.placeholder.shell": "輸入 shell 命令... {{example}}",
"prompt.placeholder.normal": '隨便問點什麼... "{{example}}"', "prompt.placeholder.normal": '隨便問點什麼... "{{example}}"',
"prompt.placeholder.simple": "隨便問點什麼...", "prompt.placeholder.simple": "隨便問點什麼...",
"prompt.placeholder.summarizeComments": "摘要評論…", "prompt.placeholder.summarizeComments": "摘要評論…",
@@ -626,7 +626,9 @@ export const dict = {
"settings.general.row.theme.title": "主題", "settings.general.row.theme.title": "主題",
"settings.general.row.theme.description": "自訂 OpenCode 的主題。", "settings.general.row.theme.description": "自訂 OpenCode 的主題。",
"settings.general.row.font.title": "程式碼字型", "settings.general.row.font.title": "程式碼字型",
"settings.general.row.font.description": "自訂程式碼區塊和終端機使用的字型", "settings.general.row.font.description": "自訂程式碼區塊使用的字型",
"settings.general.row.terminalFont.title": "Terminal Font",
"settings.general.row.terminalFont.description": "Customise the font used in the terminal",
"settings.general.row.uiFont.title": "介面字型", "settings.general.row.uiFont.title": "介面字型",
"settings.general.row.uiFont.description": "自訂整個介面使用的字型", "settings.general.row.uiFont.description": "自訂整個介面使用的字型",
"settings.general.row.followup.title": "後續追問行為", "settings.general.row.followup.title": "後續追問行為",
@@ -640,6 +642,8 @@ export const dict = {
"settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊", "settings.general.row.shellToolPartsExpanded.description": "在時間軸中預設展開 shell 工具區塊",
"settings.general.row.editToolPartsExpanded.title": "展開 edit 工具區塊", "settings.general.row.editToolPartsExpanded.title": "展開 edit 工具區塊",
"settings.general.row.editToolPartsExpanded.description": "在時間軸中預設展開 edit、write 和 patch 工具區塊", "settings.general.row.editToolPartsExpanded.description": "在時間軸中預設展開 edit、write 和 patch 工具區塊",
"settings.general.row.showSessionProgressBar.title": "顯示工作階段進度列",
"settings.general.row.showSessionProgressBar.description": "當代理程式正在運作時,在工作階段頂部顯示動畫進度列",
"settings.general.row.wayland.title": "使用原生 Wayland", "settings.general.row.wayland.title": "使用原生 Wayland",
"settings.general.row.wayland.description": "在 Wayland 上停用 X11 後備模式。需要重新啟動。", "settings.general.row.wayland.description": "在 Wayland 上停用 X11 後備模式。需要重新啟動。",
"settings.general.row.wayland.tooltip": "在混合更新率螢幕的 Linux 系統上,原生 Wayland 可能更穩定。", "settings.general.row.wayland.tooltip": "在混合更新率螢幕的 Linux 系統上,原生 Wayland 可能更穩定。",
@@ -785,8 +789,6 @@ export const dict = {
"settings.permissions.tool.webfetch.description": "從 URL 取得內容", "settings.permissions.tool.webfetch.description": "從 URL 取得內容",
"settings.permissions.tool.websearch.title": "Web Search", "settings.permissions.tool.websearch.title": "Web Search",
"settings.permissions.tool.websearch.description": "搜尋網頁", "settings.permissions.tool.websearch.description": "搜尋網頁",
"settings.permissions.tool.codesearch.title": "Code Search",
"settings.permissions.tool.codesearch.description": "在網路上搜尋程式碼",
"settings.permissions.tool.external_directory.title": "外部目錄", "settings.permissions.tool.external_directory.title": "外部目錄",
"settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案", "settings.permissions.tool.external_directory.description": "存取專案目錄之外的檔案",
"settings.permissions.tool.doom_loop.title": "Doom Loop", "settings.permissions.tool.doom_loop.title": "Doom Loop",
+16
View File
@@ -1,5 +1,12 @@
@import "@opencode-ai/ui/styles/tailwind"; @import "@opencode-ai/ui/styles/tailwind";
@font-face {
font-family: "JetBrainsMono Nerd Font Mono";
src: url("/assets/JetBrainsMonoNerdFontMono-Regular.woff2") format("woff2");
font-weight: normal;
font-style: normal;
}
@layer components { @layer components {
@keyframes session-progress-whip { @keyframes session-progress-whip {
0% { 0% {
@@ -66,4 +73,13 @@
width: auto; width: auto;
} }
} }
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
} }
+6 -7
View File
@@ -1,8 +1,8 @@
import { DataProvider } from "@opencode-ai/ui/context" import { DataProvider } from "@opencode-ai/ui/context"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { useLocation, useNavigate, useParams } from "@solidjs/router" import { useLocation, useNavigate, useParams } from "@solidjs/router"
import { createEffect, createMemo, type ParentProps, Show } from "solid-js" import { createEffect, createMemo, createResource, type ParentProps, Show } from "solid-js"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { LocalProvider } from "@/context/local" import { LocalProvider } from "@/context/local"
import { SDKProvider } from "@/context/sdk" import { SDKProvider } from "@/context/sdk"
@@ -23,11 +23,10 @@ function DirectoryDataProvider(props: ParentProps<{ directory: string }>) {
navigate(`/${base64Encode(next)}${path}${location.search}${location.hash}`, { replace: true }) navigate(`/${base64Encode(next)}${path}${location.search}${location.hash}`, { replace: true })
}) })
createEffect(() => { createResource(
const id = params.id () => params.id,
if (!id) return (id) => sync.session.sync(id),
void sync.session.sync(id) )
})
return ( return (
<DataProvider <DataProvider
+2 -3
View File
@@ -244,10 +244,9 @@ export const ErrorPage: Component<ErrorPageProps> = (props) => {
} }
async function installUpdate() { async function installUpdate() {
if (!platform.update || !platform.restart) return if (!platform.updateAndRestart) return
await platform await platform
.update() .updateAndRestart()
.then(() => platform.restart!())
.then(() => setStore("actionError", undefined)) .then(() => setStore("actionError", undefined))
.catch((err) => { .catch((err) => {
setStore("actionError", formatError(err, language.t)) setStore("actionError", formatError(err, language.t))
+1 -1
View File
@@ -3,7 +3,7 @@ import { Button } from "@opencode-ai/ui/button"
import { Logo } from "@opencode-ai/ui/logo" import { Logo } from "@opencode-ai/ui/logo"
import { useLayout } from "@/context/layout" import { useLayout } from "@/context/layout"
import { useNavigate } from "@solidjs/router" import { useNavigate } from "@solidjs/router"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Icon } from "@opencode-ai/ui/icon" import { Icon } from "@opencode-ai/ui/icon"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { DateTime } from "luxon" import { DateTime } from "luxon"
+39 -46
View File
@@ -17,7 +17,7 @@ import { useLocation, useNavigate, useParams } from "@solidjs/router"
import { useLayout, LocalProject } from "@/context/layout" import { useLayout, LocalProject } from "@/context/layout"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
import { Persist, persisted } from "@/utils/persist" import { Persist, persisted } from "@/utils/persist"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { decode64 } from "@/utils/base64" import { decode64 } from "@/utils/base64"
import { ResizeHandle } from "@opencode-ai/ui/resize-handle" import { ResizeHandle } from "@opencode-ai/ui/resize-handle"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
@@ -25,7 +25,7 @@ import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { Dialog } from "@opencode-ai/ui/dialog" import { Dialog } from "@opencode-ai/ui/dialog"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Session, type Message } from "@opencode-ai/sdk/v2/client" import { Session, type Message } from "@opencode-ai/sdk/v2/client"
import { usePlatform } from "@/context/platform" import { usePlatform } from "@/context/platform"
import { useSettings } from "@/context/settings" import { useSettings } from "@/context/settings"
@@ -48,8 +48,8 @@ import {
} from "@/context/global-sync/session-prefetch" } from "@/context/global-sync/session-prefetch"
import { useNotification } from "@/context/notification" import { useNotification } from "@/context/notification"
import { usePermission } from "@/context/permission" import { usePermission } from "@/context/permission"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { retry } from "@opencode-ai/shared/util/retry" import { retry } from "@opencode-ai/core/util/retry"
import { playSoundById } from "@/utils/sound" import { playSoundById } from "@/utils/sound"
import { createAim } from "@/utils/aim" import { createAim } from "@/utils/aim"
import { setNavigate } from "@/utils/notification-click" import { setNavigate } from "@/utils/notification-click"
@@ -64,13 +64,13 @@ import { DebugBar } from "@/components/debug-bar"
import { Titlebar } from "@/components/titlebar" import { Titlebar } from "@/components/titlebar"
import { useServer } from "@/context/server" import { useServer } from "@/context/server"
import { useLanguage, type Locale } from "@/context/language" import { useLanguage, type Locale } from "@/context/language"
import { pathKey } from "@/utils/path-key"
import { import {
displayName, displayName,
effectiveWorkspaceOrder, effectiveWorkspaceOrder,
errorMessage, errorMessage,
latestRootSession, latestRootSession,
sortedRootSessions, sortedRootSessions,
workspaceKey,
} from "./layout/helpers" } from "./layout/helpers"
import { import {
collectNewSessionDeepLinks, collectNewSessionDeepLinks,
@@ -164,7 +164,7 @@ export default function Layout(props: ParentProps) {
const editor = createInlineEditorController() const editor = createInlineEditorController()
const setBusy = (directory: string, value: boolean) => { const setBusy = (directory: string, value: boolean) => {
const key = workspaceKey(directory) const key = pathKey(directory)
if (value) { if (value) {
setState("busyWorkspaces", key, true) setState("busyWorkspaces", key, true)
return return
@@ -176,7 +176,7 @@ export default function Layout(props: ParentProps) {
}), }),
) )
} }
const isBusy = (directory: string) => !!state.busyWorkspaces[workspaceKey(directory)] const isBusy = (directory: string) => !!state.busyWorkspaces[pathKey(directory)]
const navLeave = { current: undefined as number | undefined } const navLeave = { current: undefined as number | undefined }
const sortNow = () => state.sortNow const sortNow = () => state.sortNow
let sizet: number | undefined let sizet: number | undefined
@@ -366,7 +366,7 @@ export default function Layout(props: ParentProps) {
const useUpdatePolling = () => const useUpdatePolling = () =>
onMount(() => { onMount(() => {
if (!platform.checkUpdate || !platform.update || !platform.restart) return if (!platform.checkUpdate || !platform.updateAndRestart) return
let toastId: number | undefined let toastId: number | undefined
let interval: ReturnType<typeof setInterval> | undefined let interval: ReturnType<typeof setInterval> | undefined
@@ -384,8 +384,7 @@ export default function Layout(props: ParentProps) {
{ {
label: language.t("toast.update.action.installRestart"), label: language.t("toast.update.action.installRestart"),
onClick: async () => { onClick: async () => {
await platform.update!() await platform.updateAndRestart!()
await platform.restart!()
}, },
}, },
{ {
@@ -498,8 +497,8 @@ export default function Layout(props: ParentProps) {
} }
const currentSession = params.id const currentSession = params.id
if (workspaceKey(directory) === workspaceKey(currentDir()) && props.sessionID === currentSession) return if (pathKey(directory) === pathKey(currentDir()) && props.sessionID === currentSession) return
if (workspaceKey(directory) === workspaceKey(currentDir()) && session?.parentID === currentSession) return if (pathKey(directory) === pathKey(currentDir()) && session?.parentID === currentSession) return
dismissSessionAlert(sessionKey) dismissSessionAlert(sessionKey)
@@ -557,14 +556,14 @@ export default function Layout(props: ParentProps) {
const currentProject = createMemo(() => { const currentProject = createMemo(() => {
const directory = currentDir() const directory = currentDir()
if (!directory) return if (!directory) return
const key = workspaceKey(directory) const key = pathKey(directory)
const projects = layout.projects.list() const projects = layout.projects.list()
const sandbox = projects.find((p) => p.sandboxes?.some((item) => workspaceKey(item) === key)) const sandbox = projects.find((p) => p.sandboxes?.some((item) => pathKey(item) === key))
if (sandbox) return sandbox if (sandbox) return sandbox
const direct = projects.find((p) => workspaceKey(p.worktree) === key) const direct = projects.find((p) => pathKey(p.worktree) === key)
if (direct) return direct if (direct) return direct
const [child] = globalSync.child(directory, { bootstrap: false }) const [child] = globalSync.child(directory, { bootstrap: false })
@@ -597,7 +596,7 @@ export default function Layout(props: ParentProps) {
}) })
const workspaceName = (directory: string, projectId?: string, branch?: string) => { const workspaceName = (directory: string, projectId?: string, branch?: string) => {
const key = workspaceKey(directory) const key = pathKey(directory)
const direct = store.workspaceName[key] ?? store.workspaceName[directory] const direct = store.workspaceName[key] ?? store.workspaceName[directory]
if (direct) return direct if (direct) return direct
if (!projectId) return if (!projectId) return
@@ -606,7 +605,7 @@ export default function Layout(props: ParentProps) {
} }
const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => { const setWorkspaceName = (directory: string, next: string, projectId?: string, branch?: string) => {
const key = workspaceKey(directory) const key = pathKey(directory)
setStore("workspaceName", key, next) setStore("workspaceName", key, next)
if (!projectId) return if (!projectId) return
if (!branch) return if (!branch) return
@@ -634,7 +633,7 @@ export default function Layout(props: ParentProps) {
const activeDir = currentDir() const activeDir = currentDir()
return workspaceIds(project).filter((directory) => { return workspaceIds(project).filter((directory) => {
const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree const expanded = store.workspaceExpanded[directory] ?? directory === project.worktree
const active = workspaceKey(directory) === workspaceKey(activeDir) const active = pathKey(directory) === pathKey(activeDir)
return expanded || active return expanded || active
}) })
}) })
@@ -645,10 +644,9 @@ export default function Layout(props: ParentProps) {
const projects = layout.projects.list() const projects = layout.projects.list()
for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) { for (const [directory, expanded] of Object.entries(store.workspaceExpanded)) {
if (!expanded) continue if (!expanded) continue
const key = workspaceKey(directory) const key = pathKey(directory)
const project = projects.find( const project = projects.find(
(item) => (item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key),
workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
) )
if (!project) continue if (!project) continue
if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue if (project.vcs === "git" && layout.sidebar.workspaces(project.worktree)()) continue
@@ -701,7 +699,7 @@ export default function Layout(props: ParentProps) {
seen: lru, seen: lru,
keep: sessionID, keep: sessionID,
limit: PREFETCH_MAX_SESSIONS_PER_DIR, limit: PREFETCH_MAX_SESSIONS_PER_DIR,
preserve: params.id && workspaceKey(directory) === workspaceKey(currentDir()) ? [params.id] : undefined, preserve: params.id && pathKey(directory) === pathKey(currentDir()) ? [params.id] : undefined,
}) })
} }
@@ -1222,17 +1220,14 @@ export default function Layout(props: ParentProps) {
} }
function projectRoot(directory: string) { function projectRoot(directory: string) {
const key = workspaceKey(directory) const key = pathKey(directory)
const project = layout.projects const project = layout.projects
.list() .list()
.find( .find((item) => pathKey(item.worktree) === key || item.sandboxes?.some((sandbox) => pathKey(sandbox) === key))
(item) =>
workspaceKey(item.worktree) === key || item.sandboxes?.some((sandbox) => workspaceKey(sandbox) === key),
)
if (project) return project.worktree if (project) return project.worktree
const known = Object.entries(store.workspaceOrder).find( const known = Object.entries(store.workspaceOrder).find(
([root, dirs]) => workspaceKey(root) === key || dirs.some((item) => workspaceKey(item) === key), ([root, dirs]) => pathKey(root) === key || dirs.some((item) => pathKey(item) === key),
) )
if (known) return known[0] if (known) return known[0]
@@ -1284,7 +1279,7 @@ export default function Layout(props: ParentProps) {
: [root] : [root]
const canOpen = (value: string | undefined) => { const canOpen = (value: string | undefined) => {
if (!value) return false if (!value) return false
return dirs.some((item) => workspaceKey(item) === workspaceKey(value)) return dirs.some((item) => pathKey(item) === pathKey(value))
} }
const refreshDirs = async (target?: string) => { const refreshDirs = async (target?: string) => {
if (!target || target === root || canOpen(target)) return canOpen(target) if (!target || target === root || canOpen(target)) return canOpen(target)
@@ -1410,9 +1405,9 @@ export default function Layout(props: ParentProps) {
function closeProject(directory: string) { function closeProject(directory: string) {
const list = layout.projects.list() const list = layout.projects.list()
const key = workspaceKey(directory) const key = pathKey(directory)
const index = list.findIndex((x) => workspaceKey(x.worktree) === key) const index = list.findIndex((x) => pathKey(x.worktree) === key)
const active = workspaceKey(currentProject()?.worktree ?? "") === key const active = pathKey(currentProject()?.worktree ?? "") === key
if (index === -1) return if (index === -1) return
const next = list[index + 1] const next = list[index + 1]
@@ -1486,8 +1481,8 @@ export default function Layout(props: ParentProps) {
if (directory === root) return if (directory === root) return
const current = currentDir() const current = currentDir()
const currentKey = workspaceKey(current) const currentKey = pathKey(current)
const deletedKey = workspaceKey(directory) const deletedKey = pathKey(directory)
const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey) const shouldLeave = leaveDeletedWorkspace || (!!params.dir && currentKey === deletedKey)
if (!leaveDeletedWorkspace && shouldLeave) { if (!leaveDeletedWorkspace && shouldLeave) {
navigateWithSidebarReset(`/${base64Encode(root)}/session`) navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@@ -1510,7 +1505,7 @@ export default function Layout(props: ParentProps) {
if (!result) return if (!result) return
if (workspaceKey(store.lastProjectSession[root]?.directory ?? "") === workspaceKey(directory)) { if (pathKey(store.lastProjectSession[root]?.directory ?? "") === pathKey(directory)) {
clearLastProjectSession(root) clearLastProjectSession(root)
} }
@@ -1530,12 +1525,12 @@ export default function Layout(props: ParentProps) {
if (shouldLeave) return if (shouldLeave) return
const nextCurrent = currentDir() const nextCurrent = currentDir()
const nextKey = workspaceKey(nextCurrent) const nextKey = pathKey(nextCurrent)
const project = layout.projects.list().find((item) => item.worktree === root) const project = layout.projects.list().find((item) => item.worktree === root)
const dirs = project const dirs = project
? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root]) ? effectiveWorkspaceOrder(root, [root, ...(project.sandboxes ?? [])], store.workspaceOrder[root])
: [root] : [root]
const valid = dirs.some((item) => workspaceKey(item) === nextKey) const valid = dirs.some((item) => pathKey(item) === nextKey)
if (params.dir && projectRoot(nextCurrent) === root && !valid) { if (params.dir && projectRoot(nextCurrent) === root && !valid) {
navigateWithSidebarReset(`/${base64Encode(root)}/session`) navigateWithSidebarReset(`/${base64Encode(root)}/session`)
@@ -1641,7 +1636,7 @@ export default function Layout(props: ParentProps) {
}) })
const handleDelete = () => { const handleDelete = () => {
const leaveDeletedWorkspace = !!params.dir && workspaceKey(currentDir()) === workspaceKey(props.directory) const leaveDeletedWorkspace = !!params.dir && pathKey(currentDir()) === pathKey(props.directory)
if (leaveDeletedWorkspace) { if (leaveDeletedWorkspace) {
navigateWithSidebarReset(`/${base64Encode(props.root)}/session`) navigateWithSidebarReset(`/${base64Encode(props.root)}/session`)
} }
@@ -1868,11 +1863,9 @@ export default function Layout(props: ParentProps) {
const local = project.worktree const local = project.worktree
const dirs = [local, ...(project.sandboxes ?? [])] const dirs = [local, ...(project.sandboxes ?? [])]
const active = currentProject() const active = currentProject()
const directory = workspaceKey(active?.worktree ?? "") === workspaceKey(project.worktree) ? currentDir() : undefined const directory = pathKey(active?.worktree ?? "") === pathKey(project.worktree) ? currentDir() : undefined
const extra = const extra =
directory && directory && pathKey(directory) !== pathKey(local) && !dirs.some((item) => pathKey(item) === pathKey(directory))
workspaceKey(directory) !== workspaceKey(local) &&
!dirs.some((item) => workspaceKey(item) === workspaceKey(directory))
? directory ? directory
: undefined : undefined
const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false const pending = extra ? WorktreeState.get(extra)?.status === "pending" : false
@@ -1917,7 +1910,7 @@ export default function Layout(props: ParentProps) {
setStore( setStore(
"workspaceOrder", "workspaceOrder",
project.worktree, project.worktree,
result.filter((directory) => workspaceKey(directory) !== workspaceKey(project.worktree)), result.filter((directory) => pathKey(directory) !== pathKey(project.worktree)),
) )
} }
@@ -1943,8 +1936,8 @@ export default function Layout(props: ParentProps) {
setWorkspaceName(created.directory, created.branch, project.id, created.branch) setWorkspaceName(created.directory, created.branch, project.id, created.branch)
const local = project.worktree const local = project.worktree
const key = workspaceKey(created.directory) const key = pathKey(created.directory)
const root = workspaceKey(local) const root = pathKey(local)
setBusy(created.directory, true) setBusy(created.directory, true)
WorktreeState.pending(created.directory) WorktreeState.pending(created.directory)
@@ -1955,7 +1948,7 @@ export default function Layout(props: ParentProps) {
setStore("workspaceOrder", project.worktree, (prev) => { setStore("workspaceOrder", project.worktree, (prev) => {
const existing = prev ?? [] const existing = prev ?? []
const next = existing.filter((item) => { const next = existing.filter((item) => {
const id = workspaceKey(item) const id = pathKey(item)
return id !== root && id !== key return id !== root && id !== key
}) })
return [created.directory, ...next] return [created.directory, ...next]
@@ -14,8 +14,8 @@ import {
errorMessage, errorMessage,
hasProjectPermissions, hasProjectPermissions,
latestRootSession, latestRootSession,
workspaceKey,
} from "./helpers" } from "./helpers"
import { pathKey } from "@/utils/path-key"
const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) => const session = (input: Partial<Session> & Pick<Session, "id" | "directory">) =>
({ ({
@@ -104,16 +104,16 @@ describe("layout deep links", () => {
describe("layout workspace helpers", () => { describe("layout workspace helpers", () => {
test("normalizes trailing slash in workspace key", () => { test("normalizes trailing slash in workspace key", () => {
expect(workspaceKey("/tmp/demo///")).toBe("/tmp/demo") expect(String(pathKey("/tmp/demo///"))).toBe("/tmp/demo")
expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:/tmp/demo") expect(String(pathKey("C:\\tmp\\demo\\\\"))).toBe("C:/tmp/demo")
}) })
test("preserves posix and drive roots in workspace key", () => { test("preserves posix and drive roots in workspace key", () => {
expect(workspaceKey("/")).toBe("/") expect(String(pathKey("/"))).toBe("/")
expect(workspaceKey("///")).toBe("/") expect(String(pathKey("///"))).toBe("/")
expect(workspaceKey("C:\\")).toBe("C:/") expect(String(pathKey("C:\\"))).toBe("C:/")
expect(workspaceKey("C://")).toBe("C:/") expect(String(pathKey("C://"))).toBe("C:/")
expect(workspaceKey("C:///")).toBe("C:/") expect(String(pathKey("C:///"))).toBe("C:/")
}) })
test("keeps local first while preserving known order", () => { test("keeps local first while preserving known order", () => {
+7 -14
View File
@@ -1,19 +1,12 @@
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { type Session } from "@opencode-ai/sdk/v2/client" import { type Session } from "@opencode-ai/sdk/v2/client"
import { pathKey } from "@/utils/path-key"
type SessionStore = { type SessionStore = {
session?: Session[] session?: Session[]
path: { directory: string } path: { directory: string }
} }
export const workspaceKey = (directory: string) => {
const value = directory.replaceAll("\\", "/")
const drive = value.match(/^([A-Za-z]:)\/+$/)
if (drive) return `${drive[1]}/`
if (/^\/+$/i.test(value)) return "/"
return value.replace(/\/+$/, "")
}
function sortSessions(now: number) { function sortSessions(now: number) {
const oneMinuteAgo = now - 60 * 1000 const oneMinuteAgo = now - 60 * 1000
return (a: Session, b: Session) => { return (a: Session, b: Session) => {
@@ -29,9 +22,9 @@ function sortSessions(now: number) {
} }
const isRootVisibleSession = (session: Session, directory: string) => const isRootVisibleSession = (session: Session, directory: string) =>
workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived pathKey(session.directory) === pathKey(directory) && !session.parentID && !session.time?.archived
const roots = (store: SessionStore) => export const roots = (store: SessionStore) =>
(store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory)) (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory))
export const sortedRootSessions = (store: SessionStore, now: number) => roots(store).sort(sortSessions(now)) export const sortedRootSessions = (store: SessionStore, now: number) => roots(store).sort(sortSessions(now))
@@ -72,11 +65,11 @@ export const errorMessage = (err: unknown, fallback: string) => {
} }
export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => { export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => {
const root = workspaceKey(local) const root = pathKey(local)
const live = new Map<string, string>() const live = new Map<string, string>()
for (const dir of dirs) { for (const dir of dirs) {
const key = workspaceKey(dir) const key = pathKey(dir)
if (key === root) continue if (key === root) continue
if (!live.has(key)) live.set(key, dir) if (!live.has(key)) live.set(key, dir)
} }
@@ -85,7 +78,7 @@ export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted
const result = [local] const result = [local]
for (const dir of persisted) { for (const dir of persisted) {
const key = workspaceKey(dir) const key = pathKey(dir)
if (key === root) continue if (key === root) continue
const match = live.get(key) const match = live.get(key)
if (!match) continue if (!match) continue
@@ -4,7 +4,7 @@ import { Icon } from "@opencode-ai/ui/icon"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { Spinner } from "@opencode-ai/ui/spinner" import { Spinner } from "@opencode-ai/ui/spinner"
import { Tooltip } from "@opencode-ai/ui/tooltip" import { Tooltip } from "@opencode-ai/ui/tooltip"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { A, useParams } from "@solidjs/router" import { A, useParams } from "@solidjs/router"
import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js" import { type Accessor, createMemo, For, type JSX, Match, Show, Switch } from "solid-js"
import { useGlobalSync } from "@/context/global-sync" import { useGlobalSync } from "@/context/global-sync"
@@ -19,6 +19,13 @@ import { childSessionOnPath, hasProjectPermissions } from "./helpers"
const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750" const OPENCODE_PROJECT_ID = "4b0ea68d7af9a6031a7ffda7ad66e0cb83315750"
export function getProjectAvatarSource(id?: string, icon?: { color?: string; url?: string; override?: string }) {
if (id === OPENCODE_PROJECT_ID) return "https://opencode.ai/favicon.svg"
if (icon?.override) return icon?.override
if (icon?.color) return undefined
return icon?.url
}
export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => { export const ProjectIcon = (props: { project: LocalProject; class?: string; notify?: boolean }): JSX.Element => {
const globalSync = useGlobalSync() const globalSync = useGlobalSync()
const notification = useNotification() const notification = useNotification()
@@ -42,9 +49,7 @@ export const ProjectIcon = (props: { project: LocalProject; class?: string; noti
<div class="size-full rounded overflow-clip"> <div class="size-full rounded overflow-clip">
<Avatar <Avatar
fallback={name()} fallback={name()}
src={ src={getProjectAvatarSource(props.project.id, props.project.icon)}
props.project.id === OPENCODE_PROJECT_ID ? "https://opencode.ai/favicon.svg" : props.project.icon?.override
}
{...getAvatarColors(props.project.icon?.color)} {...getAvatarColors(props.project.icon?.color)}
class="size-full rounded" class="size-full rounded"
classList={{ "badge-mask": notify() }} classList={{ "badge-mask": notify() }}
@@ -263,10 +268,10 @@ export const SessionItem = (props: SessionItemProps): JSX.Element => {
</Show> </Show>
</div> </div>
</div> </div>
<Show when={currentChild()}> <Show when={currentChild()} keyed>
{(child) => ( {(child) => (
<div class="w-full"> <div class="w-full">
<SessionItem {...props} session={child()} level={(props.level ?? 0) + 1} /> <SessionItem {...props} session={child} level={(props.level ?? 0) + 1} />
</div> </div>
)} )}
</Show> </Show>
@@ -1,6 +1,6 @@
import { createMemo, For, Show, type Accessor, type JSX } from "solid-js" import { createMemo, For, Show, type Accessor, type JSX } from "solid-js"
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { ContextMenu } from "@opencode-ai/ui/context-menu" import { ContextMenu } from "@opencode-ai/ui/context-menu"
import { HoverCard } from "@opencode-ai/ui/hover-card" import { HoverCard } from "@opencode-ai/ui/hover-card"
@@ -3,8 +3,8 @@ import { createEffect, createMemo, For, Show, type Accessor, type JSX } from "so
import { createStore } from "solid-js/store" import { createStore } from "solid-js/store"
import { createSortable } from "@thisbeyond/solid-dnd" import { createSortable } from "@thisbeyond/solid-dnd"
import { createMediaQuery } from "@solid-primitives/media" import { createMediaQuery } from "@solid-primitives/media"
import { base64Encode } from "@opencode-ai/shared/util/encode" import { base64Encode } from "@opencode-ai/core/util/encode"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { Collapsible } from "@opencode-ai/ui/collapsible" import { Collapsible } from "@opencode-ai/ui/collapsible"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
@@ -16,8 +16,9 @@ import { type Session } from "@opencode-ai/sdk/v2/client"
import { type LocalProject } from "@/context/layout" import { type LocalProject } from "@/context/layout"
import { loadSessionsQuery, useGlobalSync } from "@/context/global-sync" import { loadSessionsQuery, useGlobalSync } from "@/context/global-sync"
import { useLanguage } from "@/context/language" import { useLanguage } from "@/context/language"
import { pathKey } from "@/utils/path-key"
import { NewSessionItem, SessionItem, SessionSkeleton } from "./sidebar-items" import { NewSessionItem, SessionItem, SessionSkeleton } from "./sidebar-items"
import { sortedRootSessions, workspaceKey } from "./helpers" import { sortedRootSessions } from "./helpers"
import { useQuery } from "@tanstack/solid-query" import { useQuery } from "@tanstack/solid-query"
type InlineEditorComponent = (props: { type InlineEditorComponent = (props: {
@@ -309,7 +310,7 @@ export const SortableWorkspace = (props: {
const slug = createMemo(() => base64Encode(props.directory)) const slug = createMemo(() => base64Encode(props.directory))
const sessions = createMemo(() => sortedRootSessions(workspaceStore, props.sortNow())) const sessions = createMemo(() => sortedRootSessions(workspaceStore, props.sortNow()))
const local = createMemo(() => props.directory === props.project.worktree) const local = createMemo(() => props.directory === props.project.worktree)
const active = createMemo(() => workspaceKey(props.ctx.currentDir()) === workspaceKey(props.directory)) const active = createMemo(() => pathKey(props.ctx.currentDir()) === pathKey(props.directory))
const workspaceValue = createMemo(() => { const workspaceValue = createMemo(() => {
const branch = workspaceStore.vcs?.branch const branch = workspaceStore.vcs?.branch
const name = branch ?? getFilename(props.directory) const name = branch ?? getFilename(props.directory)
@@ -321,7 +322,7 @@ export const SortableWorkspace = (props: {
const hasMore = createMemo(() => workspaceStore.sessionTotal > count()) const hasMore = createMemo(() => workspaceStore.sessionTotal > count())
const query = useQuery(() => ({ ...loadSessionsQuery(props.project.worktree) })) const query = useQuery(() => ({ ...loadSessionsQuery(props.project.worktree) }))
const busy = createMemo(() => props.ctx.isBusy(props.directory)) const busy = createMemo(() => props.ctx.isBusy(props.directory))
const loading = () => query.isLoading const loading = () => query.isLoading && count() === 0
const touch = createMediaQuery("(hover: none)") const touch = createMediaQuery("(hover: none)")
const showNew = createMemo(() => !loading() && (touch() || count() === 0 || (active() && !params.id))) const showNew = createMemo(() => !loading() && (touch() || count() === 0 || (active() && !params.id)))
const loadMore = async () => { const loadMore = async () => {
+1 -1
View File
@@ -28,7 +28,7 @@ import { createAutoScroll } from "@opencode-ai/ui/hooks"
import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge" import { previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
import { Button } from "@opencode-ai/ui/button" import { Button } from "@opencode-ai/ui/button"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { useSearchParams } from "@solidjs/router" import { useSearchParams } from "@solidjs/router"
import { NewSessionView, SessionHeader } from "@/components/session" import { NewSessionView, SessionHeader } from "@/components/session"
import { useComments } from "@/context/comments" import { useComments } from "@/context/comments"
+1 -1
View File
@@ -6,7 +6,7 @@ import type { FileSearchHandle } from "@opencode-ai/ui/file"
import { useFileComponent } from "@opencode-ai/ui/context/file" import { useFileComponent } from "@opencode-ai/ui/context/file"
import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge" import { cloneSelectedLineRange, previewSelectedLines } from "@opencode-ai/ui/pierre/selection-bridge"
import { createLineCommentController } from "@opencode-ai/ui/line-comment-annotations" import { createLineCommentController } from "@opencode-ai/ui/line-comment-annotations"
import { sampledChecksum } from "@opencode-ai/shared/util/encode" import { sampledChecksum } from "@opencode-ai/core/util/encode"
import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu" import { DropdownMenu } from "@opencode-ai/ui/dropdown-menu"
import { IconButton } from "@opencode-ai/ui/icon-button" import { IconButton } from "@opencode-ai/ui/icon-button"
import { Tabs } from "@opencode-ai/ui/tabs" import { Tabs } from "@opencode-ai/ui/tabs"
@@ -15,8 +15,8 @@ import { ScrollView } from "@opencode-ai/ui/scroll-view"
import { TextField } from "@opencode-ai/ui/text-field" import { TextField } from "@opencode-ai/ui/text-field"
import type { AssistantMessage, Message as MessageType, Part, TextPart, UserMessage } from "@opencode-ai/sdk/v2" import type { AssistantMessage, Message as MessageType, Part, TextPart, UserMessage } from "@opencode-ai/sdk/v2"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { Binary } from "@opencode-ai/shared/util/binary" import { Binary } from "@opencode-ai/core/util/binary"
import { getFilename } from "@opencode-ai/shared/util/path" import { getFilename } from "@opencode-ai/core/util/path"
import { Popover as KobaltePopover } from "@kobalte/core/popover" import { Popover as KobaltePopover } from "@kobalte/core/popover"
import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture" import { shouldMarkBoundaryGesture, normalizeWheelDelta } from "@/pages/session/message-gesture"
import { SessionContextUsage } from "@/components/session-context-usage" import { SessionContextUsage } from "@/components/session-context-usage"
@@ -259,7 +259,7 @@ export function MessageTimeline(props: {
if (!id) return idle if (!id) return idle
return sync.data.session_status[id] ?? idle return sync.data.session_status[id] ?? idle
}) })
const working = createMemo(() => !!pending() || sessionStatus().type !== "idle") const working = createMemo(() => sessionStatus().type !== "idle")
const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent)) const tint = createMemo(() => messageAgentColor(sessionMessages(), sync.data.agent))
const [timeoutDone, setTimeoutDone] = createSignal(true) const [timeoutDone, setTimeoutDone] = createSignal(true)
@@ -721,7 +721,7 @@ export function MessageTimeline(props: {
"md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered, "md:max-w-200 md:mx-auto 2xl:max-w-[1000px]": props.centered,
}} }}
> >
<Show when={workingStatus() !== "hidden"}> <Show when={workingStatus() !== "hidden" && settings.general.showSessionProgressBar()}>
<div <div
data-component="session-progress" data-component="session-progress"
data-state={workingStatus()} data-state={workingStatus()}
@@ -812,7 +812,7 @@ export function MessageTimeline(props: {
</Show> </Show>
</div> </div>
</div> </div>
<Show when={sessionID()}> <Show when={sessionID()} keyed>
{(id) => ( {(id) => (
<div class="shrink-0 flex items-center gap-3"> <div class="shrink-0 flex items-center gap-3">
<SessionContextUsage placement="bottom" /> <SessionContextUsage placement="bottom" />
@@ -878,12 +878,12 @@ export function MessageTimeline(props: {
</DropdownMenu.ItemLabel> </DropdownMenu.ItemLabel>
</DropdownMenu.Item> </DropdownMenu.Item>
</Show> </Show>
<DropdownMenu.Item onSelect={() => void archiveSession(id())}> <DropdownMenu.Item onSelect={() => void archiveSession(id)}>
<DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel> <DropdownMenu.ItemLabel>{language.t("common.archive")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item> </DropdownMenu.Item>
<DropdownMenu.Separator /> <DropdownMenu.Separator />
<DropdownMenu.Item <DropdownMenu.Item
onSelect={() => dialog.show(() => <DialogDeleteSession sessionID={id()} />)} onSelect={() => dialog.show(() => <DialogDeleteSession sessionID={id} />)}
> >
<DropdownMenu.ItemLabel>{language.t("common.delete")}</DropdownMenu.ItemLabel> <DropdownMenu.ItemLabel>{language.t("common.delete")}</DropdownMenu.ItemLabel>
</DropdownMenu.Item> </DropdownMenu.Item>
@@ -52,7 +52,12 @@ export function SessionSidePanel(props: {
const { sessionKey, tabs, view } = useSessionLayout() const { sessionKey, tabs, view } = useSessionLayout()
const isDesktop = createMediaQuery("(min-width: 768px)") const isDesktop = createMediaQuery("(min-width: 768px)")
const shown = createMemo(() => platform.platform !== "desktop" || settings.general.showFileTree()) const shown = createMemo(
() =>
platform.platform !== "desktop" ||
import.meta.env.VITE_OPENCODE_CHANNEL !== "beta" ||
settings.general.showFileTree(),
)
const reviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened()) const reviewOpen = createMemo(() => isDesktop() && view().reviewPanel.opened())
const fileOpen = createMemo(() => isDesktop() && shown() && layout.fileTree.opened()) const fileOpen = createMemo(() => isDesktop() && shown() && layout.fileTree.opened())
@@ -14,7 +14,7 @@ import { useSettings } from "@/context/settings"
import { useSync } from "@/context/sync" import { useSync } from "@/context/sync"
import { useTerminal } from "@/context/terminal" import { useTerminal } from "@/context/terminal"
import { showToast } from "@opencode-ai/ui/toast" import { showToast } from "@opencode-ai/ui/toast"
import { findLast } from "@opencode-ai/shared/util/array" import { findLast } from "@opencode-ai/core/util/array"
import { createSessionTabs } from "@/pages/session/helpers" import { createSessionTabs } from "@/pages/session/helpers"
import { extractPromptFromParts } from "@/utils/prompt" import { extractPromptFromParts } from "@/utils/prompt"
import { UserMessage } from "@opencode-ai/sdk/v2" import { UserMessage } from "@opencode-ai/sdk/v2"
@@ -70,7 +70,10 @@ export const useSessionCommands = (actions: SessionCommandContext) => {
}) })
const activeFileTab = tabState.activeFileTab const activeFileTab = tabState.activeFileTab
const closableTab = tabState.closableTab const closableTab = tabState.closableTab
const shown = () => platform.platform !== "desktop" || settings.general.showFileTree() const shown = () =>
platform.platform !== "desktop" ||
import.meta.env.VITE_OPENCODE_CHANNEL !== "beta" ||
settings.general.showFileTree()
const idle = { type: "idle" as const } const idle = { type: "idle" as const }
const status = () => sync.data.session_status[params.id ?? ""] ?? idle const status = () => sync.data.session_status[params.id ?? ""] ?? idle
+1 -1
View File
@@ -1,4 +1,4 @@
import { base64Decode } from "@opencode-ai/shared/util/encode" import { base64Decode } from "@opencode-ai/core/util/encode"
export function decode64(value: string | undefined) { export function decode64(value: string | undefined) {
if (value === undefined) return if (value === undefined) return
+24
View File
@@ -0,0 +1,24 @@
export type PathKey = string & { _brand: "PathKey" }
const isDrive = (value: string) => {
if (value.length !== 2) return false
const code = value.charCodeAt(0)
return value[1] === ":" && ((code >= 65 && code <= 90) || (code >= 97 && code <= 122))
}
const trimTrailingSlashes = (value: string) => {
for (let i = value.length - 1; i >= 0; i--) {
if (value[i] !== "/") return value.slice(0, i + 1)
}
return ""
}
const isWindowsPath = (value: string) => value[1] === ":" || value.startsWith("\\\\")
export const pathKey = (path: string) => {
const value = isWindowsPath(path) ? path.replaceAll("\\", "/") : path
const trimmed = trimTrailingSlashes(value)
if (!trimmed && value.startsWith("/")) return "/" as PathKey
if (isDrive(trimmed)) return `${trimmed}/` as PathKey
return trimmed as PathKey
}
+52
View File
@@ -1,6 +1,8 @@
import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test" import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test"
type PersistTestingType = typeof import("./persist").PersistTesting type PersistTestingType = typeof import("./persist").PersistTesting
type PersistType = typeof import("./persist").Persist
type RemovePersistedType = typeof import("./persist").removePersisted
class MemoryStorage implements Storage { class MemoryStorage implements Storage {
private values = new Map<string, string>() private values = new Map<string, string>()
@@ -45,6 +47,8 @@ class MemoryStorage implements Storage {
const storage = new MemoryStorage() const storage = new MemoryStorage()
let persistTesting: PersistTestingType let persistTesting: PersistTestingType
let Persist: PersistType
let removePersisted: RemovePersistedType
beforeAll(async () => { beforeAll(async () => {
mock.module("@/context/platform", () => ({ mock.module("@/context/platform", () => ({
@@ -53,6 +57,8 @@ beforeAll(async () => {
const mod = await import("./persist") const mod = await import("./persist")
persistTesting = mod.PersistTesting persistTesting = mod.PersistTesting
Persist = mod.Persist
removePersisted = mod.removePersisted
}) })
beforeEach(() => { beforeEach(() => {
@@ -112,4 +118,50 @@ describe("persist localStorage resilience", () => {
expect(result.endsWith(".dat")).toBeTrue() expect(result.endsWith(".dat")).toBeTrue()
expect(/[:\\/]/.test(result)).toBeFalse() expect(/[:\\/]/.test(result)).toBeFalse()
}) })
test("workspace target keeps raw path storage as legacy fallback", () => {
const target = Persist.workspace("C:\\Users\\foo", "vcs")
expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo"))
expect(target.legacyStorageNames).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")])
})
test("workspace target keeps backslash storage as fallback for normalized Windows paths", () => {
const target = Persist.workspace("C:/Users/foo", "vcs")
expect(target.storage).toBe(persistTesting.workspaceStorage("C:/Users/foo"))
expect(target.legacyStorageNames).toEqual([persistTesting.workspaceStorage("C:\\Users\\foo")])
})
test("migrates direct legacy keys into scoped storage", () => {
storage.setItem("legacy.workspace", '{"value":2}')
const target = Persist.workspace("C:/Users/foo", "demo", ["legacy.workspace"])
const current = persistTesting.localStorageWithPrefix(target.storage!)
const legacyStore = persistTesting.localStorageDirect()
const result = persistTesting.migrateLegacy({
current,
legacyStore,
stores: [],
keys: target.legacy!,
key: target.key,
defaults: { value: 1 },
})
expect(result).toBe('{"value":2}')
expect(storage.getItem(`${target.storage}:${target.key}`)).toBe('{"value":2}')
expect(legacyStore.getItem("legacy.workspace")).toBeNull()
expect(storage.getItem("legacy.workspace")).toBeNull()
})
test("removes legacy workspace storage when removing persisted target", () => {
const target = Persist.workspace("C:\\Users\\foo", "terminal")
storage.setItem(`${target.storage}:${target.key}`, '{"value":1}')
storage.setItem(`${target.legacyStorageNames![0]}:${target.key}`, '{"value":2}')
removePersisted(target)
expect(storage.getItem(`${target.storage}:${target.key}`)).toBeNull()
expect(storage.getItem(`${target.legacyStorageNames![0]}:${target.key}`)).toBeNull()
})
}) })
+194 -59
View File
@@ -1,8 +1,9 @@
import { Platform, usePlatform } from "@/context/platform" import { Platform, usePlatform } from "@/context/platform"
import { makePersisted, type AsyncStorage, type SyncStorage } from "@solid-primitives/storage" import { makePersisted, type AsyncStorage, type SyncStorage } from "@solid-primitives/storage"
import { checksum } from "@opencode-ai/shared/util/encode" import { checksum } from "@opencode-ai/core/util/encode"
import { createResource, type Accessor } from "solid-js" import { createResource, type Accessor } from "solid-js"
import type { SetStoreFunction, Store } from "solid-js/store" import type { SetStoreFunction, Store } from "solid-js/store"
import { pathKey } from "@/utils/path-key"
type InitType = Promise<string> | string | null type InitType = Promise<string> | string | null
type PersistedWithReady<T> = [ type PersistedWithReady<T> = [
@@ -14,6 +15,7 @@ type PersistedWithReady<T> = [
type PersistTarget = { type PersistTarget = {
storage?: string storage?: string
legacyStorageNames?: string[]
key: string key: string
legacy?: string[] legacy?: string[]
migrate?: (value: unknown) => unknown migrate?: (value: unknown) => unknown
@@ -208,12 +210,153 @@ function normalize(defaults: unknown, raw: string, migrate?: (value: unknown) =>
return JSON.stringify(merged) return JSON.stringify(merged)
} }
function readCurrent(input: {
storage: SyncStorage
key: string
defaults: unknown
migrate?: (value: unknown) => unknown
}) {
const raw = input.storage.getItem(input.key)
if (raw === null) return
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
input.storage.removeItem(input.key)
return null
}
if (raw !== next) input.storage.setItem(input.key, next)
return next
}
function migrateLegacy(input: {
current: SyncStorage
legacyStore?: SyncStorage
stores: SyncStorage[]
keys: string[]
key: string
defaults: unknown
migrate?: (value: unknown) => unknown
}) {
for (const store of input.stores) {
const raw = store.getItem(input.key)
if (raw === null) continue
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
store.removeItem(input.key)
continue
}
input.current.setItem(input.key, next)
store.removeItem(input.key)
return next
}
if (!input.legacyStore) return null
for (const key of input.keys) {
const raw = input.legacyStore.getItem(key)
if (raw === null) continue
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
input.legacyStore.removeItem(key)
continue
}
input.current.setItem(input.key, next)
input.legacyStore.removeItem(key)
return next
}
return null
}
async function readCurrentAsync(input: {
storage: AsyncStorage
key: string
defaults: unknown
migrate?: (value: unknown) => unknown
}) {
const raw = await input.storage.getItem(input.key)
if (raw === null) return
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
await input.storage.removeItem(input.key).catch(() => undefined)
return null
}
if (raw !== next) await input.storage.setItem(input.key, next)
return next
}
async function removeAsync(storage: AsyncStorage, key: string) {
try {
await storage.removeItem(key)
} catch {}
}
async function migrateLegacyAsync(input: {
current: AsyncStorage
legacyStore?: AsyncStorage
stores: AsyncStorage[]
keys: string[]
key: string
defaults: unknown
migrate?: (value: unknown) => unknown
}) {
for (const store of input.stores) {
const raw = await store.getItem(input.key)
if (raw === null) continue
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
await removeAsync(store, input.key)
continue
}
await input.current.setItem(input.key, next)
await store.removeItem(input.key)
return next
}
if (!input.legacyStore) return null
for (const key of input.keys) {
const raw = await input.legacyStore.getItem(key)
if (raw === null) continue
const next = normalize(input.defaults, raw, input.migrate)
if (next === undefined) {
await removeAsync(input.legacyStore, key)
continue
}
await input.current.setItem(input.key, next)
await input.legacyStore.removeItem(key)
return next
}
return null
}
function workspaceStorage(dir: string) { function workspaceStorage(dir: string) {
const head = (dir.slice(0, 12) || "workspace").replace(/[^a-zA-Z0-9._-]/g, "-") const head = (dir.slice(0, 12) || "workspace").replace(/[^a-zA-Z0-9._-]/g, "-")
const sum = checksum(dir) ?? "0" const sum = checksum(dir) ?? "0"
return `opencode.workspace.${head}.${sum}.dat` return `opencode.workspace.${head}.${sum}.dat`
} }
function legacyWorkspaceStorage(dir: string) {
const storage = workspaceStorage(pathKey(dir))
const result = new Set<string>()
const raw = workspaceStorage(dir)
if (raw !== storage) result.add(raw)
const key = pathKey(dir)
const drive = key.length >= 3 && key[1] === ":" && key[2] === "/"
if (drive) {
const backslash = workspaceStorage(key.replaceAll("/", "\\"))
if (backslash !== storage) result.add(backslash)
}
if (result.size === 0) return
return [...result]
}
function localStorageWithPrefix(prefix: string): SyncStorage { function localStorageWithPrefix(prefix: string): SyncStorage {
const base = `${prefix}:` const base = `${prefix}:`
const scope = `prefix:${prefix}` const scope = `prefix:${prefix}`
@@ -304,6 +447,7 @@ function localStorageDirect(): SyncStorage {
export const PersistTesting = { export const PersistTesting = {
localStorageDirect, localStorageDirect,
localStorageWithPrefix, localStorageWithPrefix,
migrateLegacy,
normalize, normalize,
workspaceStorage, workspaceStorage,
} }
@@ -313,10 +457,17 @@ export const Persist = {
return { storage: GLOBAL_STORAGE, key, legacy } return { storage: GLOBAL_STORAGE, key, legacy }
}, },
workspace(dir: string, key: string, legacy?: string[]): PersistTarget { workspace(dir: string, key: string, legacy?: string[]): PersistTarget {
return { storage: workspaceStorage(dir), key: `workspace:${key}`, legacy } const storage = workspaceStorage(pathKey(dir))
return { storage, legacyStorageNames: legacyWorkspaceStorage(dir), key: `workspace:${key}`, legacy }
}, },
session(dir: string, session: string, key: string, legacy?: string[]): PersistTarget { session(dir: string, session: string, key: string, legacy?: string[]): PersistTarget {
return { storage: workspaceStorage(dir), key: `session:${session}:${key}`, legacy } const storage = workspaceStorage(pathKey(dir))
return {
storage,
legacyStorageNames: legacyWorkspaceStorage(dir),
key: `session:${session}:${key}`,
legacy,
}
}, },
scoped(dir: string, session: string | undefined, key: string, legacy?: string[]): PersistTarget { scoped(dir: string, session: string | undefined, key: string, legacy?: string[]): PersistTarget {
if (session) return Persist.session(dir, session, key, legacy) if (session) return Persist.session(dir, session, key, legacy)
@@ -324,11 +475,18 @@ export const Persist = {
}, },
} }
export function removePersisted(target: { storage?: string; key: string }, platform?: Platform) { export function removePersisted(
target: { storage?: string; legacyStorageNames?: string[]; key: string },
platform?: Platform,
) {
const isDesktop = platform?.platform === "desktop" && !!platform.storage const isDesktop = platform?.platform === "desktop" && !!platform.storage
if (isDesktop) { if (isDesktop) {
return platform.storage?.(target.storage)?.removeItem(target.key) void platform.storage?.(target.storage)?.removeItem(target.key)
for (const storage of target.legacyStorageNames ?? []) {
void platform.storage?.(storage)?.removeItem(target.key)
}
return
} }
if (!target.storage) { if (!target.storage) {
@@ -337,6 +495,9 @@ export function removePersisted(target: { storage?: string; key: string }, platf
} }
localStorageWithPrefix(target.storage).removeItem(target.key) localStorageWithPrefix(target.storage).removeItem(target.key)
for (const storage of target.legacyStorageNames ?? []) {
localStorageWithPrefix(storage).removeItem(target.key)
}
} }
export function persisted<T>( export function persisted<T>(
@@ -363,39 +524,27 @@ export function persisted<T>(
return platform.storage?.(LEGACY_STORAGE) return platform.storage?.(LEGACY_STORAGE)
})() })()
const legacyStorageNames = config.legacyStorageNames ?? []
const storage = (() => { const storage = (() => {
if (!isDesktop) { if (!isDesktop) {
const current = currentStorage as SyncStorage const current = currentStorage as SyncStorage
const legacyStore = legacyStorage as SyncStorage const legacyStore = legacyStorage as SyncStorage
const legacyStores = legacyStorageNames.map(localStorageWithPrefix)
const api: SyncStorage = { const api: SyncStorage = {
getItem: (key) => { getItem: (key) => {
const raw = current.getItem(key) const value = readCurrent({ storage: current, key, defaults, migrate: config.migrate })
if (raw !== null) { if (value !== undefined) return value
const next = normalize(defaults, raw, config.migrate) return migrateLegacy({
if (next === undefined) { current,
current.removeItem(key) legacyStore,
return null stores: legacyStores,
} keys: legacy,
if (raw !== next) current.setItem(key, next) key,
return next defaults,
} migrate: config.migrate,
})
for (const legacyKey of legacy) {
const legacyRaw = legacyStore.getItem(legacyKey)
if (legacyRaw === null) continue
const next = normalize(defaults, legacyRaw, config.migrate)
if (next === undefined) {
legacyStore.removeItem(legacyKey)
continue
}
current.setItem(key, next)
legacyStore.removeItem(legacyKey)
return next
}
return null
}, },
setItem: (key, value) => { setItem: (key, value) => {
current.setItem(key, value) current.setItem(key, value)
@@ -410,37 +559,23 @@ export function persisted<T>(
const current = currentStorage as AsyncStorage const current = currentStorage as AsyncStorage
const legacyStore = legacyStorage as AsyncStorage | undefined const legacyStore = legacyStorage as AsyncStorage | undefined
const legacyStores = legacyStorageNames
.map((name) => platform.storage?.(name) as AsyncStorage | undefined)
.filter((x) => !!x)
const api: AsyncStorage = { const api: AsyncStorage = {
getItem: async (key) => { getItem: async (key) => {
const raw = await current.getItem(key) const value = await readCurrentAsync({ storage: current, key, defaults, migrate: config.migrate })
if (raw !== null) { if (value !== undefined) return value
const next = normalize(defaults, raw, config.migrate) return migrateLegacyAsync({
if (next === undefined) { current,
await current.removeItem(key).catch(() => undefined) legacyStore,
return null stores: legacyStores,
} keys: legacy,
if (raw !== next) await current.setItem(key, next) key,
return next defaults,
} migrate: config.migrate,
})
if (!legacyStore) return null
for (const legacyKey of legacy) {
const legacyRaw = await legacyStore.getItem(legacyKey)
if (legacyRaw === null) continue
const next = normalize(defaults, legacyRaw, config.migrate)
if (next === undefined) {
await legacyStore.removeItem(legacyKey).catch(() => undefined)
continue
}
await current.setItem(key, next)
await legacyStore.removeItem(legacyKey)
return next
}
return null
}, },
setItem: async (key, value) => { setItem: async (key, value) => {
await current.setItem(key, value) await current.setItem(key, value)
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@opencode-ai/console-app", "name": "@opencode-ai/console-app",
"version": "1.4.8", "version": "1.14.30",
"type": "module", "type": "module",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@@ -204,6 +204,14 @@ export function IconGemini(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
) )
} }
export function IconDeepSeek(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
<path d="M23.748 4.482c-.254-.124-.364.113-.512.234-.051.039-.094.09-.137.136-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.249-1.247-1.548-.352-.156-.708-.311-.955-.65-.172-.241-.219-.51-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.093.172.187.129.323-.082.28-.18.552-.266.833-.055.179-.137.217-.329.14a5.526 5.526 0 0 1-1.736-1.18c-.857-.828-1.631-1.742-2.597-2.458a11.365 11.365 0 0 0-.689-.471c-.985-.957.13-1.743.388-1.836.27-.098.093-.432-.779-.428-.872.004-1.67.295-2.687.684a3.055 3.055 0 0 1-.465.137 9.597 9.597 0 0 0-2.883-.102c-1.885.21-3.39 1.102-4.497 2.623C.082 8.606-.231 10.684.152 12.85c.403 2.284 1.569 4.175 3.36 5.653 1.858 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.133-.284 4.994-1.86.47.234.962.327 1.78.397.63.059 1.236-.03 1.705-.128.735-.156.684-.837.419-.961-2.155-1.004-1.682-.595-2.113-.926 1.096-1.296 2.746-2.642 3.392-7.003.05-.347.007-.565 0-.845-.004-.17.035-.237.23-.256a4.173 4.173 0 0 0 1.545-.475c1.396-.763 1.96-2.015 2.093-3.517.02-.23-.004-.467-.247-.588zM11.581 18c-2.089-1.642-3.102-2.183-3.52-2.16-.392.024-.321.471-.235.763.09.288.207.486.371.739.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.167-1.361-.802-2.5-1.86-3.301-3.307-.774-1.393-1.224-2.887-1.298-4.482-.02-.386.093-.522.477-.592a4.696 4.696 0 0 1 1.529-.039c2.132.312 3.946 1.265 5.468 2.774.868.86 1.525 1.887 2.202 2.891.72 1.066 1.494 2.082 2.48 2.914.348.292.625.514.891.677-.802.09-2.14.11-3.054-.614zm1-6.44a.306.306 0 0 1 .415-.287.302.302 0 0 1 .2.288.306.306 0 0 1-.31.307.303.303 0 0 1-.304-.308zm3.11 1.596c-.2.081-.399.151-.59.16a1.245 1.245 0 0 1-.798-.254c-.274-.23-.47-.358-.552-.758a1.73 1.73 0 0 1 .016-.588c.07-.327-.008-.537-.239-.727-.187-.156-.426-.199-.688-.199a.559.559 0 0 1-.254-.078c-.11-.054-.2-.19-.114-.358.028-.054.16-.186.192-.21.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.391.451.462.576.685.914.176.265.336.537.445.848.067.195-.019.354-.25.452z" />
</svg>
)
}
export function IconMiMo(props: JSX.SvgSVGAttributes<SVGSVGElement>) { export function IconMiMo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return ( return (
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"> <svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
+2 -2
View File
@@ -9,8 +9,8 @@ export const config = {
github: { github: {
repoUrl: "https://github.com/anomalyco/opencode", repoUrl: "https://github.com/anomalyco/opencode",
starsFormatted: { starsFormatted: {
compact: "140K", compact: "150K",
full: "140,000", full: "150,000",
}, },
}, },
+14 -5
View File
@@ -11,7 +11,7 @@ export const dict = {
"nav.enterprise": "المؤسسات", "nav.enterprise": "المؤسسات",
"nav.zen": "Zen", "nav.zen": "Zen",
"nav.login": "تسجيل الدخول", "nav.login": "تسجيل الدخول",
"nav.free": "مجانا", "nav.free": "تحميل",
"nav.home": "الرئيسية", "nav.home": "الرئيسية",
"nav.openMenu": "فتح القائمة", "nav.openMenu": "فتح القائمة",
"nav.getStartedFree": "ابدأ مجانا", "nav.getStartedFree": "ابدأ مجانا",
@@ -249,7 +249,7 @@ export const dict = {
"go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع", "go.title": "OpenCode Go | نماذج برمجة منخفضة التكلفة للجميع",
"go.meta.description": "go.meta.description":
"يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 و Kimi K2.5 وMiMo-V2-Pro وMiMo-V2-Omni و Qwen3.5 Plus و Qwen3.6 Plus و MiniMax M2.5 وMiniMax M2.7.", "يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود طلب سخية لمدة 5 ساعات لـ GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع", "go.hero.title": "نماذج برمجة منخفضة التكلفة للجميع",
"go.hero.body": "go.hero.body":
"يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.", "يجلب Go البرمجة الوكيلة للمبرمجين حول العالم. يوفر حدودًا سخية ووصولًا موثوقًا إلى أقوى النماذج مفتوحة المصدر، حتى تتمكن من البناء باستخدام وكلاء أقوياء دون القلق بشأن التكلفة أو التوفر.",
@@ -261,6 +261,8 @@ export const dict = {
"go.cta.promo": "$5 للشهر الأول", "go.cta.promo": "$5 للشهر الأول",
"go.pricing.body": "go.pricing.body":
"استخدمه مع أي وكيل. $5 للشهر الأول، ثم $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.", "استخدمه مع أي وكيل. $5 للشهر الأول، ثم $10/شهر. قم بزيادة الرصيد إذا لزم الأمر. الإلغاء في أي وقت.",
"go.banner.badge": "3x",
"go.banner.text": "Kimi K2.6: حد الاستخدام 3 أضعاف حتى 27 أبريل",
"go.graph.free": "مجاني", "go.graph.free": "مجاني",
"go.graph.freePill": "Big Pickle ونماذج مجانية", "go.graph.freePill": "Big Pickle ونماذج مجانية",
"go.graph.go": "Go", "go.graph.go": "Go",
@@ -298,7 +300,7 @@ export const dict = {
"go.problem.item2": "حدود سخية ووصول موثوق", "go.problem.item2": "حدود سخية ووصول موثوق",
"go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين", "go.problem.item3": "مصمم لأكبر عدد ممكن من المبرمجين",
"go.problem.item4": "go.problem.item4":
"يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وMiMo-V2-Pro وMiMo-V2-Omni وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7", "يتضمن GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash",
"go.how.title": "كيف يعمل Go", "go.how.title": "كيف يعمل Go",
"go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.", "go.how.body": "يبدأ Go من $5 للشهر الأول، ثم $10/شهر. يمكنك استخدامه مع OpenCode أو أي وكيل.",
"go.how.step1.title": "أنشئ حسابًا", "go.how.step1.title": "أنشئ حسابًا",
@@ -322,7 +324,7 @@ export const dict = {
"go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.", "go.faq.a2": "يتضمن Go النماذج المدرجة أدناه، مع حدود سخية وإتاحة موثوقة.",
"go.faq.q3": "هل Go هو نفسه Zen؟", "go.faq.q3": "هل Go هو نفسه Zen؟",
"go.faq.a3": "go.faq.a3":
"لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 و Kimi K2.5 وMiMo-V2-Pro وMiMo-V2-Omni و Qwen3.5 Plus و Qwen3.6 Plus و MiniMax M2.5 وMiniMax M2.7.", "لا. Zen هو الدفع حسب الاستخدام، بينما يبدأ Go من $5 للشهر الأول، ثم $10/شهر، مع حدود سخية ووصول موثوق إلى نماذج المصدر المفتوح GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash.",
"go.faq.q4": "كم تكلفة Go؟", "go.faq.q4": "كم تكلفة Go؟",
"go.faq.a4.p1.beforePricing": "تكلفة Go", "go.faq.a4.p1.beforePricing": "تكلفة Go",
"go.faq.a4.p1.pricingLink": "$5 للشهر الأول", "go.faq.a4.p1.pricingLink": "$5 للشهر الأول",
@@ -345,7 +347,7 @@ export const dict = {
"go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟", "go.faq.q9": "ما الفرق بين النماذج المجانية وGo؟",
"go.faq.a9": "go.faq.a9":
"تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وMiMo-V2-Pro وMiMo-V2-Omni وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).", "تشمل النماذج المجانية Big Pickle بالإضافة إلى النماذج الترويجية المتاحة في ذلك الوقت، مع حصة 200 طلب/يوم. يتضمن Go نماذج GLM-5.1 وGLM-5 وKimi K2.5 وKimi K2.6 وMiMo-V2-Pro وMiMo-V2-Omni وMiMo-V2.5-Pro وMiMo-V2.5 وQwen3.5 Plus وQwen3.6 Plus وMiniMax M2.5 وMiniMax M2.7 وDeepSeek V4 Pro وDeepSeek V4 Flash مع حصص طلبات أعلى مطبقة عبر نوافذ متجددة (5 ساعات، أسبوعيًا، وشهريًا)، تعادل تقريبًا 12 دولارًا كل 5 ساعات، و30 دولارًا في الأسبوع، و60 دولارًا في الشهر (تختلف أعداد الطلبات الفعلية حسب النموذج والاستخدام).",
"zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.", "zen.api.error.rateLimitExceeded": "تم تجاوز حد الطلبات. يرجى المحاولة مرة أخرى لاحقًا.",
"zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم", "zen.api.error.modelNotSupported": "النموذج {{model}} غير مدعوم",
@@ -558,6 +560,13 @@ export const dict = {
"workspace.monthlyLimit.currentUsage.beforeMonth": "الاستخدام الحالي لـ", "workspace.monthlyLimit.currentUsage.beforeMonth": "الاستخدام الحالي لـ",
"workspace.monthlyLimit.currentUsage.beforeAmount": "هو $", "workspace.monthlyLimit.currentUsage.beforeAmount": "هو $",
"workspace.redeem.title": "استرداد قسيمة",
"workspace.redeem.subtitle": "استرد رمز القسيمة للحصول على رصيد أو مزايا.",
"workspace.redeem.placeholder": "أدخل رمز القسيمة",
"workspace.redeem.redeem": "استرداد",
"workspace.redeem.redeeming": "جارٍ الاسترداد...",
"workspace.redeem.success": "تم استرداد القسيمة بنجاح.",
"workspace.reload.title": "إعادة الشحن التلقائي", "workspace.reload.title": "إعادة الشحن التلقائي",
"workspace.reload.disabled.before": "إعادة الشحن التلقائي", "workspace.reload.disabled.before": "إعادة الشحن التلقائي",
"workspace.reload.disabled.state": "معطّل", "workspace.reload.disabled.state": "معطّل",
+14 -5
View File
@@ -11,7 +11,7 @@ export const dict = {
"nav.enterprise": "Enterprise", "nav.enterprise": "Enterprise",
"nav.zen": "Zen", "nav.zen": "Zen",
"nav.login": "Entrar", "nav.login": "Entrar",
"nav.free": "Grátis", "nav.free": "Download",
"nav.home": "Início", "nav.home": "Início",
"nav.openMenu": "Abrir menu", "nav.openMenu": "Abrir menu",
"nav.getStartedFree": "Começar grátis", "nav.getStartedFree": "Começar grátis",
@@ -253,7 +253,7 @@ export const dict = {
"go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos", "go.title": "OpenCode Go | Modelos de codificação de baixo custo para todos",
"go.meta.description": "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, MiMo-V2-Pro, MiMo-V2-Omni, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.", "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-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"go.hero.title": "Modelos de codificação de baixo custo para todos", "go.hero.title": "Modelos de codificação de baixo custo para todos",
"go.hero.body": "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.", "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.",
@@ -265,6 +265,8 @@ export const dict = {
"go.cta.promo": "$5 no primeiro mês", "go.cta.promo": "$5 no primeiro mês",
"go.pricing.body": "go.pricing.body":
"Use com qualquer agente. $5 no primeiro mês, depois $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.", "Use com qualquer agente. $5 no primeiro mês, depois $10/mês. Recarregue o crédito se necessário. Cancele a qualquer momento.",
"go.banner.badge": "3x",
"go.banner.text": "Kimi K2.6: limite de uso 3x maior até 27 de abril",
"go.graph.free": "Grátis", "go.graph.free": "Grátis",
"go.graph.freePill": "Big Pickle e modelos gratuitos", "go.graph.freePill": "Big Pickle e modelos gratuitos",
"go.graph.go": "Go", "go.graph.go": "Go",
@@ -303,7 +305,7 @@ export const dict = {
"go.problem.item2": "Limites generosos e acesso confiável", "go.problem.item2": "Limites generosos e acesso confiável",
"go.problem.item3": "Feito para o maior número possível de programadores", "go.problem.item3": "Feito para o maior número possível de programadores",
"go.problem.item4": "go.problem.item4":
"Inclui GLM-5.1, GLM-5, Kimi K2.5, MiMo-V2-Pro, MiMo-V2-Omni, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7", "Inclui GLM-5.1, GLM-5, Kimi K2.5, Kimi K2.6, MiMo-V2-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash",
"go.how.title": "Como o Go funciona", "go.how.title": "Como o Go funciona",
"go.how.body": "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.", "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 +331,7 @@ export const dict = {
"go.faq.a2": "O Go inclui os modelos listados abaixo, com limites generosos e acesso confiável.", "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.q3": "O Go é o mesmo que o Zen?",
"go.faq.a3": "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, MiMo-V2-Pro, MiMo-V2-Omni, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7.", "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-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5, MiniMax M2.7, DeepSeek V4 Pro e DeepSeek V4 Flash.",
"go.faq.q4": "Quanto custa o Go?", "go.faq.q4": "Quanto custa o Go?",
"go.faq.a4.p1.beforePricing": "O Go custa", "go.faq.a4.p1.beforePricing": "O Go custa",
"go.faq.a4.p1.pricingLink": "$5 no primeiro mês", "go.faq.a4.p1.pricingLink": "$5 no primeiro mês",
@@ -353,7 +355,7 @@ export const dict = {
"go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?", "go.faq.q9": "Qual a diferença entre os modelos gratuitos e o Go?",
"go.faq.a9": "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, MiMo-V2-Pro, MiMo-V2-Omni, Qwen3.5 Plus, Qwen3.6 Plus, MiniMax M2.5 e MiniMax M2.7 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-Pro, MiMo-V2-Omni, MiMo-V2.5-Pro, MiMo-V2.5, Qwen3.5 Plus, Qwen3.6 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).",
"zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.", "zen.api.error.rateLimitExceeded": "Limite de taxa excedido. Por favor, tente novamente mais tarde.",
"zen.api.error.modelNotSupported": "Modelo {{model}} não suportado", "zen.api.error.modelNotSupported": "Modelo {{model}} não suportado",
@@ -567,6 +569,13 @@ export const dict = {
"workspace.monthlyLimit.currentUsage.beforeMonth": "Uso atual para", "workspace.monthlyLimit.currentUsage.beforeMonth": "Uso atual para",
"workspace.monthlyLimit.currentUsage.beforeAmount": "é $", "workspace.monthlyLimit.currentUsage.beforeAmount": "é $",
"workspace.redeem.title": "Resgatar Cupom",
"workspace.redeem.subtitle": "Resgate um código de cupom para receber créditos ou vantagens.",
"workspace.redeem.placeholder": "Digite o código do cupom",
"workspace.redeem.redeem": "Resgatar",
"workspace.redeem.redeeming": "Resgatando...",
"workspace.redeem.success": "Cupom resgatado com sucesso.",
"workspace.reload.title": "Recarga Automática", "workspace.reload.title": "Recarga Automática",
"workspace.reload.disabled.before": "A recarga automática está", "workspace.reload.disabled.before": "A recarga automática está",
"workspace.reload.disabled.state": "desativada", "workspace.reload.disabled.state": "desativada",

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