mirror of
https://github.com/anomalyco/opencode.git
synced 2026-08-10 03:19:59 -04:00
Compare commits
403 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 930a1bf358 | |||
| 6cf7f18cc9 | |||
| 3f59570ee6 | |||
| 304e956b5d | |||
| 538eaa42aa | |||
| 67c41fd389 | |||
| 83ea19770a | |||
| 3ace8543b2 | |||
| eb855e1e31 | |||
| 5e53f054c6 | |||
| 5d5e184329 | |||
| 2fbb49ac30 | |||
| c56b407e1d | |||
| bdaa0e8b8c | |||
| 4e549b1c05 | |||
| 7be8e16c33 | |||
| d1588f93a1 | |||
| 576696a370 | |||
| 2c6f9043e8 | |||
| 9f771ef0ae | |||
| 356715f67d | |||
| 540421267a | |||
| e253398936 | |||
| ee87e1f139 | |||
| 8887616457 | |||
| 905c034885 | |||
| 92f7c4943f | |||
| 10bde356b1 | |||
| f7cc46cd9f | |||
| d9ffe07391 | |||
| c0702ed8bd | |||
| b927b9dca6 | |||
| 4b7231be68 | |||
| 70a6fe96ea | |||
| 6e5971dff2 | |||
| d48d6b3577 | |||
| 4b1668c3ef | |||
| d85eb1b880 | |||
| 9637d70407 | |||
| cfbbdc2e14 | |||
| feb65201f6 | |||
| f1f07a56d8 | |||
| 0fe313bd87 | |||
| 1fd676528d | |||
| 0a2801444b | |||
| c9adbc7c21 | |||
| ba8de38435 | |||
| 8166612467 | |||
| 4d20e1c3c6 | |||
| 4bb7ea9127 | |||
| 969af4d541 | |||
| 271b679058 | |||
| 83b16cb18e | |||
| 431ffc94f5 | |||
| 47b4cc6d53 | |||
| ce9b758d0a | |||
| f8a1a0b26f | |||
| 6ecaf83f76 | |||
| 30b1ae5d4b | |||
| 9cd465f9c0 | |||
| 1747979568 | |||
| 062023fa06 | |||
| 954a796b8a | |||
| 34ff87d504 | |||
| 16357e8041 | |||
| dabb1aa719 | |||
| 7af3380455 | |||
| dcaa90808e | |||
| 01705fd467 | |||
| 006f3bdeb6 | |||
| 1d43b4e6d7 | |||
| 8cef7940fe | |||
| b2dd9fdfdf | |||
| b82a52cb85 | |||
| 7294d86778 | |||
| 3bb3f4f2c9 | |||
| d31f97343c | |||
| 536d10e5ab | |||
| 9885c716f3 | |||
| 39461fbbce | |||
| 1a2b3701f2 | |||
| 0a395d8783 | |||
| 79bb22a573 | |||
| 4271df96d2 | |||
| aa07be09e1 | |||
| 5d6bdca6d0 | |||
| 58bbe9e689 | |||
| b5a035ceab | |||
| b3c6d0b08a | |||
| 090d27df11 | |||
| b374a6cac9 | |||
| 73cd8a334c | |||
| b46c3f2a26 | |||
| 45fabec091 | |||
| a96365fd81 | |||
| 5f7e1e099b | |||
| d462e380f4 | |||
| c5a558f3da | |||
| 7f51b181d4 | |||
| 7adbc3ad44 | |||
| 89922a8598 | |||
| 3a1d1a6284 | |||
| 4463d319c9 | |||
| c6eea0343d | |||
| e317e7e481 | |||
| 287855336d | |||
| d55f4f3322 | |||
| b708d0ecec | |||
| afb831c93c | |||
| 14397651b5 | |||
| e5804f64f9 | |||
| ce7b73170f | |||
| 9554abb56e | |||
| d0f5c825bd | |||
| 9f603e39a6 | |||
| da51c9dfac | |||
| 9e04ff013c | |||
| 6bfccace0c | |||
| b25d4f9dfb | |||
| d1962ca5a7 | |||
| 25f31f3096 | |||
| 11a6f0886e | |||
| 3ba7e243d0 | |||
| a2ab019317 | |||
| 21957406ff | |||
| 61c4747fbe | |||
| 957c43aa09 | |||
| 96c57418f3 | |||
| b8c51e307f | |||
| 6791233ca0 | |||
| cd6072ec58 | |||
| 017e42bbcd | |||
| 2d20582802 | |||
| 2bcc00dbf0 | |||
| e45e94634f | |||
| de1278414f | |||
| 3c2803fd9a | |||
| 90c2b26733 | |||
| 1ea3a8eb9b | |||
| 8729edc5e0 | |||
| d8bcf1f5f3 | |||
| 67f3c934fe | |||
| 065f656fb0 | |||
| f636d937c4 | |||
| 492bf51a0d | |||
| 81ab127f63 | |||
| 6ba7c54bab | |||
| 146bae82cb | |||
| ab345cf0da | |||
| a1836527ce | |||
| 49e4cfb286 | |||
| e52bfab79d | |||
| cc6d5c8ddd | |||
| afe8508949 | |||
| 7c098c8849 | |||
| 11d6005b77 | |||
| 2cc072b3dc | |||
| 86247b8ea9 | |||
| 0a5a02043c | |||
| 6e553f7e20 | |||
| bb6acc0ec6 | |||
| 5a84b9f467 | |||
| c7031dfd77 | |||
| e136a40771 | |||
| ef25650ced | |||
| 6555a33eff | |||
| 247ce44776 | |||
| 4e7bfaab8b | |||
| 8b26a1f9bd | |||
| 2a9b6a85de | |||
| c9ae89a38b | |||
| e316050bf5 | |||
| 306f45f04a | |||
| e006e3355c | |||
| d7e31f76c4 | |||
| d425723249 | |||
| c59ec71918 | |||
| 05ae99a09b | |||
| 6e22b45905 | |||
| c664f92829 | |||
| f95333aaa4 | |||
| ef0b5e3dcb | |||
| b7262b8527 | |||
| 1f44c7f750 | |||
| 7dba570195 | |||
| 3d04ba26a3 | |||
| 3660e2c481 | |||
| 06ca45189b | |||
| 674febcf60 | |||
| 582d9a9622 | |||
| d525fbf829 | |||
| 69a499f807 | |||
| 37e564139f | |||
| ee8b81269b | |||
| 53998a2fed | |||
| af7b9e77d1 | |||
| 77c65b18b5 | |||
| c9dfe6d964 | |||
| 03f7f18260 | |||
| 2db76fc6dd | |||
| 7269c2316d | |||
| 1e0596bc46 | |||
| 3ebec2435a | |||
| b90c0b5fac | |||
| 3b1ab444fd | |||
| 234db24f1f | |||
| 04546c0873 | |||
| f51bd91af4 | |||
| ebca25462e | |||
| 01b9148c04 | |||
| d3e080894c | |||
| ee9aa24a55 | |||
| 16e2bded5b | |||
| 9fb49ab87b | |||
| 8d6a03cc89 | |||
| 71b04ffa99 | |||
| 678ca757c9 | |||
| 272349b8da | |||
| fe94bb8e50 | |||
| ba8bc1b8b4 | |||
| 8a9a474df6 | |||
| 52e2b40610 | |||
| ee1ff8cc07 | |||
| 434c0ff0d7 | |||
| 7a7060ef15 | |||
| f9af9fc221 | |||
| 1bf1b93404 | |||
| bc6f4aed2b | |||
| 2af3f19397 | |||
| 9275665868 | |||
| 09bb819064 | |||
| 6f0028644e | |||
| aec44abcf6 | |||
| b41e573886 | |||
| 737ddab300 | |||
| 90fc3ddb02 | |||
| 15d7eebb92 | |||
| 33301c94df | |||
| d341d26e37 | |||
| d49b1b25d1 | |||
| 25eb100210 | |||
| 9886353715 | |||
| f501501791 | |||
| c103052f93 | |||
| 68039d4c71 | |||
| d3566d3b1a | |||
| b275e18d28 | |||
| af9a1797b5 | |||
| 29b3e40ddb | |||
| c49f5939a2 | |||
| 63862b1609 | |||
| 1cf1e88b52 | |||
| d06afd87e5 | |||
| 9fb6e81007 | |||
| 3ac82227f1 | |||
| c1f9249c84 | |||
| 9bb66946db | |||
| adcdbbddc7 | |||
| 662435c5bb | |||
| 36c1a05eaa | |||
| 5708e3bf1e | |||
| 0da1ed3fc8 | |||
| d5179c8b63 | |||
| bd0a4f7bbe | |||
| 3d43214075 | |||
| 178a14ce3e | |||
| 8e1010dc3f | |||
| 9c82f1f5e9 | |||
| e5a651eef7 | |||
| d26605aa56 | |||
| 5cc0d337b1 | |||
| 902763b47d | |||
| 55d07a139c | |||
| 05232ead93 | |||
| 7652a96064 | |||
| 901aae09f7 | |||
| f95799f17c | |||
| 99a6c5e44d | |||
| 07bb75f086 | |||
| 66eb846e6f | |||
| 34f11c699e | |||
| 7a32fec008 | |||
| 37a6b5177e | |||
| 573ffe186b | |||
| 0f7ff3fcb1 | |||
| 2c3aa330b9 | |||
| 47b2fb79dc | |||
| 6deaf54bb3 | |||
| d549cd3213 | |||
| 93e52f7ecf | |||
| 88f12b0822 | |||
| 54af7f9e18 | |||
| be685e95a3 | |||
| dc2ab75fca | |||
| f1324e886f | |||
| c47fde2ca4 | |||
| f42e1c6375 | |||
| f68374ad22 | |||
| 5e86c9b791 | |||
| 94658c31c5 | |||
| 9fd672a1cb | |||
| 10523c4372 | |||
| d1cd7d0344 | |||
| 06ac1be226 | |||
| 05489bc843 | |||
| 3f02eecf22 | |||
| f5ca78ed7b | |||
| 894cbaa51e | |||
| 8b70b89fde | |||
| f9dbc586dc | |||
| ffeef63ca1 | |||
| 4da58294d9 | |||
| fa2e88f49b | |||
| 28e765ef0a | |||
| bfbcb5f200 | |||
| 89492b3002 | |||
| 2663415d47 | |||
| 51be67cc14 | |||
| 92a1943771 | |||
| 1e15fc273a | |||
| 104a895a71 | |||
| f98e730405 | |||
| b12bef05d3 | |||
| 2f1d001cc5 | |||
| 65d0b3ed6d | |||
| 22a34d7958 | |||
| cb4401ec92 | |||
| febf467b03 | |||
| d55a2fd56c | |||
| 40f577e5e7 | |||
| 9e49870118 | |||
| fe38e3ab02 | |||
| 0170577743 | |||
| 7de6ea5922 | |||
| 2fe7d13e69 | |||
| 1bc3c98ae7 | |||
| 55787f2caa | |||
| 7df61a74a0 | |||
| 4f23110880 | |||
| 041353f4ff | |||
| c72f8b17c6 | |||
| eb304f4115 | |||
| 5565f14ef5 | |||
| 10a4455c6f | |||
| 5ded6d6ad7 | |||
| 849a38c30c | |||
| 68050ab802 | |||
| 91d01fd4cc | |||
| 9beb0f8512 | |||
| d4cb47eadc | |||
| 261ff416a9 | |||
| d0a70cb217 | |||
| 20fc56d020 | |||
| a57ae3ec93 | |||
| 30f9fa12d9 | |||
| d473d4ffc8 | |||
| af50596529 | |||
| 3823d8d50e | |||
| 7a926b32ce | |||
| a5ede68241 | |||
| 60dc38050d | |||
| 31d0caee38 | |||
| 2a7ab45605 | |||
| 019054dd1e | |||
| a018a15f32 | |||
| e630d680dd | |||
| 9e392f25a6 | |||
| 2cc4e6ad7c | |||
| 70d8d1ab1e | |||
| 342aa27e03 | |||
| e1aed0cd01 | |||
| c8ea2c5ce0 | |||
| 5e8309a353 | |||
| aae0ce9921 | |||
| 81b94d84dc | |||
| ceab70f8d9 | |||
| afe8cecc2b | |||
| 4a292bf977 | |||
| e249b41513 | |||
| 9021dd60a1 | |||
| b9a39b816c | |||
| 1eeba770b1 | |||
| 6cff306be1 | |||
| 96bdeb3c7b | |||
| 81c617770d | |||
| 021334509e | |||
| 4bde3f7b15 | |||
| 4355027408 | |||
| b022cf0ed6 | |||
| a529b0324d | |||
| 16f5e16395 | |||
| 76e080b2cb | |||
| ffc889b99e | |||
| 36b48a44ac | |||
| 5379abe330 | |||
| a5bcb76bbf | |||
| b628c580c2 | |||
| 46d675b980 | |||
| a8bf1ad40f | |||
| 0ac943de90 | |||
| 485135cf5c | |||
| 543eee78a6 | |||
| dafb63cfb3 |
@@ -5,6 +5,8 @@ runs:
|
||||
steps:
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version-file: package.json
|
||||
|
||||
- name: Cache ~/.bun
|
||||
id: cache-bun
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Auto-label TUI Issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
auto-label:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Auto-label and assign issues
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const issue = context.payload.issue;
|
||||
const title = issue.title;
|
||||
const description = issue.body || '';
|
||||
|
||||
// Check for "opencode web" keyword
|
||||
const webPattern = /(opencode web)/i;
|
||||
const isWebRelated = webPattern.test(title) || webPattern.test(description);
|
||||
|
||||
// Check for version patterns like v1.0.x or 1.0.x
|
||||
const versionPattern = /[v]?1\.0\./i;
|
||||
const isVersionRelated = versionPattern.test(title) || versionPattern.test(description);
|
||||
|
||||
if (isWebRelated) {
|
||||
// Add web label
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ['web']
|
||||
});
|
||||
|
||||
// Assign to adamdotdevin
|
||||
await github.rest.issues.addAssignees({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
assignees: ['adamdotdevin']
|
||||
});
|
||||
} else if (isVersionRelated) {
|
||||
// Only add opentui if NOT web-related
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: ['opentui']
|
||||
});
|
||||
}
|
||||
@@ -26,4 +26,4 @@ jobs:
|
||||
env:
|
||||
OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }}
|
||||
with:
|
||||
model: opencode/kimi-k2
|
||||
model: opencode/glm-4.6
|
||||
|
||||
@@ -12,6 +12,10 @@ on:
|
||||
- major
|
||||
- minor
|
||||
- patch
|
||||
version:
|
||||
description: "Override version (optional)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
@@ -62,6 +66,7 @@ jobs:
|
||||
./script/publish.ts
|
||||
env:
|
||||
OPENCODE_BUMP: ${{ inputs.bump }}
|
||||
OPENCODE_VERSION: ${{ inputs.version }}
|
||||
OPENCODE_CHANNEL: latest
|
||||
NPM_CONFIG_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
name: "sync-zed-extension"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
zed:
|
||||
name: Release Zed Extension
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: ./.github/actions/setup-bun
|
||||
|
||||
- name: Get version tag
|
||||
id: get_tag
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
TAG="${{ github.event.release.tag_name }}"
|
||||
else
|
||||
TAG=$(git tag --list 'v[0-9]*.*' --sort=-version:refname | head -n 1)
|
||||
fi
|
||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||
echo "Using tag: ${TAG}"
|
||||
|
||||
- name: Sync Zed extension
|
||||
run: |
|
||||
./script/sync-zed.ts ${{ steps.get_tag.outputs.tag }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.SST_GITHUB_TOKEN }}
|
||||
@@ -5,8 +5,11 @@ node_modules
|
||||
.env
|
||||
.idea
|
||||
.vscode
|
||||
*~
|
||||
openapi.json
|
||||
playground
|
||||
tmp
|
||||
dist
|
||||
.turbo
|
||||
**/.serena
|
||||
.serena/
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
#!/bin/sh
|
||||
bun run typecheck
|
||||
bun typecheck
|
||||
|
||||
@@ -18,3 +18,6 @@ For anything in the packages/app use the ignore: prefix.
|
||||
|
||||
prefer to explain WHY something was done from an end user perspective instead of
|
||||
WHAT was done.
|
||||
|
||||
do not do generic messages like "improved agent experience" be very specific
|
||||
about what user facing changes were made
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
description: hello world
|
||||
description: hello world iaosd ioasjdoiasjd oisadjoisajd osiajd oisaj dosaij dsoajsajdaijdoisa jdoias jdoias jdoia jois jo jdois jdoias jdoias j djoasdj
|
||||
---
|
||||
|
||||
hey there $ARGUMENTS
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"plugin": ["opencode-openai-codex-auth"],
|
||||
"mcp": {
|
||||
"weather": {
|
||||
"type": "local",
|
||||
"command": ["bun", "x", "@h1deya/mcp-server-weather"]
|
||||
},
|
||||
"context7": {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.context7.com/mcp",
|
||||
"headers": {
|
||||
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,31 +17,31 @@
|
||||
|
||||
## Tool Calling
|
||||
|
||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. Here is an example illustrating how to execute 3 parallel file reads in this chat environnement:
|
||||
- ALWAYS USE PARALLEL TOOLS WHEN APPLICABLE. Here is an example illustrating how to execute 3 parallel file reads in this chat environment:
|
||||
|
||||
json
|
||||
{
|
||||
"recipient_name": "multi_tool_use.parallel",
|
||||
"parameters": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.tsx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.md"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
"recipient_name": "multi_tool_use.parallel",
|
||||
"parameters": {
|
||||
"tool_uses": [
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.tsx"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.ts"
|
||||
}
|
||||
},
|
||||
{
|
||||
"recipient_name": "functions.read",
|
||||
"parameters": {
|
||||
"filePath": "path/to/file.md"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -26,7 +26,7 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
|
||||
|
||||
## Developing OpenCode
|
||||
|
||||
- Requirements: Bun 1.3+, Go 1.24.x.
|
||||
- Requirements: Bun 1.3+
|
||||
- Install dependencies and start the dev server from the repo root:
|
||||
|
||||
```bash
|
||||
@@ -36,11 +36,11 @@ Want to take on an issue? Leave a comment and a maintainer may assign it to you
|
||||
|
||||
- Core pieces:
|
||||
- `packages/opencode`: OpenCode core business logic & server.
|
||||
- `packages/tui`: The TUI code, written in Go (will be removed soon in favor of [opentui](https://github.com/sst/opentui))
|
||||
- `packages/opencode/src/cli/cmd/tui/`: The TUI code, written in SolidJS with [opentui](https://github.com/sst/opentui)
|
||||
- `packages/plugin`: Source for `@opencode-ai/plugin`
|
||||
|
||||
> [!NOTE]
|
||||
> After touching `packages/opencode/src/server/server.ts`, the OpenCode team must regenerate the Stainless SDK before any client updates merge.
|
||||
> After touching `packages/opencode/src/server/server.ts`, run "./packages/sdk/js/script/build.ts" to regenerate the JS sdk.
|
||||
|
||||
## Pull Request Expectations
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ curl -fsSL https://opencode.ai/install | bash
|
||||
npm i -g opencode-ai@latest # or bun/pnpm/yarn
|
||||
scoop bucket add extras; scoop install extras/opencode # Windows
|
||||
choco install opencode # Windows
|
||||
brew install sst/tap/opencode # macOS and Linux
|
||||
brew install opencode # macOS and Linux
|
||||
paru -S opencode-bin # Arch Linux
|
||||
```
|
||||
|
||||
|
||||
@@ -125,3 +125,13 @@
|
||||
| 2025-10-29 | 606,259 (+10,483) | 542,064 (+9,626) | 1,148,323 (+20,109) |
|
||||
| 2025-10-30 | 613,746 (+7,487) | 542,064 (+0) | 1,155,810 (+7,487) |
|
||||
| 2025-10-30 | 617,846 (+4,100) | 555,026 (+12,962) | 1,172,872 (+17,062) |
|
||||
| 2025-10-31 | 626,612 (+8,766) | 564,579 (+9,553) | 1,191,191 (+18,319) |
|
||||
| 2025-11-01 | 636,100 (+9,488) | 581,806 (+17,227) | 1,217,906 (+26,715) |
|
||||
| 2025-11-02 | 644,067 (+7,967) | 590,004 (+8,198) | 1,234,071 (+16,165) |
|
||||
| 2025-11-03 | 653,130 (+9,063) | 597,139 (+7,135) | 1,250,269 (+16,198) |
|
||||
| 2025-11-04 | 663,912 (+10,782) | 608,056 (+10,917) | 1,271,968 (+21,699) |
|
||||
| 2025-11-05 | 675,074 (+11,162) | 619,690 (+11,634) | 1,294,764 (+22,796) |
|
||||
| 2025-11-06 | 686,252 (+11,178) | 630,885 (+11,195) | 1,317,137 (+22,373) |
|
||||
| 2025-11-07 | 696,646 (+10,394) | 642,146 (+11,261) | 1,338,792 (+21,655) |
|
||||
| 2025-11-08 | 706,035 (+9,389) | 653,489 (+11,343) | 1,359,524 (+20,732) |
|
||||
| 2025-11-09 | 713,462 (+7,427) | 660,459 (+6,970) | 1,373,921 (+14,397) |
|
||||
|
||||
+5
-1
@@ -152,6 +152,9 @@ try {
|
||||
return session.id.slice(-8)
|
||||
})()
|
||||
console.log("opencode session", session.id)
|
||||
if (shareId) {
|
||||
console.log("Share link:", `${useShareUrl()}/s/${shareId}`)
|
||||
}
|
||||
|
||||
// Handle 3 cases
|
||||
// 1. Issue
|
||||
@@ -730,12 +733,13 @@ async function updateComment(body: string) {
|
||||
async function createPR(base: string, branch: string, title: string, body: string) {
|
||||
console.log("Creating pull request...")
|
||||
const { repo } = useContext()
|
||||
const truncatedTitle = title.length > 256 ? title.slice(0, 253) + "..." : title
|
||||
const pr = await octoRest.rest.pulls.create({
|
||||
owner: repo.owner,
|
||||
repo: repo.repo,
|
||||
head: branch,
|
||||
base,
|
||||
title,
|
||||
title: truncatedTitle,
|
||||
body,
|
||||
})
|
||||
return pr.data.number
|
||||
|
||||
Vendored
+1
-1
@@ -6,4 +6,4 @@
|
||||
/// <reference path="../sst-env.d.ts" />
|
||||
|
||||
import "sst"
|
||||
export {}
|
||||
export {}
|
||||
|
||||
+12
-2
@@ -97,7 +97,8 @@ export const stripeWebhook = new stripe.WebhookEndpoint("StripeWebhookEndpoint",
|
||||
],
|
||||
})
|
||||
|
||||
const ZEN_MODELS = new sst.Secret("ZEN_MODELS")
|
||||
const ZEN_MODELS1 = new sst.Secret("ZEN_MODELS1")
|
||||
const ZEN_MODELS2 = new sst.Secret("ZEN_MODELS2")
|
||||
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!) },
|
||||
@@ -105,6 +106,7 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
|
||||
properties: { value: stripeWebhook.secret },
|
||||
})
|
||||
const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
|
||||
|
||||
////////////////
|
||||
// CONSOLE
|
||||
@@ -130,10 +132,18 @@ new sst.cloudflare.x.SolidStart("Console", {
|
||||
AUTH_API_URL,
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
STRIPE_SECRET_KEY,
|
||||
ZEN_MODELS,
|
||||
ZEN_MODELS1,
|
||||
ZEN_MODELS2,
|
||||
EMAILOCTOPUS_API_KEY,
|
||||
AWS_SES_ACCESS_KEY_ID,
|
||||
AWS_SES_SECRET_ACCESS_KEY,
|
||||
...($dev
|
||||
? [
|
||||
new sst.Secret("CLOUDFLARE_DEFAULT_ACCOUNT_ID", process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!),
|
||||
new sst.Secret("CLOUDFLARE_API_TOKEN", process.env.CLOUDFLARE_API_TOKEN!),
|
||||
]
|
||||
: []),
|
||||
gatewayKv,
|
||||
],
|
||||
environment: {
|
||||
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
||||
|
||||
@@ -10,10 +10,14 @@ NC='\033[0m' # No Color
|
||||
|
||||
requested_version=${VERSION:-}
|
||||
|
||||
os=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
if [[ "$os" == "darwin" ]]; then
|
||||
os="darwin"
|
||||
fi
|
||||
raw_os=$(uname -s)
|
||||
os=$(echo "$raw_os" | tr '[:upper:]' '[:lower:]')
|
||||
# Normalize various Unix-like identifiers
|
||||
case "$raw_os" in
|
||||
Darwin*) os="darwin" ;;
|
||||
Linux*) os="linux" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) os="windows" ;;
|
||||
esac
|
||||
arch=$(uname -m)
|
||||
|
||||
if [[ "$arch" == "aarch64" ]]; then
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"keep": {
|
||||
"days": true,
|
||||
"amount": 14
|
||||
},
|
||||
"auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json",
|
||||
"files": [
|
||||
{
|
||||
"date": 1759827172859,
|
||||
"name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log",
|
||||
"hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16"
|
||||
}
|
||||
],
|
||||
"hashType": "sha256"
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.879"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:52.880"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.191"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:56.192"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.267"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:52:59.268"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.276"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:20.277"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.838"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:30.839"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:42.452"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.499"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:53:46.500"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:02.295"}
|
||||
{"arguments":{"url":"https://google.com"},"level":"debug","message":"Tool call received","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150","tool":"puppeteer_navigate"}
|
||||
{"0":"n","1":"p","2":"x","level":"info","message":"Launching browser with config:","service":"mcp-puppeteer","timestamp":"2025-10-07 04:54:37.150"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.488"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 04:55:08.489"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.815"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:11.816"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.934"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:21.935"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:32.544"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.154"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:41.155"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.426"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:23:55.427"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.715"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:15.716"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.063"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:25.064"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.567"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:24:48.568"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.937"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 05:25:08.938"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.120"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:37.121"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.490"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:38:52.491"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.524"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:39:25.525"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.126"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:40:57.127"}
|
||||
{"level":"info","message":"Starting MCP server","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.175"}
|
||||
{"level":"info","message":"MCP server started successfully","service":"mcp-puppeteer","timestamp":"2025-10-07 22:42:24.176"}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json"
|
||||
}
|
||||
+9
-6
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "opencode",
|
||||
"description": "AI-powered development tool",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "bun@1.3.0",
|
||||
"packageManager": "bun@1.3.1",
|
||||
"scripts": {
|
||||
"dev": "bun run packages/opencode/src/index.ts",
|
||||
"dev": "bun run --cwd packages/opencode --conditions=browser src/index.ts",
|
||||
"typecheck": "bun turbo typecheck",
|
||||
"prepare": "husky"
|
||||
"prepare": "husky",
|
||||
"random": "echo 'Random script'"
|
||||
},
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
@@ -19,13 +21,14 @@
|
||||
"catalog": {
|
||||
"@types/bun": "1.3.0",
|
||||
"@hono/zod-validator": "0.4.2",
|
||||
"ulid": "3.0.1",
|
||||
"@kobalte/core": "0.13.11",
|
||||
"@types/node": "22.13.9",
|
||||
"@tsconfig/node22": "22.0.2",
|
||||
"@tsconfig/bun": "1.0.9",
|
||||
"@cloudflare/workers-types": "4.20251008.0",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@pierre/precision-diffs": "0.4.1",
|
||||
"@pierre/precision-diffs": "0.4.4",
|
||||
"@solidjs/meta": "0.29.4",
|
||||
"@tailwindcss/vite": "4.1.11",
|
||||
"diff": "8.0.2",
|
||||
@@ -49,7 +52,7 @@
|
||||
"@tsconfig/bun": "catalog:",
|
||||
"husky": "9.1.7",
|
||||
"prettier": "3.6.2",
|
||||
"sst": "3.17.19",
|
||||
"sst": "3.17.23",
|
||||
"turbo": "2.5.6"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -63,7 +66,7 @@
|
||||
"license": "MIT",
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
"printWidth": 100
|
||||
"printWidth": 120
|
||||
},
|
||||
"trustedDependencies": [
|
||||
"esbuild",
|
||||
|
||||
@@ -23,6 +23,9 @@ app.config.timestamp_*.js
|
||||
# Temp
|
||||
gitignore
|
||||
|
||||
# Generated files
|
||||
public/sitemap.xml
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
@@ -5,15 +5,17 @@
|
||||
"typecheck": "tsgo --noEmit",
|
||||
"dev": "vinxi dev --host 0.0.0.0",
|
||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"build": "./script/generate-sitemap.ts && vinxi build && ../../opencode/script/schema.ts ./.output/public/config.json",
|
||||
"start": "vinxi start",
|
||||
"version": "0.15.30"
|
||||
"version": "1.0.55"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
"@kobalte/core": "catalog:",
|
||||
"@openauthjs/openauth": "0.0.0-20250322224806",
|
||||
"@opencode-ai/console-core": "workspace:*",
|
||||
"@opencode-ai/console-mail": "workspace:*",
|
||||
"@openauthjs/openauth": "catalog:",
|
||||
"@kobalte/core": "catalog:",
|
||||
"@jsx-email/render": "1.1.1",
|
||||
"@opencode-ai/console-resource": "workspace:*",
|
||||
"@solidjs/meta": "^0.29.4",
|
||||
"@solidjs/router": "^0.15.0",
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bun
|
||||
import { readdir, writeFile } from "fs/promises"
|
||||
import { join, dirname } from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import { config } from "../src/config.js"
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const BASE_URL = config.baseUrl
|
||||
const PUBLIC_DIR = join(__dirname, "../public")
|
||||
const ROUTES_DIR = join(__dirname, "../src/routes")
|
||||
const DOCS_DIR = join(__dirname, "../../../web/src/content/docs")
|
||||
|
||||
interface SitemapEntry {
|
||||
url: string
|
||||
priority: number
|
||||
changefreq: string
|
||||
}
|
||||
|
||||
async function getMainRoutes(): Promise<SitemapEntry[]> {
|
||||
const routes: SitemapEntry[] = []
|
||||
|
||||
// Add main static routes
|
||||
const staticRoutes = [
|
||||
{ path: "/", priority: 1.0, changefreq: "daily" },
|
||||
{ path: "/enterprise", priority: 0.8, changefreq: "weekly" },
|
||||
{ path: "/brand", priority: 0.6, changefreq: "monthly" },
|
||||
{ path: "/zen", priority: 0.8, changefreq: "weekly" },
|
||||
]
|
||||
|
||||
for (const route of staticRoutes) {
|
||||
routes.push({
|
||||
url: `${BASE_URL}${route.path}`,
|
||||
priority: route.priority,
|
||||
changefreq: route.changefreq,
|
||||
})
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
async function getDocsRoutes(): Promise<SitemapEntry[]> {
|
||||
const routes: SitemapEntry[] = []
|
||||
|
||||
try {
|
||||
const files = await readdir(DOCS_DIR)
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.endsWith(".mdx")) continue
|
||||
|
||||
const slug = file.replace(".mdx", "")
|
||||
const path = slug === "index" ? "/docs/" : `/docs/${slug}`
|
||||
|
||||
routes.push({
|
||||
url: `${BASE_URL}${path}`,
|
||||
priority: slug === "index" ? 0.9 : 0.7,
|
||||
changefreq: "weekly",
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error reading docs directory:", error)
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
function generateSitemapXML(entries: SitemapEntry[]): string {
|
||||
const urls = entries
|
||||
.map(
|
||||
(entry) => ` <url>
|
||||
<loc>${entry.url}</loc>
|
||||
<changefreq>${entry.changefreq}</changefreq>
|
||||
<priority>${entry.priority}</priority>
|
||||
</url>`,
|
||||
)
|
||||
.join("\n")
|
||||
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
${urls}
|
||||
</urlset>`
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Generating sitemap...")
|
||||
|
||||
const mainRoutes = await getMainRoutes()
|
||||
const docsRoutes = await getDocsRoutes()
|
||||
|
||||
const allRoutes = [...mainRoutes, ...docsRoutes]
|
||||
|
||||
console.log(`Found ${mainRoutes.length} main routes`)
|
||||
console.log(`Found ${docsRoutes.length} docs routes`)
|
||||
console.log(`Total: ${allRoutes.length} routes`)
|
||||
|
||||
const xml = generateSitemapXML(allRoutes)
|
||||
|
||||
const outputPath = join(PUBLIC_DIR, "sitemap.xml")
|
||||
await writeFile(outputPath, xml, "utf-8")
|
||||
|
||||
console.log(`✓ Sitemap generated at ${outputPath}`)
|
||||
}
|
||||
|
||||
main()
|
||||
@@ -12,7 +12,7 @@ export default function App() {
|
||||
root={(props) => (
|
||||
<MetaProvider>
|
||||
<Title>opencode</Title>
|
||||
<Meta name="description" content="opencode - The AI coding agent built for the terminal." />
|
||||
<Meta name="description" content="OpenCode - The AI coding agent built for the terminal." />
|
||||
<Suspense>{props.children}</Suspense>
|
||||
</MetaProvider>
|
||||
)}
|
||||
|
||||
@@ -77,4 +77,4 @@
|
||||
background-color: var(--color-accent-alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createAsync } from "@solidjs/router"
|
||||
import { createMemo } from "solid-js"
|
||||
import { github } from "~/lib/github"
|
||||
import { config } from "~/config"
|
||||
|
||||
export function Footer() {
|
||||
const githubData = createAsync(() => github())
|
||||
@@ -10,13 +11,13 @@ export function Footer() {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}).format(githubData()!.stars!)
|
||||
: "25K",
|
||||
: config.github.starsFormatted.compact,
|
||||
)
|
||||
|
||||
return (
|
||||
<footer data-component="footer">
|
||||
<div data-slot="cell">
|
||||
<a href="https://github.com/sst/opencode" target="_blank">
|
||||
<a href={config.github.repoUrl} target="_blank">
|
||||
GitHub <span>[{starCount()}]</span>
|
||||
</a>
|
||||
</div>
|
||||
@@ -27,7 +28,7 @@ export function Footer() {
|
||||
<a href="/discord">Discord</a>
|
||||
</div>
|
||||
<div data-slot="cell">
|
||||
<a href="https://x.com/opencode">X</a>
|
||||
<a href={config.social.twitter}>X</a>
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ import { createMemo, Match, Show, Switch } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { github } from "~/lib/github"
|
||||
import { createEffect, onCleanup } from "solid-js"
|
||||
import { config } from "~/config"
|
||||
import "./header-context-menu.css"
|
||||
|
||||
const isDarkMode = () => window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
@@ -42,7 +43,7 @@ export function Header(props: { zen?: boolean }) {
|
||||
notation: "compact",
|
||||
compactDisplay: "short",
|
||||
}).format(githubData()?.stars!)
|
||||
: "29K",
|
||||
: config.github.starsFormatted.compact,
|
||||
)
|
||||
|
||||
const [store, setStore] = createStore({
|
||||
@@ -148,7 +149,7 @@ export function Header(props: { zen?: boolean }) {
|
||||
<nav data-component="nav-desktop">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://github.com/sst/opencode" target="_blank">
|
||||
<a href={config.github.repoUrl} target="_blank">
|
||||
GitHub <span>[{starCount()}]</span>
|
||||
</a>
|
||||
</li>
|
||||
@@ -222,7 +223,7 @@ export function Header(props: { zen?: boolean }) {
|
||||
<A href="/">Home</A>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/sst/opencode" target="_blank">
|
||||
<a href={config.github.repoUrl} target="_blank">
|
||||
GitHub <span>[{starCount()}]</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -63,4 +63,4 @@
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Application-wide constants and configuration
|
||||
*/
|
||||
export const config = {
|
||||
// Base URL
|
||||
baseUrl: "https://opencode.ai",
|
||||
|
||||
// GitHub
|
||||
github: {
|
||||
repoUrl: "https://github.com/sst/opencode",
|
||||
starsFormatted: {
|
||||
compact: "30K",
|
||||
full: "30,000",
|
||||
},
|
||||
},
|
||||
|
||||
// Social links
|
||||
social: {
|
||||
twitter: "https://x.com/opencode",
|
||||
discord: "https://discord.gg/opencode",
|
||||
},
|
||||
|
||||
// Static stats (used on landing page)
|
||||
stats: {
|
||||
contributors: "250",
|
||||
commits: "3,500",
|
||||
monthlyUsers: "300,000",
|
||||
},
|
||||
} as const
|
||||
@@ -1,4 +1,5 @@
|
||||
import { query } from "@solidjs/router"
|
||||
import { config } from "~/config"
|
||||
|
||||
export const github = query(async () => {
|
||||
"use server"
|
||||
@@ -6,11 +7,12 @@ export const github = query(async () => {
|
||||
"User-Agent":
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
||||
}
|
||||
const apiBaseUrl = config.github.repoUrl.replace("https://github.com/", "https://api.github.com/repos/")
|
||||
try {
|
||||
const [meta, releases, contributors] = await Promise.all([
|
||||
fetch("https://api.github.com/repos/sst/opencode", { headers }).then((res) => res.json()),
|
||||
fetch("https://api.github.com/repos/sst/opencode/releases", { headers }).then((res) => res.json()),
|
||||
fetch("https://api.github.com/repos/sst/opencode/contributors?per_page=1", { headers }),
|
||||
fetch(apiBaseUrl, { headers }).then((res) => res.json()),
|
||||
fetch(`${apiBaseUrl}/releases`, { headers }).then((res) => res.json()),
|
||||
fetch(`${apiBaseUrl}/contributors?per_page=1`, { headers }),
|
||||
])
|
||||
const [release] = releases
|
||||
const contributorCount = Number.parseInt(
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
[data-component="brand-content"] {
|
||||
padding: 4rem 5rem;
|
||||
|
||||
h2 {
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
@@ -299,7 +299,6 @@
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-background-strong-hover);
|
||||
}
|
||||
@@ -385,23 +384,21 @@
|
||||
0 1px 2px -1px rgba(19, 16, 16, 0.12);
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(19, 16, 16, 0.16)
|
||||
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.16);
|
||||
}
|
||||
|
||||
|
||||
&:hover {
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.08), 0 6px 8px -8px rgba(19, 16, 16, 0.50);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(19, 16, 16, 0.08),
|
||||
0 6px 8px -8px rgba(19, 16, 16, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "./index.css"
|
||||
import { Title, Meta } from "@solidjs/meta"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
import { Header } from "~/component/header"
|
||||
import { config } from "~/config"
|
||||
import { Footer } from "~/component/footer"
|
||||
import { Legal } from "~/component/legal"
|
||||
import previewLogoLight from "../../asset/brand/preview-opencode-logo-light.png"
|
||||
@@ -53,26 +54,21 @@ export default function Brand() {
|
||||
return (
|
||||
<main data-page="enterprise">
|
||||
<Title>OpenCode | Brand</Title>
|
||||
<Link rel="canonical" href={`${config.baseUrl}/brand`} />
|
||||
<Meta name="description" content="OpenCode brand guidelines" />
|
||||
<div data-component="container">
|
||||
<Header />
|
||||
|
||||
<div data-component="content">
|
||||
<section data-component="brand-content">
|
||||
<h2>Brand guidelines</h2>
|
||||
<h1>Brand guidelines</h1>
|
||||
<p>Resources and assets to help you work with the OpenCode brand.</p>
|
||||
<button
|
||||
data-component="download-button"
|
||||
onClick={() => downloadFile(brandAssets, "opencode-brand-assets.zip")}
|
||||
>
|
||||
Download all assets
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -88,13 +84,7 @@ export default function Brand() {
|
||||
<div data-component="actions">
|
||||
<button onClick={() => downloadFile(logoLightPng, "opencode-logo-light.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -105,13 +95,7 @@ export default function Brand() {
|
||||
</button>
|
||||
<button onClick={() => downloadFile(logoLightSvg, "opencode-logo-light.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -127,13 +111,7 @@ export default function Brand() {
|
||||
<div data-component="actions">
|
||||
<button onClick={() => downloadFile(logoDarkPng, "opencode-logo-dark.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -144,13 +122,7 @@ export default function Brand() {
|
||||
</button>
|
||||
<button onClick={() => downloadFile(logoDarkSvg, "opencode-logo-dark.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -164,17 +136,9 @@ export default function Brand() {
|
||||
<div>
|
||||
<img src={previewWordmarkLight} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button
|
||||
onClick={() => downloadFile(wordmarkLightPng, "opencode-wordmark-light.png")}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkLightPng, "opencode-wordmark-light.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -183,17 +147,9 @@ export default function Brand() {
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => downloadFile(wordmarkLightSvg, "opencode-wordmark-light.svg")}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkLightSvg, "opencode-wordmark-light.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -207,17 +163,9 @@ export default function Brand() {
|
||||
<div>
|
||||
<img src={previewWordmarkDark} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button
|
||||
onClick={() => downloadFile(wordmarkDarkPng, "opencode-wordmark-dark.png")}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkDarkPng, "opencode-wordmark-dark.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -226,17 +174,9 @@ export default function Brand() {
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => downloadFile(wordmarkDarkSvg, "opencode-wordmark-dark.svg")}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkDarkSvg, "opencode-wordmark-dark.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -250,19 +190,9 @@ export default function Brand() {
|
||||
<div>
|
||||
<img src={previewWordmarkSimpleLight} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button
|
||||
onClick={() =>
|
||||
downloadFile(wordmarkSimpleLightPng, "opencode-wordmark-simple-light.png")
|
||||
}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkSimpleLightPng, "opencode-wordmark-simple-light.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -271,19 +201,9 @@ export default function Brand() {
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
downloadFile(wordmarkSimpleLightSvg, "opencode-wordmark-simple-light.svg")
|
||||
}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkSimpleLightSvg, "opencode-wordmark-simple-light.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -297,19 +217,9 @@ export default function Brand() {
|
||||
<div>
|
||||
<img src={previewWordmarkSimpleDark} alt="OpenCode brand guidelines" />
|
||||
<div data-component="actions">
|
||||
<button
|
||||
onClick={() =>
|
||||
downloadFile(wordmarkSimpleDarkPng, "opencode-wordmark-simple-dark.png")
|
||||
}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkSimpleDarkPng, "opencode-wordmark-simple-dark.png")}>
|
||||
PNG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
@@ -318,19 +228,9 @@ export default function Brand() {
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
onClick={() =>
|
||||
downloadFile(wordmarkSimpleDarkSvg, "opencode-wordmark-simple-dark.svg")
|
||||
}
|
||||
>
|
||||
<button onClick={() => downloadFile(wordmarkSimpleDarkSvg, "opencode-wordmark-simple-dark.svg")}>
|
||||
SVG
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.9583 10.6247L10 14.583L6.04167 10.6247M10 2.08301V13.958M16.25 17.9163H3.75"
|
||||
stroke="currentColor"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { redirect } from "@solidjs/router"
|
||||
|
||||
export async function GET() {
|
||||
return redirect("https://discord.gg/h5TNnkFVNy")
|
||||
}
|
||||
@@ -287,7 +287,7 @@
|
||||
}
|
||||
|
||||
[data-component="enterprise-column-1"] {
|
||||
h2 {
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: var(--color-text-strong);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "./index.css"
|
||||
import { Title, Meta } from "@solidjs/meta"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
import { createSignal, Show } from "solid-js"
|
||||
import { config } from "~/config"
|
||||
import { Header } from "~/component/header"
|
||||
import { Footer } from "~/component/footer"
|
||||
import { Legal } from "~/component/legal"
|
||||
@@ -54,6 +55,7 @@ export default function Enterprise() {
|
||||
return (
|
||||
<main data-page="enterprise">
|
||||
<Title>OpenCode | Enterprise solutions for your organisation</Title>
|
||||
<Link rel="canonical" href={`${config.baseUrl}/enterprise`} />
|
||||
<Meta name="description" content="Contact OpenCode for enterprise solutions" />
|
||||
<div data-component="container">
|
||||
<Header />
|
||||
@@ -62,41 +64,28 @@ export default function Enterprise() {
|
||||
<section data-component="enterprise-content">
|
||||
<div data-component="enterprise-columns">
|
||||
<div data-component="enterprise-column-1">
|
||||
<h2>Your code is yours</h2>
|
||||
<h1>Your code is yours</h1>
|
||||
<p>
|
||||
OpenCode operates securely inside your organization with no data or context stored
|
||||
and no licensing restrictions or ownership claims. Start a trial with your team,
|
||||
then deploy it across your organization by integrating it with your SSO and
|
||||
internal AI gateway.
|
||||
OpenCode operates securely inside your organization with no data or context stored and no licensing
|
||||
restrictions or ownership claims. Start a trial with your team, then deploy it across your
|
||||
organization by integrating it with your SSO and internal AI gateway.
|
||||
</p>
|
||||
<p>Let us know and how we can help.</p>
|
||||
|
||||
<Show when={false}>
|
||||
<div data-component="testimonial">
|
||||
<div data-component="quotation">
|
||||
<svg
|
||||
width="20"
|
||||
height="17"
|
||||
viewBox="0 0 20 17"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="20" height="17" viewBox="0 0 20 17" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M19.4118 0L16.5882 9.20833H20V17H12.2353V10.0938L16 0H19.4118ZM7.17647 0L4.35294 9.20833H7.76471V17H0V10.0938L3.76471 0H7.17647Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
Thanks to OpenCode, we found a way to create software to track all our assets —
|
||||
even the imaginary ones.
|
||||
Thanks to OpenCode, we found a way to create software to track all our assets — even the imaginary
|
||||
ones.
|
||||
<div data-component="testimonial-logo">
|
||||
<svg
|
||||
width="80"
|
||||
height="79"
|
||||
viewBox="0 0 80 79"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<svg width="80" height="79" viewBox="0 0 80 79" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
@@ -213,11 +202,7 @@ export default function Enterprise() {
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{showSuccess() && (
|
||||
<div data-component="success-message">
|
||||
Message sent, we'll be in touch soon.
|
||||
</div>
|
||||
)}
|
||||
{showSuccess() && <div data-component="success-message">Message sent, we'll be in touch soon.</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -230,31 +215,29 @@ export default function Enterprise() {
|
||||
<ul>
|
||||
<li>
|
||||
<Faq question="What is OpenCode Enterprise?">
|
||||
OpenCode Enterprise is for organizations that want to ensure that their code and
|
||||
data never leaves their infrastructure. It can do this by using a centralized
|
||||
config that integrates with your SSO and internal AI gateway.
|
||||
OpenCode Enterprise is for organizations that want to ensure that their code and data never leaves
|
||||
their infrastructure. It can do this by using a centralized config that integrates with your SSO and
|
||||
internal AI gateway.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="How do I get started with OpenCode Enterprise?">
|
||||
Simply start with an internal trial with your team. OpenCode by default does not
|
||||
store your code or context data, making it easy to get started. Then contact us to
|
||||
discuss pricing and implementation options.
|
||||
Simply start with an internal trial with your team. OpenCode by default does not store your code or
|
||||
context data, making it easy to get started. Then contact us to discuss pricing and implementation
|
||||
options.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="How does enterprise pricing work?">
|
||||
We offer per-seat enterprise pricing. If you have your own LLM gateway, we do not
|
||||
charge for tokens used. For further details, contact us for a custom quote based
|
||||
on your organization's needs.
|
||||
We offer per-seat enterprise pricing. If you have your own LLM gateway, we do not charge for tokens
|
||||
used. For further details, contact us for a custom quote based on your organization's needs.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Is my data secure with OpenCode Enterprise?">
|
||||
Yes. OpenCode does not store your code or context data. All processing happens
|
||||
locally or through direct API calls to your AI provider. With central config and
|
||||
SSO integration, your data remains secure within your organization's
|
||||
infrastructure.
|
||||
Yes. OpenCode does not store your code or context data. All processing happens locally or through
|
||||
direct API calls to your AI provider. With central config and SSO integration, your data remains
|
||||
secure within your organization's infrastructure.
|
||||
</Faq>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -479,7 +479,7 @@ body {
|
||||
border-bottom: 1px solid var(--color-border-weak);
|
||||
}
|
||||
|
||||
strong {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: var(--color-text-strong);
|
||||
font-weight: 500;
|
||||
|
||||
@@ -13,6 +13,7 @@ import { Footer } from "~/component/footer"
|
||||
import { Legal } from "~/component/legal"
|
||||
import { github } from "~/lib/github"
|
||||
import { createMemo } from "solid-js"
|
||||
import { config } from "~/config"
|
||||
|
||||
function CopyStatus() {
|
||||
return (
|
||||
@@ -43,6 +44,7 @@ export default function Home() {
|
||||
<main data-page="opencode">
|
||||
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
|
||||
<Title>OpenCode | The AI coding agent built for the terminal</Title>
|
||||
<Link rel="canonical" href={config.baseUrl} />
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<Meta property="og:image" content="/social-share.png" />
|
||||
<Meta name="twitter:image" content="/social-share.png" />
|
||||
@@ -52,14 +54,10 @@ export default function Home() {
|
||||
<div data-component="content">
|
||||
<section data-component="hero">
|
||||
<div data-slot="hero-copy">
|
||||
<a
|
||||
data-slot="releases"
|
||||
href={release()?.url ?? "https://github.com/sst/opencode/releases"}
|
||||
target="_blank"
|
||||
>
|
||||
<a data-slot="releases" href={release()?.url ?? `${config.github.repoUrl}/releases`} target="_blank">
|
||||
What’s new in {release()?.name ?? "the latest release"}
|
||||
</a>
|
||||
<strong>The AI coding agent built for the terminal</strong>
|
||||
<h1>The AI coding agent built for the terminal</h1>
|
||||
<p>
|
||||
OpenCode is fully open source, giving you control and freedom to use any provider, any model, and any
|
||||
editor.
|
||||
@@ -219,9 +217,10 @@ export default function Home() {
|
||||
<div>
|
||||
<span>[*]</span>
|
||||
<p>
|
||||
With over <strong>29,000</strong> GitHub stars, <strong>230</strong> contributors, and almost{" "}
|
||||
<strong>3,500</strong> commits, OpenCode is used and trusted by over <strong>250,000</strong>{" "}
|
||||
developers every month.
|
||||
With over <strong>{config.github.starsFormatted.full}</strong> GitHub stars,{" "}
|
||||
<strong>{config.stats.contributors}</strong> contributors, and almost{" "}
|
||||
<strong>{config.stats.commits}</strong> commits, OpenCode is used and trusted by over{" "}
|
||||
<strong>{config.stats.monthlyUsers}</strong> developers every month.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -274,7 +273,7 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<span>
|
||||
<figure>Fig 1.</figure> <strong>29K</strong> GitHub Stars
|
||||
<figure>Fig 1.</figure> <strong>{config.github.starsFormatted.compact}</strong> GitHub Stars
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -577,7 +576,7 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<span>
|
||||
<figure>Fig 2.</figure> <strong>230</strong> Contributors
|
||||
<figure>Fig 2.</figure> <strong>{config.stats.contributors}</strong> Contributors
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -619,7 +618,7 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<span>
|
||||
<figure>Fig 3.</figure> <strong>250K</strong> Monthly Devs
|
||||
<figure>Fig 3.</figure> <strong>{config.stats.monthlyUsers}</strong> Monthly Devs
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -688,11 +687,11 @@ export default function Home() {
|
||||
<li>
|
||||
<Faq question="Is OpenCode open source?">
|
||||
Yes, OpenCode is fully open source. The source code is public on{" "}
|
||||
<a href="https://github.com/sst/opencode" target="_blank">
|
||||
<a href={config.github.repoUrl} target="_blank">
|
||||
GitHub
|
||||
</a>{" "}
|
||||
under the{" "}
|
||||
<a href="https://github.com/sst/opencode?tab=MIT-1-ov-file#readme" target="_blank">
|
||||
<a href={`${config.github.repoUrl}?tab=MIT-1-ov-file#readme`} target="_blank">
|
||||
MIT License
|
||||
</a>
|
||||
, meaning anyone can use, modify, or contribute to its development. Anyone from the community can file
|
||||
|
||||
@@ -13,124 +13,144 @@ export async function POST(input: APIEvent) {
|
||||
input.request.headers.get("stripe-signature")!,
|
||||
Resource.STRIPE_WEBHOOK_SECRET.value,
|
||||
)
|
||||
|
||||
console.log(body.type, JSON.stringify(body, null, 2))
|
||||
if (body.type === "customer.updated") {
|
||||
// check default payment method changed
|
||||
const prevInvoiceSettings = body.data.previous_attributes?.invoice_settings ?? {}
|
||||
if (!("default_payment_method" in prevInvoiceSettings)) return
|
||||
|
||||
const customerID = body.data.object.id
|
||||
const paymentMethodID = body.data.object.invoice_settings.default_payment_method as string
|
||||
return (async () => {
|
||||
if (body.type === "customer.updated") {
|
||||
// check default payment method changed
|
||||
const prevInvoiceSettings = body.data.previous_attributes?.invoice_settings ?? {}
|
||||
if (!("default_payment_method" in prevInvoiceSettings)) return "ignored"
|
||||
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!paymentMethodID) throw new Error("Payment method ID not found")
|
||||
const customerID = body.data.object.id
|
||||
const paymentMethodID = body.data.object.invoice_settings.default_payment_method as string
|
||||
|
||||
const paymentMethod = await Billing.stripe().paymentMethods.retrieve(paymentMethodID)
|
||||
await Database.use(async (tx) => {
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
paymentMethodID,
|
||||
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
|
||||
paymentMethodType: paymentMethod.type,
|
||||
})
|
||||
.where(eq(BillingTable.customerID, customerID))
|
||||
})
|
||||
}
|
||||
if (body.type === "checkout.session.completed") {
|
||||
const workspaceID = body.data.object.metadata?.workspaceID
|
||||
const customerID = body.data.object.customer as string
|
||||
const paymentID = body.data.object.payment_intent as string
|
||||
const invoiceID = body.data.object.invoice as string
|
||||
const amount = body.data.object.amount_total
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!paymentMethodID) throw new Error("Payment method ID not found")
|
||||
|
||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!amount) throw new Error("Amount not found")
|
||||
if (!paymentID) throw new Error("Payment ID not found")
|
||||
if (!invoiceID) throw new Error("Invoice ID not found")
|
||||
|
||||
await Actor.provide("system", { workspaceID }, async () => {
|
||||
const customer = await Billing.get()
|
||||
if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch")
|
||||
|
||||
// set customer metadata
|
||||
if (!customer?.customerID) {
|
||||
await Billing.stripe().customers.update(customerID, {
|
||||
metadata: {
|
||||
workspaceID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// get payment method for the payment intent
|
||||
const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
|
||||
expand: ["payment_method"],
|
||||
})
|
||||
const paymentMethod = paymentIntent.payment_method
|
||||
if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")
|
||||
|
||||
await Database.transaction(async (tx) => {
|
||||
const paymentMethod = await Billing.stripe().paymentMethods.retrieve(paymentMethodID)
|
||||
await Database.use(async (tx) => {
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} + ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`,
|
||||
customerID,
|
||||
paymentMethodID: paymentMethod.id,
|
||||
paymentMethodID,
|
||||
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
|
||||
paymentMethodType: paymentMethod.type,
|
||||
reload: true,
|
||||
reloadError: null,
|
||||
timeReloadError: null,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
await tx.insert(PaymentTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("payment"),
|
||||
amount: centsToMicroCents(Billing.CHARGE_AMOUNT),
|
||||
paymentID,
|
||||
invoiceID,
|
||||
customerID,
|
||||
.where(eq(BillingTable.customerID, customerID))
|
||||
})
|
||||
}
|
||||
if (body.type === "checkout.session.completed") {
|
||||
const workspaceID = body.data.object.metadata?.workspaceID
|
||||
const amountInCents = body.data.object.metadata?.amount && parseInt(body.data.object.metadata?.amount)
|
||||
const customerID = body.data.object.customer as string
|
||||
const paymentID = body.data.object.payment_intent as string
|
||||
const invoiceID = body.data.object.invoice as string
|
||||
|
||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!amountInCents) throw new Error("Amount not found")
|
||||
if (!paymentID) throw new Error("Payment ID not found")
|
||||
if (!invoiceID) throw new Error("Invoice ID not found")
|
||||
|
||||
await Actor.provide("system", { workspaceID }, async () => {
|
||||
const customer = await Billing.get()
|
||||
if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch")
|
||||
|
||||
// set customer metadata
|
||||
if (!customer?.customerID) {
|
||||
await Billing.stripe().customers.update(customerID, {
|
||||
metadata: {
|
||||
workspaceID,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// get payment method for the payment intent
|
||||
const paymentIntent = await Billing.stripe().paymentIntents.retrieve(paymentID, {
|
||||
expand: ["payment_method"],
|
||||
})
|
||||
const paymentMethod = paymentIntent.payment_method
|
||||
if (!paymentMethod || typeof paymentMethod === "string") throw new Error("Payment method not expanded")
|
||||
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} + ${centsToMicroCents(amountInCents)}`,
|
||||
customerID,
|
||||
paymentMethodID: paymentMethod.id,
|
||||
paymentMethodLast4: paymentMethod.card?.last4 ?? null,
|
||||
paymentMethodType: paymentMethod.type,
|
||||
// enable reload if first time enabling billing
|
||||
...(customer?.customerID
|
||||
? {}
|
||||
: {
|
||||
reload: true,
|
||||
reloadError: null,
|
||||
timeReloadError: null,
|
||||
}),
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
await tx.insert(PaymentTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("payment"),
|
||||
amount: centsToMicroCents(amountInCents),
|
||||
paymentID,
|
||||
invoiceID,
|
||||
customerID,
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
if (body.type === "charge.refunded") {
|
||||
const customerID = body.data.object.customer as string
|
||||
const paymentIntentID = body.data.object.payment_intent as string
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!paymentIntentID) throw new Error("Payment ID not found")
|
||||
|
||||
const workspaceID = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
workspaceID: BillingTable.workspaceID,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.where(eq(BillingTable.customerID, customerID))
|
||||
.then((rows) => rows[0]?.workspaceID),
|
||||
)
|
||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||
|
||||
const amount = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
amount: PaymentTable.amount,
|
||||
})
|
||||
.from(PaymentTable)
|
||||
.where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID)))
|
||||
.then((rows) => rows[0]?.amount),
|
||||
)
|
||||
if (!amount) throw new Error("Payment not found")
|
||||
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(PaymentTable)
|
||||
.set({
|
||||
timeRefunded: new Date(body.created * 1000),
|
||||
})
|
||||
.where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID)))
|
||||
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} - ${amount}`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
})
|
||||
}
|
||||
})()
|
||||
.then((message) => {
|
||||
return Response.json({ message: message ?? "done" }, { status: 200 })
|
||||
})
|
||||
}
|
||||
if (body.type === "charge.refunded") {
|
||||
const customerID = body.data.object.customer as string
|
||||
const paymentIntentID = body.data.object.payment_intent as string
|
||||
if (!customerID) throw new Error("Customer ID not found")
|
||||
if (!paymentIntentID) throw new Error("Payment ID not found")
|
||||
|
||||
const workspaceID = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
workspaceID: BillingTable.workspaceID,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.where(eq(BillingTable.customerID, customerID))
|
||||
.then((rows) => rows[0]?.workspaceID),
|
||||
)
|
||||
if (!workspaceID) throw new Error("Workspace ID not found")
|
||||
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx
|
||||
.update(PaymentTable)
|
||||
.set({
|
||||
timeRefunded: new Date(body.created * 1000),
|
||||
})
|
||||
.where(and(eq(PaymentTable.paymentID, paymentIntentID), eq(PaymentTable.workspaceID, workspaceID)))
|
||||
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} - ${centsToMicroCents(Billing.CHARGE_AMOUNT)}`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
.catch((error: any) => {
|
||||
return Response.json({ message: error.message }, { status: 500 })
|
||||
})
|
||||
}
|
||||
|
||||
console.log("finished handling")
|
||||
|
||||
return Response.json("ok", { status: 200 })
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ export default function Home() {
|
||||
<h3 data-component="title">homebrew</h3>
|
||||
<button data-copy data-slot="button">
|
||||
<span>
|
||||
brew install <strong>sst/tap/opencode</strong>
|
||||
brew install <strong>opencode</strong>
|
||||
</span>
|
||||
<CopyStatus />
|
||||
</button>
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,4 @@
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +104,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
-49
@@ -4,9 +4,6 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
|
||||
p {
|
||||
color: var(--color-danger);
|
||||
@@ -24,27 +21,116 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="payment"] {
|
||||
[data-slot="section-content"] {
|
||||
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);
|
||||
min-width: 14.5rem;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
[data-slot="balance-display"] {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-3);
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
[data-slot="balance-amount"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
padding: var(--space-4);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg-surface);
|
||||
align-self: stretch;
|
||||
|
||||
[data-slot="balance-label"] {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-muted);
|
||||
margin-top: var(--space-2);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
[data-slot="balance-value"] {
|
||||
font-size: var(--font-size-2xl);
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="balance-right-section"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[data-slot="add-balance-form-container"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
[data-slot="add-balance-form"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
|
||||
label {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
color: var(--color-text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
input[data-component="input"] {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-accent);
|
||||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="form-actions"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="form-error"] {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
[data-slot="credit-card"] {
|
||||
padding: var(--space-3-5) var(--space-4);
|
||||
padding: var(--space-2) var(--space-4);
|
||||
background-color: var(--color-bg-surface);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-3);
|
||||
min-width: 150px;
|
||||
align-self: flex-start;
|
||||
|
||||
[data-slot="card-icon"] {
|
||||
display: flex;
|
||||
@@ -56,19 +142,19 @@
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: var(--space-1);
|
||||
flex: 1;
|
||||
justify-content: flex-end;
|
||||
|
||||
[data-slot="secret"] {
|
||||
position: relative;
|
||||
bottom: 2px;
|
||||
font-size: var(--font-size-lg);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-muted);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
[data-slot="number"] {
|
||||
font-size: var(--font-size-3xl);
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
[data-slot="type"] {
|
||||
@@ -77,41 +163,23 @@
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="button-row"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
align-items: center;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
flex-direction: column;
|
||||
|
||||
>button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Make Enable Billing button full width when it's the only button */
|
||||
>button {
|
||||
flex: 1;
|
||||
}
|
||||
button {
|
||||
align-self: flex-start;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="usage"] {
|
||||
p {
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-secondary);
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
[data-slot="enable-billing-button"] {
|
||||
align-self: flex-start;
|
||||
padding: var(--space-4);
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,86 @@
|
||||
import { json, query, action, useParams, useAction, createAsync, useSubmission } from "@solidjs/router"
|
||||
import { createMemo, Match, Show, Switch } from "solid-js"
|
||||
import { action, useParams, useAction, createAsync, useSubmission, json } from "@solidjs/router"
|
||||
import { createMemo, Match, Show, Switch, createEffect } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { IconCreditCard, IconStripe } from "~/component/icon"
|
||||
import styles from "./billing-section.module.css"
|
||||
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||
import { createCheckoutUrl } from "../../common"
|
||||
|
||||
const reload = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
return json(await withActor(() => Billing.reload(), workspaceID), { revalidate: getBillingInfo.key })
|
||||
}, "billing.reload")
|
||||
|
||||
const setReload = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
const reload = form.get("reload")?.toString() === "true"
|
||||
return json(
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
reload,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID)),
|
||||
),
|
||||
{ revalidate: getBillingInfo.key },
|
||||
)
|
||||
}, "billing.setReload")
|
||||
import { createCheckoutUrl, formatBalance, queryBillingInfo } from "../../common"
|
||||
|
||||
const createSessionUrl = action(async (workspaceID: string, returnUrl: string) => {
|
||||
"use server"
|
||||
return withActor(() => Billing.generateSessionUrl({ returnUrl }), workspaceID)
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
Billing.generateSessionUrl({ returnUrl })
|
||||
.then((data) => ({ error: undefined, data }))
|
||||
.catch((e) => ({
|
||||
error: e.message as string,
|
||||
data: undefined,
|
||||
})),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: queryBillingInfo.key },
|
||||
)
|
||||
}, "sessionUrl")
|
||||
|
||||
const getBillingInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
return await Billing.get()
|
||||
}, workspaceID)
|
||||
}, "billing.get")
|
||||
|
||||
export function BillingSection() {
|
||||
const params = useParams()
|
||||
// ORIGINAL CODE - COMMENTED OUT FOR TESTING
|
||||
const balanceInfo = createAsync(() => getBillingInfo(params.id))
|
||||
const createCheckoutUrlAction = useAction(createCheckoutUrl)
|
||||
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
||||
const createSessionUrlAction = useAction(createSessionUrl)
|
||||
const createSessionUrlSubmission = useSubmission(createSessionUrl)
|
||||
const setReloadSubmission = useSubmission(setReload)
|
||||
const reloadSubmission = useSubmission(reload)
|
||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
||||
const checkoutAction = useAction(createCheckoutUrl)
|
||||
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
||||
const sessionAction = useAction(createSessionUrl)
|
||||
const sessionSubmission = useSubmission(createSessionUrl)
|
||||
const [store, setStore] = createStore({
|
||||
showAddBalanceForm: false,
|
||||
addBalanceAmount: billingInfo()?.reloadAmount.toString() ?? "",
|
||||
checkoutRedirecting: false,
|
||||
sessionRedirecting: false,
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
const info = billingInfo()
|
||||
if (info) {
|
||||
setStore("addBalanceAmount", info.reloadAmount.toString())
|
||||
}
|
||||
})
|
||||
const balance = createMemo(() => formatBalance(billingInfo()?.balance ?? 0))
|
||||
|
||||
async function onClickCheckout() {
|
||||
const amount = parseInt(store.addBalanceAmount)
|
||||
const baseUrl = window.location.href
|
||||
|
||||
const checkout = await checkoutAction(params.id, amount, baseUrl, baseUrl)
|
||||
if (checkout && checkout.data) {
|
||||
setStore("checkoutRedirecting", true)
|
||||
window.location.href = checkout.data
|
||||
}
|
||||
}
|
||||
|
||||
async function onClickSession() {
|
||||
const baseUrl = window.location.href
|
||||
const sessionUrl = await sessionAction(params.id, baseUrl)
|
||||
if (sessionUrl && sessionUrl.data) {
|
||||
setStore("sessionRedirecting", true)
|
||||
window.location.href = sessionUrl.data
|
||||
}
|
||||
}
|
||||
|
||||
function showAddBalanceForm() {
|
||||
while (true) {
|
||||
checkoutSubmission.clear()
|
||||
if (!checkoutSubmission.result) break
|
||||
}
|
||||
setStore({
|
||||
showAddBalanceForm: true,
|
||||
})
|
||||
}
|
||||
|
||||
function hideAddBalanceForm() {
|
||||
setStore("showAddBalanceForm", false)
|
||||
checkoutSubmission.clear()
|
||||
}
|
||||
|
||||
// DUMMY DATA FOR TESTING - UNCOMMENT ONE OF THE SCENARIOS BELOW
|
||||
|
||||
@@ -108,14 +134,6 @@ export function BillingSection() {
|
||||
// timeReloadError: null as Date | null
|
||||
// })
|
||||
|
||||
const balanceAmount = createMemo(() => {
|
||||
return ((balanceInfo()?.balance ?? 0) / 100000000).toFixed(2)
|
||||
})
|
||||
|
||||
const hasBalance = createMemo(() => {
|
||||
return (balanceInfo()?.balance ?? 0) > 0 && balanceAmount() !== "0.00"
|
||||
})
|
||||
|
||||
return (
|
||||
<section class={styles.root}>
|
||||
<div data-slot="section-title">
|
||||
@@ -125,130 +143,97 @@ export function BillingSection() {
|
||||
</p>
|
||||
</div>
|
||||
<div data-slot="section-content">
|
||||
<Show when={balanceInfo()?.reloadError}>
|
||||
<div data-slot="reload-error">
|
||||
<p>
|
||||
Reload failed at{" "}
|
||||
{balanceInfo()?.timeReloadError!.toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
})}
|
||||
. Reason: {balanceInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment method and try
|
||||
again.
|
||||
</p>
|
||||
<form action={reload} method="post" data-slot="create-form">
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<button data-color="primary" type="submit" disabled={reloadSubmission.pending}>
|
||||
{reloadSubmission.pending ? "Reloading..." : "Reload"}
|
||||
</button>
|
||||
</form>
|
||||
<div data-slot="balance-display">
|
||||
<div data-slot="balance-amount">
|
||||
<span data-slot="balance-value">${balance()}</span>
|
||||
<span data-slot="balance-label">Current Balance</span>
|
||||
</div>
|
||||
</Show>
|
||||
<div data-slot="payment">
|
||||
<div data-slot="credit-card">
|
||||
<div data-slot="card-icon">
|
||||
<Switch fallback={<IconCreditCard style={{ width: "32px", height: "32px" }} />}>
|
||||
<Match when={balanceInfo()?.paymentMethodType === "link"}>
|
||||
<IconStripe style={{ width: "32px", height: "32px" }} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<div data-slot="card-details">
|
||||
<Switch
|
||||
<Show when={billingInfo()?.customerID}>
|
||||
<div data-slot="balance-right-section">
|
||||
<Show
|
||||
when={!store.showAddBalanceForm}
|
||||
fallback={
|
||||
<Show when={balanceInfo()?.paymentMethodLast4} fallback={<span data-slot="number">----</span>}>
|
||||
<span data-slot="secret">••••</span>
|
||||
<span data-slot="number">{balanceInfo()?.paymentMethodLast4}</span>
|
||||
</Show>
|
||||
<div data-slot="add-balance-form-container">
|
||||
<div data-slot="add-balance-form">
|
||||
<label>Add $</label>
|
||||
<input
|
||||
data-component="input"
|
||||
type="number"
|
||||
min={billingInfo()?.reloadAmountMin.toString()}
|
||||
step="1"
|
||||
value={store.addBalanceAmount}
|
||||
onInput={(e) => {
|
||||
setStore("addBalanceAmount", e.currentTarget.value)
|
||||
checkoutSubmission.clear()
|
||||
}}
|
||||
placeholder="Enter amount"
|
||||
/>
|
||||
<div data-slot="form-actions">
|
||||
<button data-color="ghost" type="button" onClick={() => hideAddBalanceForm()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
data-color="primary"
|
||||
type="button"
|
||||
disabled={!store.addBalanceAmount || checkoutSubmission.pending || store.checkoutRedirecting}
|
||||
onClick={onClickCheckout}
|
||||
>
|
||||
{checkoutSubmission.pending || store.checkoutRedirecting ? "Loading..." : "Add"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={checkoutSubmission.result && (checkoutSubmission.result as any).error}>
|
||||
{(err: any) => <div data-slot="form-error">{err()}</div>}
|
||||
</Show>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Match when={balanceInfo()?.paymentMethodType === "link"}>
|
||||
<span data-slot="type">Linked to Stripe</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="button-row">
|
||||
<Show
|
||||
when={balanceInfo()?.reload}
|
||||
fallback={
|
||||
<Show
|
||||
when={hasBalance()}
|
||||
fallback={
|
||||
<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..." : "Enable Billing"}
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<form action={setReload} method="post" data-slot="create-form">
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<input type="hidden" name="reload" value="true" />
|
||||
<button data-color="primary" type="submit" disabled={setReloadSubmission.pending}>
|
||||
{setReloadSubmission.pending ? "Enabling..." : "Enable Billing"}
|
||||
</button>
|
||||
</form>
|
||||
</Show>
|
||||
}
|
||||
>
|
||||
<button
|
||||
data-color="primary"
|
||||
disabled={createSessionUrlSubmission.pending}
|
||||
onClick={async () => {
|
||||
const baseUrl = window.location.href
|
||||
const sessionUrl = await createSessionUrlAction(params.id, baseUrl)
|
||||
if (sessionUrl) {
|
||||
window.location.href = sessionUrl
|
||||
}
|
||||
}}
|
||||
>
|
||||
{createSessionUrlSubmission.pending ? "Loading..." : "Manage Payment Methods"}
|
||||
</button>
|
||||
<form action={setReload} method="post" data-slot="create-form">
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<input type="hidden" name="reload" value="false" />
|
||||
<button data-color="ghost" type="submit" disabled={setReloadSubmission.pending}>
|
||||
{setReloadSubmission.pending ? "Disabling..." : "Disable"}
|
||||
<button data-color="primary" onClick={() => showAddBalanceForm()}>
|
||||
Add Balance
|
||||
</button>
|
||||
</form>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
<div data-slot="usage">
|
||||
<Show when={!balanceInfo()?.reload}>
|
||||
<Show
|
||||
when={hasBalance()}
|
||||
fallback={
|
||||
<p>
|
||||
We'll load <b>$20</b> (+$1.23 processing fee) and reload it when it reaches <b>$5</b>.
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
You have <b data-slot="value">${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()}</b> remaining in
|
||||
your account. You can continue using the API with your remaining balance.
|
||||
</p>
|
||||
</Show>
|
||||
</Show>
|
||||
<Show when={balanceInfo()?.reload && !balanceInfo()?.reloadError}>
|
||||
<p>
|
||||
Your current balance is <b data-slot="value">${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()}</b>
|
||||
. We'll automatically reload <b>$20</b> (+$1.23 processing fee) when it reaches <b>$5</b>.
|
||||
</p>
|
||||
</Show>
|
||||
<div data-slot="credit-card">
|
||||
<div data-slot="card-icon">
|
||||
<Switch fallback={<IconCreditCard style={{ width: "24px", height: "24px" }} />}>
|
||||
<Match when={billingInfo()?.paymentMethodType === "link"}>
|
||||
<IconStripe style={{ width: "24px", height: "24px" }} />
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<div data-slot="card-details">
|
||||
<Switch>
|
||||
<Match when={billingInfo()?.paymentMethodType === "card"}>
|
||||
<Show when={billingInfo()?.paymentMethodLast4} fallback={<span data-slot="number">----</span>}>
|
||||
<span data-slot="secret">••••</span>
|
||||
<span data-slot="number">{billingInfo()?.paymentMethodLast4}</span>
|
||||
</Show>
|
||||
</Match>
|
||||
<Match when={billingInfo()?.paymentMethodType === "link"}>
|
||||
<span data-slot="type">Linked to Stripe</span>
|
||||
</Match>
|
||||
</Switch>
|
||||
</div>
|
||||
<button
|
||||
data-color="ghost"
|
||||
disabled={sessionSubmission.pending || store.sessionRedirecting}
|
||||
onClick={onClickSession}
|
||||
>
|
||||
{sessionSubmission.pending || store.sessionRedirecting ? "Loading..." : "Manage"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={!billingInfo()?.customerID}>
|
||||
<button
|
||||
data-slot="enable-billing-button"
|
||||
data-color="primary"
|
||||
disabled={checkoutSubmission.pending || store.checkoutRedirecting}
|
||||
onClick={onClickCheckout}
|
||||
>
|
||||
{checkoutSubmission.pending || store.checkoutRedirecting ? "Loading..." : "Enable Billing"}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { MonthlyLimitSection } from "./monthly-limit-section"
|
||||
import { BillingSection } from "./billing-section"
|
||||
import { ReloadSection } from "./reload-section"
|
||||
import { PaymentSection } from "./payment-section"
|
||||
import { Show } from "solid-js"
|
||||
import { createAsync, useParams } from "@solidjs/router"
|
||||
import { querySessionInfo } from "../../common"
|
||||
import { queryBillingInfo, querySessionInfo } from "../../common"
|
||||
|
||||
export default function () {
|
||||
const params = useParams()
|
||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
||||
|
||||
return (
|
||||
<div data-page="workspace-[id]">
|
||||
<div data-slot="sections">
|
||||
<Show when={userInfo()?.isAdmin}>
|
||||
<BillingSection />
|
||||
<MonthlyLimitSection />
|
||||
<PaymentSection />
|
||||
<Show when={billingInfo()?.customerID}>
|
||||
<ReloadSection />
|
||||
<MonthlyLimitSection />
|
||||
<PaymentSection />
|
||||
</Show>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -93,4 +93,4 @@
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { json, query, action, useParams, createAsync, useSubmission } from "@solidjs/router"
|
||||
import { json, action, useParams, createAsync, useSubmission } from "@solidjs/router"
|
||||
import { createEffect, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||
import styles from "./monthly-limit-section.module.css"
|
||||
|
||||
const getBillingInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
return await Billing.get()
|
||||
}, workspaceID)
|
||||
}, "billing.get")
|
||||
import { queryBillingInfo } from "../../common"
|
||||
|
||||
const setMonthlyLimit = action(async (form: FormData) => {
|
||||
"use server"
|
||||
@@ -28,7 +22,7 @@ const setMonthlyLimit = action(async (form: FormData) => {
|
||||
.catch((e) => ({ error: e.message as string })),
|
||||
workspaceID,
|
||||
),
|
||||
{ revalidate: getBillingInfo.key },
|
||||
{ revalidate: queryBillingInfo.key },
|
||||
)
|
||||
}, "billing.setMonthlyLimit")
|
||||
|
||||
@@ -36,7 +30,7 @@ export function MonthlyLimitSection() {
|
||||
const params = useParams()
|
||||
const submission = useSubmission(setMonthlyLimit)
|
||||
const [store, setStore] = createStore({ show: false })
|
||||
const balanceInfo = createAsync(() => getBillingInfo(params.id))
|
||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
||||
|
||||
let input: HTMLInputElement
|
||||
|
||||
@@ -68,13 +62,13 @@ export function MonthlyLimitSection() {
|
||||
<section class={styles.root}>
|
||||
<div data-slot="section-title">
|
||||
<h2>Monthly Limit</h2>
|
||||
<p>Set a monthly spending limit for your account.</p>
|
||||
<p>Set a monthly usage limit for your account.</p>
|
||||
</div>
|
||||
<div data-slot="section-content">
|
||||
<div data-slot="balance">
|
||||
<div data-slot="amount">
|
||||
{balanceInfo()?.monthlyLimit ? <span data-slot="currency">$</span> : null}
|
||||
<span data-slot="value">{balanceInfo()?.monthlyLimit ?? "-"}</span>
|
||||
{billingInfo()?.monthlyLimit ? <span data-slot="currency">$</span> : null}
|
||||
<span data-slot="value">{billingInfo()?.monthlyLimit ?? "-"}</span>
|
||||
</div>
|
||||
<Show
|
||||
when={!store.show}
|
||||
@@ -106,15 +100,15 @@ export function MonthlyLimitSection() {
|
||||
}
|
||||
>
|
||||
<button data-color="primary" onClick={() => show()}>
|
||||
{balanceInfo()?.monthlyLimit ? "Edit Limit" : "Set Limit"}
|
||||
{billingInfo()?.monthlyLimit ? "Edit Limit" : "Set Limit"}
|
||||
</button>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={balanceInfo()?.monthlyLimit} fallback={<p data-slot="usage-status">No spending limit set.</p>}>
|
||||
<Show when={billingInfo()?.monthlyLimit} fallback={<p data-slot="usage-status">No usage limit set.</p>}>
|
||||
<p data-slot="usage-status">
|
||||
Current usage for {new Date().toLocaleDateString("en-US", { month: "long", timeZone: "UTC" })} is $
|
||||
{(() => {
|
||||
const dateLastUsed = balanceInfo()?.timeMonthlyUsageUpdated
|
||||
const dateLastUsed = billingInfo()?.timeMonthlyUsageUpdated
|
||||
if (!dateLastUsed) return "0"
|
||||
|
||||
const current = new Date().toLocaleDateString("en-US", {
|
||||
@@ -128,7 +122,7 @@ export function MonthlyLimitSection() {
|
||||
timeZone: "UTC",
|
||||
})
|
||||
if (current !== lastUsed) return "0"
|
||||
return ((balanceInfo()?.monthlyUsage ?? 0) / 100000000).toFixed(2)
|
||||
return ((billingInfo()?.monthlyUsage ?? 0) / 100000000).toFixed(2)
|
||||
})()}
|
||||
.
|
||||
</p>
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
.root {
|
||||
[data-slot="title-row"] {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
|
||||
[data-slot="section-content"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
[data-slot="setting-row"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
|
||||
p {
|
||||
flex: 1;
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
color: var(--color-text-secondary);
|
||||
margin: 0;
|
||||
|
||||
b {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[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);
|
||||
margin-top: var(--space-4);
|
||||
|
||||
[data-slot="form-field"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
[data-slot="field-label"] {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: 500;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
[data-slot="toggle-container"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input[data-component="input"] {
|
||||
flex: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
font-family: var(--font-mono);
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="input-row"] {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--space-3);
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="input-field"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
flex: 1;
|
||||
|
||||
p {
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
color: var(--color-text-muted);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
input[data-component="input"] {
|
||||
flex: 1;
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
background-color: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.5;
|
||||
min-width: 0;
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-accent);
|
||||
box-shadow: 0 0 0 3px var(--color-accent-alpha);
|
||||
}
|
||||
|
||||
&::placeholder {
|
||||
color: var(--color-text-disabled);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background-color: var(--color-bg-surface);
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="field-with-connector"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
|
||||
[data-slot="field-connector"] {
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
input[data-component="input"] {
|
||||
flex: 1;
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="form-actions"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
|
||||
[data-slot="form-error"] {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.4;
|
||||
margin-top: calc(var(--space-1) * -1);
|
||||
}
|
||||
|
||||
[data-slot="model-toggle-label"] {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 2.5rem;
|
||||
height: 1.5rem;
|
||||
cursor: pointer;
|
||||
|
||||
input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: #ccc;
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0.125rem;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
background-color: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50%;
|
||||
transform: translateY(-50%);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
}
|
||||
|
||||
input:checked + span {
|
||||
background-color: #21ad0e;
|
||||
border-color: #148605;
|
||||
|
||||
&::before {
|
||||
transform: translateX(1rem) translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover span {
|
||||
box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.2);
|
||||
}
|
||||
|
||||
input:checked:hover + span {
|
||||
box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.3);
|
||||
}
|
||||
|
||||
&:has(input:disabled) {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input:disabled + span {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="reload-error"] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-4);
|
||||
margin-top: var(--space-4);
|
||||
|
||||
p {
|
||||
color: var(--color-danger);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
[data-slot="create-form"] {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
margin: 0;
|
||||
flex-shrink: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
import { json, action, useParams, createAsync, useSubmission } from "@solidjs/router"
|
||||
import { createEffect, Show } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||
import { Database, eq } from "@opencode-ai/console-core/drizzle/index.js"
|
||||
import { BillingTable } from "@opencode-ai/console-core/schema/billing.sql.js"
|
||||
import styles from "./reload-section.module.css"
|
||||
import { queryBillingInfo } from "../../common"
|
||||
|
||||
const reload = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
return json(await withActor(() => Billing.reload(), workspaceID), {
|
||||
revalidate: queryBillingInfo.key,
|
||||
})
|
||||
}, "billing.reload")
|
||||
|
||||
const setReload = action(async (form: FormData) => {
|
||||
"use server"
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
const reloadValue = form.get("reload")?.toString() === "true"
|
||||
const amountStr = form.get("reloadAmount")?.toString()
|
||||
const triggerStr = form.get("reloadTrigger")?.toString()
|
||||
|
||||
const reloadAmount = amountStr && amountStr.trim() !== "" ? parseInt(amountStr) : null
|
||||
const reloadTrigger = triggerStr && triggerStr.trim() !== "" ? parseInt(triggerStr) : null
|
||||
|
||||
if (reloadValue) {
|
||||
if (reloadAmount === null || reloadAmount < Billing.RELOAD_AMOUNT_MIN)
|
||||
return { error: `Reload amount must be at least $${Billing.RELOAD_AMOUNT_MIN}` }
|
||||
if (reloadTrigger === null || reloadTrigger < Billing.RELOAD_TRIGGER_MIN)
|
||||
return { error: `Balance trigger must be at least $${Billing.RELOAD_TRIGGER_MIN}` }
|
||||
}
|
||||
|
||||
return json(
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
reload: reloadValue,
|
||||
...(reloadAmount !== null ? { reloadAmount } : {}),
|
||||
...(reloadTrigger !== null ? { reloadTrigger } : {}),
|
||||
...(reloadValue
|
||||
? {
|
||||
reloadError: null,
|
||||
timeReloadError: null,
|
||||
}
|
||||
: {}),
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID)),
|
||||
),
|
||||
{ revalidate: queryBillingInfo.key },
|
||||
)
|
||||
}, "billing.setReload")
|
||||
|
||||
export function ReloadSection() {
|
||||
const params = useParams()
|
||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
||||
const setReloadSubmission = useSubmission(setReload)
|
||||
const reloadSubmission = useSubmission(reload)
|
||||
const [store, setStore] = createStore({
|
||||
show: false,
|
||||
reload: false,
|
||||
reloadAmount: "",
|
||||
reloadTrigger: "",
|
||||
})
|
||||
|
||||
createEffect(() => {
|
||||
if (!setReloadSubmission.pending && setReloadSubmission.result && !(setReloadSubmission.result as any).error) {
|
||||
setStore("show", false)
|
||||
}
|
||||
})
|
||||
|
||||
function show() {
|
||||
while (true) {
|
||||
setReloadSubmission.clear()
|
||||
if (!setReloadSubmission.result) break
|
||||
}
|
||||
const info = billingInfo()!
|
||||
setStore("show", true)
|
||||
setStore("reload", info.reload ? true : true)
|
||||
setStore("reloadAmount", info.reloadAmount.toString())
|
||||
setStore("reloadTrigger", info.reloadTrigger.toString())
|
||||
}
|
||||
|
||||
function hide() {
|
||||
setStore("show", false)
|
||||
}
|
||||
|
||||
return (
|
||||
<section class={styles.root}>
|
||||
<div data-slot="section-title">
|
||||
<h2>Auto Reload</h2>
|
||||
<div data-slot="title-row">
|
||||
<Show
|
||||
when={billingInfo()?.reload}
|
||||
fallback={
|
||||
<p>
|
||||
Auto reload is <b>disabled</b>. Enable to automatically reload when balance is low.
|
||||
</p>
|
||||
}
|
||||
>
|
||||
<p>
|
||||
Auto reload is <b>enabled</b>. We'll reload <b>${billingInfo()?.reloadAmount}</b> (+$1.23 processing fee)
|
||||
when balance reaches <b>${billingInfo()?.reloadTrigger}</b>.
|
||||
</p>
|
||||
</Show>
|
||||
<button data-color="primary" type="button" onClick={() => show()}>
|
||||
{billingInfo()?.reload ? "Edit" : "Enable"}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Show when={store.show}>
|
||||
<form action={setReload} method="post" data-slot="create-form">
|
||||
<div data-slot="form-field">
|
||||
<label>
|
||||
<span data-slot="field-label">Enable Auto Reload</span>
|
||||
<div data-slot="toggle-container">
|
||||
<label data-slot="model-toggle-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="reload"
|
||||
value="true"
|
||||
checked={store.reload}
|
||||
onChange={(e) => setStore("reload", e.currentTarget.checked)}
|
||||
/>
|
||||
<span></span>
|
||||
</label>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div data-slot="input-row">
|
||||
<div data-slot="input-field">
|
||||
<p>Reload $</p>
|
||||
<input
|
||||
data-component="input"
|
||||
name="reloadAmount"
|
||||
type="number"
|
||||
min={billingInfo()?.reloadAmountMin.toString()}
|
||||
step="1"
|
||||
value={store.reloadAmount}
|
||||
onInput={(e) => setStore("reloadAmount", e.currentTarget.value)}
|
||||
placeholder={billingInfo()?.reloadAmount.toString()}
|
||||
disabled={!store.reload}
|
||||
/>
|
||||
</div>
|
||||
<div data-slot="input-field">
|
||||
<p>When balance reaches $</p>
|
||||
<input
|
||||
data-component="input"
|
||||
name="reloadTrigger"
|
||||
type="number"
|
||||
min={billingInfo()?.reloadTriggerMin.toString()}
|
||||
step="1"
|
||||
value={store.reloadTrigger}
|
||||
onInput={(e) => setStore("reloadTrigger", e.currentTarget.value)}
|
||||
placeholder={billingInfo()?.reloadTrigger.toString()}
|
||||
disabled={!store.reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Show when={setReloadSubmission.result && (setReloadSubmission.result as any).error}>
|
||||
{(err: any) => <div data-slot="form-error">{err()}</div>}
|
||||
</Show>
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<div data-slot="form-actions">
|
||||
<button type="button" data-color="ghost" onClick={() => hide()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" data-color="primary" disabled={setReloadSubmission.pending}>
|
||||
{setReloadSubmission.pending ? "Saving..." : "Save"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</Show>
|
||||
<Show when={billingInfo()?.reload && billingInfo()?.reloadError}>
|
||||
<div data-slot="section-content">
|
||||
<div data-slot="reload-error">
|
||||
<p>
|
||||
Reload failed at{" "}
|
||||
{billingInfo()?.timeReloadError!.toLocaleString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
})}
|
||||
. Reason: {billingInfo()?.reloadError?.replace(/\.$/, "")}. Please update your payment method and try
|
||||
again.
|
||||
</p>
|
||||
<form action={reload} method="post" data-slot="create-form">
|
||||
<input type="hidden" name="workspaceID" value={params.id} />
|
||||
<button data-color="ghost" type="submit" disabled={reloadSubmission.pending}>
|
||||
{reloadSubmission.pending ? "Retrying..." : "Retry"}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Show>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@@ -1,22 +1,32 @@
|
||||
import { Show, createMemo } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { createAsync, useParams, useAction, useSubmission } from "@solidjs/router"
|
||||
import { NewUserSection } from "./new-user-section"
|
||||
import { UsageSection } from "./usage-section"
|
||||
import { ModelSection } from "./model-section"
|
||||
import { ProviderSection } from "./provider-section"
|
||||
import { IconLogo } from "~/component/icon"
|
||||
import { createAsync, useParams, useAction, useSubmission } from "@solidjs/router"
|
||||
import { querySessionInfo, queryBillingInfo, createCheckoutUrl } from "../common"
|
||||
import { Show, createMemo } from "solid-js"
|
||||
import { querySessionInfo, queryBillingInfo, createCheckoutUrl, formatBalance } from "../common"
|
||||
|
||||
export default function () {
|
||||
const params = useParams()
|
||||
const userInfo = createAsync(() => querySessionInfo(params.id))
|
||||
const billingInfo = createAsync(() => queryBillingInfo(params.id))
|
||||
const createCheckoutUrlAction = useAction(createCheckoutUrl)
|
||||
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
||||
|
||||
const balanceAmount = createMemo(() => {
|
||||
return ((billingInfo()?.balance ?? 0) / 100000000).toFixed(2)
|
||||
const checkoutAction = useAction(createCheckoutUrl)
|
||||
const checkoutSubmission = useSubmission(createCheckoutUrl)
|
||||
const [store, setStore] = createStore({
|
||||
checkoutRedirecting: false,
|
||||
})
|
||||
const balance = createMemo(() => formatBalance(billingInfo()?.balance ?? 0))
|
||||
|
||||
async function onClickCheckout() {
|
||||
const baseUrl = window.location.href
|
||||
const checkout = await checkoutAction(params.id, billingInfo()!.reloadAmount, baseUrl, baseUrl)
|
||||
if (checkout && checkout.data) {
|
||||
setStore("checkoutRedirecting", true)
|
||||
window.location.href = checkout.data
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div data-page="workspace-[id]">
|
||||
@@ -38,21 +48,15 @@ export default function () {
|
||||
<button
|
||||
data-color="primary"
|
||||
data-size="sm"
|
||||
disabled={createCheckoutUrlSubmission.pending}
|
||||
onClick={async () => {
|
||||
const baseUrl = window.location.href
|
||||
const checkoutUrl = await createCheckoutUrlAction(params.id, baseUrl, baseUrl)
|
||||
if (checkoutUrl) {
|
||||
window.location.href = checkoutUrl
|
||||
}
|
||||
}}
|
||||
disabled={checkoutSubmission.pending || store.checkoutRedirecting}
|
||||
onClick={onClickCheckout}
|
||||
>
|
||||
{createCheckoutUrlSubmission.pending ? "Loading..." : "Enable billing"}
|
||||
{checkoutSubmission.pending || store.checkoutRedirecting ? "Loading..." : "Enable billing"}
|
||||
</button>
|
||||
}
|
||||
>
|
||||
<span data-slot="balance">
|
||||
Current balance <b>${balanceAmount() === "-0.00" ? "0.00" : balanceAmount()}</b>
|
||||
Current balance <b>${balance()}</b>
|
||||
</span>
|
||||
</Show>
|
||||
</span>
|
||||
|
||||
@@ -171,7 +171,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
@@ -181,8 +180,7 @@
|
||||
th {
|
||||
&:nth-child(3)
|
||||
|
||||
/* Date */
|
||||
{
|
||||
/* Date */ {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -190,11 +188,10 @@
|
||||
td {
|
||||
&:nth-child(3)
|
||||
|
||||
/* Date */
|
||||
{
|
||||
/* Date */ {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const getModelsInfo = query(async (workspaceID: string) => {
|
||||
return withActor(async () => {
|
||||
return {
|
||||
all: Object.entries(ZenData.list().models)
|
||||
.filter(([id, _model]) => !["claude-3-5-haiku"].includes(id))
|
||||
.filter(([id, _model]) => !["claude-3-5-haiku", "minimax-m2"].includes(id))
|
||||
.filter(([id, _model]) => !id.startsWith("an-"))
|
||||
.sort(([_idA, modelA], [_idB, modelB]) => modelA.name.localeCompare(modelB.name))
|
||||
.map(([id, model]) => ({ id, name: model.name })),
|
||||
|
||||
@@ -140,4 +140,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
@@ -136,4 +135,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ const removeProvider = action(async (form: FormData) => {
|
||||
if (!provider) return { error: "Provider is required" }
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
return json(await withActor(() => Provider.remove({ provider }), workspaceID), { revalidate: listProviders.key })
|
||||
return json(await withActor(() => Provider.remove({ provider }), workspaceID), {
|
||||
revalidate: listProviders.key,
|
||||
})
|
||||
}, "provider.remove")
|
||||
|
||||
const saveProvider = action(async (form: FormData) => {
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
>button {
|
||||
> button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
>button[type="reset"] {
|
||||
> button[type="reset"] {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
@@ -91,4 +91,4 @@
|
||||
margin-top: calc(var(--space-1) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { Actor } from "@opencode-ai/console-core/actor.js"
|
||||
import { action, query } from "@solidjs/router"
|
||||
import { action, json, query } from "@solidjs/router"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { Billing } from "@opencode-ai/console-core/billing.js"
|
||||
import { User } from "@opencode-ai/console-core/user.js"
|
||||
@@ -34,6 +34,11 @@ export function formatDateUTC(date: Date) {
|
||||
return date.toLocaleDateString("en-US", options)
|
||||
}
|
||||
|
||||
export function formatBalance(amount: number) {
|
||||
const balance = ((amount ?? 0) / 100000000).toFixed(2)
|
||||
return balance === "-0.00" ? "0.00" : balance
|
||||
}
|
||||
|
||||
export async function getLastSeenWorkspaceID() {
|
||||
"use server"
|
||||
return withActor(async () => {
|
||||
@@ -67,12 +72,35 @@ export const querySessionInfo = query(async (workspaceID: string) => {
|
||||
}, workspaceID)
|
||||
}, "session.get")
|
||||
|
||||
export const createCheckoutUrl = action(async (workspaceID: string, successUrl: string, cancelUrl: string) => {
|
||||
"use server"
|
||||
return withActor(() => Billing.generateCheckoutUrl({ successUrl, cancelUrl }), workspaceID)
|
||||
}, "checkoutUrl")
|
||||
export const createCheckoutUrl = action(
|
||||
async (workspaceID: string, amount: number, successUrl: string, cancelUrl: string) => {
|
||||
"use server"
|
||||
return json(
|
||||
await withActor(
|
||||
() =>
|
||||
Billing.generateCheckoutUrl({ amount, successUrl, cancelUrl })
|
||||
.then((data) => ({ error: undefined, data }))
|
||||
.catch((e) => ({
|
||||
error: e.message as string,
|
||||
data: undefined,
|
||||
})),
|
||||
workspaceID,
|
||||
),
|
||||
)
|
||||
},
|
||||
"checkoutUrl",
|
||||
)
|
||||
|
||||
export const queryBillingInfo = query(async (workspaceID: string) => {
|
||||
"use server"
|
||||
return withActor(() => Billing.get(), workspaceID)
|
||||
return withActor(async () => {
|
||||
const billing = await Billing.get()
|
||||
return {
|
||||
...billing,
|
||||
reloadAmount: billing.reloadAmount ?? Billing.RELOAD_AMOUNT,
|
||||
reloadAmountMin: Billing.RELOAD_AMOUNT_MIN,
|
||||
reloadTrigger: billing.reloadTrigger ?? Billing.RELOAD_TRIGGER,
|
||||
reloadTriggerMin: Billing.RELOAD_TRIGGER_MIN,
|
||||
}
|
||||
}, workspaceID)
|
||||
}, "billing.get")
|
||||
|
||||
@@ -277,7 +277,7 @@ body {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
strong {
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: var(--color-text-strong);
|
||||
font-weight: 500;
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createAsync, query, redirect } from "@solidjs/router"
|
||||
import { Title, Meta, Link } from "@solidjs/meta"
|
||||
import { HttpHeader } from "@solidjs/start"
|
||||
import zenLogoLight from "../../asset/zen-ornate-light.svg"
|
||||
import { config } from "~/config"
|
||||
import zenLogoDark from "../../asset/zen-ornate-dark.svg"
|
||||
import compareVideo from "../../asset/lander/opencode-comparison-min.mp4"
|
||||
import compareVideoPoster from "../../asset/lander/opencode-comparison-poster.png"
|
||||
@@ -30,6 +31,7 @@ export default function Home() {
|
||||
<main data-page="zen">
|
||||
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
|
||||
<Title>OpenCode Zen | A curated set of reliable optimized models for coding agents</Title>
|
||||
<Link rel="canonical" href={`${config.baseUrl}/zen`} />
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
||||
<Meta property="og:image" content="/social-share-zen.png" />
|
||||
<Meta name="twitter:image" content="/social-share-zen.png" />
|
||||
@@ -42,7 +44,7 @@ export default function Home() {
|
||||
<div data-slot="hero-copy">
|
||||
<img data-slot="zen logo light" src={zenLogoLight} alt="zen logo light" />
|
||||
<img data-slot="zen logo dark" src={zenLogoDark} alt="zen logo dark" />
|
||||
<strong>Reliable optimized models for coding agents</strong>
|
||||
<h1>Reliable optimized models for coding agents</h1>
|
||||
<p>
|
||||
Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically
|
||||
for coding agents. No need to worry about inconsistent performance and quality, use validated models
|
||||
|
||||
@@ -3,3 +3,4 @@ export class CreditsError extends Error {}
|
||||
export class MonthlyLimitError extends Error {}
|
||||
export class UserLimitError extends Error {}
|
||||
export class ModelError extends Error {}
|
||||
export class RateLimitError extends Error {}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export type Format = "anthropic" | "openai" | "oa-compat"
|
||||
@@ -12,27 +12,26 @@ import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
|
||||
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
|
||||
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
|
||||
import { logger } from "./logger"
|
||||
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError } from "./error"
|
||||
import {
|
||||
createBodyConverter,
|
||||
createStreamPartConverter,
|
||||
createResponseConverter,
|
||||
} from "./provider/provider"
|
||||
import { Format } from "./format"
|
||||
import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
|
||||
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
|
||||
import { anthropicHelper } from "./provider/anthropic"
|
||||
import { openaiHelper } from "./provider/openai"
|
||||
import { oaCompatHelper } from "./provider/openai-compatible"
|
||||
import { createRateLimiter } from "./rateLimiter"
|
||||
|
||||
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
|
||||
type Model = ZenData["models"][string]
|
||||
|
||||
export async function handler(
|
||||
input: APIEvent,
|
||||
opts: {
|
||||
format: Format
|
||||
format: ZenData.Format
|
||||
parseApiKey: (headers: Headers) => string | undefined
|
||||
},
|
||||
) {
|
||||
type AuthInfo = Awaited<ReturnType<typeof authenticate>>
|
||||
type ModelInfo = Awaited<ReturnType<typeof validateModel>>
|
||||
type ProviderInfo = Awaited<ReturnType<typeof selectProvider>>
|
||||
|
||||
const FREE_WORKSPACES = [
|
||||
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
|
||||
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
|
||||
@@ -40,6 +39,7 @@ export async function handler(
|
||||
|
||||
try {
|
||||
const body = await input.request.json()
|
||||
const ip = input.request.headers.get("x-real-ip") ?? ""
|
||||
logger.metric({
|
||||
is_tream: !!body.stream,
|
||||
session: input.request.headers.get("x-opencode-session"),
|
||||
@@ -47,13 +47,11 @@ export async function handler(
|
||||
})
|
||||
const zenData = ZenData.list()
|
||||
const modelInfo = validateModel(zenData, body.model)
|
||||
const providerInfo = selectProvider(
|
||||
zenData,
|
||||
modelInfo,
|
||||
input.request.headers.get("x-real-ip") ?? "",
|
||||
)
|
||||
const providerInfo = selectProvider(zenData, modelInfo, ip)
|
||||
const authInfo = await authenticate(modelInfo, providerInfo)
|
||||
validateBilling(modelInfo, authInfo)
|
||||
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
|
||||
await rateLimiter?.check()
|
||||
validateBilling(authInfo, modelInfo)
|
||||
validateModelSettings(authInfo)
|
||||
updateProviderKey(authInfo, providerInfo)
|
||||
logger.metric({ provider: providerInfo.id })
|
||||
@@ -68,7 +66,7 @@ export async function handler(
|
||||
}),
|
||||
)
|
||||
logger.debug("REQUEST URL: " + reqUrl)
|
||||
logger.debug("REQUEST: " + reqBody)
|
||||
logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
|
||||
const res = await fetch(reqUrl, {
|
||||
method: "POST",
|
||||
headers: (() => {
|
||||
@@ -93,9 +91,6 @@ export async function handler(
|
||||
}
|
||||
}
|
||||
logger.debug("STATUS: " + res.status + " " + res.statusText)
|
||||
if (res.status === 400 || res.status === 503) {
|
||||
logger.debug("RESPONSE: " + (await res.text()))
|
||||
}
|
||||
|
||||
// Handle non-streaming response
|
||||
if (!body.stream) {
|
||||
@@ -104,6 +99,7 @@ export async function handler(
|
||||
const body = JSON.stringify(responseConverter(json))
|
||||
logger.metric({ response_length: body.length })
|
||||
logger.debug("RESPONSE: " + body)
|
||||
await rateLimiter?.track()
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, json.usage)
|
||||
await reload(authInfo)
|
||||
return new Response(body, {
|
||||
@@ -132,6 +128,7 @@ export async function handler(
|
||||
response_length: responseLength,
|
||||
"timestamp.last_byte": Date.now(),
|
||||
})
|
||||
await rateLimiter?.track()
|
||||
const usage = usageParser.retrieve()
|
||||
if (usage) {
|
||||
await trackUsage(authInfo, modelInfo, providerInfo, usage)
|
||||
@@ -206,6 +203,15 @@ export async function handler(
|
||||
{ status: 401 },
|
||||
)
|
||||
|
||||
if (error instanceof RateLimitError)
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
error: { type: error.constructor.name, message: error.message },
|
||||
}),
|
||||
{ status: 429 },
|
||||
)
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
type: "error",
|
||||
@@ -230,19 +236,15 @@ export async function handler(
|
||||
return { id: modelId, ...modelData }
|
||||
}
|
||||
|
||||
function selectProvider(
|
||||
zenData: ZenData,
|
||||
model: Awaited<ReturnType<typeof validateModel>>,
|
||||
ip: string,
|
||||
) {
|
||||
const providers = model.providers
|
||||
function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string) {
|
||||
const providers = modelInfo.providers
|
||||
.filter((provider) => !provider.disabled)
|
||||
.flatMap((provider) => Array<typeof provider>(provider.weight ?? 1).fill(provider))
|
||||
|
||||
// Use last character of IP address to select a provider
|
||||
const lastChar = ip.charCodeAt(ip.length - 1) || 0
|
||||
const index = lastChar % providers.length
|
||||
const provider = providers[index]
|
||||
// Use the last 2 characters of IP address to select a provider
|
||||
const lastChars = ip.slice(-2)
|
||||
const index = parseInt(lastChars, 16) % providers.length
|
||||
const provider = providers[index || 0]
|
||||
|
||||
if (!(provider.id in zenData.providers)) {
|
||||
throw new ModelError(`Provider ${provider.id} not supported`)
|
||||
@@ -251,21 +253,19 @@ export async function handler(
|
||||
return {
|
||||
...provider,
|
||||
...zenData.providers[provider.id],
|
||||
...(provider.id === "anthropic"
|
||||
? anthropicHelper
|
||||
: provider.id === "openai"
|
||||
? openaiHelper
|
||||
: oaCompatHelper),
|
||||
...(() => {
|
||||
const format = zenData.providers[provider.id].format
|
||||
if (format === "anthropic") return anthropicHelper
|
||||
if (format === "openai") return openaiHelper
|
||||
return oaCompatHelper
|
||||
})(),
|
||||
}
|
||||
}
|
||||
|
||||
async function authenticate(
|
||||
model: Awaited<ReturnType<typeof validateModel>>,
|
||||
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
|
||||
) {
|
||||
async function authenticate(modelInfo: ModelInfo, providerInfo: ProviderInfo) {
|
||||
const apiKey = opts.parseApiKey(input.request.headers)
|
||||
if (!apiKey) {
|
||||
if (model.allowAnonymous) return
|
||||
if (modelInfo.allowAnonymous) return
|
||||
throw new AuthError("Missing API key.")
|
||||
}
|
||||
|
||||
@@ -280,6 +280,7 @@ export async function handler(
|
||||
monthlyLimit: BillingTable.monthlyLimit,
|
||||
monthlyUsage: BillingTable.monthlyUsage,
|
||||
timeMonthlyUsageUpdated: BillingTable.timeMonthlyUsageUpdated,
|
||||
reloadTrigger: BillingTable.reloadTrigger,
|
||||
},
|
||||
user: {
|
||||
id: UserTable.id,
|
||||
@@ -295,20 +296,11 @@ export async function handler(
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID))
|
||||
.innerJoin(
|
||||
UserTable,
|
||||
and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)),
|
||||
)
|
||||
.leftJoin(
|
||||
ModelTable,
|
||||
and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, model.id)),
|
||||
)
|
||||
.innerJoin(UserTable, and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)))
|
||||
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, modelInfo.id)))
|
||||
.leftJoin(
|
||||
ProviderTable,
|
||||
and(
|
||||
eq(ProviderTable.workspaceID, KeyTable.workspaceID),
|
||||
eq(ProviderTable.provider, providerInfo.id),
|
||||
),
|
||||
and(eq(ProviderTable.workspaceID, KeyTable.workspaceID), eq(ProviderTable.provider, providerInfo.id)),
|
||||
)
|
||||
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
|
||||
.then((rows) => rows[0]),
|
||||
@@ -331,11 +323,11 @@ export async function handler(
|
||||
}
|
||||
}
|
||||
|
||||
function validateBilling(model: Model, authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
function validateBilling(authInfo: AuthInfo, modelInfo: ModelInfo) {
|
||||
if (!authInfo) return
|
||||
if (authInfo.provider?.credentials) return
|
||||
if (authInfo.isFree) return
|
||||
if (model.allowAnonymous) return
|
||||
if (modelInfo.allowAnonymous) return
|
||||
|
||||
const billing = authInfo.billing
|
||||
if (!billing.paymentMethodID)
|
||||
@@ -379,39 +371,24 @@ export async function handler(
|
||||
}
|
||||
}
|
||||
|
||||
function validateModelSettings(authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
function validateModelSettings(authInfo: AuthInfo) {
|
||||
if (!authInfo) return
|
||||
if (authInfo.isDisabled) throw new ModelError("Model is disabled")
|
||||
}
|
||||
|
||||
function updateProviderKey(
|
||||
authInfo: Awaited<ReturnType<typeof authenticate>>,
|
||||
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
|
||||
) {
|
||||
function updateProviderKey(authInfo: AuthInfo, providerInfo: ProviderInfo) {
|
||||
if (!authInfo) return
|
||||
if (!authInfo.provider?.credentials) return
|
||||
providerInfo.apiKey = authInfo.provider.credentials
|
||||
}
|
||||
|
||||
async function trackUsage(
|
||||
authInfo: Awaited<ReturnType<typeof authenticate>>,
|
||||
modelInfo: ReturnType<typeof validateModel>,
|
||||
providerInfo: Awaited<ReturnType<typeof selectProvider>>,
|
||||
usage: any,
|
||||
) {
|
||||
const {
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
reasoningTokens,
|
||||
cacheReadTokens,
|
||||
cacheWrite5mTokens,
|
||||
cacheWrite1hTokens,
|
||||
} = providerInfo.normalizeUsage(usage)
|
||||
async function trackUsage(authInfo: AuthInfo, modelInfo: ModelInfo, providerInfo: ProviderInfo, usage: any) {
|
||||
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
|
||||
providerInfo.normalizeUsage(usage)
|
||||
|
||||
const modelCost =
|
||||
modelInfo.cost200K &&
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) >
|
||||
200_000
|
||||
inputTokens + (cacheReadTokens ?? 0) + (cacheWrite5mTokens ?? 0) + (cacheWrite1hTokens ?? 0) > 200_000
|
||||
? modelInfo.cost200K
|
||||
: modelInfo.cost
|
||||
|
||||
@@ -462,8 +439,7 @@ export async function handler(
|
||||
|
||||
if (!authInfo) return
|
||||
|
||||
const cost =
|
||||
authInfo.isFree || authInfo.provider?.credentials ? 0 : centsToMicroCents(totalCostInCent)
|
||||
const cost = authInfo.isFree || authInfo.provider?.credentials ? 0 : centsToMicroCents(totalCostInCent)
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx.insert(UsageTable).values({
|
||||
workspaceID: authInfo.workspaceID,
|
||||
@@ -503,9 +479,7 @@ export async function handler(
|
||||
`,
|
||||
timeMonthlyUsageUpdated: sql`now()`,
|
||||
})
|
||||
.where(
|
||||
and(eq(UserTable.workspaceID, authInfo.workspaceID), eq(UserTable.id, authInfo.user.id)),
|
||||
)
|
||||
.where(and(eq(UserTable.workspaceID, authInfo.workspaceID), eq(UserTable.id, authInfo.user.id)))
|
||||
})
|
||||
|
||||
await Database.use((tx) =>
|
||||
@@ -516,7 +490,7 @@ export async function handler(
|
||||
)
|
||||
}
|
||||
|
||||
async function reload(authInfo: Awaited<ReturnType<typeof authenticate>>) {
|
||||
async function reload(authInfo: AuthInfo) {
|
||||
if (!authInfo) return
|
||||
if (authInfo.isFree) return
|
||||
if (authInfo.provider?.credentials) return
|
||||
@@ -531,11 +505,11 @@ export async function handler(
|
||||
and(
|
||||
eq(BillingTable.workspaceID, authInfo.workspaceID),
|
||||
eq(BillingTable.reload, true),
|
||||
lt(BillingTable.balance, centsToMicroCents(Billing.CHARGE_THRESHOLD)),
|
||||
or(
|
||||
isNull(BillingTable.timeReloadLockedTill),
|
||||
lt(BillingTable.timeReloadLockedTill, sql`now()`),
|
||||
lt(
|
||||
BillingTable.balance,
|
||||
centsToMicroCents((authInfo.billing.reloadTrigger ?? Billing.RELOAD_TRIGGER) * 100),
|
||||
),
|
||||
or(isNull(BillingTable.timeReloadLockedTill), lt(BillingTable.timeReloadLockedTill, sql`now()`)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -98,7 +98,10 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
typeof (src as any).media_type === "string" &&
|
||||
typeof (src as any).data === "string"
|
||||
)
|
||||
return { type: "image_url", image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` } }
|
||||
return {
|
||||
type: "image_url",
|
||||
image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -165,7 +168,11 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
.filter((t: any) => t && typeof t === "object" && "input_schema" in t)
|
||||
.map((t: any) => ({
|
||||
type: "function",
|
||||
function: { name: (t as any).name, description: (t as any).description, parameters: (t as any).input_schema },
|
||||
function: {
|
||||
name: (t as any).name,
|
||||
description: (t as any).description,
|
||||
parameters: (t as any).input_schema,
|
||||
},
|
||||
}))
|
||||
: undefined
|
||||
|
||||
@@ -452,7 +459,12 @@ export function toAnthropicResponse(resp: CommonResponse) {
|
||||
} catch {
|
||||
input = (tc as any).function.arguments
|
||||
}
|
||||
content.push({ type: "tool_use", id: (tc as any).id, name: (tc as any).function.name, input })
|
||||
content.push({
|
||||
type: "tool_use",
|
||||
id: (tc as any).id,
|
||||
name: (tc as any).function.name,
|
||||
input,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,13 +523,22 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
|
||||
if (json.type === "content_block_start") {
|
||||
const cb = json.content_block
|
||||
if (cb?.type === "text") {
|
||||
out.choices.push({ index: json.index ?? 0, delta: { role: "assistant", content: "" }, finish_reason: null })
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: { role: "assistant", content: "" },
|
||||
finish_reason: null,
|
||||
})
|
||||
} else if (cb?.type === "tool_use") {
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{ index: json.index ?? 0, id: cb.id, type: "function", function: { name: cb.name, arguments: "" } },
|
||||
{
|
||||
index: json.index ?? 0,
|
||||
id: cb.id,
|
||||
type: "function",
|
||||
function: { name: cb.name, arguments: "" },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
@@ -532,7 +553,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
|
||||
} else if (d?.type === "input_json_delta") {
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: { tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }] },
|
||||
delta: {
|
||||
tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }],
|
||||
},
|
||||
finish_reason: null,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -532,7 +532,9 @@ export function toOaCompatibleChunk(chunk: CommonChunk): string {
|
||||
total_tokens: chunk.usage.total_tokens,
|
||||
...(chunk.usage.prompt_tokens_details?.cached_tokens
|
||||
? {
|
||||
prompt_tokens_details: { cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens },
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
@@ -77,7 +77,10 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
typeof (s as any).media_type === "string" &&
|
||||
typeof (s as any).data === "string"
|
||||
)
|
||||
return { type: "image_url", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
|
||||
return {
|
||||
type: "image_url",
|
||||
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -153,7 +156,11 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
}
|
||||
|
||||
if ((m as any).role === "tool") {
|
||||
msgs.push({ role: "tool", tool_call_id: (m as any).tool_call_id, content: (m as any).content })
|
||||
msgs.push({
|
||||
role: "tool",
|
||||
tool_call_id: (m as any).tool_call_id,
|
||||
content: (m as any).content,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -210,7 +217,10 @@ export function toOpenaiRequest(body: CommonRequest) {
|
||||
typeof (s as any).media_type === "string" &&
|
||||
typeof (s as any).data === "string"
|
||||
)
|
||||
return { type: "input_image", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
|
||||
return {
|
||||
type: "input_image",
|
||||
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -498,7 +508,9 @@ export function fromOpenaiChunk(chunk: string): CommonChunk | string {
|
||||
if (typeof name === "string" && name.length > 0) {
|
||||
out.choices.push({
|
||||
index: 0,
|
||||
delta: { tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }] },
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }],
|
||||
},
|
||||
finish_reason: null,
|
||||
})
|
||||
}
|
||||
@@ -555,7 +567,12 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
const model = chunk.model
|
||||
|
||||
if (d.content) {
|
||||
const data = { id, type: "response.output_text.delta", delta: d.content, response: { id, model } }
|
||||
const data = {
|
||||
id,
|
||||
type: "response.output_text.delta",
|
||||
delta: d.content,
|
||||
response: { id, model },
|
||||
}
|
||||
return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
|
||||
@@ -565,7 +582,13 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
const data = {
|
||||
type: "response.output_item.added",
|
||||
output_index: 0,
|
||||
item: { id: tc.id, type: "function_call", name: tc.function.name, call_id: tc.id, arguments: "" },
|
||||
item: {
|
||||
id: tc.id,
|
||||
type: "function_call",
|
||||
name: tc.function.name,
|
||||
call_id: tc.id,
|
||||
arguments: "",
|
||||
},
|
||||
}
|
||||
return `event: response.output_item.added\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
@@ -593,7 +616,11 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
}
|
||||
: undefined
|
||||
|
||||
const data: any = { id, type: "response.completed", response: { id, model, ...(usage ? { usage } : {}) } }
|
||||
const data: any = {
|
||||
id,
|
||||
type: "response.completed",
|
||||
response: { id, model, ...(usage ? { usage } : {}) },
|
||||
}
|
||||
return `event: response.completed\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Format } from "../format"
|
||||
|
||||
import { ZenData } from "@opencode-ai/console-core/model.js"
|
||||
import {
|
||||
fromAnthropicChunk,
|
||||
fromAnthropicRequest,
|
||||
@@ -26,7 +25,7 @@ import {
|
||||
} from "./openai-compatible"
|
||||
|
||||
export type ProviderHelper = {
|
||||
format: Format
|
||||
format: ZenData.Format
|
||||
modifyUrl: (providerApi: string) => string
|
||||
modifyHeaders: (headers: Headers, body: Record<string, any>, apiKey: string) => void
|
||||
modifyBody: (body: Record<string, any>) => Record<string, any>
|
||||
@@ -158,7 +157,7 @@ export interface CommonChunk {
|
||||
}
|
||||
}
|
||||
|
||||
export function createBodyConverter(from: Format, to: Format) {
|
||||
export function createBodyConverter(from: ZenData.Format, to: ZenData.Format) {
|
||||
return (body: any): any => {
|
||||
if (from === to) return body
|
||||
|
||||
@@ -173,7 +172,7 @@ export function createBodyConverter(from: Format, to: Format) {
|
||||
}
|
||||
}
|
||||
|
||||
export function createStreamPartConverter(from: Format, to: Format) {
|
||||
export function createStreamPartConverter(from: ZenData.Format, to: ZenData.Format) {
|
||||
return (part: any): any => {
|
||||
if (from === to) return part
|
||||
|
||||
@@ -191,7 +190,7 @@ export function createStreamPartConverter(from: Format, to: Format) {
|
||||
}
|
||||
}
|
||||
|
||||
export function createResponseConverter(from: Format, to: Format) {
|
||||
export function createResponseConverter(from: ZenData.Format, to: ZenData.Format) {
|
||||
return (response: any): any => {
|
||||
if (from === to) return response
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Resource } from "@opencode-ai/console-resource"
|
||||
import { RateLimitError } from "./error"
|
||||
import { logger } from "./logger"
|
||||
|
||||
export function createRateLimiter(model: string, limit: number | undefined, ip: string) {
|
||||
if (!limit) return
|
||||
|
||||
const now = Date.now()
|
||||
const currKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now)}`
|
||||
const prevKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now - 3_600_000)}`
|
||||
let currRate: number
|
||||
let prevRate: number
|
||||
|
||||
return {
|
||||
track: async () => {
|
||||
await Resource.GatewayKv.put(currKey, currRate + 1, { expirationTtl: 3600 })
|
||||
},
|
||||
check: async () => {
|
||||
const values = await Resource.GatewayKv.get([currKey, prevKey])
|
||||
const prevValue = values?.get(prevKey)
|
||||
const currValue = values?.get(currKey)
|
||||
prevRate = prevValue ? parseInt(prevValue) : 0
|
||||
currRate = currValue ? parseInt(currValue) : 0
|
||||
logger.debug(`rate limit ${model} prev/curr: ${prevRate}/${currRate}`)
|
||||
if (prevRate + currRate >= limit) throw new RateLimitError(`Rate limit exceeded. Please try again later.`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function buildYYYYMMDDHH(timestamp: number) {
|
||||
return new Date(timestamp)
|
||||
.toISOString()
|
||||
.replace(/[^0-9]/g, "")
|
||||
.substring(0, 10)
|
||||
}
|
||||
@@ -50,10 +50,7 @@ export async function GET(input: APIEvent) {
|
||||
})
|
||||
.from(KeyTable)
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
|
||||
.leftJoin(
|
||||
ModelTable,
|
||||
and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)),
|
||||
)
|
||||
.leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), isNull(ModelTable.timeDeleted)))
|
||||
.where(and(eq(KeyTable.key, apiKey), isNull(KeyTable.timeDeleted)))
|
||||
.then((rows) => rows.map((row) => row.model)),
|
||||
)
|
||||
|
||||
Vendored
+1
-1
@@ -6,4 +6,4 @@
|
||||
/// <reference path="../../../sst-env.d.ts" />
|
||||
|
||||
import "sst"
|
||||
export {}
|
||||
export {}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `billing` ADD `reload_trigger` int;--> statement-breakpoint
|
||||
ALTER TABLE `billing` ADD `reload_amount` int;
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -599,10 +578,7 @@
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -610,10 +586,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -670,9 +643,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -680,9 +651,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -699,4 +668,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -599,10 +578,7 @@
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -610,10 +586,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -670,9 +643,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -680,9 +651,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -699,4 +668,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -592,10 +571,7 @@
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -603,10 +579,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -663,9 +636,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -673,9 +644,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -692,4 +661,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -599,10 +578,7 @@
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -610,10 +586,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -670,9 +643,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -680,9 +651,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -699,4 +668,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -613,18 +592,12 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -632,10 +605,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -692,9 +662,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -702,9 +670,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -721,4 +687,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -613,32 +592,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -646,10 +615,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -706,9 +672,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -716,9 +680,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -735,4 +697,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,17 +473,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -504,10 +486,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -599,32 +578,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -632,10 +601,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -692,9 +658,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -702,9 +666,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -721,4 +683,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -493,17 +480,12 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"name"
|
||||
],
|
||||
"columns": ["workspace_id", "name"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -511,10 +493,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -606,32 +585,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -639,10 +608,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -699,9 +665,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -709,9 +673,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -728,4 +690,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -493,9 +480,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -503,10 +488,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -598,32 +580,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -631,10 +603,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -691,9 +660,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -701,9 +668,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -720,4 +685,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -479,9 +466,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -489,10 +474,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -584,32 +566,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -617,10 +589,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -677,9 +646,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -687,9 +654,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -706,4 +671,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -479,9 +466,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -489,10 +474,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -584,32 +566,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -617,10 +589,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -677,9 +646,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -687,9 +654,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -706,4 +671,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -479,9 +466,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -489,10 +474,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -605,32 +587,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -638,10 +610,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -698,9 +667,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -708,9 +675,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -727,4 +692,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -479,9 +466,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -489,10 +474,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -549,10 +531,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -560,10 +539,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -676,32 +652,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -709,10 +675,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -769,9 +732,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -779,9 +740,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -798,4 +757,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -398,10 +388,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -479,9 +466,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -489,10 +474,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -549,10 +531,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -560,10 +539,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -627,10 +603,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -638,10 +611,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -754,32 +724,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -787,10 +747,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -847,9 +804,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -857,9 +812,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -876,4 +829,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -180,9 +178,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -190,10 +186,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -280,10 +273,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -405,10 +395,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -486,9 +473,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -496,10 +481,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -556,10 +538,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -567,10 +546,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -634,10 +610,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -645,10 +618,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -761,32 +731,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -794,10 +754,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -854,9 +811,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -864,9 +819,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -883,4 +836,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -187,9 +185,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -197,10 +193,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -287,10 +280,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -412,10 +402,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -493,9 +480,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -503,10 +488,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -563,10 +545,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -574,10 +553,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -641,10 +617,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -652,10 +625,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -768,32 +738,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -801,10 +761,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -861,9 +818,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -871,9 +826,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -890,4 +843,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -117,10 +115,7 @@
|
||||
"indexes": {
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": [
|
||||
"provider",
|
||||
"subject"
|
||||
],
|
||||
"columns": ["provider", "subject"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -257,9 +252,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -267,10 +260,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -357,10 +347,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -482,10 +469,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -563,9 +547,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -573,10 +555,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -633,10 +612,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -644,10 +620,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -711,10 +684,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -722,10 +692,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -838,32 +805,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -871,10 +828,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -931,9 +885,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -941,9 +893,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -960,4 +910,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,17 +102,12 @@
|
||||
"indexes": {
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": [
|
||||
"provider",
|
||||
"subject"
|
||||
],
|
||||
"columns": ["provider", "subject"],
|
||||
"isUnique": true
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -249,9 +244,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -259,10 +252,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -349,10 +339,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -474,10 +461,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -555,9 +539,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -565,10 +547,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -625,10 +604,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -636,10 +612,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -703,10 +676,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -714,10 +684,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -830,32 +797,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -863,10 +820,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -923,9 +877,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -933,9 +885,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -952,4 +902,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,9 +43,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"account_id_pk": {
|
||||
"name": "account_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -109,17 +107,12 @@
|
||||
"indexes": {
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": [
|
||||
"provider",
|
||||
"subject"
|
||||
],
|
||||
"columns": ["provider", "subject"],
|
||||
"isUnique": true
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -127,9 +120,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"auth_id_pk": {
|
||||
"name": "auth_id_pk",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -263,9 +254,7 @@
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": [
|
||||
"customer_id"
|
||||
],
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -273,10 +262,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -363,10 +349,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -488,10 +471,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -569,9 +549,7 @@
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"columns": ["key"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -579,10 +557,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -639,10 +614,7 @@
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"model"
|
||||
],
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -650,10 +622,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -717,10 +686,7 @@
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"provider"
|
||||
],
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -728,10 +694,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -844,32 +807,22 @@
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": [
|
||||
"account_id"
|
||||
],
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
@@ -877,10 +830,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -937,9 +887,7 @@
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"columns": ["slug"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
@@ -947,9 +895,7 @@
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
@@ -966,4 +912,4 @@
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,929 @@
|
||||
{
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "8b7fa839-a088-408e-84a4-1a07325c0290",
|
||||
"prevId": "5470c8b4-296d-47bd-85a7-88cfd3b71434",
|
||||
"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
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"account_id_pk": {
|
||||
"name": "account_id_pk",
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"auth": {
|
||||
"name": "auth",
|
||||
"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
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "enum('email','github','google')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"subject": {
|
||||
"name": "subject",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"columns": ["provider", "subject"],
|
||||
"isUnique": true
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"auth_id_pk": {
|
||||
"name": "auth_id_pk",
|
||||
"columns": ["id"]
|
||||
}
|
||||
},
|
||||
"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_type": {
|
||||
"name": "payment_method_type",
|
||||
"type": "varchar(32)",
|
||||
"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
|
||||
},
|
||||
"monthly_limit": {
|
||||
"name": "monthly_limit",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_usage": {
|
||||
"name": "monthly_usage",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_monthly_usage_updated": {
|
||||
"name": "time_monthly_usage_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_trigger": {
|
||||
"name": "reload_trigger",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_amount": {
|
||||
"name": "reload_amount",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload_error": {
|
||||
"name": "reload_error",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_reload_error": {
|
||||
"name": "time_reload_error",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_reload_locked_till": {
|
||||
"name": "time_reload_locked_till",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_customer_id": {
|
||||
"name": "global_customer_id",
|
||||
"columns": ["customer_id"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"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
|
||||
},
|
||||
"invoice_id": {
|
||||
"name": "invoice_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
|
||||
},
|
||||
"time_refunded": {
|
||||
"name": "time_refunded",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"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
|
||||
},
|
||||
"key_id": {
|
||||
"name": "key_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"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
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(30)",
|
||||
"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": {}
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"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(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"model_workspace_model": {
|
||||
"name": "model_workspace_model",
|
||||
"columns": ["workspace_id", "model"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"model_workspace_id_id_pk": {
|
||||
"name": "model_workspace_id_id_pk",
|
||||
"columns": ["workspace_id", "id"]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"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
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"credentials": {
|
||||
"name": "credentials",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"workspace_provider": {
|
||||
"name": "workspace_provider",
|
||||
"columns": ["workspace_id", "provider"],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"provider_workspace_id_id_pk": {
|
||||
"name": "provider_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
|
||||
},
|
||||
"account_id": {
|
||||
"name": "account_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"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
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "enum('admin','member')",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_limit": {
|
||||
"name": "monthly_limit",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"monthly_usage": {
|
||||
"name": "monthly_usage",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_monthly_usage_updated": {
|
||||
"name": "time_monthly_usage_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_account_id": {
|
||||
"name": "user_account_id",
|
||||
"columns": ["workspace_id", "account_id"],
|
||||
"isUnique": true
|
||||
},
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": ["workspace_id", "email"],
|
||||
"isUnique": true
|
||||
},
|
||||
"global_account_id": {
|
||||
"name": "global_account_id",
|
||||
"columns": ["account_id"],
|
||||
"isUnique": false
|
||||
},
|
||||
"global_email": {
|
||||
"name": "global_email",
|
||||
"columns": ["email"],
|
||||
"isUnique": false
|
||||
}
|
||||
},
|
||||
"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": 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
|
||||
}
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
}
|
||||
@@ -260,6 +260,13 @@
|
||||
"when": 1760668952329,
|
||||
"tag": "0036_slimy_energizer",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 37,
|
||||
"version": "5",
|
||||
"when": 1761928273807,
|
||||
"tag": "0037_messy_jackal",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/console-core",
|
||||
"version": "0.15.30",
|
||||
"version": "1.0.55",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
@@ -14,7 +14,7 @@
|
||||
"drizzle-orm": "0.41.0",
|
||||
"postgres": "3.4.7",
|
||||
"stripe": "18.0.0",
|
||||
"ulid": "3.0.0",
|
||||
"ulid": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"exports": {
|
||||
|
||||
@@ -8,22 +8,15 @@ if (!email) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const authData = await printTable("Auth", (tx) =>
|
||||
tx.select().from(AuthTable).where(eq(AuthTable.subject, email)),
|
||||
)
|
||||
const authData = await printTable("Auth", (tx) => tx.select().from(AuthTable).where(eq(AuthTable.subject, email)))
|
||||
if (authData.length === 0) {
|
||||
console.error("User not found")
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
await printTable("Auth", (tx) =>
|
||||
tx.select().from(AuthTable).where(eq(AuthTable.accountID, authData[0].accountID)),
|
||||
)
|
||||
await printTable("Auth", (tx) => tx.select().from(AuthTable).where(eq(AuthTable.accountID, authData[0].accountID)))
|
||||
|
||||
function printTable(
|
||||
title: string,
|
||||
callback: (tx: Database.TxOrDb) => Promise<any[]>,
|
||||
): Promise<any[]> {
|
||||
function printTable(title: string, callback: (tx: Database.TxOrDb) => Promise<any[]>): Promise<any[]> {
|
||||
return Database.use(async (tx) => {
|
||||
const data = await callback(tx)
|
||||
console.log(`== ${title} ==`)
|
||||
|
||||
@@ -11,14 +11,20 @@ const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
|
||||
// read the secret
|
||||
const ret = await $`bun sst secret list`.cwd(root).text()
|
||||
const value = ret
|
||||
const value1 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS"))
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
if (!value) throw new Error("ZEN_MODELS not found")
|
||||
const value2 = ret
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!value1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!value2) throw new Error("ZEN_MODELS2 not found")
|
||||
|
||||
// validate value
|
||||
ZenData.validate(JSON.parse(value))
|
||||
ZenData.validate(JSON.parse(value1 + value2))
|
||||
|
||||
// update the secret
|
||||
await $`bun sst secret set ZEN_MODELS ${value} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS1 ${value1} --stage ${stage}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${value2} --stage ${stage}`
|
||||
|
||||
@@ -8,14 +8,6 @@ import { KeyTable } from "../src/schema/key.sql.js"
|
||||
|
||||
if (Resource.App.stage !== "frank") throw new Error("This script is only for frank")
|
||||
|
||||
for (const table of [
|
||||
AccountTable,
|
||||
BillingTable,
|
||||
KeyTable,
|
||||
PaymentTable,
|
||||
UsageTable,
|
||||
UserTable,
|
||||
WorkspaceTable,
|
||||
]) {
|
||||
for (const table of [AccountTable, BillingTable, KeyTable, PaymentTable, UsageTable, UserTable, WorkspaceTable]) {
|
||||
await Database.use((tx) => tx.delete(table))
|
||||
}
|
||||
|
||||
@@ -7,26 +7,31 @@ import { ZenData } from "../src/model"
|
||||
|
||||
const root = path.resolve(process.cwd(), "..", "..", "..")
|
||||
const models = await $`bun sst secret list`.cwd(root).text()
|
||||
console.log("models", models)
|
||||
|
||||
// read the line starting with "ZEN_MODELS"
|
||||
const oldValue = models
|
||||
const oldValue1 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS"))
|
||||
.find((line) => line.startsWith("ZEN_MODELS1"))
|
||||
?.split("=")[1]
|
||||
if (!oldValue) throw new Error("ZEN_MODELS not found")
|
||||
console.log("oldValue", oldValue)
|
||||
const oldValue2 = models
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("ZEN_MODELS2"))
|
||||
?.split("=")[1]
|
||||
if (!oldValue1) throw new Error("ZEN_MODELS1 not found")
|
||||
if (!oldValue2) throw new Error("ZEN_MODELS2 not found")
|
||||
|
||||
// store the prettified json to a temp file
|
||||
const filename = `models-${Date.now()}.json`
|
||||
const tempFile = Bun.file(path.join(os.tmpdir(), filename))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue), null, 2))
|
||||
await tempFile.write(JSON.stringify(JSON.parse(oldValue1 + oldValue2), null, 2))
|
||||
console.log("tempFile", tempFile.name)
|
||||
|
||||
// open temp file in vim and read the file on close
|
||||
await $`vim ${tempFile.name}`
|
||||
const newValue = JSON.parse(await tempFile.text())
|
||||
ZenData.validate(newValue)
|
||||
const newValue = JSON.stringify(JSON.parse(await tempFile.text()))
|
||||
ZenData.validate(JSON.parse(newValue))
|
||||
|
||||
// update the secret
|
||||
await $`bun sst secret set ZEN_MODELS ${JSON.stringify(newValue)}`
|
||||
const mid = Math.floor(newValue.length / 2)
|
||||
await $`bun sst secret set ZEN_MODELS1 ${newValue.slice(0, mid)}`
|
||||
await $`bun sst secret set ZEN_MODELS2 ${newValue.slice(mid)}`
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user