Compare commits

...

1067 Commits

Author SHA1 Message Date
opencode 567a1964c0 release: v0.5.27 2025-08-25 17:10:18 +00:00
adamdotdevin 564418f1ff fix(tui): too early 2025-08-25 12:04:49 -05:00
opencode d7c4faec58 release: v0.5.26 2025-08-25 16:54:15 +00:00
adamdotdevin 34982b5d18 fix(tui): wording 2025-08-25 16:38:25 +00:00
opencode 5b5bd146ea release: v0.5.25 2025-08-25 16:38:24 +00:00
adamdotdevin 836c2060c7 fix(tui): sort custom commands lower 2025-08-25 11:32:15 -05:00
adamdotdevin 6357136ca5 fix(tui): sort custom commands lower 2025-08-25 11:29:56 -05:00
adamdotdevin 0a0b363587 feat(tui): grok free 2025-08-25 11:27:58 -05:00
Jay V f5f6167146 docs: edit 2025-08-25 12:11:02 -04:00
adamdotdevin f1684c9e15 fix(tui): fix logo color 2025-08-25 10:08:52 -05:00
adamdotdevin f597b7287b chore: cleanup 2025-08-25 10:08:10 -05:00
GitHub Action 95fabc1407 ignore: update download stats 2025-08-25 2025-08-25 12:04:21 +00:00
Aiden Cline 315c366e11 docs: fix shell examples (#2236) 2025-08-24 23:53:39 -05:00
opencode 5d68a7c2e0 release: v0.5.24 2025-08-24 23:01:00 +00:00
Dax Raad 1b2d3bf659 ci: tweak 2025-08-24 18:55:44 -04:00
opencode 24e4f5b051 release: v0.5.23 2025-08-24 22:53:37 +00:00
Dax Raad 2992c5a6bf ci: retry clone 2025-08-24 18:48:03 -04:00
Dax Raad ca2660ccf8 ci: ignore 2025-08-24 18:31:44 -04:00
Frank 6b4bd590ac Add opencode workflow 2025-08-25 06:16:21 +08:00
Frank 60ba42af15 Add opencode workflow 2025-08-25 06:14:47 +08:00
Frank f22827bdfa Add opencode workflow 2025-08-25 03:39:11 +08:00
Frank f9b5b6d129 Add opencode workflow 2025-08-25 03:38:02 +08:00
Aiden Cline cc66e06101 fix: command model selection (#2219) 2025-08-24 12:06:48 -05:00
GitHub Action d4c8d95ec6 ignore: update download stats 2025-08-24 2025-08-24 12:03:59 +00:00
Aiden Cline 0fd312346b docs: fix plan agent docs (#2215) 2025-08-23 14:52:02 -05:00
OpeOginni b80046120c docs: document editor --wait flag (#2209)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-08-23 14:43:20 -05:00
Aiden Cline 07ed2a8391 docs: document out of box lsps (#2213) 2025-08-23 14:22:22 -05:00
opencode e9f52934e9 release: v0.5.18 2025-08-23 16:27:02 +00:00
Dax Raad 732b67f8ce ci: stuff 2025-08-23 12:21:58 -04:00
Dax Raad d47bb96784 ci: ignore 2025-08-23 12:10:08 -04:00
Johnny 6456350564 docs: fix nodejs installation commands (#2193) 2025-08-23 08:23:24 -05:00
GitHub Action c5c0a2ca6e ignore: update download stats 2025-08-23 2025-08-23 12:03:48 +00:00
Vasiliy Kulikov 3706b2bca7 feat(lsp): option to disable lsps installing automatically (#1997)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-08-22 22:39:19 -05:00
Aiden Cline 1f57b9a70f fix: count reasoning tokens (#2187) 2025-08-22 18:21:39 -05:00
Aiden Cline 004f53f741 ignore: update json schema for better lsp dx (#2186) 2025-08-22 17:59:18 -05:00
Jay V cf29ec0a59 docs: edit 2025-08-22 18:36:17 -04:00
Jay V b5e08acdf7 docs: update 2025-08-22 18:34:35 -04:00
Dax Raad 7ddeeeb4f8 ignore: typecheck 2025-08-22 18:31:51 -04:00
Dax Raad 0f1697b2ab add sse streaming to sdk 2025-08-22 18:30:25 -04:00
Lubos 6e626afdcb chore(openapi): set correct content type for server-sent events (#2045) 2025-08-22 17:51:24 -04:00
Dax Raad 0fe94c1616 docs: add file names to code block titles in commands.mdx 2025-08-22 17:23:59 -04:00
Dax Raad a42b004c72 docs: add commands page to sidebar 2025-08-22 17:23:59 -04:00
opencode 35f57768fd release: v0.5.15 2025-08-22 21:16:23 +00:00
Aiden Cline 9a90ce84fb fix: format error log (#2184) 2025-08-22 16:09:15 -05:00
Dax 133fe41cd5 slash commands (#2157)
Co-authored-by: adamdotdevin <2363879+adamdottv@users.noreply.github.com>
2025-08-22 17:04:28 -04:00
Jay V 74c1085103 docs: edit 2025-08-22 15:14:02 -04:00
Jay V 497fc170fd docs: edit 2025-08-22 13:54:56 -04:00
Aiden Cline 3edab60560 docs: remove fake model (#2175) 2025-08-22 11:55:11 -05:00
Dax 3f2ac2b9b0 Update duplicate-issues.yml 2025-08-22 08:52:39 -04:00
GitHub Action 1577b44087 ignore: update download stats 2025-08-22 2025-08-22 12:04:13 +00:00
Thai Nguyen Hung 39f52f48f2 fix: correct typo in LSP documentation (#2164) 2025-08-22 06:43:24 -05:00
Aiden Cline 4fadbcfb90 fix: error logging (#2165) 2025-08-21 23:27:49 -05:00
Dax Raad 08c5c401ba deal with non existing cache folder 2025-08-21 22:58:39 -04:00
Aiden Cline ba2e86c7ef tweak: adjust plan agent to ask when running bash, give it edit tooli… (#2150) 2025-08-21 18:25:31 -04:00
Dax Raad 6d056789c7 ignore: docs agent 2025-08-21 17:15:43 -04:00
Dax Raad 5d508cc9c2 docs: update SDK documentation 2025-08-21 17:15:21 -04:00
Dax Raad d9233872b9 add createOpencodeServer to js sdk and wait for readiness. always use random port for opencode serve. add /client and /server imports for js sdk 2025-08-21 17:13:24 -04:00
Aiden Cline aa4dba1541 fix: if lsp fails to spawn it shouldn't inject errors into edit diagnostics (#2145) 2025-08-21 12:06:32 -05:00
Dax Raad 947a3e8aff fix sdk config type 2025-08-21 13:00:16 -04:00
Dax Raad 9a3186317b allow importing sdk from @opencode-ai/sdk/server and @opencode-ai/sdk/client 2025-08-21 12:58:37 -04:00
zWing b1e584ca1d chore: add export types in js-sdk (#1923)
Co-authored-by: zwingzheng <zwingzheng@tencent.com>
2025-08-21 11:06:27 -05:00
zWing bca523eb63 fix(js-sdk): fix types in session.chat (#1925)
Co-authored-by: zwingzheng <zwingzheng@tencent.com>
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-08-21 10:44:20 -05:00
Denys Pavlov 2ff4cd2c2b fix: preserve cache dir on cleanup (#2126) 2025-08-21 15:27:25 +00:00
Dax Raad d686269377 await config hooks 2025-08-21 15:27:25 +00:00
opencode 491abd6b5b release: v0.5.13 2025-08-21 15:27:25 +00:00
Dax Raad 4518f96e3d add plugin hook for config 2025-08-21 11:22:24 -04:00
GitHub Action a9dcbedf99 ignore: update download stats 2025-08-21 2025-08-21 12:04:21 +00:00
Aiden Cline 9231043eb4 tweak: adjust plan -> build transition prompt (#2111) 2025-08-21 06:52:38 -05:00
Frank 04dcd87170 fix do migration 2025-08-21 18:02:32 +08:00
Frank c31fd9ed79 fix do migration 2025-08-21 17:52:55 +08:00
Jay V 2989d92794 docs: update 2025-08-20 17:58:56 -04:00
Jay V 256d074411 docs: gitlab 2025-08-20 17:51:16 -04:00
Jay V 8b01676ec0 docs: edit 2025-08-20 17:39:37 -04:00
Lee Tickett 34c6c8494a docs: Add GitLab CLI agent integration doc (#2103) 2025-08-20 17:37:43 -04:00
Dax Raad 522bed6b7d ignore: cloud stuff 2025-08-20 17:01:18 -04:00
Vincent Bernat dda672284c fix: ignore case when checking Qwen in model ID for todos (#2122) 2025-08-20 14:44:27 -05:00
Jay V 6018364164 docs: edit 2025-08-20 18:22:48 +00:00
opencode bc0d438cee release: v0.5.12 2025-08-20 18:22:48 +00:00
Jay V abef91c223 docs: edit server 2025-08-20 14:13:02 -04:00
Dax Raad 1bbf6d38e5 ci: turn back on aur 2025-08-20 12:46:17 -04:00
opencode c9c9db1e8d release: v0.5.11 2025-08-20 16:36:05 +00:00
Dax Raad b11fe9fbc6 ignore: remove import 2025-08-20 12:29:24 -04:00
Dax Raad 60f3d413de remove auto browser open for now 2025-08-20 12:28:00 -04:00
opencode 1df2d78b85 release: v0.5.10 2025-08-20 16:12:00 +00:00
opencode 2286a872c1 release: v0.5.9 2025-08-20 15:51:24 +00:00
Dax Raad 8a83301e0d copilot auth update version 2025-08-20 11:46:14 -04:00
GitHub Action 9bc40f00e3 ignore: update download stats 2025-08-20 2025-08-20 12:04:23 +00:00
opencode c3c440948a release: v0.5.8 2025-08-20 05:08:31 +00:00
Dax Raad aa10f8a7f6 sonic model 2025-08-20 01:02:41 -04:00
Aiden Cline a2db58f125 fix: don't let --continue access subagent session (#2091) 2025-08-19 22:40:07 -05:00
Aiden Cline 574be9febf fix: keybind panic (#2092) 2025-08-19 22:39:59 -05:00
Aiden Cline 5b05ede748 fix: agent casing issue (#2081) 2025-08-19 18:08:56 -05:00
Aiden Cline 4032426185 docs: remove non existent keybind (#2080) 2025-08-19 17:39:02 -05:00
Jay V 8d8045ff95 docs: add sdk doc 2025-08-19 18:11:36 -04:00
Jay V b3c8bec019 docs: edit server 2025-08-19 17:21:45 -04:00
Aiden Cline 25f43adaa0 tweak: notify agent it is in build mode when switching from plan mode (#2065) 2025-08-19 15:32:31 -05:00
Timo Clasen 4913ee6afd fix(TUI): make it less shimmer (#2076) 2025-08-19 15:30:54 -05:00
Zack Jackson c59ded82b3 docs: document server API endpoints (#2019)
Co-authored-by: Jay <air@live.ca>
2025-08-19 16:13:02 -04:00
Aiden Cline 40bdbf92a3 fix: tui panic from logger (#2075) 2025-08-19 14:47:44 -05:00
Aiden Cline ad76d7e57d fix: add type checking for MCP tool path parameters (#2073)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
2025-08-19 13:38:33 -05:00
GitHub Action 863ae6fa7d ignore: update download stats 2025-08-19 2025-08-19 12:04:15 +00:00
Aiden Cline 8f230ad4b4 fix: interface conversion panic (#2060) 2025-08-19 05:25:46 -05:00
Aiden Cline c0f90eb564 tweak: better agent create error handling (#2058) 2025-08-19 00:14:50 -05:00
Dax 50fb337270 Update duplicate-issues.yml 2025-08-18 20:00:59 -04:00
Aiden Cline e08ec077b0 fix: ensure name isn't added as field in options: {...} (#2053) 2025-08-18 18:15:20 -05:00
Aiden Cline 796245d146 blacklist gpt-5-chat-latest (#2048) 2025-08-18 17:50:38 -04:00
opencode 303a1044a8 release: v0.5.7 2025-08-18 21:43:17 +00:00
Dax f19586cebd fix anthropic console auth (#2049) 2025-08-18 17:12:21 -04:00
Jay V 5d12cadba7 docs:edit 2025-08-18 13:52:53 -04:00
Jay V 745988f9e3 docs:edit 2025-08-18 13:51:08 -04:00
Jay V 61580e6dce docs: edits 2025-08-18 13:31:01 -04:00
Jay V 2dea8f0f6b docs: add tui doc 2025-08-18 13:31:01 -04:00
opencode 446ce488c0 release: v0.5.6 2025-08-18 15:56:22 +00:00
John Connor 21b000aed0 Remove redundant line from agents.mdx (#2031) 2025-08-18 08:34:57 -05:00
GitHub Action 0cdd8be70a ignore: update download stats 2025-08-18 2025-08-18 12:04:33 +00:00
adamdotdevin 2f4db2777c fix(tui): title bg color missing on system theme 2025-08-18 06:00:38 -05:00
Ytzhak 667ff90dd6 feat: add shimmer text rendering (#2027) 2025-08-18 05:55:01 -05:00
spoons-and-mirrors cd3d91209a tweak(timeline): add a dot to the session timeline modal for better visual cue of session's revert point (#1978) 2025-08-18 05:50:43 -05:00
Frank 75ed131abf sync 2025-08-18 07:58:46 +00:00
Frank 2034fabc7d Squashed commit of the following:
publish-github-action / publish (push) Has been cancelled
commit 7b2ad6a1abf88e0731f15bbf6e281b29a610dd76
Merge: 74c85391 847a63e1
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 15:31:54 2025 +0800

    Merge branch 'dev' into github

commit 74c85391b576d01df298f6c30e3399b281b5c997
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 15:30:14 2025 +0800

    sync

commit 0d27f8e490f1aa242e1a3fcd1f21eb077f852207
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 14:30:57 2025 +0800

    sync

commit 0cf7e6c89f173b053f37cc0d316011b3e9d5fcc4
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 11:54:57 2025 +0800

    sync

commit a782cb7a268bf98916c3850083eaf44ebc38de05
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 11:53:25 2025 +0800

    sync

commit aa557014584abaf462656ba9b1de7c8bd6e9b9d8
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 11:48:10 2025 +0800

    sync

commit 73c8150479bd3c965087c634102df047a36b40ab
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 01:29:29 2025 +0800

    sync

commit c5325134e80ce3f9e2cb88e5a51893e4ffd880c2
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 01:07:48 2025 +0800

    sync

commit c5b646aa88760731ac9cd221f677bd400c31224b
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 01:02:02 2025 +0800

    sync

commit 27f7cc86ab4713a26d316ae71d2aa5978aaa2007
Author: Frank <frank@sst.dev>
Date:   Mon Aug 18 00:59:22 2025 +0800

    sync

commit 0a6152a0e0c2bb0e5b7cafbcb92b908433dd6c5b
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 18:11:31 2025 +0800

    fix /opencode trigger

commit f1089103c607ac11251cac5e032e62c8b4667b30
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 17:55:14 2025 +0800

    sync

commit 3ad18240248301380a68880315bfa83c18e9652d
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 17:44:11 2025 +0800

    sync

commit 24f0f81773762a38ba0a26e599b718495e2f4b54
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 17:18:22 2025 +0800

    sync

commit bc199d32bed9679d2f80ade527fa57a91e0883ca
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 16:59:03 2025 +0800

    sync

commit 6cf860be843e94401166a6de83e36d6bdd8ca6d7
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 16:54:48 2025 +0800

    sync

commit f5f753ff38498062b2e3de38a1be94158fce1463
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 14:43:12 2025 +0800

    sync

commit 26d2e23a3ee99141a5951a153e444a1be25548dc
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 14:33:40 2025 +0800

    sync

commit c5b3f54a0ae6064ff51c11ade41e21b594939715
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 14:16:10 2025 +0800

    sync

commit 1c74e9a7ad35551eea53d0e51dcd28e6ae30a944
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 08:17:53 2025 +0800

    sync

commit 89052dc9aaf7e4f02b7ca869ef6017322ee21c94
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 08:12:43 2025 +0800

    sync

commit 42931d4d2a942eedef44f5570a57bf84df26ecfa
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 08:08:37 2025 +0800

    sync

commit f22e97dd051ae3f592f4258a8d0270ca7fd60338
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 08:01:57 2025 +0800

    sync

commit 2dda422ef85d2308b459cebe7f202b7fb782e75e
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 07:55:38 2025 +0800

    sync

commit b8be1d0e9e89732bd60185c724cda72b8de5f145
Author: Frank <frank@sst.dev>
Date:   Sun Aug 17 07:48:18 2025 +0800

    sync

commit 78c84b96a3c8aa78e0ffa089a2a72ad80348fe72
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 20:49:26 2025 +0800

    sync

commit dd9c0c83090ea6c5da963303227a1e09a8434994
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 20:47:25 2025 +0800

    sync

commit 5eb917abba182712d1581376e95de45a092bbb24
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 20:35:48 2025 +0800

    sync

commit 43cf83e7ccbc99484602b06cbb6aafdbc63bf11c
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 20:32:49 2025 +0800

    sync

commit 10673ca3d2e1572e15c944ddd7d7af8175971f74
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 19:55:53 2025 +0800

    sync

commit c45ae8a233ed64c49a08b98f3ad01e0348b2df22
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 19:53:52 2025 +0800

    sync

commit 3c329dee05ecda95f5d249552aafc885997f07f2
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 19:49:56 2025 +0800

    sync

commit 5797048db864142f15d73c854131a77a31a421ee
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 18:00:04 2025 +0800

    sync

commit 2741338e8a27e57d9d023cf9c0a6a05276b82f41
Author: Frank <frank@sst.dev>
Date:   Sat Aug 16 17:54:42 2025 +0800

    sync

commit a51a8ca6d094bd5f98330c730d335285688c6ed8
Author: Frank <frank@sst.dev>
Date:   Fri Aug 15 18:59:29 2025 +0800

    sync

commit f4eeeb612dfa6f1714a954dd167519ade0c36a2d
Author: Frank <frank@sst.dev>
Date:   Fri Aug 15 18:56:35 2025 +0800

    sync

commit 1d0509c5630904a5a9e89ce0de09fbebb6f711be
Author: Frank <frank@sst.dev>
Date:   Fri Aug 15 18:54:21 2025 +0800

    sync

commit 339807d1b88d2439e9543b5da4ca2538a49f4ab8
Author: Frank <frank@sst.dev>
Date:   Fri Aug 15 18:49:22 2025 +0800

    sync

commit 70b4b78922fe80424d8922bb999ed84d28dff005
Author: Frank <frank@sst.dev>
Date:   Fri Aug 15 18:04:57 2025 +0800

    sync
2025-08-18 15:34:28 +08:00
Aiden Cline 847a63e15a fix: gh install trim remote origin (#2030) 2025-08-17 22:45:22 -05:00
Aiden Cline ebd1b18b70 fix: better binary file detection (#2025) 2025-08-17 17:59:51 -05:00
GitHub Action de1764841c ignore: update download stats 2025-08-17 2025-08-17 12:03:50 +00:00
Thierry Delafontaine 5d5ac168a4 fix(opencode): add ulid dependency (#1988) (#1989)
Co-authored-by: Dax <mail@thdxr.com>
2025-08-16 23:21:29 -04:00
Lucas 5d8d896fa2 feat(lsp): add rust-analyzer (#1972) 2025-08-16 22:59:51 -04:00
Aiden Cline 85c6301ac5 fix: lsp bug (#1994) 2025-08-16 22:59:18 -04:00
Aiden Cline 664d826642 fix: install script didnt use -e flag (#2009) 2025-08-16 22:55:49 -04:00
spoons-and-mirrors 1e204c23b9 tweak(config): make markdown agent files in subfolder discoverable .opencode/agent/some-folder/*.md (#1999) 2025-08-16 22:55:14 -04:00
Aiden Cline daea79c0d4 feat: top level tool config (#2008) 2025-08-16 22:51:56 -04:00
Aiden Cline 9c7fa35051 docs: more troubleshooting examples (#2004) 2025-08-16 19:33:49 -05:00
opencode 0b45187dc7 release: v0.5.5 2025-08-16 15:00:04 +00:00
Yihui Khuu 3f3da44ed9 fix(tui): text selection is sometimes not cleared when click+release without dragging (#1993) 2025-08-16 09:16:09 -05:00
Yihui Khuu b3885d1614 feat(tui): retain cache when cycling between subagent/parent sessions for perf (#1981) 2025-08-16 08:58:13 -05:00
Aiden Cline ca3769b7fa tweak: plan prompt, more explicit about not modifying files (#1991) 2025-08-16 08:56:43 -05:00
GitHub Action 99e740e692 ignore: update download stats 2025-08-16 2025-08-16 12:03:50 +00:00
Aiden Cline 576f5242bc fix: remove unsupported mode flag, change to agent (#1979) 2025-08-15 22:02:06 -05:00
Dax Raad f40feed190 wip: cloud 2025-08-15 19:50:46 -04:00
Dax Raad 6bbc4cca92 wip: fix CSS syntax issues in index.css 2025-08-15 19:42:50 -04:00
Dax Raad 10dfc7893a wip: replace hardcoded spacing and font values with design tokens 2025-08-15 19:39:34 -04:00
Dax Raad 4c783a362a wip: agent css 2025-08-15 19:34:45 -04:00
Dax Raad 3f9203f9fa wip: css agent 2025-08-15 19:32:32 -04:00
Dax Raad 07cf8847fb wip: cloud stuff 2025-08-15 19:29:42 -04:00
opencode 650e67f1df release: v0.5.4 2025-08-15 22:52:49 +00:00
Aiden Cline e545bfef1f tweak: fix scroll speed (#1974) 2025-08-15 16:19:58 -05:00
Timo Clasen af5f7d0887 fix: run command (#1971) 2025-08-15 15:58:20 -05:00
opencode 314f7c56e7 release: v0.5.3 2025-08-15 18:56:32 +00:00
adamdotdevin 58ca434c78 fix: some visual bugs in dialogs 2025-08-15 13:50:54 -05:00
Yihui Khuu 70f14cccd6 feat(shell): load .zshenv and respect ZDOTDIR if present (#1958) 2025-08-15 13:24:34 -05:00
Yihui Khuu 86df4073d1 fix(shell): commands expecting stdin will be "working" indefinitely (#1964) 2025-08-15 13:23:59 -05:00
spoons-and-mirrors 69117fa453 feat(TUI): improves UX with message navigation modal to jump and restore to specific messages (#1969) 2025-08-15 13:23:21 -05:00
Aiden Cline dc01071498 feat: add scroll speed to config (#1968) 2025-08-15 13:21:02 -05:00
opencode 57b04d9eb7 release: v0.5.2 2025-08-15 15:22:21 +00:00
adamdotdevin 07dbc30c63 feat(tui): navigate child sessions (subagents) 2025-08-15 10:16:08 -05:00
adamdotdevin 1ae38c90a3 feat(api): get session and session children routes 2025-08-15 08:49:19 -05:00
adamdotdevin 9609c1803e feat: /tui/show-toast api 2025-08-15 08:39:58 -05:00
adamdotdevin 6e0e87fb2a fix: more commands cleanup 2025-08-15 07:43:30 -05:00
GitHub Action c875d11959 ignore: update download stats 2025-08-15 2025-08-15 12:04:27 +00:00
adamdotdevin 08a83b7337 feat: better queued visual 2025-08-15 06:55:16 -05:00
adamdotdevin 79a4e35a74 fix: keybind docs out of sync 2025-08-15 06:45:20 -05:00
adamdotdevin 40ed73af17 chore: deprecate unused keybinds 2025-08-15 06:36:28 -05:00
adamdotdevin 74da6b1bef fix: add missing keybinds to config 2025-08-15 06:33:45 -05:00
Yihui Khuu c35e1a03d1 fix(tui): issue with rendering markdown tables (#1956) 2025-08-15 06:21:08 -05:00
Yihui Khuu 92d4366a20 feat(tui): support cycling recent models in reverse (#1953) 2025-08-15 06:20:07 -05:00
Andre van Tonder 17a7c824b8 Add Vue LSP and enable eslint for .vue files. (#1952) 2025-08-15 06:18:27 -05:00
Mariano Uvalle 0befc5d602 Feat: Render tool metadata after permission rejection. (#1949)
Signed-off-by: jmug <u.g.a.mariano@gmail.com>
2025-08-15 06:16:40 -05:00
Aiden Cline 8355ee2061 fix: more permissive owner/repo detection (#1948) 2025-08-15 06:11:41 -05:00
Aiden Cline 62fed8d2ce fix: fish shell (#1950) 2025-08-15 06:11:09 -05:00
Frank 6fbe28619c Docs: update z.ai provider doc 2025-08-15 15:52:00 +08:00
Timo Clasen 156cc6cffe fix(TUI): fix agent types agents modal (#1942) 2025-08-14 17:58:44 -05:00
Jay V bcd1dddcbe lander: match alternatives h3 font size with figcaption on mobile 2025-08-14 18:55:07 -04:00
Aiden Cline 6eaaaffcdd fix: small tweak to support nushell (#1943) 2025-08-14 17:54:22 -05:00
Jay V 766fa521ea ignore: lander 2025-08-14 17:19:14 -04:00
opencode ecafa40bcf release: v0.5.1 2025-08-14 20:47:45 +00:00
Dax Raad 25f4721c71 ci: aur is down 2025-08-14 16:42:39 -04:00
Dax Raad a433766a31 allow plugins to create custom auth providers 2025-08-14 16:25:08 -04:00
Jay V c93d50e8c7 ignore: lander tweaks 2025-08-14 15:53:41 -04:00
Jay V 3f879859d7 ignore: lander styles 2025-08-14 15:53:41 -04:00
Dax Raad ee62dc0745 wip: sdk 2025-08-14 12:22:27 -04:00
Dax Raad b7aefa715a ci: tweak 2025-08-14 12:20:22 -04:00
Dax Raad 796bc390db fix for session stuck in "Working..." 2025-08-14 12:20:22 -04:00
Lubos 703ae49675 chore: declare OpenAPI version 3.1.1 (#1931) 2025-08-14 12:10:32 -04:00
GitHub Action 37c0570a9f ignore: update download stats 2025-08-14 2025-08-14 12:04:42 +00:00
Aiden Cline 4dea0209bb fix: support fish shell (#1911) 2025-08-13 20:04:04 -05:00
Aiden Cline bb4b24a05f docs: fix bad example (#1913) 2025-08-13 20:03:44 -05:00
opencode e789abec79 release: v0.4.45 2025-08-13 22:32:26 +00:00
Aiden Cline 118617473e fix: bash should hide stdout from zshrc (#1909) 2025-08-13 17:04:32 -04:00
adamdotdevin a4beb60e19 chore: rename bash -> shell 2025-08-13 15:11:30 -05:00
Yuu Toriyama 3f0f910f7b Fix: Error [ERR_DLOPEN_FAILED] (#1546) 2025-08-13 19:49:14 +00:00
opencode 5bf841ab7a release: v0.4.44 2025-08-13 19:49:14 +00:00
Dax Raad 49727e3eab re-enable aur 2025-08-13 15:44:11 -04:00
Dax Raad 592c6ef97f ci: issue 2025-08-13 15:43:06 -04:00
envolution 00579f0ec1 Fix incorrect AUR namespace (#1907) 2025-08-13 15:37:15 -04:00
adamdotdevin 69d516c7fa fix: default scroll speed should be slower 2025-08-13 14:35:18 -05:00
Dax Raad bedeb626b2 docs: fix 2025-08-13 19:33:38 +00:00
Dominik Engelhardt a4c14dbb2d feat: convert attachments to text on delete (#1863)
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Dax <mail@thdxr.com>
2025-08-13 19:33:38 +00:00
opencode 036b24791d release: v0.4.43 2025-08-13 19:33:38 +00:00
Dax Raad 93b71477e6 support !shell commands 2025-08-13 15:26:13 -04:00
adamdotdevin 1357319f6f feat: bash commands 2025-08-13 13:28:22 -05:00
Dax Raad e729eed34d wip: bash 2025-08-13 14:14:27 -04:00
Jay V 2e5fdd8cef docs: global model options 2025-08-13 14:07:10 -04:00
Dax Raad 21f15f15c1 docs(cli): document ! bash commands and session persistence in CLI docs 2025-08-13 13:37:19 -04:00
Dax Raad c6344c5714 wip: bash 2025-08-13 13:31:29 -04:00
Dax Raad 7505fa61b9 wip: bash commands 2025-08-13 13:29:06 -04:00
Matt Cook 77bb5af092 fix: grammatical error in agent launch example (by Opencode) (#1897) 2025-08-13 12:25:38 -05:00
Aiden Cline 0c4fe73cbf fix: js plugin support as per documentation (#1896) 2025-08-13 12:25:04 -05:00
Aiden Cline e132f6183d fix: duplicates bot prompt (#1901) 2025-08-13 11:54:37 -05:00
opencode e06ebb6780 release: v0.4.42 2025-08-13 16:48:35 +00:00
adamdotdevin 66d99ba527 fix: messages layout instability 2025-08-13 11:43:28 -05:00
adamdotdevin f2021a85d6 fix: allow attachments outside cwd, and support svg 2025-08-13 10:36:50 -05:00
adamdotdevin 7d54f893c9 fix: update read tool description to exclude binary/image files 2025-08-13 10:13:57 -05:00
Mariano Uvalle e1f80c0067 Merge default agent permissions with global config (#1879) 2025-08-13 09:01:17 -04:00
GitHub Action 4ff13d3290 ignore: update download stats 2025-08-13 2025-08-13 12:04:41 +00:00
Aiden Cline 832d8da453 fix: permission prompting issues (#1884) 2025-08-13 06:34:06 -05:00
Aiden Cline b5d61b77f7 fix: reasoning not supported (#1882) 2025-08-13 06:26:07 -05:00
Aiden Cline 790e9947bd fix: task tool prompt (#1887) 2025-08-12 23:41:12 -04:00
Dax Raad 2056781cf7 ci: disable 2025-08-12 22:55:57 -04:00
Aiden Cline ed5f76d849 fix: better error message when config has invalid references (#1874) 2025-08-12 19:28:41 -05:00
opencode 93102dc84b release: v0.4.41 2025-08-13 00:05:51 +00:00
Dax Raad e2920ac262 update copilot prompt 2025-08-12 20:01:34 -04:00
Dax Raad aa5e39e744 fix unzip not found printing to tui 2025-08-12 18:43:24 -04:00
opencode 296cc41a07 release: v0.4.40 2025-08-12 21:51:19 +00:00
Dax Raad 482239b848 ci: sync 2025-08-12 17:47:04 -04:00
Dax Raad 17b07877e5 ci: disable AUR packaging in publish workflow 2025-08-12 17:44:39 -04:00
spoons-and-mirrors dedaa34dc1 fix(TUI): unsurfacing subagent from agents modal (#1873) 2025-08-12 17:38:35 -04:00
Dax Raad 5785ded6e2 add openai prompt cache key 2025-08-12 17:37:15 -04:00
Dax Raad d1876e3031 ci: enable aur 2025-08-12 17:29:10 -04:00
Tom Hackshaw 9fa3e0a0ec chore: spelling in git committer file (#1869) 2025-08-12 16:29:41 -04:00
spoons-and-mirrors 47c327641b feat: add session rename functionality to TUI modal (#1821)
Co-authored-by: opencode <noreply@opencode.ai>
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Dax <mail@thdxr.com>
2025-08-12 16:22:03 -04:00
spoons-and-mirrors 81583cddbd refactor(agent-modal): revamped UI/UX for the agent modal (#1838)
Co-authored-by: Dax Raad <d@ironbay.co>
Co-authored-by: Dax <mail@thdxr.com>
2025-08-12 16:21:57 -04:00
Dax Raad d16ae1fc4e bash truncate character max instead of line max 2025-08-12 16:14:40 -04:00
Dax Raad 14e00a06b6 ci: sync 2025-08-12 15:09:21 -04:00
Dax Raad 5cc44c872e disable todo tools for qwen models to improve compatibility 2025-08-12 18:56:26 +00:00
opencode cadc5982f1 release: v0.4.37 2025-08-12 18:56:25 +00:00
Dax Raad 6aa157cfe6 limit bash tool to 1000 lines of output 2025-08-12 14:51:13 -04:00
Dax Raad 9fff9a37d0 ci: sync 2025-08-12 14:38:58 -04:00
Dax Raad b289fd9dc7 ci: sync 2025-08-12 14:33:09 -04:00
opencode 13d4a802ac release: v0.4.36 2025-08-12 18:31:12 +00:00
adamdotdevin c4ae3e429c fix: markdown lists 2025-08-12 13:22:28 -05:00
Dax Raad e9cb360cb7 ci: sync 2025-08-12 14:21:34 -04:00
Dax Raad 596d4e4490 ci: sync 2025-08-12 14:17:18 -04:00
Dax Raad 45451095f7 ci: sync 2025-08-12 14:11:10 -04:00
adamdotdevin aae354c951 fix: word wrapping with hyphens 2025-08-12 13:03:35 -05:00
Dax Raad 4cddda3e16 ci: sync 2025-08-12 14:00:30 -04:00
Dax Raad 834aa036a4 ci: sync 2025-08-12 13:58:51 -04:00
Dax Raad 6d1b6a6fb1 ci: ignore 2025-08-12 13:54:53 -04:00
Dax Raad e78fe8dcba ci: tweak 2025-08-12 13:53:32 -04:00
Dax Raad f62d826037 ci: sync 2025-08-12 13:42:46 -04:00
Dax Raad 342f4239e4 ci: softer language 2025-08-12 13:39:53 -04:00
Dax Raad 5141fe8d7d ci: sync 2025-08-12 17:36:48 +00:00
opencode 3a9dd306db release: v0.4.35 2025-08-12 17:36:48 +00:00
Dax Raad e32b6e2cc2 ci: ignore 2025-08-12 13:31:23 -04:00
Dax Raad fab0e5de04 fix issue when @ tagging fiels throwing error 2025-08-12 13:30:52 -04:00
Dax Raad 61105b487f ci: ignore 2025-08-12 13:25:05 -04:00
Dax 6f584ec641 ci: improve guideline check (#1867) 2025-08-12 13:23:19 -04:00
opencode 3a2b2f13f2 release: v0.4.34 2025-08-12 17:14:23 +00:00
Dax Raad be13e71fb9 ci: sync 2025-08-12 13:09:24 -04:00
Dax Raad dd0c049119 ci: tweak 2025-08-12 13:01:45 -04:00
Dax Raad ee2b57958d ci: disable aur 2025-08-12 12:58:18 -04:00
Dax Raad 4178b3c6ae ci: sync 2025-08-12 12:53:55 -04:00
Dax Raad 2c2752ee02 ci: ignore 2025-08-12 12:33:10 -04:00
Dax Raad 5a17f44da4 support OPENCODE_PERMISSION json env variable 2025-08-12 12:28:08 -04:00
Dax Raad 354e55ecef ci: ignore 2025-08-12 12:12:54 -04:00
Dax 10735f93ca Add agent-level permissions with whitelist/blacklist support (#1862) 2025-08-12 11:39:39 -04:00
adamdotdevin ccaebdcd16 fix: long word and attachment wrapping in editor 2025-08-12 10:34:54 -05:00
opencode 2bbd7a167a release: v0.4.29 2025-08-12 14:00:15 +00:00
Aiden Cline f12d470b33 fix: Missing ~/.local/share/opencode/bin directory causes misleading … (#1860) 2025-08-12 13:54:33 +00:00
opencode 0835170224 release: v0.4.28 2025-08-12 13:54:32 +00:00
adamdotdevin 3530885f48 fix: vscode extension cursor placement 2025-08-12 08:48:42 -05:00
opencode a071a2b7f4 release: v0.4.27 2025-08-12 12:58:53 +00:00
adamdotdevin b2f2c9ac37 fix: use real cursor instead of virtual cursor 2025-08-12 07:52:19 -05:00
GitHub Action 631722213b ignore: update download stats 2025-08-12 2025-08-12 12:04:42 +00:00
Camden Clark 80b25c79bb fix: preserve process.env when spawning formatter commands (#1850) 2025-08-12 02:05:26 -04:00
Dax 3d9c5b4adf Update guidelines-check.yml 2025-08-12 00:34:34 -04:00
Dax a3064e2c32 Update duplicate-issues.yml 2025-08-12 00:34:16 -04:00
Dax Raad 275bc4d2c8 ci: add guidelines check workflow for PRs 2025-08-12 00:01:59 -04:00
Dax Raad 39106718ed ci: tweak 2025-08-11 23:52:16 -04:00
Dax Raad 02cfdfbf5b ci: add duplicate issue detection workflow 2025-08-11 23:51:08 -04:00
Dax Raad 1ec71e419b support wildcard matching tool names in config 2025-08-11 23:37:09 -04:00
opencode 5fbbdcaf64 release: v0.4.26 2025-08-12 03:25:36 +00:00
Dax Raad 2b6afe90d0 fix azure reasoningEffort 2025-08-11 23:20:19 -04:00
Dax Raad 5f34dcc792 azure reasoning effort 2025-08-11 23:04:32 -04:00
opencode 681abcbf2d release: v0.4.25 2025-08-12 02:51:32 +00:00
Dax Raad 603e81ef5a Simplify git-committer agent definition for better maintainability 2025-08-11 22:45:40 -04:00
Dax Raad fb0a200ecf refactor: replace OPENCODE_AGENTS env var with HTTP API call
Replace environment variable passing of agent data from Node.js to TUI
with proper HTTP API call to /agent endpoint. This improves architecture
by eliminating env var dependencies and allows dynamic agent data fetching.
2025-08-11 22:42:25 -04:00
opencode 3ec670784d release: v0.4.24 2025-08-12 02:33:35 +00:00
Dax Raad e6f3cf0839 fix pyright 2025-08-11 22:27:24 -04:00
opencode 9437cf4ff6 release: v0.4.23 2025-08-12 02:04:53 +00:00
Dax Raad 7d095d19f6 fix undo/redo when opencode is run in nested folders 2025-08-11 21:59:12 -04:00
Dax Raad 0ca10ec2f5 ignore: log 2025-08-11 21:52:05 -04:00
Dax Raad f03fae03e5 switch back to didUpdate instead of closing and opening file 2025-08-11 21:36:05 -04:00
opencode bb14a955a0 release: v0.4.22 2025-08-12 01:07:27 +00:00
Dax Raad dac1506680 update anthropic prompt and variables 2025-08-11 21:01:33 -04:00
opencode 3946a08f40 release: v0.4.21 2025-08-12 00:30:49 +00:00
adamdotdevin ee0519aacc feat: add clangd for cpp 2025-08-11 19:21:59 -05:00
adamdotdevin dec1e3fdda fix: complete item on space 2025-08-11 18:58:42 -05:00
Carl Brugger f54e900716 Fix plugin file name (#1837) 2025-08-11 23:43:43 +00:00
opencode 7e8b5749fa release: v0.4.20 2025-08-11 23:43:43 +00:00
adamdotdevin febf902dc4 Revert "feat: improve file attachment pasting (#1704)"
This reverts commit 81a3e02474.
2025-08-11 18:37:34 -05:00
Jay V 04b51f2610 ignore: share page thinking blocks 2025-08-11 19:36:34 -04:00
Aiden Cline b2a4f57d64 feat: add -c and -s args to tui command following run command pattern (#1835) 2025-08-11 18:32:09 -05:00
Dax Raad 0ce7d92a8b ignore: fix share page 2025-08-11 16:12:26 -04:00
adamdotdevin 7a67fe7dde fix: collapsed tool calls hidden at times 2025-08-11 13:54:58 -05:00
Aiden Cline 00b4670b8b docs: fix instructions (#1827) 2025-08-11 13:44:12 -05:00
Dax Raad 7633a951e6 ignore: test 2025-08-11 14:43:42 -04:00
adamdotdevin 4ff64c6209 fix: take up less vertical space 2025-08-11 13:38:39 -05:00
Dax Raad 22023fa9e7 remove git bash tool coauthor message 2025-08-11 18:36:06 +00:00
opencode 85e0b53c33 release: v0.4.19 2025-08-11 18:36:06 +00:00
Dax Raad 6eaa231587 Update GPT-5 system prompt to use copilot-specific prompt instead of codex prompt
🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <noreply@opencode.ai>
2025-08-11 14:28:49 -04:00
Jay V befb7509de docs: share 2025-08-11 14:19:22 -04:00
Jay V 09bf0b86d8 docs: share page 2025-08-11 14:01:20 -04:00
Jay V b5d45fa9f5 docs: share page 2025-08-11 13:57:41 -04:00
Jay V a6a633d5c1 docs: test share 2025-08-11 13:45:55 -04:00
Jay V e83e8001da docs: test share 2025-08-11 13:38:03 -04:00
Jay V 0386898476 docs: comment out thinking blocks for share page 2025-08-11 13:21:59 -04:00
adamdotdevin 5e777fd2a2 feat: toggle tool details visible 2025-08-11 11:58:46 -05:00
adamdotdevin 3c71fda648 fix: don't display placeholder on error 2025-08-11 11:58:46 -05:00
Jay V 42329a038a docs: share page fix 2025-08-11 12:51:19 -04:00
Jay V 10f3983f0b docs: edits 2025-08-11 12:41:13 -04:00
opencode e9de7f95a7 release: v0.4.18 2025-08-11 16:04:07 +00:00
adamdotdevin a4113acd15 fix: assistant message footer styles 2025-08-11 10:57:18 -05:00
adamdotdevin 9c8e56fc96 fix: assistant message footer styles 2025-08-11 10:52:49 -05:00
adamdotdevin c78cb57c41 fix: assistant message footer styles 2025-08-11 10:50:00 -05:00
opencode eb15b2ba75 release: v0.4.17 2025-08-11 15:15:24 +00:00
Dax Raad 279edb6f24 fix azure gpt config 2025-08-11 10:56:16 -04:00
Dax Raad c51a34bf4b make models key optional in config 2025-08-11 10:54:14 -04:00
adamdotdevin e8d144d2a2 fix: reformat assistant message footer 2025-08-11 09:38:52 -05:00
adamdotdevin a760e8364f feat: placeholder on pending assistant message 2025-08-11 09:29:44 -05:00
adamdotdevin fa7cae59c0 fix: re-render messages on session error 2025-08-11 09:19:45 -05:00
spoons-and-mirrors 8780fa6ccf Fix: Respect agent's preferred model at TUI startup (#1683)
Co-authored-by: opencode <noreply@opencode.ai>
2025-08-11 08:51:35 -05:00
spoons-and-mirrors ab2df0ae33 Feat: Implement Wrap-Around Navigation for List Selection (for Models and Tools modal) (#1768) 2025-08-11 08:47:51 -05:00
Timo Clasen 23757f3ac0 fix: only load the first local and global rule file (#1761) 2025-08-11 08:28:03 -05:00
Aiden Cline df7296cfe1 fix: instructions should be able to handle absolute paths (#1762) 2025-08-11 08:23:41 -05:00
opencode 776276d5a4 release: v0.4.16 2025-08-11 12:59:20 +00:00
Dax Raad eea45a22fa ci: tweak 2025-08-11 08:54:14 -04:00
opencode ddacb04f99 release: v0.4.15 2025-08-11 12:49:52 +00:00
GitHub Action 09561254a8 ignore: update download stats 2025-08-11 2025-08-11 12:04:33 +00:00
spoons-and-mirrors 73a8356b10 Feat: Add F2 Keybind to Cycle Through the 5 Most Recent Models (#1778) 2025-08-11 07:00:32 -05:00
cvzakharchenko 8db75266d0 Issue 1676: Don't eat up the last newline in a multi-line replacement (#1777) 2025-08-11 06:55:45 -05:00
Jake 6c30565d40 Add support for biome.jsonc config file (#1791) 2025-08-11 06:48:46 -05:00
spoons-and-mirrors b223a29603 Fix: Sanitize MCP Tool Names for Consistency in User Expectations (#1769) 2025-08-11 01:59:50 -04:00
Stibbs 8ed72ae087 chore: add OPENCODE env var (#1780) 2025-08-11 01:56:42 -04:00
Aiden Cline 62b8c7aee0 feat (tui): agents dialog (#1802) 2025-08-11 01:46:38 -04:00
Dax Raad 6145dfcca0 fix run command to be less messy 2025-08-11 01:45:05 -04:00
opencode 4580c88c0b release: v0.4.12 2025-08-11 05:28:22 +00:00
Dax Raad 061ba65d20 show combined output of bash tool progressively 2025-08-11 01:23:00 -04:00
Dax Raad 457386ad08 fix plan mode bash tool making changes 2025-08-11 01:15:12 -04:00
opencode fce04dc48b release: v0.4.11 2025-08-11 02:30:21 +00:00
Dax Raad 81534ab387 ci: tweaks 2025-08-10 22:23:59 -04:00
Aiden Cline 409a6f93b2 fix: enforce field requirement for cli cmds (#1796) 2025-08-10 22:17:12 -04:00
opencode 55c294c013 release: v0.4.6 2025-08-11 01:59:27 +00:00
Dax Raad 70db372466 add OPENCODE_DISABLE_AUTOUPDATE flag 2025-08-10 21:52:52 -04:00
Dax Raad 8cc427daba docs: docs agent 2025-08-10 21:40:49 -04:00
Dax Raad 8fde772957 ci: smoke test 2025-08-10 21:37:48 -04:00
Dax Raad d8dc23bde9 pass through additional agent options to the provider 2025-08-10 21:34:46 -04:00
Tom 1c83ef75a2 fix(plugin): prevent compiled binary hang by removing lazy dynamic import (#1794)
Co-authored-by: opencode <noreply@opencode.ai>
2025-08-10 21:31:15 -04:00
opencode 95e410db88 release: v0.4.3 2025-08-11 00:53:06 +00:00
Dax Raad 13d3fba86b switch gpt-5 to codex prompt 2025-08-10 20:47:38 -04:00
Dax Raad 3ab4f42ebb support agent options 2025-08-10 20:30:37 -04:00
adamdotdevin b8d2aebf09 feat: thinking blocks rendered in tui and share page 2025-08-10 19:25:03 -05:00
Frank 20e818ad05 wip gateway 2025-08-10 12:30:52 -04:00
Aiden Cline 542186aa49 feat: webfetch permission support (#1772) 2025-08-10 08:00:44 -05:00
GitHub Action c478d1bdbb ignore: update download stats 2025-08-10 2025-08-10 12:04:14 +00:00
Frank 7fd2222976 wip: gateway 2025-08-10 01:17:48 -04:00
Frank e86adb2ec8 wip: gateway 2025-08-10 01:03:15 -04:00
Frank 34ac0e895d wip: gateway 2025-08-10 00:29:00 -04:00
spoons-and-mirrors bd4319f2bc Feat: Add Agent Name in the LLM Response Footer (and re-order it) (#1770) 2025-08-09 20:22:16 -05:00
Frank 696ab1a752 Update moonshot ai provider doc 2025-08-09 19:22:50 -04:00
Dax Raad d3ff66e911 use minimal reasoning effort for gpt-5 2025-08-09 15:38:48 -04:00
Aiden Cline 1954b59167 feat: eslint lsp (#1744) 2025-08-09 11:04:58 -05:00
Aiden Cline e2fac991dc better permissions ux when denying (#1747) 2025-08-09 11:03:33 -05:00
GitHub Action 8ba35eadd4 ignore: update download stats 2025-08-09 2025-08-09 12:04:16 +00:00
Frank 7446f5ad7b wip gateway 2025-08-09 01:28:27 -04:00
Dominik Engelhardt 81a3e02474 feat: improve file attachment pasting (#1704) 2025-08-08 20:06:38 -05:00
Dax Raad 7bbc643600 remove synthetic message in plan mode, fixes being confused in build mode 2025-08-08 20:45:24 -04:00
Dax Raad 53630ebdce gpt-5 lower verbosity 2025-08-08 20:42:22 -04:00
Dax 85eaa5b58b Remove unused OpenTelemetry tracing and fix overlapping highlights (#1738)
Co-authored-by: opencode <noreply@opencode.ai>
2025-08-08 20:20:01 -04:00
Erick Christian b789844b9c feat(agent): allow mode selection during creation (#1699) 2025-08-08 20:07:20 -04:00
Clayton 9b6ef074f0 Reference the actual name of the windows package (#1700) 2025-08-08 20:07:00 -04:00
zWing 2f4291672b chore(js-sdk): Compatible with nodenext (#1667) 2025-08-08 20:05:50 -04:00
rmoriz 83f4e8e156 Clarify remote mcp error (#1729)
Co-authored-by: opencode <noreply@opencode.ai>
2025-08-08 20:04:26 -04:00
gsbain 7af2771a7e Docs: Homebrew can install Opencode on Linux (#1737) 2025-08-08 20:04:02 -04:00
Frank c9a3b35ac2 fix deploy 2025-08-08 18:39:47 -04:00
Frank 0dde6d0840 fix deploy script 2025-08-08 17:59:21 -04:00
Max Pod d1208bf0a1 docs: Update plugins.mdx (#1690) 2025-08-08 17:11:06 -04:00
Typing Turtle 0a9463541a docs: Adds required models field to variables documentation (#1709) 2025-08-08 16:57:31 -04:00
Yihui Khuu fe26b4a7b1 fix(tui): preserve scroll position when reflowing due to message stream (#1716) 2025-08-08 13:14:09 -05:00
Frank 8c173e18b7 wip: gateway 2025-08-08 13:24:50 -04:00
Frank 183e0911b7 wip: gateway 2025-08-08 13:24:32 -04:00
GitHub Action c7bb19ad07 ignore: update download stats 2025-08-08 2025-08-08 12:04:40 +00:00
Timo Clasen e444d15b57 fix(TUI): enable general (sub-) agent for @ referencing (#1705) 2025-08-08 05:36:55 -05:00
opencode 063d67a046 release: v0.4.1 2025-08-08 03:01:03 +00:00
Dax Raad 4f164c53d2 temporary fix for max output token 2025-08-07 22:54:59 -04:00
Dax Raad 02ef96f89b docs: fix 2025-08-07 21:49:18 -04:00
Dax Raad 8750744068 renable todo tool 2025-08-07 21:47:37 -04:00
Dax Raad 3e74107e36 looser todo tool schema 2025-08-07 21:47:37 -04:00
Jay V 160f839b25 docs: update cli 2025-08-07 19:24:08 -04:00
Jay V bf5b109c1f docs: edit agent doc 2025-08-07 18:51:54 -04:00
Dax Raad 60254d8ac0 docs: remove modes from sidebar navigation
🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <noreply@opencode.ai>
2025-08-07 16:35:35 -04:00
Dax c34aec060f Merge agent and mode into one (#1689)
The concept of mode has been deprecated, there is now only the agent field in the config.

An agent can be cycled through as your primary agent with <tab> or you can spawn a subagent by @ mentioning it. if you include a description of when to use it, the primary agent will try to automatically use it

Full docs here: https://opencode.ai/docs/agents/
2025-08-07 16:32:12 -04:00
Jay V 12f1ad521f docs: slash commands 2025-08-07 16:16:16 -04:00
Timo Clasen 723a37ea9a fix: get session api (#1684) 2025-08-07 15:28:18 -04:00
Aiden Cline c6a46615c0 fix: modal pastes (#1677) 2025-08-07 13:23:58 -05:00
GitHub Action da29380093 ignore: update download stats 2025-08-07 2025-08-07 12:04:29 +00:00
Aiden Cline 7950ae1462 fix: text selection bug (#1664) 2025-08-07 05:32:34 -05:00
opencode 15e830410f release: v0.3.133 2025-08-07 00:30:05 +00:00
Dax Raad 1a561bb512 add api to get session 2025-08-06 20:24:36 -04:00
Jay V fecae609d9 docs: config doc edits 2025-08-06 16:10:17 -04:00
Jay V e01a540b08 docs: typos 2025-08-06 15:45:16 -04:00
Timo Clasen 54457e48bb fix(docs): small_model is not used for summarization (#1360) 2025-08-06 14:03:14 -05:00
Aiden Cline b179d08484 fix: interface conversion panic (#1655) 2025-08-06 14:02:33 -05:00
Jay V d9edd6818f docs: add undo to tutorial 2025-08-06 13:51:47 -04:00
Dax Raad 4217286b72 ignore: remove demo plugin 2025-08-06 11:36:53 -04:00
Dax Raad 28a4517ec6 add snapshot field in config to disable snapshots 2025-08-06 11:35:37 -04:00
Aiden Cline b00b2ded4f docs: update readme (#1654) 2025-08-06 09:35:02 -05:00
Aiden Cline 7b6d5b1429 chore: update marked-shiki, remove patch (#1653) 2025-08-06 08:47:53 -05:00
GitHub Action 7210db19e9 ignore: update download stats 2025-08-06 2025-08-06 12:04:53 +00:00
Yihui Khuu 90d2b26426 fix: run command should use specified model from cli args if provided (#1648) 2025-08-06 05:39:44 -05:00
Aiden Cline 6beba2c04f docs: document permissions (#1638) 2025-08-06 05:18:08 -05:00
Aiden Cline b8a0ecca98 fix: highlight after text wrap (#1640) 2025-08-06 05:17:35 -05:00
Aiden Cline ad10d3a126 fix: handle undefined agent in task tool (#1642) 2025-08-06 05:16:43 -05:00
Aiden Cline a48274f82b permissions disallow support (#1627) 2025-08-05 19:14:28 -05:00
adamdotdevin 6b25b7e95e feat: better assistant message visual 2025-08-05 19:05:44 -05:00
Jay V 030a3a7446 docs: identity 2025-08-05 19:36:10 -04:00
Timo Clasen 1a0e7f1e63 docs(plugins): fix typo (#1621) 2025-08-05 17:16:47 -05:00
Aiden Cline 677fb6032b fix: markdown table renders (#1623) 2025-08-05 17:16:35 -05:00
Timo Clasen 49aa48ce58 fix: prevent title regeneration on auto compact (#1628) 2025-08-05 17:15:50 -05:00
Dax Raad 857a3cd522 hint back to llm when tool does not exist 2025-08-05 15:58:12 -04:00
Jay V 6ed774ef62 docs: edit 2025-08-05 12:55:57 -04:00
adamdotdevin 5e825a4b6a chore: cleanup old sdk 2025-08-05 11:46:12 -05:00
Dax Raad 3db8e7c2b6 ci: send stats to posthog 2025-08-05 12:01:48 -04:00
GitHub Action b459055757 ignore: update download stats 2025-08-05 2025-08-05 12:04:48 +00:00
Yihui Khuu 2b195e82ee fix: allow disabling the default general agent (#1616) 2025-08-05 05:20:00 -05:00
Omar Shaarawi 58e889796c validate file part bounds to prevent panic (#1612) 2025-08-05 05:18:50 -05:00
Aiden Cline 51498c8de4 docs: make formatter docs a bit more clear (#1613) 2025-08-05 05:17:56 -05:00
Aiden Cline 7a495faa49 fix: server.root is not a function (#1614) 2025-08-05 05:17:32 -05:00
Timo Clasen 4957fca718 fix(plugins): improve session idle event (#1615) 2025-08-05 05:16:43 -05:00
opencode 8168626cd3 release: v0.3.130 2025-08-05 03:25:28 +00:00
Dax Raad b824809605 re-export shell $ for plugin 2025-08-04 23:20:04 -04:00
opencode 5536b14347 release: v0.3.129 2025-08-05 01:18:50 +00:00
Dax Raad 01efe236ef fix @opencode-ai/plugin exports 2025-08-04 21:12:18 -04:00
Frank 7a1f96399d sync 2025-08-04 21:09:44 -04:00
Frank 40036abb9d wip: gateway 2025-08-04 21:08:29 -04:00
Jay V 2970ba6416 docs: lock 2025-08-04 19:53:50 -04:00
Jay V 81412b6197 docs: edit new docs 2025-08-04 19:52:03 -04:00
Mahamed-Belkheir 5bf7691ea6 fix: default value for models with no cost object (#1601) 2025-08-04 16:45:35 -05:00
Min Chun Fu b1055a74d3 added vesper theme (#1602) 2025-08-04 16:45:00 -05:00
Aiden Cline ffcb27fa9a docs: make plugins page exposed (#1603) 2025-08-04 16:44:28 -05:00
opencode 38819e89b8 release: v0.3.128 2025-08-04 16:20:39 +00:00
Dax Raad 0a42068fbb hack to return tool call errors back to model 2025-08-04 12:15:24 -04:00
opencode b05decc572 release: v0.3.127 2025-08-04 16:06:13 +00:00
GitHub Action c2f487906a ignore: update download stats 2025-08-04 2025-08-04 12:04:34 +00:00
Aiden Cline ae78ec7a0c fix double help printing (#1580) 2025-08-04 05:03:27 -05:00
Frank e8c03f13dd fix docs 2025-08-04 00:23:02 -04:00
Dax Raad f85d30c484 wip: plugins 2025-08-03 21:43:58 -04:00
Dax Raad 1bac46612c wip: plugin load from package 2025-08-03 21:19:03 -04:00
Dax Raad 9ab3462821 Add workflow_dispatch trigger to typecheck workflow 2025-08-03 17:16:20 -04:00
Aiden Cline 3b36822696 fix: patch marked-shiki (#1569) 2025-08-03 16:13:35 -05:00
Dax Raad 5b731479d5 Add typecheck workflow 2025-08-03 17:12:23 -04:00
Dax Raad a50bef6913 ignore: cleanup 2025-08-03 17:09:30 -04:00
Yordis Prieto ed397c5057 chore: add ts-expected-error (#1575) 2025-08-03 17:09:19 -04:00
Yordis Prieto c9187a9f3a chore: remove unnecessary TypeScript error suppression (#1571) 2025-08-03 15:50:08 -04:00
opencode 2c67b26b5d release: v0.3.126 2025-08-03 19:45:14 +00:00
Dax Raad 170b94a99e ci: ignore 2025-08-03 15:39:34 -04:00
Dax Raad cd58f10e3c ci: ignore 2025-08-03 15:38:39 -04:00
Dax Raad ea85fdf3cd fix bash tool not showing stderr 2025-08-03 15:34:52 -04:00
Aiden Cline edda26ab33 tweak: filter out duplicate instructions (#1567) 2025-08-03 15:10:21 -04:00
Dax Raad ea4e1913c0 increase models.dev polling interval to hourly 2025-08-03 14:58:35 -04:00
Aiden Cline 5eebc8ab51 docs: fix mixed up documentation (#1564) 2025-08-03 13:01:09 -05:00
Dax Raad 21c52fd5cb fix bash tool getting stuck on interactive commands 2025-08-03 13:52:50 -04:00
opencode 5e8634afaf release: v0.3.123 2025-08-03 17:13:33 +00:00
Dax Raad d4bac5cdbd ci: ignore 2025-08-03 13:12:35 -04:00
opencode 263b266476 release: v0.3.122 2025-08-03 16:19:09 +00:00
Dax Raad 06830327e7 more efficient snapshots in parallel toolcalls 2025-08-03 12:12:28 -04:00
Giuseppe Rota 4b204fee58 fix(docs): move disabled providers paragraph to its proper section (#1547) 2025-08-03 11:28:57 -04:00
Dax Raad 99d3a0bb24 more fixes for shell 128 error 2025-08-03 11:25:58 -04:00
opencode 0930f6ac55 release: v0.3.120 2025-08-03 14:59:03 +00:00
Dax Raad 24515162fa ci: ignore 2025-08-03 10:52:35 -04:00
Dax Raad 53aa899e45 ci: ignore 2025-08-03 10:42:52 -04:00
Dax Raad 7e763e1c06 fix shell error 128 2025-08-03 10:30:23 -04:00
GitHub Action b0f2cc0c22 ignore: update download stats 2025-08-03 2025-08-03 12:04:04 +00:00
Aiden Cline f90aa62784 fix: expand tilde for file: references (#1553) 2025-08-03 06:15:06 -05:00
Dax Raad 852191f6cb ci: ignore 2025-08-03 03:54:17 -04:00
Dax Raad c5e9dc081c ci: bun cache 2025-08-03 03:53:31 -04:00
Dax Raad 49c8889228 ci: ignore 2025-08-03 03:45:05 -04:00
Dax Raad f739e1a958 ci: ignore 2025-08-03 03:37:53 -04:00
Dax Raad 841f1907bb ci: ignore 2025-08-03 03:35:17 -04:00
The Pangolier 9255c507d6 Share link hotfix (#1513) 2025-08-03 03:02:24 -04:00
Yordis Prieto 2711047166 remove: delete extension test file (#1554) 2025-08-03 02:58:10 -04:00
Frank 908048baef sync 2025-08-02 21:28:03 -04:00
Frank a9fbe07408 Add Zhipu AI provider 2025-08-02 21:20:44 -04:00
Dax Raad 0ae213ee0e ci: ignore 2025-08-02 18:56:34 -04:00
Dax Raad ca031278ca wip: plugins 2025-08-02 18:50:19 -04:00
Aiden Cline ae6e47bb42 tweak: make gh action ignore url mentions of opencode (#1531) 2025-08-02 09:31:23 -05:00
Dominik Engelhardt 42a5fcead4 Choose model according to the docs (#1536) 2025-08-02 09:29:03 -05:00
Yihui Khuu 8ad83f71a9 fix(tui): attachment highlighting issues in messages (#1534) 2025-08-02 09:26:44 -05:00
Yihui Khuu fa95c09cdc fix(tui): attachment source is not stored when using message from message history (#1542) 2025-08-02 09:23:32 -05:00
Aiden Cline 0b132c032a ignore: fix dev branch (#1529) 2025-08-02 09:11:38 -05:00
GitHub Action 44d7103a42 ignore: update download stats 2025-08-02 2025-08-02 12:04:12 +00:00
Ricardo Gonzalez 8f45a0e227 feat(models): enable Kimi k2 ⇄ Claude trajectory handoff (#1525) 2025-08-01 23:05:06 -04:00
Aiden Cline 6581741318 fix: include stderr in bash tool output (#1511) 2025-08-01 19:20:32 -05:00
Aiden Cline 80d68d01f4 better configuration error messages (#1517) 2025-08-01 19:10:32 -04:00
Jay V fa9db3c167 docs: cerebras 2025-08-01 18:30:29 -04:00
opencode 5a727c0794 release: v0.3.112 2025-08-01 21:53:33 +00:00
Dax Raad 71cd84dbbb force models.dev refresh on auth login 2025-08-01 17:48:01 -04:00
Dax Raad e1b7e25f4d make top_p configurable 2025-08-01 17:03:33 -04:00
Dax Raad 98b6bb218b configurable lsp 2025-08-01 14:52:10 -04:00
Brinsil Elias 5592ce8eaf fix(docs): Fix formatting for Node.js installation section (#1497) 2025-08-01 14:15:38 -04:00
CodinCat 510fe8a72a handle the optional v in upgrade command when using curl (#1500) 2025-08-01 14:15:22 -04:00
Yordis Prieto 04a1ab3893 chore: enhance bash command tests with config mock and timeout adjustments (#1486)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
2025-08-01 14:14:54 -04:00
Dax Raad e74b4d098b allow search in provider select 2025-08-01 14:03:22 -04:00
Dax Raad 50e4b3e6a7 add version to user-agent 2025-08-01 12:18:09 -04:00
adamdotdevin 6ebd828aa5 fix: unshare command missing 2025-08-01 09:30:42 -05:00
Aiden Cline 022c979d28 tweak: sanitize mcp server names (#831) 2025-08-01 09:11:40 -05:00
Aiden Cline 4172e3ad28 fix: bash tool errors for chmod (#1502) 2025-08-01 09:10:09 -05:00
Aiden Cline 90d1698aed fix: {file:...} references weren't being parsed correctly in some cases (#1499) 2025-08-01 08:39:21 -05:00
adamdotdevin b0c38ce56b ignore: include usage in local setup 2025-08-01 07:42:36 -05:00
GitHub Action 9b37d0e191 ignore: update download stats 2025-08-01 2025-08-01 12:04:33 +00:00
adamdotdevin ea794a4bf6 chore: add local qwen3 to config 2025-08-01 06:27:08 -05:00
Timo Clasen 52f9b37576 docs(permissions): add wildcard example (#1494) 2025-08-01 05:24:32 -05:00
Dax Raad a0d2e53bde poll for models.dev changes 2025-07-31 23:47:42 -04:00
Dax Raad 851e900982 add user agent for models.dev request 2025-07-31 22:00:45 -04:00
Dax Raad 3aa6eeb426 do not mark errored tool calls as aborted 2025-07-31 21:45:40 -04:00
Dax Raad b6ee8e92f9 better guarding against bash commands that go outside of cwd 2025-07-31 21:42:30 -04:00
Frank 44211e1526 Update STATS.md 2025-07-31 21:42:05 -04:00
Dax Raad 12f84f198f improve wildcard matching for permissions 2025-07-31 20:40:05 -04:00
Dax Raad e6db1cf29d ci: ignore release commits 2025-07-31 19:57:07 -04:00
Dax Raad f07f04d969 fix escape button not canceling if retry in progress 2025-07-31 19:55:57 -04:00
Dax Raad 33d613a470 docs: sync 2025-07-31 19:50:51 -04:00
Dax Raad 0bbd7ea17b docs: formatters 2025-07-31 19:50:31 -04:00
Dax Raad 87f3166437 ignore: config 2025-07-31 19:45:44 -04:00
opencode 7665bd9439 Release v0.3.105 2025-07-31 23:41:27 +00:00
Dax Raad 30e10127f2 formatter config 2025-07-31 19:36:07 -04:00
Jay V 5e66fc2318 docs: edit premissions doc 2025-07-31 19:10:54 -04:00
opencode c1c99c7e0f Release v0.3.104 2025-07-31 23:02:36 +00:00
Dax Raad 04e3e83db3 allow disabling formatter 2025-07-31 18:56:04 -04:00
Dax Raad 4273714a62 fix issue with some bash commands asking for permission 2025-07-31 18:35:51 -04:00
Dax Raad a21e237706 ignore: update opencode.json 2025-07-31 18:13:40 -04:00
Dax Raad aa9105649d docs: permissions 2025-07-31 18:11:34 -04:00
Dax Raad 53be288040 docs: permissions 2025-07-31 18:11:34 -04:00
Frank 13dbf912ca Remove hardcoded vscode extension theme
publish-vscode / publish (push) Has been cancelled
2025-07-31 17:53:18 -04:00
Jay V 69966c73f8 docs: add more providers 2025-07-31 17:47:24 -04:00
opencode a00de2df08 Release v0.3.102 2025-07-31 21:25:12 +00:00
Dax Raad 5e72f50554 wip: permissions 2025-07-31 17:19:56 -04:00
Dax Raad d558f15c91 ignore: ts optimization 2025-07-31 16:54:15 -04:00
Dax Raad 614a23698f wip: permissions 2025-07-31 16:51:55 -04:00
Dax Raad a2191ce6fb wip: permissions 2025-07-31 16:38:37 -04:00
Aiden Cline 168350c981 fix: load global jsonc (#1479) 2025-07-31 15:02:28 -05:00
Aiden Cline f5f55062f1 fix: session ordering (#1474) 2025-07-31 14:17:47 -05:00
Frank 360194e219 Add provider instruction for Azure OpenAI 2025-07-31 14:37:26 -04:00
Jay V 5ee994c31f docs: edit providers doc 2025-07-31 14:11:40 -04:00
opencode-agent[bot] fc73d4b1f9 docs: Enhanced providers docs with troubleshooting (#1441)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: jayair <jayair@users.noreply.github.com>
2025-07-31 13:08:12 -04:00
adamdotdevin 936f4cb0c6 fix: permission state hangs 2025-07-31 11:36:08 -05:00
Dax Raad a5b20f973f wip: refactor permissions 2025-07-31 12:26:47 -04:00
adamdotdevin 872b1e068f feat: more scriptable tui (api) 2025-07-31 11:24:23 -05:00
neolooong e4e0b8fd34 fix(editor): handle UTF-8 characters properly in SetValueWithAttachments (#1469) 2025-07-31 10:45:43 -05:00
adamdotdevin c5368e7412 fix: missing operationId 2025-07-31 10:19:42 -05:00
adamdotdevin 1d682544b9 fix: test 2025-07-31 10:10:34 -05:00
adamdotdevin d9210af98c fix: optional toolCallID 2025-07-31 10:09:44 -05:00
adamdotdevin ef633fe92e fix: test 2025-07-31 10:07:58 -05:00
adamdotdevin 5500698734 wip: tui permissions 2025-07-31 09:59:17 -05:00
opencode e7631763f3 Release v0.3.101 2025-07-31 14:23:13 +00:00
Dax Raad 18a572b079 ci: tweak 2025-07-31 10:09:43 -04:00
Dax Raad 060a62ecfb ci: fix 2025-07-31 09:46:36 -04:00
Dax Raad ac3813549a ci: tweak 2025-07-31 09:39:44 -04:00
Dax Raad b14da5fb1f ci: tweak 2025-07-31 09:35:57 -04:00
Dax Raad 416f2235fc ci: reorder 2025-07-31 09:29:55 -04:00
GitHub Action 4fabca426a ignore: update download stats 2025-07-31 2025-07-31 12:04:25 +00:00
Aiden Cline 7e9050edb9 feat: jsonc configuration file support (#1434) 2025-07-31 06:25:26 -05:00
Aiden Cline 3c49a9b7dd fix: process revert cleanup before creating new messages (#1448) 2025-07-31 05:07:59 -05:00
Dax Raad ad66b97463 ci: stainless 2025-07-31 01:42:52 -04:00
Dax Raad 10a0b7f60c ci: tweak 2025-07-31 01:35:11 -04:00
Dax Raad ac8709ac7a ci: tweak 2025-07-31 01:33:21 -04:00
Dax Raad 2d9ed06367 ci: scripts 2025-07-31 01:25:24 -04:00
Dax Raad 50be2aee39 ci tweaks 2025-07-31 01:20:12 -04:00
Dax 0bdbe6261a ci: new publish method (#1451) 2025-07-31 01:05:35 -04:00
Dax 33cef075d2 ci: new publish method (#1451) 2025-07-31 01:00:29 -04:00
Simon Westlin Green b09ebf4645 Use responses API for Azure (#1428)
publish / publish (push) Has been cancelled
2025-07-30 23:22:59 -04:00
Robert Holden 3268c61813 feat: mode directory markdown configuration loading (#1377) 2025-07-30 23:22:43 -04:00
Josh 4a221868da Add http-referer header for vercel ai gateway requests (#1403) 2025-07-30 23:22:24 -04:00
Yordis Prieto 31b8e3d5ab docs: clarify Bun's default registry resolution in index.ts (#1438) 2025-07-30 23:21:07 -04:00
CodinCat 1a78d833a8 fix typo in bash.ts (#1444) 2025-07-30 23:20:48 -04:00
Dax 18888351e9 use treesitter to parse bash commands and catch commands that go outside of cwd (#1443) 2025-07-30 20:57:52 -04:00
Jay V 3b7085ca28 docs: edit 2025-07-30 19:11:36 -04:00
Jay V 160923dcf0 docs: add new providers doc, reorg sidebar, edits 2025-07-30 18:16:11 -04:00
Yordis Prieto c38b091895 fix: update glob pattern and path in tool test (#1436) 2025-07-30 15:42:13 -05:00
Yordis Prieto eecfd6d0ca fix: unit test assertion (#1435)
Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
2025-07-30 15:13:37 -05:00
Dax Raad 6ef4cfa2fa lower max retries to 3 - ai sdk currently cannot abort during a retry delay so things appear to be frozen
publish / publish (push) Has been cancelled
2025-07-30 15:08:25 -04:00
Dax Raad 190dee080c release undo/redo 2025-07-30 13:09:18 -04:00
Aiden Cline 09074dc639 fix: attachment highlighting (#1427) 2025-07-30 11:43:34 -05:00
Aiden Cline 1b3d58e791 fix: prevent read tool from opening binary files and corrupting session (#1425) 2025-07-30 11:00:23 -05:00
GitHub Action 772c83c1d5 ignore: update download stats 2025-07-30 2025-07-30 12:04:23 +00:00
Sam Huckaby 54dc937fa1 fix: quick grammar and spelling check (#1402) 2025-07-30 05:54:47 -05:00
Aiden Cline b5219f7585 tweak: adjust astro css to render mixed nested lists (#1411) 2025-07-30 05:51:52 -05:00
municorn 0bd0453866 build: add @octokit/rest to opencode dependencies (#1396)
Co-authored-by: Frank <frank@sst.dev>
2025-07-29 22:33:25 -04:00
Dax Raad 8bf36d174b update beast prompt for openai models
publish / publish (push) Has been cancelled
2025-07-29 22:15:13 -04:00
Dax Raad 9bedd62da4 experimental well-known auth support
publish / publish (push) Has been cancelled
2025-07-29 19:30:51 -04:00
Yordis Prieto 4c34b69ae6 chore: fix test to have deterministic testing (#1401) 2025-07-29 17:54:22 -05:00
Dax Raad 7e9ac35666 remove min/max in tool schemas
publish / publish (push) Has been cancelled
2025-07-29 17:39:47 -04:00
Frank 4a46144419 convert share backend to hono app 2025-07-29 16:39:48 -04:00
adamdotdevin a129e122aa feat: show git diff in reverted messages 2025-07-29 13:11:38 -05:00
Yordis Prieto c0ee6a6d05 fix: update file name extraction in uploads test to use __filename (#1395) 2025-07-29 12:28:44 -05:00
Yordis Prieto 68ae0d107c fix: improve handling of global File object in uploads tests (#1394) 2025-07-29 11:30:39 -05:00
Yordis Prieto df63008a94 chore: fix null handling in multipartFormRequestOptions test (#1385) 2025-07-29 11:17:03 -05:00
Andrea Grandi 3bd2b340c8 feat: show current git branch in status bar, and make it responsive (#1339)
Co-authored-by: adamdotdevin <2363879+adamdottv@users.noreply.github.com>
2025-07-29 11:15:04 -05:00
Dax Raad df03e182d2 strip todo tool instructions from non anthropic models 2025-07-29 11:56:53 -04:00
Jacob Hands 862a50d61d feat: add OPENCODE_CONFIG env var for specifying a custom config file (#1370) 2025-07-29 11:03:11 -04:00
GitHub Action a7cfd36b07 ignore: update download stats 2025-07-29 2025-07-29 12:04:40 +00:00
Aiden Cline c165360e17 fix: task type error (#1384) 2025-07-29 06:18:34 -05:00
Dax Raad 9cb0f21b4e trim opencode title 2025-07-28 23:24:38 -04:00
Dax Raad 9c9cbb3e81 wip: undo properly remove messages from UI 2025-07-28 22:58:31 -04:00
Dax Raad c24fbb4292 wip: snapshot 2025-07-28 22:58:31 -04:00
Jay V 99dfe65862 docs: share page hide patch part 2025-07-28 20:04:00 -04:00
Jay V 4506e5a824 docs: adding 2025-07-28 20:00:30 -04:00
Jay V b65172a2b7 Tweak auth cli copy 2025-07-28 20:00:30 -04:00
Dax Raad 081f100c93 ignore: tweak
publish / publish (push) Has been cancelled
2025-07-28 12:20:37 -04:00
Dax Raad f2bdb8159f fix phantom tool call failed messages and empty text parts with some models 2025-07-28 12:19:38 -04:00
GitHub Action 10d749a85e ignore: update download stats 2025-07-28 2025-07-28 12:04:21 +00:00
Frank a07d149e28 vscode: add cmd+shift+esc keybinding
publish-vscode / publish (push) Has been cancelled
2025-07-27 15:54:45 -04:00
Frank 3eb982c8cd vscode: bring oc terminal to front if already opened 2025-07-27 14:57:45 -04:00
Frank 45c4e0b8f8 show opencode button in vscode when focused on terminal 2025-07-27 14:44:14 -04:00
Aiden Cline b18b646f8e fix: attachment bugs (#1335) 2025-07-27 12:21:31 -05:00
Frank 9741a6703c fix input format affected by installing vscode extension
publish / publish (push) Has been cancelled
2025-07-27 11:56:18 -04:00
Frank 27a079d9cb simplify github action 2025-07-27 09:56:09 -04:00
GitHub Action 2eeb987680 ignore: update download stats 2025-07-27 2025-07-27 12:04:15 +00:00
Aiden Cline e827294c9b docs: document small_model cfg option (#1347) 2025-07-26 16:56:38 -05:00
Dax Raad 7cf4ed6ad6 ci: fix opencode github 2025-07-26 10:02:31 -04:00
Aiden Cline ad8a4bc744 fix: strip thinking blocks from title (#1325) 2025-07-26 08:29:04 -05:00
GitHub Action 2630104f18 ignore: update download stats 2025-07-26 2025-07-26 12:03:59 +00:00
Frank 670f470eee wip: github actions
publish / publish (push) Has been cancelled
2025-07-26 02:49:05 -04:00
Frank c2b3c52b76 wip: github action 2025-07-26 01:03:23 -04:00
Frank a007d65f62 wip: github actions
publish / publish (push) Has been cancelled
2025-07-25 20:27:42 -04:00
Didier Durand 2c924b9fdb fixing various typos in text. (#1185) 2025-07-25 20:20:01 -04:00
Dax Raad e8eaa77bf1 better mcp support - should fix hanging when streamable http server is added
publish / publish (push) Has been cancelled
2025-07-25 19:19:47 -04:00
Frank a07f37073b wip: github actions
publish / publish (push) Has been cancelled
2025-07-25 19:05:55 -04:00
Frank 4d760a1984 wip: github action
publish-github-action / publish (push) Has been cancelled
2025-07-25 18:33:45 -04:00
Dax Raad 6b7058fe1c qwen optimizations it works good now 2025-07-25 18:31:08 -04:00
Frank 1149b984d9 wip: github actions
publish-github-action / publish (push) Has been cancelled
publish / publish (push) Has been cancelled
2025-07-25 18:29:53 -04:00
Michael Hanson 81fb1b313e Fix a broken example in the MCP documentation and add more clarity (#1322) 2025-07-25 17:47:01 -04:00
Frank 3a7a2a838e wip: github actions
publish / publish (push) Has been cancelled
2025-07-25 17:34:47 -04:00
Dax Raad 10ae43a121 wip: sync
publish / publish (push) Has been cancelled
2025-07-25 15:52:27 -04:00
Dax Raad c85b970903 wip: drop 2025-07-25 15:51:02 -04:00
Dax Raad 7044662cfa handle uploaded text/plain 2025-07-25 15:48:42 -04:00
kehanzhang 92656fdf29 fix(headless): respect mode passed to /message endpoint (#1300) 2025-07-25 15:26:49 -04:00
Dax Raad c65e7aff86 docs: mode temperature 2025-07-25 13:45:04 -04:00
Dax Raad e97613ef9f allow temperature to be configured per mode
publish / publish (push) Has been cancelled
2025-07-25 13:29:44 -04:00
Dominik Engelhardt 827469c725 fix: apply content-level caching for non-anthropic providers (#1305) 2025-07-25 12:19:44 -04:00
Yihui Khuu 613b5fbe48 feat: add csharp lsp (#1312) 2025-07-25 12:17:06 -04:00
Dax Raad 7ed05962db fix issue with trailing whitespace error in assistant message 2025-07-25 10:56:16 -04:00
Dax Raad 250a86ec52 fix reading model from config
publish / publish (push) Has been cancelled
2025-07-25 10:53:37 -04:00
Yihui Khuu 0795a577e0 fix: header width to display header in one line when sharing disabled (#1310) 2025-07-25 09:32:06 -05:00
Dax Raad 8e5607f9c0 fix double system prompt 2025-07-25 10:28:42 -04:00
Dax Raad d6b3bb0807 disable todo tools by default in agent
publish / publish (push) Has been cancelled
2025-07-25 10:23:23 -04:00
Dax Raad f307a5ce0b fix symlinked agents
publish / publish (push) Has been cancelled
2025-07-25 10:20:16 -04:00
GitHub Action 151c7ed5a2 ignore: update download stats 2025-07-25 2025-07-25 12:04:21 +00:00
Dax Raad fc13d057f8 agents better display when spawning
publish / publish (push) Has been cancelled
2025-07-24 23:08:03 -04:00
Dax Raad fc73d3c523 docs: agents
publish / publish (push) Has been cancelled
2025-07-24 22:18:49 -04:00
Dax Raad 5d871b2075 docs: agents 2025-07-24 22:16:16 -04:00
Dax Raad 529a171d51 docs: agents 2025-07-24 22:07:30 -04:00
Dax Raad 8dcd39f5b7 real life totally configurabl ai subasians
publish / publish (push) Has been cancelled
2025-07-24 21:21:02 -04:00
Frank 88477b3ee7 wip: github actions 2025-07-24 19:03:10 -04:00
Jay V 0c7e529e6d docs: add to quick start 2025-07-24 18:57:54 -04:00
Filip 01f75839a9 Fix: added environment() to summarize() (#1290) 2025-07-24 18:24:54 -04:00
Dax Raad 4306f1a339 wip: handle deleting file 2025-07-24 17:49:23 -04:00
Dax Raad aa2a5057ac wip: fix type errors 2025-07-24 17:38:11 -04:00
Dax Raad 284c01018e wip: more snapshot stuff 2025-07-24 17:38:11 -04:00
Aiden Cline 22c9e2942b (tui) tweak: add setting for scroll speed (#1288) 2025-07-24 16:34:59 -05:00
Clay Warren d50ae8e4d4 feat: Replace unzip with @zip.js/zip.js for Windows compatibility (#662) 2025-07-24 16:49:04 -04:00
Filip e9074e60cf fix: add custom() to system prompt on summarize (#1289) 2025-07-24 16:48:17 -04:00
Filip 541a7a39d3 fix: edit tool (#1287) 2025-07-24 16:18:04 -04:00
Dax Raad 72e464ac3e ci: tweak
publish / publish (push) Has been cancelled
2025-07-24 15:55:45 -04:00
Dax Raad 20bf27feda ci: tweak
publish / publish (push) Has been cancelled
2025-07-24 15:51:33 -04:00
Dax Raad d288d21330 includ baseline builds
publish / publish (push) Has been cancelled
2025-07-24 14:37:38 -04:00
Jesse van der Pluijm 34f6ffe1d7 Check if modelID includes "claude" for antropic/claude prompt caching (#1284) 2025-07-24 11:31:28 -04:00
Dax Raad a11999137f disable snapshots
publish / publish (push) Has been cancelled
2025-07-24 11:08:20 -04:00
Aiden Cline a16554d445 fix: slog error log serialization (#1276) 2025-07-24 07:19:00 -05:00
danielfyhr 2553137395 add aura theme (#1280) 2025-07-24 07:17:27 -05:00
GitHub Action 6b6b81556f ignore: update download stats 2025-07-24 2025-07-24 12:04:18 +00:00
Dax Raad ff23f67ad5 disable undo/redo for now 2025-07-23 21:02:13 -04:00
Rico Sta. Cruz 8f0644e35b fix: update max visible height in list tests (#1269) 2025-07-23 20:49:15 -04:00
Dax Raad 3fdd23df16 fix header width 2025-07-23 20:48:35 -04:00
Dax Raad 2c82ee592c wip: always force create snapshot 2025-07-23 20:46:43 -04:00
Dax Raad 1ad529db59 wip: fix redoing 2025-07-23 20:42:02 -04:00
Dax 96866e52ce basic undo feature (#1268)
Co-authored-by: adamdotdevin <2363879+adamdottv@users.noreply.github.com>
Co-authored-by: Jay V <air@live.ca>
Co-authored-by: Aiden Cline <63023139+rekram1-node@users.noreply.github.com>
Co-authored-by: Andrew Joslin <andrew@ajoslin.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Tobias Walle <9933601+tobias-walle@users.noreply.github.com>
2025-07-23 20:30:46 -04:00
Yihui Khuu 507c975e92 feat: pass mode into task tool (#1248) 2025-07-23 20:29:59 -04:00
Aiden Cline 3e69d5276b docs: remove deprecated 'log_level' reference in docs (#1258) 2025-07-23 18:53:58 -04:00
Aiden Cline 289a4d9b18 tweak: handle pasted attachment references (#1257) 2025-07-23 15:41:17 -05:00
Tobias Walle 12bf5f641d fix "working" spinner animation (#1054) (#1259) 2025-07-23 15:40:34 -05:00
Dax Raad 2051e85e96 remove providers path
publish / publish (push) Has been cancelled
2025-07-23 12:15:31 -04:00
Dax Raad 12b86829d9 add debug paths command 2025-07-23 12:14:54 -04:00
GitHub Action 6c9ec54129 ignore: update download stats 2025-07-23 2025-07-23 12:04:18 +00:00
Aiden Cline b7b0cdbd7c tweak: ensure most recently interacted with session appears at the top (#1239) 2025-07-22 22:37:36 -05:00
Dax Raad fd98c3189a config: improve config schema
publish / publish (push) Has been cancelled
2025-07-22 20:35:40 -04:00
Jay V 1278353616 docs: edit ide 2025-07-22 19:02:30 -04:00
Andrew Joslin 638ec7bc50 Allow multiline prompts for github agent (#1225) 2025-07-22 18:30:51 -04:00
Aiden Cline 38ae7d60aa feat(tui): support pipe into tui (#1230) 2025-07-22 17:19:20 -05:00
Jay V 2d1f9fc321 docs: add tutorial closes #740 2025-07-22 17:54:53 -04:00
Frank ee0c8132db wip: vscode extension 2025-07-22 17:13:58 -04:00
Dax Raad c2208fa1f9 ci: error github api fail 2025-07-22 17:06:06 -04:00
Frank bf42d8b011 wip: vscode extension 2025-07-22 16:50:56 -04:00
Frank 0deb85fa45 wip: vscode extension
publish / publish (push) Has been cancelled
2025-07-22 16:46:44 -04:00
Frank da19b10703 wip: vscode extension 2025-07-22 16:46:44 -04:00
Frank 80b17dab44 wip: vscode extension 2025-07-22 16:46:44 -04:00
Dax Raad 6d2ffa82de ignore: lock changes 2025-07-22 15:49:36 -04:00
Dax Raad 7998c3b5ce wip: tui api 2025-07-22 15:49:24 -04:00
Frank 13def91e9a wip: vscode extension
publish-vscode / publish (push) Has been cancelled
2025-07-22 15:36:55 -04:00
Frank 26a40610dd wip: vscode extension 2025-07-22 15:28:09 -04:00
Frank db2fbed691 wip: vscode extension 2025-07-22 13:21:49 -04:00
Aiden Cline 3d4c1425d9 tweak: cleanup cancelled markdown (#1222) 2025-07-22 12:08:03 -05:00
adamdotdevin 10c8b49590 chore: generate sdk into packages/sdk 2025-07-22 11:50:51 -05:00
Dax Raad 500cea5ce7 wip: append-prompt is better 2025-07-22 12:27:02 -04:00
Dax Raad 5aafab118f wip: tui api 2025-07-22 12:15:50 -04:00
Frank 01f8d3b05d wip: vscode extension 2025-07-22 11:21:29 -04:00
adamdotdevin 99d6a28249 fix(tui): more defensive attachment conversion
publish / publish (push) Has been cancelled
2025-07-22 09:28:13 -05:00
GitHub Action 5eaf7ab586 ignore: update download stats 2025-07-22 2025-07-22 12:04:22 +00:00
Aiden Cline e4f754eee7 fix: mouse text selection bug (#1206) 2025-07-21 19:15:36 -05:00
Dax Raad f20ef61bc7 wip: api for tui 2025-07-21 19:53:58 -04:00
Frank 5611ef8b28 wip: vscode extension 2025-07-21 19:10:57 -04:00
Timo Clasen bec796e3c3 feat(tui): add ctrl+p and ctrl-n to history navigation (#1199) 2025-07-21 15:10:50 -05:00
Frank 0bd8b2c72f wip: vscode extension
publish-vscode / publish (push) Has been cancelled
2025-07-21 15:48:46 -04:00
Dax Raad 5550ce47e1 ci: tweaks 2025-07-21 15:45:44 -04:00
Dax Raad 2d84dadc0c fix broken attachments
publish / publish (push) Has been cancelled
2025-07-21 15:38:41 -04:00
Dax Raad 45c0578b22 fix title generation bug
publish / publish (push) Has been cancelled
2025-07-21 15:23:47 -04:00
Dax 1ded535175 message queuing (#1200)
publish / publish (push) Has been cancelled
2025-07-21 15:14:54 -04:00
adamdotdevin d957ab849b fix(tui): up/down arrow handling
publish / publish (push) Has been cancelled
2025-07-21 10:44:21 -05:00
plyght 4b2e52c834 feat(tui): paste minimizing (#784)
Co-authored-by: adamdotdevin <2363879+adamdottv@users.noreply.github.com>
2025-07-21 10:31:29 -05:00
Dax Raad 6867658c0f do not copy empty strings 2025-07-21 11:27:15 -04:00
Dax Raad b8620395cb include newline between messages when copying
publish / publish (push) Has been cancelled
2025-07-21 11:22:51 -04:00
Dax Raad 90d37c98f8 add toast for copy
publish / publish (push) Has been cancelled
2025-07-21 11:19:54 -04:00
adamelmore c9a40917c2 feat(tui): disable keybinds 2025-07-21 10:08:25 -05:00
adamelmore 0aa0e740cd docs: cleanup 2025-07-21 10:02:58 -05:00
adamelmore bb17d14665 feat(tui): theme override with OPENCODE_THEME 2025-07-21 10:02:57 -05:00
adamdotdevin cd0b2ae032 fix(tui): restore spinner ticks
publish / publish (push) Has been cancelled
2025-07-21 05:58:24 -05:00
adamdotdevin 8e8796507d feat(tui): message history select with up/down arrows
publish / publish (push) Has been cancelled
2025-07-21 05:52:11 -05:00
Aiden Cline cef5c29583 fix: pasting issue (#1182) 2025-07-21 04:09:16 -05:00
Aiden Cline acaed1f270 fix: export cmd (#1184) 2025-07-21 04:08:26 -05:00
Dax cda0dbc195 Update STATS.md 2025-07-20 20:36:23 -04:00
Dax Raad 758425a8e4 trimmed selection ui
publish / publish (push) Has been cancelled
2025-07-20 19:36:56 -04:00
Dax Raad 93446df335 ignore: remove log 2025-07-20 19:08:19 -04:00
Dax Raad adc8b90e0f implement copy paste much wow can you believe we went this long without it so stupid i blame adam
publish / publish (push) Has been cancelled
2025-07-20 19:05:38 -04:00
Dax Raad 733c9903ec do not snapshot nongit projects for now
publish / publish (push) Has been cancelled
2025-07-20 13:59:30 -04:00
Frank 7306e20361 wip: vscode extension
publish-vscode / publish (push) Has been cancelled
2025-07-20 13:31:16 -04:00
Frank b4c7042c17 wip: vscode extension
publish-github-action / publish (push) Has been cancelled
publish-vscode / publish (push) Has been cancelled
2025-07-20 13:27:37 -04:00
Frank 6965787b33 wip: vscode extension 2025-07-20 13:17:51 -04:00
Frank ce064b8b0e wip: github action 2025-07-20 13:14:14 -04:00
Frank 0fc546fc6b wip: vscode extension 2025-07-20 13:13:18 -04:00
Frank 77ac9e5ec2 wip: github action 2025-07-20 13:13:00 -04:00
Frank af2c0b3695 wip: github action 2025-07-20 13:07:48 -04:00
Frank 811b22367d wip: github action 2025-07-20 12:41:02 -04:00
Frank 933d50e25a wip: github actions
publish-github-action / publish (push) Has been cancelled
2025-07-20 12:36:53 -04:00
Frank 800bee2722 wip: vscode extension 2025-07-20 12:00:09 -04:00
Dax Raad 5b4fb96c2e wip: make api logger sort correctly 2025-07-20 11:54:56 -04:00
Frank 1d20bf343d wip: vscode extension 2025-07-20 11:54:30 -04:00
Frank 79d9bf57f7 wip: vscode extension 2025-07-20 11:47:18 -04:00
Frank 7b63db6a13 wip: vscode extension 2025-07-20 11:45:35 -04:00
Frank 0e1565449e wip: vscode extension
publish-vscode / publish (push) Has been cancelled
publish / publish (push) Has been cancelled
2025-07-20 11:33:44 -04:00
GitHub Action f9a47fe5a3 ignore: update download stats 2025-07-20 2025-07-20 12:04:10 +00:00
adamdotdevin 2bf9d5d4ec wip: file part source in server/api (optional) 2025-07-20 05:39:18 -05:00
adamdotdevin c18f9ece69 chore: updated tui gitignore 2025-07-20 05:39:18 -05:00
adamdotdevin 4e3c73c4f5 chore: updated stainless script 2025-07-20 05:39:18 -05:00
b0tmtl 8bf2eeccd0 fix(windows): resolve numlock and French keyboard input issues (#1165) 2025-07-20 05:28:15 -05:00
Dax Raad 6232e0fc58 fix bad layout on first render of chat history
publish / publish (push) Has been cancelled
2025-07-19 22:38:36 -04:00
Dax Raad a8b4aed446 fix bash tool rendering
publish / publish (push) Has been cancelled
2025-07-19 22:25:15 -04:00
Aiden Cline 03de0c406d fix: title generation for certain providers (#1159) 2025-07-19 20:01:55 -05:00
Aiden Cline faf8da8743 fix: adjust editor parsing to handle flags like --wait (#1160) 2025-07-19 20:01:25 -05:00
Dax Raad 3386908fd6 ci: ignore
publish / publish (push) Has been cancelled
2025-07-19 19:30:12 -04:00
Dax Raad 5a8847952a ci: ignore
publish / publish (push) Has been cancelled
2025-07-19 19:29:05 -04:00
Dax Raad 87d21ebf2b Revert "fix: prevent sparse spacing in hyphenated words (#1102)"
publish / publish (push) Has been cancelled
This reverts commit 2b44dbdbf1.
2025-07-19 19:25:15 -04:00
Timo Clasen a524fc545c fix(hooks): prevent session_complete hook from firing on subagent sessions (#1149)
publish / publish (push) Has been cancelled
2025-07-19 18:20:07 -05:00
Dax Raad 4316edaf43 fix first run github copilot
publish / publish (push) Has been cancelled
2025-07-19 19:19:38 -04:00
Dax Raad d845924e8b ci: ignore
publish / publish (push) Has been cancelled
2025-07-19 19:00:17 -04:00
Dax Raad a29b322bdd ci: ignore
publish / publish (push) Has been cancelled
2025-07-19 18:54:46 -04:00
Dax Raad 9723ffa7a6 ignore: ci
publish / publish (push) Has been cancelled
2025-07-19 18:48:43 -04:00
Dax Raad f06cd88773 perf: more performance improvements
publish / publish (push) Has been cancelled
2025-07-19 18:41:21 -04:00
Dax Raad 9af92b6914 perf: scroll to bottom in thread
publish / publish (push) Has been cancelled
2025-07-19 17:55:01 -04:00
Dax Raad 8f64c4b312 disable todo tools when running as task
publish / publish (push) Has been cancelled
2025-07-19 15:54:11 -04:00
Dax Raad a32877e908 ignore: create memo abstraction
publish / publish (push) Has been cancelled
2025-07-19 15:26:26 -04:00
Dax Raad 6465c9c44a fix openrouter caching
publish / publish (push) Has been cancelled
2025-07-19 15:11:21 -04:00
Dax Raad 4699739814 shitty hack for terrible charm bubbletea performance
publish / publish (push) Has been cancelled
2025-07-19 15:00:11 -04:00
Dax Raad c1d87c32a2 remove log level from config 2025-07-19 13:37:02 -04:00
Aiden Cline 9c5d9be33a fix: bullet display (#1148) 2025-07-19 12:36:50 -05:00
Aiden Cline 97d9c851e6 fix: escape ansi sequences (#1139) 2025-07-19 12:02:24 -05:00
Dax Raad 76bd702992 docs: fix typo 2025-07-19 12:45:33 -04:00
Yihui Khuu 50c453e577 feat(tui): collapse session header into single line when sharing is disabled (#1145) 2025-07-19 11:43:04 -05:00
Dax Raad 86d5b25d18 pass through model.options properly without having to nest it under provider name. you may have to update your configs see https://opencode.ai/docs/models/#openrouter for an example
publish / publish (push) Has been cancelled
2025-07-19 12:41:58 -04:00
Tom 2b44dbdbf1 fix: prevent sparse spacing in hyphenated words (#1102) 2025-07-19 09:28:40 -05:00
Dax Raad 4bbbbac5f6 vercel ai gateway
publish / publish (push) Has been cancelled
2025-07-19 10:08:36 -04:00
GitHub Action 3c3a997d2a ignore: update download stats 2025-07-19 2025-07-19 12:04:11 +00:00
CodinCat 1676f8b5dd fix table heading rendering (#1138) 2025-07-18 20:17:22 -05:00
Dax Raad c87a7469a0 ci: rollback install script 2025-07-18 18:57:58 -04:00
Michael Hanson 132e26ddbf docs: Clarify MCP config instructions (#1026)
publish / publish (push) Has been cancelled
2025-07-18 16:04:29 -04:00
Rami Chowdhury f1da70b1de feat(provider): add Gemini tool schema sanitization (#1132) 2025-07-18 16:02:54 -04:00
Aiden Cline 5c9d1910af fix: func called before definition (#1134) 2025-07-18 15:00:32 -05:00
Timo Clasen 18abcab208 feat(config): make small model configurable (#1030)
publish / publish (push) Has been cancelled
2025-07-18 14:16:50 -04:00
opencode-agent[bot] 01e7dc2d02 Added install dir priority & user feedback (#1129)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: thdxr <thdxr@users.noreply.github.com>
2025-07-18 14:15:10 -04:00
adamdotdevin 611854e4b6 feat(tui): simpler layout, always stretched 2025-07-18 13:03:27 -05:00
Dax d56dec4ba7 wip: optional IDs in api (#1128)
publish / publish (push) Has been cancelled
2025-07-18 13:42:50 -04:00
Dax Raad c952e9ae3d message rendering performance improvements 2025-07-18 13:40:07 -04:00
GitHub Action 6470243095 ignore: update download stats 2025-07-18 2025-07-18 12:04:28 +00:00
GitHub Action c8321cfbd9 ignore: update download stats 2025-07-18 2025-07-18 12:02:18 +00:00
Yihui Khuu 46c246e01f fix: \{return} should be replaced with new line on all lines (#1119) 2025-07-18 06:22:36 -05:00
adamdotdevin 9964d8e6c0 fix: model cost overrides
publish / publish (push) Has been cancelled
2025-07-18 05:08:35 -05:00
Timo Clasen df33143396 feat(tui): parse for file attachments when exiting EDITOR (#1117) 2025-07-18 04:47:20 -05:00
Aiden Cline 571aeaaea2 tweak: remove needless resorting (#1116) 2025-07-18 04:42:43 -05:00
Aiden Cline edfea03917 tweak: fix [object Object] in logging (#1114) 2025-07-18 04:41:23 -05:00
Tom 81c88cc742 fix(tui): ensure viewport scrolls to bottom on new messages (#1110) 2025-07-18 04:41:03 -05:00
Mike Wallio 99b9390d80 Update to a customized beast mode v3 for opencode. (#1109) 2025-07-17 20:10:06 -05:00
Dax Raad 23c30521d8 only enable ruff if it seems to be used
publish / publish (push) Has been cancelled
2025-07-17 18:07:06 -04:00
Wendell Misiedjan e681d610de feat: support AWS_BEARER_TOKEN_BEDROCK for amazon bedrock provider autoloading (#1094)
publish / publish (push) Has been cancelled
2025-07-17 09:12:30 -05:00
Aiden Cline a1fdeded3e tweak: allow mcp servers to include headers (#1096) 2025-07-17 09:11:48 -05:00
GitHub Action 2051312d12 ignore: update download stats 2025-07-17 2025-07-17 14:07:13 +00:00
Alexander Drottsgård 20cb7a76af feat(tui): highlight current session in sessions modal (#1093) 2025-07-17 07:40:15 -05:00
Timo Clasen a493aec174 feat(tui): remove share commands from help if sharing is disabled (#1087) 2025-07-17 04:28:12 -05:00
Aiden Cline 3ce3ac8e61 fix: message error centering (#1085) 2025-07-17 04:27:40 -05:00
Timo Clasen 91ad64feda fix(tui): user defined ctrl+z should take precedence over suspending (#1088) 2025-07-17 04:27:02 -05:00
Timo Clasen 60b55f9d92 feat(tui): remove sharing info from session header when sharing is disabled (#1076) 2025-07-16 17:36:48 -05:00
Timo Clasen 3c6c2bf13b docs(share): add explicit manual share mode (#1074) 2025-07-16 16:08:25 -05:00
Aiden Cline d4f9375548 fix: type 'reasoning' was provided without its required following item (#1072) 2025-07-16 15:59:40 -05:00
Jay V 28b39f547e docs: edit 2025-07-16 16:59:12 -04:00
Jay V 7520f5efa8 docs: update enterprise doc 2025-07-16 16:44:28 -04:00
Jay V eb4cdf4b20 docs: config doc 2025-07-16 16:27:44 -04:00
Jay V 9f6fc1c3c5 docs: edits 2025-07-16 16:20:09 -04:00
Mike Wallio dfede9ae6e Remove binary file opencode (#1069) 2025-07-16 15:10:40 -05:00
Daniel Saldarriaga López fc45c0c944 docs: fix keybinds documentation to match actual config schema (#867)
publish / publish (push) Has been cancelled
2025-07-16 15:34:52 -04:00
adamdotdevin 9d869f784c fix(tui): expand edit calls 2025-07-16 14:33:57 -05:00
adamdotdevin bd244f73af fix(tui): slightly faster scroll speed 2025-07-16 14:26:46 -05:00
Dax Raad dd34556e9c only include severity 1 diagnostics from lsp in edit tool output
publish / publish (push) Has been cancelled
2025-07-16 15:25:37 -04:00
adamdotdevin f7dd48e60d feat(tui): more ways to quit 2025-07-16 14:20:28 -05:00
Dax Raad 93c779cf48 docs: better variable examples 2025-07-16 14:56:24 -04:00
adamdotdevin 360c04c542 docs: copying text 2025-07-16 13:26:26 -05:00
adamdotdevin 529fd57e75 fix: missing dependency
publish / publish (push) Has been cancelled
2025-07-16 12:58:29 -05:00
adamdotdevin faea3777e1 fix: missing dependency 2025-07-16 12:56:11 -05:00
Aiden Cline a4664e2344 fix: generate title should use same options as model it uses to gen (#1064) 2025-07-16 12:46:52 -05:00
adamdotdevin cdc1d8a94d feat(tui): layout config to render full width
publish / publish (push) Has been cancelled
2025-07-16 12:43:02 -05:00
Jay V fdd6d6600f docs: rename workflow 2025-07-16 13:38:00 -04:00
Jay V 9f44cfd595 docs: discord releases 2025-07-16 13:17:04 -04:00
Aiden Cline 70229b150c Fix: better title generation (needs to change due to small models) (#1059) 2025-07-16 11:47:56 -05:00
John Henry Rudden 050ff943a6 Fix: Add escape sequence for @ symbols to prevent send blocking (#1029) 2025-07-16 11:18:48 -05:00
Tom 88b58fd6a0 fix: Prevent division by zero in context percentage calculation (#1055) 2025-07-16 09:35:20 -05:00
Jeremy Mack 5d67e13df5 fix: grep omitting text after a colon (#1053) 2025-07-16 09:09:05 -05:00
Adi Yeroslav 57d1a60efc feat(tui): shift+tab to cycle modes backward (#1049) 2025-07-16 07:43:48 -05:00
Nipuna Perera add81b9739 Enhance private npm registry support (#998) 2025-07-16 08:31:38 -04:00
GitHub Action 81bdb8e269 ignore: update download stats 2025-07-16 2025-07-16 12:04:30 +00:00
adamdotdevin a563fdd287 fix(tui): diagnostics rendering 2025-07-16 06:55:14 -05:00
adamdotdevin 7c93bf5993 fix(tui): pending tool call width 2025-07-16 06:27:32 -05:00
adamdotdevin 6a5a4247c6 fix(gh): build 2025-07-16 06:13:43 -05:00
adamdotdevin a39136a2a0 fix(tui): render attachments in user messages in accent color 2025-07-16 06:09:27 -05:00
adamdotdevin 9f5b59f336 chore: messages cleanup 2025-07-16 06:09:27 -05:00
adamdotdevin 01c125b058 fix(tui): faster cache algo 2025-07-16 06:09:27 -05:00
adamdotdevin d41aa2bc72 chore(tui): simplify messages component, remove navigate, add copy last message 2025-07-16 06:09:26 -05:00
Robin Moser f45deb37f0 fix: don't sign snapshot commits (#1046) 2025-07-16 04:46:32 -05:00
Matias Insaurralde e89972a396 perf: move ANSI regex compilations to package level (#1040)
Signed-off-by: Matías Insaurralde <matias@insaurral.de>
2025-07-16 04:20:25 -05:00
Frank c3c647a21a wip: github actions 2025-07-16 16:20:06 +08:00
Frank b79167ce66 sync 2025-07-16 16:12:31 +08:00
Frank 7ac0a2bc65 wip: github actions 2025-07-16 16:05:51 +08:00
Frank cb032cff2b wip: github actions 2025-07-16 03:57:14 -04:00
Frank 867a69a751 wip: github actions 2025-07-16 03:54:20 -04:00
Frank 20b8efcc50 wip: github actions 2025-07-16 15:36:23 +08:00
Frank a86d42149f wip: github actions 2025-07-16 14:59:53 +08:00
Frank 82a36acfe3 wip: github action 2025-07-16 14:59:53 +08:00
Dax Raad 0793c3f2a3 clean up export command
publish / publish (push) Has been cancelled
2025-07-15 21:50:43 -04:00
Dax Raad 5c860b0d69 fix share page v1 message 2025-07-15 21:35:32 -04:00
Dax Raad 05bb127a8e enable bash tool in plan mode 2025-07-15 21:28:03 -04:00
aron 1bbd84008f move spoof prompt to support anthropic with custom modes (#1031)
publish / publish (push) Has been cancelled
2025-07-15 21:16:27 -04:00
Stephen Murray fdfd4d69d3 add support for modified gemini-cli system prompt (#1033)
Co-authored-by: Dax Raad <d@ironbay.co>
2025-07-15 21:13:11 -04:00
Jay 7f659cce36 docs: Update README.md 2025-07-15 20:09:26 -04:00
Jay V 48fcaa83be docs: fix config 2025-07-15 19:54:51 -04:00
Jay V 70c16c4c95 docs: adding action to notify discord 2025-07-15 19:49:38 -04:00
Jay V c1e1ef6eb5 docs: readme 2025-07-15 18:32:04 -04:00
Jay V bb155db8b2 docs: share tweak copy button 2025-07-15 18:25:25 -04:00
John Henry Rudden 7c91f668d1 docs: share add copy button to messages in web interface (#902)
Co-authored-by: Jay <air@live.ca>
2025-07-15 17:56:33 -04:00
Jay V 1af103d29e docs: share handle non bundled langs 2025-07-15 17:47:22 -04:00
Jay V 8a3e581edc docs: share fix diff bugs 2025-07-15 17:47:22 -04:00
Jay V 749e7838a4 docs: share page task tool 2025-07-15 17:47:22 -04:00
Dax Raad 73b46c2bf9 docs: document base URL 2025-07-15 14:57:50 -04:00
Joe Schmitt 8bd250fb15 feat(tui): add /export command to export conversation to editor (#989)
Co-authored-by: opencode <noreply@opencode.ai>
2025-07-15 13:53:21 -05:00
Dax Raad b1ab641905 add small model for title generation
publish / publish (push) Has been cancelled
2025-07-15 14:00:52 -04:00
adamdotdevin 76e256ed64 fix(tui): wider max width
publish / publish (push) Has been cancelled
2025-07-15 12:44:41 -05:00
adamdotdevin 4f955f2127 fix(tui): mouse scroll ansi parsing and perf 2025-07-15 12:03:30 -05:00
Aiden Cline bbeb579d3a tweak: (opencode run): adjust tool call rendering, reduce number of "Unknowns" (#1012) 2025-07-15 11:22:57 -05:00
Timo Clasen f707fb3f8d feat(tui): add keymap to remove entries from recently used models (#1019) 2025-07-15 11:20:56 -05:00
adamdotdevin 6b98acb7be chore: update stainless defs 2025-07-15 10:03:11 -05:00
adamdotdevin 2487b18f62 chore: update stainless script to kick off prod build 2025-07-15 08:15:31 -05:00
adamdotdevin 533f64fe26 fix(tui): rework lists and search dialog 2025-07-15 08:07:26 -05:00
Dax Raad b5c85d3806 fix logic for suprpessing snapshots in big directories
publish / publish (push) Has been cancelled
2025-07-15 09:07:04 -04:00
Dax Raad bcf952bc8a upgrade ai sdk 2025-07-15 09:06:35 -04:00
GitHub Action a6dc75a44c ignore: update download stats 2025-07-15 2025-07-15 12:04:28 +00:00
Joohoon Cha 416daca9c6 fix(tui): close completion dialog on ctrl+h (#1005) 2025-07-15 06:24:05 -05:00
Aiden Cline 636fe0fb64 Fix: failed to open session (#999) 2025-07-15 05:40:29 -05:00
Frank 95e0957d64 wip: github actions 2025-07-15 17:45:16 +08:00
Dax Raad 2eefdae6a9 ignore: fix types 2025-07-15 00:56:03 -04:00
Dax Raad d62746ceb7 fix panic
publish / publish (push) Has been cancelled
2025-07-15 00:35:02 -04:00
Dax Raad 4b2ce14ff3 bring back task tool
publish / publish (push) Has been cancelled
2025-07-15 00:05:54 -04:00
Jase Kraft 294a11752e fix: --continue pull the latest session id consistently (#918)
Co-authored-by: Dax Raad <d@ironbay.co>
2025-07-14 20:32:00 -04:00
Dax Raad 1cf1d1f634 docs: fix agents.md 2025-07-14 20:23:05 -04:00
Ryan Roden-Corrent 2ce694d41f Add support for job-control suspend (ctrl+z/SIGSTP). (#944) 2025-07-14 20:13:46 -04:00
CodinCat d6eff3b3a3 improve error handling and logging for GitHub API failures in upgrade and install script (#972) 2025-07-14 20:13:12 -04:00
Dax Raad e63a6d45c1 docs: README 2025-07-14 20:10:43 -04:00
Dax Raad 93686519ba docs: README 2025-07-14 20:06:15 -04:00
Mike Wallio f593792fb5 Standardize parameter description references in Edit and MultiEdit tools (#984) 2025-07-14 20:03:59 -04:00
Dax Raad 2cdb37c32b support anthropic console login flow 2025-07-14 18:07:55 -04:00
Timo Clasen 535d79b64c docs: fix typo (#982) 2025-07-14 16:40:16 -04:00
Dax Raad b4e4c3f662 wip: snapshot 2025-07-14 15:29:08 -04:00
adamdotdevin ba676e7ae0 fix(tui): support readline nav in new search component
publish / publish (push) Has been cancelled
2025-07-14 12:20:58 -05:00
adamdotdevin a1c8e5af45 chore: use new search component in find dialog 2025-07-14 12:15:47 -05:00
adamdotdevin f1e7e7c138 feat(tui): even better model selector 2025-07-14 12:15:46 -05:00
Dax Raad 80b77caec0 ignore: share page fix 2025-07-14 13:13:33 -04:00
Dorian Karter 86a2ea44b5 feat(tui): add support for readline list nav (ctrl-p/ctrl-n) (#955) 2025-07-14 10:21:09 -05:00
Dax Raad a2002c88c6 wip: update sdk
publish / publish (push) Has been cancelled
2025-07-14 11:18:08 -04:00
opencode-agent[bot] d8bcf4f4e7 Fix issue: Option to update username shown in conversations. (#975)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: thdxr <thdxr@users.noreply.github.com>
2025-07-14 11:03:04 -04:00
Dax Raad 31e0326f78 fix init command and escape to cancel
publish / publish (push) Has been cancelled
2025-07-14 10:48:17 -04:00
adamdotdevin a53d2ea356 fix(tui): build and bg color 2025-07-14 09:14:02 -05:00
adamdotdevin 229a280652 fix(tui): find dialog bg color 2025-07-14 09:09:55 -05:00
Nicholas Hamilton 8d0350d923 feat: ability to create new session from session dialog (#920) 2025-07-14 09:04:43 -05:00
Almir Sarajčić 4192d7eacc Fix failing git hooks (#966) 2025-07-14 07:52:29 -05:00
Munawwar Firoz 7b8b4cf8c7 feat: ctrl+left arrow / ctrl+right arrow key support (#969) 2025-07-14 07:16:06 -05:00
Almir Sarajčić 1f4de75348 Explain usage of external references in AGENTS.md (#965) 2025-07-14 07:06:37 -05:00
GitHub Action 457755c690 ignore: update download stats 2025-07-14 2025-07-14 12:04:16 +00:00
Aiden Cline 052a1e7514 fix: file command visual bug (#959) 2025-07-14 07:03:02 -05:00
Daniel Nouri 139d6e2818 Fix clipboard on Wayland systems (#941)
Co-authored-by: Daniel Nouri <daniel@redhotcar>
2025-07-14 06:57:45 -05:00
Dax Raad 06554efdf4 get rid of cli markdown dep
publish / publish (push) Has been cancelled
2025-07-13 23:06:31 -04:00
Dax Raad 67e9bda94f ci
publish / publish (push) Has been cancelled
2025-07-13 22:58:33 -04:00
Dax Raad 53bb6b4c4f fix missing tokens
publish / publish (push) Has been cancelled
2025-07-13 22:56:29 -04:00
Dax Raad 73d54c7068 fix type error 2025-07-13 17:25:13 -04:00
Dax 90d6c4ab41 Part data model (#950) 2025-07-13 17:22:11 -04:00
opencode-agent[bot] 736396fc70 Added sharing config with auto/disabled options (#951)
Co-authored-by: opencode-agent[bot] <opencode-agent[bot]@users.noreply.github.com>
Co-authored-by: thdxr <thdxr@users.noreply.github.com>
2025-07-13 16:43:58 -04:00
Dax Raad 177bfed93e ci: github action 2025-07-13 16:22:58 -04:00
Dax Raad 91f8477ef5 wip: mcp 2025-07-13 16:22:16 -04:00
John Henry Rudden f04a5e50ee fix: deduplicate command suggestions (#934) 2025-07-13 14:47:26 -05:00
Aiden Cline bb28b70700 Fix: title generation (#949) 2025-07-13 14:46:36 -05:00
Frank 7361a02ef3 wip: github actions 2025-07-13 23:59:25 +08:00
GitHub Action d465f150fc ignore: update download stats 2025-07-13 2025-07-13 12:04:11 +00:00
Dax Raad 17fa8c117b fix packages being reinstalled on every start
publish / publish (push) Has been cancelled
2025-07-12 12:41:12 -04:00
Muzammil Khan 9aa0c40a00 feat: add more ignore patterns to the ls tool (#913) 2025-07-12 12:06:58 -04:00
GitHub Action fd4648da17 ignore: update download stats 2025-07-12 2025-07-12 12:03:59 +00:00
Dax Raad aadca5013a fix share page timestamps 2025-07-11 21:49:20 -04:00
Dax Raad 5c3d490e59 share page hide step-finish events 2025-07-11 21:45:56 -04:00
Dax Raad 1254f48135 fix issue preventing things from working when node_modules or package.json present in ~/
publish / publish (push) Has been cancelled
2025-07-11 21:09:39 -04:00
Dax Raad 1729c310d9 switch global config to ~/.config/opencode/opencode.json
publish / publish (push) Has been cancelled
2025-07-11 20:51:23 -04:00
Dax Raad 0130190bbd docs: add model docs 2025-07-11 20:33:06 -04:00
Aiden Cline 97a31ddffc tweak: plan interactions should match web (TUI) (#895) 2025-07-11 18:03:22 -04:00
zWing 3249420ad1 fix: avoid overwriting the provider.option.baseURL (#880) 2025-07-11 18:01:28 -04:00
Dax Raad 4bb8536d34 introduce cache version concept for auto cleanup when breaking cache changes happen
publish / publish (push) Has been cancelled
2025-07-11 17:50:49 -04:00
Jay c73d4a137e docs: Update troubleshooting.mdx 2025-07-11 17:50:25 -04:00
Dax Raad 57ac8f2741 wip: stats 2025-07-11 17:37:41 -04:00
Jay V 2f1acee5a1 docs: share page add time footer back
publish / publish (push) Has been cancelled
2025-07-11 14:24:20 -04:00
Jay V 9ca54020ac docs: share page mobile bugs 2025-07-11 14:24:20 -04:00
Jay V f7d44b178b docs: share fix mobile diffs 2025-07-11 14:24:20 -04:00
Sergii Kozak b4950a157c fix(session): add fallback for undefined output token limit (#860)
Co-authored-by: opencode <noreply@opencode.ai>
2025-07-11 10:55:13 -04:00
alexz dfbef066c7 fix: ENAMETOOLONG: name too long when adding custom mode (#881) 2025-07-11 10:54:52 -04:00
GitHub Action 26fd76fbee ignore: update download stats 2025-07-11 2025-07-11 12:04:08 +00:00
adamdotdevin 04769d8a26 fix(tui): help commands bg color 2025-07-11 06:03:21 -05:00
adamdotdevin 34b576d9b5 fix(tui): don't include /mode trigger 2025-07-11 06:01:51 -05:00
adamdotdevin 22b244f847 fix(tui): actually fix mouse ansi codes leaking 2025-07-11 06:00:20 -05:00
Aiden Cline 7e1fc275e7 fix: avoid worker exception, graceful 404 (#869) 2025-07-11 04:55:56 -05:00
Frank 3b9b391320 wip: github actions 2025-07-11 06:55:13 +08:00
Frank 766bfd025c wip: github actions 2025-07-11 05:23:24 +08:00
Jay V c7f30e1065 docs: share page fix terminal part 2025-07-10 17:21:21 -04:00
Frank 1c4fd7f28f Api: add endpoint for getting github app token 2025-07-11 05:01:27 +08:00
adamdotdevin 85805d2c38 fix(tui): handle SIGTERM, closes #319
publish / publish (push) Has been cancelled
2025-07-10 15:59:03 -05:00
Timo Clasen 982cb3e71a fix(tui): center help dilaog (#853) 2025-07-10 15:56:19 -05:00
adamdotdevin 294d0e7ee3 fix(tui): mouse wheel ansi codes leaking into editor 2025-07-10 15:49:58 -05:00
Jay V 8be1ca836c docs: fix diag styles 2025-07-10 16:38:51 -04:00
Jay V 2e5f96fa41 docs: share page attachment 2025-07-10 16:38:51 -04:00
Dax Raad c056b0add9 add step finish part
publish / publish (push) Has been cancelled
2025-07-10 16:25:38 -04:00
Dax Raad b00bb3c083 run: properly close session.list 2025-07-10 16:13:01 -04:00
Dax Raad d9befd3aa6 disable filewatcher, fixes file descriptor leak
publish / publish (push) Has been cancelled
2025-07-10 15:58:45 -04:00
Dax Raad 49de703ba1 config: escape file: string content
publish / publish (push) Has been cancelled
2025-07-10 15:38:58 -04:00
Dax Raad 22988894c8 ci: slow down stats 2025-07-10 15:31:06 -04:00
adamdotdevin 34b1754f25 docs: clipboard requirements on linux 2025-07-10 13:12:37 -05:00
adamdotdevin 54fe3504ba feat(tui): accent editor border on leader key 2025-07-10 12:57:22 -05:00
Jay V d2c862e32d docs: edit local models 2025-07-10 13:49:24 -04:00
Jay V afc53afb35 docs: edit mode 2025-07-10 13:29:37 -04:00
Gabriel Garrett b56e49c5dc Adds real example in docs of how to configure custom provider (#840) 2025-07-10 13:29:30 -04:00
Aiden Cline 8b2a909e1f fix: encode & decode file paths (#843) 2025-07-10 11:19:54 -05:00
Jay V e9c954d45e docs: add modes to sidebar 2025-07-10 12:07:44 -04:00
Jay V 6f449d13af docs: add modes to sidebar 2025-07-10 12:07:18 -04:00
Dax Raad 6e375bef0d docs: modes
publish / publish (push) Has been cancelled
2025-07-10 11:53:28 -04:00
Dax Raad 67106a6967 docs: add config variable docs 2025-07-10 11:48:55 -04:00
Dax Raad b5d690620d support env and file pointers in config 2025-07-10 11:45:31 -04:00
Dax Raad 9db3ce1d0b opencode run respects mode 2025-07-10 11:28:28 -04:00
Dax Raad 1cc55b68ef wip: scrap 2025-07-10 11:25:37 -04:00
Dax Raad 469f667774 set max output token limit to 32_000 2025-07-10 11:25:37 -04:00
adamdottv 6603d9a9f0 feat: --mode flag passed to tui 2025-07-10 10:19:25 -05:00
adamdottv 5dc1920a4c feat: mode flag in cli run command 2025-07-10 10:13:15 -05:00
adamdottv d3e5f3f3a8 feat(tui): add token and cost info to session header 2025-07-10 10:06:51 -05:00
adamdottv ce4cb820f7 feat(tui): modes 2025-07-10 10:06:51 -05:00
Dax Raad ba5be6b625 make LSP lazy again
publish / publish (push) Has been cancelled
2025-07-10 09:37:40 -04:00
adamdottv f95c3f4177 fix(tui): fouc in textarea on app load 2025-07-10 08:20:17 -05:00
adamdottv d2b1307bff fix(tui): textarea cursor sync issues with attachments 2025-07-10 07:49:36 -05:00
adamdottv b40ba32adc fix(tui): textarea issues 2025-07-10 07:38:57 -05:00
GitHub Action ce0cebb7d7 ignore: update download stats 2025-07-10 2025-07-10 12:04:15 +00:00
Dax Raad f478f89a68 temporary grok 4 patch
publish / publish (push) Has been cancelled
2025-07-10 07:57:55 -04:00
Dax Raad 85d95f0f2b disable lsp on non-git folders 2025-07-10 07:39:02 -04:00
Dax Raad 1515efc77c fix session is busy error
publish / publish (push) Has been cancelled
2025-07-10 07:27:03 -04:00
Josh Medeski 6d393759e1 feat(tui): subsitute cwd home path on status bar (#808) 2025-07-10 06:12:19 -05:00
Adi Yeroslav a1701678cd feat(tui): /editor - change the auto-send behavior to put content in input box instead (#827) 2025-07-10 05:57:52 -05:00
Timo Clasen c411a26d6f feat(tui): hide cost if using subscription model (#828) 2025-07-10 05:56:36 -05:00
adamdottv 85dbfeb314 feat(tui): @symbol attachments 2025-07-10 05:53:00 -05:00
Dax Raad 085c0e4e2b respect go.work when spawning LSP
publish / publish (push) Has been cancelled
2025-07-09 22:54:47 -04:00
Dax Raad 8404a97c3e better detection of prettier formatter
publish / publish (push) Has been cancelled
2025-07-09 22:37:31 -04:00
Dax Raad 0ee3b1ede2 do not wait for LSP to be fully ready
publish / publish (push) Has been cancelled
2025-07-09 21:59:38 -04:00
Dax Raad a826936702 modes concept 2025-07-09 21:59:38 -04:00
Jay V fd4a5d5a63 docs: share doc edit 2025-07-09 20:26:31 -04:00
Jay V 69cf1d7b7e docs: share doc 2025-07-09 20:24:09 -04:00
Jay V 8e0a1d1167 docs: edit troubleshooting 2025-07-09 19:55:14 -04:00
Timo Clasen f22021187d feat(tui): treat pasted text file paths as file references (#809) 2025-07-09 18:37:39 -05:00
Jay V febecc348a docs: enterprise doc 2025-07-09 15:46:57 -04:00
Jay V c5ccfc3e94 docs: share page last part fix 2025-07-09 15:46:57 -04:00
Mike Wallio 1f6efc6b94 Add gpt-4.1 beast prompt (#778)
publish / publish (push) Has been cancelled
Co-authored-by: Dax Raad <d@ironbay.co>
2025-07-09 12:11:54 -04:00
Frank Denis 727fe6f942 LSP: fix SimpleRoots to actually search in the root directory (#795) 2025-07-09 10:35:06 -05:00
Dax Raad a91e79382e ci: remove checked in config.schema.json 2025-07-09 11:30:42 -04:00
Dax Raad 5c626e0a2f ci: generate config schema as part of build 2025-07-09 11:25:58 -04:00
adamdottv 8e9e383219 chore: troubleshooting docs 2025-07-09 10:12:36 -05:00
Dax Raad f383008cc1 lsp: spawn only a single tsserver in project root
publish / publish (push) Has been cancelled
2025-07-09 11:06:44 -04:00
adamdottv 303ade25ed feat: discord redirect 2025-07-09 10:01:42 -05:00
adamdottv 53f8e7850e feat: configurable log levels 2025-07-09 10:00:03 -05:00
adamdottv ca8ce88354 feat(tui): move logging to server logs 2025-07-09 08:16:10 -05:00
adamdottv 37a86439c4 fix(tui): don't panic on missing linux clipboard tool 2025-07-09 06:51:58 -05:00
adamdottv 269b43f4de fix(tui): markdown wrapping off sometimes 2025-07-09 06:41:53 -05:00
adamdottv 3f25e5bf86 chore: internal clipboard package 2025-07-09 04:55:24 -05:00
Aiden Cline 67765fa47c tweak: keep completion options open when trigger is still present (#789) 2025-07-09 04:42:31 -05:00
adamdottv 58b1c58bc5 fix(tui): clear command priority
publish / publish (push) Has been cancelled
2025-07-08 19:26:50 -05:00
Dax Raad d80badc50f ci: ignore chore commits 2025-07-08 20:05:33 -04:00
Dax Raad 75279e5ccf wip: symbols endpoint 2025-07-08 20:05:33 -04:00
Yihui Khuu 7893b84614 Add debounce before exit when using non-leader exit command (#759) 2025-07-08 18:53:38 -05:00
Dax Raad cfc715bd48 wip: remove excess import
publish / publish (push) Has been cancelled
2025-07-08 19:51:09 -04:00
adamdottv 39bcba85a9 chore: vendor clipboard into go package 2025-07-08 18:48:40 -05:00
adamdottv da3df51316 chore: remove clipboard temp 2025-07-08 18:47:59 -05:00
adamdottv 12190e4efc chore: vendor clipboard into go package 2025-07-08 18:46:42 -05:00
Aiden Cline d2a9b2f64a fix: documentation typo (#781) 2025-07-08 18:30:46 -05:00
adamdottv aacadd8a8a fix(tui): panic when reading/writing clipboard on linux 2025-07-08 18:29:45 -05:00
Jay V 969154a473 docs: share page image 2025-07-08 19:24:21 -04:00
Jay V 4d6ca3fab1 docs: share page many model case 2025-07-08 19:08:33 -04:00
Dax Raad 00ea5082e7 add typescript lsp timeout if it fails to start
publish / publish (push) Has been cancelled
2025-07-08 18:33:12 -04:00
Dax Raad 4a878b88c0 properly load typescript lsp in subpaths
publish / publish (push) Has been cancelled
2025-07-08 18:18:45 -04:00
Dax Raad 6de955847c big rework of LSP system 2025-07-08 18:14:49 -04:00
Jay V 3ba5d528b4 docs: share bugs 2025-07-08 18:14:36 -04:00
Jay V f99e2b3429 docs: share error part 2025-07-08 18:00:08 -04:00
Jay V 7e4e6f6e51 docs: share page bugs 2025-07-08 17:18:38 -04:00
Jay V 0514f3f43b docs: share image model 2025-07-08 17:18:38 -04:00
Timo Clasen 1e07384364 fix: make compact command interruptible (#691)
publish / publish (push) Has been cancelled
Co-authored-by: GitHub Action <action@github.com>
2025-07-08 15:37:25 -05:00
strager 4c4739c422 fix(tool): fix ripgrep invocation on Windows (#700)
Co-authored-by: Adam <2363879+adamdotdevin@users.noreply.github.com>
2025-07-08 15:36:26 -05:00
Rami Chowdhury 2d8b90a6ff feat(storage): ensure storage directory exists and handle paths correctly (#771) 2025-07-08 15:34:11 -05:00
Robb Currall a2fa7ffa42 fix: support cancelled task state (#775) 2025-07-08 15:33:39 -05:00
Frank Denis f7d6175283 Add support for the Zig Language Server (ZLS) (#756) 2025-07-08 15:31:11 -05:00
Tommy 9ed187ee52 docs: add terminal requirements (#708) 2025-07-08 15:30:05 -05:00
Gal Schlezinger 14d81e574b [config json schema] declare default values and examples for in-ide documentation (#754) 2025-07-08 15:29:07 -05:00
adamdottv 6efe8cc8df fix: env has to be string
publish / publish (push) Has been cancelled
2025-07-08 14:59:03 -05:00
adamdottv daa5fc916a fix(tui): pasting causes panic on macos 2025-07-08 14:57:17 -05:00
adamdottv c659496b96 fix(tui): model/provider arg parsing 2025-07-08 14:11:57 -05:00
Timo Clasen 21fbf21cb6 fix(copilot): add vision request header (#773) 2025-07-08 14:01:54 -05:00
adamdottv f31cbf2744 fix: image reading
publish / publish (push) Has been cancelled
2025-07-08 13:02:13 -05:00
Aiden Cline 8322f18e03 fix: display errors when using opencode run ... (#751) 2025-07-08 10:38:11 -05:00
adamdottv 562bdb95e2 fix: include symlinks in ripgrep searches 2025-07-08 10:02:19 -05:00
Dax a57ce8365d Update STATS.md 2025-07-08 10:30:02 -04:00
adamdottv 0da83ae67e feat(tui): command aliases 2025-07-08 08:20:55 -05:00
adamdottv 662d022a48 feat(tui): paste images and pdfs 2025-07-08 08:09:01 -05:00
GitHub Action 9efef03919 ignore: update download stats 2025-07-08 2025-07-08 12:04:27 +00:00
GitHub Action 7a9fb3fa92 ignore: update download stats 2025-07-08 2025-07-08 10:51:06 +00:00
adamdottv ea96ead346 feat(tui): handle --model and --prompt flags 2025-07-08 05:50:18 -05:00
Dax Raad 6100a77b85 start file watcher only for tui 2025-07-07 21:05:04 -04:00
Dax Raad c7a59ee2b1 better handling of aborting sessions
publish / publish (push) Has been cancelled
2025-07-07 20:59:00 -04:00
Jay V a272b58fe9 docs: intro 2025-07-07 17:41:46 -04:00
Dax Raad 9948fcf1b6 fix crash when running on new project
publish / publish (push) Has been cancelled
2025-07-07 17:39:52 -04:00
Dax Raad 0d50c867ff fix mcp tools corrupting session
publish / publish (push) Has been cancelled
2025-07-07 17:05:16 -04:00
Dax Raad 27f7e02f12 run: truncate prompt 2025-07-07 16:41:42 -04:00
Jay V 0f93ecd564 docs: canonical url 2025-07-07 16:37:00 -04:00
576 changed files with 60581 additions and 10136 deletions
+2 -1
View File
@@ -17,10 +17,11 @@ jobs:
- uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.17
bun-version: 1.2.19
- run: bun install
- run: bun sst deploy --stage=${{ github.ref_name }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
+58
View File
@@ -0,0 +1,58 @@
name: Duplicate Issue Detection
on:
issues:
types: [opened]
jobs:
check-duplicates:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Check for duplicate issues
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: |
{
"bash": {
"gh issue*": "allow",
"*": "deny"
},
"webfetch": "deny"
}
run: |
opencode run -m anthropic/claude-sonnet-4-20250514 "A new issue has been created:'
Issue number:
${{ github.event.issue.number }}
Lookup this issue and search through existing issues (excluding #${{ github.event.issue.number }}) in this repository to find any potential duplicates of this new issue.
Consider:
1. Similar titles or descriptions
2. Same error messages or symptoms
3. Related functionality or components
4. Similar feature requests
If you find any potential duplicates, please comment on the new issue with:
- A brief explanation of why it might be a duplicate
- Links to the potentially duplicate issues
- A suggestion to check those issues first
Use this format for the comment:
'This issue might be a duplicate of existing issues. Please check:
- #[issue_number]: [brief description of similarity]
Feel free to ignore if none of these address your specific case.'
If no clear duplicates are found, do not comment."
+53
View File
@@ -0,0 +1,53 @@
name: Guidelines Check
on:
# Disabled - uncomment to re-enable
# pull_request_target:
# types: [opened, synchronize]
jobs:
check-guidelines:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Install opencode
run: curl -fsSL https://opencode.ai/install | bash
- name: Check PR guidelines compliance
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENCODE_PERMISSION: '{ "bash": { "gh*": "allow", "gh pr review*": "deny", "*": "deny" } }'
run: |
opencode run -m anthropic/claude-sonnet-4-20250514 "A new pull request has been created: '${{ github.event.pull_request.title }}'
<pr-number>
${{ github.event.pull_request.number }}
</pr-number>
<pr-description>
${{ github.event.pull_request.body }}
</pr-description>
Please check all the code changes in this pull request against the guidelines in AGENTS.md file in this repository. Diffs are important but make sure you read the entire file to get proper context. Make it clear the suggestions are merely suggestions and the human can decide what to do
Use the gh cli to create comments on the files for the violations. Try to leave the comment on the exact line number. If you have a suggested fix include it in a suggestion code block.
Command MUST be like this.
```
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments \
-f 'body=[summary of issue]' -f 'commit_id=${{ github.event.pull_request.head.sha }}' -f 'path=[path-to-file]' -F "line=[line]" -f 'side=RIGHT'
```
Only create comments for actual violations. If the code follows all guidelines, don't run any gh commands."
+14
View File
@@ -0,0 +1,14 @@
name: discord
on:
release:
types: [published] # fires only when a release is published
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Send nicely-formatted embed to Discord
uses: SethCohen/github-releases-to-discord@v1
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
+27
View File
@@ -0,0 +1,27 @@
name: opencode
on:
issue_comment:
types: [created]
jobs:
opencode:
if: |
contains(github.event.comment.body, ' /oc') ||
contains(github.event.comment.body, ' /opencode')
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: read
issues: read
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run opencode
uses: sst/opencode/github@latest
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/sonic
@@ -0,0 +1,30 @@
name: publish-github-action
on:
workflow_dispatch:
push:
tags:
- "github-v*.*.*"
- "!github-v1"
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- run: git fetch --force --tags
- name: Publish
run: |
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
./script/publish
working-directory: ./github
+36
View File
@@ -0,0 +1,36 @@
name: publish-vscode
on:
workflow_dispatch:
push:
tags:
- "vscode-v*.*.*"
concurrency: ${{ github.workflow }}-${{ github.ref }}
permissions:
contents: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.19
- run: git fetch --force --tags
- run: bun install -g @vscode/vsce
- name: Publish
run: |
bun install
./script/publish
working-directory: ./sdks/vscode
env:
VSCE_PAT: ${{ secrets.VSCE_PAT }}
OPENVSX_TOKEN: ${{ secrets.OPENVSX_TOKEN }}
+24 -15
View File
@@ -1,12 +1,17 @@
name: publish
run-name: "${{ format('v{0}', inputs.version) }}"
on:
workflow_dispatch:
push:
branches:
- dev
tags:
- "*"
inputs:
version:
description: "Version to publish"
required: true
type: string
title:
description: "Custom title for this run"
required: false
type: string
concurrency: ${{ github.workflow }}-${{ github.ref }}
@@ -32,31 +37,35 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.17
bun-version: 1.2.19
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v3
with:
path: ~/.bun
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
- name: Install makepkg
run: |
sudo apt-get update
sudo apt-get install -y pacman-package-manager
- name: Setup SSH for AUR
run: |
mkdir -p ~/.ssh
echo "${{ secrets.AUR_KEY }}" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts
git config --global user.email "opencode@sst.dev"
git config --global user.name "opencode"
ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts || true
- name: Install dependencies
run: bun install
- name: Publish
run: |
bun install
if [ "${{ startsWith(github.ref, 'refs/tags/') }}" = "true" ]; then
./script/publish.ts
else
./script/publish.ts --snapshot
fi
working-directory: ./packages/opencode
OPENCODE_VERSION=${{ inputs.version }} ./script/publish.ts
env:
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
+3 -1
View File
@@ -21,7 +21,7 @@ jobs:
bun-version: latest
- name: Run stats script
run: bun scripts/stats.ts
run: bun script/stats.ts
- name: Commit stats
run: |
@@ -30,3 +30,5 @@ jobs:
git add STATS.md
git diff --staged --quiet || git commit -m "ignore: update download stats $(date -I)"
git push
env:
POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
+24
View File
@@ -0,0 +1,24 @@
name: Typecheck
on:
pull_request:
branches: [dev]
workflow_dispatch:
jobs:
typecheck:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.19
- name: Install dependencies
run: bun install
- name: Run typecheck
run: bun typecheck
+2 -1
View File
@@ -1,8 +1,9 @@
.DS_Store
node_modules
.opencode
.sst
.env
.idea
.vscode
openapi.json
playground
tmp
+31
View File
@@ -0,0 +1,31 @@
---
description: ALWAYS use this when writing docs
---
You are an expert technical documentation writer
You are not verbose
The title of the page should be a word or a 2-3 word phrase
The description should be one short line, should not start with "The", should
avoid repeating the title of the page, should be 5-10 words long
Chunks of text should not be more than 2 sentences long
Each section is spearated by a divider of 3 dashes
The section titles are short with only the first letter of the word capitalized
The section titles are in the imperative mood
The section titles should not repeat the term used in the page title, for
example, if the page title is "Models", avoid using a section title like "Add
new models". This might be unavoidable in some cases, but try to avoid it.
Check out the /packages/web/src/content/docs/docs/index.mdx as an example.
For JS or TS code snippets remove trailing semicolons and any trailing commas
that might not be needed.
If you are making a commit prefix the commit message with `docs:`
+10
View File
@@ -0,0 +1,10 @@
---
description: Use this agent when you are asked to commit and push code changes to a git repository.
mode: subagent
---
You commit and push to git
Commit messages should be brief since they are used to generate release notes.
Messages should say WHY the change was made and not WHAT was changed.
+9
View File
@@ -0,0 +1,9 @@
commit and push
make sure it includes a prefix like
docs:
tui:
core:
ci:
ignore:
wip:
+8
View File
@@ -0,0 +1,8 @@
---
description: hello world
---
hey there $ARGUMENTS
!`ls`
check out @README.md
+12
View File
@@ -0,0 +1,12 @@
## IMPORTANT
- Try to keep things in one function unless composable or reusable
- DO NOT do unnecessary destructuring of variables
- DO NOT use `else` statements unless necessary
- DO NOT use `try`/`catch` if it can be avoided
- AVOID `try`/`catch` where possible
- AVOID `else` statements
- AVOID using `any` type
- AVOID `let` statements
- PREFER single word variable names where possible
- Use as many bun apis as possible like Bun.file()
+40 -9
View File
@@ -9,7 +9,7 @@
</p>
<p align="center">AI coding agent, built for the terminal.</p>
<p align="center">
<a href="https://discord.gg/opencode"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://opencode.ai/discord"><img alt="Discord" src="https://img.shields.io/discord/1391832426048651334?style=flat-square&label=discord" /></a>
<a href="https://www.npmjs.com/package/opencode-ai"><img alt="npm" src="https://img.shields.io/npm/v/opencode-ai?style=flat-square" /></a>
<a href="https://github.com/sst/opencode/actions/workflows/publish.yml"><img alt="Build status" src="https://img.shields.io/github/actions/workflow/status/sst/opencode/publish.yml?style=flat-square&branch=dev" /></a>
</p>
@@ -26,11 +26,27 @@ curl -fsSL https://opencode.ai/install | bash
# Package managers
npm i -g opencode-ai@latest # or bun/pnpm/yarn
brew install sst/tap/opencode # macOS
brew install sst/tap/opencode # macOS and Linux
paru -S opencode-bin # Arch Linux
```
> **Note:** Remove versions older than 0.1.x before installing
> [!TIP]
> Remove versions older than 0.1.x before installing.
#### Installation Directory
The install script respects the following priority order for the installation path:
1. `$OPENCODE_INSTALL_DIR` - Custom installation directory
2. `$XDG_BIN_DIR` - XDG Base Directory Specification compliant path
3. `$HOME/bin` - Standard user binary directory (if exists or can be created)
4. `$HOME/.opencode/bin` - Default fallback
```bash
# Examples
OPENCODE_INSTALL_DIR=/usr/local/bin curl -fsSL https://opencode.ai/install | bash
XDG_BIN_DIR=$HOME/.local/bin curl -fsSL https://opencode.ai/install | bash
```
### Documentation
@@ -38,10 +54,25 @@ For more info on how to configure opencode [**head over to our docs**](https://o
### Contributing
For any new features we'd appreciate it if you could open an issue first to discuss what you'd like to implement. We're pretty responsive there and it'll save you from working on something that we don't end up using. No need to do this for simpler fixes.
opencode is an opinionated tool so any fundamental feature needs to go through a
design process with the core team.
> **Note**: Please talk to us via github issues before spending time working on
> a new feature
> [!IMPORTANT]
> We do not accept PRs for core features.
However we still merge a ton of PRs - you can contribute:
- Bug fixes
- Improvements to LLM performance
- Support for new providers
- Fixes for env specific quirks
- Missing standard behavior
- Documentation
Take a look at the git history to see what kind of PRs we end up merging.
> [!NOTE]
> If you do not follow the above guidelines we might close your PR.
To run opencode locally you need.
@@ -52,7 +83,7 @@ And run.
```bash
$ bun install
$ bun run packages/opencode/src/index.ts
$ bun dev
```
#### Development Notes
@@ -66,7 +97,7 @@ $ bun run packages/opencode/src/index.ts
It's very similar to Claude Code in terms of capability. Here are the key differences:
- 100% open source
- Not coupled to any provider. Although Anthropic is recommended, opencode can be used with OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider agnostic is important.
- Not coupled to any provider. Although Anthropic is recommended, opencode can be used with OpenAI, Google or even local models. As models evolve the gaps between them will close and pricing will drop so being provider-agnostic is important.
- A focus on TUI. opencode is built by neovim users and the creators of [terminal.shop](https://terminal.shop); we are going to push the limits of what's possible in the terminal.
- A client/server architecture. This for example can allow opencode to run on your computer, while you can drive it remotely from a mobile app. Meaning that the TUI frontend is just one of the possible clients.
@@ -76,4 +107,4 @@ The other confusingly named repo has no relation to this one. You can [read the
---
**Join our community** [Discord](https://discord.gg/opencode) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/SST_dev)
**Join our community** [Discord](https://discord.gg/opencode) | [YouTube](https://www.youtube.com/c/sst-dev) | [X.com](https://x.com/anomaly_inv)
+59 -10
View File
@@ -1,12 +1,61 @@
# Download Stats
| Date | GitHub Downloads | npm Downloads | Total |
| ---------- | ---------------- | --------------- | --------------- |
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
| Date | GitHub Downloads | npm Downloads | Total |
| ---------- | ---------------- | ---------------- | ----------------- |
| 2025-06-29 | 18,789 (+0) | 39,420 (+0) | 58,209 (+0) |
| 2025-06-30 | 20,127 (+1,338) | 41,059 (+1,639) | 61,186 (+2,977) |
| 2025-07-01 | 22,108 (+1,981) | 43,745 (+2,686) | 65,853 (+4,667) |
| 2025-07-02 | 24,814 (+2,706) | 46,168 (+2,423) | 70,982 (+5,129) |
| 2025-07-03 | 27,834 (+3,020) | 49,955 (+3,787) | 77,789 (+6,807) |
| 2025-07-04 | 30,608 (+2,774) | 54,758 (+4,803) | 85,366 (+7,577) |
| 2025-07-05 | 32,524 (+1,916) | 58,371 (+3,613) | 90,895 (+5,529) |
| 2025-07-06 | 33,766 (+1,242) | 59,694 (+1,323) | 93,460 (+2,565) |
| 2025-07-08 | 38,052 (+4,286) | 64,468 (+4,774) | 102,520 (+9,060) |
| 2025-07-09 | 40,924 (+2,872) | 67,935 (+3,467) | 108,859 (+6,339) |
| 2025-07-10 | 43,796 (+2,872) | 71,402 (+3,467) | 115,198 (+6,339) |
| 2025-07-11 | 46,982 (+3,186) | 77,462 (+6,060) | 124,444 (+9,246) |
| 2025-07-12 | 49,302 (+2,320) | 82,177 (+4,715) | 131,479 (+7,035) |
| 2025-07-13 | 50,803 (+1,501) | 86,394 (+4,217) | 137,197 (+5,718) |
| 2025-07-14 | 53,283 (+2,480) | 87,860 (+1,466) | 141,143 (+3,946) |
| 2025-07-15 | 57,590 (+4,307) | 91,036 (+3,176) | 148,626 (+7,483) |
| 2025-07-16 | 62,313 (+4,723) | 95,258 (+4,222) | 157,571 (+8,945) |
| 2025-07-17 | 66,684 (+4,371) | 100,048 (+4,790) | 166,732 (+9,161) |
| 2025-07-18 | 70,379 (+3,695) | 102,587 (+2,539) | 172,966 (+6,234) |
| 2025-07-19 | 73,497 (+3,117) | 105,904 (+3,317) | 179,401 (+6,434) |
| 2025-07-20 | 76,453 (+2,956) | 109,044 (+3,140) | 185,497 (+6,096) |
| 2025-07-21 | 80,197 (+3,744) | 113,537 (+4,493) | 193,734 (+8,237) |
| 2025-07-22 | 84,251 (+4,054) | 118,073 (+4,536) | 202,324 (+8,590) |
| 2025-07-23 | 88,589 (+4,338) | 121,436 (+3,363) | 210,025 (+7,701) |
| 2025-07-24 | 92,469 (+3,880) | 124,091 (+2,655) | 216,560 (+6,535) |
| 2025-07-25 | 96,417 (+3,948) | 126,985 (+2,894) | 223,402 (+6,842) |
| 2025-07-26 | 100,646 (+4,229) | 131,411 (+4,426) | 232,057 (+8,655) |
| 2025-07-27 | 102,644 (+1,998) | 134,736 (+3,325) | 237,380 (+5,323) |
| 2025-07-28 | 105,446 (+2,802) | 136,016 (+1,280) | 241,462 (+4,082) |
| 2025-07-29 | 108,998 (+3,552) | 137,542 (+1,526) | 246,540 (+5,078) |
| 2025-07-30 | 113,544 (+4,546) | 140,317 (+2,775) | 253,861 (+7,321) |
| 2025-07-31 | 118,339 (+4,795) | 143,344 (+3,027) | 261,683 (+7,822) |
| 2025-08-01 | 123,539 (+5,200) | 146,680 (+3,336) | 270,219 (+8,536) |
| 2025-08-02 | 127,864 (+4,325) | 149,236 (+2,556) | 277,100 (+6,881) |
| 2025-08-03 | 131,397 (+3,533) | 150,451 (+1,215) | 281,848 (+4,748) |
| 2025-08-04 | 136,266 (+4,869) | 153,260 (+2,809) | 289,526 (+7,678) |
| 2025-08-05 | 141,596 (+5,330) | 155,752 (+2,492) | 297,348 (+7,822) |
| 2025-08-06 | 147,067 (+5,471) | 158,309 (+2,557) | 305,376 (+8,028) |
| 2025-08-07 | 152,591 (+5,524) | 160,889 (+2,580) | 313,480 (+8,104) |
| 2025-08-08 | 158,187 (+5,596) | 163,448 (+2,559) | 321,635 (+8,155) |
| 2025-08-09 | 162,770 (+4,583) | 165,721 (+2,273) | 328,491 (+6,856) |
| 2025-08-10 | 165,695 (+2,925) | 167,109 (+1,388) | 332,804 (+4,313) |
| 2025-08-11 | 169,297 (+3,602) | 167,953 (+844) | 337,250 (+4,446) |
| 2025-08-12 | 176,307 (+7,010) | 171,876 (+3,923) | 348,183 (+10,933) |
| 2025-08-13 | 182,997 (+6,690) | 177,182 (+5,306) | 360,179 (+11,996) |
| 2025-08-14 | 189,063 (+6,066) | 179,741 (+2,559) | 368,804 (+8,625) |
| 2025-08-15 | 193,608 (+4,545) | 181,792 (+2,051) | 375,400 (+6,596) |
| 2025-08-16 | 198,118 (+4,510) | 184,558 (+2,766) | 382,676 (+7,276) |
| 2025-08-17 | 201,299 (+3,181) | 186,269 (+1,711) | 387,568 (+4,892) |
| 2025-08-18 | 204,559 (+3,260) | 187,399 (+1,130) | 391,958 (+4,390) |
| 2025-08-19 | 209,814 (+5,255) | 189,668 (+2,269) | 399,482 (+7,524) |
| 2025-08-20 | 214,497 (+4,683) | 191,481 (+1,813) | 405,978 (+6,496) |
| 2025-08-21 | 220,465 (+5,968) | 194,784 (+3,303) | 415,249 (+9,271) |
| 2025-08-22 | 225,899 (+5,434) | 197,204 (+2,420) | 423,103 (+7,854) |
| 2025-08-23 | 229,005 (+3,106) | 199,238 (+2,034) | 428,243 (+5,140) |
| 2025-08-24 | 232,098 (+3,093) | 201,157 (+1,919) | 433,255 (+5,012) |
| 2025-08-25 | 236,607 (+4,509) | 202,650 (+1,493) | 439,257 (+6,002) |
+2409 -275
View File
File diff suppressed because it is too large Load Diff
+28
View File
@@ -0,0 +1,28 @@
dist
.wrangler
.output
.vercel
.netlify
.vinxi
app.config.timestamp_*.js
# Environment
.env
.env*.local
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
*.launch
.settings/
# Temp
gitignore
# System Files
.DS_Store
Thumbs.db
+149
View File
@@ -0,0 +1,149 @@
---
description: use whenever you are styling a ui with css
---
you are very good at writing clean maintainable css using modern techniques
css is structured like this
```css
[data-page="home"] {
[data-component="header"] {
[data-slot="logo"] {
}
}
}
```
top level pages are scoped using `data-page`
pages can break down into components using `data-component`
components can break down into slots using `data-slot`
structure things so that this hierarchy is followed IN YOUR CSS - you should rarely need to
nest components inside other components. you should NEVER nest components inside
slots. you should NEVER nest slots inside other slots.
**IMPORTANT: This hierarchy rule applies to CSS structure, NOT JSX/DOM structure.**
The hierarchy in css file does NOT have to match the hierarchy in the dom - you
can put components or slots at the same level in CSS even if one goes inside another in the DOM.
Your JSX can nest however makes semantic sense - components can be inside slots,
slots can contain components, etc. The DOM structure should be whatever makes the most
semantic and functional sense.
It is more important to follow the pages -> components -> slots structure IN YOUR CSS,
while keeping your JSX/DOM structure logical and semantic.
use data attributes to represent different states of the component
```css
[data-component="modal"] {
opacity: 0;
&[data-state="open"] {
opacity: 1;
}
}
```
this will allow jsx to control the syling
avoid selectors that just target an element type like `> span` you should assign
it a slot name. it's ok to do this sometimes where it makes sense semantically
like targeting `li` elements in a list
in terms of file structure `./src/style/` contains all universal styling rules.
these should not contain anything specific to a page
`./src/style/token` contains all the tokens used in the project
`./src/style/component` is for reusable components like buttons or inputs
page specific styles should go next to the page they are styling so
`./src/routes/about.tsx` should have its styles in `./src/routes/about.css`
`about.css` should be scoped using `data-page="about"`
## Example of correct implementation
JSX can nest however makes sense semantically:
```jsx
<div data-slot="left">
<div data-component="title">Section Title</div>
<div data-slot="content">Content here</div>
</div>
```
CSS maintains clean hierarchy regardless of DOM nesting:
```css
[data-page="home"] {
[data-component="screenshots"] {
[data-slot="left"] {
/* styles */
}
[data-slot="content"] {
/* styles */
}
}
[data-component="title"] {
/* can be at same level even though nested in DOM */
}
}
```
## Reusable Components
If a component is reused across multiple sections of the same page, define it at the page level:
```jsx
<!-- Used in multiple places on the same page -->
<section data-component="install">
<div data-component="method">
<h3 data-component="title">npm</h3>
</div>
<div data-component="method">
<h3 data-component="title">bun</h3>
</div>
</section>
<section data-component="screenshots">
<div data-slot="left">
<div data-component="title">Screenshot Title</div>
</div>
</section>
```
```css
[data-page="home"] {
/* Reusable title component defined at page level since it's used in multiple components */
[data-component="title"] {
text-transform: uppercase;
font-weight: 400;
}
[data-component="install"] {
/* install-specific styles */
}
[data-component="screenshots"] {
/* screenshots-specific styles */
}
}
```
This is correct because the `title` component has consistent styling and behavior across the page.
## Key Clarifications
1. **JSX Nesting is Flexible**: Components can be nested inside slots, slots can contain components - whatever makes semantic sense
2. **CSS Hierarchy is Strict**: Follow pages → components → slots structure in CSS
3. **Reusable Components**: Define at the appropriate level where they're shared (page level if used across the page, component level if only used within that component)
4. **DOM vs CSS Structure**: These don't need to match - optimize each for its purpose
See ./src/routes/index.css and ./src/routes/index.tsx for a complete example.
+32
View File
@@ -0,0 +1,32 @@
# SolidStart
Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
## Creating a project
```bash
# create a new project in the current directory
npm init solid@latest
# create a new project in my-app
npm init solid@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
Solid apps are built with _presets_, which optimise your project for deployment to different environments.
By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add it to the `devDependencies` in `package.json` and specify in your `app.config.js`.
## This project was created with the [Solid CLI](https://github.com/solidjs-community/solid-cli)
+9
View File
@@ -0,0 +1,9 @@
import { defineConfig } from "@solidjs/start/config"
export default defineConfig({
vite: {
server: {
allowedHosts: true,
},
},
})
+23
View File
@@ -0,0 +1,23 @@
{
"name": "@opencode/cloud-app",
"type": "module",
"scripts": {
"dev": "vinxi dev --host 0.0.0.0",
"build": "vinxi build",
"start": "vinxi start",
"version": "0.5.27"
},
"dependencies": {
"@ibm/plex": "6.4.1",
"@openauthjs/openauth": "0.0.0-20250322224806",
"@solidjs/meta": "^0.29.4",
"@solidjs/router": "^0.15.0",
"@solidjs/start": "^1.1.0",
"solid-js": "^1.9.5",
"vinxi": "^0.5.7",
"@opencode/cloud-core": "workspace:*"
},
"engines": {
"node": ">=22"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 664 B

+1
View File
@@ -0,0 +1 @@
@import "./style/index.css";
+23
View File
@@ -0,0 +1,23 @@
import { MetaProvider, Title } from "@solidjs/meta";
import { Router } from "@solidjs/router";
import { FileRoutes } from "@solidjs/start/router";
import { ErrorBoundary, Suspense } from "solid-js";
import "@ibm/plex/css/ibm-plex.css";
import "./app.css";
export default function App() {
return (
<Router
root={props => (
<MetaProvider>
<Title>SolidStart - Basic</Title>
<ErrorBoundary fallback={<div>Something went wrong</div>}>
<Suspense>{props.children}</Suspense>
</ErrorBoundary>
</MetaProvider>
)}
>
<FileRoutes />
</Router>
);
}
+19
View File
@@ -0,0 +1,19 @@
<svg width="289" height="50" viewBox="0 0 289 50" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.5 16.5H24.5V33H8.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M48.5 16.5H64.5V33H48.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M120.5 16.5H136.5V33H120.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M160.5 16.5H176.5V33H160.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M192.5 16.5H208.5V33H192.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M232.5 16.5H248.5V33H232.5V16.5Z" fill="white" fill-opacity="0.2"/>
<path d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z" fill="white" fill-opacity="0.95"/>
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="white" fill-opacity="0.95"/>
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="white" fill-opacity="0.95"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z" fill="white" fill-opacity="0.95"/>
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="white" fill-opacity="0.5"/>
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="white" fill-opacity="0.5"/>
<path d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z" fill="white" fill-opacity="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z" fill="white" fill-opacity="0.5"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z" fill="white" fill-opacity="0.5"/>
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="white" fill-opacity="0.95"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 998 KiB

+24
View File
@@ -0,0 +1,24 @@
import { JSX } from "solid-js"
export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg
{...props}
viewBox="0 0 512 512" >
<rect width="336" height="336" x="128" y="128" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32" rx="57" ry="57"></rect>
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="m383.5 128l.5-24a56.16 56.16 0 0 0-56-56H112a64.19 64.19 0 0 0-64 64v216a56.16 56.16 0 0 0 56 56h24"></path>
</svg>
)
}
export function IconCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
return (
<svg
{...props}
viewBox="0 0 24 24" >
<path fill="currentColor" d="M9 16.17L5.53 12.7a.996.996 0 1 0-1.41 1.41l4.18 4.18c.39.39 1.02.39 1.41 0L20.29 7.71a.996.996 0 1 0-1.41-1.41z"></path>
</svg>
)
}
+109
View File
@@ -0,0 +1,109 @@
import { useSession } from "vinxi/http"
import { createClient } from "@openauthjs/openauth/client"
import { getRequestEvent } from "solid-js/web"
import { and, Database, eq, inArray } from "@opencode/cloud-core/drizzle/index.js"
import { WorkspaceTable } from "@opencode/cloud-core/schema/workspace.sql.js"
import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
import { query, redirect } from "@solidjs/router"
import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js"
import { Actor } from "@opencode/cloud-core/actor.js"
export async function withActor<T>(fn: () => T) {
const actor = await getActor()
return Actor.provide(actor.type, actor.properties, fn)
}
export const getActor = query(async (): Promise<Actor.Info> => {
"use server"
const evt = getRequestEvent()
const url = new URL(evt!.request.headers.get("referer") ?? evt!.request.url)
const auth = await useAuthSession()
const [workspaceHint] = url.pathname.split("/").filter((x) => x.length > 0)
if (!workspaceHint) {
if (auth.data.current) {
const current = auth.data.account[auth.data.current]
return {
type: "account",
properties: {
email: current.email,
accountID: current.id,
},
}
}
if (Object.keys(auth.data.account).length > 0) {
const current = Object.values(auth.data.account)[0]
await auth.update(val => ({
...val,
current: current.id,
}))
return {
type: "account",
properties: {
email: current.email,
accountID: current.id,
},
}
}
return {
type: "public",
properties: {},
}
}
const accounts = Object.keys(auth.data.account)
const result = await Database.transaction(async (tx) => {
return await tx.select({
user: UserTable
})
.from(AccountTable)
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
.where(
and(
inArray(AccountTable.id, accounts),
eq(WorkspaceTable.id, workspaceHint),
)
)
.limit(1)
.execute()
.then((x) => x[0])
})
if (result) {
return {
type: "user",
properties: {
userID: result.user.id,
workspaceID: result.user.workspaceID,
},
}
}
throw redirect("/auth/authorize")
}, "actor")
export const AuthClient = createClient({
clientID: "app",
issuer: import.meta.env.VITE_AUTH_URL,
})
export interface AuthSession {
account: Record<string, {
id: string
email: string
}>
current?: string
}
export function useAuthSession() {
return useSession<AuthSession>({
password: "0".repeat(32),
name: "auth"
})
}
export function AuthProvider() {
}
+4
View File
@@ -0,0 +1,4 @@
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client";
mount(() => <StartClient />, document.getElementById("app")!);
+21
View File
@@ -0,0 +1,21 @@
// @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server";
export default createHandler(() => (
<StartServer
document={({ assets, children, scripts }) => (
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
{assets}
</head>
<body data-color-mode="dark">
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
));
+1
View File
@@ -0,0 +1 @@
/// <reference types="@solidjs/start/env" />
+19
View File
@@ -0,0 +1,19 @@
import { Title } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
export default function NotFound() {
return (
<main>
<Title>Not Found</Title>
<HttpStatusCode code={404} />
<h1>Page Not Found</h1>
<p>
Visit{" "}
<a href="https://start.solidjs.com" target="_blank">
start.solidjs.com
</a>{" "}
to learn how to build SolidStart apps.
</p>
</main>
);
}
+15
View File
@@ -0,0 +1,15 @@
import { createAsync, query } from "@solidjs/router"
import { getActor, withActor } from "~/context/auth"
const getPosts = query(async () => {
"use server"
return withActor(() => {
return "ok"
})
}, "posts")
export default function () {
const actor = createAsync(async () => getActor())
return <div>{JSON.stringify(actor())}</div>
}
+7
View File
@@ -0,0 +1,7 @@
import type { APIEvent } from "@solidjs/start/server"
import { AuthClient } from "~/context/auth"
export async function GET(input: APIEvent) {
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
return Response.redirect(result.url, 302)
}
+31
View File
@@ -0,0 +1,31 @@
import type { APIEvent } from "@solidjs/start/server"
import { AuthClient, useAuthSession } from "~/context/auth"
export async function GET(input: APIEvent) {
const url = new URL(input.request.url)
const code = url.searchParams.get("code")
if (!code) throw new Error("No code found")
const result = await AuthClient.exchange(code, `${url.origin}${url.pathname}`)
if (result.err) {
throw new Error(result.err.message)
}
const decoded = AuthClient.decode(result.tokens.access, {} as any)
if (decoded.err) throw new Error(decoded.err.message)
const session = await useAuthSession()
const id = decoded.subject.properties.accountID
await session.update((value) => {
return {
...value,
account: {
[id]: {
id,
email: decoded.subject.properties.email,
},
},
current: id,
}
})
return {
result,
}
}
+257
View File
@@ -0,0 +1,257 @@
[data-page="home"] {
--color-bg: oklch(0.2097 0.008 274.53);
--color-border: oklch(0.46 0.02 269.88);
--color-text: #ffffff;
--color-text-secondary: oklch(0.72 0.01 270.15);
--color-text-dimmed: hsl(224, 7%, 46%);
padding: var(--space-6);
font-family: var(--font-mono);
color: var(--color-text);
a {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: var(--space-0-75);
}
background: var(--color-bg);
position: fixed;
overflow-y: scroll;
inset: 0;
[data-component="content"] {
max-width: 67.5rem;
margin: 0 auto;
border: 2px solid var(--color-border);
}
[data-component="top"] {
padding: var(--space-12);
display: flex;
flex-direction: column;
align-items: start;
gap: var(--space-4);
[data-slot="logo"] {
height: 70px;
}
[data-slot="title"] {
font-size: var(--font-size-2xl);
text-transform: uppercase;
}
}
[data-component="cta"] {
height: var(--space-19);
border-top: 2px solid var(--color-border);
display: flex;
[data-slot="left"] {
display: flex;
padding: 0 var(--space-12);
text-transform: uppercase;
text-decoration: underline;
align-items: center;
justify-content: center;
text-underline-offset: var(--space-0-75);
border-right: 2px solid var(--color-border);
}
[data-slot="right"] {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2-5);
padding: 0 var(--space-6);
}
[data-slot="command"] {
all: unset;
display: flex;
align-items: center;
cursor: pointer;
color: var(--color-text-secondary);
font-size: var(--font-size-lg);
font-family: var(--font-mono);
gap: var(--space-2);
}
[data-slot="highlight"] {
color: var(--color-text);
font-weight: 500;
}
}
[data-component="features"] {
border-top: 2px solid var(--color-border);
padding: var(--space-12);
[data-slot="list"] {
padding-left: var(--space-4);
margin: 0;
list-style: disc;
li {
margin-bottom: var(--space-4);
strong {
text-transform: uppercase;
font-weight: 600;
}
}
li:last-child {
margin-bottom: 0;
}
}
}
[data-component="install"] {
border-top: 2px solid var(--color-border);
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
@media (max-width: 40rem) {
grid-template-columns: 1fr;
grid-template-rows: auto;
}
}
[data-component="title"] {
letter-spacing: -0.03125rem;
text-transform: uppercase;
font-weight: 400;
font-size: var(--font-size-md);
flex-shrink: 0;
color: oklch(0.55 0.02 269.87);
}
[data-component="method"] {
padding: var(--space-4) var(--space-6);
display: flex;
flex-direction: column;
align-items: start;
gap: var(--space-3);
&:nth-child(2) {
border-left: 2px solid var(--color-border);
}
&:nth-child(3) {
border-top: 2px solid var(--color-border);
}
&:nth-child(4) {
border-top: 2px solid var(--color-border);
border-left: 2px solid var(--color-border);
}
[data-slot="button"] {
all: unset;
cursor: pointer;
display: flex;
align-items: center;
color: var(--color-text-secondary);
gap: var(--space-2);
strong {
color: var(--color-text);
font-weight: 500;
}
}
}
[data-component="screenshots"] {
border-top: 2px solid var(--color-border);
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0;
[data-slot="left"] {
padding: var(--space-8) var(--space-6);
display: flex;
flex-direction: column;
img {
width: 100%;
height: auto;
}
}
[data-slot="right"] {
display: grid;
grid-template-rows: 1fr 1fr;
border-left: 2px solid var(--color-border);
}
[data-slot="filler"] {
display: flex;
flex-grow: 1;
align-items: center;
justify-content: center;
}
[data-slot="cell"] {
padding: var(--space-8) var(--space-6);
display: flex;
flex-direction: column;
gap: var(--space-4);
&:nth-child(2) {
border-top: 2px solid var(--color-border);
}
img {
width: 80%;
height: auto;
}
}
}
[data-component="copy-status"] {
[data-slot="copy"] {
display: block;
width: var(--space-4);
height: var(--space-4);
color: var(--color-text-dimmed);
[data-copied] & {
display: none;
}
}
[data-slot="check"] {
display: none;
width: var(--space-4);
height: var(--space-4);
color: white;
[data-copied] & {
display: block;
}
}
}
[data-component="footer"] {
border-top: 2px solid var(--color-border);
display: grid;
grid-template-columns: 1fr 1fr 1fr;
font-size: var(--font-size-lg);
height: var(--space-20);
[data-slot="cell"] {
display: flex;
align-items: center;
justify-content: center;
border-right: 2px solid var(--color-border);
text-transform: uppercase;
&:last-child {
border-right: none;
}
}
}
}
+186
View File
@@ -0,0 +1,186 @@
import { Title } from "@solidjs/meta"
import { onCleanup, onMount } from "solid-js"
import "./index.css"
import logo from "../asset/logo-ornate-dark.svg"
import IMG_SPLASH from "../asset/screenshot-splash.webp"
import IMG_VSCODE from "../asset/screenshot-vscode.webp"
import IMG_GITHUB from "../asset/screenshot-github.webp"
import { IconCopy, IconCheck } from "../component/icon"
import { createAsync, query, redirect, RouteDefinition } from "@solidjs/router"
import { getActor, withActor } from "~/context/auth"
import { Account } from "@opencode/cloud-core/account.js"
function CopyStatus() {
return (
<div data-component="copy-status">
<IconCopy data-slot="copy" />
<IconCheck data-slot="check" />
</div>
)
}
const isLoggedIn = query(async () => {
"use server"
const actor = await getActor()
if (actor.type === "account") {
const workspaces = await withActor(() => Account.workspaces())
throw redirect("/" + workspaces[0].id)
}
return
}, "isLoggedIn")
export default function Home() {
createAsync(() => isLoggedIn(), {
deferStream: true,
})
onMount(() => {
const commands = document.querySelectorAll("[data-copy]")
for (const button of commands) {
const callback = () => {
const text = button.textContent
if (text) {
navigator.clipboard.writeText(text)
button.setAttribute("data-copied", "")
setTimeout(() => {
button.removeAttribute("data-copied")
}, 1500)
}
}
button.addEventListener("click", callback)
onCleanup(() => {
button.removeEventListener("click", callback)
})
}
})
return (
<main data-page="home">
<Title>opencode | AI coding agent built for the terminal</Title>
<div data-component="content">
<section data-component="top">
<img data-slot="logo" src={logo} alt="logo" />
<h1 data-slot="title">The AI coding agent built for the terminal.</h1>
</section>
<section data-component="cta">
<div data-slot="left">
<a href="/docs">Get Started</a>
</div>
<div data-slot="right">
<button data-copy data-slot="command">
<span>
<span>curl -fsSL&nbsp;</span>
<span data-slot="protocol">https://</span>
<span data-slot="highlight">opencode.ai/install</span>
&nbsp;| bash
</span>
<CopyStatus />
</button>
</div>
</section>
<section data-component="features">
<ul data-slot="list">
<li>
<strong>Native TUI</strong>: A responsive, native, themeable terminal UI.
</li>
<li>
<strong>LSP enabled</strong>: Automatically loads the right LSPs for the LLM.
</li>
<li>
<strong>Multi-session</strong>: Start multiple agents in parallel on the same project.
</li>
<li>
<strong>Shareable links</strong>: Share a link to any sessions for reference or to debug.
</li>
<li>
<strong>Claude Pro</strong>: Log in with Anthropic to use your Claude Pro or Max account.
</li>
<li>
<strong>Use any model</strong>: Supports 75+ LLM providers through{" "}
<a href="https://models.dev">Models.dev</a>, including local models.
</li>
</ul>
</section>
<section data-component="install">
<div data-component="method">
<h3 data-component="title">npm</h3>
<button data-copy data-slot="button">
<span>
npm install -g&nbsp;<strong>opencode-ai</strong>
</span>
<CopyStatus />
</button>
</div>
<div data-component="method">
<h3 data-component="title">bun</h3>
<button data-copy data-slot="button">
<span>
bun install -g&nbsp;<strong>opencode-ai</strong>
</span>
<CopyStatus />
</button>
</div>
<div data-component="method">
<h3 data-component="title">homebrew</h3>
<button data-copy data-slot="button">
<span>
brew install&nbsp;<strong>sst/tap/opencode</strong>
</span>
<CopyStatus />
</button>
</div>
<div data-component="method">
<h3 data-component="title">paru</h3>
<button data-copy data-slot="button">
<span>
paru -S&nbsp;<strong>opencode-bin</strong>
</span>
<CopyStatus />
</button>
</div>
</section>
<section data-component="screenshots">
<div data-slot="left">
<div data-component="title">opencode TUI with tokyonight theme</div>
<div data-slot="filler">
<img src={IMG_SPLASH} alt="opencode TUI with tokyonight theme" />
</div>
</div>
<div data-slot="right">
<div data-slot="cell">
<div data-component="title">opencode in VS Code</div>
<div data-slot="filler">
<img src={IMG_VSCODE} alt="opencode in VS Code" />
</div>
</div>
<div data-slot="cell">
<div data-component="title">opencode in GitHub</div>
<div data-slot="filler">
<img src={IMG_GITHUB} alt="opencode in GitHub" />
</div>
</div>
</div>
</section>
<footer data-component="footer">
<div data-slot="cell">
<a href="https://github.com/sst/opencode">GitHub</a>
</div>
<div data-slot="cell">
<a href="https://opencode.ai/discord">Discord</a>
</div>
<div data-slot="cell">
<span>
©2025 <a href="https://anoma.ly">Anomaly Innovations</a>
</span>
</div>
</footer>
</div>
</main>
)
}
+8
View File
@@ -0,0 +1,8 @@
html {
color-scheme: dark;
line-height: 1;
}
body {
font-family: var(--font-sans);
}
+102
View File
@@ -0,0 +1,102 @@
[data-component="button"] {
display: inline-flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border: 1px solid transparent;
border-radius: var(--space-2);
font-family: var(--font-sans);
font-size: var(--font-size-md);
font-weight: 500;
line-height: 1.25;
cursor: pointer;
transition: all 0.2s ease-in-out;
text-decoration: none;
user-select: none;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
&:focus {
outline: none;
box-shadow: 0 0 0 2px var(--color-primary);
}
&[data-color="primary"] {
background-color: var(--color-primary);
color: var(--color-primary-text);
border-color: var(--color-primary);
&:hover:not(:disabled) {
background-color: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
&:active:not(:disabled) {
background-color: var(--color-primary-active);
border-color: var(--color-primary-active);
}
}
&[data-color="danger"] {
background-color: var(--color-danger);
color: var(--color-danger-text);
border-color: var(--color-danger);
&:hover:not(:disabled) {
background-color: var(--color-danger-hover);
border-color: var(--color-danger-hover);
}
&:active:not(:disabled) {
background-color: var(--color-danger-active);
border-color: var(--color-danger-active);
}
&:focus {
box-shadow: 0 0 0 2px var(--color-danger);
}
}
&[data-color="warning"] {
background-color: var(--color-warning);
color: var(--color-warning-text);
border-color: var(--color-warning);
&:hover:not(:disabled) {
background-color: var(--color-warning-hover);
border-color: var(--color-warning-hover);
}
&:active:not(:disabled) {
background-color: var(--color-warning-active);
border-color: var(--color-warning-active);
}
&:focus {
box-shadow: 0 0 0 2px var(--color-warning);
}
}
&[data-size="small"] {
padding: var(--space-2) var(--space-3);
font-size: var(--font-size-sm);
gap: var(--space-1-5);
}
&[data-size="large"] {
padding: var(--space-4) var(--space-6);
font-size: var(--font-size-lg);
gap: var(--space-3);
}
[data-slot="icon"] {
display: flex;
align-items: center;
width: 1em;
height: 1em;
}
}
+8
View File
@@ -0,0 +1,8 @@
@import "./token/color.css";
@import "./token/font.css";
@import "./token/space.css";
@import "./component/button.css";
@import "./reset.css";
@import "./base.css";
+76
View File
@@ -0,0 +1,76 @@
/* 1. Use a more-intuitive box-sizing model */
*,
*::before,
*::after {
box-sizing: border-box;
}
/* 2. Remove default margin */
* {
margin: 0;
}
/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
html {
interpolate-size: allow-keywords;
}
}
body {
/* 4. Add accessible line-height */
line-height: 1.5;
/* 5. Improve text rendering */
-webkit-font-smoothing: antialiased;
}
/* 6. Improve media defaults */
img,
picture,
video,
canvas,
svg {
display: block;
max-width: 100%;
}
/* 7. Inherit fonts for form controls */
input,
button,
textarea,
select {
font: inherit;
}
/* 8. Avoid text overflows */
p,
h1,
h2,
h3,
h4,
h5,
h6 {
overflow-wrap: break-word;
}
/* 9. Improve line wrapping */
p {
text-wrap: pretty;
}
h1,
h2,
h3,
h4,
h5,
h6 {
text-wrap: balance;
}
/*
10. Create a root stacking context
*/
#root,
#__next {
isolation: isolate;
}
+90
View File
@@ -0,0 +1,90 @@
body {
--color-white: #ffffff;
--color-black: #000000;
}
[data-color-mode="dark"] {
/* OpenCode theme colors */
--color-bg: #0c0c0e;
--color-bg-surface: #161618;
--color-bg-elevated: #1c1c1f;
--color-text: #ffffff;
--color-text-muted: #a1a1a6;
--color-text-disabled: #68686f;
--color-accent: #007aff;
--color-accent-hover: #0056b3;
--color-accent-active: #004085;
--color-success: #30d158;
--color-warning: #ff9f0a;
--color-danger: #ff453a;
--color-border: #38383a;
--color-border-muted: #2c2c2e;
/* Button colors */
--color-primary: var(--color-accent);
--color-primary-hover: var(--color-accent-hover);
--color-primary-active: var(--color-accent-active);
--color-primary-text: #ffffff;
--color-danger: #ff453a;
--color-danger-hover: #d70015;
--color-danger-active: #a50011;
--color-danger-text: #ffffff;
--color-warning: #ff9f0a;
--color-warning-hover: #cc7f08;
--color-warning-active: #995f06;
--color-warning-text: #000000;
/* Surface colors */
--color-surface: var(--color-bg-surface);
--color-surface-hover: var(--color-bg-elevated);
--color-border: var(--color-border);
}
[data-color-mode="light"] {
/* OpenCode light theme colors */
--color-bg: #ffffff;
--color-bg-surface: #f5f5f7;
--color-bg-elevated: #ffffff;
--color-text: #1d1d1f;
--color-text-muted: #6e6e73;
--color-text-disabled: #86868b;
--color-accent: #007aff;
--color-accent-hover: #0056b3;
--color-accent-active: #004085;
--color-success: #30d158;
--color-warning: #ff9f0a;
--color-danger: #ff3b30;
--color-border: #d2d2d7;
--color-border-muted: #e5e5ea;
/* Button colors */
--color-primary: var(--color-accent);
--color-primary-hover: var(--color-accent-hover);
--color-primary-active: var(--color-accent-active);
--color-primary-text: #ffffff;
--color-danger: #ff3b30;
--color-danger-hover: #d70015;
--color-danger-active: #a50011;
--color-danger-text: #ffffff;
--color-warning: #ff9f0a;
--color-warning-hover: #cc7f08;
--color-warning-active: #995f06;
--color-warning-text: #000000;
/* Surface colors */
--color-surface: var(--color-bg-surface);
--color-surface-hover: var(--color-bg-elevated);
--color-border: var(--color-border);
}
+18
View File
@@ -0,0 +1,18 @@
body {
--font-size-2xs: 0.6875rem;
--font-size-xs: 0.75rem;
--font-size-sm: 0.8125rem;
--font-size-md: 0.9375rem;
--font-size-lg: 1.125rem;
--font-size-xl: 1.25rem;
--font-size-2xl: 1.5rem;
--font-size-3xl: 1.875rem;
--font-size-4xl: 2.25rem;
--font-size-5xl: 3rem;
--font-size-6xl: 3.75rem;
--font-size-7xl: 4.5rem;
--font-size-8xl: 6rem;
--font-size-9xl: 8rem;
--font-mono: IBM Plex Mono;
--font-sans: Inter;
}
+42
View File
@@ -0,0 +1,42 @@
body {
--space-0: 0;
--space-px: 1px;
--space-0-5: 0.125rem;
--space-0-75: 0.1875rem;
--space-1: 0.25rem;
--space-1-5: 0.375rem;
--space-2: 0.5rem;
--space-2-5: 0.625rem;
--space-3: 0.75rem;
--space-3-5: 0.875rem;
--space-4: 1rem;
--space-4-5: 1.125rem;
--space-5: 1.25rem;
--space-6: 1.5rem;
--space-7: 1.75rem;
--space-8: 2rem;
--space-9: 2.25rem;
--space-10: 2.5rem;
--space-11: 2.75rem;
--space-12: 3rem;
--space-14: 3.5rem;
--space-16: 4rem;
--space-17: 4.25rem;
--space-18: 4.5rem;
--space-19: 4.75rem;
--space-20: 5rem;
--space-24: 6rem;
--space-28: 7rem;
--space-32: 8rem;
--space-36: 9rem;
--space-40: 10rem;
--space-44: 11rem;
--space-48: 12rem;
--space-52: 13rem;
--space-56: 14rem;
--space-60: 15rem;
--space-64: 16rem;
--space-72: 18rem;
--space-80: 20rem;
--space-96: 24rem;
}
+9
View File
@@ -0,0 +1,9 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+19
View File
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"allowJs": true,
"strict": true,
"noEmit": true,
"types": ["vinxi/types/client"],
"isolatedModules": true,
"paths": {
"~/*": ["./src/*"]
}
}
}
+20
View File
@@ -0,0 +1,20 @@
import { defineConfig } from "drizzle-kit"
import { Resource } from "sst"
export default defineConfig({
out: "./migrations/",
strict: true,
schema: ["./src/**/*.sql.ts"],
verbose: true,
dialect: "postgresql",
dbCredentials: {
database: Resource.Database.database,
host: Resource.Database.host,
user: Resource.Database.username,
password: Resource.Database.password,
port: Resource.Database.port,
ssl: {
rejectUnauthorized: false,
},
},
})
@@ -0,0 +1,66 @@
CREATE TABLE "billing" (
"id" varchar(30) NOT NULL,
"workspace_id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"customer_id" varchar(255),
"payment_method_id" varchar(255),
"payment_method_last4" varchar(4),
"balance" bigint NOT NULL,
"reload" boolean,
CONSTRAINT "billing_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
);
--> statement-breakpoint
CREATE TABLE "payment" (
"id" varchar(30) NOT NULL,
"workspace_id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"customer_id" varchar(255),
"payment_id" varchar(255),
"amount" bigint NOT NULL,
CONSTRAINT "payment_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
);
--> statement-breakpoint
CREATE TABLE "usage" (
"id" varchar(30) NOT NULL,
"workspace_id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"request_id" varchar(255),
"model" varchar(255) NOT NULL,
"input_tokens" integer NOT NULL,
"output_tokens" integer NOT NULL,
"reasoning_tokens" integer,
"cache_read_tokens" integer,
"cache_write_tokens" integer,
"cost" bigint NOT NULL,
CONSTRAINT "usage_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
);
--> statement-breakpoint
CREATE TABLE "user" (
"id" varchar(30) NOT NULL,
"workspace_id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"email" text NOT NULL,
"name" varchar(255) NOT NULL,
"time_seen" timestamp with time zone,
"color" integer,
CONSTRAINT "user_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
);
--> statement-breakpoint
CREATE TABLE "workspace" (
"id" varchar(30) PRIMARY KEY NOT NULL,
"slug" varchar(255),
"name" varchar(255),
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone
);
--> statement-breakpoint
ALTER TABLE "billing" ADD CONSTRAINT "billing_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "payment" ADD CONSTRAINT "payment_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "usage" ADD CONSTRAINT "usage_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "user" ADD CONSTRAINT "user_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "user_email" ON "user" USING btree ("workspace_id","email");--> statement-breakpoint
CREATE UNIQUE INDEX "slug" ON "workspace" USING btree ("slug");
@@ -0,0 +1,8 @@
CREATE TABLE "account" (
"id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"email" varchar(255) NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX "email" ON "account" USING btree ("email");
@@ -0,0 +1,14 @@
CREATE TABLE "key" (
"id" varchar(30) NOT NULL,
"workspace_id" varchar(30) NOT NULL,
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
"time_deleted" timestamp with time zone,
"user_id" text NOT NULL,
"name" varchar(255) NOT NULL,
"key" varchar(255) NOT NULL,
"time_used" timestamp with time zone,
CONSTRAINT "key_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
);
--> statement-breakpoint
ALTER TABLE "key" ADD CONSTRAINT "key_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "global_key" ON "key" USING btree ("key");
@@ -0,0 +1 @@
ALTER TABLE "usage" DROP COLUMN "request_id";
@@ -0,0 +1,461 @@
{
"id": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"billing_workspace_id_workspace_id_fk": {
"name": "billing_workspace_id_workspace_id_fk",
"tableFrom": "billing",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"payment_workspace_id_workspace_id_fk": {
"name": "payment_workspace_id_workspace_id_fk",
"tableFrom": "payment",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"request_id": {
"name": "request_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"usage_workspace_id_workspace_id_fk": {
"name": "usage_workspace_id_workspace_id_fk",
"tableFrom": "usage",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"color": {
"name": "color",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
{
"expression": "workspace_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"user_workspace_id_workspace_id_fk": {
"name": "user_workspace_id_workspace_id_fk",
"tableFrom": "user",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
@@ -0,0 +1,515 @@
{
"id": "bf9e9084-4073-4ecb-8e56-5610816c9589",
"prevId": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"billing_workspace_id_workspace_id_fk": {
"name": "billing_workspace_id_workspace_id_fk",
"tableFrom": "billing",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"payment_workspace_id_workspace_id_fk": {
"name": "payment_workspace_id_workspace_id_fk",
"tableFrom": "payment",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"request_id": {
"name": "request_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"usage_workspace_id_workspace_id_fk": {
"name": "usage_workspace_id_workspace_id_fk",
"tableFrom": "usage",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"color": {
"name": "color",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
{
"expression": "workspace_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"user_workspace_id_workspace_id_fk": {
"name": "user_workspace_id_workspace_id_fk",
"tableFrom": "user",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
@@ -0,0 +1,615 @@
{
"id": "351e4956-74e0-4282-a23b-02f1a73fa38c",
"prevId": "bf9e9084-4073-4ecb-8e56-5610816c9589",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"billing_workspace_id_workspace_id_fk": {
"name": "billing_workspace_id_workspace_id_fk",
"tableFrom": "billing",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"payment_workspace_id_workspace_id_fk": {
"name": "payment_workspace_id_workspace_id_fk",
"tableFrom": "payment",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"request_id": {
"name": "request_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"usage_workspace_id_workspace_id_fk": {
"name": "usage_workspace_id_workspace_id_fk",
"tableFrom": "usage",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.key": {
"name": "key",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_used": {
"name": "time_used",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
{
"expression": "key",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"key_workspace_id_workspace_id_fk": {
"name": "key_workspace_id_workspace_id_fk",
"tableFrom": "key",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"color": {
"name": "color",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
{
"expression": "workspace_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"user_workspace_id_workspace_id_fk": {
"name": "user_workspace_id_workspace_id_fk",
"tableFrom": "user",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
@@ -0,0 +1,609 @@
{
"id": "fa935883-9e51-4811-90c7-8967eefe458c",
"prevId": "351e4956-74e0-4282-a23b-02f1a73fa38c",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {
"billing_workspace_id_workspace_id_fk": {
"name": "billing_workspace_id_workspace_id_fk",
"tableFrom": "billing",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"payment_workspace_id_workspace_id_fk": {
"name": "payment_workspace_id_workspace_id_fk",
"tableFrom": "payment",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"primaryKey": false,
"notNull": true
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"primaryKey": false,
"notNull": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"usage_workspace_id_workspace_id_fk": {
"name": "usage_workspace_id_workspace_id_fk",
"tableFrom": "usage",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.key": {
"name": "key",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_used": {
"name": "time_used",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
{
"expression": "key",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"key_workspace_id_workspace_id_fk": {
"name": "key_workspace_id_workspace_id_fk",
"tableFrom": "key",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
"color": {
"name": "color",
"type": "integer",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
{
"expression": "workspace_id",
"isExpression": false,
"asc": true,
"nulls": "last"
},
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {
"user_workspace_id_workspace_id_fk": {
"name": "user_workspace_id_workspace_id_fk",
"tableFrom": "user",
"tableTo": "workspace",
"columnsFrom": [
"workspace_id"
],
"columnsTo": [
"id"
],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
+34
View File
@@ -0,0 +1,34 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1754518198186,
"tag": "0000_amused_mojo",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1754609655262,
"tag": "0001_thankful_chat",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1754627626945,
"tag": "0002_stale_jackal",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1754672464106,
"tag": "0003_tranquil_spencer_smythe",
"breakpoints": true
}
]
}
+23
View File
@@ -0,0 +1,23 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode/cloud-core",
"version": "0.5.27",
"private": true,
"type": "module",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"drizzle-orm": "0.41.0",
"postgres": "3.4.7",
"stripe": "18.0.0",
"ulid": "3.0.0"
},
"exports": {
"./*": "./src/*"
},
"scripts": {
"db": "sst shell drizzle-kit"
},
"devDependencies": {
"drizzle-kit": "0.30.5"
}
}
+67
View File
@@ -0,0 +1,67 @@
import { z } from "zod"
import { and, eq, getTableColumns, isNull } from "drizzle-orm"
import { fn } from "./util/fn"
import { Database } from "./drizzle"
import { Identifier } from "./identifier"
import { AccountTable } from "./schema/account.sql"
import { Actor } from "./actor"
import { WorkspaceTable } from "./schema/workspace.sql"
import { UserTable } from "./schema/user.sql"
export namespace Account {
export const create = fn(
z.object({
email: z.string().email(),
id: z.string().optional(),
}),
async (input) =>
Database.transaction(async (tx) => {
const id = input.id ?? Identifier.create("account")
await tx.insert(AccountTable).values({
id,
email: input.email,
})
return id
}),
)
export const fromID = fn(z.string(), async (id) =>
Database.transaction(async (tx) => {
return tx
.select()
.from(AccountTable)
.where(eq(AccountTable.id, id))
.execute()
.then((rows) => rows[0])
}),
)
export const fromEmail = fn(z.string().email(), async (email) =>
Database.transaction(async (tx) => {
return tx
.select()
.from(AccountTable)
.where(eq(AccountTable.email, email))
.execute()
.then((rows) => rows[0])
}),
)
export const workspaces = async () => {
const actor = Actor.assert("account")
return Database.transaction(async (tx) =>
tx
.select(getTableColumns(WorkspaceTable))
.from(WorkspaceTable)
.innerJoin(UserTable, eq(UserTable.workspaceID, WorkspaceTable.id))
.where(
and(
eq(UserTable.email, actor.properties.email),
isNull(UserTable.timeDeleted),
isNull(WorkspaceTable.timeDeleted),
),
)
.execute(),
)
}
}
+74
View File
@@ -0,0 +1,74 @@
import { Context } from "./context"
import { Log } from "./util/log"
export namespace Actor {
interface Account {
type: "account"
properties: {
accountID: string
email: string
}
}
interface Public {
type: "public"
properties: {}
}
interface User {
type: "user"
properties: {
userID: string
workspaceID: string
}
}
interface System {
type: "system"
properties: {
workspaceID: string
}
}
export type Info = Account | Public | User | System
const ctx = Context.create<Info>()
export const use = ctx.use
const log = Log.create().tag("namespace", "actor")
export function provide<R, T extends Info["type"]>(
type: T,
properties: Extract<Info, { type: T }>["properties"],
cb: () => R,
) {
return ctx.provide(
{
type,
properties,
} as any,
() => {
return Log.provide({ ...properties }, () => {
log.info("provided")
return cb()
})
},
)
}
export function assert<T extends Info["type"]>(type: T) {
const actor = use()
if (actor.type !== type) {
throw new Error(`Expected actor type ${type}, got ${actor.type}`)
}
return actor as Extract<Info, { type: T }>
}
export function workspace() {
const actor = use()
if ("workspaceID" in actor.properties) {
return actor.properties.workspaceID
}
throw new Error(`actor of type "${actor.type}" is not associated with a workspace`)
}
}
+71
View File
@@ -0,0 +1,71 @@
import { Resource } from "sst"
import { Stripe } from "stripe"
import { Database, eq, sql } from "./drizzle"
import { BillingTable, UsageTable } from "./schema/billing.sql"
import { Actor } from "./actor"
import { fn } from "./util/fn"
import { z } from "zod"
import { Identifier } from "./identifier"
import { centsToMicroCents } from "./util/price"
export namespace Billing {
export const stripe = () =>
new Stripe(Resource.STRIPE_SECRET_KEY.value, {
apiVersion: "2025-03-31.basil",
})
export const get = async () => {
return Database.use(async (tx) =>
tx
.select({
customerID: BillingTable.customerID,
paymentMethodID: BillingTable.paymentMethodID,
balance: BillingTable.balance,
reload: BillingTable.reload,
})
.from(BillingTable)
.where(eq(BillingTable.workspaceID, Actor.workspace()))
.then((r) => r[0]),
)
}
export const consume = fn(
z.object({
requestID: z.string().optional(),
model: z.string(),
inputTokens: z.number(),
outputTokens: z.number(),
reasoningTokens: z.number().optional(),
cacheReadTokens: z.number().optional(),
cacheWriteTokens: z.number().optional(),
costInCents: z.number(),
}),
async (input) => {
const workspaceID = Actor.workspace()
const cost = centsToMicroCents(input.costInCents)
return await Database.transaction(async (tx) => {
await tx.insert(UsageTable).values({
workspaceID,
id: Identifier.create("usage"),
requestID: input.requestID,
model: input.model,
inputTokens: input.inputTokens,
outputTokens: input.outputTokens,
reasoningTokens: input.reasoningTokens,
cacheReadTokens: input.cacheReadTokens,
cacheWriteTokens: input.cacheWriteTokens,
cost,
})
const [updated] = await tx
.update(BillingTable)
.set({
balance: sql`${BillingTable.balance} - ${cost}`,
})
.where(eq(BillingTable.workspaceID, workspaceID))
.returning()
return updated.balance
})
},
)
}
+21
View File
@@ -0,0 +1,21 @@
import { AsyncLocalStorage } from "node:async_hooks"
export namespace Context {
export class NotFound extends Error {}
export function create<T>() {
const storage = new AsyncLocalStorage<T>()
return {
use() {
const result = storage.getStore()
if (!result) {
throw new NotFound()
}
return result
},
provide<R>(value: T, fn: () => R) {
return storage.run<R>(value, fn)
},
}
}
}
+95
View File
@@ -0,0 +1,95 @@
import { drizzle } from "drizzle-orm/postgres-js"
import { Resource } from "sst"
export * from "drizzle-orm"
import postgres from "postgres"
const createClient = memo(() => {
const client = postgres({
idle_timeout: 30000,
connect_timeout: 30000,
host: Resource.Database.host,
database: Resource.Database.database,
user: Resource.Database.username,
password: Resource.Database.password,
port: Resource.Database.port,
ssl: {
rejectUnauthorized: false,
},
max: 1,
})
return drizzle(client, {})
})
import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
import type { ExtractTablesWithRelations } from "drizzle-orm"
import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
import { Context } from "../context"
import { memo } from "../util/memo"
export namespace Database {
export type Transaction = PgTransaction<
PostgresJsQueryResultHKT,
Record<string, unknown>,
ExtractTablesWithRelations<Record<string, unknown>>
>
export type TxOrDb = Transaction | ReturnType<typeof createClient>
const TransactionContext = Context.create<{
tx: TxOrDb
effects: (() => void | Promise<void>)[]
}>()
export async function use<T>(callback: (trx: TxOrDb) => Promise<T>) {
try {
const { tx } = TransactionContext.use()
return tx.transaction(callback)
} catch (err) {
if (err instanceof Context.NotFound) {
const client = createClient()
const effects: (() => void | Promise<void>)[] = []
const result = await TransactionContext.provide(
{
effects,
tx: client,
},
() => callback(client),
)
await Promise.all(effects.map((x) => x()))
return result
}
throw err
}
}
export async function fn<Input, T>(callback: (input: Input, trx: TxOrDb) => Promise<T>) {
return (input: Input) => use(async (tx) => callback(input, tx))
}
export async function effect(effect: () => any | Promise<any>) {
try {
const { effects } = TransactionContext.use()
effects.push(effect)
} catch {
await effect()
}
}
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: PgTransactionConfig) {
try {
const { tx } = TransactionContext.use()
return callback(tx)
} catch (err) {
if (err instanceof Context.NotFound) {
const client = createClient()
const effects: (() => void | Promise<void>)[] = []
const result = await client.transaction(async (tx) => {
return TransactionContext.provide({ tx, effects }, () => callback(tx))
}, config)
await Promise.all(effects.map((x) => x()))
return result
}
throw err
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import { bigint, timestamp, varchar } from "drizzle-orm/pg-core"
export const ulid = (name: string) => varchar(name, { length: 30 })
export const workspaceColumns = {
get id() {
return ulid("id").notNull()
},
get workspaceID() {
return ulid("workspace_id").notNull()
},
}
export const id = () => ulid("id").notNull()
export const utc = (name: string) =>
timestamp(name, {
withTimezone: true,
})
export const currency = (name: string) =>
bigint(name, {
mode: "number",
})
export const timestamps = {
timeCreated: utc("time_created").notNull().defaultNow(),
timeDeleted: utc("time_deleted"),
}
+26
View File
@@ -0,0 +1,26 @@
import { ulid } from "ulid"
import { z } from "zod"
export namespace Identifier {
const prefixes = {
account: "acc",
billing: "bil",
key: "key",
payment: "pay",
usage: "usg",
user: "usr",
workspace: "wrk",
} as const
export function create(prefix: keyof typeof prefixes, given?: string): string {
if (given) {
if (given.startsWith(prefixes[prefix])) return given
throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`)
}
return [prefixes[prefix], ulid()].join("_")
}
export function schema(prefix: keyof typeof prefixes) {
return z.string().startsWith(prefixes[prefix])
}
}
+12
View File
@@ -0,0 +1,12 @@
import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
import { id, timestamps } from "../drizzle/types"
export const AccountTable = pgTable(
"account",
{
id: id(),
...timestamps,
email: varchar("email", { length: 255 }).notNull(),
},
(table) => [uniqueIndex("email").on(table.email)],
)
+45
View File
@@ -0,0 +1,45 @@
import { bigint, boolean, integer, pgTable, varchar } from "drizzle-orm/pg-core"
import { timestamps, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
export const BillingTable = pgTable(
"billing",
{
...workspaceColumns,
...timestamps,
customerID: varchar("customer_id", { length: 255 }),
paymentMethodID: varchar("payment_method_id", { length: 255 }),
paymentMethodLast4: varchar("payment_method_last4", { length: 4 }),
balance: bigint("balance", { mode: "number" }).notNull(),
reload: boolean("reload"),
},
(table) => [...workspaceIndexes(table)],
)
export const PaymentTable = pgTable(
"payment",
{
...workspaceColumns,
...timestamps,
customerID: varchar("customer_id", { length: 255 }),
paymentID: varchar("payment_id", { length: 255 }),
amount: bigint("amount", { mode: "number" }).notNull(),
},
(table) => [...workspaceIndexes(table)],
)
export const UsageTable = pgTable(
"usage",
{
...workspaceColumns,
...timestamps,
model: varchar("model", { length: 255 }).notNull(),
inputTokens: integer("input_tokens").notNull(),
outputTokens: integer("output_tokens").notNull(),
reasoningTokens: integer("reasoning_tokens"),
cacheReadTokens: integer("cache_read_tokens"),
cacheWriteTokens: integer("cache_write_tokens"),
cost: bigint("cost", { mode: "number" }).notNull(),
},
(table) => [...workspaceIndexes(table)],
)
+16
View File
@@ -0,0 +1,16 @@
import { text, pgTable, varchar, uniqueIndex } from "drizzle-orm/pg-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
export const KeyTable = pgTable(
"key",
{
...workspaceColumns,
...timestamps,
userID: text("user_id").notNull(),
name: varchar("name", { length: 255 }).notNull(),
key: varchar("key", { length: 255 }).notNull(),
timeUsed: utc("time_used"),
},
(table) => [...workspaceIndexes(table), uniqueIndex("global_key").on(table.key)],
)
+16
View File
@@ -0,0 +1,16 @@
import { text, pgTable, uniqueIndex, varchar, integer } from "drizzle-orm/pg-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
export const UserTable = pgTable(
"user",
{
...workspaceColumns,
...timestamps,
email: text("email").notNull(),
name: varchar("name", { length: 255 }).notNull(),
timeSeen: utc("time_seen"),
color: integer("color"),
},
(table) => [...workspaceIndexes(table), uniqueIndex("user_email").on(table.workspaceID, table.email)],
)
+25
View File
@@ -0,0 +1,25 @@
import { primaryKey, foreignKey, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
import { timestamps, ulid } from "../drizzle/types"
export const WorkspaceTable = pgTable(
"workspace",
{
id: ulid("id").notNull().primaryKey(),
slug: varchar("slug", { length: 255 }),
name: varchar("name", { length: 255 }),
...timestamps,
},
(table) => [uniqueIndex("slug").on(table.slug)],
)
export function workspaceIndexes(table: any) {
return [
primaryKey({
columns: [table.workspaceID, table.id],
}),
foreignKey({
foreignColumns: [WorkspaceTable.id],
columns: [table.workspaceID],
}),
]
}
+14
View File
@@ -0,0 +1,14 @@
import { z } from "zod"
export function fn<T extends z.ZodType, Result>(
schema: T,
cb: (input: z.output<T>) => Result,
) {
const result = (input: z.input<T>) => {
const parsed = schema.parse(input)
return cb(parsed)
}
result.force = (input: z.input<T>) => cb(input)
result.schema = schema
return result
}
+55
View File
@@ -0,0 +1,55 @@
import { Context } from "../context"
export namespace Log {
const ctx = Context.create<{
tags: Record<string, any>
}>()
export function create(tags?: Record<string, any>) {
tags = tags || {}
const result = {
info(message?: any, extra?: Record<string, any>) {
const prefix = Object.entries({
...use().tags,
...tags,
...extra,
})
.map(([key, value]) => `${key}=${value}`)
.join(" ")
console.log(prefix, message)
return result
},
tag(key: string, value: string) {
if (tags) tags[key] = value
return result
},
clone() {
return Log.create({ ...tags })
},
}
return result
}
export function provide<R>(tags: Record<string, any>, cb: () => R) {
const existing = use()
return ctx.provide(
{
tags: {
...existing.tags,
...tags,
},
},
cb,
)
}
function use() {
try {
return ctx.use()
} catch (e) {
return { tags: {} }
}
}
}
+11
View File
@@ -0,0 +1,11 @@
export function memo<T>(fn: () => T) {
let value: T | undefined
let loaded = false
return (): T => {
if (loaded) return value as T
loaded = true
value = fn()
return value as T
}
}
+3
View File
@@ -0,0 +1,3 @@
export function centsToMicroCents(amount: number) {
return Math.round(amount * 1000000)
}
+48
View File
@@ -0,0 +1,48 @@
import { z } from "zod"
import { fn } from "./util/fn"
import { centsToMicroCents } from "./util/price"
import { Actor } from "./actor"
import { Database, eq } from "./drizzle"
import { Identifier } from "./identifier"
import { UserTable } from "./schema/user.sql"
import { BillingTable } from "./schema/billing.sql"
import { WorkspaceTable } from "./schema/workspace.sql"
export namespace Workspace {
export const create = fn(z.void(), async () => {
const account = Actor.assert("account")
const workspaceID = Identifier.create("workspace")
await Database.transaction(async (tx) => {
await tx.insert(WorkspaceTable).values({
id: workspaceID,
})
await tx.insert(UserTable).values({
workspaceID,
id: Identifier.create("user"),
email: account.properties.email,
name: "",
})
await tx.insert(BillingTable).values({
workspaceID,
id: Identifier.create("billing"),
balance: centsToMicroCents(100),
})
})
return workspaceID
})
export async function list() {
const account = Actor.assert("account")
return Database.use(async (tx) => {
return tx
.select({
id: WorkspaceTable.id,
slug: WorkspaceTable.slug,
name: WorkspaceTable.name,
})
.from(UserTable)
.innerJoin(WorkspaceTable, eq(UserTable.workspaceID, WorkspaceTable.id))
.where(eq(UserTable.email, account.properties.email))
})
}
}
+9
View File
@@ -0,0 +1,9 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
+9
View File
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "node"]
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"name": "@opencode/cloud-function",
"version": "0.5.27",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"devDependencies": {
"@cloudflare/workers-types": "4.20250522.0",
"@types/node": "catalog:",
"openai": "5.11.0",
"typescript": "catalog:"
},
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
"@ai-sdk/openai-compatible": "1.0.1",
"@hono/zod-validator": "catalog:",
"@openauthjs/openauth": "0.0.0-20250322224806",
"ai": "catalog:",
"hono": "catalog:",
"zod": "catalog:"
}
}
+131
View File
@@ -0,0 +1,131 @@
import { Resource } from "sst"
import { z } from "zod"
import { issuer } from "@openauthjs/openauth"
import { createSubjects } from "@openauthjs/openauth/subject"
import { GithubProvider } from "@openauthjs/openauth/provider/github"
import { GoogleOidcProvider } from "@openauthjs/openauth/provider/google"
import { CloudflareStorage } from "@openauthjs/openauth/storage/cloudflare"
import { Account } from "@opencode/cloud-core/account.js"
import { Workspace } from "@opencode/cloud-core/workspace.js"
import { Actor } from "@opencode/cloud-core/actor.js"
type Env = {
AuthStorage: KVNamespace
}
export const subjects = createSubjects({
account: z.object({
accountID: z.string(),
email: z.string(),
}),
user: z.object({
userID: z.string(),
workspaceID: z.string(),
}),
})
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return issuer({
providers: {
github: GithubProvider({
clientID: Resource.GITHUB_CLIENT_ID_CONSOLE.value,
clientSecret: Resource.GITHUB_CLIENT_SECRET_CONSOLE.value,
scopes: ["read:user", "user:email"],
}),
google: GoogleOidcProvider({
clientID: Resource.GOOGLE_CLIENT_ID.value,
scopes: ["openid", "email"],
}),
// email: CodeProvider({
// async request(req, state, form, error) {
// console.log(state)
// const params = new URLSearchParams()
// if (error) {
// params.set("error", error.type)
// }
// if (state.type === "start") {
// return Response.redirect(process.env.AUTH_FRONTEND_URL + "/auth/email?" + params.toString(), 302)
// }
//
// if (state.type === "code") {
// return Response.redirect(process.env.AUTH_FRONTEND_URL + "/auth/code?" + params.toString(), 302)
// }
//
// return new Response("ok")
// },
// async sendCode(claims, code) {
// const email = z.string().email().parse(claims.email)
// const cmd = new SendEmailCommand({
// Destination: {
// ToAddresses: [email],
// },
// FromEmailAddress: `SST <auth@${Resource.Email.sender}>`,
// Content: {
// Simple: {
// Body: {
// Html: {
// Data: `Your pin code is <strong>${code}</strong>`,
// },
// Text: {
// Data: `Your pin code is ${code}`,
// },
// },
// Subject: {
// Data: "SST Console Pin Code: " + code,
// },
// },
// },
// })
// await ses.send(cmd)
// },
// }),
},
storage: CloudflareStorage({
namespace: env.AuthStorage,
}),
subjects,
async success(ctx, response) {
console.log(response)
let email: string | undefined
if (response.provider === "github") {
const userResponse = await fetch("https://api.github.com/user", {
headers: {
Authorization: `Bearer ${response.tokenset.access}`,
"User-Agent": "opencode",
Accept: "application/vnd.github+json",
},
})
const user = (await userResponse.json()) as { email: string }
email = user.email
} else if (response.provider === "google") {
if (!response.id.email_verified) throw new Error("Google email not verified")
email = response.id.email as string
}
//if (response.provider === "email") {
// email = response.claims.email
//}
else throw new Error("Unsupported provider")
if (!email) throw new Error("No email found")
let accountID = await Account.fromEmail(email).then((x) => x?.id)
if (!accountID) {
console.log("creating account for", email)
accountID = await Account.create({
email: email!,
})
}
await Actor.provide("account", { accountID, email }, async () => {
const workspaces = await Account.workspaces()
if (workspaces.length === 0) {
await Workspace.create()
}
})
return ctx.subject("account", accountID, { accountID, email })
},
}).fetch(request, env, ctx)
},
}
+909
View File
@@ -0,0 +1,909 @@
import { z } from "zod"
import { Hono, MiddlewareHandler } from "hono"
import { cors } from "hono/cors"
import { HTTPException } from "hono/http-exception"
import { zValidator } from "@hono/zod-validator"
import { Resource } from "sst"
import { type ProviderMetadata, type LanguageModelUsage, generateText, streamText } from "ai"
import { createAnthropic } from "@ai-sdk/anthropic"
import { createOpenAI } from "@ai-sdk/openai"
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
import type { LanguageModelV2Prompt } from "@ai-sdk/provider"
import { type ChatCompletionCreateParamsBase } from "openai/resources/chat/completions"
import { Actor } from "@opencode/cloud-core/actor.js"
import { and, Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js"
import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
import { createClient } from "@openauthjs/openauth/client"
import { Log } from "@opencode/cloud-core/util/log.js"
import { Billing } from "@opencode/cloud-core/billing.js"
import { Workspace } from "@opencode/cloud-core/workspace.js"
import { BillingTable, PaymentTable, UsageTable } from "@opencode/cloud-core/schema/billing.sql.js"
import { centsToMicroCents } from "@opencode/cloud-core/util/price.js"
import { Identifier } from "../../core/src/identifier"
type Env = {}
let _client: ReturnType<typeof createClient>
const client = () => {
if (_client) return _client
_client = createClient({
clientID: "api",
issuer: Resource.AUTH_API_URL.value,
})
return _client
}
const SUPPORTED_MODELS = {
"anthropic/claude-sonnet-4": {
input: 0.0000015,
output: 0.000006,
reasoning: 0.0000015,
cacheRead: 0.0000001,
cacheWrite: 0.0000001,
model: () =>
createAnthropic({
apiKey: Resource.ANTHROPIC_API_KEY.value,
})("claude-sonnet-4-20250514"),
},
"openai/gpt-4.1": {
input: 0.0000015,
output: 0.000006,
reasoning: 0.0000015,
cacheRead: 0.0000001,
cacheWrite: 0.0000001,
model: () =>
createOpenAI({
apiKey: Resource.OPENAI_API_KEY.value,
})("gpt-4.1"),
},
"zhipuai/glm-4.5-flash": {
input: 0,
output: 0,
reasoning: 0,
cacheRead: 0,
cacheWrite: 0,
model: () =>
createOpenAICompatible({
name: "Zhipu AI",
baseURL: "https://api.z.ai/api/paas/v4",
apiKey: Resource.ZHIPU_API_KEY.value,
})("glm-4.5-flash"),
},
}
const log = Log.create({
namespace: "api",
})
const GatewayAuth: MiddlewareHandler = async (c, next) => {
const authHeader = c.req.header("authorization")
if (!authHeader || !authHeader.startsWith("Bearer ")) {
return c.json(
{
error: {
message: "Missing API key.",
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
},
401,
)
}
const apiKey = authHeader.split(" ")[1]
// Check against KeyTable
const keyRecord = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
workspaceID: KeyTable.workspaceID,
})
.from(KeyTable)
.where(eq(KeyTable.key, apiKey))
.then((rows) => rows[0]),
)
if (!keyRecord) {
return c.json(
{
error: {
message: "Invalid API key.",
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
},
401,
)
}
c.set("keyRecord", keyRecord)
await next()
}
const RestAuth: MiddlewareHandler = async (c, next) => {
const authorization = c.req.header("authorization")
if (!authorization) {
return Actor.provide("public", {}, next)
}
const token = authorization.split(" ")[1]
if (!token)
throw new HTTPException(403, {
message: "Bearer token is required.",
})
const verified = await client().verify(token)
if (verified.err) {
throw new HTTPException(403, {
message: "Invalid token.",
})
}
let subject = verified.subject as Actor.Info
if (subject.type === "account") {
const workspaceID = c.req.header("x-opencode-workspace")
const email = subject.properties.email
if (workspaceID) {
const user = await Database.use((tx) =>
tx
.select({
id: UserTable.id,
workspaceID: UserTable.workspaceID,
email: UserTable.email,
})
.from(UserTable)
.where(and(eq(UserTable.email, email), eq(UserTable.workspaceID, workspaceID)))
.then((rows) => rows[0]),
)
if (!user)
throw new HTTPException(403, {
message: "You do not have access to this workspace.",
})
subject = {
type: "user",
properties: {
userID: user.id,
workspaceID: workspaceID,
email: user.email,
},
}
}
}
await Actor.provide(subject.type, subject.properties, next)
}
const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; workspaceID: string } } }>()
.get("/", (c) => c.text("Hello, world!"))
.post("/v1/chat/completions", GatewayAuth, async (c) => {
const keyRecord = c.get("keyRecord")!
return await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => {
try {
// Check balance
const customer = await Billing.get()
if (customer.balance <= 0) {
return c.json(
{
error: {
message: "Insufficient balance",
type: "insufficient_quota",
param: null,
code: "insufficient_quota",
},
},
401,
)
}
const body = await c.req.json<ChatCompletionCreateParamsBase>()
const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model()
if (!model) throw new Error(`Unsupported model: ${body.model}`)
const requestBody = transformOpenAIRequestToAiSDK()
return body.stream ? await handleStream() : await handleGenerate()
async function handleStream() {
const result = await model.doStream({
...requestBody,
})
const encoder = new TextEncoder()
const stream = new ReadableStream({
async start(controller) {
const id = `chatcmpl-${Date.now()}`
const created = Math.floor(Date.now() / 1000)
try {
for await (const chunk of result.stream) {
console.log("!!! CHUNK !!! : " + chunk.type)
switch (chunk.type) {
case "text-delta": {
const data = {
id,
object: "chat.completion.chunk",
created,
model: body.model,
choices: [
{
index: 0,
delta: {
content: chunk.delta,
},
finish_reason: null,
},
],
}
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
break
}
case "reasoning-delta": {
const data = {
id,
object: "chat.completion.chunk",
created,
model: body.model,
choices: [
{
index: 0,
delta: {
reasoning_content: chunk.delta,
},
finish_reason: null,
},
],
}
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
break
}
case "tool-call": {
const data = {
id,
object: "chat.completion.chunk",
created,
model: body.model,
choices: [
{
index: 0,
delta: {
tool_calls: [
{
index: 0,
id: chunk.toolCallId,
type: "function",
function: {
name: chunk.toolName,
arguments: chunk.input,
},
},
],
},
finish_reason: null,
},
],
}
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
break
}
case "error": {
const data = {
id,
object: "chat.completion.chunk",
created,
model: body.model,
choices: [
{
index: 0,
delta: {},
finish_reason: "stop",
},
],
error: {
message: typeof chunk.error === "string" ? chunk.error : chunk.error,
type: "server_error",
},
}
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
controller.close()
break
}
case "finish": {
const data = {
id,
object: "chat.completion.chunk",
created,
model: body.model,
choices: [
{
index: 0,
delta: {},
finish_reason:
{
stop: "stop",
length: "length",
"content-filter": "content_filter",
"tool-calls": "tool_calls",
error: "stop",
other: "stop",
unknown: "stop",
}[chunk.finishReason] || "stop",
},
],
usage: {
prompt_tokens: chunk.usage.inputTokens,
completion_tokens: chunk.usage.outputTokens,
total_tokens: chunk.usage.totalTokens,
completion_tokens_details: {
reasoning_tokens: chunk.usage.reasoningTokens,
},
prompt_tokens_details: {
cached_tokens: chunk.usage.cachedInputTokens,
},
},
}
await trackUsage(body.model, chunk.usage, chunk.providerMetadata)
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
controller.close()
break
}
//case "stream-start":
//case "response-metadata":
case "text-start":
case "text-end":
case "reasoning-start":
case "reasoning-end":
case "tool-input-start":
case "tool-input-delta":
case "tool-input-end":
case "raw":
default:
// Log unknown chunk types for debugging
console.warn(`Unknown chunk type: ${(chunk as any).type}`)
break
}
}
} catch (error) {
controller.error(error)
}
},
})
return new Response(stream, {
headers: {
"Content-Type": "text/plain; charset=utf-8",
"Cache-Control": "no-cache",
Connection: "keep-alive",
},
})
}
async function handleGenerate() {
const response = await model.doGenerate({
...requestBody,
})
await trackUsage(body.model, response.usage, response.providerMetadata)
return c.json({
id: `chatcmpl-${Date.now()}`,
object: "chat.completion" as const,
created: Math.floor(Date.now() / 1000),
model: body.model,
choices: [
{
index: 0,
message: {
role: "assistant" as const,
content: response.content?.find((c) => c.type === "text")?.text ?? "",
reasoning_content: response.content?.find((c) => c.type === "reasoning")?.text,
tool_calls: response.content
?.filter((c) => c.type === "tool-call")
.map((toolCall) => ({
id: toolCall.toolCallId,
type: "function" as const,
function: {
name: toolCall.toolName,
arguments: toolCall.input,
},
})),
},
finish_reason:
(
{
stop: "stop",
length: "length",
"content-filter": "content_filter",
"tool-calls": "tool_calls",
error: "stop",
other: "stop",
unknown: "stop",
} as const
)[response.finishReason] || "stop",
},
],
usage: {
prompt_tokens: response.usage?.inputTokens,
completion_tokens: response.usage?.outputTokens,
total_tokens: response.usage?.totalTokens,
completion_tokens_details: {
reasoning_tokens: response.usage?.reasoningTokens,
},
prompt_tokens_details: {
cached_tokens: response.usage?.cachedInputTokens,
},
},
})
}
function transformOpenAIRequestToAiSDK() {
const prompt = transformMessages()
const tools = transformTools()
return {
prompt,
maxOutputTokens: body.max_tokens ?? body.max_completion_tokens ?? undefined,
temperature: body.temperature ?? undefined,
topP: body.top_p ?? undefined,
frequencyPenalty: body.frequency_penalty ?? undefined,
presencePenalty: body.presence_penalty ?? undefined,
providerOptions: body.reasoning_effort
? {
anthropic: {
reasoningEffort: body.reasoning_effort,
},
}
: undefined,
stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined,
responseFormat: (() => {
if (!body.response_format) return { type: "text" as const }
if (body.response_format.type === "json_schema")
return {
type: "json" as const,
schema: body.response_format.json_schema.schema,
name: body.response_format.json_schema.name,
description: body.response_format.json_schema.description,
}
if (body.response_format.type === "json_object") return { type: "json" as const }
throw new Error("Unsupported response format")
})(),
seed: body.seed ?? undefined,
tools: tools.tools,
toolChoice: tools.toolChoice,
}
function transformTools() {
const { tools, tool_choice } = body
if (!tools || tools.length === 0) {
return { tools: undefined, toolChoice: undefined }
}
const aiSdkTools = tools.map((tool) => {
return {
type: tool.type,
name: tool.function.name,
description: tool.function.description,
inputSchema: tool.function.parameters!,
}
})
let aiSdkToolChoice
if (tool_choice == null) {
aiSdkToolChoice = undefined
} else if (tool_choice === "auto") {
aiSdkToolChoice = { type: "auto" as const }
} else if (tool_choice === "none") {
aiSdkToolChoice = { type: "none" as const }
} else if (tool_choice === "required") {
aiSdkToolChoice = { type: "required" as const }
} else if (tool_choice.type === "function") {
aiSdkToolChoice = {
type: "tool" as const,
toolName: tool_choice.function.name,
}
}
return { tools: aiSdkTools, toolChoice: aiSdkToolChoice }
}
function transformMessages() {
const { messages } = body
const prompt: LanguageModelV2Prompt = []
for (const message of messages) {
switch (message.role) {
case "system": {
prompt.push({
role: "system",
content: message.content as string,
})
break
}
case "user": {
if (typeof message.content === "string") {
prompt.push({
role: "user",
content: [{ type: "text", text: message.content }],
})
} else {
const content = message.content.map((part) => {
switch (part.type) {
case "text":
return { type: "text" as const, text: part.text }
case "image_url":
return {
type: "file" as const,
mediaType: "image/jpeg" as const,
data: part.image_url.url,
}
default:
throw new Error(`Unsupported content part type: ${(part as any).type}`)
}
})
prompt.push({
role: "user",
content,
})
}
break
}
case "assistant": {
const content: Array<
| { type: "text"; text: string }
| {
type: "tool-call"
toolCallId: string
toolName: string
input: any
}
> = []
if (message.content) {
content.push({
type: "text",
text: message.content as string,
})
}
if (message.tool_calls) {
for (const toolCall of message.tool_calls) {
content.push({
type: "tool-call",
toolCallId: toolCall.id,
toolName: toolCall.function.name,
input: JSON.parse(toolCall.function.arguments),
})
}
}
prompt.push({
role: "assistant",
content,
})
break
}
case "tool": {
prompt.push({
role: "tool",
content: [
{
type: "tool-result",
toolName: "placeholder",
toolCallId: message.tool_call_id,
output: {
type: "text",
value: message.content as string,
},
},
],
})
break
}
default: {
throw new Error(`Unsupported message role: ${message.role}`)
}
}
}
return prompt
}
}
async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) {
const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS]
if (!modelData) throw new Error(`Unsupported model: ${model}`)
const inputTokens = usage.inputTokens ?? 0
const outputTokens = usage.outputTokens ?? 0
const reasoningTokens = usage.reasoningTokens ?? 0
const cacheReadTokens = usage.cachedInputTokens ?? 0
const cacheWriteTokens =
providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
// @ts-expect-error
providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
0
const inputCost = modelData.input * inputTokens
const outputCost = modelData.output * outputTokens
const reasoningCost = modelData.reasoning * reasoningTokens
const cacheReadCost = modelData.cacheRead * cacheReadTokens
const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
await Billing.consume({
model,
inputTokens,
outputTokens,
reasoningTokens,
cacheReadTokens,
cacheWriteTokens,
costInCents,
})
await Database.use((tx) =>
tx
.update(KeyTable)
.set({ timeUsed: sql`now()` })
.where(eq(KeyTable.id, keyRecord.id)),
)
}
} catch (error: any) {
return c.json({ error: { message: error.message } }, 500)
}
})
})
.use("/*", cors())
.use(RestAuth)
.get("/rest/account", async (c) => {
const account = Actor.assert("account")
let workspaces = await Workspace.list()
if (workspaces.length === 0) {
await Workspace.create()
workspaces = await Workspace.list()
}
return c.json({
id: account.properties.accountID,
email: account.properties.email,
workspaces,
})
})
.get("/billing/info", async (c) => {
const billing = await Billing.get()
const payments = await Database.use((tx) =>
tx
.select()
.from(PaymentTable)
.where(eq(PaymentTable.workspaceID, Actor.workspace()))
.orderBy(sql`${PaymentTable.timeCreated} DESC`)
.limit(100),
)
const usage = await Database.use((tx) =>
tx
.select()
.from(UsageTable)
.where(eq(UsageTable.workspaceID, Actor.workspace()))
.orderBy(sql`${UsageTable.timeCreated} DESC`)
.limit(100),
)
return c.json({ billing, payments, usage })
})
.post(
"/billing/checkout",
zValidator(
"json",
z.custom<{
success_url: string
cancel_url: string
}>(),
),
async (c) => {
const account = Actor.assert("user")
const body = await c.req.json()
const customer = await Billing.get()
const session = await Billing.stripe().checkout.sessions.create({
mode: "payment",
line_items: [
{
price_data: {
currency: "usd",
product_data: {
name: "opencode credits",
},
unit_amount: 2000, // $20 minimum
},
quantity: 1,
},
],
payment_intent_data: {
setup_future_usage: "on_session",
},
...(customer.customerID
? { customer: customer.customerID }
: {
customer_email: account.properties.email,
customer_creation: "always",
}),
metadata: {
workspaceID: Actor.workspace(),
},
currency: "usd",
payment_method_types: ["card"],
success_url: body.success_url,
cancel_url: body.cancel_url,
})
return c.json({
url: session.url,
})
},
)
.post("/billing/portal", async (c) => {
const body = await c.req.json()
const customer = await Billing.get()
if (!customer?.customerID) {
throw new Error("No stripe customer ID")
}
const session = await Billing.stripe().billingPortal.sessions.create({
customer: customer.customerID,
return_url: body.return_url,
})
return c.json({
url: session.url,
})
})
.post("/stripe/webhook", async (c) => {
const body = await Billing.stripe().webhooks.constructEventAsync(
await c.req.text(),
c.req.header("stripe-signature")!,
Resource.STRIPE_WEBHOOK_SECRET.value,
)
console.log(body.type, JSON.stringify(body, null, 2))
if (body.type === "checkout.session.completed") {
const workspaceID = body.data.object.metadata?.workspaceID
const customerID = body.data.object.customer as string
const paymentID = body.data.object.payment_intent as string
const amount = body.data.object.amount_total
if (!workspaceID) throw new Error("Workspace ID not found")
if (!customerID) throw new Error("Customer ID not found")
if (!amount) throw new Error("Amount not found")
if (!paymentID) throw new Error("Payment ID not found")
await Actor.provide("system", { workspaceID }, async () => {
const customer = await Billing.get()
if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch")
// set customer metadata
if (!customer?.customerID) {
await Billing.stripe().customers.update(customerID, {
metadata: {
workspaceID,
},
})
}
// get payment method for the payment intent
const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
expand: ["payment_method"],
})
const paymentMethod = paymentIntent.payment_method
if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")
await Database.transaction(async (tx) => {
await tx
.update(BillingTable)
.set({
balance: sql`${BillingTable.balance} + ${centsToMicroCents(amount)}`,
customerID,
paymentMethodID: paymentMethod.id,
paymentMethodLast4: paymentMethod.card!.last4,
})
.where(eq(BillingTable.workspaceID, workspaceID))
await tx.insert(PaymentTable).values({
workspaceID,
id: Identifier.create("payment"),
amount: centsToMicroCents(amount),
paymentID,
customerID,
})
})
})
}
console.log("finished handling")
return c.json("ok", 200)
})
.get("/keys", async (c) => {
const user = Actor.assert("user")
const keys = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
name: KeyTable.name,
key: KeyTable.key,
userID: KeyTable.userID,
timeCreated: KeyTable.timeCreated,
timeUsed: KeyTable.timeUsed,
})
.from(KeyTable)
.where(eq(KeyTable.workspaceID, user.properties.workspaceID))
.orderBy(sql`${KeyTable.timeCreated} DESC`),
)
return c.json({ keys })
})
.post("/keys", zValidator("json", z.object({ name: z.string().min(1).max(255) })), async (c) => {
const user = Actor.assert("user")
const { name } = c.req.valid("json")
// Generate secret key: sk- + 64 random characters (upper, lower, numbers)
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
let randomPart = ""
for (let i = 0; i < 64; i++) {
randomPart += chars.charAt(Math.floor(Math.random() * chars.length))
}
const secretKey = `sk-${randomPart}`
const keyRecord = await Database.use((tx) =>
tx
.insert(KeyTable)
.values({
id: Identifier.create("key"),
workspaceID: user.properties.workspaceID,
userID: user.properties.userID,
name,
key: secretKey,
timeUsed: null,
})
.returning(),
)
return c.json({
key: secretKey,
id: keyRecord[0].id,
name: keyRecord[0].name,
created: keyRecord[0].timeCreated,
})
})
.delete("/keys/:id", async (c) => {
const user = Actor.assert("user")
const keyId = c.req.param("id")
const result = await Database.use((tx) =>
tx
.delete(KeyTable)
.where(and(eq(KeyTable.id, keyId), eq(KeyTable.workspaceID, user.properties.workspaceID)))
.returning({ id: KeyTable.id }),
)
if (result.length === 0) {
return c.json({ error: "Key not found" }, 404)
}
return c.json({ success: true, id: result[0].id })
})
.all("*", (c) => c.text("Not Found"))
export type ApiType = typeof app
export default app
+88
View File
@@ -0,0 +1,88 @@
/* This file is auto-generated by SST. Do not edit. */
/* tslint:disable */
/* eslint-disable */
/* deno-fmt-ignore-file */
import "sst"
declare module "sst" {
export interface Resource {
"ANTHROPIC_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
}
"DATABASE_PASSWORD": {
"type": "sst.sst.Secret"
"value": string
}
"DATABASE_USERNAME": {
"type": "sst.sst.Secret"
"value": string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
}
"OPENAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
}
"ZHIPU_API_KEY": {
"type": "sst.sst.Secret"
"value": string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
"Api": cloudflare.Service
"AuthApi": cloudflare.Service
"AuthStorage": cloudflare.KVNamespace
"Bucket": cloudflare.R2Bucket
"GatewayApi": cloudflare.Service
}
}
import "sst"
export {}
+9
View File
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node22/tsconfig.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types", "node"]
}
}
+2
View File
@@ -0,0 +1,2 @@
node_modules
dist
+38
View File
@@ -0,0 +1,38 @@
<!doctype html>
<html lang="en" data-color-mode="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OpenControl</title>
<link rel="shortcut icon" type="image/ico" href="/favicon.ico" />
<link rel="icon" href="/favicon.ico" sizes="48x48">
<link rel="icon" href="/favicon.svg" media="(prefers-color-scheme: light)">
<link rel="icon" href="/favicon-dark.svg" media="(prefers-color-scheme: dark)">
<link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml">
<meta property="twitter:image" content="%BASE_URL%/social-share.png">
<meta property="og:title" content="OpenControl">
<meta property="og:url" content="%BASE_URL%">
<meta property="og:locale" content="en">
<meta property="og:description" content="Control your infrastructure with AI.">
<meta property="og:site_name" content="OpenControl">
<meta name="twitter:card" content="summary_large_image">
<meta name="description" content="Control your infrastructure with AI.">
<meta property="og:image" content="%BASE_URL%/social-share.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Rubik:wght@300..900&display=swap" rel="stylesheet">
<!--ssr-head-->
<!--ssr-assets-->
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<!--ssr-outlet-->
</div>
<script type="module" src="/src/entry-client.tsx"></script>
</body>
</html>
+29
View File
@@ -0,0 +1,29 @@
0 info it worked if it ends with ok
1 verbose cli [
1 verbose cli '/usr/local/bin/node',
1 verbose cli '/Users/frank/Sites/opencode/node_modules/.bin/npm',
1 verbose cli 'run',
1 verbose cli 'dev'
1 verbose cli ]
2 info using npm@2.15.12
3 info using node@v20.18.1
4 verbose stack Error: Invalid name: "@opencode/cloud/web"
4 verbose stack at ensureValidName (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/fixer.js:336:15)
4 verbose stack at Object.fixNameField (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/fixer.js:215:5)
4 verbose stack at /Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/normalize.js:32:38
4 verbose stack at Array.forEach (<anonymous>)
4 verbose stack at normalize (/Users/frank/Sites/opencode/node_modules/npm/node_modules/normalize-package-data/lib/normalize.js:31:15)
4 verbose stack at final (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:349:5)
4 verbose stack at then (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:124:5)
4 verbose stack at ReadFileContext.<anonymous> (/Users/frank/Sites/opencode/node_modules/npm/node_modules/read-package-json/read-json.js:295:20)
4 verbose stack at ReadFileContext.callback (/Users/frank/Sites/opencode/node_modules/npm/node_modules/graceful-fs/graceful-fs.js:78:16)
4 verbose stack at FSReqCallback.readFileAfterOpen [as oncomplete] (node:fs:299:13)
5 verbose cwd /Users/frank/Sites/opencode/cloud/web
6 error Darwin 24.5.0
7 error argv "/usr/local/bin/node" "/Users/frank/Sites/opencode/node_modules/.bin/npm" "run" "dev"
8 error node v20.18.1
9 error npm v2.15.12
10 error Invalid name: "@opencode/cloud/web"
11 error If you need help, you may report this error at:
11 error <https://github.com/npm/npm/issues>
12 verbose exit [ 1, true ]
+32
View File
@@ -0,0 +1,32 @@
{
"name": "@opencode/cloud-web",
"version": "0.5.27",
"private": true,
"description": "",
"type": "module",
"scripts": {
"start": "vite",
"dev": "vite",
"build": "bun build:server && bun build:client",
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --ssr src/entry-server.tsx --outDir dist/server",
"serve": "vite preview",
"sst:dev": "bun sst shell --target Console -- bun dev"
},
"license": "MIT",
"devDependencies": {
"typescript": "catalog:",
"vite": "6.2.2",
"vite-plugin-pages": "0.32.5",
"vite-plugin-solid": "2.11.6"
},
"dependencies": {
"@kobalte/core": "0.13.9",
"@openauthjs/solid": "0.0.0-20250322224806",
"@solid-primitives/storage": "4.3.1",
"@solidjs/meta": "0.29.4",
"@solidjs/router": "0.15.3",
"solid-js": "1.9.5",
"solid-list": "0.3.0"
}
}
+3
View File
@@ -0,0 +1,3 @@
<svg width="28" height="32" viewBox="0 0 28 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 31.5L0 23.6873V7.81266L14 0L28 7.81266V23.6873L14 31.5ZM14 28.4664L25.3456 22.0251V9.47493L14 2.99209L2.65443 9.47493V22.0251L14 28.4664ZM13.9572 24.6016C12.2732 24.6016 10.7176 24.1999 9.29052 23.3964C7.89195 22.593 6.7788 21.5125 5.95107 20.155C5.12334 18.7698 4.70948 17.2599 4.70948 15.6253C4.70948 13.9908 5.12334 12.4947 5.95107 11.1372C6.7788 9.77968 7.89195 8.69921 9.29052 7.89578C10.7176 7.06464 12.2732 6.64908 13.9572 6.64908C15.6412 6.64908 17.1825 7.06464 18.581 7.89578C19.9796 8.69921 21.0928 9.77968 21.9205 11.1372C22.7768 12.4947 23.2049 13.9908 23.2049 15.6253C23.2049 17.2599 22.791 18.7559 21.9633 20.1135C21.1356 21.471 20.0224 22.5653 18.6239 23.3964C17.2253 24.1999 15.6697 24.6016 13.9572 24.6016ZM13.9572 22.2744C15.213 22.2744 16.3547 21.9697 17.3823 21.3602C18.4098 20.7507 19.2375 19.9472 19.8654 18.9499C20.4934 17.9248 20.8073 16.8166 20.8073 15.6253C20.8073 14.4063 20.4934 13.2982 19.8654 12.3008C19.2375 11.3034 18.4098 10.5 17.3823 9.8905C16.3547 9.281 15.213 8.97625 13.9572 8.97625C12.7299 8.97625 11.5882 9.281 10.5321 9.8905C9.50459 10.5 8.67686 11.3034 8.04893 12.3008C7.421 13.2982 7.10703 14.4063 7.10703 15.6253C7.10703 16.8166 7.421 17.9248 8.04893 18.9499C8.67686 19.9472 9.50459 20.7507 10.5321 21.3602C11.5882 21.9697 12.7299 22.2744 13.9572 22.2744Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="28" height="32" viewBox="0 0 28 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 31.5L0 23.6873V7.81266L14 0L28 7.81266V23.6873L14 31.5ZM14 28.4664L25.3456 22.0251V9.47493L14 2.99209L2.65443 9.47493V22.0251L14 28.4664ZM13.9572 24.6016C12.2732 24.6016 10.7176 24.1999 9.29052 23.3964C7.89195 22.593 6.7788 21.5125 5.95107 20.155C5.12334 18.7698 4.70948 17.2599 4.70948 15.6253C4.70948 13.9908 5.12334 12.4947 5.95107 11.1372C6.7788 9.77968 7.89195 8.69921 9.29052 7.89578C10.7176 7.06464 12.2732 6.64908 13.9572 6.64908C15.6412 6.64908 17.1825 7.06464 18.581 7.89578C19.9796 8.69921 21.0928 9.77968 21.9205 11.1372C22.7768 12.4947 23.2049 13.9908 23.2049 15.6253C23.2049 17.2599 22.791 18.7559 21.9633 20.1135C21.1356 21.471 20.0224 22.5653 18.6239 23.3964C17.2253 24.1999 15.6697 24.6016 13.9572 24.6016ZM13.9572 22.2744C15.213 22.2744 16.3547 21.9697 17.3823 21.3602C18.4098 20.7507 19.2375 19.9472 19.8654 18.9499C20.4934 17.9248 20.8073 16.8166 20.8073 15.6253C20.8073 14.4063 20.4934 13.2982 19.8654 12.3008C19.2375 11.3034 18.4098 10.5 17.3823 9.8905C16.3547 9.281 15.213 8.97625 13.9572 8.97625C12.7299 8.97625 11.5882 9.281 10.5321 9.8905C9.50459 10.5 8.67686 11.3034 8.04893 12.3008C7.421 13.2982 7.10703 14.4063 7.10703 15.6253C7.10703 16.8166 7.421 17.9248 8.04893 18.9499C8.67686 19.9472 9.50459 20.7507 10.5321 21.3602C11.5882 21.9697 12.7299 22.2744 13.9572 22.2744Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+24
View File
@@ -0,0 +1,24 @@
import fs from "fs"
import path from "path"
import { generateHydrationScript, getAssets } from "solid-js/web"
const dist = import.meta.resolve("../dist").replace("file://", "")
const serverEntry = await import("../dist/server/entry-server.js")
const template = fs.readFileSync(path.join(dist, "client/index.html"), "utf-8")
fs.writeFileSync(path.join(dist, "client/fallback.html"), template)
const routes = ["/", "/foo"]
for (const route of routes) {
const { app } = serverEntry.render({ url: route })
const html = template
.replace("<!--ssr-outlet-->", app)
.replace("<!--ssr-head-->", generateHydrationScript())
.replace("<!--ssr-assets-->", getAssets())
const filePath = dist + `/client${route === "/" ? "/index" : route}.html`
fs.mkdirSync(path.dirname(filePath), {
recursive: true,
})
fs.writeFileSync(filePath, html)
console.log(`Pre-rendered: ${filePath}`)
}
+42
View File
@@ -0,0 +1,42 @@
/// <reference types="vite-plugin-pages/client-solid" />
import { Router } from "@solidjs/router"
import routes from "~solid-pages"
import "./ui/style/index.css"
import { MetaProvider } from "@solidjs/meta"
import { AccountProvider } from "./components/context-account"
import { DialogProvider } from "./ui/context-dialog"
import { DialogString } from "./ui/dialog-string"
import { DialogSelect } from "./ui/dialog-select"
import { ThemeProvider } from "./components/context-theme"
import { Suspense } from "solid-js"
import { OpenAuthProvider } from "./components/context-openauth"
export function App(props: { url?: string }) {
return (
<ThemeProvider>
<Suspense>
<DialogProvider>
<DialogString />
<DialogSelect />
<OpenAuthProvider
clientID="web"
issuer={import.meta.env.VITE_AUTH_URL || "http://dummy"}
>
<AccountProvider>
<MetaProvider>
<Router
children={routes}
url={props.url}
root={(props) => {
return <>{props.children}</>
}}
/>
</MetaProvider>
</AccountProvider>
</OpenAuthProvider>
</DialogProvider>
</Suspense>
</ThemeProvider>
)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

@@ -0,0 +1,99 @@
import { createContext, createEffect, ParentProps, Suspense, useContext } from "solid-js"
import { makePersisted } from "@solid-primitives/storage"
import { createStore } from "solid-js/store"
import { useOpenAuth } from "./context-openauth"
import { createAsync } from "@solidjs/router"
import { isServer } from "solid-js/web"
type Storage = {
accounts: Record<
string,
{
id: string
email: string
workspaces: {
id: string
name: string
slug: string
}[]
}
>
}
const context = createContext<ReturnType<typeof init>>()
function init() {
const auth = useOpenAuth()
const [store, setStore] = makePersisted(
createStore<Storage>({
accounts: {},
}),
{
name: "opencontrol.account",
},
)
async function refresh(id: string) {
return fetch(import.meta.env.VITE_API_URL + "/rest/account", {
headers: {
authorization: `Bearer ${await auth.access(id)}`,
},
})
.then((val) => val.json())
.then((val) => setStore("accounts", id, val as any))
}
createEffect((previous: string[]) => {
if (Object.keys(auth.all).length === 0) {
return []
}
for (const item of Object.values(auth.all)) {
if (previous.includes(item.id)) continue
refresh(item.id)
}
return Object.keys(auth.all)
}, [] as string[])
const result = {
get all() {
return Object.keys(auth.all)
.map((id) => store.accounts[id])
.filter(Boolean)
},
get current() {
if (!auth.subject) return undefined
return store.accounts[auth.subject.id]
},
refresh,
get ready() {
return Object.keys(auth.all).length === result.all.length
},
}
return result
}
export function AccountProvider(props: ParentProps) {
const ctx = init()
const resource = createAsync(async () => {
await new Promise<void>((resolve) => {
if (isServer) return resolve()
createEffect(() => {
if (ctx.ready) resolve()
})
})
return null
})
return (
<Suspense>
{resource()}
<context.Provider value={ctx}>{props.children}</context.Provider>
</Suspense>
)
}
export function useAccount() {
const result = useContext(context)
if (!result) throw new Error("no account context")
return result
}
@@ -0,0 +1,180 @@
import { createClient } from "@openauthjs/openauth/client"
import { makePersisted } from "@solid-primitives/storage"
import { createAsync } from "@solidjs/router"
import {
batch,
createContext,
createEffect,
createResource,
createSignal,
onMount,
ParentProps,
Show,
Suspense,
useContext,
} from "solid-js"
import { createStore, produce } from "solid-js/store"
import { isServer } from "solid-js/web"
interface Storage {
subjects: Record<string, SubjectInfo>
current?: string
}
interface Context {
all: Record<string, SubjectInfo>
subject?: SubjectInfo
switch(id: string): void
logout(id: string): void
access(id?: string): Promise<string | undefined>
authorize(opts?: AuthorizeOptions): void
}
export interface AuthorizeOptions {
redirectPath?: string
provider?: string
}
interface SubjectInfo {
id: string
refresh: string
}
interface AuthContextOpts {
issuer: string
clientID: string
}
const context = createContext<Context>()
export function OpenAuthProvider(props: ParentProps<AuthContextOpts>) {
const client = createClient({
issuer: props.issuer,
clientID: props.clientID,
})
const [storage, setStorage] = makePersisted(
createStore<Storage>({
subjects: {},
}),
{
name: `${props.issuer}.auth`,
},
)
const resource = createAsync(async () => {
if (isServer) return true
const hash = new URLSearchParams(window.location.search.substring(1))
const code = hash.get("code")
const state = hash.get("state")
if (code && state) {
const oldState = sessionStorage.getItem("openauth.state")
const verifier = sessionStorage.getItem("openauth.verifier")
const redirect = sessionStorage.getItem("openauth.redirect")
if (redirect && verifier && oldState === state) {
const result = await client.exchange(code, redirect, verifier)
if (!result.err) {
const id = result.tokens.refresh.split(":").slice(0, -1).join(":")
batch(() => {
setStorage("subjects", id, {
id: id,
refresh: result.tokens.refresh,
})
setStorage("current", id)
})
}
}
}
return true
})
async function authorize(opts?: AuthorizeOptions) {
const redirect = new URL(window.location.origin + (opts?.redirectPath ?? "/")).toString()
const authorize = await client.authorize(redirect, "code", {
pkce: true,
provider: opts?.provider,
})
sessionStorage.setItem("openauth.state", authorize.challenge.state)
sessionStorage.setItem("openauth.redirect", redirect)
if (authorize.challenge.verifier) sessionStorage.setItem("openauth.verifier", authorize.challenge.verifier)
window.location.href = authorize.url
}
const accessCache = new Map<string, string>()
const pendingRequests = new Map<string, Promise<any>>()
async function access(id: string) {
const pending = pendingRequests.get(id)
if (pending) return pending
const promise = (async () => {
const existing = accessCache.get(id)
const subject = storage.subjects[id]
const access = await client.refresh(subject.refresh, {
access: existing,
})
if (access.err) {
pendingRequests.delete(id)
ctx.logout(id)
return
}
if (access.tokens) {
setStorage("subjects", id, "refresh", access.tokens.refresh)
accessCache.set(id, access.tokens.access)
}
pendingRequests.delete(id)
return access.tokens?.access || existing!
})()
pendingRequests.set(id, promise)
return promise
}
const ctx: Context = {
get all() {
return storage.subjects
},
get subject() {
if (!storage.current) return
return storage.subjects[storage.current!]
},
switch(id: string) {
if (!storage.subjects[id]) return
setStorage("current", id)
},
authorize,
logout(id: string) {
if (!storage.subjects[id]) return
setStorage(
produce((s) => {
delete s.subjects[id]
if (s.current === id) s.current = Object.keys(s.subjects)[0]
}),
)
},
async access(id?: string) {
id = id || storage.current
if (!id) return
return access(id || storage.current!)
},
}
createEffect(() => {
if (!resource()) return
if (storage.current) return
const [first] = Object.keys(storage.subjects)
if (first) {
setStorage("current", first)
return
}
})
return (
<>
{resource()}
<context.Provider value={ctx}>{props.children}</context.Provider>
</>
)
}
export function useOpenAuth() {
const result = useContext(context)
if (!result) throw new Error("no auth context")
return result
}
@@ -0,0 +1,39 @@
import { createStore } from "solid-js/store"
import { makePersisted } from "@solid-primitives/storage"
import { createEffect } from "solid-js"
import { createInitializedContext } from "../util/context"
import { isServer } from "solid-js/web"
interface Storage {
mode: "light" | "dark"
}
export const { provider: ThemeProvider, use: useTheme } =
createInitializedContext("ThemeContext", () => {
const [store, setStore] = makePersisted(
createStore<Storage>({
mode:
!isServer &&
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light",
}),
{
name: "theme",
},
)
createEffect(() => {
document.documentElement.setAttribute("data-color-mode", store.mode)
})
return {
setMode(mode: Storage["mode"]) {
setStore("mode", mode)
},
get mode() {
return store.mode
},
ready: true,
}
})

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