Compare commits

..
Author SHA1 Message Date
Dax Raad 614536a2cc Snapshot release v0.0.0-202509130949 2025-09-13 05:50:27 -04:00
9bb25a9260 Session management and prompt handling improvements (#2577)
Co-authored-by: GitHub Action <action@github.com>
2025-09-13 05:46:14 -04:00
opencode 535230dce4 release: v0.7.9 2025-09-13 05:29:37 +00:00
Dax Raad 555fb53505 nudge llm to continue properly after compaction 2025-09-13 01:23:54 -04:00
b1e0a23351 fix: ShellError: exit code 1 errors (#2568)
Co-authored-by: rekram1-node <aidenpcline@gmail.com>
2025-09-13 00:06:07 -05:00
Nicholas HamiltonandGitHub 2b69bcccdf docs: typo in web agents.mdx (#2574) 2025-09-12 23:26:52 -05:00
e03f27381f docs: add tip block for finding available models (#2501)
Co-authored-by: GitHub Action <action@github.com>
2025-09-12 21:22:54 -04:00
Aiden ClineandGitHub aebd50da7e fix: make permission always behavior match expectation (#2573) 2025-09-12 18:59:38 -05:00
Stephen MurrayandGitHub c02f58c2af fix: await cleanupRevert() to prevent dupe msgs after undo (#2572) 2025-09-12 18:42:39 -05:00
Dax Raad c8f4d54f7f wip: zen 2025-09-12 14:53:00 -04:00
GitHub Action 4983d255dd chore: format code 2025-09-12 18:46:43 +00:00
Dax Raad f2b4891ff0 wip: zen 2025-09-12 14:46:08 -04:00
GitHub Action efcb5abbf7 chore: format code 2025-09-12 18:33:14 +00:00
Jay V d37e58719e ignore: zen 2025-09-12 14:32:43 -04:00
Frank c6c153de95 wip: zen 2025-09-12 14:22:42 -04:00
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 Raadandopencode 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
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 ClineandGitHub 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-GABRIELandGitHub 5b56848c3d Update providers.mdx because of small typo (#2539) 2025-09-10 15:49:25 -05:00
Aiden ClineandGitHub 780e532094 resolve nested commands (#2537) 2025-09-10 14:05:26 -05:00
Aiden ClineandGitHub 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 ClineandGitHub 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
madflowandGitHub 32b47fcc1e feat: svelte lsp (#2508) 2025-09-09 13:59:58 -05:00
Aiden ClineandGitHub 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 ClineandGitHub 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 JacksonandGitHub 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 SundararajanandGitHub 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 ClineandGitHub 1cea8b9e77 tweak: reenable todowrite & todoread for qwen models (#2499) 2025-09-08 13:21:16 -05:00
Douglas DennisandGitHub 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 ClineandGitHub c8ff81bae4 fix: silent error if bad flag was passed (#2486) 2025-09-07 23:14:38 -05:00
Aiden ClineandGitHub 74469a0d3d fix: shell invocations are dropped if last interaction was revert (#2485) 2025-09-07 21:45:13 -05:00
Aiden ClineandGitHub 4d481dea7e fix: dont paste collapse if in bash mode (#2482) 2025-09-07 20:24:49 -05:00
Jay V 48328bec6e docs: fix lander 2025-09-04 17:44:32 -10:00
163 changed files with 9110 additions and 3733 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- uses: oven-sh/setup-bun@v1
with:
bun-version: 1.2.19
bun-version: 1.2.21
- run: bun install
+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)
+5
View File
@@ -72,3 +72,8 @@
| 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) |
+20 -19
View File
@@ -8,7 +8,7 @@
},
"devDependencies": {
"prettier": "3.5.3",
"sst": "3.17.12",
"sst": "3.17.13",
},
},
"cloud/app": {
@@ -26,7 +26,7 @@
},
"cloud/core": {
"name": "@opencode/cloud-core",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@aws-sdk/client-sts": "3.782.0",
"@opencode/cloud-resource": "workspace:*",
@@ -43,7 +43,7 @@
},
"cloud/function": {
"name": "@opencode/cloud-function",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@ai-sdk/anthropic": "2.0.0",
"@ai-sdk/openai": "2.0.2",
@@ -69,7 +69,7 @@
},
"cloud/scripts": {
"name": "@opencode/cloud-scripts",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@opencode/cloud-core": "workspace:*",
"tsx": "4.20.5",
@@ -81,7 +81,7 @@
},
"packages/function": {
"name": "@opencode/function",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@octokit/auth-app": "8.0.1",
"@octokit/rest": "22.0.0",
@@ -96,7 +96,7 @@
},
"packages/opencode": {
"name": "opencode",
"version": "0.6.4",
"version": "0.7.6",
"bin": {
"opencode": "./bin/opencode",
},
@@ -136,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:",
@@ -146,7 +146,7 @@
},
"packages/plugin": {
"name": "@opencode-ai/plugin",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@opencode-ai/sdk": "workspace:*",
},
@@ -157,7 +157,7 @@
},
"packages/sdk/js": {
"name": "@opencode-ai/sdk",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@hey-api/openapi-ts": "0.81.0",
},
@@ -169,7 +169,7 @@
},
"packages/web": {
"name": "@opencode/web",
"version": "0.6.4",
"version": "0.7.6",
"dependencies": {
"@astrojs/cloudflare": "12.6.3",
"@astrojs/markdown-remark": "6.3.1",
@@ -217,6 +217,7 @@
"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",
@@ -2646,23 +2647,23 @@
"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=="],
+1 -1
View File
@@ -7,7 +7,7 @@
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
"build": "vinxi build && ../../packages/opencode/script/schema.ts ./.output/public/config.json",
"start": "vinxi start",
"version": "0.6.5"
"version": "0.7.9"
},
"dependencies": {
"@ibm/plex": "6.4.1",
+8 -8
View File
@@ -1,15 +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
explicitLinks={true}
root={props => (
root={(props) => (
<MetaProvider>
<Title>opencode</Title>
<Meta name="description" content="opencode - The AI coding agent built for the terminal." />
@@ -19,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>
)
}
+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 -23
View File
@@ -1,26 +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: "async",
})
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",
},
)
@@ -1,308 +0,0 @@
/**
* @deprecated Use zen/v1/chat/completions instead
*/
import { Resource } from "@opencode/cloud-resource"
import type { APIEvent } from "@solidjs/start/server"
import { Database, eq, 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"
const MODELS = {
// "anthropic/claude-sonnet-4": {
// auth: true,
// api: "https://api.anthropic.com",
// apiKey: Resource.ANTHROPIC_API_KEY.value,
// model: "claude-sonnet-4-20250514",
// cost: {
// input: 0.0000015,
// output: 0.000006,
// reasoning: 0.0000015,
// cacheRead: 0.0000001,
// cacheWrite: 0.0000001,
// },
// headerMappings: {},
// },
"qwen/qwen3-coder": {
id: "qwen/qwen3-coder",
auth: true,
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
cost: {
input: 0.00000038,
output: 0.00000153,
reasoning: 0,
cacheRead: 0,
cacheWrite: 0,
},
headerMappings: {},
},
"grok-code": {
id: "x-ai/grok-code-fast-1",
auth: false,
api: "https://api.x.ai",
apiKey: Resource.XAI_API_KEY.value,
model: "grok-code",
cost: {
input: 0,
output: 0,
reasoning: 0,
cacheRead: 0,
cacheWrite: 0,
},
headerMappings: {
"x-grok-conv-id": "x-opencode-session",
"x-grok-req-id": "x-opencode-request",
},
},
}
class AuthError extends Error {}
class CreditsError extends Error {}
class ModelError extends Error {}
export async function POST(input: APIEvent) {
try {
const url = new URL(input.request.url)
const body = await input.request.json()
const MODEL = validateModel()
const apiKey = await authenticate()
await checkCredits()
// Request to model provider
const res = await fetch(new URL(url.pathname.replace(/^\/gateway/, "") + url.search, MODEL.api), {
method: "POST",
headers: (() => {
const headers = input.request.headers
headers.delete("host")
headers.delete("content-length")
headers.set("authorization", `Bearer ${MODEL.apiKey}`)
Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => {
headers.set(k, headers.get(v)!)
})
return headers
})(),
body: JSON.stringify({
...body,
model: MODEL.model,
stream_options: {
include_usage: true,
},
}),
})
// 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 body = await res.json()
await trackUsage(body)
return new Response(JSON.stringify(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 = ""
function pump(): Promise<void> {
return (
reader?.read().then(async ({ done, value }) => {
if (done) {
c.close()
return
}
buffer += decoder.decode(value, { stream: true })
const parts = buffer.split("\n\n")
buffer = parts.pop() ?? ""
const usage = parts
.map((part) => part.trim())
.filter((part) => part.startsWith("data: "))
.map((part) => {
try {
return JSON.parse(part.slice(6))
} catch (e) {
return {}
}
})
.find((part) => part.usage)
if (usage) await trackUsage(usage)
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`)
}
return MODELS[body.model as keyof typeof MODELS]
}
async function authenticate() {
try {
const authHeader = input.request.headers.get("authorization")
if (!authHeader || !authHeader.startsWith("Bearer ")) throw new AuthError("Missing API key.")
const apiKey = authHeader.split(" ")[1]
const key = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
workspaceID: KeyTable.workspaceID,
})
.from(KeyTable)
.where(eq(KeyTable.key, apiKey))
.then((rows) => rows[0]),
)
if (!key) throw new AuthError("Invalid API key.")
return key
} catch (e) {
console.log(e)
// ignore error if model does not require authentication
if (!MODEL.auth) return
throw e
}
}
async function checkCredits() {
if (!apiKey || !MODEL.auth) 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")
}
async function trackUsage(chunk: any) {
console.log(`trackUsage ${apiKey}`)
if (!apiKey) return
const usage = chunk.usage
const inputTokens = usage.prompt_tokens ?? 0
const outputTokens = usage.completion_tokens ?? 0
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0
const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0
//const cacheWriteTokens = providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? 0
const cacheWriteTokens = 0
const inputCost = MODEL.cost.input * inputTokens
const outputCost = MODEL.cost.output * outputTokens
const reasoningCost = MODEL.cost.reasoning * reasoningTokens
const cacheReadCost = MODEL.cost.cacheRead * cacheReadTokens
const cacheWriteCost = MODEL.cost.cacheWrite * cacheWriteTokens
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
const cost = centsToMicroCents(costInCents)
await Database.transaction(async (tx) => {
await tx.insert(UsageTable).values({
workspaceID: apiKey.workspaceID,
id: Identifier.create("usage"),
model: MODEL.id,
inputTokens,
outputTokens,
reasoningTokens,
cacheReadTokens,
cacheWriteTokens,
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) {
if (error instanceof AuthError) {
return new Response(
JSON.stringify({
error: {
message: error.message,
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
}),
{
status: 401,
},
)
}
if (error instanceof CreditsError) {
return new Response(
JSON.stringify({
error: {
message: error.message,
type: "insufficient_quota",
param: null,
code: "insufficient_quota",
},
}),
{
status: 401,
},
)
}
if (error instanceof ModelError) {
return new Response(JSON.stringify({ error: { message: error.message } }), {
status: 401,
})
}
console.log(error)
return new Response(JSON.stringify({ error: { message: error.message } }), {
status: 500,
})
}
}
+47 -16
View File
@@ -29,6 +29,9 @@
--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);
@@ -47,7 +50,8 @@
}
[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: center;
@@ -79,6 +83,25 @@
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"] {
@@ -108,6 +131,19 @@
}
}
[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;
@@ -440,22 +476,8 @@
border-left: 1px 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;
}
}
/* Mobile: third column on its own row */
@media (max-width: 40rem) {
@media (max-width: 30rem) {
flex-wrap: wrap;
[data-slot="cell"]:nth-child(1),
@@ -470,4 +492,13 @@
}
}
}
[data-component="legal"] {
color: var(--color-text-dimmed);
text-align: center;
a {
color: var(--color-text-dimmed);
}
}
}
+25 -17
View File
@@ -1,13 +1,11 @@
import "./index.css"
import { Title } from "@solidjs/meta"
import { Match, onCleanup, onMount, Switch } from "solid-js"
import { onCleanup, onMount } from "solid-js"
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, A } 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"
@@ -55,18 +53,23 @@ 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>
<div data-slot="login">
<a href="/auth">opencode zen</a>
</div>
</section>
<section data-component="cta">
<div data-slot="left">
<a href="/docs">
Get Started
</a>
<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">
@@ -90,7 +93,8 @@ export default function Home() {
<strong>LSP enabled</strong> Automatically loads the right LSPs for the LLM
</li>
<li>
<strong>opencode zen</strong> A <a href="/docs/zen">curated list of models</a> provided by opencode <label>New</label>
<strong>opencode zen</strong> A <a href="/docs/zen">curated list of models</a> provided by opencode{" "}
<label>New</label>
</li>
<li>
<strong>Multi-session</strong> Start multiple agents in parallel on the same project
@@ -113,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>
@@ -122,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>
@@ -131,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>
@@ -140,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>
@@ -157,19 +161,23 @@ export default function Home() {
</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>
)
}
+4 -10
View File
@@ -15,7 +15,7 @@
cursor: pointer;
transition: all 0.15s ease;
&:hover {
&:hover:not(:disabled) {
background-color: var(--color-surface-hover);
border-color: var(--color-accent);
}
@@ -26,13 +26,7 @@
&:disabled {
opacity: 0.5;
cursor: not-allowed;
&:hover {
background-color: var(--color-bg);
border-color: var(--color-border);
transform: none;
}
transform: none;
}
&[data-color="primary"] {
@@ -40,7 +34,7 @@
border-color: var(--color-primary);
color: var(--color-primary-text);
&:hover {
&:hover:not(:disabled) {
background-color: var(--color-primary-hover);
border-color: var(--color-primary-hover);
}
@@ -51,7 +45,7 @@
border-color: transparent;
color: var(--color-text-muted);
&:hover {
&:hover:not(:disabled) {
background-color: var(--color-surface-hover);
border-color: var(--color-border);
color: var(--color-text);
+11 -1
View File
@@ -2,7 +2,17 @@ import "./workspace.css"
import { useAuthSession } from "~/context/auth.session"
import { IconLogo } from "../component/icon"
import { withActor } from "~/context/auth.withActor"
import { query, action, redirect, createAsync, RouteSectionProps, Navigate, useNavigate, useParams, A } 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"
+207 -8
View File
@@ -47,12 +47,6 @@
font-size: var(--font-size-md);
}
}
p {
line-height: 1.4;
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
}
}
section:not(:last-child) {
@@ -121,11 +115,18 @@
[data-component="api-keys-section"] {
[data-slot="create-form"] {
display: flex;
flex-direction: column;
gap: var(--space-3);
padding: var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
[data-slot="input-container"] {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
@media (max-width: 30rem) {
gap: var(--space-2);
}
@@ -154,6 +155,13 @@
display: flex;
gap: var(--space-2);
}
[data-slot="form-error"] {
color: var(--color-danger);
font-size: var(--font-size-sm);
margin-top: var(--space-1);
line-height: 1.4;
}
}
[data-slot="api-keys-table"] {
@@ -192,11 +200,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;
}
}
}
@@ -262,6 +294,9 @@
[data-slot="value"] {
color: var(--color-danger);
}
[data-slot="currency"] {
color: var(--color-danger);
}
}
[data-slot="currency"] {
@@ -428,4 +463,168 @@
}
}
}
[data-slot="new-user-sections"] {
display: flex;
flex-direction: column;
gap: var(--space-8);
padding: var(--space-6);
background-color: var(--color-bg-surface);
border: 1px dashed var(--color-border);
border-radius: var(--border-radius-sm);
@media (max-width: 30rem) {
gap: var(--space-8);
padding: var(--space-4);
}
[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);
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);
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);
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-md);
line-height: 1.5;
color: var(--color-text-secondary);
code {
font-family: var(--font-mono);
font-size: var(--font-size-sm);
padding: var(--space-1) var(--space-2);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-sm);
color: var(--color-text);
}
}
}
}
}
}
+256 -252
View File
@@ -1,18 +1,11 @@
import "./[id].css"
import { Billing } from "@opencode/cloud-core/billing.js"
import { Key } from "@opencode/cloud-core/key.js"
import {
json,
query,
action,
useParams,
useAction,
createAsync,
useSubmission,
} from "@solidjs/router"
import { createMemo, createSignal, For, Show } from "solid-js"
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 { createStore } from "solid-js/store"
function formatDateForTable(date: Date) {
const options: Intl.DateTimeFormatOptions = {
@@ -49,20 +42,31 @@ const listKeys = query(async (workspaceID: string) => {
return withActor(() => Key.list(), workspaceID)
}, "key.list")
const createKey = action(async (workspaceID: string, 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 }), workspaceID),
await withActor(
() =>
Key.create({ name })
.then((data) => ({ error: undefined, data }))
.catch((e) => ({ error: e.message as string })),
workspaceID,
),
{ revalidate: listKeys.key },
)
}, "key.create")
const removeKey = action(async (workspaceID: string, id: string) => {
const removeKey = action(async (form: FormData) => {
"use server"
return json(
withActor(() => Key.remove({ id }), workspaceID),
{ revalidate: listKeys.key },
)
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(await withActor(() => Key.remove({ id }), workspaceID), { revalidate: listKeys.key })
}, "key.remove")
/////////////////////////////////////
@@ -100,127 +104,22 @@ const createCheckoutUrl = action(async (workspaceID: string, successUrl: string,
// return withActor(() => Billing.generatePortalUrl({ returnUrl }), workspaceID)
// }, "portalUrl")
function KeysSection() {
// Dummy data for testing
const dummyKeys = [
{
id: "key_1",
name: "Development API Key",
key: "oc_dev_1234567890abcdef1234567890abcdef12345678",
timeCreated: new Date("2024-01-15T10:30:00Z"),
},
{
id: "key_2",
name: "Production API Key",
key: "oc_prod_abcdef1234567890abcdef1234567890abcdef12",
timeCreated: new Date("2024-02-01T14:22:00Z"),
},
{
id: "key_3",
name: "Testing Environment",
key: "oc_test_9876543210fedcba9876543210fedcba98765432",
timeCreated: new Date("2024-02-10T09:15:00Z"),
},
]
function KeySection() {
const params = useParams()
const keys = createAsync(() => listKeys(params.id))
// const keys = () => dummyKeys
const [showForm, setShowForm] = createSignal(false)
const [name, setName] = createSignal("")
const removeAction = useAction(removeKey)
const createAction = useAction(createKey)
const createSubmission = useSubmission(createKey)
const [copiedId, setCopiedId] = createSignal<string | null>(null)
function formatKey(key: string) {
if (key.length <= 11) return key
return `${key.slice(0, 7)}...${key.slice(-4)}`
}
async function handleCreateKey() {
if (!name().trim()) return
try {
await createAction(params.id, name().trim())
setName("")
setShowForm(false)
} catch (error) {
console.error("Failed to create API key:", error)
}
}
async function copyKeyToClipboard(text: string, keyId: string) {
try {
await navigator.clipboard.writeText(text)
setCopiedId(keyId)
setTimeout(() => setCopiedId(null), 1500)
} catch (error) {
console.error("Failed to copy to clipboard:", error)
}
}
async function handleDeleteKey(keyId: string) {
if (!confirm("Are you sure you want to delete this API key?")) {
return
}
try {
await removeAction(params.id, keyId)
} catch (error) {
console.error("Failed to delete API key:", 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>
<Show
when={!showForm()}
fallback={
<div data-slot="create-form">
<input
data-component="input"
type="text"
placeholder="Enter key name"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
onKeyPress={(e) => e.key === "Enter" && handleCreateKey()}
/>
<div data-slot="form-actions">
<button
data-color="ghost"
onClick={() => {
setShowForm(false)
setName("")
}}
>
Cancel
</button>
<button
data-color="primary"
disabled={createSubmission.pending || !name().trim()}
onClick={handleCreateKey}
>
{createSubmission.pending ? "Creating..." : "Create"}
</button>
</div>
</div>
}
>
<button
data-color="primary"
onClick={() => {
console.log("clicked")
setShowForm(true)
}}
>
Create API Key
</button>
</Show>
<KeyCreateForm />
<div data-slot="api-keys-table">
<Show
when={keys()?.length}
@@ -241,30 +140,42 @@ function KeysSection() {
</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={copiedId() === key.id}
fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}
{(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"
>
<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>
)}
<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>
@@ -274,27 +185,73 @@ function KeysSection() {
)
}
function KeyCreateForm() {
const params = useParams()
const submission = useSubmission(createKey)
const [store, setStore] = createStore({ show: false })
let input: HTMLInputElement
createEffect(() => {
if (!submission.pending && submission.result && !submission.result.error) {
hide()
}
})
function show() {
// submission.clear() does not clear the result in some cases, ie.
// 1. Create key with empty name => error shows
// 2. Put in a key name and creates the key => form hides
// 3. Click add key button again => form shows with the same error if
// submission.clear() is called only once
while (true) {
submission.clear()
if (!submission.result) break
}
setStore("show", true)
input.focus()
}
function hide() {
setStore("show", false)
}
return (
<Show
when={store.show}
fallback={
<button data-color="primary" onClick={() => show()}>
Create API Key
</button>
}
>
<form action={createKey} method="post" data-slot="create-form">
<div data-slot="input-container">
<input ref={(r) => (input = r)} data-component="input" name="name" type="text" placeholder="Enter key name" />
<Show when={submission.result && submission.result.error}>
{(err) => <div data-slot="form-error">{err()}</div>}
</Show>
</div>
<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 dummyBalanceInfo = { balance: 2500000000 } // $25.00 in cents
const balanceInfo = createAsync(() => getBalanceInfo(params.id))
// const balanceInfo = () => dummyBalanceInfo
const createCheckoutUrlAction = useAction(createCheckoutUrl)
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
async function handleBuyCredits() {
try {
const baseUrl = window.location.href
const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
if (checkoutUrl) {
window.location.href = checkoutUrl
}
} catch (error) {
console.error("Failed to get checkout URL:", error)
}
}
return (
<section data-component="balance-section">
<div data-slot="section-title">
@@ -317,7 +274,17 @@ function BalanceSection() {
})()}
</span>
</div>
<button data-color="primary" disabled={createCheckoutUrlSubmission.pending} onClick={handleBuyCredits}>
<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>
@@ -327,43 +294,8 @@ function BalanceSection() {
function UsageSection() {
const params = useParams()
const dummyUsage = [
{
id: "usage_1",
model: "claude-3-sonnet-20240229",
inputTokens: 1250,
outputTokens: 890,
cost: 125000000, // $1.25 in cents
timeCreated: "2024-02-10T15:30:00Z",
},
{
id: "usage_2",
model: "gpt-4-turbo-preview",
inputTokens: 2100,
outputTokens: 1456,
cost: 340000000, // $3.40 in cents
timeCreated: "2024-02-09T09:45:00Z",
},
{
id: "usage_3",
model: "claude-3-haiku-20240307",
inputTokens: 850,
outputTokens: 620,
cost: 45000000, // $0.45 in cents
timeCreated: "2024-02-08T13:22:00Z",
},
{
id: "usage_4",
model: "gpt-3.5-turbo",
inputTokens: 1800,
outputTokens: 1200,
cost: 85000000, // $0.85 in cents
timeCreated: "2024-02-07T11:15:00Z",
},
]
const usage = createAsync(() => getUsageInfo(params.id))
// const usage = () => dummyUsage
return (
<section data-component="usage-section">
<div data-slot="section-title">
@@ -414,76 +346,148 @@ function UsageSection() {
)
}
function PaymentsSection() {
function PaymentSection() {
const params = useParams()
const dummyPayments = [
{
id: "pi_1234567890",
amount: 5000000000, // $50.00 in cents
timeCreated: "2024-02-01T10:00:00Z",
},
{
id: "pi_0987654321",
amount: 2500000000, // $25.00 in cents
timeCreated: "2024-01-15T14:30:00Z",
},
]
const payments = createAsync(() => getPaymentsInfo(params.id))
// const payments = () => dummyPayments
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>
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>
)
)
}
export default function() {
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">
<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">
<ol>
<li>
Run <code>opencode auth login</code> and select opencode
</li>
<li>Paste your API key</li>
<li>Start opencode</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>
Curated list of models provided by opencode. <a target="_blank" href="/docs/zen">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">
<KeysSection />
<NewUserSection />
<KeySection />
<BalanceSection />
<UsageSection />
<PaymentsSection />
<PaymentSection />
</div>
</div>
)
+50 -306
View File
@@ -1,310 +1,54 @@
import { Resource } from "@opencode/cloud-resource"
import type { APIEvent } from "@solidjs/start/server"
import { Database, eq, 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 { handler } from "~/util/zen"
const MODELS = {
// "anthropic/claude-sonnet-4": {
// auth: true,
// api: "https://api.anthropic.com",
// apiKey: Resource.ANTHROPIC_API_KEY.value,
// model: "claude-sonnet-4-20250514",
// cost: {
// input: 0.0000015,
// output: 0.000006,
// reasoning: 0.0000015,
// cacheRead: 0.0000001,
// cacheWrite: 0.0000001,
// },
// headerMappings: {},
// },
"qwen/qwen3-coder": {
id: "qwen/qwen3-coder",
auth: true,
api: "https://inference.baseten.co",
apiKey: Resource.BASETEN_API_KEY.value,
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
cost: {
input: 0.00000038,
output: 0.00000153,
reasoning: 0,
cacheRead: 0,
cacheWrite: 0,
},
headerMappings: {},
},
"grok-code": {
id: "x-ai/grok-code-fast-1",
auth: false,
api: "https://api.x.ai",
apiKey: Resource.XAI_API_KEY.value,
model: "grok-code",
cost: {
input: 0,
output: 0,
reasoning: 0,
cacheRead: 0,
cacheWrite: 0,
},
headerMappings: {
"x-grok-conv-id": "x-opencode-session",
"x-grok-req-id": "x-opencode-request",
},
},
}
const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
]
class AuthError extends Error {}
class CreditsError extends Error {}
class ModelError extends Error {}
export async function POST(input: APIEvent) {
try {
const url = new URL(input.request.url)
const body = await input.request.json()
const MODEL = validateModel()
const apiKey = await authenticate()
const isFree = FREE_WORKSPACES.includes(apiKey?.workspaceID ?? "")
await checkCredits()
// Request to model provider
const res = await fetch(new URL(url.pathname.replace(/^\/zen/, "") + url.search, MODEL.api), {
method: "POST",
headers: (() => {
const headers = input.request.headers
headers.delete("host")
headers.delete("content-length")
headers.set("authorization", `Bearer ${MODEL.apiKey}`)
Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => {
headers.set(k, headers.get(v)!)
})
return headers
})(),
body: JSON.stringify({
...body,
model: MODEL.model,
stream_options: {
include_usage: true,
},
}),
})
// 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 body = await res.json()
await trackUsage(body)
return new Response(JSON.stringify(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 = ""
function pump(): Promise<void> {
return (
reader?.read().then(async ({ done, value }) => {
if (done) {
c.close()
return
}
buffer += decoder.decode(value, { stream: true })
const parts = buffer.split("\n\n")
buffer = parts.pop() ?? ""
const usage = parts
.map((part) => part.trim())
.filter((part) => part.startsWith("data: "))
.map((part) => {
try {
return JSON.parse(part.slice(6))
} catch (e) {
return {}
}
})
.find((part) => part.usage)
if (usage) await trackUsage(usage)
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`)
}
return MODELS[body.model as keyof typeof MODELS]
}
async function authenticate() {
try {
const authHeader = input.request.headers.get("authorization")
if (!authHeader || !authHeader.startsWith("Bearer ")) throw new AuthError("Missing API key.")
const apiKey = authHeader.split(" ")[1]
const key = await Database.use((tx) =>
tx
.select({
id: KeyTable.id,
workspaceID: KeyTable.workspaceID,
})
.from(KeyTable)
.where(eq(KeyTable.key, apiKey))
.then((rows) => rows[0]),
)
if (!key) throw new AuthError("Invalid API key.")
return key
} catch (e) {
console.log(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")
}
async function trackUsage(chunk: any) {
console.log(`trackUsage ${apiKey}`)
if (!apiKey) return
const usage = chunk.usage
const inputTokens = usage.prompt_tokens ?? 0
const outputTokens = usage.completion_tokens ?? 0
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0
const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0
//const cacheWriteTokens = providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? 0
const cacheWriteTokens = 0
const inputCost = MODEL.cost.input * inputTokens
const outputCost = MODEL.cost.output * outputTokens
const reasoningCost = MODEL.cost.reasoning * reasoningTokens
const cacheReadCost = MODEL.cost.cacheRead * cacheReadTokens
const cacheWriteCost = MODEL.cost.cacheWrite * cacheWriteTokens
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
const cost = isFree ? 0 : centsToMicroCents(costInCents)
await Database.transaction(async (tx) => {
await tx.insert(UsageTable).values({
workspaceID: apiKey.workspaceID,
id: Identifier.create("usage"),
model: MODEL.id,
inputTokens,
outputTokens,
reasoningTokens,
cacheReadTokens,
cacheWriteTokens,
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) {
if (error instanceof AuthError) {
return new Response(
JSON.stringify({
error: {
message: error.message,
type: "invalid_request_error",
param: null,
code: "unauthorized",
},
}),
{
status: 401,
},
)
}
if (error instanceof CreditsError) {
return new Response(
JSON.stringify({
error: {
message: error.message,
type: "insufficient_quota",
param: null,
code: "insufficient_quota",
},
}),
{
status: 401,
},
)
}
if (error instanceof ModelError) {
return new Response(JSON.stringify({ error: { message: error.message } }), {
status: 401,
})
}
console.log(error)
return new Response(JSON.stringify({ error: { message: error.message } }), {
status: 500,
})
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 {}
+2 -6
View File
@@ -12,14 +12,10 @@
"allowJs": true,
"strict": true,
"noEmit": true,
"types": [
"vinxi/types/client"
],
"types": ["vinxi/types/client"],
"isolatedModules": true,
"paths": {
"~/*": [
"./src/*"
]
"~/*": ["./src/*"]
}
}
}
@@ -0,0 +1 @@
ALTER TABLE `key` ADD `old_name` varchar(255);
@@ -0,0 +1 @@
ALTER TABLE `key` ADD CONSTRAINT `name` UNIQUE(`workspace_id`,`name`);
@@ -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`;
+11 -37
View File
@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -140,10 +138,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -216,10 +211,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -320,10 +312,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -401,9 +390,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -411,10 +398,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -492,10 +476,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -503,10 +484,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -563,9 +541,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -573,9 +549,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -592,4 +566,4 @@
"tables": {},
"indexes": {}
}
}
}
+11 -37
View File
@@ -48,9 +48,7 @@
"indexes": {
"email": {
"name": "email",
"columns": [
"email"
],
"columns": ["email"],
"isUnique": true
}
},
@@ -140,10 +138,7 @@
"compositePrimaryKeys": {
"billing_workspace_id_id_pk": {
"name": "billing_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -216,10 +211,7 @@
"compositePrimaryKeys": {
"payment_workspace_id_id_pk": {
"name": "payment_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -320,10 +312,7 @@
"compositePrimaryKeys": {
"usage_workspace_id_id_pk": {
"name": "usage_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -401,9 +390,7 @@
"indexes": {
"global_key": {
"name": "global_key",
"columns": [
"key"
],
"columns": ["key"],
"isUnique": true
}
},
@@ -411,10 +398,7 @@
"compositePrimaryKeys": {
"key_workspace_id_id_pk": {
"name": "key_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -492,10 +476,7 @@
"indexes": {
"user_email": {
"name": "user_email",
"columns": [
"workspace_id",
"email"
],
"columns": ["workspace_id", "email"],
"isUnique": true
}
},
@@ -503,10 +484,7 @@
"compositePrimaryKeys": {
"user_workspace_id_id_pk": {
"name": "user_workspace_id_id_pk",
"columns": [
"workspace_id",
"id"
]
"columns": ["workspace_id", "id"]
}
},
"uniqueConstraints": {},
@@ -563,9 +541,7 @@
"indexes": {
"slug": {
"name": "slug",
"columns": [
"slug"
],
"columns": ["slug"],
"isUnique": true
}
},
@@ -573,9 +549,7 @@
"compositePrimaryKeys": {
"workspace_id": {
"name": "workspace_id",
"columns": [
"id"
]
"columns": ["id"]
}
},
"uniqueConstraints": {},
@@ -592,4 +566,4 @@
"tables": {},
"indexes": {}
}
}
}
@@ -0,0 +1,576 @@
{
"version": "5",
"dialect": "mysql",
"id": "9f51ef52-31ac-4ace-8b6d-39b35efe9c81",
"prevId": "79b7ee25-1c1c-41ff-9bbf-754af257102b",
"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
},
"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
}
},
"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,581 @@
{
"version": "5",
"dialect": "mysql",
"id": "26cebd59-f553-441c-a2b2-2f9578a0ad2b",
"prevId": "9f51ef52-31ac-4ace-8b6d-39b35efe9c81",
"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
},
"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": "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": {}
}
}
+43 -1
View File
@@ -15,6 +15,48 @@
"when": 1756871639102,
"tag": "0001_serious_whistler",
"breakpoints": true
},
{
"idx": 2,
"version": "5",
"when": 1757597611832,
"tag": "0002_violet_loners",
"breakpoints": true
},
{
"idx": 3,
"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
}
]
}
}
+3 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://json.schemastore.org/package.json",
"name": "@opencode/cloud-core",
"version": "0.6.5",
"version": "0.7.9",
"private": true,
"type": "module",
"dependencies": {
@@ -18,6 +18,8 @@
},
"scripts": {
"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": {
+26 -6
View File
@@ -1,7 +1,7 @@
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"
@@ -12,7 +12,7 @@ export namespace Key {
tx
.select()
.from(KeyTable)
.where(eq(KeyTable.workspaceID, workspace))
.where(and(eq(KeyTable.workspaceID, workspace), isNull(KeyTable.timeDeleted)))
.orderBy(sql`${KeyTable.timeCreated} DESC`),
)
return keys
@@ -41,15 +41,35 @@ export namespace Key {
key: secretKey,
timeUsed: null,
}),
)
).catch((e: any) => {
if (e.message.match(/Duplicate entry '.*' for key 'key.name'/))
throw new Error("A key with this name already exists. Please choose a different name.")
throw e
})
return keyID
})
export const remove = fn(z.object({ id: z.string() }), async (input) => {
const workspace = Actor.workspace()
await Database.use((tx) =>
tx.delete(KeyTable).where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, 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
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)))
})
})
}
+4 -2
View File
@@ -1,4 +1,4 @@
import { bigint, boolean, int, mysqlTable, varchar } from "drizzle-orm/mysql-core"
import { bigint, boolean, int, mysqlTable, varchar, json } from "drizzle-orm/mysql-core"
import { timestamps, workspaceColumns } from "../drizzle/types"
import { workspaceIndexes } from "./workspace.sql"
@@ -34,11 +34,13 @@ export const UsageTable = mysqlTable(
...workspaceColumns,
...timestamps,
model: varchar("model", { length: 255 }).notNull(),
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"),
cacheWriteTokens: int("cache_write_tokens"),
cacheWrite5mTokens: int("cache_write_5m_tokens"),
cacheWrite1hTokens: int("cache_write_1h_tokens"),
cost: bigint("cost", { mode: "number" }).notNull(),
},
(table) => [...workspaceIndexes(table)],
+6 -1
View File
@@ -10,8 +10,13 @@ export const KeyTable = mysqlTable(
...timestamps,
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),
],
)
+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)
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode/cloud-function",
"version": "0.6.5",
"version": "0.7.9",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+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())
}
},
}
+66 -53
View File
@@ -6,78 +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
}
"BASETEN_API_KEY": {
"type": "sst.sst.Secret"
"value": string
BASETEN_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: 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
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
FIREWORKS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
OPENAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"XAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
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
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)`)
},
+66 -53
View File
@@ -6,78 +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
}
"BASETEN_API_KEY": {
"type": "sst.sst.Secret"
"value": string
BASETEN_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: 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
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
FIREWORKS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
OPENAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"XAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
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
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"]
}
}
+4 -3
View File
@@ -1,12 +1,13 @@
{
"name": "@opencode/cloud-scripts",
"version": "0.6.5",
"version": "0.7.9",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
"scripts": {
"start": "tsx",
"shell": "sst shell"
"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:*",
@@ -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)
})
-1
View File
@@ -1 +0,0 @@
// placeholder
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}
+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 {}
+16 -4
View File
@@ -7,7 +7,7 @@ import { domain } from "./stage"
const cluster = planetscale.getDatabaseOutput({
name: "opencode",
organization: "sst",
organization: "anomalyco",
})
const branch =
@@ -100,8 +100,10 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhookEndpoint", {
})
const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY")
const OPENAI_API_KEY = new sst.Secret("OPENAI_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!) },
@@ -114,6 +116,15 @@ const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
// CONSOLE
////////////////
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",
@@ -123,8 +134,10 @@ new sst.cloudflare.x.SolidStart("Console", {
STRIPE_WEBHOOK_SECRET,
STRIPE_SECRET_KEY,
ANTHROPIC_API_KEY,
OPENAI_API_KEY,
XAI_API_KEY,
BASETEN_API_KEY,
FIREWORKS_API_KEY,
],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),
@@ -135,9 +148,8 @@ new sst.cloudflare.x.SolidStart("Console", {
server: {
transform: {
worker: {
placement: {
mode: "smart",
},
placement: { mode: "smart" },
tailConsumers: logProcessor ? [{ service: logProcessor.nodes.worker.scriptName }] : [],
},
},
},
+3 -2
View File
@@ -3,7 +3,7 @@
"name": "opencode",
"private": true,
"type": "module",
"packageManager": "bun@1.2.19",
"packageManager": "bun@1.2.21",
"scripts": {
"dev": "bun run --conditions=development packages/opencode/src/index.ts",
"typecheck": "bun run --filter='*' typecheck",
@@ -17,6 +17,7 @@
"packages/sdk/js"
],
"catalog": {
"@types/bun": "1.2.21",
"@hono/zod-validator": "0.4.2",
"@types/node": "22.13.9",
"@tsconfig/node22": "22.0.2",
@@ -33,7 +34,7 @@
},
"devDependencies": {
"prettier": "3.5.3",
"sst": "3.17.12"
"sst": "3.17.13"
},
"repository": {
"type": "git",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@opencode/function",
"version": "0.6.5",
"version": "0.7.9",
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"type": "module",
+66 -53
View File
@@ -6,78 +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
}
"BASETEN_API_KEY": {
"type": "sst.sst.Secret"
"value": string
BASETEN_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Console": {
"type": "sst.cloudflare.SolidStart"
"url": string
Console: {
type: "sst.cloudflare.SolidStart"
url: 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
}
"GITHUB_APP_ID": {
"type": "sst.sst.Secret"
"value": string
FIREWORKS_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_APP_PRIVATE_KEY": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_ID: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_ID_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_APP_PRIVATE_KEY: {
type: "sst.sst.Secret"
value: string
}
"GITHUB_CLIENT_SECRET_CONSOLE": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_ID_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"GOOGLE_CLIENT_ID": {
"type": "sst.sst.Secret"
"value": string
GITHUB_CLIENT_SECRET_CONSOLE: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_SECRET_KEY": {
"type": "sst.sst.Secret"
"value": string
GOOGLE_CLIENT_ID: {
type: "sst.sst.Secret"
value: string
}
"STRIPE_WEBHOOK_SECRET": {
"type": "sst.sst.Linkable"
"value": string
HONEYCOMB_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"Web": {
"type": "sst.cloudflare.Astro"
"url": string
OPENAI_API_KEY: {
type: "sst.sst.Secret"
value: string
}
"XAI_API_KEY": {
"type": "sst.sst.Secret"
"value": string
STRIPE_SECRET_KEY: {
type: "sst.sst.Secret"
value: string
}
STRIPE_WEBHOOK_SECRET: {
type: "sst.sst.Linkable"
value: string
}
Web: {
type: "sst.cloudflare.Astro"
url: string
}
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
Api: cloudflare.Service
AuthApi: cloudflare.Service
AuthStorage: cloudflare.KVNamespace
Bucket: cloudflare.R2Bucket
LogProcessor: cloudflare.Service
}
}
import "sst"
export {}
export {}
+4 -2
View File
@@ -52,5 +52,7 @@ echo It seems that your package manager failed to install the right version of t
exit /b 1
:execute
rem Execute the binary with all arguments
"%resolved%" %*
rem Execute the binary with all arguments in the same console window
rem Use start /b /wait to ensure it runs in the current shell context for all shells
start /b /wait "" "%resolved%" %*
exit /b %ERRORLEVEL%
+2 -2
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/package.json",
"version": "0.6.5",
"version": "0.7.9",
"name": "opencode",
"type": "module",
"private": true,
@@ -19,7 +19,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:",
+5
View File
@@ -70,6 +70,11 @@ function findBinary() {
function main() {
try {
if (os.platform() === "win32") {
console.log("Windows detected, skipping postinstall")
return
}
const binaryPath = findBinary()
const binScript = path.join(__dirname, "bin", "opencode")
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env node
import fs from "fs"
import path from "path"
import os from "os"
import { fileURLToPath } from "url"
const __dirname = path.dirname(fileURLToPath(import.meta.url))
function main() {
if (os.platform() !== "win32") {
console.log("Non-Windows platform detected, skipping preinstall")
return
}
const binDir = path.join(__dirname, "bin")
const unixScript = path.join(binDir, "opencode")
console.log("Windows detected: Configuring bin scripts for Windows")
if (fs.existsSync(unixScript)) {
console.log("Removing Unix shell script from bin/")
fs.unlinkSync(unixScript)
}
}
try {
main()
} catch (error) {
console.error("Preinstall script error:", error.message)
process.exit(0)
}
+16 -1
View File
@@ -38,7 +38,20 @@ for (const [os, arch] of targets) {
await $`CGO_ENABLED=0 GOOS=${os} GOARCH=${GOARCH[arch]} go build -ldflags="-s -w -X main.Version=${version}" -o ../opencode/dist/${name}/bin/tui ../tui/cmd/opencode/main.go`.cwd(
"../tui",
)
await $`bun build --define OPENCODE_TUI_PATH="'../../../dist/${name}/bin/tui'" --define OPENCODE_VERSION="'${version}'" --compile --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts`
await Bun.build({
compile: {
target: `bun-${os}-${arch}` as any,
outfile: `dist/${name}/bin/opencode`,
execArgv: [`--user-agent=opencode/${version}`, `--`],
windows: {},
},
entrypoints: ["./src/index.ts"],
define: {
OPENCODE_VERSION: `'${version}'`,
OPENCODE_TUI_PATH: `'../../../dist/${name}/bin/tui'`,
},
})
// await $`bun build --define OPENCODE_TUI_PATH="'../../../dist/${name}/bin/tui'" --define OPENCODE_VERSION="'${version}'" --compile --target=bun-${os}-${arch} --outfile=dist/${name}/bin/opencode ./src/index.ts`
// Run the binary only if it matches current OS/arch
if (
process.platform === (os === "windows" ? "win32" : os) &&
@@ -66,6 +79,7 @@ for (const [os, arch] of targets) {
await $`mkdir -p ./dist/${pkg.name}`
await $`cp -r ./bin ./dist/${pkg.name}/bin`
await $`cp ./script/preinstall.mjs ./dist/${pkg.name}/preinstall.mjs`
await $`cp ./script/postinstall.mjs ./dist/${pkg.name}/postinstall.mjs`
await Bun.file(`./dist/${pkg.name}/package.json`).write(
JSON.stringify(
@@ -75,6 +89,7 @@ await Bun.file(`./dist/${pkg.name}/package.json`).write(
[pkg.name]: `./bin/${pkg.name}`,
},
scripts: {
preinstall: "node ./preinstall.mjs",
postinstall: "node ./postinstall.mjs",
},
version,
+1 -1
View File
@@ -50,7 +50,7 @@ export namespace Agent {
const planPermission = mergeAgentPermissions(
{
edit: "ask",
edit: "deny",
bash: "ask",
webfetch: "allow",
},
+7 -5
View File
@@ -11,6 +11,7 @@ import { MessageV2 } from "../../session/message-v2"
import { Identifier } from "../../id/id"
import { Agent } from "../../agent/agent"
import { Command } from "../../command"
import { SessionPrompt } from "../../session/prompt"
const TOOL: Record<string, [string, string]> = {
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
@@ -71,7 +72,7 @@ export const RunCommand = cmd({
if (message.trim().length === 0 && !args.command) {
UI.error("You must provide a message or a command")
return
process.exit(1)
}
await bootstrap(process.cwd(), async () => {
@@ -79,7 +80,7 @@ export const RunCommand = cmd({
const exists = await Command.get(args.command)
if (!exists) {
UI.error(`Command "${args.command}" not found`)
return
process.exit(1)
}
}
const session = await (async () => {
@@ -104,7 +105,7 @@ export const RunCommand = cmd({
if (!session) {
UI.error("Session not found")
return
process.exit(1)
}
const cfg = await Config.get()
@@ -185,7 +186,7 @@ export const RunCommand = cmd({
})
if (args.command) {
await Session.command({
await SessionPrompt.command({
messageID: Identifier.ascending("message"),
sessionID: session.id,
agent: agent.name,
@@ -197,7 +198,7 @@ export const RunCommand = cmd({
}
const messageID = Identifier.ascending("message")
const result = await Session.prompt({
const result = await SessionPrompt.prompt({
sessionID: session.id,
messageID,
model: {
@@ -221,6 +222,7 @@ export const RunCommand = cmd({
if (errorMsg) process.stdout.write(errorMsg)
}
UI.empty()
if (errorMsg) process.exit(1)
})
},
})
+7 -3
View File
@@ -16,6 +16,7 @@ import { Ide } from "../../ide"
import { Flag } from "../../flag/flag"
import { Session } from "../../session"
import { Instance } from "../../project/instance"
import { $ } from "bun"
declare global {
const OPENCODE_TUI_PATH: string
@@ -111,8 +112,7 @@ export const TuiCommand = cmd({
hostname: args.hostname,
})
let cmd = ["go", "run", "./main.go"]
let cwd = Bun.fileURLToPath(new URL("../../../../tui/cmd/opencode", import.meta.url))
let cmd = [] as string[]
const tui = Bun.embeddedFiles.find((item) => (item as File).name.includes("tui")) as File
if (tui) {
let binaryName = tui.name
@@ -125,9 +125,13 @@ export const TuiCommand = cmd({
await Bun.write(file, tui, { mode: 0o755 })
await fs.chmod(binary, 0o755)
}
cwd = process.cwd()
cmd = [binary]
}
if (!tui) {
const dir = Bun.fileURLToPath(new URL("../../../../tui/cmd/opencode", import.meta.url))
await $`go build -o ./dist/tui ./main.go`.cwd(dir)
cmd = [path.join(dir, "dist/tui")]
}
Log.Default.info("tui", {
cmd,
})
+15 -3
View File
@@ -118,16 +118,27 @@ export namespace Config {
// Load command markdown files
result.command = result.command || {}
const markdownCommands = [
...(await Filesystem.globUp("command/*.md", Global.Path.config, Global.Path.config)),
...(await Filesystem.globUp(".opencode/command/*.md", Instance.directory, Instance.worktree)),
...(await Filesystem.globUp("command/**/*.md", Global.Path.config, Global.Path.config)),
...(await Filesystem.globUp(".opencode/command/**/*.md", Instance.directory, Instance.worktree)),
]
for (const item of markdownCommands) {
const content = await Bun.file(item).text()
const md = matter(content)
if (!md.data) continue
const name = (() => {
const patterns = ["/.opencode/command/", "/command/"]
const pattern = patterns.find((p) => item.includes(p))
if (pattern) {
const index = item.indexOf(pattern)
return item.slice(index + pattern.length, -3)
}
return path.basename(item, ".md")
})()
const config = {
name: path.basename(item, ".md"),
name,
...md.data,
template: md.content.trim(),
}
@@ -488,6 +499,7 @@ export namespace Config {
.optional(),
})
.optional(),
disable_paste_summary: z.boolean().optional(),
})
.optional(),
})
+15 -21
View File
@@ -46,10 +46,7 @@ export namespace Fzf {
log.info("found", { filepath })
return { filepath }
}
filepath = path.join(
Global.Path.bin,
"fzf" + (process.platform === "win32" ? ".exe" : ""),
)
filepath = path.join(Global.Path.bin, "fzf" + (process.platform === "win32" ? ".exe" : ""))
const file = Bun.file(filepath)
if (!(await file.exists())) {
@@ -57,18 +54,15 @@ export namespace Fzf {
const arch = archMap[process.arch as keyof typeof archMap] ?? "amd64"
const config = PLATFORM[process.platform as keyof typeof PLATFORM]
if (!config)
throw new UnsupportedPlatformError({ platform: process.platform })
if (!config) throw new UnsupportedPlatformError({ platform: process.platform })
const version = VERSION
const platformName =
process.platform === "win32" ? "windows" : process.platform
const platformName = process.platform === "win32" ? "windows" : process.platform
const filename = `fzf-${version}-${platformName}_${arch}.${config.extension}`
const url = `https://github.com/junegunn/fzf/releases/download/v${version}/${filename}`
const response = await fetch(url)
if (!response.ok)
throw new DownloadFailedError({ url, status: response.status })
if (!response.ok) throw new DownloadFailedError({ url, status: response.status })
const buffer = await response.arrayBuffer()
const archivePath = path.join(Global.Path.bin, filename)
@@ -87,13 +81,13 @@ export namespace Fzf {
})
}
if (config.extension === "zip") {
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])));
const entries = await zipFileReader.getEntries();
let fzfEntry: any;
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])))
const entries = await zipFileReader.getEntries()
let fzfEntry: any
for (const entry of entries) {
if (entry.filename === "fzf.exe") {
fzfEntry = entry;
break;
fzfEntry = entry
break
}
}
@@ -101,18 +95,18 @@ export namespace Fzf {
throw new ExtractionFailedError({
filepath: archivePath,
stderr: "fzf.exe not found in zip archive",
});
})
}
const fzfBlob = await fzfEntry.getData(new BlobWriter());
const fzfBlob = await fzfEntry.getData(new BlobWriter())
if (!fzfBlob) {
throw new ExtractionFailedError({
filepath: archivePath,
stderr: "Failed to extract fzf.exe from zip archive",
});
})
}
await Bun.write(filepath, await fzfBlob.arrayBuffer());
await zipFileReader.close();
await Bun.write(filepath, await fzfBlob.arrayBuffer())
await zipFileReader.close()
}
await fs.unlink(archivePath)
if (process.platform !== "win32") await fs.chmod(filepath, 0o755)
@@ -127,4 +121,4 @@ export namespace Fzf {
const { filepath } = await state()
return filepath
}
}
}
+9 -11
View File
@@ -3,18 +3,16 @@ import { Log } from "../util/log"
export namespace FileTime {
const log = Log.create({ service: "file.time" })
export const state = Instance.state(
() => {
const read: {
[sessionID: string]: {
[path: string]: Date | undefined
}
} = {}
return {
read,
export const state = Instance.state(() => {
const read: {
[sessionID: string]: {
[path: string]: Date | undefined
}
},
)
} = {}
return {
read,
}
})
export function read(sessionID: string, file: string) {
log.info("read", { sessionID, file })
+1
View File
@@ -7,6 +7,7 @@ export namespace Flag {
export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"]
export const OPENCODE_DISABLE_DEFAULT_PLUGINS = truthy("OPENCODE_DISABLE_DEFAULT_PLUGINS")
export const OPENCODE_DISABLE_LSP_DOWNLOAD = truthy("OPENCODE_DISABLE_LSP_DOWNLOAD")
export const OPENCODE_ENABLE_EXPERIMENTAL_MODELS = truthy("OPENCODE_ENABLE_EXPERIMENTAL_MODELS")
function truthy(key: string) {
const value = process.env[key]?.toLowerCase()
+3 -3
View File
@@ -30,7 +30,7 @@ export namespace Identifier {
function generateID(prefix: keyof typeof prefixes, descending: boolean, given?: string): string {
if (!given) {
return generateNewID(prefix, descending)
return create(prefix, descending)
}
if (!given.startsWith(prefixes[prefix])) {
@@ -49,8 +49,8 @@ export namespace Identifier {
return result
}
function generateNewID(prefix: keyof typeof prefixes, descending: boolean): string {
const currentTimestamp = Date.now()
export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string {
const currentTimestamp = timestamp ?? Date.now()
if (currentTimestamp !== lastTimestamp) {
lastTimestamp = currentTimestamp
+5 -1
View File
@@ -84,7 +84,11 @@ const cli = yargs(hideBin(process.argv))
.command(ExportCommand)
.command(GithubCommand)
.fail((msg) => {
if (msg.startsWith("Unknown argument") || msg.startsWith("Not enough non-option arguments")) {
if (
msg.startsWith("Unknown argument") ||
msg.startsWith("Not enough non-option arguments") ||
msg.startsWith("Invalid values:")
) {
cli.showHelp("log")
}
process.exit(1)
+1
View File
@@ -81,6 +81,7 @@ export const LANGUAGE_EXTENSIONS: Record<string, string> = {
".zsh": "shellscript",
".ksh": "shellscript",
".sql": "sql",
".svelte": "svelte",
".swift": "swift",
".ts": "typescript",
".tsx": "typescriptreact",
+52
View File
@@ -654,4 +654,56 @@ export namespace LSPServer {
}
},
}
export const Svelte: Info = {
id: "svelte",
extensions: [".svelte"],
root: NearestRoot([
"tsconfig.json",
"jsconfig.json",
"package.json",
"pnpm-lock.yaml",
"yarn.lock",
"bun.lockb",
"bun.lock",
"vite.config.ts",
"vite.config.js",
"svelte.config.ts",
"svelte.config.js",
]),
async spawn(root) {
let binary = Bun.which("svelteserver")
const args: string[] = []
if (!binary) {
const js = path.join(Global.Path.bin, "node_modules", "svelte-language-server", "bin", "server.js")
if (!(await Bun.file(js).exists())) {
if (Flag.OPENCODE_DISABLE_LSP_DOWNLOAD) return
await Bun.spawn([BunProc.which(), "install", "svelte-language-server"], {
cwd: Global.Path.bin,
env: {
...process.env,
BUN_BE_BUN: "1",
},
stdout: "pipe",
stderr: "pipe",
stdin: "pipe",
}).exited
}
binary = BunProc.which()
args.push("run", js)
}
args.push("--stdio")
const proc = spawn(binary, args, {
cwd: root,
env: {
...process.env,
BUN_BE_BUN: "1",
},
})
return {
process: proc,
initialization: {},
}
},
}
}
+3 -3
View File
@@ -83,7 +83,7 @@ export namespace Permission {
toolCallID: input.callID,
pattern: input.pattern,
})
if (approved[input.sessionID]?.[input.pattern ?? input.type]) return
if (approved[input.sessionID]?.[input.type]) return
const info: Info = {
id: Identifier.ascending("permission"),
type: input.type,
@@ -141,9 +141,9 @@ export namespace Permission {
})
if (input.response === "always") {
approved[input.sessionID] = approved[input.sessionID] || {}
approved[input.sessionID][match.info.pattern ?? match.info.type] = true
approved[input.sessionID][match.info.type] = true
for (const item of Object.values(pending[input.sessionID])) {
if ((item.info.pattern ?? item.info.type) === (match.info.pattern ?? match.info.type)) {
if (item.info.type === match.info.type) {
respond({ sessionID: item.info.sessionID, permissionID: item.info.id, response: input.response })
}
}
+11
View File
@@ -7,6 +7,7 @@ import { Server } from "../server/server"
import { BunProc } from "../bun"
import { Instance } from "../project/instance"
import { Flag } from "../flag/flag"
import { ToolRegistry } from "../tool/registry"
export namespace Plugin {
const log = Log.create({ service: "plugin" })
@@ -24,6 +25,8 @@ export namespace Plugin {
worktree: Instance.worktree,
directory: Instance.directory,
$: Bun.$,
Tool: await import("../tool/tool").then((m) => m.Tool),
z: await import("zod").then((m) => m.z),
}
const plugins = [...(config.plugin ?? [])]
if (!Flag.OPENCODE_DISABLE_DEFAULT_PLUGINS) {
@@ -75,6 +78,14 @@ export namespace Plugin {
const config = await Config.get()
for (const hook of hooks) {
await hook.config?.(config)
// Let plugins register tools at startup
await hook["tool.register"]?.(
{},
{
registerHTTP: ToolRegistry.registerHTTP,
register: ToolRegistry.register,
},
)
}
Bus.subscribeAll(async (input) => {
const hooks = await state().then((x) => x.hooks)
+2
View File
@@ -28,7 +28,9 @@ export namespace ModelsDev {
context: z.number(),
output: z.number(),
}),
experimental: z.boolean().optional(),
options: z.record(z.any()),
provider: z.object({ npm: z.string() }).optional(),
})
.openapi({
ref: "Model",
+21 -14
View File
@@ -11,6 +11,7 @@ import { NamedError } from "../util/error"
import { Auth } from "../auth"
import { Instance } from "../project/instance"
import { Global } from "../global"
import { Flag } from "../flag/flag"
export namespace Provider {
const log = Log.create({ service: "provider" })
@@ -81,7 +82,8 @@ export namespace Provider {
switch (regionPrefix) {
case "us": {
const modelRequiresPrefix = ["claude", "deepseek"].some((m) => modelID.includes(m))
if (modelRequiresPrefix) {
const isGovCloud = region.startsWith("us-gov")
if (modelRequiresPrefix && !isGovCloud) {
modelID = `${regionPrefix}.${modelID}`
}
break
@@ -159,7 +161,7 @@ export namespace Provider {
string,
{ providerID: string; modelID: string; info: ModelsDev.Model; language: LanguageModel }
>()
const sdk = new Map<string, SDK>()
const sdk = new Map<number, SDK>()
log.info("init")
@@ -233,6 +235,7 @@ export namespace Provider {
context: 0,
output: 0,
},
provider: model.provider ?? existing?.provider,
}
parsed.models[modelID] = parsedModel
}
@@ -287,12 +290,15 @@ export namespace Provider {
}
for (const [providerID, provider] of Object.entries(providers)) {
// Filter out blacklisted models
const filteredModels = Object.fromEntries(
Object.entries(provider.info.models).filter(
([modelID]) =>
modelID !== "gpt-5-chat-latest" && !(providerID === "openrouter" && modelID === "openai/gpt-5-chat"),
),
Object.entries(provider.info.models)
// Filter out blacklisted models
.filter(
([modelID]) =>
modelID !== "gpt-5-chat-latest" && !(providerID === "openrouter" && modelID === "openai/gpt-5-chat"),
)
// Filter out experimental models
.filter(([, model]) => !model.experimental || Flag.OPENCODE_ENABLE_EXPERIMENTAL_MODELS),
)
provider.info.models = filteredModels
@@ -314,29 +320,30 @@ export namespace Provider {
return state().then((state) => state.providers)
}
async function getSDK(provider: ModelsDev.Provider) {
async function getSDK(provider: ModelsDev.Provider, model: ModelsDev.Model) {
return (async () => {
using _ = log.time("getSDK", {
providerID: provider.id,
})
const s = await state()
const existing = s.sdk.get(provider.id)
const pkg = model.provider?.npm ?? provider.npm ?? provider.id
const options = { ...s.providers[provider.id]?.options }
const key = Bun.hash.xxHash32(JSON.stringify({ pkg, options }))
const existing = s.sdk.get(key)
if (existing) return existing
const pkg = provider.npm ?? provider.id
const mod = await import(await BunProc.install(pkg, "latest"))
const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!]
let options = { ...s.providers[provider.id]?.options }
if (options["timeout"] !== undefined) {
// Only override fetch if user explicitly sets timeout
options["fetch"] = async (input: any, init?: any) => {
return await fetch(input, { ...init, timeout: options["timeout"] })
}
}
const fn = mod[Object.keys(mod).find((key) => key.startsWith("create"))!]
const loaded = fn({
name: provider.id,
...options,
})
s.sdk.set(provider.id, loaded)
s.sdk.set(key, loaded)
return loaded as SDK
})().catch((e) => {
throw new InitError({ providerID: provider.id }, { cause: e })
@@ -361,7 +368,7 @@ export namespace Provider {
if (!provider) throw new ModelNotFoundError({ providerID, modelID })
const info = provider.info.models[modelID]
if (!info) throw new ModelNotFoundError({ providerID, modelID })
const sdk = await getSDK(provider.info)
const sdk = await getSDK(provider.info, info)
try {
const language = provider.getModel ? await provider.getModel(sdk, modelID) : sdk.languageModel(modelID)
+133 -19
View File
@@ -23,6 +23,11 @@ import { Auth } from "../auth"
import { Command } from "../command"
import { Global } from "../global"
import { ProjectRoute } from "./project"
import { ToolRegistry } from "../tool/registry"
import { zodToJsonSchema } from "zod-to-json-schema"
import { SessionPrompt } from "../session/prompt"
import { SessionCompaction } from "../session/compaction"
import { SessionRevert } from "../session/revert"
const ERRORS = {
400: {
@@ -46,6 +51,29 @@ const ERRORS = {
export namespace Server {
const log = Log.create({ service: "server" })
// Schemas for HTTP tool registration
const HttpParamSpec = z
.object({
type: z.enum(["string", "number", "boolean", "array"]),
description: z.string().optional(),
optional: z.boolean().optional(),
items: z.enum(["string", "number", "boolean"]).optional(),
})
.openapi({ ref: "HttpParamSpec" })
const HttpToolRegistration = z
.object({
id: z.string(),
description: z.string(),
parameters: z.object({
type: z.literal("object"),
properties: z.record(HttpParamSpec),
}),
callbackUrl: z.string(),
headers: z.record(z.string(), z.string()).optional(),
})
.openapi({ ref: "HttpToolRegistration" })
export const Event = {
Connected: Bus.event("server.connected", z.object({})),
}
@@ -166,6 +194,99 @@ export namespace Server {
return c.json(await Config.get())
},
)
.post(
"/experimental/tool/register",
describeRoute({
description: "Register a new HTTP callback tool",
operationId: "tool.register",
responses: {
200: {
description: "Tool registered successfully",
content: {
"application/json": {
schema: resolver(z.boolean()),
},
},
},
...ERRORS,
},
}),
zValidator("json", HttpToolRegistration),
async (c) => {
ToolRegistry.registerHTTP(c.req.valid("json"))
return c.json(true)
},
)
.get(
"/experimental/tool/ids",
describeRoute({
description: "List all tool IDs (including built-in and dynamically registered)",
operationId: "tool.ids",
responses: {
200: {
description: "Tool IDs",
content: {
"application/json": {
schema: resolver(z.array(z.string()).openapi({ ref: "ToolIDs" })),
},
},
},
...ERRORS,
},
}),
async (c) => {
return c.json(ToolRegistry.ids())
},
)
.get(
"/experimental/tool",
describeRoute({
description: "List tools with JSON schema parameters for a provider/model",
operationId: "tool.list",
responses: {
200: {
description: "Tools",
content: {
"application/json": {
schema: resolver(
z
.array(
z
.object({
id: z.string(),
description: z.string(),
parameters: z.any(),
})
.openapi({ ref: "ToolListItem" }),
)
.openapi({ ref: "ToolList" }),
),
},
},
},
...ERRORS,
},
}),
zValidator(
"query",
z.object({
provider: z.string(),
model: z.string(),
}),
),
async (c) => {
const { provider, model } = c.req.valid("query")
const tools = await ToolRegistry.tools(provider, model)
return c.json(
tools.map((t) => ({
id: t.id,
description: t.description,
// Handle both Zod schemas and plain JSON schemas
parameters: (t.parameters as any)?._def ? zodToJsonSchema(t.parameters as any) : t.parameters,
})),
)
},
)
.get(
"/path",
describeRoute({
@@ -440,7 +561,7 @@ export namespace Server {
}),
),
async (c) => {
return c.json(Session.abort(c.req.valid("param").id))
return c.json(SessionPrompt.abort(c.req.valid("param").id))
},
)
.post(
@@ -533,7 +654,7 @@ export namespace Server {
async (c) => {
const id = c.req.valid("param").id
const body = c.req.valid("json")
await Session.summarize({ ...body, sessionID: id })
await SessionCompaction.run({ ...body, sessionID: id })
return c.json(true)
},
)
@@ -547,14 +668,7 @@ export namespace Server {
description: "List of messages",
content: {
"application/json": {
schema: resolver(
z
.object({
info: MessageV2.Info,
parts: MessageV2.Part.array(),
})
.array(),
),
schema: resolver(MessageV2.WithParts.array()),
},
},
},
@@ -632,11 +746,11 @@ export namespace Server {
id: z.string().openapi({ description: "Session ID" }),
}),
),
zValidator("json", Session.PromptInput.omit({ sessionID: true })),
zValidator("json", SessionPrompt.PromptInput.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const body = c.req.valid("json")
const msg = await Session.prompt({ ...body, sessionID })
const msg = await SessionPrompt.prompt({ ...body, sessionID })
return c.json(msg)
},
)
@@ -667,11 +781,11 @@ export namespace Server {
id: z.string().openapi({ description: "Session ID" }),
}),
),
zValidator("json", Session.CommandInput.omit({ sessionID: true })),
zValidator("json", SessionPrompt.CommandInput.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const body = c.req.valid("json")
const msg = await Session.command({ ...body, sessionID })
const msg = await SessionPrompt.command({ ...body, sessionID })
return c.json(msg)
},
)
@@ -697,11 +811,11 @@ export namespace Server {
id: z.string().openapi({ description: "Session ID" }),
}),
),
zValidator("json", Session.ShellInput.omit({ sessionID: true })),
zValidator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
async (c) => {
const sessionID = c.req.valid("param").id
const body = c.req.valid("json")
const msg = await Session.shell({ ...body, sessionID })
const msg = await SessionPrompt.shell({ ...body, sessionID })
return c.json(msg)
},
)
@@ -727,11 +841,11 @@ export namespace Server {
id: z.string(),
}),
),
zValidator("json", Session.RevertInput.omit({ sessionID: true })),
zValidator("json", SessionRevert.RevertInput.omit({ sessionID: true })),
async (c) => {
const id = c.req.valid("param").id
log.info("revert", c.req.valid("json"))
const session = await Session.revert({ sessionID: id, ...c.req.valid("json") })
const session = await SessionRevert.revert({ sessionID: id, ...c.req.valid("json") })
return c.json(session)
},
)
@@ -759,7 +873,7 @@ export namespace Server {
),
async (c) => {
const id = c.req.valid("param").id
const session = await Session.unrevert({ sessionID: id })
const session = await SessionRevert.unrevert({ sessionID: id })
return c.json(session)
},
)
+120
View File
@@ -0,0 +1,120 @@
import { generateText, type ModelMessage } from "ai"
import { Session } from "."
import { Identifier } from "../id/id"
import { Instance } from "../project/instance"
import { Provider } from "../provider/provider"
import { defer } from "../util/defer"
import { MessageV2 } from "./message-v2"
import { SystemPrompt } from "./system"
import { Bus } from "../bus"
import z from "zod"
import type { ModelsDev } from "../provider/models"
import { SessionPrompt } from "./prompt"
export namespace SessionCompaction {
export const Event = {
Compacted: Bus.event(
"session.compacted",
z.object({
sessionID: z.string(),
}),
),
}
export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) {
const count = input.tokens.input + input.tokens.cache.read + input.tokens.output
const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX
const usable = input.model.limit.context - output
return count > usable / 2
}
export async function run(input: { sessionID: string; providerID: string; modelID: string }) {
await Session.update(input.sessionID, (draft) => {
draft.time.compacting = Date.now()
})
await using _ = defer(async () => {
await Session.update(input.sessionID, (draft) => {
draft.time.compacting = undefined
})
})
const toSummarize = await Session.messages(input.sessionID).then(MessageV2.filterSummarized)
const model = await Provider.getModel(input.providerID, input.modelID)
const system = [
...SystemPrompt.summarize(model.providerID),
...(await SystemPrompt.environment()),
...(await SystemPrompt.custom()),
]
const msg = (await Session.updateMessage({
id: Identifier.ascending("message"),
role: "assistant",
sessionID: input.sessionID,
system,
mode: "build",
path: {
cwd: Instance.directory,
root: Instance.worktree,
},
cost: 0,
tokens: {
output: 0,
input: 0,
reasoning: 0,
cache: { read: 0, write: 0 },
},
modelID: input.modelID,
providerID: model.providerID,
time: {
created: Date.now(),
},
})) as MessageV2.Assistant
const generated = await generateText({
maxRetries: 10,
model: model.language,
messages: [
...system.map(
(x): ModelMessage => ({
role: "system",
content: x,
}),
),
...MessageV2.toModelMessage(toSummarize),
{
role: "user",
content: [
{
type: "text",
text: "Provide a detailed but concise summary of our conversation above. Focus on information that would be helpful for continuing the conversation, including what we did, what we're doing, which files we're working on, and what we're going to do next.",
},
],
},
],
})
const usage = Session.getUsage(model.info, generated.usage, generated.providerMetadata)
msg.cost += usage.cost
msg.tokens = usage.tokens
msg.summary = true
msg.time.completed = Date.now()
await Session.updateMessage(msg)
const part = await Session.updatePart({
type: "text",
sessionID: input.sessionID,
messageID: msg.id,
id: Identifier.ascending("part"),
text: generated.text,
time: {
start: Date.now(),
end: Date.now(),
},
})
Bus.publish(Event.Compacted, {
sessionID: input.sessionID,
})
return {
info: msg,
parts: [part],
}
}
}
File diff suppressed because it is too large Load Diff
+16 -3
View File
@@ -52,6 +52,7 @@ export namespace MessageV2 {
time: z.object({
start: z.number(),
end: z.number(),
compacted: z.number().optional(),
}),
})
.openapi({
@@ -330,6 +331,12 @@ export namespace MessageV2 {
),
}
export const WithParts = z.object({
info: Info,
parts: z.array(Part),
})
export type WithParts = z.infer<typeof WithParts>
export function fromV1(v1: Message.Info) {
if (v1.role === "assistant") {
const info: Assistant = {
@@ -487,8 +494,8 @@ export namespace MessageV2 {
text: part.text,
},
]
// text/plain files are converted into text parts, ignore them
if (part.type === "file" && part.mime !== "text/plain")
// text/plain and directory files are converted into text parts, ignore them
if (part.type === "file" && part.mime !== "text/plain" && part.mime !== "application/x-directory")
return [
{
type: "file",
@@ -528,7 +535,7 @@ export namespace MessageV2 {
state: "output-available",
toolCallId: part.callID,
input: part.state.input,
output: part.state.output,
output: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output,
},
]
if (part.state.status === "error")
@@ -551,4 +558,10 @@ export namespace MessageV2 {
return convertToModelMessages(result)
}
export function filterSummarized(msgs: { info: MessageV2.Info; parts: MessageV2.Part[] }[]) {
const i = msgs.findLastIndex((m) => m.info.role === "assistant" && !!m.info.summary)
if (i === -1) return msgs.slice()
return msgs.slice(i)
}
}
File diff suppressed because it is too large Load Diff
@@ -1 +1,5 @@
Your operational mode has changed from plan to build. You are no longer in read-only mode. You are permitted to make file changes as necessary and utilize your arsenal of tools as needed.
<system-reminder>
Your operational mode has changed from plan to build.
You are no longer in read-only mode.
You are permitted to make file changes, run shell commands, and utilize your arsenal of tools as needed.
</system-reminder>
+105
View File
@@ -0,0 +1,105 @@
import z from "zod"
import { Identifier } from "../id/id"
import { Snapshot } from "../snapshot"
import { MessageV2 } from "./message-v2"
import { Session } from "."
import { Log } from "../util/log"
import { splitWhen } from "remeda"
import { Storage } from "../storage/storage"
import { Bus } from "../bus"
export namespace SessionRevert {
const log = Log.create({ service: "session.revert" })
export const RevertInput = z.object({
sessionID: Identifier.schema("session"),
messageID: Identifier.schema("message"),
partID: Identifier.schema("part").optional(),
})
export type RevertInput = z.infer<typeof RevertInput>
export async function revert(input: RevertInput) {
const all = await Session.messages(input.sessionID)
let lastUser: MessageV2.User | undefined
const session = await Session.get(input.sessionID)
let revert: Session.Info["revert"]
const patches: Snapshot.Patch[] = []
for (const msg of all) {
if (msg.info.role === "user") lastUser = msg.info
const remaining = []
for (const part of msg.parts) {
if (revert) {
if (part.type === "patch") {
patches.push(part)
}
continue
}
if (!revert) {
if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) {
// if no useful parts left in message, same as reverting whole message
const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined
revert = {
messageID: !partID && lastUser ? lastUser.id : msg.info.id,
partID,
}
}
remaining.push(part)
}
}
}
if (revert) {
const session = await Session.get(input.sessionID)
revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track())
await Snapshot.revert(patches)
if (revert.snapshot) revert.diff = await Snapshot.diff(revert.snapshot)
return Session.update(input.sessionID, (draft) => {
draft.revert = revert
})
}
return session
}
export async function unrevert(input: { sessionID: string }) {
log.info("unreverting", input)
const session = await Session.get(input.sessionID)
if (!session.revert) return session
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
const next = await Session.update(input.sessionID, (draft) => {
draft.revert = undefined
})
return next
}
export async function cleanup(session: Session.Info) {
if (!session.revert) return
const sessionID = session.id
let msgs = await Session.messages(sessionID)
const messageID = session.revert.messageID
const [preserve, remove] = splitWhen(msgs, (x) => x.info.id === messageID)
msgs = preserve
for (const msg of remove) {
await Storage.remove(["message", sessionID, msg.info.id])
await Bus.publish(MessageV2.Event.Removed, { sessionID: sessionID, messageID: msg.info.id })
}
const last = preserve.at(-1)
if (session.revert.partID && last) {
const partID = session.revert.partID
const [preserveParts, removeParts] = splitWhen(last.parts, (x) => x.id === partID)
last.parts = preserveParts
for (const part of removeParts) {
await Storage.remove(["part", last.info.id, part.id])
await Bus.publish(MessageV2.Event.PartRemoved, {
sessionID: sessionID,
messageID: last.info.id,
partID: part.id,
})
}
}
await Session.update(sessionID, (draft) => {
draft.revert = undefined
})
}
}
+33 -4
View File
@@ -55,7 +55,15 @@ export namespace Snapshot {
export async function patch(hash: string): Promise<Patch> {
const git = gitdir()
await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow()
const files = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.cwd(Instance.directory).text()
const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.quiet().cwd(Instance.directory).nothrow()
// If git diff fails, return empty patch
if (result.exitCode !== 0) {
log.warn("failed to get diff", { hash, exitCode: result.exitCode })
return { hash, files: [] }
}
const files = result.text()
return {
hash,
files: files
@@ -70,9 +78,19 @@ export namespace Snapshot {
export async function restore(snapshot: string) {
log.info("restore", { commit: snapshot })
const git = gitdir()
await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f`
const result = await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f`
.quiet()
.cwd(Instance.worktree)
.nothrow()
if (result.exitCode !== 0) {
log.error("failed to restore snapshot", {
snapshot,
exitCode: result.exitCode,
stderr: result.stderr.toString(),
stdout: result.stdout.toString(),
})
}
}
export async function revert(patches: Patch[]) {
@@ -97,8 +115,19 @@ export namespace Snapshot {
export async function diff(hash: string) {
const git = gitdir()
const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).text()
return result.trim()
const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).nothrow()
if (result.exitCode !== 0) {
log.warn("failed to get diff", {
hash,
exitCode: result.exitCode,
stderr: result.stderr.toString(),
stdout: result.stdout.toString(),
})
return ""
}
return result.text().trim()
}
function gitdir() {
+8 -8
View File
@@ -107,14 +107,12 @@ export const EditTool = Tool.define("edit", {
for (const [file, issues] of Object.entries(diagnostics)) {
if (issues.length === 0) continue
if (file === filePath) {
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues.map(LSP.Diagnostic.pretty).join("\n")}\n</file_diagnostics>\n`
output += `\nThis file has errors, please fix\n<file_diagnostics>\n${issues
.filter((item) => item.severity === 1)
.map(LSP.Diagnostic.pretty)
.join("\n")}\n</file_diagnostics>\n`
continue
}
output += `\n<project_diagnostics>\n${file}\n${issues
// TODO: may want to make more leniant for eslint
.filter((item) => item.severity === 1)
.map(LSP.Diagnostic.pretty)
.join("\n")}\n</project_diagnostics>\n`
}
return {
@@ -599,7 +597,7 @@ export function replace(content: string, oldString: string, newString: string, r
for (const replacer of [
SimpleReplacer,
LineTrimmedReplacer,
BlockAnchorReplacer,
// BlockAnchorReplacer,
WhitespaceNormalizedReplacer,
IndentationFlexibleReplacer,
EscapeNormalizedReplacer,
@@ -623,5 +621,7 @@ export function replace(content: string, oldString: string, newString: string, r
if (notFound) {
throw new Error("oldString not found in content")
}
throw new Error("oldString found multiple times and requires more code context to uniquely identify the intended match")
throw new Error(
"oldString found multiple times and requires more code context to uniquely identify the intended match",
)
}
+97 -12
View File
@@ -12,9 +12,11 @@ import { WebFetchTool } from "./webfetch"
import { WriteTool } from "./write"
import { InvalidTool } from "./invalid"
import type { Agent } from "../agent/agent"
import { Tool } from "./tool"
export namespace ToolRegistry {
const ALL = [
// Built-in tools that ship with opencode
const BUILTIN = [
InvalidTool,
BashTool,
EditTool,
@@ -30,13 +32,101 @@ export namespace ToolRegistry {
TaskTool,
]
// Extra tools registered at runtime (via plugins)
const EXTRA: Tool.Info[] = []
// Tools registered via HTTP callback (via SDK/API)
const HTTP: Tool.Info[] = []
export type HttpParamSpec = {
type: "string" | "number" | "boolean" | "array"
description?: string
optional?: boolean
items?: "string" | "number" | "boolean"
}
export type HttpToolRegistration = {
id: string
description: string
parameters: {
type: "object"
properties: Record<string, HttpParamSpec>
}
callbackUrl: string
headers?: Record<string, string>
}
function buildZodFromHttpSpec(spec: HttpToolRegistration["parameters"]) {
const shape: Record<string, z.ZodTypeAny> = {}
for (const [key, val] of Object.entries(spec.properties)) {
let base: z.ZodTypeAny
switch (val.type) {
case "string":
base = z.string()
break
case "number":
base = z.number()
break
case "boolean":
base = z.boolean()
break
case "array":
if (!val.items) throw new Error(`array spec for ${key} requires 'items'`)
base = z.array(val.items === "string" ? z.string() : val.items === "number" ? z.number() : z.boolean())
break
default:
base = z.any()
}
if (val.description) base = base.describe(val.description)
shape[key] = val.optional ? base.optional() : base
}
return z.object(shape)
}
export function register(tool: Tool.Info) {
// Prevent duplicates by id (replace existing)
const idx = EXTRA.findIndex((t) => t.id === tool.id)
if (idx >= 0) EXTRA.splice(idx, 1, tool)
else EXTRA.push(tool)
}
export function registerHTTP(input: HttpToolRegistration) {
const parameters = buildZodFromHttpSpec(input.parameters)
const info = Tool.define(input.id, {
description: input.description,
parameters,
async execute(args) {
const res = await fetch(input.callbackUrl, {
method: "POST",
headers: { "content-type": "application/json", ...(input.headers ?? {}) },
body: JSON.stringify({ args }),
})
if (!res.ok) {
throw new Error(`HTTP tool callback failed: ${res.status} ${await res.text()}`)
}
const json = (await res.json()) as { title?: string; output: string; metadata?: Record<string, any> }
return {
title: json.title ?? input.id,
output: json.output ?? "",
metadata: (json.metadata ?? {}) as any,
}
},
})
const idx = HTTP.findIndex((t) => t.id === info.id)
if (idx >= 0) HTTP.splice(idx, 1, info)
else HTTP.push(info)
}
function allTools(): Tool.Info[] {
return [...BUILTIN, ...EXTRA, ...HTTP]
}
export function ids() {
return ALL.map((t) => t.id)
return allTools().map((t) => t.id)
}
export async function tools(providerID: string, _modelID: string) {
const result = await Promise.all(
ALL.map(async (t) => ({
allTools().map(async (t) => ({
id: t.id,
...(await t.init()),
})),
@@ -45,21 +135,21 @@ export namespace ToolRegistry {
if (providerID === "openai") {
return result.map((t) => ({
...t,
parameters: optionalToNullable(t.parameters),
parameters: optionalToNullable(t.parameters as unknown as z.ZodTypeAny),
}))
}
if (providerID === "azure") {
return result.map((t) => ({
...t,
parameters: optionalToNullable(t.parameters),
parameters: optionalToNullable(t.parameters as unknown as z.ZodTypeAny),
}))
}
if (providerID === "google") {
return result.map((t) => ({
...t,
parameters: sanitizeGeminiParameters(t.parameters),
parameters: sanitizeGeminiParameters(t.parameters as unknown as z.ZodTypeAny),
}))
}
@@ -68,7 +158,7 @@ export namespace ToolRegistry {
export async function enabled(
_providerID: string,
modelID: string,
_modelID: string,
agent: Agent.Info,
): Promise<Record<string, boolean>> {
const result: Record<string, boolean> = {}
@@ -86,11 +176,6 @@ export namespace ToolRegistry {
result["webfetch"] = false
}
if (modelID.toLowerCase().includes("qwen")) {
result["todowrite"] = false
result["todoread"] = false
}
return result
}
+3 -2
View File
@@ -6,6 +6,7 @@ import { Bus } from "../bus"
import { MessageV2 } from "../session/message-v2"
import { Identifier } from "../id/id"
import { Agent } from "../agent/agent"
import { SessionPrompt } from "../session/prompt"
export const TaskTool = Tool.define("task", async () => {
const agents = await Agent.list().then((x) => x.filter((a) => a.mode !== "primary"))
@@ -49,9 +50,9 @@ export const TaskTool = Tool.define("task", async () => {
}
ctx.abort.addEventListener("abort", () => {
Session.abort(session.id)
SessionPrompt.abort(session.id)
})
const result = await Session.prompt({
const result = await SessionPrompt.prompt({
messageID,
sessionID: session.id,
model: {
+6 -8
View File
@@ -11,14 +11,12 @@ const TodoInfo = z.object({
})
type TodoInfo = z.infer<typeof TodoInfo>
const state = Instance.state(
() => {
const todos: {
[sessionId: string]: TodoInfo[]
} = {}
return todos
},
)
const state = Instance.state(() => {
const todos: {
[sessionId: string]: TodoInfo[]
} = {}
return todos
})
export const TodoWriteTool = Tool.define("todowrite", {
description: DESCRIPTION_WRITE,
+7
View File
@@ -0,0 +1,7 @@
export namespace Token {
const CHARS_PER_TOKEN = 4
export function estimate(input: string) {
return Math.max(0, Math.round((input || "").length / CHARS_PER_TOKEN))
}
}
+1 -1
View File
@@ -6,4 +6,4 @@
/// <reference path="../../sst-env.d.ts" />
import "sst"
export {}
export {}

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