Compare commits

...

189 Commits

Author SHA1 Message Date
opencode 417e8f619c release: v0.7.8 2025-09-12 18:09:55 +00:00
Dax Raad f2094b7bb3 temporarily disable midstream compaction 2025-09-12 14:00:54 -04:00
Dax Raad 176dc51b2e ci: exclude production branch from format workflow 2025-09-12 13:41:38 -04:00
opencode f7d9a031e6 release: v0.7.7
Format / format (push) Has been cancelled
2025-09-12 17:28:35 +00:00
Dax Raad 3e2478ebf9 undo session pruning 2025-09-12 13:20:13 -04:00
GitHub Action 1f4e8b4954 chore: format code 2025-09-12 16:19:09 +00:00
Frank 9a346a00fb wip: zen 2025-09-12 12:18:32 -04:00
Frank 0a13820927 Merge branch 'production' into dev 2025-09-12 12:04:27 -04:00
GitHub Action c5fa3ee9f8 chore: format code 2025-09-12 15:57:50 +00:00
Frank c294a18155 wip: zen 2025-09-12 11:57:14 -04:00
Frank c3dc6d6df6 wip: zen 2025-09-12 11:57:14 -04:00
GitHub Action ef3425a177 ignore: update download stats 2025-09-12 2025-09-12 12:04:14 +00:00
Dax Raad 0290b4aaf0 ignore: internal 2025-09-12 10:45:44 +00:00
opencode 4ceee53480 release: v0.7.6
Format / format (push) Has been cancelled
2025-09-12 10:45:44 +00:00
Dax Raad 469dc9095f add microcompact 2025-09-12 06:38:47 -04:00
opencode 661d50f95f release: v0.7.5
Format / format (push) Has been cancelled
2025-09-12 10:25:57 +00:00
opencode 3978a8e636 release: v0.7.4
Format / format (push) Has been cancelled
2025-09-12 10:08:33 +00:00
Dax Raad 983e3b2ee3 fix compaction issues 2025-09-12 06:01:11 -04:00
GitHub Action 1bd198eb34 chore: format code 2025-09-11 22:34:21 +00:00
GitHub Action 3c502861a7 chore: format code 2025-09-11 22:30:52 +00:00
Frank a52b352b24 wip: zen 2025-09-11 18:30:13 -04:00
Dax Raad 79c73267cf wip: zen 2025-09-11 18:20:37 -04:00
opencode 54f7fb5019 release: v0.7.3
Format / format (push) Has been cancelled
2025-09-11 21:38:17 +00:00
Frank dd97d784b6 Merge branch 'production' into dev 2025-09-11 17:28:51 -04:00
GitHub Action 91832bd5d7 chore: format code 2025-09-11 21:22:56 +00:00
Frank 3abca8fd4b wip: zen 2025-09-11 17:22:05 -04:00
Dax Raad f5b3992479 properly support model level npm definition 2025-09-11 16:22:44 -04:00
Chris Covington 53f1f16122 feat: Add an experimental option to disable paste summaries (#2552)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-11 14:21:08 -05:00
Aiden Cline 4614e4983e fix: command being passed as arg when no args present (#2553) 2025-09-11 13:03:12 -05:00
opencode 84f0c63fa1 release: v0.7.2
Format / format (push) Has been cancelled
2025-09-11 17:02:59 +00:00
Dax Raad 3e9b451fb4 reduce LSP verbosity 2025-09-11 12:54:12 -04:00
Dax Raad 4ccf683527 remove block anchor edit 2025-09-11 12:53:10 -04:00
GitHub Action b236ca9047 ignore: update download stats 2025-09-11 2025-09-11 12:04:26 +00:00
Dax Raad aa9ebe5d7c ignore: compacting 2025-09-11 02:31:28 -04:00
Dax Raad 4c94753eda compaction improvements 2025-09-11 02:22:51 -04:00
GitHub Action c3a55c35bb chore: format code 2025-09-11 05:33:59 +00:00
Frank d5275010d5 wip: zen 2025-09-11 01:33:23 -04:00
Frank dedfa563c2 wip: zen 2025-09-11 01:32:06 -04:00
GitHub Action 37b6a55eb1 chore: format code 2025-09-11 04:00:17 +00:00
GitHub Action 7aa57accf5 chore: format code 2025-09-11 03:59:39 +00:00
Jay V c2fa28c1be ignore: zen 2025-09-10 17:59:03 -10:00
Jay V 30aae66320 docs: lander 2025-09-10 16:42:32 -10:00
Jay V 7b95190df3 docs: add twitter 2025-09-10 11:47:41 -10:00
Frank fa3e7bb9b0 wip: zen 2025-09-10 17:39:28 -04:00
Emmanuel LOUISY-GABRIEL 5b56848c3d Update providers.mdx because of small typo (#2539) 2025-09-10 15:49:25 -05:00
Aiden Cline 780e532094 resolve nested commands (#2537) 2025-09-10 14:05:26 -05:00
Aiden Cline 29310957c8 fix: handle @dir in command (#2533) 2025-09-10 13:27:44 -05:00
opencode 2b0577c725 release: v0.7.1
Format / format (push) Has been cancelled
2025-09-10 15:40:31 +00:00
Dax Raad bcd656ffae fix issue with flags being parsed incorrectly 2025-09-10 11:34:39 -04:00
GitHub Action 0e0c5a9b68 ignore: update download stats 2025-09-10 2025-09-10 12:04:18 +00:00
opencode d36fcc4f8e release: v0.7.0
Format / format (push) Has been cancelled
2025-09-10 08:42:45 +00:00
Dax Raad ea82b60d7d ci: stuff 2025-09-10 04:35:49 -04:00
Dax Raad ea0285a96c ci: stuff 2025-09-10 04:27:23 -04:00
Dax Raad 6960408ca2 ci: bump version 2025-09-10 04:23:57 -04:00
GitHub Action fa36195492 chore: format code 2025-09-10 07:40:01 +00:00
Dax Raad a6265ea3d2 upgrade to latest bun 2025-09-10 03:36:42 -04:00
Dax Raad bb3f02b8bb wip: ignore 2025-09-10 03:13:42 -04:00
Aiden Cline bdc0f7c86d tweak: wrap build-switch w/ system-reminder (#2525) 2025-09-09 23:57:13 -05:00
GitHub Action c8ca036834 chore: format code 2025-09-10 03:49:07 +00:00
Dax Raad 8c7fee7840 ci: fix 2025-09-09 23:48:35 -04:00
Dax Raad e53fb7f8ed ci: format 2025-09-09 23:47:47 -04:00
Dax Raad b05cbc9101 ci: format 2025-09-09 23:44:04 -04:00
Dax Raad 38e8c42cf0 ci: format 2025-09-09 23:44:04 -04:00
opencode 58fe884327 release: v0.6.10 2025-09-10 03:32:47 +00:00
Dax Raad e69d10b6c9 repair tool calls when casing is wrong 2025-09-09 23:25:27 -04:00
opencode 10aee9755c release: v0.6.9 2025-09-09 21:17:41 +00:00
Frank 63384bc214 wip: zen 2025-09-09 16:40:12 -04:00
Frank 2508e06c58 wip: zen 2025-09-09 16:32:56 -04:00
Frank 6487d0607b wip: zen 2025-09-09 16:15:35 -04:00
Frank a3513244f1 wip: zen 2025-09-09 15:47:28 -04:00
madflow 32b47fcc1e feat: svelte lsp (#2508) 2025-09-09 13:59:58 -05:00
Aiden Cline fde03d3c93 fix: exit code being non zero when using run cmd (#2523) 2025-09-09 12:00:55 -05:00
GitHub Action 9045f13acc ignore: update download stats 2025-09-09 2025-09-09 12:04:32 +00:00
Frank 74f0edc7a8 wip: zen 2025-09-09 05:42:15 -04:00
opencode dcabafcdce release: v0.6.8 2025-09-09 07:40:23 +00:00
Frank 02e8242c3b Remove debug logging 2025-09-09 03:35:09 -04:00
opencode 57e26bd2fe release: v0.6.7 2025-09-09 07:23:01 +00:00
Frank 0f263bfefe Hide experimental models 2025-09-09 03:16:44 -04:00
Frank 34a33dfc16 wip: zen 2025-09-09 02:44:36 -04:00
Aiden Cline 162a789fa2 remove edit tool from plan agent (#2505) 2025-09-08 22:00:14 -05:00
Frank 198a753b62 Merge branch 'production' into dev 2025-09-08 16:37:38 -04:00
Zack Jackson ab3c22b77a feat: add dynamic tool registration for plugins and external services (#2420) 2025-09-08 16:25:04 -04:00
opencode f0f6e9cad7 release: v0.6.6 2025-09-08 20:20:35 +00:00
Mani Sundararajan bbaae459c6 feat: make npm package install work on windows (#2419) 2025-09-08 16:14:18 -04:00
Frank eb3c820fb8 wip: zen 2025-09-08 15:57:29 -04:00
Frank 3468808fc6 wip: zen 2025-09-08 15:51:01 -04:00
Frank cd42503e2c Zen: telemetry 2025-09-08 15:46:59 -04:00
Aiden Cline 1cea8b9e77 tweak: reenable todowrite & todoread for qwen models (#2499) 2025-09-08 13:21:16 -05:00
Douglas Dennis d8fd7b155f fix: aws bedrock add check for govcloud (#2495) 2025-09-08 11:54:06 -05:00
GitHub Action 248a644fb0 ignore: update download stats 2025-09-08 2025-09-08 12:04:58 +00:00
Aiden Cline c8ff81bae4 fix: silent error if bad flag was passed (#2486) 2025-09-07 23:14:38 -05:00
Aiden Cline 74469a0d3d fix: shell invocations are dropped if last interaction was revert (#2485) 2025-09-07 21:45:13 -05:00
Aiden Cline 4d481dea7e fix: dont paste collapse if in bash mode (#2482) 2025-09-07 20:24:49 -05:00
opencode 7df32eac2a release: v0.6.5 2025-09-07 19:44:44 +00:00
Ytzhak 4654fb88de fix: max output tokens when setting budget thinking tokens (#2056)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-07 13:15:53 -05:00
GitHub Action e915a3720e ignore: update download stats 2025-09-07 2025-09-07 12:03:51 +00:00
Aiden Cline 93c2f5060e fix: title gen w/ gpt-5-nano (#2473) 2025-09-06 22:50:16 -05:00
Aiden Cline 564143071e fix: title not generated if first msg is shell invocation (#2451) 2025-09-06 09:47:42 -05:00
GitHub Action 3cdfc529a0 ignore: update download stats 2025-09-06 2025-09-06 12:03:41 +00:00
Aiden Cline bffe547417 fix: command model selection (#2448) 2025-09-05 20:54:39 -05:00
Aiden Cline dc99005e65 fix: default to last used model (#2443) 2025-09-05 17:25:25 -05:00
Adam 8ffedbe157 fix: file read response 2025-09-05 15:58:56 -05:00
spoons-and-mirrors 900fe5ca04 tweak(edit): separate edit tool error message with clearer guidance to avoid llm doom editing loop (#2051) 2025-09-05 12:36:13 -04:00
GitHub Action 66a5d58221 ignore: update download stats 2025-09-05 2025-09-05 12:04:16 +00:00
Jay V 48328bec6e docs: fix lander 2025-09-04 17:44:32 -10:00
Michał Olender 8426a0d595 Update index.tsx (#2423) 2025-09-04 17:44:26 -10:00
Aiden Cline 9186c3feae fix: webfetch prompt mistake (#2424) 2025-09-04 13:35:25 -05:00
Adam f171250033 fix: better file/content return 2025-09-04 12:39:49 -05:00
GitHub Action d440ba32ab ignore: update download stats 2025-09-04 2025-09-04 12:04:06 +00:00
Adam f7ab6beaf3 fix: worktree file/content never includes patch 2025-09-04 06:10:07 -05:00
Jay V 85ac243752 ignore: 404 2025-09-04 01:39:54 -07:00
Jay V 03522471a1 docs: fix 2025-09-04 01:03:23 -07:00
Jay V 42b440be0c docs: handle base path 2025-09-04 00:53:45 -07:00
Jay V 133ae42c55 ignore: zen 2025-09-04 00:17:06 -07:00
Zack Jackson e001af2709 feat: add createOpencodeTui() function to SDK for programmatic TUI launching (#2410) 2025-09-04 02:49:44 -04:00
Aiden Cline a97612287f fix: file fuzzy search (#2409) 2025-09-03 23:20:16 -05:00
Jay V d13467d869 ignore: mobile styles 2025-09-03 19:33:01 -07:00
Jay V 368bd97952 ignore: lander 2025-09-03 19:05:49 -07:00
Jay V d0104278fa docs: lander 2025-09-03 18:30:04 -07:00
Jay V 222244719b ignore: cloud 2025-09-03 17:21:46 -07:00
Jay V 21008d733f docs: link 2025-09-03 17:12:51 -07:00
Jay V 2808e95ac7 ignore: zen 2025-09-03 15:53:31 -07:00
Frank 70e0d71ac2 wip console 2025-09-03 15:57:41 -04:00
Frank 93f507d330 wip console 2025-09-03 15:35:46 -04:00
Dax Raad 7119ace940 wip: cloud 2025-09-03 14:57:02 -04:00
Dax Raad 4e24e04aec ignore: opencode auth stuff 2025-09-03 14:43:50 -04:00
Dax Raad 469a83a55b wip: email 2025-09-03 14:32:05 -04:00
Dax Raad e8f54b9b38 wip: fix logout 2025-09-03 14:24:37 -04:00
Frank 01b18456a3 wip console 2025-09-03 14:09:39 -04:00
Frank 1acd5445b5 wip console 2025-09-03 14:07:57 -04:00
Dax Raad ff89305ebe wip: logout 2025-09-03 14:07:23 -04:00
Dax Raad 605f78944d wip: css 2025-09-03 14:05:10 -04:00
Dax Raad 9f7e14dc7e wip: css hack 2025-09-03 13:58:08 -04:00
Dax Raad cb7f3cf2f1 wip: cloud 2025-09-03 13:58:08 -04:00
Dax Raad 4406096974 wip: cloud 2025-09-03 13:58:08 -04:00
Frank fefaad6226 wip cloud smart 2025-09-03 13:52:01 -04:00
Frank 3f9b569575 script package 2025-09-03 13:43:32 -04:00
Dax Raad 1e4f5710aa wip: cloud 2025-09-03 13:17:32 -04:00
Dax Raad 48a79b1173 wip: make less shit 2025-09-03 13:10:57 -04:00
Dax Raad 59e550271d wip: cloud 2025-09-03 12:32:03 -04:00
Frank afebe920b2 wip console 2025-09-03 11:50:18 -04:00
Frank 6921702605 wip console 2025-09-03 11:40:59 -04:00
Frank 9c6783e88e wip console 2025-09-03 11:31:16 -04:00
Frank f1a60a0a93 wip: generate config.json 2025-09-03 11:24:13 -04:00
Jay V 4b9cae82e6 ignore: zen 2025-09-03 11:16:49 -04:00
Jay V fdf08ecfab ignore: zen 2025-09-03 11:10:07 -04:00
Jay V 22f5c26eec docs: edits 2025-09-03 11:05:43 -04:00
opencode b6de122ddc release: v0.6.4 2025-09-03 13:31:11 +00:00
Frank 0f8cb69bff wip console 2025-09-03 09:24:23 -04:00
GitHub Action fca2bddc3b ignore: update download stats 2025-09-03 2025-09-03 12:04:06 +00:00
Frank f65e20b8ce wip console 2025-09-03 06:53:30 -04:00
Frank 93f2805bc2 wip: console 2025-09-03 06:37:40 -04:00
Frank 9ad4dc9296 wip: console 2025-09-03 06:27:53 -04:00
Frank 23af974bd3 wip: console 2025-09-03 06:22:44 -04:00
Frank 36ea46ee67 wip: console 2025-09-03 06:15:08 -04:00
Frank 4d2cc9d858 wip: console 2025-09-03 06:12:17 -04:00
Dax Raad 610ffbdd61 wip: console 2025-09-03 01:33:49 -04:00
Brendan Allan 854f9227a2 Patch Start to preload route css in SSR (#2389) 2025-09-03 01:28:34 -04:00
Dax Raad 8d368fdfd2 wip: zen 2025-09-02 23:56:10 -04:00
Dax Raad 1c31c2dd97 wip: zen 2025-09-02 23:30:48 -04:00
Frank c1d754bec9 wip cloud 2025-09-02 23:28:54 -04:00
Dax Raad c67b721787 docs: remove remaining directory query param mentions from SDK docs 2025-09-02 22:25:32 -04:00
Dax Raad 11e41e7564 docs: remove directory query param mentions from SDK docs 2025-09-02 22:25:32 -04:00
Dax Raad afd42bf46d docs: fix SDK usage to use path/query/body, correct return types, and update examples 2025-09-02 22:25:32 -04:00
Aiden Cline f740663ded fix: more durable @ references for commands (#2386) 2025-09-02 21:24:56 -05:00
Jay V 751b81af34 docs: zen 2025-09-02 21:29:03 -04:00
Jay V b725bcd2cd ignore: adding public files 2025-09-02 21:25:09 -04:00
Frank c278e16e4e generate api key 2025-09-02 20:38:36 -04:00
Frank 4e629c5b64 wip: cloud 2025-09-02 20:01:13 -04:00
Dax Raad 4624f0a260 ci: ignore 2025-09-02 19:32:21 -04:00
Dax Raad 2e16d685eb wip: zen 2025-09-02 18:00:48 -04:00
Jay V e544cccc70 ignore: zen 2025-09-02 17:30:51 -04:00
Jay V c141b88087 ignore: zen 2025-09-02 17:28:35 -04:00
Jay V 023c4532c1 ignore: cloud lander 2025-09-02 17:28:35 -04:00
Dax Raad 042802848d wip: zen 2025-09-02 16:38:50 -04:00
Dax Raad a8aa44bd3f docs: simplify config example to show only model 2025-09-02 16:38:50 -04:00
Dax Raad db2a3a171e docs: clarify config behavior and remove theme example 2025-09-02 16:38:50 -04:00
Dax Raad 38a4bee1be docs: add config example to SDK server creation 2025-09-02 16:38:50 -04:00
Dax Raad 8952b3d246 support OPENCODE_CONFIG_CONTENT 2025-09-02 16:38:50 -04:00
Aiden Cline d6350a7fa6 tweak: update ls tool to use rg (#2367) 2025-09-02 10:40:20 -05:00
Yuta URANO ae83138832 docs: update log level configuration in troubleshooting guide (#2374) 2025-09-02 10:31:04 -05:00
OpeOginni 3ee4280dfa fix: local subdirectory subagents not being picked up (#2376) 2025-09-02 09:46:00 -05:00
GitHub Action 26fbf9e647 ignore: update download stats 2025-09-02 2025-09-02 12:04:59 +00:00
Adam 97a41062c9 fix: file.list relative to root 2025-09-02 06:20:08 -05:00
Dax Raad 4a76224268 wip: typechecking 2025-09-02 03:18:30 -04:00
Dax Raad 810c9cff1d wip: cloud 2025-09-02 03:18:30 -04:00
Adam Spiers 47d4c87bdd make @file references in custom slash commands more robust (#2203)
Co-authored-by: Adam Spiers <opencode@adamspiers.org>
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-01 21:14:27 -05:00
opencode a9875c5531 release: v0.6.3 2025-09-02 01:52:01 +00:00
Dax Raad 4c261ab1db switch gpt-5 to default to codex prompt + high reasoning 2025-09-01 21:46:03 -04:00
226 changed files with 10394 additions and 5735 deletions
+3 -1
View File
@@ -17,11 +17,13 @@ jobs:
- uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.19
bun-version: 1.2.21
- run: bun install
- run: bun sst deploy --stage=${{ github.ref_name }}
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
+32
View File
@@ -0,0 +1,32 @@
name: Format
on:
push:
branches-ignore:
- production
pull_request:
branches-ignore:
- production
workflow_dispatch:
jobs:
format:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.21
- name: run
run: |
bun install
./script/format.ts
env:
CI: true
+1 -1
View File
@@ -2,7 +2,7 @@ name: discord
on:
release:
types: [published] # fires only when a release is published
types: [published] # fires only when a release is published
jobs:
notify:
+1 -1
View File
@@ -24,4 +24,4 @@ jobs:
env:
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
with:
model: opencode/sonic
model: opencode/sonic
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.19
bun-version: 1.2.21
- run: git fetch --force --tags
- run: bun install -g @vscode/vsce
+13 -12
View File
@@ -1,17 +1,17 @@
name: publish
run-name: "${{ format('v{0}', inputs.version) }}"
run-name: "${{ format('release {0}', inputs.bump) }}"
on:
workflow_dispatch:
inputs:
version:
description: "Version to publish"
bump:
description: "Bump major, minor, or patch"
required: true
type: string
title:
description: "Custom title for this run"
required: false
type: string
type: choice
options:
- major
- minor
- patch
concurrency: ${{ github.workflow }}-${{ github.ref }}
@@ -37,16 +37,16 @@ jobs:
- uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.19
bun-version: 1.2.21
- name: Cache ~/.bun
id: cache-bun
uses: actions/cache@v3
with:
path: ~/.bun
key: ${{ runner.os }}-bun-${{ hashFiles('bun.lock') }}
key: ${{ runner.os }}-bun-1-2-21-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-
${{ runner.os }}-bun-1-2-21-
- name: Install makepkg
run: |
@@ -65,8 +65,9 @@ jobs:
- name: Publish
run: |
OPENCODE_VERSION=${{ inputs.version }} ./script/publish.ts
./script/publish.ts
env:
OPENCODE_BUMP: ${{ inputs.bump }}
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.19
bun-version: 1.2.21
- name: Install dependencies
run: bun install
+1 -1
View File
@@ -107,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/anomaly_inv)
**Join our community** [Discord](https://discord.gg/opencode) | [X.com](https://x.com/opencode)
+11
View File
@@ -66,3 +66,14 @@
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
| 2025-09-04 | 280,430 (+5,637) | 222,103 (+2,348) | 502,533 (+7,985) |
| 2025-09-05 | 283,769 (+3,339) | 223,793 (+1,690) | 507,562 (+5,029) |
| 2025-09-06 | 286,245 (+2,476) | 225,036 (+1,243) | 511,281 (+3,719) |
| 2025-09-07 | 288,623 (+2,378) | 225,866 (+830) | 514,489 (+3,208) |
| 2025-09-08 | 293,341 (+4,718) | 227,073 (+1,207) | 520,414 (+5,925) |
| 2025-09-09 | 300,036 (+6,695) | 229,788 (+2,715) | 529,824 (+9,410) |
| 2025-09-10 | 307,287 (+7,251) | 233,435 (+3,647) | 540,722 (+10,898) |
| 2025-09-11 | 314,083 (+6,796) | 237,356 (+3,921) | 551,439 (+10,717) |
| 2025-09-12 | 321,046 (+6,963) | 240,728 (+3,372) | 561,774 (+10,335) |
+67 -19
View File
@@ -8,7 +8,7 @@
},
"devDependencies": {
"prettier": "3.5.3",
"sst": "3.17.12",
"sst": "3.17.13",
},
},
"cloud/app": {
@@ -26,10 +26,11 @@
},
"cloud/core": {
"name": "@opencode/cloud-core",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@opencode/cloud-resource": "workspace:*",
"@planetscale/database": "1.19.0",
"drizzle-orm": "0.41.0",
"postgres": "3.4.7",
"stripe": "18.0.0",
@@ -37,11 +38,12 @@
},
"devDependencies": {
"drizzle-kit": "0.30.5",
"mysql2": "3.14.4",
},
},
"cloud/function": {
"name": "@opencode/cloud-function",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -65,9 +67,21 @@
"@cloudflare/workers-types": "^4.20250830.0",
},
},
"cloud/scripts": {
"name": "@opencode/cloud-scripts",
"version": "0.7.6",
"dependencies": {
"@opencode/cloud-core": "workspace:*",
"tsx": "4.20.5",
},
"devDependencies": {
"@types/node": "catalog:",
"typescript": "catalog:",
},
},
"packages/function": {
"name": "@opencode/function",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0",
@@ -82,7 +96,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "0.5.28",
"version": "0.7.6",
"bin": {
"opencode": "./bin/opencode",
},
@@ -122,7 +136,7 @@
"@octokit/webhooks-types": "7.6.1",
"@standard-schema/spec": "1.0.0",
"@tsconfig/bun": "1.0.7",
"@types/bun": "latest",
"@types/bun": "catalog:",
"@types/turndown": "5.0.5",
"@types/yargs": "17.0.33",
"typescript": "catalog:",
@@ -132,7 +146,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
},
@@ -143,7 +157,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@hey-api/openapi-ts": "0.81.0",
},
@@ -155,7 +169,7 @@
},
"packages/web": {
"name": "@opencode/web",
"version": "0.5.28",
"version": "0.7.6",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -194,12 +208,16 @@
"web-tree-sitter",
"tree-sitter-bash",
],
"patchedDependencies": {
"@solidjs/start@1.1.7": "patches/@solidjs%2Fstart@1.1.7.patch",
},
"overrides": {
"zod": "3.25.76",
},
"catalog": {
"@hono/zod-validator": "0.4.2",
"@tsconfig/node22": "22.0.2",
"@types/bun": "1.2.21",
"@types/node": "22.13.9",
"ai": "5.0.8",
"hono": "4.7.10",
@@ -661,6 +679,8 @@
"@opencode/cloud-resource": ["@opencode/cloud-resource@workspace:cloud/resource"],
"@opencode/cloud-scripts": ["@opencode/cloud-scripts@workspace:cloud/scripts"],
"@opencode/function": ["@opencode/function@workspace:packages/function"],
"@opencode/web": ["@opencode/web@workspace:packages/web"],
@@ -747,6 +767,8 @@
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
"@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="],
"@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="],
"@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="],
@@ -1161,6 +1183,8 @@
"aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="],
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
"aws4fetch": ["aws4fetch@1.0.18", "", {}, "sha512-3Cf+YaUl07p24MoQ46rFwulAmiyCwH2+1zw1ZyPAX5OtJ34Hh185DwB8y/qRLb6cYYYtSFJ9pthyLc0MD4e8sQ=="],
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
@@ -1631,6 +1655,8 @@
"gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="],
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
"get-amd-module-type": ["get-amd-module-type@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ=="],
@@ -1769,7 +1795,7 @@
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
"ieee754": ["ieee754@1.1.13", "", {}, "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="],
@@ -1845,6 +1871,8 @@
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
"is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
@@ -1957,6 +1985,8 @@
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
"lru.min": ["lru.min@1.1.2", "", {}, "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg=="],
"luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="],
"magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="],
@@ -2145,6 +2175,10 @@
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"mysql2": ["mysql2@3.14.4", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-Cs/jx3WZPNrYHVz+Iunp9ziahaG5uFMvD2R8Zlmc194AqXNxt9HBNu7ZsPYrUtmJsF0egETCWIdMIYAwOGjL1w=="],
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
@@ -2523,6 +2557,8 @@
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
"seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="],
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
"seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
@@ -2607,25 +2643,27 @@
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
"ssri": ["ssri@10.0.6", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ=="],
"sst": ["sst@3.17.12", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.12", "sst-darwin-x64": "3.17.12", "sst-linux-arm64": "3.17.12", "sst-linux-x64": "3.17.12", "sst-linux-x86": "3.17.12", "sst-win32-arm64": "3.17.12", "sst-win32-x64": "3.17.12", "sst-win32-x86": "3.17.12" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-UwUbucNZRLp9GHgPAwkat1sBsNGaJfHsLXZHCMKsolCW7CEuugJfvBl2vOyJrhKP4N+Xnv1QFh0BGsOmN0kQeA=="],
"sst": ["sst@3.17.13", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.13", "sst-darwin-x64": "3.17.13", "sst-linux-arm64": "3.17.13", "sst-linux-x64": "3.17.13", "sst-linux-x86": "3.17.13", "sst-win32-arm64": "3.17.13", "sst-win32-x64": "3.17.13", "sst-win32-x86": "3.17.13" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-NaNTZL7uk2AsXzPBySQy7aqXlStXorR+bA785NxvCbskwkc44nVSQcEsvX5tdsD6/jrWpw9tDy4sStv2ycLAng=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.17.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9Oky2ZmJoeEN97ALWtFRt3kvSIZLjYoQoOtJvTaNQJTFi/9OsUE/6I5zdedf5GhMKCT1JvY+Ngpv3U3Y6SEYOg=="],
"sst-darwin-arm64": ["sst-darwin-arm64@3.17.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HZaDReT/c+2CcEnFkYjMty63II2ckQrUniiSdoEH6eAWyU1Iy7UwKK4I2GYm+5dy9xeSBaOKga6FMdLqFxIiUg=="],
"sst-darwin-x64": ["sst-darwin-x64@3.17.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-n6tWCjFF9Pb+QzxXJmuTGfQ4GW96Nf6ATtb7Wpa+9RDLRHrEBdOjXAp7osr7MB9djPRkt4942nwUZ7wX/EULpg=="],
"sst-darwin-x64": ["sst-darwin-x64@3.17.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-1DlYMrmrI5RY3/Ob039JatgvDKZ5QNtyRkVu0WcnsOvcxFE4dzrCiYKYHg2A+FMDl+H1qcwy2gGA3BTwC9in1w=="],
"sst-linux-arm64": ["sst-linux-arm64@3.17.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-iMflBzQWhF5kmRdXu402dwVpQI9LfFR3yFok3HUTV0ema5Pq2kPphEatEEw1dyG2ZXCBLeKN+T3Ujjfer+ddRA=="],
"sst-linux-arm64": ["sst-linux-arm64@3.17.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-A4+ZamchUdaX0pqvYWZ+r7OP1bruwEj9qgWT5kU7Q5pqaotIsEitYQi0q9nZFKH+5mXYesUwSy5FA+Q8T3X/Rg=="],
"sst-linux-x64": ["sst-linux-x64@3.17.12", "", { "os": "linux", "cpu": "x64" }, "sha512-89rZXs3IfGrY9yiDNuLfcJvHnAUX1gRVeB+lqQ1M2sbJD2iMpN+fx93owcApAndtZYzYNfQYEZ/xYwI6HFfu4w=="],
"sst-linux-x64": ["sst-linux-x64@3.17.13", "", { "os": "linux", "cpu": "x64" }, "sha512-yhKZc5CojqjB2DnyeVka5jeRb4oc3lBx8Qf6or0w4cs47SBIqyvO0iR/3IeKvRRL1hiEUeUF8r/q83rQo9jZMw=="],
"sst-linux-x86": ["sst-linux-x86@3.17.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zc2nd2syaq/DfNxtDcn4NOh8RBCaCZ1qsjLFpvGGfMMRnGiWjofuE6eFX3fhchGL3uvaqwlENvtzj4UC/MF5wQ=="],
"sst-linux-x86": ["sst-linux-x86@3.17.13", "", { "os": "linux", "cpu": "none" }, "sha512-G1FIUmpUaECB/3CV5EO/y1QmV5mQ8RUkFeZq64oyILEEaMzSWWKz0glHzQ3+p316VE74MzbktiWRqsCKQy8GeA=="],
"sst-win32-arm64": ["sst-win32-arm64@3.17.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-Bb7M6PoImmGeyzJu75QbNHBs0mDp21DsKFyMucn2dwxYwahuFPjjMbG+tlziWtxcNgdZMdEcy9jR8ot1jAIh0Q=="],
"sst-win32-arm64": ["sst-win32-arm64@3.17.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-9uCiIXmsGoxGeNWgM81x/d6V/vecjoiHXhBUPDGlQ1Dct1SbtHAgaf/g2ec5XwSQb9B/tne4qk81eMlTl83Z1A=="],
"sst-win32-x64": ["sst-win32-x64@3.17.12", "", { "os": "win32", "cpu": "x64" }, "sha512-7f41o1WhxdcuLhHijoavkX5O3L/Pnma6zCoL3kG6f9Njc6Zyj8Oha2fQz6Tesb/Qt8deG04WU4bL3FmxgNHU6g=="],
"sst-win32-x64": ["sst-win32-x64@3.17.13", "", { "os": "win32", "cpu": "x64" }, "sha512-hTuj4rFSCI/9tX4NMUpNJ69Q9td/giekELDRNv03ys8TpJGoGvPT8D6VD1eX7j1CQnOZIgeEphfW9WmCXkLaIA=="],
"sst-win32-x86": ["sst-win32-x86@3.17.12", "", { "os": "win32", "cpu": "none" }, "sha512-AfsNJQMTlefHitaVRWh5Uf3AaICIaomFbSo5qDbibgkvhbppCxgMFpW0IxiWySjWrCN5hMMkxdxlZP9IHqqxjQ=="],
"sst-win32-x86": ["sst-win32-x86@3.17.13", "", { "os": "win32", "cpu": "none" }, "sha512-AuMDGux+H1kPckKJ7Szgi04EpBoOKh/v5zFNAPjvWSkcWcGZ+hsBUx3h/FO/AkGK3RnlLMRj4CQQLoa10RSSIg=="],
"stack-trace": ["stack-trace@0.0.10", "", {}, "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="],
@@ -2733,6 +2771,8 @@
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tsx": ["tsx@4.20.5", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw=="],
"tuf-js": ["tuf-js@2.2.1", "", { "dependencies": { "@tufjs/models": "2.0.1", "debug": "^4.3.4", "make-fetch-happen": "^13.0.1" } }, "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA=="],
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
@@ -3113,6 +3153,8 @@
"bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"body-parser/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"cacheable-request/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
@@ -3129,6 +3171,8 @@
"drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
"encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"estree-util-to-js/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
"express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
@@ -3187,6 +3231,8 @@
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"named-placeholders/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
"netlify/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
"nitropack/c12": ["c12@3.2.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ=="],
@@ -3243,6 +3289,8 @@
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
"raw-body/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
"read-pkg/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
+1
View File
@@ -1,6 +1,7 @@
import { defineConfig } from "@solidjs/start/config"
export default defineConfig({
middleware: "./src/middleware.ts",
vite: {
server: {
allowedHosts: true,
+3 -2
View File
@@ -2,11 +2,12 @@
"name": "@opencode/cloud-app",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"dev": "vinxi dev --host 0.0.0.0",
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build",
"build": "vinxi build && ../../packages/opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "0.6.2"
"version": "0.7.8"
},
"dependencies": {
"@ibm/plex": "6.4.1",
+5
View File
@@ -0,0 +1,5 @@
User-agent: *
Allow: /
# Disallow shared content pages
Disallow: /s/
+182
View File
@@ -0,0 +1,182 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"description": "JSON schema reference for configuration validation"
},
"defs": {
"type": "object",
"description": "Color definitions that can be referenced in the theme",
"patternProperties": {
"^[a-zA-Z][a-zA-Z0-9_]*$": {
"oneOf": [
{
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$",
"description": "Hex color value"
},
{
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "ANSI color code (0-255)"
},
{
"type": "string",
"enum": ["none"],
"description": "No color (uses terminal default)"
}
]
}
},
"additionalProperties": false
},
"theme": {
"type": "object",
"description": "Theme color definitions",
"properties": {
"primary": { "$ref": "#/definitions/colorValue" },
"secondary": { "$ref": "#/definitions/colorValue" },
"accent": { "$ref": "#/definitions/colorValue" },
"error": { "$ref": "#/definitions/colorValue" },
"warning": { "$ref": "#/definitions/colorValue" },
"success": { "$ref": "#/definitions/colorValue" },
"info": { "$ref": "#/definitions/colorValue" },
"text": { "$ref": "#/definitions/colorValue" },
"textMuted": { "$ref": "#/definitions/colorValue" },
"background": { "$ref": "#/definitions/colorValue" },
"backgroundPanel": { "$ref": "#/definitions/colorValue" },
"backgroundElement": { "$ref": "#/definitions/colorValue" },
"border": { "$ref": "#/definitions/colorValue" },
"borderActive": { "$ref": "#/definitions/colorValue" },
"borderSubtle": { "$ref": "#/definitions/colorValue" },
"diffAdded": { "$ref": "#/definitions/colorValue" },
"diffRemoved": { "$ref": "#/definitions/colorValue" },
"diffContext": { "$ref": "#/definitions/colorValue" },
"diffHunkHeader": { "$ref": "#/definitions/colorValue" },
"diffHighlightAdded": { "$ref": "#/definitions/colorValue" },
"diffHighlightRemoved": { "$ref": "#/definitions/colorValue" },
"diffAddedBg": { "$ref": "#/definitions/colorValue" },
"diffRemovedBg": { "$ref": "#/definitions/colorValue" },
"diffContextBg": { "$ref": "#/definitions/colorValue" },
"diffLineNumber": { "$ref": "#/definitions/colorValue" },
"diffAddedLineNumberBg": { "$ref": "#/definitions/colorValue" },
"diffRemovedLineNumberBg": { "$ref": "#/definitions/colorValue" },
"markdownText": { "$ref": "#/definitions/colorValue" },
"markdownHeading": { "$ref": "#/definitions/colorValue" },
"markdownLink": { "$ref": "#/definitions/colorValue" },
"markdownLinkText": { "$ref": "#/definitions/colorValue" },
"markdownCode": { "$ref": "#/definitions/colorValue" },
"markdownBlockQuote": { "$ref": "#/definitions/colorValue" },
"markdownEmph": { "$ref": "#/definitions/colorValue" },
"markdownStrong": { "$ref": "#/definitions/colorValue" },
"markdownHorizontalRule": { "$ref": "#/definitions/colorValue" },
"markdownListItem": { "$ref": "#/definitions/colorValue" },
"markdownListEnumeration": { "$ref": "#/definitions/colorValue" },
"markdownImage": { "$ref": "#/definitions/colorValue" },
"markdownImageText": { "$ref": "#/definitions/colorValue" },
"markdownCodeBlock": { "$ref": "#/definitions/colorValue" },
"syntaxComment": { "$ref": "#/definitions/colorValue" },
"syntaxKeyword": { "$ref": "#/definitions/colorValue" },
"syntaxFunction": { "$ref": "#/definitions/colorValue" },
"syntaxVariable": { "$ref": "#/definitions/colorValue" },
"syntaxString": { "$ref": "#/definitions/colorValue" },
"syntaxNumber": { "$ref": "#/definitions/colorValue" },
"syntaxType": { "$ref": "#/definitions/colorValue" },
"syntaxOperator": { "$ref": "#/definitions/colorValue" },
"syntaxPunctuation": { "$ref": "#/definitions/colorValue" }
},
"required": ["primary", "secondary", "accent", "text", "textMuted", "background"],
"additionalProperties": false
}
},
"required": ["theme"],
"additionalProperties": false,
"definitions": {
"colorValue": {
"oneOf": [
{
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$",
"description": "Hex color value (same for dark and light)"
},
{
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "ANSI color code (0-255, same for dark and light)"
},
{
"type": "string",
"enum": ["none"],
"description": "No color (uses terminal default)"
},
{
"type": "string",
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
"description": "Reference to another color in the theme or defs"
},
{
"type": "object",
"properties": {
"dark": {
"oneOf": [
{
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$",
"description": "Hex color value for dark mode"
},
{
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "ANSI color code for dark mode"
},
{
"type": "string",
"enum": ["none"],
"description": "No color (uses terminal default)"
},
{
"type": "string",
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
"description": "Reference to another color for dark mode"
}
]
},
"light": {
"oneOf": [
{
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$",
"description": "Hex color value for light mode"
},
{
"type": "integer",
"minimum": 0,
"maximum": 255,
"description": "ANSI color code for light mode"
},
{
"type": "string",
"enum": ["none"],
"description": "No color (uses terminal default)"
},
{
"type": "string",
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
"description": "Reference to another color for light mode"
}
]
}
},
"required": ["dark", "light"],
"additionalProperties": false,
"description": "Separate colors for dark and light modes"
}
]
}
}
}
+9 -8
View File
@@ -1,14 +1,15 @@
import { MetaProvider, Title, Meta } 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";
import { MetaProvider, Title, Meta } 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 => (
explicitLinks={true}
root={(props) => (
<MetaProvider>
<Title>opencode</Title>
<Meta name="description" content="opencode - The AI coding agent built for the terminal." />
@@ -18,5 +19,5 @@ export default function App() {
>
<FileRoutes />
</Router>
);
)
}
+45 -13
View File
@@ -6,34 +6,66 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
<path d="M264.5 0H288.5V8.5H272.5V16.5H288.5V25H272.5V33H288.5V41.5H264.5V0Z" fill="currentColor" />
<path d="M248.5 0H224.5V41.5H248.5V33H232.5V8.5H248.5V0Z" fill="currentColor" />
<path d="M256.5 8.5H248.5V33H256.5V8.5Z" fill="currentColor" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z" fill="currentColor" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M184.5 0H216.5V41.5H184.5V0ZM208.5 8.5H192.5V33H208.5V8.5Z"
fill="currentColor"
/>
<path d="M144.5 8.5H136.5V41.5H144.5V8.5Z" fill="currentColor" />
<path d="M136.5 0H112.5V41.5H120.5V8.5H136.5V0Z" fill="currentColor" />
<path d="M80.5 0H104.5V8.5H88.5V16.5H104.5V25H88.5V33H104.5V41.5H80.5V0Z" fill="currentColor" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z" fill="currentColor" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z" fill="currentColor" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M40.5 0H72.5V41.5H48.5V49.5H40.5V0ZM64.5 8.5H48.5V33H64.5V8.5Z"
fill="currentColor"
/>
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.5 0H32.5V41.5955H0.5V0ZM24.5 8.5H8.5V33H24.5V8.5Z"
fill="currentColor"
/>
<path d="M152.5 0H176.5V8.5H160.5V33H176.5V41.5H152.5V0Z" fill="currentColor" />
</svg>
);
)
}
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 {...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 {...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>
)
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { useSession } from "vinxi/http"
export interface AuthSession {
account: Record<
account?: Record<
string,
{
id: string
+60 -56
View File
@@ -2,7 +2,7 @@ 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 { redirect } from "@solidjs/router"
import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js"
import { Actor } from "@opencode/cloud-core/actor.js"
@@ -14,66 +14,70 @@ export const AuthClient = createClient({
issuer: import.meta.env.VITE_AUTH_URL,
})
export const getActor = query(async (): Promise<Actor.Info> => {
export const getActor = async (workspace?: string): Promise<Actor.Info> => {
"use server"
const evt = getRequestEvent()
if (!evt) throw new Error("No request event")
const url = new URL(evt.request.headers.has("x-server-id") ? evt.request.headers.get("referer")! : evt.request.url)
const auth = await useAuthSession()
const splits = url.pathname.split("/").filter(Boolean)
if (splits[0] !== "workspace") {
if (auth.data.current) {
const current = auth.data.account[auth.data.current]
if (evt.locals.actor) return evt.locals.actor
evt.locals.actor = (async () => {
const auth = await useAuthSession()
if (!workspace) {
const account = auth.data.account ?? {}
const current = account[auth.data.current ?? ""]
if (current) {
return {
type: "account",
properties: {
email: current.email,
accountID: current.id,
},
}
}
if (Object.keys(account).length > 0) {
const current = Object.values(account)[0]
await auth.update((val) => ({
...val,
current: current.id,
}))
return {
type: "account",
properties: {
email: current.email,
accountID: current.id,
},
}
}
return {
type: "account",
properties: {
email: current.email,
accountID: current.id,
},
type: "public",
properties: {},
}
}
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 workspaceHint = splits[1]
const accounts = Object.keys(auth.data.account ?? {})
const result = await Database.transaction(async (tx) => {
return await tx
.select({
user: UserTable,
const accounts = Object.keys(auth.data.account ?? {})
if (accounts.length) {
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, workspace)))
.limit(1)
.execute()
.then((x) => x[0])
})
.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,
},
if (result) {
return {
type: "user",
properties: {
userID: result.user.id,
workspaceID: result.user.workspaceID,
},
}
}
}
}
throw redirect("/auth/authorize")
}, "actor")
throw redirect("/auth/authorize")
})()
return evt.locals.actor
}
+2 -11
View File
@@ -1,16 +1,7 @@
import { Actor } from "@opencode/cloud-core/actor.js"
import { getActor } from "./auth"
import { query } from "@solidjs/router"
export async function withActor<T>(fn: () => T) {
const actor = await getActor()
export async function withActor<T>(fn: () => T, workspace?: string) {
const actor = await getActor(workspace)
return Actor.provide(actor.type, actor.properties, fn)
}
export function actorQuery<T>(cb: () => T, name: string) {
"use server"
return query(async () => {
const actor = await getActor()
return withActor(cb)
}, name)
}
+2 -2
View File
@@ -1,4 +1,4 @@
// @refresh reload
import { mount, StartClient } from "@solidjs/start/client";
import { mount, StartClient } from "@solidjs/start/client"
mount(() => <StartClient />, document.getElementById("app")!);
mount(() => <StartClient />, document.getElementById("app")!)
+25 -22
View File
@@ -1,25 +1,28 @@
// @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.svg" />
<meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
), {
mode: "sync",
})
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.svg" />
<meta property="og:image" content="/social-share.png" />
<meta property="twitter:image" content="/social-share.png" />
{assets}
</head>
<body>
<div id="app">{children}</div>
{scripts}
</body>
</html>
)}
/>
),
{
mode: "async",
},
)
+5
View File
@@ -0,0 +1,5 @@
import { defineMiddleware } from "vinxi/http"
export default defineMiddleware({
onBeforeResponse() {},
})
+130
View File
@@ -0,0 +1,130 @@
[data-page="not-found"] {
--color-text: hsl(224, 10%, 10%);
--color-text-secondary: hsl(224, 7%, 46%);
--color-text-dimmed: hsl(224, 6%, 63%);
--color-text-inverted: hsl(0, 0%, 100%);
--color-border: hsl(224, 6%, 77%);
}
[data-page="not-found"] {
@media (prefers-color-scheme: dark) {
--color-text: hsl(0, 0%, 100%);
--color-text-secondary: hsl(224, 6%, 66%);
--color-text-dimmed: hsl(224, 7%, 46%);
--color-text-inverted: hsl(224, 10%, 10%);
--color-border: hsl(224, 6%, 36%);
}
}
[data-page="not-found"] {
--padding: 3rem;
--vertical-padding: 1.5rem;
--heading-font-size: 1.375rem;
@media (max-width: 30rem) {
--padding: 1rem;
--vertical-padding: 0.75rem;
--heading-font-size: 1rem;
}
font-family: var(--font-mono);
color: var(--color-text);
padding: calc(var(--padding) + 1rem);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
a {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: var(--space-0-75);
text-decoration-thickness: 1px;
}
[data-component="content"] {
max-width: 40rem;
width: 100%;
border: 1px solid var(--color-border);
}
[data-component="top"] {
padding: var(--padding);
display: flex;
flex-direction: column;
align-items: center;
gap: calc(var(--vertical-padding) / 2);
text-align: center;
[data-slot="logo-link"] {
text-decoration: none;
}
img {
height: auto;
width: clamp(200px, 85vw, 400px);
}
[data-slot="logo dark"] {
display: none;
}
@media (prefers-color-scheme: dark) {
[data-slot="logo light"] {
display: none;
}
[data-slot="logo dark"] {
display: block;
}
}
[data-slot="title"] {
line-height: 1.25;
font-weight: 500;
text-align: center;
font-size: var(--heading-font-size);
color: var(--color-text);
text-transform: uppercase;
margin: 0;
}
}
[data-component="actions"] {
border-top: 1px solid var(--color-border);
display: flex;
[data-slot="action"] {
flex: 1;
text-align: center;
line-height: 1.4;
padding: var(--vertical-padding) 1rem;
text-transform: uppercase;
font-size: 1rem;
a {
display: block;
width: 100%;
height: 100%;
color: var(--color-text);
text-decoration: underline;
text-underline-offset: var(--space-0-75);
text-decoration-thickness: 1px;
}
}
[data-slot="action"] + [data-slot="action"] {
border-left: 1px solid var(--color-border);
}
@media (max-width: 40rem) {
flex-direction: column;
[data-slot="action"] + [data-slot="action"] {
border-left: none;
border-top: 1px solid var(--color-border);
}
}
}
}
+32 -13
View File
@@ -1,19 +1,38 @@
import { Title } from "@solidjs/meta";
import { HttpStatusCode } from "@solidjs/start";
import "./[...404].css"
import { Title } from "@solidjs/meta"
import { HttpStatusCode } from "@solidjs/start"
import logoLight from "../asset/logo-ornate-light.svg"
import logoDark from "../asset/logo-ornate-dark.svg"
export default function NotFound() {
return (
<main>
<Title>Not Found</Title>
<main data-page="not-found">
<Title>Not Found | opencode</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>
<div data-component="content">
<section data-component="top">
<a href="/" data-slot="logo-link">
<img data-slot="logo light" src={logoLight} alt="opencode logo light" />
<img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
</a>
<h1 data-slot="title">404 - Page Not Found</h1>
</section>
<section data-component="actions">
<div data-slot="action">
<a href="/">Home</a>
</div>
<div data-slot="action">
<a href="/docs">Docs</a>
</div>
<div data-slot="action">
<a href="https://github.com/sst/opencode">GitHub</a>
</div>
<div data-slot="action">
<a href="/discord">Discord</a>
</div>
</section>
</div>
</main>
);
)
}
+1 -1
View File
@@ -27,5 +27,5 @@ export async function GET(input: APIEvent) {
current: id,
}
})
return redirect("/")
return redirect("/auth")
}
+13
View File
@@ -0,0 +1,13 @@
import { Account } from "@opencode/cloud-core/account.js"
import { redirect } from "@solidjs/router"
import type { APIEvent } from "@solidjs/start/server"
import { withActor } from "~/context/auth.withActor"
export async function GET(input: APIEvent) {
try {
const workspaces = await withActor(async () => Account.workspaces())
return redirect(`/workspace/${workspaces[0].id}`)
} catch {
return redirect("/auth/authorize")
}
}
-7
View File
@@ -1,7 +0,0 @@
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)
}
+5
View File
@@ -0,0 +1,5 @@
import { redirect } from "@solidjs/router"
export async function GET() {
return redirect("https://discord.gg/opencode")
}
+20
View File
@@ -0,0 +1,20 @@
import type { APIEvent } from "@solidjs/start/server"
async function handler(evt: APIEvent) {
const req = evt.request.clone()
const url = new URL(req.url)
const targetUrl = `https://docs.opencode.ai${url.pathname}${url.search}`
const response = await fetch(targetUrl, {
method: req.method,
headers: req.headers,
body: req.body,
})
return response
}
export const GET = handler
export const POST = handler
export const PUT = handler
export const DELETE = handler
export const OPTIONS = handler
export const PATCH = handler
+20
View File
@@ -0,0 +1,20 @@
import type { APIEvent } from "@solidjs/start/server"
async function handler(evt: APIEvent) {
const req = evt.request.clone()
const url = new URL(req.url)
const targetUrl = `https://docs.opencode.ai${url.pathname}${url.search}`
const response = await fetch(targetUrl, {
method: req.method,
headers: req.headers,
body: req.body,
})
return response
}
export const GET = handler
export const POST = handler
export const PUT = handler
export const DELETE = handler
export const OPTIONS = handler
export const PATCH = handler
@@ -1,576 +0,0 @@
import { Resource } from "@opencode/cloud-resource"
import { Billing } from "@opencode/cloud-core/billing.js"
import type { APIEvent } from "@solidjs/start/server"
import { Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js"
import { BillingTable, PaymentTable } from "@opencode/cloud-core/schema/billing.sql.js"
import { Identifier } from "@opencode/cloud-core/identifier.js"
import { centsToMicroCents } from "@opencode/cloud-core/util/price.js"
import { Actor } from "@opencode/cloud-core/actor.js"
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
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"),
// },
}
export async function POST(input: APIEvent) {
// Check auth header
const authHeader = input.request.headers.get("authorization")
if (!authHeader || !authHeader.startsWith("Bearer "))
return Response.json(
{
error: {
message: "Missing API key.",
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
},
{ status: 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 Response.json(
{
error: {
message: "Invalid API key.",
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
},
{ status: 401 },
)
/*
return await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => {
try {
// Check balance
const customer = await Billing.get()
if (customer.balance <= 0) {
return Response.json(
{
error: {
message: "Insufficient balance",
type: "insufficient_quota",
param: null,
code: "insufficient_quota",
},
},
{ status: 401 },
)
}
const body = await input.request.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 Response.json({ error: { message: error.message } }, { status: 500 })
}
})
*/
}
+105 -124
View File
@@ -2,6 +2,7 @@
--color-text: hsl(224, 10%, 10%);
--color-text-secondary: hsl(224, 7%, 46%);
--color-text-dimmed: hsl(224, 6%, 63%);
--color-text-inverted: hsl(0, 0%, 100%);
--color-border: hsl(224, 6%, 77%);
}
@@ -11,6 +12,7 @@
--color-text: hsl(0, 0%, 100%);
--color-text-secondary: hsl(224, 6%, 66%);
--color-text-dimmed: hsl(224, 7%, 46%);
--color-text-inverted: hsl(224, 10%, 10%);
--color-border: hsl(224, 6%, 36%);
}
@@ -19,14 +21,17 @@
[data-page="home"] {
--padding: 3rem;
--vertical-padding: 1.5rem;
--heading-font-size: 1.5625rem;
--heading-font-size: 1.375rem;
@media (max-width: 30rem) {
--padding: 1rem;
--vertical-padding: 0.75rem;
--heading-font-size: 1.375rem;
--heading-font-size: 1rem;
}
display: flex;
gap: var(--vertical-padding);
flex-direction: column;
font-family: var(--font-mono);
color: var(--color-text);
padding: calc(var(--padding) + 1rem);
@@ -41,19 +46,20 @@
[data-component="content"] {
max-width: 67.5rem;
margin: 0 auto;
border: 2px solid var(--color-border);
border: 1px solid var(--color-border);
}
[data-component="top"] {
padding: var(--padding);
padding: calc(var(--padding) * 1.5) var(--padding) var(--padding);
position: relative;
display: flex;
flex-direction: column;
align-items: start;
align-items: center;
gap: calc(var(--vertical-padding) / 2);
img {
height: auto;
width: clamp(200px, 70vw, 400px);
width: clamp(200px, 85vw, 552px);
}
[data-slot="logo dark"] {
@@ -71,29 +77,51 @@
[data-slot="title"] {
line-height: 1.25;
font-weight: 500;
text-align: center;
font-size: var(--heading-font-size);
color: var(--color-text-secondary);
text-transform: uppercase;
}
[data-slot="login"] {
position: absolute;
top: 0;
right: 0;
border-width: 0 0 1px 1px;
border-style: solid;
border-color: var(--color-border);
background-color: var(--color-bg);
@media (max-width: 30rem) {
display: none;
}
a {
display: block;
padding: 0.5rem 1rem calc(0.5rem + 4px);
}
}
}
[data-component="cta"] {
border-top: 2px solid var(--color-border);
display: grid;
grid-template-columns: auto 1fr auto;
align-items: stretch;
border-top: 1px solid var(--color-border);
display: flex;
& > div + div {
border-left: 2px solid var(--color-border);
border-left: 1px solid var(--color-border);
}
[data-slot="left"],
[data-slot="right"] {
[data-slot="left"] {
flex: 0 0 auto;
text-align: center;
line-height: 1.4;
padding: var(--vertical-padding) 2rem;
text-transform: uppercase;
font-size: 1.125rem;
@media (max-width: 30rem) {
font-size: 1rem;
padding-bottom: calc(var(--vertical-padding) + 4px);
}
@@ -104,51 +132,29 @@
}
[data-slot="center"] {
display: none;
@media (max-width: 30rem) {
display: block;
flex: 1;
text-align: center;
padding: var(--vertical-padding) 0.5rem;
border-top: 1px solid var(--color-border);
border-left: none;
}
}
[data-slot="right"] {
flex: 1;
padding: var(--vertical-padding) 1rem;
}
@media (max-width: 55rem) {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto;
[data-slot="left"] {
grid-column: 1;
grid-row: 1;
}
@media (max-width: 50rem) {
flex-direction: column;
[data-slot="right"] {
grid-column: 2;
grid-row: 1;
}
[data-slot="center"] {
grid-column: 1 / -1;
grid-row: 2;
border-left: none;
border-top: 2px solid var(--color-border);
}
@media (max-width: 22rem) {
grid-template-columns: 1fr;
grid-template-rows: auto auto auto;
[data-slot="left"] {
grid-column: 1;
grid-row: 1;
}
[data-slot="right"] {
grid-column: 1;
grid-row: 2;
border-top: 2px solid var(--color-border);
border-left: none;
}
[data-slot="center"] {
grid-column: 1;
grid-row: 3;
}
border-top: 1px solid var(--color-border);
}
}
@@ -168,7 +174,7 @@
@media (max-width: 24rem) {
font-size: 0.875rem;
}
@media (max-width: 64rem) {
@media (max-width: 56rem) {
[data-slot="protocol"] {
display: none;
}
@@ -189,7 +195,7 @@
}
[data-component="features"] {
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
padding: var(--padding);
[data-slot="list"] {
@@ -199,10 +205,21 @@
li {
margin-bottom: var(--space-4);
line-height: 1.6;
strong {
text-transform: uppercase;
font-weight: 700;
font-weight: 600;
}
label {
line-height: 1;
text-transform: uppercase;
font-size: 0.75rem;
letter-spacing: 0.03125rem;
background: var(--color-border);
padding: 0.125rem 0.375rem;
color: var(--color-text-inverted);
}
}
@@ -213,7 +230,7 @@
}
[data-component="install"] {
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
@@ -240,21 +257,21 @@
}
&:nth-child(2) {
border-left: 2px solid var(--color-border);
border-left: 1px solid var(--color-border);
@media (max-width: 40rem) {
border-left: none;
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
}
}
&:nth-child(3) {
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
}
&:nth-child(4) {
border-top: 2px solid var(--color-border);
border-left: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
border-left: 1px solid var(--color-border);
@media (max-width: 40rem) {
border-left: none;
@@ -303,38 +320,7 @@
}
[data-component="screenshots"] {
--images-height: 600px;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: var(--images-height);
border-top: 2px solid var(--color-border);
& > div.left {
display: flex;
grid-row: 1;
grid-column: 1;
}
& > div.right {
display: grid;
grid-template-rows: 1fr 1fr;
grid-row: 1;
grid-column: 2;
border-left: 2px solid var(--color-border);
& > div.row1 {
display: flex;
grid-row: 1;
border-bottom: 2px solid var(--color-border);
height: calc(var(--images-height) / 2);
}
& > div.row2 {
display: flex;
grid-row: 2;
height: calc(var(--images-height) / 2);
}
}
border-top: 1px solid var(--color-border);
figure {
flex: 1;
@@ -384,24 +370,24 @@
}
}
& > div.left figure {
& > [data-slot="left"] figure {
height: var(--images-height);
box-sizing: border-box;
}
& > div.right figure {
& > [data-slot="right"] figure {
height: calc(var(--images-height) / 2);
box-sizing: border-box;
}
& > div.left img {
& > [data-slot="left"] img {
width: 100%;
height: 100%;
min-width: 0;
object-fit: contain;
}
& > div.right img {
& > [data-slot="right"] img {
width: 100%;
height: calc(100% - 2rem);
object-fit: contain;
@@ -415,30 +401,30 @@
grid-template-rows: auto auto;
}
& > div.left {
& > [data-slot="left"] {
grid-row: 1;
grid-column: 1;
}
& > div.right {
& > [data-slot="right"] {
grid-row: 2;
grid-column: 1;
border-left: none;
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
& > div.row1,
& > div.row2 {
& > [data-slot="row1"],
& > [data-slot="row2"] {
height: auto;
}
}
& > div.left figure,
& > div.right figure {
& > [data-slot="left"] figure,
& > [data-slot="right"] figure {
height: auto;
}
& > div.left img,
& > div.right img {
& > [data-slot="left"] img,
& > [data-slot="right"] img {
width: 100%;
height: auto;
max-height: none;
@@ -475,7 +461,7 @@
}
[data-component="footer"] {
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
display: flex;
flex-direction: row;
@@ -487,25 +473,11 @@
}
[data-slot="cell"] + [data-slot="cell"] {
border-left: 2px solid var(--color-border);
}
/* Small desktop: first two columns shrink to content, third expands */
@media (max-width: 57rem) {
[data-slot="cell"]:nth-child(1),
[data-slot="cell"]:nth-child(2) {
flex: 0 0 auto;
padding-left: calc(var(--padding) / 2);
padding-right: calc(var(--padding) / 2);
}
[data-slot="cell"]:nth-child(3) {
flex: 1;
}
border-left: 1px solid var(--color-border);
}
/* Mobile: third column on its own row */
@media (max-width: 40rem) {
@media (max-width: 30rem) {
flex-wrap: wrap;
[data-slot="cell"]:nth-child(1),
@@ -516,8 +488,17 @@
[data-slot="cell"]:nth-child(3) {
flex: 1 0 100%;
border-left: none;
border-top: 2px solid var(--color-border);
border-top: 1px solid var(--color-border);
}
}
}
[data-component="legal"] {
color: var(--color-text-dimmed);
text-align: center;
a {
color: var(--color-text-dimmed);
}
}
}
+46 -61
View File
@@ -1,13 +1,11 @@
import "./index.css"
import { Title } from "@solidjs/meta"
import { onCleanup, onMount } from "solid-js"
import "./index.css"
import logoLight from "../asset/logo-ornate-light.svg"
import logoDark from "../asset/logo-ornate-dark.svg"
import IMG_SPLASH from "../asset/lander/screenshot-splash.png"
import IMG_VSCODE from "../asset/lander/screenshot-vscode.png"
import IMG_GITHUB from "../asset/lander/screenshot-github.png"
import { IconCopy, IconCheck } from "../component/icon"
import { createAsync, query, redirect } from "@solidjs/router"
import { createAsync, query } from "@solidjs/router"
import { getActor } from "~/context/auth"
import { withActor } from "~/context/auth.withActor"
import { Account } from "@opencode/cloud-core/account.js"
@@ -21,20 +19,17 @@ function CopyStatus() {
)
}
const isLoggedIn = query(async () => {
const defaultWorkspace = query(async () => {
"use server"
const actor = await getActor()
if (actor.type === "account") {
const workspaces = await withActor(() => Account.workspaces())
throw redirect(`/workspace/${workspaces[0].id}`)
return workspaces[0].id
}
return false
}, "isLoggedIn")
}, "defaultWorkspace")
export default function Home() {
createAsync(() => isLoggedIn(), {
deferStream: true,
})
const workspace = createAsync(() => defaultWorkspace())
onMount(() => {
const commands = document.querySelectorAll("[data-copy]")
for (const button of commands) {
@@ -58,11 +53,15 @@ export default function Home() {
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 light" src={logoLight} alt="opencode logo light" />
<img data-slot="logo dark" src={logoDark} alt="opencode logo dark" />
<h1 data-slot="title">The AI coding agent built for the terminal.</h1>
<h1 data-slot="title">The AI coding agent built for the terminal</h1>
<div data-slot="login">
<a href="/auth">opencode zen</a>
</div>
</section>
<section data-component="cta">
@@ -70,43 +69,45 @@ export default function Home() {
<a href="/docs">Get Started</a>
</div>
<div data-slot="center">
<a href="/auth">opencode zen</a>
</div>
<div data-slot="right">
<button data-copy data-slot="command">
<span>
<span>curl -fsSL&nbsp;</span>
<span>curl -fsSL </span>
<span data-slot="protocol">https://</span>
<span data-slot="highlight">opencode.ai/install</span>
&nbsp;| bash
<span> | bash</span>
</span>
<CopyStatus />
</button>
</div>
<div data-slot="right">
<a href="/auth/authorize" target="_self">
Login
</a>
</div>
</section>
<section data-component="features">
<ul data-slot="list">
<li>
<strong>Native TUI</strong>: A responsive, native, themeable terminal UI.
<strong>Native TUI</strong> A responsive, native, themeable terminal UI
</li>
<li>
<strong>LSP enabled</strong>: Automatically loads the right LSPs for the LLM.
<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.
<strong>opencode zen</strong> A <a href="/docs/zen">curated list of models</a> provided by opencode{" "}
<label>New</label>
</li>
<li>
<strong>Shareable links</strong>: Share a link to any sessions for reference or to debug.
<strong>Multi-session</strong> Start multiple agents in parallel on the same project
</li>
<li>
<strong>Claude Pro</strong>: Log in with Anthropic to use your Claude Pro or Max account.
<strong>Shareable links</strong> Share a link to any sessions for reference or to debug
</li>
<li>
<strong>Use any model</strong>: Supports 75+ LLM providers through{" "}
<a href="https://models.dev">Models.dev</a>, including local models.
<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>
@@ -116,7 +117,7 @@ export default function Home() {
<h3 data-component="title">npm</h3>
<button data-copy data-slot="button">
<span>
npm install -g&nbsp;<strong>opencode-ai</strong>
npm install -g <strong>opencode-ai</strong>
</span>
<CopyStatus />
</button>
@@ -125,7 +126,7 @@ export default function Home() {
<h3 data-component="title">bun</h3>
<button data-copy data-slot="button">
<span>
bun install -g&nbsp;<strong>opencode-ai</strong>
bun install -g <strong>opencode-ai</strong>
</span>
<CopyStatus />
</button>
@@ -134,7 +135,7 @@ export default function Home() {
<h3 data-component="title">homebrew</h3>
<button data-copy data-slot="button">
<span>
brew install&nbsp;<strong>sst/tap/opencode</strong>
brew install <strong>sst/tap/opencode</strong>
</span>
<CopyStatus />
</button>
@@ -143,7 +144,7 @@ export default function Home() {
<h3 data-component="title">paru</h3>
<button data-copy data-slot="button">
<span>
paru -S&nbsp;<strong>opencode-bin</strong>
paru -S <strong>opencode-bin</strong>
</span>
<CopyStatus />
</button>
@@ -151,48 +152,32 @@ export default function Home() {
</section>
<section data-component="screenshots">
<div class="left">
<figure>
<figcaption>opencode TUI with the tokyonight theme</figcaption>
<a href="/docs/cli">
<img src={IMG_SPLASH} alt="opencode TUI with tokyonight theme" />
</a>
</figure>
</div>
<div class="right">
<div class="row1">
<figure>
<figcaption>opencode in VS Code</figcaption>
<a href="/docs/ide">
<img src={IMG_VSCODE} alt="opencode in VS Code" />
</a>
</figure>
</div>
<div class="row2">
<figure>
<figcaption>opencode in GitHub</figcaption>
<a href="/docs/github">
<img src={IMG_GITHUB} alt="opencode in GitHub" />
</a>
</figure>
</div>
</div>
<figure>
<figcaption>opencode TUI with the tokyonight theme</figcaption>
<a href="/docs/cli">
<img src={IMG_SPLASH} alt="opencode TUI with tokyonight theme" />
</a>
</figure>
</section>
<footer data-component="footer">
<div data-slot="cell">
<a href="https://x.com/opencode">X.com</a>
</div>
<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>
<div data-component="legal">
<span>
©2025 <a href="https://anoma.ly">Anomaly Innovations</a>
</span>
</div>
</main>
)
}
+20
View File
@@ -0,0 +1,20 @@
import type { APIEvent } from "@solidjs/start/server"
async function handler(evt: APIEvent) {
const req = evt.request.clone()
const url = new URL(req.url)
const targetUrl = `https://docs.opencode.ai/docs${url.pathname}${url.search}`
const response = await fetch(targetUrl, {
method: req.method,
headers: req.headers,
body: req.body,
})
return response
}
export const GET = handler
export const POST = handler
export const PUT = handler
export const DELETE = handler
export const OPTIONS = handler
export const PATCH = handler
+4 -2
View File
@@ -26,6 +26,8 @@ export async function POST(input: APIEvent) {
if (!amount) throw new Error("Amount not found")
if (!paymentID) throw new Error("Payment ID not found")
const chargedAmount = 2000
await Actor.provide("system", { workspaceID }, async () => {
const customer = await Billing.get()
if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch")
@@ -50,7 +52,7 @@ export async function POST(input: APIEvent) {
await tx
.update(BillingTable)
.set({
balance: sql`${BillingTable.balance} + ${centsToMicroCents(amount)}`,
balance: sql`${BillingTable.balance} + ${centsToMicroCents(chargedAmount)}`,
customerID,
paymentMethodID: paymentMethod.id,
paymentMethodLast4: paymentMethod.card!.last4,
@@ -59,7 +61,7 @@ export async function POST(input: APIEvent) {
await tx.insert(PaymentTable).values({
workspaceID,
id: Identifier.create("payment"),
amount: centsToMicroCents(amount),
amount: centsToMicroCents(chargedAmount),
paymentID,
customerID,
})
+65 -7
View File
@@ -1,9 +1,63 @@
[data-page="workspace"] {
line-height: 1;
@media (max-width: 30rem) {
padding: var(--space-4);
gap: var(--space-5);
/* Common elements */
button {
padding: var(--space-3) var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
background-color: var(--color-bg);
color: var(--color-text);
font-size: var(--font-size-sm);
font-family: var(--font-sans);
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
transition: all 0.15s ease;
&:hover:not(:disabled) {
background-color: var(--color-surface-hover);
border-color: var(--color-accent);
}
&:active {
transform: translateY(1px);
}
&:disabled {
opacity: 0.5;
transform: none;
}
&[data-color="primary"] {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-primary-text);
&:hover:not(:disabled) {
background-color: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
}
&[data-color="ghost"] {
background-color: transparent;
border-color: transparent;
color: var(--color-text-muted);
&:hover:not(:disabled) {
background-color: var(--color-surface-hover);
border-color: var(--color-border);
color: var(--color-text);
}
}
}
a {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: var(--space-0-75);
text-decoration-thickness: 1px;
}
/* Workspace Header */
@@ -19,9 +73,7 @@
background-color: var(--color-bg);
@media (max-width: 30rem) {
padding: 0 var(--space-4);
margin: calc(-1 * var(--space-4));
margin-bottom: var(--space-5);
padding: var(--space-4) var(--space-4);
}
}
@@ -48,10 +100,16 @@
align-items: center;
font-size: var(--font-size-sm);
span {
[data-slot="user"] {
color: var(--color-text-muted);
}
@media (max-width: 30rem) {
[data-slot="user"] {
display: none;
}
}
a,
button {
appearance: none;
+30 -17
View File
@@ -2,53 +2,66 @@ import "./workspace.css"
import { useAuthSession } from "~/context/auth.session"
import { IconLogo } from "../component/icon"
import { withActor } from "~/context/auth.withActor"
import "./workspace.css"
import { query, action, redirect, createAsync, RouteSectionProps } from "@solidjs/router"
import {
query,
action,
redirect,
createAsync,
RouteSectionProps,
Navigate,
useNavigate,
useParams,
A,
} from "@solidjs/router"
import { User } from "@opencode/cloud-core/user.js"
import { Actor } from "@opencode/cloud-core/actor.js"
import { getRequestEvent } from "solid-js/web"
const getUserInfo = query(async () => {
const getUserInfo = query(async (workspaceID: string) => {
"use server"
return withActor(async () => {
const actor = Actor.assert("user")
const user = await User.fromID(actor.properties.userID)
return { user }
})
return await User.fromID(actor.properties.userID)
}, workspaceID)
}, "userInfo")
const logout = action(async () => {
"use server"
const auth = await useAuthSession()
const event = getRequestEvent()
const current = auth.data.current
if (current)
await auth.update((val) => {
delete val.account[current]
delete val.account?.[current]
const first = Object.keys(val.account ?? {})[0]
val.current = first
event!.locals.actor = undefined
return val
})
return redirect("/")
throw redirect("/")
})
export default function WorkspaceLayout(props: RouteSectionProps) {
const userInfo = createAsync(() => getUserInfo(), {
deferStream: true,
})
const params = useParams()
const userInfo = createAsync(() => getUserInfo(params.id))
return (
<main data-page="workspace">
<header data-component="workspace-header">
<div data-slot="header-brand">
<a href="/" data-component="site-title">
<A href="/" data-component="site-title">
<IconLogo />
</a>
</A>
</div>
<div data-slot="header-actions">
<span>{userInfo()?.user.email}</span>
<span data-slot="user">{userInfo()?.email}</span>
<form action={logout} method="post">
<button type="submit" formaction={logout}>Logout</button>
<button type="submit" formaction={logout}>
Logout
</button>
</form>
</div>
</header>
<div data-slot="content">{props.children}</div>
<div>{props.children}</div>
</main>
)
}
+263 -103
View File
@@ -1,5 +1,4 @@
/* Root container */
[data-slot="root"] {
[data-page="workspace-[id]"] {
max-width: 64rem;
padding: var(--space-10) var(--space-4);
margin: 0 auto;
@@ -8,88 +7,59 @@
flex-direction: column;
gap: var(--space-10);
@media (max-width: 30rem) {
padding-top: var(--space-4);
padding-bottom: var(--space-4);
gap: var(--space-8);
}
[data-slot="sections"] {
display: flex;
flex-direction: column;
gap: var(--space-16);
@media (max-width: 30rem) {
gap: var(--space-8);
}
section {
display: flex;
flex-direction: column;
gap: var(--space-6);
/* Section titles */
[data-slot="section-title"] {
display: flex;
flex-direction: column;
gap: var(--space-1);
h2 {
font-size: var(--font-size-md);
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.03125rem;
margin: 0;
color: var(--color-text-secondary);
text-transform: uppercase;
@media (max-width: 30rem) {
font-size: var(--font-size-md);
}
}
}
}
section:not(:last-child) {
border-bottom: 1px solid var(--color-border);
padding-bottom: var(--space-16);
}
}
/* Common elements */
button {
padding: var(--space-3) var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
background-color: var(--color-bg);
color: var(--color-text);
font-size: var(--font-size-sm);
font-family: var(--font-sans);
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
transition: all 0.15s ease;
&:hover {
background-color: var(--color-surface-hover);
border-color: var(--color-accent);
}
&:active {
transform: translateY(1px);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
&:hover {
background-color: var(--color-bg);
border-color: var(--color-border);
transform: none;
}
}
&[data-color="primary"] {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-primary-text);
&:hover {
background-color: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
}
&[data-color="ghost"] {
background-color: transparent;
border-color: transparent;
color: var(--color-text-muted);
&:hover {
background-color: var(--color-surface-hover);
border-color: var(--color-border);
color: var(--color-text);
@media (max-width: 30rem) {
padding-bottom: var(--space-8);
}
}
}
a {
color: var(--color-text);
text-decoration: underline;
text-underline-offset: var(--space-0-75);
text-decoration-thickness: 1px;
}
[data-slot="empty-state"] {
[data-component="empty-state"] {
padding: var(--space-20) var(--space-6);
text-align: center;
border: 1px dashed var(--color-border);
@@ -99,20 +69,24 @@
gap: var(--space-2);
p {
line-height: 1.5;
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin: 0;
}
}
/* Title section */
[data-slot="title-section"] {
[data-component="title-section"] {
display: flex;
flex-direction: column;
gap: var(--space-2);
padding-bottom: var(--space-8);
border-bottom: 1px solid var(--color-border);
@media (max-width: 30rem) {
padding-bottom: var(--space-6);
}
h1 {
font-size: var(--font-size-2xl);
font-weight: 500;
@@ -123,11 +97,11 @@
@media (max-width: 30rem) {
font-size: var(--font-size-xl);
line-height: 1.25;
}
}
p {
line-height: 1.5;
font-size: var(--font-size-md);
color: var(--color-text-muted);
@@ -137,35 +111,8 @@
}
}
/* Section titles */
[data-slot="section-title"] {
display: flex;
flex-direction: column;
gap: var(--space-1);
h2 {
font-size: var(--font-size-md);
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.03125rem;
margin: 0;
color: var(--color-text-secondary);
text-transform: uppercase;
@media (max-width: 30rem) {
font-size: var(--font-size-lg);
line-height: 1.25;
}
}
p {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
}
/* API Keys Section */
[data-slot="api-keys-section"] {
[data-component="api-keys-section"] {
[data-slot="create-form"] {
display: flex;
gap: var(--space-3);
@@ -173,6 +120,10 @@
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
@media (max-width: 30rem) {
gap: var(--space-2);
}
input {
flex: 1;
padding: var(--space-2) var(--space-3);
@@ -235,11 +186,35 @@
&[data-slot="key-value"] {
font-family: var(--font-mono);
div {
cursor: pointer;
button {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-2) var(--space-3);
font-size: var(--font-size-sm);
font-weight: 400;
border: none;
background-color: transparent;
color: var(--color-text-muted);
font-family: var(--font-mono);
border-radius: var(--border-radius-sm);
cursor: pointer;
transition: all 0.15s ease;
text-transform: none;
&:hover:not(:disabled) {
background-color: var(--color-bg-surface);
color: var(--color-text);
}
&:disabled {
cursor: default;
color: var(--color-text);
}
span {
font-family: inherit;
}
}
}
@@ -281,7 +256,7 @@
}
/* Balance Section */
[data-slot="balance-section"] {
[data-component="balance-section"] {
[data-slot="balance"] {
display: flex;
flex-direction: column;
@@ -301,10 +276,13 @@
gap: var(--space-1);
justify-content: flex-end;
&.danger {
&[data-state="danger"] {
[data-slot="value"] {
color: var(--color-danger);
}
[data-slot="currency"] {
color: var(--color-danger);
}
}
[data-slot="currency"] {
@@ -325,7 +303,7 @@
}
/* Payments Section */
[data-slot="payments-section"] {
[data-component="payments-section"] {
[data-slot="payments-table"] {
overflow-x: auto;
}
@@ -399,7 +377,7 @@
}
/* Usage Section */
[data-slot="usage-section"] {
[data-component="usage-section"] {
[data-slot="usage-table"] {
overflow-x: auto;
}
@@ -471,4 +449,186 @@
}
}
}
[data-slot="new-user-sections"] {
display: flex;
flex-direction: column;
gap: var(--space-16);
@media (max-width: 30rem) {
gap: var(--space-8);
}
[data-component="feature-grid"] {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--space-6);
@media (max-width: 30rem) {
grid-template-columns: 1fr;
gap: var(--space-4);
}
[data-slot="feature"] {
display: flex;
flex-direction: column;
gap: var(--space-2);
padding: var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
background-color: var(--color-bg-surface);
h3 {
font-size: var(--font-size-sm);
font-weight: 600;
margin: 0;
color: var(--color-text);
text-transform: uppercase;
letter-spacing: -0.025rem;
}
p {
font-size: var(--font-size-sm);
line-height: 1.5;
margin: 0;
color: var(--color-text-muted);
}
}
}
[data-component="api-key-highlight"] {
display: flex;
flex-direction: column;
gap: var(--space-6);
[data-slot="section-title"] {
display: flex;
flex-direction: column;
gap: var(--space-1);
h2 {
font-size: var(--font-size-md);
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.03125rem;
margin: 0;
color: var(--color-text-secondary);
text-transform: uppercase;
@media (max-width: 30rem) {
font-size: var(--font-size-md);
}
}
}
[data-slot="key-display"] {
display: flex;
flex-direction: column;
gap: var(--space-3);
[data-slot="key-container"] {
display: flex;
gap: var(--space-3);
padding: var(--space-4);
border: 2px solid var(--color-accent);
border-radius: var(--border-radius-sm);
background-color: var(--color-bg-surface);
align-items: center;
@media (max-width: 40rem) {
flex-direction: column;
gap: var(--space-3);
align-items: stretch;
}
[data-slot="key-value"] {
flex: 1;
font-family: var(--font-mono);
font-size: var(--font-size-sm);
color: var(--color-text);
background-color: var(--color-bg);
padding: var(--space-3);
border-radius: var(--border-radius-sm);
border: 1px solid var(--color-border);
word-break: break-all;
line-height: 1.4;
@media (max-width: 40rem) {
font-size: var(--font-size-xs);
padding: var(--space-2-5);
}
}
button {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
font-size: var(--font-size-sm);
font-weight: 500;
white-space: nowrap;
min-width: 130px;
@media (max-width: 40rem) {
justify-content: center;
padding: var(--space-2-5) var(--space-3);
font-size: var(--font-size-xs);
min-width: 96px;
}
}
}
}
}
[data-component="next-steps"] {
display: flex;
flex-direction: column;
gap: var(--space-6);
[data-slot="section-title"] {
display: flex;
flex-direction: column;
gap: var(--space-1);
h2 {
font-size: var(--font-size-md);
font-weight: 600;
line-height: 1.2;
letter-spacing: -0.03125rem;
margin: 0;
color: var(--color-text-secondary);
text-transform: uppercase;
@media (max-width: 30rem) {
font-size: var(--font-size-md);
}
}
}
ol {
margin: 0;
padding-left: 0;
display: flex;
flex-direction: column;
gap: var(--space-2);
list-style-position: inside;
li {
font-size: var(--font-size-sm);
line-height: 1.5;
color: var(--color-text-muted);
code {
font-family: var(--font-mono);
font-size: var(--font-size-xs);
padding: var(--space-1) var(--space-2);
background-color: var(--color-bg-surface);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
color: var(--color-text);
}
}
}
}
}
}
+430 -351
View File
@@ -1,36 +1,67 @@
import "./[id].css"
import { Billing } from "@opencode/cloud-core/billing.js"
import { Key } from "@opencode/cloud-core/key.js"
import { action, createAsync, query, useAction, useSubmission, json } from "@solidjs/router"
import { createSignal, For, onMount, Show } from "solid-js"
import { getActor } from "~/context/auth"
import { json, query, action, useParams, useAction, createAsync, useSubmission } from "@solidjs/router"
import { createEffect, createMemo, createSignal, For, Show } from "solid-js"
import { withActor } from "~/context/auth.withActor"
import { IconCopy, IconCheck } from "~/component/icon"
import "./[id].css"
import { User } from "@opencode/cloud-core/user.js"
import { Actor } from "@opencode/cloud-core/actor.js"
import { createStore } from "solid-js/store"
function formatDateForTable(date: Date) {
const options: Intl.DateTimeFormatOptions = {
day: "numeric",
month: "short",
hour: "numeric",
minute: "2-digit",
hour12: true,
}
return date.toLocaleDateString("en-GB", options).replace(",", ",")
}
function formatDateUTC(date: Date) {
const options: Intl.DateTimeFormatOptions = {
weekday: "short",
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short",
timeZone: "UTC",
}
return date.toLocaleDateString("en-US", options)
}
/////////////////////////////////////
// Keys related queries and actions
/////////////////////////////////////
const listKeys = query(() => {
const listKeys = query(async (workspaceID: string) => {
"use server"
return withActor(() => Key.list())
return withActor(() => Key.list(), workspaceID)
}, "key.list")
const createKey = action(async (name: string) => {
const createKey = action(async (form: FormData) => {
"use server"
const name = form.get("name")?.toString().trim()
if (!name) return { error: "Name is required" }
const workspaceID = form.get("workspaceID")?.toString()
if (!workspaceID) return { error: "Workspace ID is required" }
return json(
withActor(() => Key.create({ name })),
withActor(() => Key.create({ name }), workspaceID),
{ revalidate: listKeys.key },
)
}, "key.create")
const removeKey = action(async (id: string) => {
const removeKey = action(async (form: FormData) => {
"use server"
const id = form.get("id")?.toString()
if (!id) return { error: "ID is required" }
const workspaceID = form.get("workspaceID")?.toString()
if (!workspaceID) return { error: "Workspace ID is required" }
return json(
withActor(() => Key.remove({ id })),
withActor(() => Key.remove({ id }), workspaceID),
{ revalidate: listKeys.key },
)
}, "key.remove")
@@ -39,368 +70,416 @@ const removeKey = action(async (id: string) => {
// Billing related queries and actions
/////////////////////////////////////
const getBillingInfo = query(async () => {
const getBalanceInfo = query(async (workspaceID: string) => {
"use server"
return withActor(async () => {
const actor = Actor.assert("user")
const [user, billing, payments, usage] = await Promise.all([
User.fromID(actor.properties.userID),
Billing.get(),
Billing.payments(),
Billing.usages(),
])
return { user, billing, payments, usage }
})
}, "billingInfo")
return await Billing.get()
}, workspaceID)
}, "balanceInfo")
const createCheckoutUrl = action(async (successUrl: string, cancelUrl: string) => {
const getUsageInfo = query(async (workspaceID: string) => {
"use server"
return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }))
return withActor(async () => {
return await Billing.usages()
}, workspaceID)
}, "usageInfo")
const getPaymentsInfo = query(async (workspaceID: string) => {
"use server"
return withActor(async () => {
return await Billing.payments()
}, workspaceID)
}, "paymentsInfo")
const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => {
"use server"
return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID)
}, "checkoutUrl")
const createPortalUrl = action(async (returnUrl: string) => {
"use server"
return withActor(() => Billing.generatePortalUrl({ returnUrl }))
}, "portalUrl")
// const createPortalUrl = action(async (workspaceID: string, returnUrl: string) => {
// "use server"
// return withActor(() => Billing.generatePortalUrl({ returnUrl }), workspaceID)
// }, "portalUrl")
export default function () {
function KeySection() {
const params = useParams()
const keys = createAsync(() => listKeys(params.id))
/////////////////
// Keys section
/////////////////
const keys = createAsync(() => listKeys(), {
deferStream: true,
})
const createKeyAction = useAction(createKey)
const removeKeyAction = useAction(removeKey)
const createKeySubmission = useSubmission(createKey)
const [showCreateForm, setShowCreateForm] = createSignal(false)
const [keyName, setKeyName] = createSignal("")
const [copiedKeyId, setCopiedKeyId] = createSignal<string | null>(null)
const formatDate = (date: Date) => {
return date.toLocaleDateString()
}
const formatDateForTable = (date: Date) => {
const options: Intl.DateTimeFormatOptions = {
day: "numeric",
month: "short",
hour: "numeric",
minute: "2-digit",
hour12: true,
}
return date.toLocaleDateString("en-GB", options).replace(",", ",")
}
const formatDateUTC = (date: Date) => {
const options: Intl.DateTimeFormatOptions = {
weekday: "short",
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "2-digit",
second: "2-digit",
timeZoneName: "short",
timeZone: "UTC",
}
return date.toLocaleDateString("en-US", options)
}
const formatKey = (key: string) => {
function formatKey(key: string) {
if (key.length <= 11) return key
return `${key.slice(0, 7)}...${key.slice(-4)}`
}
const copyToClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text)
} catch (error) {
console.error("Failed to copy to clipboard:", error)
}
}
return (
<section data-component="api-keys-section">
<div data-slot="section-title">
<h2>API Keys</h2>
<p>Manage your API keys for accessing opencode services.</p>
</div>
<KeyCreateForm />
<div data-slot="api-keys-table">
<Show
when={keys()?.length}
fallback={
<div data-component="empty-state">
<p>Create an opencode Gateway API key</p>
</div>
}
>
<table data-slot="api-keys-table-element">
<thead>
<tr>
<th>Name</th>
<th>Key</th>
<th>Created</th>
<th></th>
</tr>
</thead>
<tbody>
<For each={keys()!}>
{(key) => {
const [copied, setCopied] = createSignal(false)
// const submission = useSubmission(removeKey, ([fd]) => fd.get("id")?.toString() === key.id)
return (
<tr>
<td data-slot="key-name">{key.name}</td>
<td data-slot="key-value">
<button
data-color="ghost"
disabled={copied()}
onClick={async () => {
await navigator.clipboard.writeText(key.key)
setCopied(true)
setTimeout(() => setCopied(false), 1000)
}}
title="Copy API key"
>
<span>{formatKey(key.key)}</span>
<Show when={copied()} fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}>
<IconCheck style={{ width: "14px", height: "14px" }} />
</Show>
</button>
</td>
<td data-slot="key-date" title={formatDateUTC(key.timeCreated)}>
{formatDateForTable(key.timeCreated)}
</td>
<td data-slot="key-actions">
<form action={removeKey} method="post">
<input type="hidden" name="id" value={key.id} />
<input type="hidden" name="workspaceID" value={params.id} />
<button data-color="ghost">Delete</button>
</form>
</td>
</tr>
)
}}
</For>
</tbody>
</table>
</Show>
</div>
</section>
)
}
const copyKeyToClipboard = async (text: string, keyId: string) => {
try {
await navigator.clipboard.writeText(text)
setCopiedKeyId(keyId)
setTimeout(() => setCopiedKeyId(null), 1500)
} catch (error) {
console.error("Failed to copy to clipboard:", error)
}
}
const handleCreateKey = async () => {
if (!keyName().trim()) return
try {
await createKeyAction(keyName().trim())
setKeyName("")
setShowCreateForm(false)
} catch (error) {
console.error("Failed to create API key:", error)
}
}
const handleDeleteKey = async (keyId: string) => {
if (!confirm("Are you sure you want to delete this API key?")) {
return
}
try {
await removeKeyAction(keyId)
} catch (error) {
console.error("Failed to delete API key:", error)
}
}
/////////////////
// Billing section
/////////////////
const billingInfo = createAsync(() => getBillingInfo(), {
deferStream: true,
function KeyCreateForm() {
const params = useParams()
const submission = useSubmission(createKey)
const [store, setStore] = createStore({
show: false,
})
const createCheckoutUrlAction = useAction(createCheckoutUrl)
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
const handleBuyCredits = async () => {
try {
const baseUrl = window.location.href
const checkoutUrl = await createCheckoutUrlAction(baseUrl, baseUrl)
if (checkoutUrl) {
window.location.href = checkoutUrl
}
} catch (error) {
console.error("Failed to get checkout URL:", error)
let input: HTMLInputElement
createEffect(() => {
if (!submission.pending && submission.result) {
hide()
}
})
function show() {
setStore("show", true)
input.focus()
}
function hide() {
setStore("show", false)
}
return (
<div data-slot="root">
{/* Title */}
<section data-slot="title-section">
<h1>Gateway</h1>
<Show
when={store.show}
fallback={
<button data-color="primary" onClick={() => show()}>
Create API Key
</button>
}
>
<form action={createKey} method="post" data-slot="create-form">
<input ref={(r) => (input = r)} data-component="input" name="name" type="text" placeholder="Enter key name" />
<input type="hidden" name="workspaceID" value={params.id} />
<div data-slot="form-actions">
<button type="reset" data-color="ghost" onClick={() => hide()}>
Cancel
</button>
<button type="submit" data-color="primary" disabled={submission.pending}>
{submission.pending ? "Creating..." : "Create"}
</button>
</div>
</form>
</Show>
)
}
function BalanceSection() {
const params = useParams()
const balanceInfo = createAsync(() => getBalanceInfo(params.id))
const createCheckoutUrlAction = useAction(createCheckoutUrl)
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
return (
<section data-component="balance-section">
<div data-slot="section-title">
<h2>Balance</h2>
<p>Add credits to your account.</p>
</div>
<div data-slot="balance">
<div
data-slot="amount"
data-state={(() => {
const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
return balanceStr === "0.00" || balanceStr === "-0.00" ? "danger" : undefined
})()}
>
<span data-slot="currency">$</span>
<span data-slot="value">
{(() => {
const balanceStr = ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
return balanceStr === "-0.00" ? "0.00" : balanceStr
})()}
</span>
</div>
<button
data-color="primary"
disabled={createCheckoutUrlSubmission.pending}
onClick={async () => {
const baseUrl = window.location.href
const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
if (checkoutUrl) {
window.location.href = checkoutUrl
}
}}
>
{createCheckoutUrlSubmission.pending ? "Loading..." : "Buy Credits"}
</button>
</div>
</section>
)
}
function UsageSection() {
const params = useParams()
const usage = createAsync(() => getUsageInfo(params.id))
return (
<section data-component="usage-section">
<div data-slot="section-title">
<h2>Usage History</h2>
<p>Recent API usage and costs.</p>
</div>
<div data-slot="usage-table">
<Show
when={usage() && usage()!.length > 0}
fallback={
<div data-component="empty-state">
<p>Make your first API call to get started.</p>
</div>
}
>
<table data-slot="usage-table-element">
<thead>
<tr>
<th>Date</th>
<th>Model</th>
<th>Input</th>
<th>Output</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<For each={usage()!}>
{(usage) => {
const date = createMemo(() => new Date(usage.timeCreated))
return (
<tr>
<td data-slot="usage-date" title={formatDateUTC(date())}>
{formatDateForTable(date())}
</td>
<td data-slot="usage-model">{usage.model}</td>
<td data-slot="usage-tokens">{usage.inputTokens}</td>
<td data-slot="usage-tokens">{usage.outputTokens}</td>
<td data-slot="usage-cost">${((usage.cost ?? 0) / 100000000).toFixed(4)}</td>
</tr>
)
}}
</For>
</tbody>
</table>
</Show>
</div>
</section>
)
}
function PaymentSection() {
const params = useParams()
const payments = createAsync(() => getPaymentsInfo(params.id))
return (
payments() &&
payments()!.length > 0 && (
<section data-component="payments-section">
<div data-slot="section-title">
<h2>Payments History</h2>
<p>Recent payment transactions.</p>
</div>
<div data-slot="payments-table">
<table data-slot="payments-table-element">
<thead>
<tr>
<th>Date</th>
<th>Payment ID</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<For each={payments()!}>
{(payment) => {
const date = new Date(payment.timeCreated)
return (
<tr>
<td data-slot="payment-date" title={formatDateUTC(date)}>
{formatDateForTable(date)}
</td>
<td data-slot="payment-id">{payment.id}</td>
<td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
</tr>
)
}}
</For>
</tbody>
</table>
</div>
</section>
)
)
}
function NewUserSection() {
const params = useParams()
const [copiedKey, setCopiedKey] = createSignal(false)
const keys = createAsync(() => listKeys(params.id))
const usage = createAsync(() => getUsageInfo(params.id))
const isNew = createMemo(() => {
const keysList = keys()
const usageList = usage()
return keysList?.length === 1 && (!usageList || usageList.length === 0)
})
const defaultKey = createMemo(() => keys()?.at(-1)?.key)
return (
<Show when={isNew()}>
<div data-slot="new-user-sections">
<div data-component="feature-grid">
<div data-slot="feature">
<h3>Tested & Verified Models</h3>
<p>We've benchmarked and tested models specifically for coding agents to ensure the best performance.</p>
</div>
<div data-slot="feature">
<h3>Highest Quality</h3>
<p>Access models configured for optimal performance - no downgrades or routing to cheaper providers.</p>
</div>
<div data-slot="feature">
<h3>No Lock-in</h3>
<p>Use Zen with any coding agent, and continue using other providers with opencode whenever you want.</p>
</div>
</div>
<div data-component="api-key-highlight">
<div data-slot="section-title">
<h2>Your API Key</h2>
</div>
<Show when={defaultKey()}>
<div data-slot="key-display">
<div data-slot="key-container">
<code data-slot="key-value">{defaultKey()}</code>
<button
data-color="primary"
disabled={copiedKey()}
onClick={async () => {
await navigator.clipboard.writeText(defaultKey() ?? "")
setCopiedKey(true)
setTimeout(() => setCopiedKey(false), 2000)
}}
title="Copy API key"
>
<Show
when={copiedKey()}
fallback={
<>
<IconCopy style={{ width: "16px", height: "16px" }} /> Copy Key
</>
}
>
<IconCheck style={{ width: "16px", height: "16px" }} /> Copied!
</Show>
</button>
</div>
</div>
</Show>
</div>
<div data-component="next-steps">
<div data-slot="section-title">
<h2>Next Steps</h2>
</div>
<ol>
<li>Copy your API key above</li>
<li>
Run <code>opencode auth login</code> and select opencode
</li>
<li>Paste your API key when prompted</li>
<li>
Run <code>/models</code> to see available models
</li>
</ol>
</div>
</div>
</Show>
)
}
export default function () {
return (
<div data-page="workspace-[id]">
<section data-component="title-section">
<h1>Zen</h1>
<p>
Coding models optimized for use with opencode. <a href="/docs">Learn more</a>.
Curated list of models provided by opencode.{" "}
<a target="_blank" href="/docs/zen">
Learn more
</a>
.
</p>
</section>
<div data-slot="sections">
{/* API Keys Section */}
<section data-slot="api-keys-section">
<div data-slot="section-title">
<h2>API Keys</h2>
<p>Manage your API keys for accessing opencode services.</p>
</div>
<Show
when={!showCreateForm()}
fallback={
<div data-slot="create-form">
<input
data-component="input"
type="text"
placeholder="Enter key name"
value={keyName()}
onInput={(e) => setKeyName(e.currentTarget.value)}
onKeyPress={(e) => e.key === "Enter" && handleCreateKey()}
/>
<div data-slot="form-actions">
<button
data-color="ghost"
onClick={() => {
setShowCreateForm(false)
setKeyName("")
}}
>
Cancel
</button>
<button
data-color="primary"
disabled={createKeySubmission.pending || !keyName().trim()}
onClick={handleCreateKey}
>
{createKeySubmission.pending ? "Creating..." : "Create"}
</button>
</div>
</div>
}
>
<button
data-color="primary"
onClick={() => {
console.log("clicked")
setShowCreateForm(true)
}}
>
Create API Key
</button>
</Show>
<div data-slot="api-keys-table">
<Show
when={keys()?.length}
fallback={
<div data-slot="empty-state">
<p>Create an opencode Gateway API key</p>
</div>
}
>
<table data-slot="api-keys-table-element">
<thead>
<tr>
<th>Name</th>
<th>Key</th>
<th>Created</th>
<th></th>
</tr>
</thead>
<tbody>
<For each={keys()!}>
{(key) => (
<tr>
<td data-slot="key-name">{key.name}</td>
<td data-slot="key-value">
<div onClick={() => copyKeyToClipboard(key.key, key.id)} title="Click to copy API key">
<span>{formatKey(key.key)}</span>
<Show
when={copiedKeyId() === key.id}
fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}
>
<IconCheck style={{ width: "14px", height: "14px" }} />
</Show>
</div>
</td>
<td data-slot="key-date" title={formatDateUTC(key.timeCreated)}>
{formatDateForTable(key.timeCreated)}
</td>
<td data-slot="key-actions">
<button data-color="ghost" onClick={() => handleDeleteKey(key.id)} title="Delete API key">
Delete
</button>
</td>
</tr>
)}
</For>
</tbody>
</table>
</Show>
</div>
</section>
{/* Balance Section */}
<section data-slot="balance-section">
<div data-slot="section-title">
<h2>Balance</h2>
<p>Add credits to your account.</p>
</div>
<div data-slot="balance">
<div
data-slot="amount"
classList={{
danger: (() => {
const balanceStr = ((billingInfo()?.billing?.balance ?? 0) / 100000000).toFixed(2)
return balanceStr === "0.00" || balanceStr === "-0.00"
})(),
}}
>
<span data-slot="currency">$</span>
<span data-slot="value">
{(() => {
const balanceStr = ((billingInfo()?.billing?.balance ?? 0) / 100000000).toFixed(2)
return balanceStr === "-0.00" ? "0.00" : balanceStr
})()}
</span>
</div>
<button data-color="primary" disabled={createCheckoutUrlSubmission.pending} onClick={handleBuyCredits}>
{createCheckoutUrlSubmission.pending ? "Loading..." : "Buy Credits"}
</button>
</div>
</section>
{/* Usage Section */}
<section data-slot="usage-section">
<div data-slot="section-title">
<h2>Usage History</h2>
<p>Recent API usage and costs.</p>
</div>
<div data-slot="usage-table">
<Show
when={billingInfo() && billingInfo()!.usage.length > 0}
fallback={
<div data-slot="empty-state">
<p>Make your first API call to get started.</p>
</div>
}
>
<table data-slot="usage-table-element">
<thead>
<tr>
<th>Date</th>
<th>Model</th>
<th>Tokens</th>
<th>Cost</th>
</tr>
</thead>
<tbody>
<For each={billingInfo()!.usage}>
{(usage) => {
const totalTokens = usage.inputTokens + usage.outputTokens + (usage.reasoningTokens || 0)
const date = new Date(usage.timeCreated)
return (
<tr>
<td data-slot="usage-date" title={formatDateUTC(date)}>
{formatDateForTable(date)}
</td>
<td data-slot="usage-model">{usage.model}</td>
<td data-slot="usage-tokens">{totalTokens.toLocaleString()}</td>
<td data-slot="usage-cost">${((usage.cost ?? 0) / 100000000).toFixed(4)}</td>
</tr>
)
}}
</For>
</tbody>
</table>
</Show>
</div>
</section>
{/* Payments Section */}
<Show when={billingInfo() && billingInfo()!.payments.length > 0}>
<section data-slot="payments-section">
<div data-slot="section-title">
<h2>Payments History</h2>
<p>Recent payment transactions.</p>
</div>
<div data-slot="payments-table">
<table data-slot="payments-table-element">
<thead>
<tr>
<th>Date</th>
<th>Payment ID</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<For each={billingInfo()!.payments}>
{(payment) => {
const date = new Date(payment.timeCreated)
return (
<tr>
<td data-slot="payment-date" title={formatDateUTC(date)}>
{formatDateForTable(date)}
</td>
<td data-slot="payment-id">{payment.id}</td>
<td data-slot="payment-amount">${((payment.amount ?? 0) / 100000000).toFixed(2)}</td>
</tr>
)
}}
</For>
</tbody>
</table>
</div>
</section>
</Show>
<NewUserSection />
<KeySection />
<BalanceSection />
<UsageSection />
<PaymentSection />
</div>
</div>
)
@@ -0,0 +1,54 @@
import type { APIEvent } from "@solidjs/start/server"
import { handler } from "~/util/zen"
type Usage = {
prompt_tokens?: number
completion_tokens?: number
total_tokens?: number
prompt_tokens_details?: {
text_tokens?: number
audio_tokens?: number
image_tokens?: number
cached_tokens?: number
}
completion_tokens_details?: {
reasoning_tokens?: number
audio_tokens?: number
accepted_prediction_tokens?: number
rejected_prediction_tokens?: number
}
}
export function POST(input: APIEvent) {
let usage: Usage
return handler(input, {
modifyBody: (body: any) => ({
...body,
...(body.stream ? { stream_options: { include_usage: true } } : {}),
}),
setAuthHeader: (headers: Headers, apiKey: string) => {
headers.set("authorization", `Bearer ${apiKey}`)
},
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
onStreamPart: (chunk: string) => {
if (!chunk.startsWith("data: ")) return
let json
try {
json = JSON.parse(chunk.slice(6)) as { usage?: Usage }
} catch (e) {
return
}
if (!json.usage) return
usage = json.usage
},
getStreamUsage: () => usage,
normalizeUsage: (usage: Usage) => ({
inputTokens: usage.prompt_tokens ?? 0,
outputTokens: usage.completion_tokens ?? 0,
reasoningTokens: usage.completion_tokens_details?.reasoning_tokens ?? undefined,
cacheReadTokens: usage.prompt_tokens_details?.cached_tokens ?? undefined,
}),
})
}
+61
View File
@@ -0,0 +1,61 @@
import type { APIEvent } from "@solidjs/start/server"
import { handler } from "~/util/zen"
type Usage = {
cache_creation?: {
ephemeral_5m_input_tokens?: number
ephemeral_1h_input_tokens?: number
}
cache_creation_input_tokens?: number
cache_read_input_tokens?: number
input_tokens?: number
output_tokens?: number
server_tool_use?: {
web_search_requests?: number
}
}
export function POST(input: APIEvent) {
let usage: Usage
return handler(input, {
modifyBody: (body: any) => ({
...body,
service_tier: "standard_only",
}),
setAuthHeader: (headers: Headers, apiKey: string) => headers.set("x-api-key", apiKey),
parseApiKey: (headers: Headers) => headers.get("x-api-key") ?? undefined,
onStreamPart: (chunk: string) => {
const data = chunk.split("\n")[1]
if (!data.startsWith("data: ")) return
let json
try {
json = JSON.parse(data.slice(6)) as { usage?: Usage }
} catch (e) {
return
}
if (!json.usage) return
usage = {
...usage,
...json.usage,
cache_creation: {
...usage?.cache_creation,
...json.usage.cache_creation,
},
server_tool_use: {
...usage?.server_tool_use,
...json.usage.server_tool_use,
},
}
},
getStreamUsage: () => usage,
normalizeUsage: (usage: Usage) => ({
inputTokens: usage.input_tokens ?? 0,
outputTokens: usage.output_tokens ?? 0,
cacheReadTokens: usage.cache_read_input_tokens ?? undefined,
cacheWrite5mTokens: usage.cache_creation?.ephemeral_5m_input_tokens ?? undefined,
cacheWrite1hTokens: usage.cache_creation?.ephemeral_1h_input_tokens ?? undefined,
}),
})
}
+52
View File
@@ -0,0 +1,52 @@
import type { APIEvent } from "@solidjs/start/server"
import { handler } from "~/util/zen"
type Usage = {
input_tokens?: number
input_tokens_details?: {
cached_tokens?: number
}
output_tokens?: number
output_tokens_details?: {
reasoning_tokens?: number
}
total_tokens?: number
}
export function POST(input: APIEvent) {
let usage: Usage
return handler(input, {
setAuthHeader: (headers: Headers, apiKey: string) => {
headers.set("authorization", `Bearer ${apiKey}`)
},
parseApiKey: (headers: Headers) => headers.get("authorization")?.split(" ")[1],
onStreamPart: (chunk: string) => {
const [event, data] = chunk.split("\n")
if (event !== "event: response.completed") return
if (!data.startsWith("data: ")) return
let json
try {
json = JSON.parse(data.slice(6)) as { response?: { usage?: Usage } }
} catch (e) {
return
}
if (!json.response?.usage) return
usage = json.response.usage
},
getStreamUsage: () => usage,
normalizeUsage: (usage: Usage) => {
const inputTokens = usage.input_tokens ?? 0
const outputTokens = usage.output_tokens ?? 0
const reasoningTokens = usage.output_tokens_details?.reasoning_tokens ?? undefined
const cacheReadTokens = usage.input_tokens_details?.cached_tokens ?? undefined
return {
inputTokens: inputTokens - (cacheReadTokens ?? 0),
outputTokens: outputTokens - (reasoningTokens ?? 0),
reasoningTokens,
cacheReadTokens,
}
},
})
}
+2 -1
View File
@@ -14,6 +14,7 @@ body {
--font-size-8xl: 6rem;
--font-size-9xl: 8rem;
--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-mono:
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-sans: var(--font-mono);
}
+531
View File
@@ -0,0 +1,531 @@
import type { APIEvent } from "@solidjs/start/server"
import path from "node:path"
import { and, Database, eq, isNull, sql } from "@opencode/cloud-core/drizzle/index.js"
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
import { BillingTable, UsageTable } from "@opencode/cloud-core/schema/billing.sql.js"
import { centsToMicroCents } from "@opencode/cloud-core/util/price.js"
import { Identifier } from "@opencode/cloud-core/identifier.js"
import { Resource } from "@opencode/cloud-resource"
type ModelCost = {
input: number
output: number
cacheRead?: number
cacheWrite5m?: number
cacheWrite1h?: number
}
type Model = {
id: string
auth: boolean
cost: ModelCost | ((usage: any) => ModelCost)
headerMappings: Record<string, string>
providers: Record<
string,
{
api: string
apiKey: string
model: string
weight?: number
}
>
}
export async function handler(
input: APIEvent,
opts: {
modifyBody?: (body: any) => any
setAuthHeader: (headers: Headers, apiKey: string) => void
parseApiKey: (headers: Headers) => string | undefined
onStreamPart: (chunk: string) => void
getStreamUsage: () => any
normalizeUsage: (body: any) => {
inputTokens: number
outputTokens: number
reasoningTokens?: number
cacheReadTokens?: number
cacheWrite5mTokens?: number
cacheWrite1hTokens?: number
}
},
) {
class AuthError extends Error {}
class CreditsError extends Error {}
class ModelError extends Error {}
const MODELS: Record<string, Model> = {
"claude-opus-4-1": {
id: "claude-opus-4-1" as const,
auth: true,
cost: {
input: 0.000015,
output: 0.000075,
cacheRead: 0.0000015,
cacheWrite5m: 0.00001875,
cacheWrite1h: 0.00003,
},
headerMappings: {},
providers: {
anthropic: {
api: "https://api.anthropic.com",
apiKey: Resource.ANTHROPIC_API_KEY.value,
model: "claude-opus-4-1-20250805",
},
},
},
"claude-sonnet-4": {
id: "claude-sonnet-4" as const,
auth: true,
cost: (usage: any) => {
const totalInputTokens =
usage.inputTokens + usage.cacheReadTokens + usage.cacheWrite5mTokens + usage.cacheWrite1hTokens
return totalInputTokens <= 200_000
? {
input: 0.000003,
output: 0.000015,
cacheRead: 0.0000003,
cacheWrite5m: 0.00000375,
cacheWrite1h: 0.000006,
}
: {
input: 0.000006,
output: 0.0000225,
cacheRead: 0.0000006,
cacheWrite5m: 0.0000075,
cacheWrite1h: 0.000012,
}
},
headerMappings: {},
providers: {
anthropic: {
api: "https://api.anthropic.com",
apiKey: Resource.ANTHROPIC_API_KEY.value,
model: "claude-sonnet-4-20250514",
},
},
},
"claude-3-5-haiku": {
id: "claude-3-5-haiku" as const,
auth: true,
cost: {
input: 0.0000008,
output: 0.000004,
cacheRead: 0.00000008,
cacheWrite5m: 0.000001,
cacheWrite1h: 0.0000016,
},
headerMappings: {},
providers: {
anthropic: {
api: "https://api.anthropic.com",
apiKey: Resource.ANTHROPIC_API_KEY.value,
model: "claude-3-5-haiku-20241022",
},
},
},
"gpt-5": {
id: "gpt-5" as const,
auth: true,
cost: {
input: 0.00000125,
output: 0.00001,
cacheRead: 0.000000125,
},
headerMappings: {},
providers: {
openai: {
api: "https://api.openai.com",
apiKey: Resource.OPENAI_API_KEY.value,
model: "gpt-5",
},
},
},
"qwen3-coder": {
id: "qwen3-coder" as const,
auth: true,
cost: {
input: 0.00000045,
output: 0.0000018,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
weight: 4,
},
fireworks: {
api: "https://api.fireworks.ai/inference",
apiKey: Resource.FIREWORKS_API_KEY.value,
model: "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct",
weight: 1,
},
},
},
"kimi-k2": {
id: "kimi-k2" as const,
auth: true,
cost: {
input: 0.0000006,
output: 0.0000025,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "moonshotai/Kimi-K2-Instruct-0905",
weight: 4,
},
fireworks: {
api: "https://api.fireworks.ai/inference",
apiKey: Resource.FIREWORKS_API_KEY.value,
model: "accounts/fireworks/models/kimi-k2-instruct-0905",
weight: 1,
},
},
},
"grok-code": {
id: "grok-code" as const,
auth: false,
cost: {
input: 0,
output: 0,
cacheRead: 0,
},
headerMappings: {
"x-grok-conv-id": "x-opencode-session",
"x-grok-req-id": "x-opencode-request",
},
providers: {
xai: {
api: "https://api.x.ai",
apiKey: Resource.XAI_API_KEY.value,
model: "grok-code",
},
},
},
// deprecated
"qwen/qwen3-coder": {
id: "qwen/qwen3-coder" as const,
auth: true,
cost: {
input: 0.00000038,
output: 0.00000153,
},
headerMappings: {},
providers: {
baseten: {
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
weight: 5,
},
fireworks: {
api: "https://api.fireworks.ai/inference",
apiKey: Resource.FIREWORKS_API_KEY.value,
model: "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct",
weight: 1,
},
},
},
}
const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
]
const logger = {
metric: (values: Record<string, any>) => {
console.log(`_metric:${JSON.stringify(values)}`)
},
log: console.log,
debug: (message: string) => {
if (Resource.App.stage === "production") return
console.debug(message)
},
}
try {
const url = new URL(input.request.url)
const body = await input.request.json()
logger.debug(JSON.stringify(body))
logger.metric({
is_tream: !!body.stream,
session: input.request.headers.get("x-opencode-session"),
request: input.request.headers.get("x-opencode-request"),
})
const MODEL = validateModel()
const apiKey = await authenticate()
const isFree = FREE_WORKSPACES.includes(apiKey?.workspaceID ?? "")
await checkCredits()
const providerName = selectProvider()
const providerData = MODEL.providers[providerName]
logger.metric({ provider: providerName })
// Request to model provider
const startTimestamp = Date.now()
const res = await fetch(path.posix.join(providerData.api, url.pathname.replace(/^\/zen/, "") + url.search), {
method: "POST",
headers: (() => {
const headers = input.request.headers
headers.delete("host")
headers.delete("content-length")
opts.setAuthHeader(headers, providerData.apiKey)
Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => {
headers.set(k, headers.get(v)!)
})
return headers
})(),
body: JSON.stringify({
...(opts.modifyBody?.(body) ?? body),
model: providerData.model,
}),
})
// Scrub response headers
const resHeaders = new Headers()
const keepHeaders = ["content-type", "cache-control"]
for (const [k, v] of res.headers.entries()) {
if (keepHeaders.includes(k.toLowerCase())) {
resHeaders.set(k, v)
}
}
// Handle non-streaming response
if (!body.stream) {
const json = await res.json()
const body = JSON.stringify(json)
logger.metric({ response_length: body.length })
logger.debug(body)
await trackUsage(json.usage)
return new Response(body, {
status: res.status,
statusText: res.statusText,
headers: resHeaders,
})
}
// Handle streaming response
const stream = new ReadableStream({
start(c) {
const reader = res.body?.getReader()
const decoder = new TextDecoder()
let buffer = ""
let responseLength = 0
function pump(): Promise<void> {
return (
reader?.read().then(async ({ done, value }) => {
if (done) {
logger.metric({ response_length: responseLength })
const usage = opts.getStreamUsage()
if (usage) await trackUsage(usage)
c.close()
return
}
if (responseLength === 0) {
logger.metric({ time_to_first_byte: Date.now() - startTimestamp })
}
responseLength += value.length
buffer += decoder.decode(value, { stream: true })
const parts = buffer.split("\n\n")
buffer = parts.pop() ?? ""
for (const part of parts) {
logger.debug(part)
opts.onStreamPart(part.trim())
}
c.enqueue(value)
return pump()
}) || Promise.resolve()
)
}
return pump()
},
})
return new Response(stream, {
status: res.status,
statusText: res.statusText,
headers: resHeaders,
})
function validateModel() {
if (!(body.model in MODELS)) {
throw new ModelError(`Model ${body.model} not supported`)
}
const model = MODELS[body.model as keyof typeof MODELS]
logger.metric({ model: model.id })
return model
}
async function authenticate() {
try {
const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey) throw new AuthError("Missing API key.")
const key = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
workspaceID: KeyTable.workspaceID,
})
.from(KeyTable)
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
.then((rows) => rows[0]),
)
if (!key) throw new AuthError("Invalid API key.")
logger.metric({
api_key: key.id,
workspace: key.workspaceID,
})
return key
} catch (e) {
// ignore error if model does not require authentication
if (!MODEL.auth) return
throw e
}
}
async function checkCredits() {
if (!apiKey || !MODEL.auth || isFree) return
const billing = await Database.use((tx) =>
tx
.select({
balance: BillingTable.balance,
})
.from(BillingTable)
.where(eq(BillingTable.workspaceID, apiKey.workspaceID))
.then((rows) => rows[0]),
)
if (billing.balance <= 0) throw new CreditsError("Insufficient balance")
}
function selectProvider() {
const picks = Object.entries(MODEL.providers).flatMap(([name, provider]) =>
Array<string>(provider.weight ?? 1).fill(name),
)
return picks[Math.floor(Math.random() * picks.length)]
}
async function trackUsage(usage: any) {
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
opts.normalizeUsage(usage)
const modelCost = typeof MODEL.cost === "function" ? MODEL.cost(usage) : MODEL.cost
const inputCost = modelCost.input * inputTokens * 100
const outputCost = modelCost.output * outputTokens * 100
const reasoningCost = (() => {
if (!reasoningTokens) return undefined
return modelCost.output * reasoningTokens * 100
})()
const cacheReadCost = (() => {
if (!cacheReadTokens) return undefined
if (!modelCost.cacheRead) return undefined
return modelCost.cacheRead * cacheReadTokens * 100
})()
const cacheWrite5mCost = (() => {
if (!cacheWrite5mTokens) return undefined
if (!modelCost.cacheWrite5m) return undefined
return modelCost.cacheWrite5m * cacheWrite5mTokens * 100
})()
const cacheWrite1hCost = (() => {
if (!cacheWrite1hTokens) return undefined
if (!modelCost.cacheWrite1h) return undefined
return modelCost.cacheWrite1h * cacheWrite1hTokens * 100
})()
const totalCostInCent =
inputCost +
outputCost +
(reasoningCost ?? 0) +
(cacheReadCost ?? 0) +
(cacheWrite5mCost ?? 0) +
(cacheWrite1hCost ?? 0)
logger.metric({
"tokens.input": inputTokens,
"tokens.output": outputTokens,
"tokens.reasoning": reasoningTokens,
"tokens.cache_read": cacheReadTokens,
"tokens.cache_write_5m": cacheWrite5mTokens,
"tokens.cache_write_1h": cacheWrite1hTokens,
"cost.input": Math.round(inputCost),
"cost.output": Math.round(outputCost),
"cost.reasoning": reasoningCost ? Math.round(reasoningCost) : undefined,
"cost.cache_read": cacheReadCost ? Math.round(cacheReadCost) : undefined,
"cost.cache_write_5m": cacheWrite5mCost ? Math.round(cacheWrite5mCost) : undefined,
"cost.cache_write_1h": cacheWrite1hCost ? Math.round(cacheWrite1hCost) : undefined,
"cost.total": Math.round(totalCostInCent),
})
if (!apiKey) return
const cost = isFree ? 0 : centsToMicroCents(totalCostInCent)
await Database.transaction(async (tx) => {
await tx.insert(UsageTable).values({
workspaceID: apiKey.workspaceID,
id: Identifier.create("usage"),
model: MODEL.id,
provider: providerName,
inputTokens,
outputTokens,
reasoningTokens,
cacheReadTokens,
cacheWrite5mTokens,
cacheWrite1hTokens,
cost,
})
await tx
.update(BillingTable)
.set({
balance: sql`${BillingTable.balance} - ${cost}`,
})
.where(eq(BillingTable.workspaceID, apiKey.workspaceID))
})
await Database.use((tx) =>
tx
.update(KeyTable)
.set({ timeUsed: sql`now()` })
.where(eq(KeyTable.id, apiKey.id)),
)
}
} catch (error: any) {
logger.metric({
"error.type": error.constructor.name,
"error.message": error.message,
})
// Note: both top level "type" and "error.type" fields are used by the @ai-sdk/anthropic client to render the error message.
if (error instanceof AuthError || error instanceof CreditsError || error instanceof ModelError)
return new Response(
JSON.stringify({
type: "error",
error: { type: error.constructor.name, message: error.message },
}),
{ status: 401 },
)
return new Response(
JSON.stringify({
type: "error",
error: {
type: "error",
message: error.message,
},
}),
{ status: 500 },
)
}
}
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}
+3 -6
View File
@@ -3,6 +3,7 @@
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
@@ -11,14 +12,10 @@
"allowJs": true,
"strict": true,
"noEmit": true,
"types": [
"vinxi/types/client"
],
"types": ["vinxi/types/client"],
"isolatedModules": true,
"paths": {
"~/*": [
"./src/*"
]
"~/*": ["./src/*"]
}
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { Resource } from "@opencode/cloud-resource"
import { Resource } from "sst"
import { defineConfig } from "drizzle-kit"
export default defineConfig({
@@ -6,7 +6,7 @@ export default defineConfig({
strict: true,
schema: ["./src/**/*.sql.ts"],
verbose: true,
dialect: "postgresql",
dialect: "mysql",
dbCredentials: {
database: Resource.Database.database,
host: Resource.Database.host,
@@ -1,66 +0,0 @@
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,89 @@
CREATE TABLE `account` (
`id` varchar(30) NOT NULL,
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`email` varchar(255) NOT NULL,
CONSTRAINT `email` UNIQUE(`email`)
);
--> statement-breakpoint
CREATE TABLE `billing` (
`id` varchar(30) NOT NULL,
`workspace_id` varchar(30) NOT NULL,
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`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(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`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(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`model` varchar(255) NOT NULL,
`input_tokens` int NOT NULL,
`output_tokens` int NOT NULL,
`reasoning_tokens` int,
`cache_read_tokens` int,
`cache_write_tokens` int,
`cost` bigint NOT NULL,
CONSTRAINT `usage_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`)
);
--> statement-breakpoint
CREATE TABLE `key` (
`id` varchar(30) NOT NULL,
`workspace_id` varchar(30) NOT NULL,
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`user_id` text NOT NULL,
`name` varchar(255) NOT NULL,
`key` varchar(255) NOT NULL,
`time_used` timestamp(3),
CONSTRAINT `key_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`),
CONSTRAINT `global_key` UNIQUE(`key`)
);
--> statement-breakpoint
CREATE TABLE `user` (
`id` varchar(30) NOT NULL,
`workspace_id` varchar(30) NOT NULL,
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
`email` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`time_seen` timestamp(3),
`color` int,
CONSTRAINT `user_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`),
CONSTRAINT `user_email` UNIQUE(`workspace_id`,`email`)
);
--> statement-breakpoint
CREATE TABLE `workspace` (
`id` varchar(30) NOT NULL,
`slug` varchar(255),
`name` varchar(255),
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`time_deleted` timestamp(3),
CONSTRAINT `workspace_id` PRIMARY KEY(`id`),
CONSTRAINT `slug` UNIQUE(`slug`)
);
@@ -0,0 +1,2 @@
ALTER TABLE `key` ADD `actor` json;--> statement-breakpoint
ALTER TABLE `key` DROP COLUMN `user_id`;
@@ -1,8 +0,0 @@
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");
@@ -1,14 +0,0 @@
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 `key` ADD `old_name` varchar(255);
@@ -0,0 +1 @@
ALTER TABLE `key` ADD CONSTRAINT `name` UNIQUE(`workspace_id`,`name`);
@@ -1 +0,0 @@
ALTER TABLE "usage" DROP COLUMN "request_id";
@@ -0,0 +1 @@
ALTER TABLE `usage` ADD `provider` varchar(255);
@@ -0,0 +1 @@
ALTER TABLE `usage` MODIFY COLUMN `provider` varchar(255) NOT NULL;
@@ -0,0 +1,2 @@
ALTER TABLE `usage` ADD `cache_write_5m_tokens` int;--> statement-breakpoint
ALTER TABLE `usage` ADD `cache_write_1h_tokens` int;
@@ -0,0 +1 @@
ALTER TABLE `usage` DROP COLUMN `cache_write_tokens`;
+532 -424
View File
@@ -1,461 +1,569 @@
{
"id": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
"version": "5",
"dialect": "mysql",
"id": "aee779c5-db1d-4655-95ec-6451c18455be",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.billing": {
"name": "billing",
"schema": "",
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"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
"notNull": false,
"autoincrement": 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
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"email": {
"name": "email",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
}
+289 -235
View File
@@ -1,515 +1,569 @@
{
"id": "bf9e9084-4073-4ecb-8e56-5610816c9589",
"prevId": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
"version": "7",
"dialect": "postgresql",
"version": "5",
"dialect": "mysql",
"id": "79b7ee25-1c1c-41ff-9bbf-754af257102b",
"prevId": "aee779c5-db1d-4655-95ec-6451c18455be",
"tables": {
"public.account": {
"account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.billing": {
"billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.payment": {
"payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.usage": {
"usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
},
"request_id": {
"name": "request_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.user": {
"name": "user",
"schema": "",
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": true
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.workspace": {
"workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
}
+243 -282
View File
@@ -1,615 +1,576 @@
{
"id": "351e4956-74e0-4282-a23b-02f1a73fa38c",
"prevId": "bf9e9084-4073-4ecb-8e56-5610816c9589",
"version": "7",
"dialect": "postgresql",
"version": "5",
"dialect": "mysql",
"id": "9f51ef52-31ac-4ace-8b6d-39b35efe9c81",
"prevId": "79b7ee25-1c1c-41ff-9bbf-754af257102b",
"tables": {
"public.account": {
"account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.billing": {
"billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.payment": {
"payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.usage": {
"usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
},
"request_id": {
"name": "request_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.key": {
"key": {
"name": "key",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": true
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
"columns": ["key"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.user": {
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.workspace": {
"workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
}
+248 -276
View File
@@ -1,609 +1,581 @@
{
"id": "fa935883-9e51-4811-90c7-8967eefe458c",
"prevId": "351e4956-74e0-4282-a23b-02f1a73fa38c",
"version": "7",
"dialect": "postgresql",
"version": "5",
"dialect": "mysql",
"id": "26cebd59-f553-441c-a2b2-2f9578a0ad2b",
"prevId": "9f51ef52-31ac-4ace-8b6d-39b35efe9c81",
"tables": {
"public.account": {
"account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": [
{
"expression": "email",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.billing": {
"billing": {
"name": "billing",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.payment": {
"payment": {
"name": "payment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.usage": {
"usage": {
"name": "usage",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
}
},
"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"
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.key": {
"key": {
"name": "key",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": true
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.user": {
"user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "text",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "integer",
"type": "int",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": 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"
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
},
"public.workspace": {
"workspace": {
"name": "workspace",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": true,
"notNull": true
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"default": "now()"
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp with time zone",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": [
{
"expression": "slug",
"isExpression": false,
"asc": true,
"nulls": "last"
}
],
"isUnique": true,
"concurrently": false,
"method": "btree",
"with": {}
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
"checkConstraint": {}
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
}
@@ -0,0 +1,588 @@
{
"version": "5",
"dialect": "mysql",
"id": "06dc6226-bfbb-4ccc-b4bc-f26070c3bed5",
"prevId": "26cebd59-f553-441c-a2b2-2f9578a0ad2b",
"tables": {
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
@@ -0,0 +1,588 @@
{
"version": "5",
"dialect": "mysql",
"id": "d13af80e-3c70-4866-8f14-48e7ff6ff0ff",
"prevId": "06dc6226-bfbb-4ccc-b4bc-f26070c3bed5",
"tables": {
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
@@ -0,0 +1,602 @@
{
"version": "5",
"dialect": "mysql",
"id": "b0ad4b11-b607-46c7-8e2d-3b9823cdc5f7",
"prevId": "d13af80e-3c70-4866-8f14-48e7ff6ff0ff",
"tables": {
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_tokens": {
"name": "cache_write_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_5m_tokens": {
"name": "cache_write_5m_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_1h_tokens": {
"name": "cache_write_1h_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
@@ -0,0 +1,595 @@
{
"version": "5",
"dialect": "mysql",
"id": "91067cc9-d492-47b3-932a-42dcc0920b3c",
"prevId": "b0ad4b11-b607-46c7-8e2d-3b9823cdc5f7",
"tables": {
"account": {
"name": "account",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"email": {
"name": "email",
"columns": ["email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraint": {}
},
"billing": {
"name": "billing",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_id": {
"name": "payment_method_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_method_last4": {
"name": "payment_method_last4",
"type": "varchar(4)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"balance": {
"name": "balance",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reload": {
"name": "reload",
"type": "boolean",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"payment": {
"name": "payment",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"customer_id": {
"name": "customer_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"payment_id": {
"name": "payment_id",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"amount": {
"name": "amount",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"usage": {
"name": "usage",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"input_tokens": {
"name": "input_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"output_tokens": {
"name": "output_tokens",
"type": "int",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"reasoning_tokens": {
"name": "reasoning_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_read_tokens": {
"name": "cache_read_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_5m_tokens": {
"name": "cache_write_5m_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cache_write_1h_tokens": {
"name": "cache_write_1h_tokens",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "bigint",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"key": {
"name": "key",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"actor": {
"name": "actor",
"type": "json",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"old_name": {
"name": "old_name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"key": {
"name": "key",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_used": {
"name": "time_used",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"global_key": {
"name": "global_key",
"columns": ["key"],
"isUnique": true
},
"name": {
"name": "name",
"columns": ["workspace_id", "name"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"user": {
"name": "user",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"workspace_id": {
"name": "workspace_id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"time_seen": {
"name": "time_seen",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"color": {
"name": "color",
"type": "int",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"user_email": {
"name": "user_email",
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
},
"workspace": {
"name": "workspace",
"columns": {
"id": {
"name": "id",
"type": "varchar(30)",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"slug": {
"name": "slug",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"name": {
"name": "name",
"type": "varchar(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"time_created": {
"name": "time_created",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(now())"
},
"time_updated": {
"name": "time_updated",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
},
"time_deleted": {
"name": "time_deleted",
"type": "timestamp(3)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"slug": {
"name": "slug",
"columns": ["slug"],
"isUnique": true
}
},
"foreignKeys": {},
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": ["id"]
}
},
"uniqueConstraints": {},
"checkConstraint": {}
}
},
"views": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"tables": {},
"indexes": {}
}
}
+42 -14
View File
@@ -1,34 +1,62 @@
{
"version": "7",
"dialect": "postgresql",
"dialect": "mysql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1754518198186,
"tag": "0000_amused_mojo",
"version": "5",
"when": 1756796050935,
"tag": "0000_fluffy_raza",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1754609655262,
"tag": "0001_thankful_chat",
"version": "5",
"when": 1756871639102,
"tag": "0001_serious_whistler",
"breakpoints": true
},
{
"idx": 2,
"version": "7",
"when": 1754627626945,
"tag": "0002_stale_jackal",
"version": "5",
"when": 1757597611832,
"tag": "0002_violet_loners",
"breakpoints": true
},
{
"idx": 3,
"version": "7",
"when": 1754672464106,
"tag": "0003_tranquil_spencer_smythe",
"version": "5",
"when": 1757600397194,
"tag": "0003_dusty_clint_barton",
"breakpoints": true
},
{
"idx": 4,
"version": "5",
"when": 1757627357232,
"tag": "0004_first_mockingbird",
"breakpoints": true
},
{
"idx": 5,
"version": "5",
"when": 1757632304856,
"tag": "0005_jazzy_skrulls",
"breakpoints": true
},
{
"idx": 6,
"version": "5",
"when": 1757643108507,
"tag": "0006_parallel_gauntlet",
"breakpoints": true
},
{
"idx": 7,
"version": "5",
"when": 1757693869142,
"tag": "0007_familiar_nightshade",
"breakpoints": true
}
]
}
}
+8 -3
View File
@@ -1,12 +1,13 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode/cloud-core",
"version": "0.6.2",
"version": "0.7.8",
"private": true,
"type": "module",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@opencode/cloud-resource": "workspace:*",
"@planetscale/database": "1.19.0",
"drizzle-orm": "0.41.0",
"postgres": "3.4.7",
"stripe": "18.0.0",
@@ -16,9 +17,13 @@
"./*": "./src/*"
},
"scripts": {
"db": "sst shell drizzle-kit"
"db": "sst shell drizzle-kit",
"db-dev": "sst shell --stage dev -- drizzle-kit",
"db-prod": "sst shell --stage production -- drizzle-kit",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"drizzle-kit": "0.30.5"
"drizzle-kit": "0.30.5",
"mysql2": "3.14.4"
}
}
+1 -43
View File
@@ -4,8 +4,6 @@ import { BillingTable, PaymentTable, 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"
import { User } from "./user"
import { Resource } from "@opencode/cloud-resource"
@@ -52,46 +50,6 @@ export namespace Billing {
)
}
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
})
},
)
export const generateCheckoutUrl = fn(
z.object({
successUrl: z.string(),
@@ -112,7 +70,7 @@ export namespace Billing {
product_data: {
name: "opencode credits",
},
unit_amount: 2000, // $20 minimum
unit_amount: 2123, // $20 minimum + Stripe fee 4.4% + $0.30
},
quantity: 1,
},
+24 -34
View File
@@ -1,41 +1,33 @@
import { drizzle } from "drizzle-orm/postgres-js"
import { drizzle } from "drizzle-orm/planetscale-serverless"
import { Resource } from "@opencode/cloud-resource"
export * from "drizzle-orm"
import postgres from "postgres"
import { Client } from "@planetscale/database"
const init = () => {
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: 6,
})
return drizzle(client, {})
}
const createClient = init
import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
import type { ExtractTablesWithRelations } from "drizzle-orm"
import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
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 Transaction = MySqlTransaction<
PlanetscaleQueryResultHKT,
PlanetScalePreparedQueryHKT,
Record<string, never>,
ExtractTablesWithRelations<Record<string, never>>
>
export type TxOrDb = Transaction | ReturnType<typeof createClient>
const client = memo(() => {
const result = new Client({
host: Resource.Database.host,
username: Resource.Database.username,
password: Resource.Database.password,
})
const db = drizzle(result, {})
return db
})
export type TxOrDb = Transaction | ReturnType<typeof client>
const TransactionContext = Context.create<{
tx: TxOrDb
@@ -48,14 +40,13 @@ export namespace Database {
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,
tx: client(),
},
() => callback(client),
() => callback(client()),
)
await Promise.all(effects.map((x) => x()))
return result
@@ -76,15 +67,14 @@ export namespace Database {
}
}
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: PgTransactionConfig) {
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: MySqlTransactionConfig) {
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) => {
const result = await client().transaction(async (tx) => {
return TransactionContext.provide({ tx, effects }, () => callback(tx))
}, config)
await Promise.all(effects.map((x) => x()))
+6 -2
View File
@@ -1,4 +1,5 @@
import { bigint, timestamp, varchar } from "drizzle-orm/pg-core"
import { sql } from "drizzle-orm"
import { bigint, timestamp, varchar } from "drizzle-orm/mysql-core"
export const ulid = (name: string) => varchar(name, { length: 30 })
@@ -15,7 +16,7 @@ export const id = () => ulid("id").notNull()
export const utc = (name: string) =>
timestamp(name, {
withTimezone: true,
fsp: 3,
})
export const currency = (name: string) =>
@@ -25,5 +26,8 @@ export const currency = (name: string) =>
export const timestamps = {
timeCreated: utc("time_created").notNull().defaultNow(),
timeUpdated: utc("time_updated")
.notNull()
.default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
timeDeleted: utc("time_deleted"),
}
+40 -48
View File
@@ -1,79 +1,71 @@
import { z } from "zod"
import { fn } from "./util/fn"
import { Actor } from "./actor"
import { and, Database, eq, sql } from "./drizzle"
import { and, Database, eq, isNull, sql } from "./drizzle"
import { Identifier } from "./identifier"
import { KeyTable } from "./schema/key.sql"
export namespace Key {
export const list = async () => {
const user = Actor.assert("user")
const workspace = Actor.workspace()
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,
})
.select()
.from(KeyTable)
.where(eq(KeyTable.workspaceID, user.properties.workspaceID))
.where(and(eq(KeyTable.workspaceID, workspace), isNull(KeyTable.timeDeleted)))
.orderBy(sql`${KeyTable.timeCreated} DESC`),
)
return keys
}
export const create = fn(z.object({ name: z.string().min(1).max(255) }), async (input) => {
const user = Actor.assert("user")
const workspaceID = Actor.workspace()
const { name } = input
// 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))
let secretKey = "sk-"
const array = new Uint32Array(64)
crypto.getRandomValues(array)
for (let i = 0, l = array.length; i < l; i++) {
secretKey += chars[array[i] % chars.length]
}
const secretKey = `sk-${randomPart}`
const keyID = Identifier.create("key")
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(),
await Database.use((tx) =>
tx.insert(KeyTable).values({
id: keyID,
workspaceID,
actor: Actor.use(),
name,
key: secretKey,
timeUsed: null,
}),
)
return {
key: secretKey,
id: keyRecord[0].id,
name: keyRecord[0].name,
created: keyRecord[0].timeCreated,
}
return keyID
})
export const remove = fn(z.object({ id: z.string() }), async (input) => {
const user = Actor.assert("user")
const { id } = input
const workspace = Actor.workspace()
await Database.transaction(async (tx) => {
const row = await tx
.select({
name: KeyTable.name,
})
.from(KeyTable)
.where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, workspace)))
.then((rows) => rows[0])
if (!row) return
const result = await Database.use((tx) =>
tx
.delete(KeyTable)
.where(and(eq(KeyTable.id, id), eq(KeyTable.workspaceID, user.properties.workspaceID)))
.returning({ id: KeyTable.id }),
)
if (result.length === 0) {
throw new Error("Key not found")
}
return { id: result[0].id }
await tx
.update(KeyTable)
.set({
timeDeleted: sql`now()`,
oldName: row.name,
name: input.id, // Use the key ID as the name
})
.where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, workspace)))
})
})
}
+2 -2
View File
@@ -1,7 +1,7 @@
import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
import { mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
import { id, timestamps } from "../drizzle/types"
export const AccountTable = pgTable(
export const AccountTable = mysqlTable(
"account",
{
id: id(),
+11 -9
View File
@@ -1,8 +1,8 @@
import { bigint, boolean, integer, pgTable, varchar } from "drizzle-orm/pg-core"
import { bigint, boolean, int, mysqlTable, varchar, json } from "drizzle-orm/mysql-core"
import { timestamps, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
export const BillingTable = pgTable(
export const BillingTable = mysqlTable(
"billing",
{
...workspaceColumns,
@@ -16,7 +16,7 @@ export const BillingTable = pgTable(
(table) => [...workspaceIndexes(table)],
)
export const PaymentTable = pgTable(
export const PaymentTable = mysqlTable(
"payment",
{
...workspaceColumns,
@@ -28,17 +28,19 @@ export const PaymentTable = pgTable(
(table) => [...workspaceIndexes(table)],
)
export const UsageTable = pgTable(
export const UsageTable = mysqlTable(
"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"),
provider: varchar("provider", { length: 255 }).notNull(),
inputTokens: int("input_tokens").notNull(),
outputTokens: int("output_tokens").notNull(),
reasoningTokens: int("reasoning_tokens"),
cacheReadTokens: int("cache_read_tokens"),
cacheWrite5mTokens: int("cache_write_5m_tokens"),
cacheWrite1hTokens: int("cache_write_1h_tokens"),
cost: bigint("cost", { mode: "number" }).notNull(),
},
(table) => [...workspaceIndexes(table)],
+10 -4
View File
@@ -1,16 +1,22 @@
import { text, pgTable, varchar, uniqueIndex } from "drizzle-orm/pg-core"
import { mysqlTable, varchar, uniqueIndex, json } from "drizzle-orm/mysql-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
import { Actor } from "../actor"
export const KeyTable = pgTable(
export const KeyTable = mysqlTable(
"key",
{
...workspaceColumns,
...timestamps,
userID: text("user_id").notNull(),
actor: json("actor").$type<Actor.Info>(),
name: varchar("name", { length: 255 }).notNull(),
oldName: varchar("old_name", { length: 255 }),
key: varchar("key", { length: 255 }).notNull(),
timeUsed: utc("time_used"),
},
(table) => [...workspaceIndexes(table), uniqueIndex("global_key").on(table.key)],
(table) => [
...workspaceIndexes(table),
uniqueIndex("global_key").on(table.key),
uniqueIndex("name").on(table.workspaceID, table.name),
],
)
+4 -4
View File
@@ -1,16 +1,16 @@
import { text, pgTable, uniqueIndex, varchar, integer } from "drizzle-orm/pg-core"
import { text, mysqlTable, uniqueIndex, varchar, int } from "drizzle-orm/mysql-core"
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
export const UserTable = pgTable(
export const UserTable = mysqlTable(
"user",
{
...workspaceColumns,
...timestamps,
email: text("email").notNull(),
email: varchar("email", { length: 255 }).notNull(),
name: varchar("name", { length: 255 }).notNull(),
timeSeen: utc("time_seen"),
color: integer("color"),
color: int("color"),
},
(table) => [...workspaceIndexes(table), uniqueIndex("user_email").on(table.workspaceID, table.email)],
)
+2 -6
View File
@@ -1,7 +1,7 @@
import { primaryKey, foreignKey, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
import { timestamps, ulid } from "../drizzle/types"
export const WorkspaceTable = pgTable(
export const WorkspaceTable = mysqlTable(
"workspace",
{
id: ulid("id").notNull().primaryKey(),
@@ -17,9 +17,5 @@ export function workspaceIndexes(table: any) {
primaryKey({
columns: [table.workspaceID, table.id],
}),
foreignKey({
foreignColumns: [WorkspaceTable.id],
columns: [table.workspaceID],
}),
]
}
+1 -4
View File
@@ -1,9 +1,6 @@
import { z } from "zod"
export function fn<T extends z.ZodType, Result>(
schema: T,
cb: (input: z.output<T>) => Result,
) {
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)
+9 -2
View File
@@ -1,11 +1,18 @@
export function memo<T>(fn: () => T) {
export function memo<T>(fn: () => T, cleanup?: (input: T) => Promise<void>) {
let value: T | undefined
let loaded = false
return (): T => {
const result = (): T => {
if (loaded) return value as T
loaded = true
value = fn()
return value as T
}
result.reset = async () => {
if (cleanup && value) await cleanup(value)
loaded = false
value = undefined
}
return result
}
+1 -1
View File
@@ -26,7 +26,7 @@ export namespace Workspace {
await tx.insert(BillingTable).values({
workspaceID,
id: Identifier.create("billing"),
balance: centsToMicroCents(100),
balance: 0,
})
})
await Actor.provide(
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}
+4 -1
View File
@@ -1,9 +1,12 @@
{
"name": "@opencode/cloud-function",
"version": "0.6.2",
"version": "0.7.8",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@cloudflare/workers-types": "4.20250522.0",
"@types/node": "catalog:",
+6 -5
View File
@@ -10,6 +10,7 @@ import { Account } from "@opencode/cloud-core/account.js"
import { Workspace } from "@opencode/cloud-core/workspace.js"
import { Actor } from "@opencode/cloud-core/actor.js"
import { Resource } from "@opencode/cloud-resource"
import { Database } from "@opencode/cloud-core/drizzle/index.js"
type Env = {
AuthStorage: KVNamespace
@@ -33,7 +34,7 @@ const MY_THEME: Theme = {
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return issuer({
const result = await issuer({
theme: MY_THEME,
providers: {
github: GithubProvider({
@@ -99,15 +100,14 @@ export default {
let email: string | undefined
if (response.provider === "github") {
const userResponse = await fetch("https://api.github.com/user", {
const emails = (await fetch("https://api.github.com/user/emails", {
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
}).then((x) => x.json())) as any
email = emails.find((x: any) => x.primary && x.verified)?.email
} else if (response.provider === "google") {
if (!response.id.email_verified) throw new Error("Google email not verified")
email = response.id.email as string
@@ -135,5 +135,6 @@ export default {
return ctx.subject("account", accountID, { accountID, email })
},
}).fetch(request, env, ctx)
return result
},
}
-596
View File
@@ -1,596 +0,0 @@
import { Hono, MiddlewareHandler } from "hono"
import { type ProviderMetadata, type LanguageModelUsage } 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 { Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js"
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
import { Billing } from "@opencode/cloud-core/billing.js"
import { Resource } from "@opencode/cloud-resource"
type Env = {}
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 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 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)
}
})
})
.all("*", (c) => c.text("Not Found"))
export type ApiType = typeof app
export default app
+49
View File
@@ -0,0 +1,49 @@
import { Resource } from "@opencode/cloud-resource"
import type { TraceItem } from "@cloudflare/workers-types"
export default {
async tail(events: TraceItem[]) {
for (const event of events) {
if (!event.event) continue
if (!("request" in event.event)) continue
if (event.event.request.method !== "POST") continue
const url = new URL(event.event.request.url)
if (url.pathname !== "/zen/v1/chat/completions") return
let metrics = {
event_type: "completions",
"cf.continent": event.event.request.cf?.continent,
"cf.country": event.event.request.cf?.country,
"cf.city": event.event.request.cf?.city,
"cf.region": event.event.request.cf?.region,
"cf.latitude": event.event.request.cf?.latitude,
"cf.longitude": event.event.request.cf?.longitude,
"cf.timezone": event.event.request.cf?.timezone,
duration: event.wallTime,
request_length: parseInt(event.event.request.headers["content-length"] ?? "0"),
status: event.event.response?.status ?? 0,
ip: event.event.request.headers["x-real-ip"],
}
for (const log of event.logs) {
for (const message of log.message) {
if (!message.startsWith("_metric:")) continue
metrics = { ...metrics, ...JSON.parse(message.slice(8)) }
}
}
console.log(JSON.stringify(metrics, null, 2))
const ret = await fetch("https://api.honeycomb.io/1/events/zen", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Honeycomb-Event-Time": (event.eventTimestamp ?? Date.now()).toString(),
"X-Honeycomb-Team": Resource.HONEYCOMB_API_KEY.value,
},
body: JSON.stringify(metrics),
})
console.log(ret.status)
console.log(await ret.text())
}
},
}
+64 -60
View File
@@ -6,87 +6,91 @@
import "sst"
declare module "sst" {
export interface Resource {
"ANTHROPIC_API_KEY": {
"type": "sst.sst.Secret"
"value": string
ANTHROPIC_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
BASETEN_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"DATABASE_PASSWORD": {
"type": "sst.sst.Secret"
"value": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"DATABASE_USERNAME": {
"type": "sst.sst.Secret"
"value": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
FIREWORKS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"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_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_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
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"OPENAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
OPENAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"ZHIPU_API_KEY": {
"type": "sst.sst.Secret"
"value": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
// 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
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}
import "sst"
export {}
export {}
+3
View File
@@ -8,6 +8,9 @@ export const Resource = new Proxy(
// @ts-expect-error
const value = env[prop]
return typeof value === "string" ? JSON.parse(value) : value
} else if (prop === "App") {
// @ts-expect-error
return JSON.parse(env.SST_RESOURCE_App)
}
throw new Error(`"${prop}" is not linked in your sst.config.ts (cloudflare)`)
},
+64 -60
View File
@@ -6,87 +6,91 @@
import "sst"
declare module "sst" {
export interface Resource {
"ANTHROPIC_API_KEY": {
"type": "sst.sst.Secret"
"value": string
ANTHROPIC_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"AUTH_API_URL": {
"type": "sst.sst.Linkable"
"value": string
AUTH_API_URL: {
type: "sst.sst.Linkable"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
BASETEN_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"DATABASE_PASSWORD": {
"type": "sst.sst.Secret"
"value": string
Console: {
type: "sst.cloudflare.SolidStart"
url: string
}
"DATABASE_USERNAME": {
"type": "sst.sst.Secret"
"value": string
Database: {
database: string
host: string
password: string
port: number
type: "sst.sst.Linkable"
username: string
}
"Database": {
"database": string
"host": string
"password": string
"port": number
"type": "sst.sst.Linkable"
"username": string
FIREWORKS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"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_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_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
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"OPENAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
OPENAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
"ZHIPU_API_KEY": {
"type": "sst.sst.Secret"
"value": string
Web: {
type: "sst.cloudflare.Astro"
url: string
}
XAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
}
}
// cloudflare
import * as cloudflare from "@cloudflare/workers-types";
// 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
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}
import "sst"
export {}
export {}
+1 -4
View File
@@ -4,9 +4,6 @@
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"types": [
"@cloudflare/workers-types",
"node"
]
"types": ["@cloudflare/workers-types", "node"]
}
}
+1
View File
@@ -0,0 +1 @@
src/scrap.ts
+20
View File
@@ -0,0 +1,20 @@
{
"name": "@opencode/cloud-scripts",
"version": "0.7.8",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"scripts": {
"shell": "sst shell -- bun tsx",
"shell-dev": "sst shell --stage dev -- bun tsx",
"shell-prod": "sst shell --stage production -- bun tsx"
},
"dependencies": {
"@opencode/cloud-core": "workspace:*",
"tsx": "4.20.5"
},
"devDependencies": {
"@types/node": "catalog:",
"typescript": "catalog:"
}
}
@@ -0,0 +1,10 @@
import { Database, eq } from "@opencode/cloud-core/drizzle/index.js"
import { UsageTable } from "@opencode/cloud-core/schema/billing.sql.js"
await Database.use(async (tx) => {
await tx
.update(UsageTable)
.set({ model: "grok-code" })
.where(eq(UsageTable.model, "x-ai/grok-code-fast-1"))
.limit(90000)
})
+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"]
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
"type": "module",
"private": true,
"devDependencies": {
"@types/bun": "latest"
"@types/bun": "catalog:"
},
"peerDependencies": {
"typescript": "^5"
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../sst-env.d.ts" />
import "sst"
export {}
export {}
+2 -2
View File
@@ -33,8 +33,8 @@ export const api = new sst.cloudflare.Worker("Api", {
},
})
export const web = new sst.cloudflare.x.Astro("Web", {
domain,
new sst.cloudflare.x.Astro("Web", {
domain: "docs." + domain,
path: "packages/web",
environment: {
// For astro config
+59 -27
View File
@@ -5,15 +5,38 @@ import { domain } from "./stage"
// DATABASE
////////////////
const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME")
const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD")
const cluster = planetscale.getDatabaseOutput({
name: "opencode",
organization: "anomalyco",
})
const branch =
$app.stage === "production"
? planetscale.getBranchOutput({
name: "production",
organization: cluster.organization,
database: cluster.name,
})
: new planetscale.Branch("DatabaseBranch", {
database: cluster.name,
organization: cluster.organization,
name: $app.stage,
parentBranch: "production",
})
const password = new planetscale.Password("DatabasePassword", {
name: $app.stage,
database: cluster.name,
organization: cluster.organization,
branch: branch.name,
})
export const database = new sst.Linkable("Database", {
properties: {
host: `aws-us-east-2-${$app.stage === "thdxr" ? "2" : "1"}.pg.psdb.cloud`,
database: "postgres",
username: DATABASE_USERNAME.value,
password: DATABASE_PASSWORD.value,
port: 6432,
host: password.accessHostUrl,
database: cluster.name,
username: password.username,
password: password.plaintext,
port: 3306,
},
})
@@ -45,8 +68,8 @@ export const auth = new sst.cloudflare.Worker("AuthApi", {
// GATEWAY
////////////////
export const stripeWebhook = new WebhookEndpoint("StripeWebhook", {
url: $interpolate`https://console.${domain}/stripe/webhook`,
export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
url: $interpolate`https://${domain}/stripe/webhook`,
enabledEvents: [
"checkout.session.async_payment_failed",
"checkout.session.async_payment_succeeded",
@@ -78,7 +101,9 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhook", {
const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY")
const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY")
const ZHIPU_API_KEY = new sst.Secret("ZHIPU_API_KEY")
const XAI_API_KEY = new sst.Secret("XAI_API_KEY")
const BASETEN_API_KEY = new sst.Secret("BASETEN_API_KEY")
const FIREWORKS_API_KEY = new sst.Secret("FIREWORKS_API_KEY")
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
properties: { value: auth.url.apply((url) => url!) },
@@ -86,27 +111,22 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
properties: { value: stripeWebhook.secret },
})
export const gateway = new sst.cloudflare.Worker("GatewayApi", {
domain: `api.gateway.${domain}`,
handler: "cloud/function/src/gateway.ts",
url: true,
link: [
database,
AUTH_API_URL,
STRIPE_WEBHOOK_SECRET,
STRIPE_SECRET_KEY,
ANTHROPIC_API_KEY,
OPENAI_API_KEY,
ZHIPU_API_KEY,
],
})
////////////////
// CONSOLE
////////////////
export const console = new sst.cloudflare.x.SolidStart("Console", {
domain: `console.${domain}`,
let logProcessor
if ($app.stage === "production" || $app.stage === "frank") {
const HONEYCOMB_API_KEY = new sst.Secret("HONEYCOMB_API_KEY")
logProcessor = new sst.cloudflare.Worker("LogProcessor", {
handler: "cloud/function/src/log-processor.ts",
link: [HONEYCOMB_API_KEY],
})
}
new sst.cloudflare.x.SolidStart("Console", {
domain,
path: "cloud/app",
link: [
database,
@@ -115,11 +135,23 @@ export const console = new sst.cloudflare.x.SolidStart("Console", {
STRIPE_SECRET_KEY,
ANTHROPIC_API_KEY,
OPENAI_API_KEY,
ZHIPU_API_KEY,
XAI_API_KEY,
BASETEN_API_KEY,
FIREWORKS_API_KEY,
],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),
//VITE_API_URL: gateway.url.apply((url) => url!),
VITE_AUTH_URL: auth.url.apply((url) => url!),
},
transform: {
server: {
transform: {
worker: {
placement: { mode: "smart" },
tailConsumers: logProcessor ? [{ service: logProcessor.nodes.worker.scriptName }] : [],
},
},
},
},
})

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