Compare commits
70
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6de122ddc | ||
|
|
0f8cb69bff | ||
|
|
fca2bddc3b | ||
|
|
f65e20b8ce | ||
|
|
93f2805bc2 | ||
|
|
9ad4dc9296 | ||
|
|
23af974bd3 | ||
|
|
36ea46ee67 | ||
|
|
4d2cc9d858 | ||
|
|
610ffbdd61 | ||
|
|
854f9227a2 | ||
|
|
8d368fdfd2 | ||
|
|
1c31c2dd97 | ||
|
|
c1d754bec9 | ||
|
|
c67b721787 | ||
|
|
11e41e7564 | ||
|
|
afd42bf46d | ||
|
|
f740663ded | ||
|
|
751b81af34 | ||
|
|
b725bcd2cd | ||
|
|
c278e16e4e | ||
|
|
4e629c5b64 | ||
|
|
4624f0a260 | ||
|
|
2e16d685eb | ||
|
|
e544cccc70 | ||
|
|
c141b88087 | ||
|
|
023c4532c1 | ||
|
|
042802848d | ||
|
|
a8aa44bd3f | ||
|
|
db2a3a171e | ||
|
|
38a4bee1be | ||
|
|
8952b3d246 | ||
|
|
d6350a7fa6 | ||
|
|
ae83138832 | ||
|
|
3ee4280dfa | ||
|
|
26fbf9e647 | ||
|
|
97a41062c9 | ||
|
|
4a76224268 | ||
|
|
810c9cff1d | ||
|
|
47d4c87bdd | ||
|
|
a9875c5531 | ||
|
|
4c261ab1db | ||
|
|
2fc8263032 | ||
|
|
a431b8922c | ||
|
|
0a01d20850 | ||
|
|
7b62c10553 | ||
|
|
61c7196bd9 | ||
|
|
365fdd9ff8 | ||
|
|
f6bc9238df | ||
|
|
26f75d4e68 | ||
|
|
8ba8d3c7e3 | ||
|
|
f993541e0b | ||
|
|
e2df3eb44d | ||
|
|
38f9ce05f6 | ||
|
|
a6e09363b8 | ||
|
|
49629bb58e | ||
|
|
2bb5b9b13a | ||
|
|
41338d1bf9 | ||
|
|
41ee9c94c7 | ||
|
|
9c16db0f36 | ||
|
|
721869353b | ||
|
|
6d22ade771 | ||
|
|
fbcceeb781 | ||
|
|
95775d68b7 | ||
|
|
cf11669618 | ||
|
|
65dc19e85a | ||
|
|
cfcfceca6d | ||
|
|
9f8899a9f9 | ||
|
|
449a063fe2 | ||
|
|
37530359ee |
@@ -24,4 +24,6 @@ jobs:
|
||||
- run: bun sst deploy --stage=${{ github.ref_name }}
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
PLANETSCALE_SERVICE_TOKEN_NAME: ${{ secrets.PLANETSCALE_SERVICE_TOKEN_NAME }}
|
||||
PLANETSCALE_SERVICE_TOKEN: ${{ secrets.PLANETSCALE_SERVICE_TOKEN }}
|
||||
STRIPE_SECRET_KEY: ${{ github.ref_name == 'production' && secrets.STRIPE_SECRET_KEY_PROD || secrets.STRIPE_SECRET_KEY_DEV }}
|
||||
|
||||
@@ -65,3 +65,6 @@
|
||||
| 2025-08-29 | 256,045 (+3,249) | 211,075 (+5,833) | 467,120 (+9,082) |
|
||||
| 2025-08-30 | 258,863 (+2,818) | 212,397 (+1,322) | 471,260 (+4,140) |
|
||||
| 2025-08-31 | 262,004 (+3,141) | 213,944 (+1,547) | 475,948 (+4,688) |
|
||||
| 2025-09-01 | 265,359 (+3,355) | 215,115 (+1,171) | 480,474 (+4,526) |
|
||||
| 2025-09-02 | 270,483 (+5,124) | 217,075 (+1,960) | 487,558 (+7,084) |
|
||||
| 2025-09-03 | 274,793 (+4,310) | 219,755 (+2,680) | 494,548 (+6,990) |
|
||||
|
||||
@@ -26,10 +26,11 @@
|
||||
},
|
||||
"cloud/core": {
|
||||
"name": "@opencode/cloud-core",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@opencode/cloud-resource": "workspace:*",
|
||||
"@planetscale/database": "1.19.0",
|
||||
"drizzle-orm": "0.41.0",
|
||||
"postgres": "3.4.7",
|
||||
"stripe": "18.0.0",
|
||||
@@ -37,11 +38,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"drizzle-kit": "0.30.5",
|
||||
"mysql2": "3.14.4",
|
||||
},
|
||||
},
|
||||
"cloud/function": {
|
||||
"name": "@opencode/cloud-function",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "2.0.0",
|
||||
"@ai-sdk/openai": "2.0.2",
|
||||
@@ -67,7 +69,7 @@
|
||||
},
|
||||
"packages/function": {
|
||||
"name": "@opencode/function",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@octokit/auth-app": "8.0.1",
|
||||
"@octokit/rest": "22.0.0",
|
||||
@@ -82,7 +84,7 @@
|
||||
},
|
||||
"packages/opencode": {
|
||||
"name": "opencode",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"bin": {
|
||||
"opencode": "./bin/opencode",
|
||||
},
|
||||
@@ -132,7 +134,7 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@opencode-ai/sdk": "workspace:*",
|
||||
},
|
||||
@@ -143,7 +145,7 @@
|
||||
},
|
||||
"packages/sdk/js": {
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@hey-api/openapi-ts": "0.81.0",
|
||||
},
|
||||
@@ -155,7 +157,7 @@
|
||||
},
|
||||
"packages/web": {
|
||||
"name": "@opencode/web",
|
||||
"version": "0.5.28",
|
||||
"version": "0.6.3",
|
||||
"dependencies": {
|
||||
"@astrojs/cloudflare": "12.6.3",
|
||||
"@astrojs/markdown-remark": "6.3.1",
|
||||
@@ -194,6 +196,9 @@
|
||||
"web-tree-sitter",
|
||||
"tree-sitter-bash",
|
||||
],
|
||||
"patchedDependencies": {
|
||||
"@solidjs/start@1.1.7": "patches/@solidjs%2Fstart@1.1.7.patch",
|
||||
},
|
||||
"overrides": {
|
||||
"zod": "3.25.76",
|
||||
},
|
||||
@@ -747,6 +752,8 @@
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="],
|
||||
|
||||
"@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="],
|
||||
|
||||
"@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="],
|
||||
@@ -1161,6 +1168,8 @@
|
||||
|
||||
"aws-sdk": ["aws-sdk@2.1692.0", "", { "dependencies": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", "util": "^0.12.4", "uuid": "8.0.0", "xml2js": "0.6.2" } }, "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw=="],
|
||||
|
||||
"aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="],
|
||||
|
||||
"aws4fetch": ["aws4fetch@1.0.18", "", {}, "sha512-3Cf+YaUl07p24MoQ46rFwulAmiyCwH2+1zw1ZyPAX5OtJ34Hh185DwB8y/qRLb6cYYYtSFJ9pthyLc0MD4e8sQ=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
@@ -1631,6 +1640,8 @@
|
||||
|
||||
"gel": ["gel@2.1.1", "", { "dependencies": { "@petamoriken/float16": "^3.8.7", "debug": "^4.3.4", "env-paths": "^3.0.0", "semver": "^7.6.2", "shell-quote": "^1.8.1", "which": "^4.0.0" }, "bin": { "gel": "dist/cli.mjs" } }, "sha512-Newg9X7mRYskoBjSw70l1YnJ/ZGbq64VPyR821H5WVkTGpHG2O0mQILxCeUhxdYERLFY9B4tUyKLyf3uMTjtKw=="],
|
||||
|
||||
"generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="],
|
||||
|
||||
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
|
||||
|
||||
"get-amd-module-type": ["get-amd-module-type@6.0.1", "", { "dependencies": { "ast-module-types": "^6.0.1", "node-source-walk": "^7.0.1" } }, "sha512-MtjsmYiCXcYDDrGqtNbeIYdAl85n+5mSv2r3FbzER/YV3ZILw4HNNIw34HuV5pyl0jzs6GFYU1VHVEefhgcNHQ=="],
|
||||
@@ -1769,7 +1780,7 @@
|
||||
|
||||
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
|
||||
|
||||
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
"iconv-lite": ["iconv-lite@0.7.0", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ=="],
|
||||
|
||||
"ieee754": ["ieee754@1.1.13", "", {}, "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="],
|
||||
|
||||
@@ -1845,6 +1856,8 @@
|
||||
|
||||
"is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="],
|
||||
|
||||
"is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="],
|
||||
|
||||
"is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="],
|
||||
|
||||
"is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
|
||||
@@ -1957,6 +1970,8 @@
|
||||
|
||||
"lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="],
|
||||
|
||||
"lru.min": ["lru.min@1.1.2", "", {}, "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg=="],
|
||||
|
||||
"luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.18", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ=="],
|
||||
@@ -2145,6 +2160,10 @@
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"mysql2": ["mysql2@3.14.4", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-Cs/jx3WZPNrYHVz+Iunp9ziahaG5uFMvD2R8Zlmc194AqXNxt9HBNu7ZsPYrUtmJsF0egETCWIdMIYAwOGjL1w=="],
|
||||
|
||||
"named-placeholders": ["named-placeholders@1.1.3", "", { "dependencies": { "lru-cache": "^7.14.1" } }, "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
@@ -2523,6 +2542,8 @@
|
||||
|
||||
"send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="],
|
||||
|
||||
"seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="],
|
||||
|
||||
"serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="],
|
||||
|
||||
"seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="],
|
||||
@@ -2607,6 +2628,8 @@
|
||||
|
||||
"sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
|
||||
|
||||
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
|
||||
|
||||
"ssri": ["ssri@10.0.6", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ=="],
|
||||
|
||||
"sst": ["sst@3.17.12", "", { "dependencies": { "aws-sdk": "2.1692.0", "aws4fetch": "1.0.18", "jose": "5.2.3", "opencontrol": "0.0.6", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "3.17.12", "sst-darwin-x64": "3.17.12", "sst-linux-arm64": "3.17.12", "sst-linux-x64": "3.17.12", "sst-linux-x86": "3.17.12", "sst-win32-arm64": "3.17.12", "sst-win32-x64": "3.17.12", "sst-win32-x86": "3.17.12" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-UwUbucNZRLp9GHgPAwkat1sBsNGaJfHsLXZHCMKsolCW7CEuugJfvBl2vOyJrhKP4N+Xnv1QFh0BGsOmN0kQeA=="],
|
||||
@@ -3113,6 +3136,8 @@
|
||||
|
||||
"bl/readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"body-parser/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
|
||||
|
||||
"cacheable-request/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
|
||||
@@ -3129,6 +3154,8 @@
|
||||
|
||||
"drizzle-kit/esbuild": ["esbuild@0.19.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.19.12", "@esbuild/android-arm": "0.19.12", "@esbuild/android-arm64": "0.19.12", "@esbuild/android-x64": "0.19.12", "@esbuild/darwin-arm64": "0.19.12", "@esbuild/darwin-x64": "0.19.12", "@esbuild/freebsd-arm64": "0.19.12", "@esbuild/freebsd-x64": "0.19.12", "@esbuild/linux-arm": "0.19.12", "@esbuild/linux-arm64": "0.19.12", "@esbuild/linux-ia32": "0.19.12", "@esbuild/linux-loong64": "0.19.12", "@esbuild/linux-mips64el": "0.19.12", "@esbuild/linux-ppc64": "0.19.12", "@esbuild/linux-riscv64": "0.19.12", "@esbuild/linux-s390x": "0.19.12", "@esbuild/linux-x64": "0.19.12", "@esbuild/netbsd-x64": "0.19.12", "@esbuild/openbsd-x64": "0.19.12", "@esbuild/sunos-x64": "0.19.12", "@esbuild/win32-arm64": "0.19.12", "@esbuild/win32-ia32": "0.19.12", "@esbuild/win32-x64": "0.19.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg=="],
|
||||
|
||||
"encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"estree-util-to-js/source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="],
|
||||
|
||||
"express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
|
||||
@@ -3187,6 +3214,8 @@
|
||||
|
||||
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
|
||||
"named-placeholders/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="],
|
||||
|
||||
"netlify/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"nitropack/c12": ["c12@3.2.0", "", { "dependencies": { "chokidar": "^4.0.3", "confbox": "^0.2.2", "defu": "^6.1.4", "dotenv": "^17.2.1", "exsolve": "^1.0.7", "giget": "^2.0.0", "jiti": "^2.5.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.2.0", "rc9": "^2.1.2" }, "peerDependencies": { "magicast": "^0.3.5" }, "optionalPeers": ["magicast"] }, "sha512-ixkEtbYafL56E6HiFuonMm1ZjoKtIo7TH68/uiEq4DAwv9NcUX2nJ95F8TrbMeNjqIkZpruo3ojXQJ+MGG5gcQ=="],
|
||||
@@ -3243,6 +3272,8 @@
|
||||
|
||||
"prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="],
|
||||
|
||||
"raw-body/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
|
||||
|
||||
"rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"read-pkg/unicorn-magic": ["unicorn-magic@0.1.0", "", {}, "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ=="],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { defineConfig } from "@solidjs/start/config"
|
||||
|
||||
export default defineConfig({
|
||||
middleware: "./src/middleware.ts",
|
||||
vite: {
|
||||
server: {
|
||||
allowedHosts: true,
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
"name": "@opencode/cloud-app",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit",
|
||||
"dev": "vinxi dev --host 0.0.0.0",
|
||||
"dev:remote": "VITE_AUTH_URL=https://auth.dev.opencode.ai bun sst shell --stage=dev bun dev",
|
||||
"build": "vinxi build",
|
||||
"start": "vinxi start",
|
||||
"version": "0.5.29"
|
||||
"version": "0.6.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ibm/plex": "6.4.1",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
# Disallow shared content pages
|
||||
Disallow: /s/
|
||||
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"$schema": {
|
||||
"type": "string",
|
||||
"description": "JSON schema reference for configuration validation"
|
||||
},
|
||||
"defs": {
|
||||
"type": "object",
|
||||
"description": "Color definitions that can be referenced in the theme",
|
||||
"patternProperties": {
|
||||
"^[a-zA-Z][a-zA-Z0-9_]*$": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$",
|
||||
"description": "Hex color value"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "ANSI color code (0-255)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["none"],
|
||||
"description": "No color (uses terminal default)"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"theme": {
|
||||
"type": "object",
|
||||
"description": "Theme color definitions",
|
||||
"properties": {
|
||||
"primary": { "$ref": "#/definitions/colorValue" },
|
||||
"secondary": { "$ref": "#/definitions/colorValue" },
|
||||
"accent": { "$ref": "#/definitions/colorValue" },
|
||||
"error": { "$ref": "#/definitions/colorValue" },
|
||||
"warning": { "$ref": "#/definitions/colorValue" },
|
||||
"success": { "$ref": "#/definitions/colorValue" },
|
||||
"info": { "$ref": "#/definitions/colorValue" },
|
||||
"text": { "$ref": "#/definitions/colorValue" },
|
||||
"textMuted": { "$ref": "#/definitions/colorValue" },
|
||||
"background": { "$ref": "#/definitions/colorValue" },
|
||||
"backgroundPanel": { "$ref": "#/definitions/colorValue" },
|
||||
"backgroundElement": { "$ref": "#/definitions/colorValue" },
|
||||
"border": { "$ref": "#/definitions/colorValue" },
|
||||
"borderActive": { "$ref": "#/definitions/colorValue" },
|
||||
"borderSubtle": { "$ref": "#/definitions/colorValue" },
|
||||
"diffAdded": { "$ref": "#/definitions/colorValue" },
|
||||
"diffRemoved": { "$ref": "#/definitions/colorValue" },
|
||||
"diffContext": { "$ref": "#/definitions/colorValue" },
|
||||
"diffHunkHeader": { "$ref": "#/definitions/colorValue" },
|
||||
"diffHighlightAdded": { "$ref": "#/definitions/colorValue" },
|
||||
"diffHighlightRemoved": { "$ref": "#/definitions/colorValue" },
|
||||
"diffAddedBg": { "$ref": "#/definitions/colorValue" },
|
||||
"diffRemovedBg": { "$ref": "#/definitions/colorValue" },
|
||||
"diffContextBg": { "$ref": "#/definitions/colorValue" },
|
||||
"diffLineNumber": { "$ref": "#/definitions/colorValue" },
|
||||
"diffAddedLineNumberBg": { "$ref": "#/definitions/colorValue" },
|
||||
"diffRemovedLineNumberBg": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownText": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownHeading": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownLink": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownLinkText": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownCode": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownBlockQuote": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownEmph": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownStrong": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownHorizontalRule": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownListItem": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownListEnumeration": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownImage": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownImageText": { "$ref": "#/definitions/colorValue" },
|
||||
"markdownCodeBlock": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxComment": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxKeyword": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxFunction": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxVariable": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxString": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxNumber": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxType": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxOperator": { "$ref": "#/definitions/colorValue" },
|
||||
"syntaxPunctuation": { "$ref": "#/definitions/colorValue" }
|
||||
},
|
||||
"required": ["primary", "secondary", "accent", "text", "textMuted", "background"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["theme"],
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"colorValue": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$",
|
||||
"description": "Hex color value (same for dark and light)"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "ANSI color code (0-255, same for dark and light)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["none"],
|
||||
"description": "No color (uses terminal default)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
|
||||
"description": "Reference to another color in the theme or defs"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"dark": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$",
|
||||
"description": "Hex color value for dark mode"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "ANSI color code for dark mode"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["none"],
|
||||
"description": "No color (uses terminal default)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
|
||||
"description": "Reference to another color for dark mode"
|
||||
}
|
||||
]
|
||||
},
|
||||
"light": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^#[0-9a-fA-F]{6}$",
|
||||
"description": "Hex color value for light mode"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255,
|
||||
"description": "ANSI color code for light mode"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"enum": ["none"],
|
||||
"description": "No color (uses terminal default)"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"pattern": "^[a-zA-Z][a-zA-Z0-9_]*$",
|
||||
"description": "Reference to another color for light mode"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": ["dark", "light"],
|
||||
"additionalProperties": false,
|
||||
"description": "Separate colors for dark and light modes"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useSession } from "vinxi/http"
|
||||
|
||||
export interface AuthSession {
|
||||
account: Record<
|
||||
account?: Record<
|
||||
string,
|
||||
{
|
||||
id: string
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getRequestEvent } from "solid-js/web"
|
||||
import { and, Database, eq, inArray } from "@opencode/cloud-core/drizzle/index.js"
|
||||
import { WorkspaceTable } from "@opencode/cloud-core/schema/workspace.sql.js"
|
||||
import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
|
||||
import { query, redirect } from "@solidjs/router"
|
||||
import { redirect } from "@solidjs/router"
|
||||
import { AccountTable } from "@opencode/cloud-core/schema/account.sql.js"
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
|
||||
@@ -14,16 +14,17 @@ export const AuthClient = createClient({
|
||||
issuer: import.meta.env.VITE_AUTH_URL,
|
||||
})
|
||||
|
||||
export const getActor = query(async (): Promise<Actor.Info> => {
|
||||
export const getActor = async (): Promise<Actor.Info> => {
|
||||
"use server"
|
||||
const evt = getRequestEvent()
|
||||
if (!evt) throw new Error("No request event")
|
||||
const url = new URL(evt.request.headers.has("x-server-id") ? evt.request.headers.get("referer")! : evt.request.url)
|
||||
const auth = await useAuthSession()
|
||||
auth.data.account = auth.data.account ?? {}
|
||||
const splits = url.pathname.split("/").filter(Boolean)
|
||||
if (splits[0] !== "workspace") {
|
||||
if (auth.data.current) {
|
||||
const current = auth.data.account[auth.data.current]
|
||||
const current = auth.data.account[auth.data.current ?? ""]
|
||||
if (current) {
|
||||
return {
|
||||
type: "account",
|
||||
properties: {
|
||||
@@ -52,28 +53,30 @@ export const getActor = query(async (): Promise<Actor.Info> => {
|
||||
}
|
||||
}
|
||||
const workspaceHint = splits[1]
|
||||
const accounts = Object.keys(auth.data.account)
|
||||
const result = await Database.transaction(async (tx) => {
|
||||
return await tx
|
||||
.select({
|
||||
user: UserTable,
|
||||
})
|
||||
.from(AccountTable)
|
||||
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
||||
.where(and(inArray(AccountTable.id, accounts), eq(WorkspaceTable.id, workspaceHint)))
|
||||
.limit(1)
|
||||
.execute()
|
||||
.then((x) => x[0])
|
||||
})
|
||||
if (result) {
|
||||
return {
|
||||
type: "user",
|
||||
properties: {
|
||||
userID: result.user.id,
|
||||
workspaceID: result.user.workspaceID,
|
||||
},
|
||||
const accounts = Object.keys(auth.data.account ?? {})
|
||||
if (accounts.length) {
|
||||
const result = await Database.transaction(async (tx) => {
|
||||
return await tx
|
||||
.select({
|
||||
user: UserTable,
|
||||
})
|
||||
.from(AccountTable)
|
||||
.innerJoin(UserTable, and(eq(UserTable.email, AccountTable.email)))
|
||||
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, UserTable.workspaceID))
|
||||
.where(and(inArray(AccountTable.id, accounts), eq(WorkspaceTable.id, workspaceHint)))
|
||||
.limit(1)
|
||||
.execute()
|
||||
.then((x) => x[0])
|
||||
})
|
||||
if (result) {
|
||||
return {
|
||||
type: "user",
|
||||
properties: {
|
||||
userID: result.user.id,
|
||||
workspaceID: result.user.workspaceID,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
throw redirect("/auth/authorize")
|
||||
}, "actor")
|
||||
}
|
||||
|
||||
@@ -1,16 +1,7 @@
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
import { getActor } from "./auth"
|
||||
import { query } from "@solidjs/router"
|
||||
|
||||
export async function withActor<T>(fn: () => T) {
|
||||
const actor = await getActor()
|
||||
return Actor.provide(actor.type, actor.properties, fn)
|
||||
}
|
||||
|
||||
export function actorQuery<T>(cb: () => T, name: string) {
|
||||
"use server"
|
||||
return query(async () => {
|
||||
const actor = await getActor()
|
||||
return withActor(cb)
|
||||
}, name)
|
||||
}
|
||||
|
||||
@@ -20,4 +20,6 @@ export default createHandler(() => (
|
||||
</html>
|
||||
)}
|
||||
/>
|
||||
))
|
||||
), {
|
||||
mode: "async",
|
||||
})
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { defineMiddleware } from "vinxi/http"
|
||||
|
||||
export default defineMiddleware({
|
||||
onBeforeResponse() {},
|
||||
})
|
||||
@@ -27,5 +27,5 @@ export async function GET(input: APIEvent) {
|
||||
current: id,
|
||||
}
|
||||
})
|
||||
return redirect("/")
|
||||
return redirect("/auth")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Account } from "@opencode/cloud-core/account.js"
|
||||
import { redirect } from "@solidjs/router"
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
try {
|
||||
const workspaces = await withActor(async () => Account.workspaces())
|
||||
return redirect(`/workspace/${workspaces[0].id}`)
|
||||
} catch {
|
||||
return redirect("/auth/authorize")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { AuthClient } from "~/context/auth"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
|
||||
return Response.redirect(result.url, 302)
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
import { json } from "@solidjs/router"
|
||||
import { Database } from "@opencode/cloud-core/drizzle/index.js"
|
||||
import { UserTable } from "@opencode/cloud-core/schema/user.sql.js"
|
||||
|
||||
export async function GET(evt: APIEvent) {
|
||||
return json({
|
||||
data: Resource.Database.host,
|
||||
data: await Database.use(async (tx) => {
|
||||
const result = await tx.$count(UserTable)
|
||||
return result
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
|
||||
async function handler(evt: APIEvent) {
|
||||
const req = evt.request.clone()
|
||||
const url = new URL(req.url)
|
||||
const targetUrl = `https://docs.opencode.ai${url.pathname}${url.search}`
|
||||
const response = await fetch(targetUrl, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
})
|
||||
return response
|
||||
}
|
||||
|
||||
export const GET = handler
|
||||
export const POST = handler
|
||||
export const PUT = handler
|
||||
export const DELETE = handler
|
||||
export const OPTIONS = handler
|
||||
export const PATCH = handler
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
|
||||
async function handler(evt: APIEvent) {
|
||||
const req = evt.request.clone()
|
||||
const url = new URL(req.url)
|
||||
const targetUrl = `https://docs.opencode.ai${url.pathname}${url.search}`
|
||||
const response = await fetch(targetUrl, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
})
|
||||
return response
|
||||
}
|
||||
|
||||
export const GET = handler
|
||||
export const POST = handler
|
||||
export const PUT = handler
|
||||
export const DELETE = handler
|
||||
export const OPTIONS = handler
|
||||
export const PATCH = handler
|
||||
@@ -1,576 +1,305 @@
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
import { Billing } from "@opencode/cloud-core/billing.js"
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
import { Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js"
|
||||
import { BillingTable, PaymentTable } from "@opencode/cloud-core/schema/billing.sql.js"
|
||||
import { Identifier } from "@opencode/cloud-core/identifier.js"
|
||||
import { centsToMicroCents } from "@opencode/cloud-core/util/price.js"
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
|
||||
import { BillingTable, UsageTable } from "@opencode/cloud-core/schema/billing.sql.js"
|
||||
import { centsToMicroCents } from "@opencode/cloud-core/util/price.js"
|
||||
import { Identifier } from "@opencode/cloud-core/identifier.js"
|
||||
|
||||
const SUPPORTED_MODELS = {
|
||||
const MODELS = {
|
||||
// "anthropic/claude-sonnet-4": {
|
||||
// input: 0.0000015,
|
||||
// output: 0.000006,
|
||||
// reasoning: 0.0000015,
|
||||
// cacheRead: 0.0000001,
|
||||
// cacheWrite: 0.0000001,
|
||||
// model: () =>
|
||||
// createAnthropic({
|
||||
// apiKey: Resource.ANTHROPIC_API_KEY.value,
|
||||
// })("claude-sonnet-4-20250514"),
|
||||
// },
|
||||
// "openai/gpt-4.1": {
|
||||
// input: 0.0000015,
|
||||
// output: 0.000006,
|
||||
// reasoning: 0.0000015,
|
||||
// cacheRead: 0.0000001,
|
||||
// cacheWrite: 0.0000001,
|
||||
// model: () =>
|
||||
// createOpenAI({
|
||||
// apiKey: Resource.OPENAI_API_KEY.value,
|
||||
// })("gpt-4.1"),
|
||||
// },
|
||||
// "zhipuai/glm-4.5-flash": {
|
||||
// input: 0,
|
||||
// output: 0,
|
||||
// reasoning: 0,
|
||||
// cacheRead: 0,
|
||||
// cacheWrite: 0,
|
||||
// model: () =>
|
||||
// createOpenAICompatible({
|
||||
// name: "Zhipu AI",
|
||||
// baseURL: "https://api.z.ai/api/paas/v4",
|
||||
// apiKey: Resource.ZHIPU_API_KEY.value,
|
||||
// })("glm-4.5-flash"),
|
||||
// auth: true,
|
||||
// api: "https://api.anthropic.com",
|
||||
// apiKey: Resource.ANTHROPIC_API_KEY.value,
|
||||
// model: "claude-sonnet-4-20250514",
|
||||
// cost: {
|
||||
// input: 0.0000015,
|
||||
// output: 0.000006,
|
||||
// reasoning: 0.0000015,
|
||||
// cacheRead: 0.0000001,
|
||||
// cacheWrite: 0.0000001,
|
||||
// },
|
||||
// headerMappings: {},
|
||||
// },
|
||||
"qwen/qwen3-coder": {
|
||||
id: "qwen/qwen3-coder",
|
||||
auth: true,
|
||||
api: "https://inference.baseten.co",
|
||||
apiKey: Resource.BASETEN_API_KEY.value,
|
||||
model: "Qwen/Qwen3-Coder-480B-A35B-Instruct",
|
||||
cost: {
|
||||
input: 0.00000038,
|
||||
output: 0.00000153,
|
||||
reasoning: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
headerMappings: {},
|
||||
},
|
||||
"x-ai/grok-code-fast-1": {
|
||||
id: "x-ai/grok-code-fast-1",
|
||||
auth: false,
|
||||
api: "https://api.x.ai",
|
||||
apiKey: Resource.XAI_API_KEY.value,
|
||||
model: "grok-code",
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
headerMappings: {
|
||||
"x-grok-conv-id": "x-opencode-session",
|
||||
"x-grok-req-id": "x-opencode-request",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
class AuthError extends Error {}
|
||||
class CreditsError extends Error {}
|
||||
class ModelError extends Error {}
|
||||
|
||||
export async function POST(input: APIEvent) {
|
||||
// Check auth header
|
||||
const authHeader = input.request.headers.get("authorization")
|
||||
if (!authHeader || !authHeader.startsWith("Bearer "))
|
||||
return Response.json(
|
||||
{
|
||||
error: {
|
||||
message: "Missing API key.",
|
||||
type: "invalid_request_error",
|
||||
param: null,
|
||||
code: "unauthorized",
|
||||
},
|
||||
},
|
||||
{ status: 401 },
|
||||
)
|
||||
const apiKey = authHeader.split(" ")[1]
|
||||
try {
|
||||
const url = new URL(input.request.url)
|
||||
const body = await input.request.json()
|
||||
const MODEL = validateModel()
|
||||
const apiKey = await authenticate()
|
||||
await checkCredits()
|
||||
|
||||
// Check against KeyTable
|
||||
const keyRecord = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: KeyTable.id,
|
||||
workspaceID: KeyTable.workspaceID,
|
||||
// Request to model provider
|
||||
const res = await fetch(new URL(url.pathname.replace(/^\/gateway/, "") + url.search, MODEL.api), {
|
||||
method: "POST",
|
||||
headers: (() => {
|
||||
const headers = input.request.headers
|
||||
headers.delete("host")
|
||||
headers.delete("content-length")
|
||||
headers.set("authorization", `Bearer ${MODEL.apiKey}`)
|
||||
Object.entries(MODEL.headerMappings ?? {}).forEach(([k, v]) => {
|
||||
headers.set(k, headers.get(v)!)
|
||||
})
|
||||
return headers
|
||||
})(),
|
||||
body: JSON.stringify({
|
||||
...body,
|
||||
model: MODEL.model,
|
||||
stream_options: {
|
||||
include_usage: true,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
// Scrub response headers
|
||||
const resHeaders = new Headers()
|
||||
const keepHeaders = ["content-type", "cache-control"]
|
||||
for (const [k, v] of res.headers.entries()) {
|
||||
if (keepHeaders.includes(k.toLowerCase())) {
|
||||
resHeaders.set(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle non-streaming response
|
||||
if (!body.stream) {
|
||||
const body = await res.json()
|
||||
await trackUsage(body)
|
||||
return new Response(JSON.stringify(body), {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: resHeaders,
|
||||
})
|
||||
.from(KeyTable)
|
||||
.where(eq(KeyTable.key, apiKey))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
}
|
||||
|
||||
if (!keyRecord)
|
||||
return Response.json(
|
||||
{
|
||||
error: {
|
||||
message: "Invalid API key.",
|
||||
type: "invalid_request_error",
|
||||
param: null,
|
||||
code: "unauthorized",
|
||||
},
|
||||
// Handle streaming response
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
const reader = res.body?.getReader()
|
||||
const decoder = new TextDecoder()
|
||||
let buffer = ""
|
||||
|
||||
function pump(): Promise<void> {
|
||||
return (
|
||||
reader?.read().then(async ({ done, value }) => {
|
||||
if (done) {
|
||||
c.close()
|
||||
return
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
|
||||
const parts = buffer.split("\n\n")
|
||||
buffer = parts.pop() ?? ""
|
||||
|
||||
const usage = parts
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part.startsWith("data: "))
|
||||
.map((part) => {
|
||||
try {
|
||||
return JSON.parse(part.slice(6))
|
||||
} catch (e) {
|
||||
return {}
|
||||
}
|
||||
})
|
||||
.find((part) => part.usage)
|
||||
if (usage) await trackUsage(usage)
|
||||
|
||||
c.enqueue(value)
|
||||
|
||||
return pump()
|
||||
}) || Promise.resolve()
|
||||
)
|
||||
}
|
||||
|
||||
return pump()
|
||||
},
|
||||
{ status: 401 },
|
||||
)
|
||||
})
|
||||
|
||||
/*
|
||||
return await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => {
|
||||
try {
|
||||
// Check balance
|
||||
const customer = await Billing.get()
|
||||
if (customer.balance <= 0) {
|
||||
return Response.json(
|
||||
{
|
||||
error: {
|
||||
message: "Insufficient balance",
|
||||
type: "insufficient_quota",
|
||||
param: null,
|
||||
code: "insufficient_quota",
|
||||
},
|
||||
},
|
||||
{ status: 401 },
|
||||
return new Response(stream, {
|
||||
status: res.status,
|
||||
statusText: res.statusText,
|
||||
headers: resHeaders,
|
||||
})
|
||||
|
||||
function validateModel() {
|
||||
if (!(body.model in MODELS)) {
|
||||
throw new ModelError(`Model ${body.model} not supported`)
|
||||
}
|
||||
return MODELS[body.model as keyof typeof MODELS]
|
||||
}
|
||||
|
||||
async function authenticate() {
|
||||
try {
|
||||
const authHeader = input.request.headers.get("authorization")
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) throw new AuthError("Missing API key.")
|
||||
|
||||
const apiKey = authHeader.split(" ")[1]
|
||||
const key = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: KeyTable.id,
|
||||
workspaceID: KeyTable.workspaceID,
|
||||
})
|
||||
.from(KeyTable)
|
||||
.where(eq(KeyTable.key, apiKey))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
|
||||
if (!key) throw new AuthError("Invalid API key.")
|
||||
return key
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
// ignore error if model does not require authentication
|
||||
if (!MODEL.auth) return
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
const body = await input.request.json<ChatCompletionCreateParamsBase>()
|
||||
const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model()
|
||||
if (!model) throw new Error(`Unsupported model: ${body.model}`)
|
||||
async function checkCredits() {
|
||||
if (!apiKey || !MODEL.auth) return
|
||||
|
||||
const requestBody = transformOpenAIRequestToAiSDK()
|
||||
|
||||
return body.stream ? await handleStream() : await handleGenerate()
|
||||
|
||||
async function handleStream() {
|
||||
const result = await model.doStream({
|
||||
...requestBody,
|
||||
})
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const id = `chatcmpl-${Date.now()}`
|
||||
const created = Math.floor(Date.now() / 1000)
|
||||
|
||||
try {
|
||||
for await (const chunk of result.stream) {
|
||||
console.log("!!! CHUNK !!! : " + chunk.type)
|
||||
switch (chunk.type) {
|
||||
case "text-delta": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
content: chunk.delta,
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "reasoning-delta": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
reasoning_content: chunk.delta,
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "tool-call": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: chunk.toolCallId,
|
||||
type: "function",
|
||||
function: {
|
||||
name: chunk.toolName,
|
||||
arguments: chunk.input,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "error": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason: "stop",
|
||||
},
|
||||
],
|
||||
error: {
|
||||
message: typeof chunk.error === "string" ? chunk.error : chunk.error,
|
||||
type: "server_error",
|
||||
},
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
break
|
||||
}
|
||||
|
||||
case "finish": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason:
|
||||
{
|
||||
stop: "stop",
|
||||
length: "length",
|
||||
"content-filter": "content_filter",
|
||||
"tool-calls": "tool_calls",
|
||||
error: "stop",
|
||||
other: "stop",
|
||||
unknown: "stop",
|
||||
}[chunk.finishReason] || "stop",
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: chunk.usage.inputTokens,
|
||||
completion_tokens: chunk.usage.outputTokens,
|
||||
total_tokens: chunk.usage.totalTokens,
|
||||
completion_tokens_details: {
|
||||
reasoning_tokens: chunk.usage.reasoningTokens,
|
||||
},
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: chunk.usage.cachedInputTokens,
|
||||
},
|
||||
},
|
||||
}
|
||||
await trackUsage(body.model, chunk.usage, chunk.providerMetadata)
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
break
|
||||
}
|
||||
|
||||
//case "stream-start":
|
||||
//case "response-metadata":
|
||||
case "text-start":
|
||||
case "text-end":
|
||||
case "reasoning-start":
|
||||
case "reasoning-end":
|
||||
case "tool-input-start":
|
||||
case "tool-input-delta":
|
||||
case "tool-input-end":
|
||||
case "raw":
|
||||
default:
|
||||
// Log unknown chunk types for debugging
|
||||
console.warn(`Unknown chunk type: ${(chunk as any).type}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
controller.error(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function handleGenerate() {
|
||||
const response = await model.doGenerate({
|
||||
...requestBody,
|
||||
})
|
||||
await trackUsage(body.model, response.usage, response.providerMetadata)
|
||||
return c.json({
|
||||
id: `chatcmpl-${Date.now()}`,
|
||||
object: "chat.completion" as const,
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
message: {
|
||||
role: "assistant" as const,
|
||||
content: response.content?.find((c) => c.type === "text")?.text ?? "",
|
||||
reasoning_content: response.content?.find((c) => c.type === "reasoning")?.text,
|
||||
tool_calls: response.content
|
||||
?.filter((c) => c.type === "tool-call")
|
||||
.map((toolCall) => ({
|
||||
id: toolCall.toolCallId,
|
||||
type: "function" as const,
|
||||
function: {
|
||||
name: toolCall.toolName,
|
||||
arguments: toolCall.input,
|
||||
},
|
||||
})),
|
||||
},
|
||||
finish_reason:
|
||||
(
|
||||
{
|
||||
stop: "stop",
|
||||
length: "length",
|
||||
"content-filter": "content_filter",
|
||||
"tool-calls": "tool_calls",
|
||||
error: "stop",
|
||||
other: "stop",
|
||||
unknown: "stop",
|
||||
} as const
|
||||
)[response.finishReason] || "stop",
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: response.usage?.inputTokens,
|
||||
completion_tokens: response.usage?.outputTokens,
|
||||
total_tokens: response.usage?.totalTokens,
|
||||
completion_tokens_details: {
|
||||
reasoning_tokens: response.usage?.reasoningTokens,
|
||||
},
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: response.usage?.cachedInputTokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function transformOpenAIRequestToAiSDK() {
|
||||
const prompt = transformMessages()
|
||||
const tools = transformTools()
|
||||
|
||||
return {
|
||||
prompt,
|
||||
maxOutputTokens: body.max_tokens ?? body.max_completion_tokens ?? undefined,
|
||||
temperature: body.temperature ?? undefined,
|
||||
topP: body.top_p ?? undefined,
|
||||
frequencyPenalty: body.frequency_penalty ?? undefined,
|
||||
presencePenalty: body.presence_penalty ?? undefined,
|
||||
providerOptions: body.reasoning_effort
|
||||
? {
|
||||
anthropic: {
|
||||
reasoningEffort: body.reasoning_effort,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined,
|
||||
responseFormat: (() => {
|
||||
if (!body.response_format) return { type: "text" as const }
|
||||
if (body.response_format.type === "json_schema")
|
||||
return {
|
||||
type: "json" as const,
|
||||
schema: body.response_format.json_schema.schema,
|
||||
name: body.response_format.json_schema.name,
|
||||
description: body.response_format.json_schema.description,
|
||||
}
|
||||
if (body.response_format.type === "json_object") return { type: "json" as const }
|
||||
throw new Error("Unsupported response format")
|
||||
})(),
|
||||
seed: body.seed ?? undefined,
|
||||
tools: tools.tools,
|
||||
toolChoice: tools.toolChoice,
|
||||
}
|
||||
|
||||
function transformTools() {
|
||||
const { tools, tool_choice } = body
|
||||
|
||||
if (!tools || tools.length === 0) {
|
||||
return { tools: undefined, toolChoice: undefined }
|
||||
}
|
||||
|
||||
const aiSdkTools = tools.map((tool) => {
|
||||
return {
|
||||
type: tool.type,
|
||||
name: tool.function.name,
|
||||
description: tool.function.description,
|
||||
inputSchema: tool.function.parameters!,
|
||||
}
|
||||
const billing = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
balance: BillingTable.balance,
|
||||
})
|
||||
.from(BillingTable)
|
||||
.where(eq(BillingTable.workspaceID, apiKey.workspaceID))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
|
||||
let aiSdkToolChoice
|
||||
if (tool_choice == null) {
|
||||
aiSdkToolChoice = undefined
|
||||
} else if (tool_choice === "auto") {
|
||||
aiSdkToolChoice = { type: "auto" as const }
|
||||
} else if (tool_choice === "none") {
|
||||
aiSdkToolChoice = { type: "none" as const }
|
||||
} else if (tool_choice === "required") {
|
||||
aiSdkToolChoice = { type: "required" as const }
|
||||
} else if (tool_choice.type === "function") {
|
||||
aiSdkToolChoice = {
|
||||
type: "tool" as const,
|
||||
toolName: tool_choice.function.name,
|
||||
}
|
||||
}
|
||||
if (billing.balance <= 0) throw new CreditsError("Insufficient balance")
|
||||
}
|
||||
|
||||
return { tools: aiSdkTools, toolChoice: aiSdkToolChoice }
|
||||
}
|
||||
async function trackUsage(chunk: any) {
|
||||
console.log(`trackUsage ${apiKey}`)
|
||||
|
||||
function transformMessages() {
|
||||
const { messages } = body
|
||||
const prompt: LanguageModelV2Prompt = []
|
||||
if (!apiKey) return
|
||||
|
||||
for (const message of messages) {
|
||||
switch (message.role) {
|
||||
case "system": {
|
||||
prompt.push({
|
||||
role: "system",
|
||||
content: message.content as string,
|
||||
})
|
||||
break
|
||||
}
|
||||
const usage = chunk.usage
|
||||
const inputTokens = usage.prompt_tokens ?? 0
|
||||
const outputTokens = usage.completion_tokens ?? 0
|
||||
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? 0
|
||||
const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0
|
||||
//const cacheWriteTokens = providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ?? 0
|
||||
const cacheWriteTokens = 0
|
||||
|
||||
case "user": {
|
||||
if (typeof message.content === "string") {
|
||||
prompt.push({
|
||||
role: "user",
|
||||
content: [{ type: "text", text: message.content }],
|
||||
})
|
||||
} else {
|
||||
const content = message.content.map((part) => {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
return { type: "text" as const, text: part.text }
|
||||
case "image_url":
|
||||
return {
|
||||
type: "file" as const,
|
||||
mediaType: "image/jpeg" as const,
|
||||
data: part.image_url.url,
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported content part type: ${(part as any).type}`)
|
||||
}
|
||||
})
|
||||
prompt.push({
|
||||
role: "user",
|
||||
content,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
const inputCost = MODEL.cost.input * inputTokens
|
||||
const outputCost = MODEL.cost.output * outputTokens
|
||||
const reasoningCost = MODEL.cost.reasoning * reasoningTokens
|
||||
const cacheReadCost = MODEL.cost.cacheRead * cacheReadTokens
|
||||
const cacheWriteCost = MODEL.cost.cacheWrite * cacheWriteTokens
|
||||
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
|
||||
const cost = centsToMicroCents(costInCents)
|
||||
|
||||
case "assistant": {
|
||||
const content: Array<
|
||||
| { type: "text"; text: string }
|
||||
| {
|
||||
type: "tool-call"
|
||||
toolCallId: string
|
||||
toolName: string
|
||||
input: any
|
||||
}
|
||||
> = []
|
||||
|
||||
if (message.content) {
|
||||
content.push({
|
||||
type: "text",
|
||||
text: message.content as string,
|
||||
})
|
||||
}
|
||||
|
||||
if (message.tool_calls) {
|
||||
for (const toolCall of message.tool_calls) {
|
||||
content.push({
|
||||
type: "tool-call",
|
||||
toolCallId: toolCall.id,
|
||||
toolName: toolCall.function.name,
|
||||
input: JSON.parse(toolCall.function.arguments),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
prompt.push({
|
||||
role: "assistant",
|
||||
content,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "tool": {
|
||||
prompt.push({
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolName: "placeholder",
|
||||
toolCallId: message.tool_call_id,
|
||||
output: {
|
||||
type: "text",
|
||||
value: message.content as string,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Unsupported message role: ${message.role}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prompt
|
||||
}
|
||||
}
|
||||
|
||||
async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) {
|
||||
const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS]
|
||||
if (!modelData) throw new Error(`Unsupported model: ${model}`)
|
||||
|
||||
const inputTokens = usage.inputTokens ?? 0
|
||||
const outputTokens = usage.outputTokens ?? 0
|
||||
const reasoningTokens = usage.reasoningTokens ?? 0
|
||||
const cacheReadTokens = usage.cachedInputTokens ?? 0
|
||||
const cacheWriteTokens =
|
||||
providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
||||
// @ts-expect-error
|
||||
providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
||||
0
|
||||
|
||||
const inputCost = modelData.input * inputTokens
|
||||
const outputCost = modelData.output * outputTokens
|
||||
const reasoningCost = modelData.reasoning * reasoningTokens
|
||||
const cacheReadCost = modelData.cacheRead * cacheReadTokens
|
||||
const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens
|
||||
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
|
||||
|
||||
await Billing.consume({
|
||||
model,
|
||||
await Database.transaction(async (tx) => {
|
||||
await tx.insert(UsageTable).values({
|
||||
workspaceID: apiKey.workspaceID,
|
||||
id: Identifier.create("usage"),
|
||||
model: MODEL.id,
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
reasoningTokens,
|
||||
cacheReadTokens,
|
||||
cacheWriteTokens,
|
||||
costInCents,
|
||||
cost,
|
||||
})
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} - ${cost}`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, apiKey.workspaceID))
|
||||
})
|
||||
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(KeyTable)
|
||||
.set({ timeUsed: sql`now()` })
|
||||
.where(eq(KeyTable.id, keyRecord.id)),
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
return Response.json({ error: { message: error.message } }, { status: 500 })
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(KeyTable)
|
||||
.set({ timeUsed: sql`now()` })
|
||||
.where(eq(KeyTable.id, apiKey.id)),
|
||||
)
|
||||
}
|
||||
})
|
||||
*/
|
||||
} catch (error: any) {
|
||||
if (error instanceof AuthError) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: error.message,
|
||||
type: "invalid_request_error",
|
||||
param: null,
|
||||
code: "unauthorized",
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (error instanceof CreditsError) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: {
|
||||
message: error.message,
|
||||
type: "insufficient_quota",
|
||||
param: null,
|
||||
code: "insufficient_quota",
|
||||
},
|
||||
}),
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if (error instanceof ModelError) {
|
||||
return new Response(JSON.stringify({ error: { message: error.message } }), {
|
||||
status: 401,
|
||||
})
|
||||
}
|
||||
|
||||
console.log(error)
|
||||
return new Response(JSON.stringify({ error: { message: error.message } }), {
|
||||
status: 500,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,22 +78,22 @@
|
||||
|
||||
[data-component="cta"] {
|
||||
border-top: 2px solid var(--color-border);
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
|
||||
& > div + div {
|
||||
border-left: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
[data-slot="left"],
|
||||
[data-slot="right"] {
|
||||
[data-slot="left"] {
|
||||
flex: 0 0 auto;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
padding: var(--vertical-padding) 2rem;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.125rem;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
font-size: 1rem;
|
||||
padding-bottom: calc(var(--vertical-padding) + 4px);
|
||||
}
|
||||
|
||||
@@ -103,53 +103,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="center"] {
|
||||
[data-slot="right"] {
|
||||
flex: 1;
|
||||
padding: var(--vertical-padding) 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 55rem) {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-rows: auto auto;
|
||||
|
||||
[data-slot="left"] {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
@media (max-width: 50rem) {
|
||||
flex-direction: column;
|
||||
|
||||
[data-slot="right"] {
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
[data-slot="center"] {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 2;
|
||||
border-left: none;
|
||||
border-top: 2px solid var(--color-border);
|
||||
}
|
||||
|
||||
@media (max-width: 22rem) {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: auto auto auto;
|
||||
|
||||
[data-slot="left"] {
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
[data-slot="right"] {
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
border-top: 2px solid var(--color-border);
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
[data-slot="center"] {
|
||||
grid-column: 1;
|
||||
grid-row: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-slot="command"] {
|
||||
@@ -168,7 +133,7 @@
|
||||
@media (max-width: 24rem) {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
@media (max-width: 64rem) {
|
||||
@media (max-width: 56rem) {
|
||||
[data-slot="protocol"] {
|
||||
display: none;
|
||||
}
|
||||
@@ -188,6 +153,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="zen"] {
|
||||
border-top: 2px solid var(--color-border);
|
||||
text-align: center;
|
||||
font-size: 1.125rem;
|
||||
padding: var(--vertical-padding) 2rem;
|
||||
|
||||
@media (max-width: 30rem) {
|
||||
font-size: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
a[target="_self"] {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
[data-slot="description"] {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
[data-slot="divider"] {
|
||||
font-weight: bold;
|
||||
color: var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
[data-component="features"] {
|
||||
border-top: 2px solid var(--color-border);
|
||||
padding: var(--padding);
|
||||
@@ -309,27 +300,27 @@
|
||||
grid-template-rows: var(--images-height);
|
||||
border-top: 2px solid var(--color-border);
|
||||
|
||||
& > div.left {
|
||||
& > [data-slot="left"] {
|
||||
display: flex;
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
& > div.right {
|
||||
& > [data-slot="right"] {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-row: 1;
|
||||
grid-column: 2;
|
||||
border-left: 2px solid var(--color-border);
|
||||
|
||||
& > div.row1 {
|
||||
& > [data-slot="row1"] {
|
||||
display: flex;
|
||||
grid-row: 1;
|
||||
border-bottom: 2px solid var(--color-border);
|
||||
height: calc(var(--images-height) / 2);
|
||||
}
|
||||
|
||||
& > div.row2 {
|
||||
& > [data-slot="row2"] {
|
||||
display: flex;
|
||||
grid-row: 2;
|
||||
height: calc(var(--images-height) / 2);
|
||||
@@ -384,24 +375,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
& > div.left figure {
|
||||
& > [data-slot="left"] figure {
|
||||
height: var(--images-height);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
& > div.right figure {
|
||||
& > [data-slot="right"] figure {
|
||||
height: calc(var(--images-height) / 2);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
& > div.left img {
|
||||
& > [data-slot="left"] img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: 0;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
& > div.right img {
|
||||
& > [data-slot="right"] img {
|
||||
width: 100%;
|
||||
height: calc(100% - 2rem);
|
||||
object-fit: contain;
|
||||
@@ -415,30 +406,30 @@
|
||||
grid-template-rows: auto auto;
|
||||
}
|
||||
|
||||
& > div.left {
|
||||
& > [data-slot="left"] {
|
||||
grid-row: 1;
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
& > div.right {
|
||||
& > [data-slot="right"] {
|
||||
grid-row: 2;
|
||||
grid-column: 1;
|
||||
border-left: none;
|
||||
border-top: 2px solid var(--color-border);
|
||||
|
||||
& > div.row1,
|
||||
& > div.row2 {
|
||||
& > [data-slot="row1"],
|
||||
& > [data-slot="row2"] {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
& > div.left figure,
|
||||
& > div.right figure {
|
||||
& > [data-slot="left"] figure,
|
||||
& > [data-slot="right"] figure {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
& > div.left img,
|
||||
& > div.right img {
|
||||
& > [data-slot="left"] img,
|
||||
& > [data-slot="right"] img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-height: none;
|
||||
|
||||
@@ -26,13 +26,13 @@ const isLoggedIn = query(async () => {
|
||||
const actor = await getActor()
|
||||
if (actor.type === "account") {
|
||||
const workspaces = await withActor(() => Account.workspaces())
|
||||
throw redirect(`/workspace/${workspaces[0].id}`)
|
||||
return workspaces[0].id
|
||||
// throw redirect(`/workspace/${workspaces[0].id}`)
|
||||
}
|
||||
return false
|
||||
}, "isLoggedIn")
|
||||
|
||||
export default function Home() {
|
||||
createAsync(() => isLoggedIn(), {
|
||||
const auth = createAsync(() => isLoggedIn(), {
|
||||
deferStream: true,
|
||||
})
|
||||
onMount(() => {
|
||||
@@ -67,9 +67,11 @@ export default function Home() {
|
||||
|
||||
<section data-component="cta">
|
||||
<div data-slot="left">
|
||||
<a href="/docs">Get Started</a>
|
||||
<a target="_self" href="/docs">
|
||||
Get Started
|
||||
</a>
|
||||
</div>
|
||||
<div data-slot="center">
|
||||
<div data-slot="right">
|
||||
<button data-copy data-slot="command">
|
||||
<span>
|
||||
<span>curl -fsSL </span>
|
||||
@@ -80,11 +82,17 @@ export default function Home() {
|
||||
<CopyStatus />
|
||||
</button>
|
||||
</div>
|
||||
<div data-slot="right">
|
||||
<a href="/auth/authorize" target="_self">
|
||||
Login
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-component="zen">
|
||||
<a target="_self" href="/docs/zen">
|
||||
opencode zen
|
||||
</a>
|
||||
<span data-slot="description">, a curated list of models provided by opencode</span>
|
||||
<span data-slot="divider"> / </span>
|
||||
<a href="/auth" target="_self">
|
||||
{auth() ? "Dashboard" : "Sign in"}
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section data-component="features">
|
||||
@@ -151,7 +159,7 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
<section data-component="screenshots">
|
||||
<div class="left">
|
||||
<div data-slot="left">
|
||||
<figure>
|
||||
<figcaption>opencode TUI with the tokyonight theme</figcaption>
|
||||
<a href="/docs/cli">
|
||||
@@ -159,8 +167,8 @@ export default function Home() {
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="row1">
|
||||
<div data-slot="right">
|
||||
<div data-slot="row1">
|
||||
<figure>
|
||||
<figcaption>opencode in VS Code</figcaption>
|
||||
<a href="/docs/ide">
|
||||
@@ -168,7 +176,7 @@ export default function Home() {
|
||||
</a>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="row2">
|
||||
<div data-slot="row2">
|
||||
<figure>
|
||||
<figcaption>opencode in GitHub</figcaption>
|
||||
<a href="/docs/github">
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { APIEvent } from "@solidjs/start/server"
|
||||
|
||||
async function handler(evt: APIEvent) {
|
||||
const req = evt.request.clone()
|
||||
const url = new URL(req.url)
|
||||
const targetUrl = `https://docs.opencode.ai/docs${url.pathname}${url.search}`
|
||||
const response = await fetch(targetUrl, {
|
||||
method: req.method,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
})
|
||||
return response
|
||||
}
|
||||
|
||||
export const GET = handler
|
||||
export const POST = handler
|
||||
export const PUT = handler
|
||||
export const DELETE = handler
|
||||
export const OPTIONS = handler
|
||||
export const PATCH = handler
|
||||
@@ -26,6 +26,8 @@ export async function POST(input: APIEvent) {
|
||||
if (!amount) throw new Error("Amount not found")
|
||||
if (!paymentID) throw new Error("Payment ID not found")
|
||||
|
||||
const chargedAmount = 2000
|
||||
|
||||
await Actor.provide("system", { workspaceID }, async () => {
|
||||
const customer = await Billing.get()
|
||||
if (customer?.customerID && customer.customerID !== customerID) throw new Error("Customer ID mismatch")
|
||||
@@ -50,7 +52,7 @@ export async function POST(input: APIEvent) {
|
||||
await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} + ${centsToMicroCents(amount)}`,
|
||||
balance: sql`${BillingTable.balance} + ${centsToMicroCents(chargedAmount)}`,
|
||||
customerID,
|
||||
paymentMethodID: paymentMethod.id,
|
||||
paymentMethodLast4: paymentMethod.card!.last4,
|
||||
@@ -59,7 +61,7 @@ export async function POST(input: APIEvent) {
|
||||
await tx.insert(PaymentTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("payment"),
|
||||
amount: centsToMicroCents(amount),
|
||||
amount: centsToMicroCents(chargedAmount),
|
||||
paymentID,
|
||||
customerID,
|
||||
})
|
||||
|
||||
@@ -22,15 +22,17 @@ const logout = action(async () => {
|
||||
const current = auth.data.current
|
||||
if (current)
|
||||
await auth.update((val) => {
|
||||
delete val.account[current]
|
||||
delete val.account?.[current]
|
||||
const first = Object.keys(val.account ?? {})[0]
|
||||
val.current = first
|
||||
return val
|
||||
})
|
||||
|
||||
return redirect("/")
|
||||
})
|
||||
|
||||
export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
const userInfo = createAsync(() => getUserInfo())
|
||||
const userInfo = createAsync(() => getUserInfo(), {
|
||||
deferStream: true,
|
||||
})
|
||||
return (
|
||||
<main data-page="workspace">
|
||||
<header data-component="workspace-header">
|
||||
@@ -40,11 +42,11 @@ export default function WorkspaceLayout(props: RouteSectionProps) {
|
||||
</a>
|
||||
</div>
|
||||
<div data-slot="header-actions">
|
||||
{userInfo() &&
|
||||
<span>{userInfo()!.user.email}</span>
|
||||
}
|
||||
<form action={logout} method="post">
|
||||
<button type="submit" formaction={logout}>Logout</button>
|
||||
<span>{userInfo()?.user.email}</span>
|
||||
<form onSubmit={() => (location.href = "/")} action={logout} method="post">
|
||||
<button type="submit" formaction={logout}>
|
||||
Logout
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import "./[id].css"
|
||||
import { Billing } from "@opencode/cloud-core/billing.js"
|
||||
import { Key } from "@opencode/cloud-core/key.js"
|
||||
import { action, createAsync, query, useAction, useSubmission, json } from "@solidjs/router"
|
||||
import { createSignal, For, onMount, Show } from "solid-js"
|
||||
import { getActor } from "~/context/auth"
|
||||
import { createSignal, For, Show } from "solid-js"
|
||||
import { withActor } from "~/context/auth.withActor"
|
||||
import { IconCopy, IconCheck } from "~/component/icon"
|
||||
import "./[id].css"
|
||||
import { User } from "@opencode/cloud-core/user.js"
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
|
||||
@@ -14,7 +13,7 @@ import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
/////////////////////////////////////
|
||||
|
||||
|
||||
const listKeys = query(() => {
|
||||
const listKeys = query(async () => {
|
||||
"use server"
|
||||
return withActor(() => Key.list())
|
||||
}, "key.list")
|
||||
@@ -63,13 +62,14 @@ const createPortalUrl = action(async (returnUrl: string) => {
|
||||
return withActor(() => Billing.generatePortalUrl({ returnUrl }))
|
||||
}, "portalUrl")
|
||||
|
||||
export default function() {
|
||||
const actor = createAsync(() => getActor())
|
||||
export default function () {
|
||||
|
||||
/////////////////
|
||||
// Keys section
|
||||
/////////////////
|
||||
const keys = createAsync(() => listKeys())
|
||||
const keys = createAsync(() => listKeys(), {
|
||||
deferStream: true,
|
||||
})
|
||||
const createKeyAction = useAction(createKey)
|
||||
const removeKeyAction = useAction(removeKey)
|
||||
const createKeySubmission = useSubmission(createKey)
|
||||
@@ -157,7 +157,9 @@ export default function() {
|
||||
/////////////////
|
||||
// Billing section
|
||||
/////////////////
|
||||
const billingInfo = createAsync(() => getBillingInfo())
|
||||
const billingInfo = createAsync(() => getBillingInfo(), {
|
||||
deferStream: true,
|
||||
})
|
||||
const createCheckoutUrlAction = useAction(createCheckoutUrl)
|
||||
const createCheckoutUrlSubmission = useSubmission(createCheckoutUrl)
|
||||
|
||||
@@ -177,9 +179,9 @@ export default function() {
|
||||
<div data-slot="root">
|
||||
{/* Title */}
|
||||
<section data-slot="title-section">
|
||||
<h1>Gateway</h1>
|
||||
<h1>opencode zen</h1>
|
||||
<p>
|
||||
Coding models optimized for use with opencode. <a href="/docs">Learn more</a>.
|
||||
Curated list of models provided by opencode. <a href="/docs/zen">Learn more</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
import { Resource } from "sst"
|
||||
import { defineConfig } from "drizzle-kit"
|
||||
|
||||
export default defineConfig({
|
||||
@@ -6,7 +6,7 @@ export default defineConfig({
|
||||
strict: true,
|
||||
schema: ["./src/**/*.sql.ts"],
|
||||
verbose: true,
|
||||
dialect: "postgresql",
|
||||
dialect: "mysql",
|
||||
dbCredentials: {
|
||||
database: Resource.Database.database,
|
||||
host: Resource.Database.host,
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
CREATE TABLE "billing" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"workspace_id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"customer_id" varchar(255),
|
||||
"payment_method_id" varchar(255),
|
||||
"payment_method_last4" varchar(4),
|
||||
"balance" bigint NOT NULL,
|
||||
"reload" boolean,
|
||||
CONSTRAINT "billing_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "payment" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"workspace_id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"customer_id" varchar(255),
|
||||
"payment_id" varchar(255),
|
||||
"amount" bigint NOT NULL,
|
||||
CONSTRAINT "payment_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "usage" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"workspace_id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"request_id" varchar(255),
|
||||
"model" varchar(255) NOT NULL,
|
||||
"input_tokens" integer NOT NULL,
|
||||
"output_tokens" integer NOT NULL,
|
||||
"reasoning_tokens" integer,
|
||||
"cache_read_tokens" integer,
|
||||
"cache_write_tokens" integer,
|
||||
"cost" bigint NOT NULL,
|
||||
CONSTRAINT "usage_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"workspace_id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"email" text NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"time_seen" timestamp with time zone,
|
||||
"color" integer,
|
||||
CONSTRAINT "user_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "workspace" (
|
||||
"id" varchar(30) PRIMARY KEY NOT NULL,
|
||||
"slug" varchar(255),
|
||||
"name" varchar(255),
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "billing" ADD CONSTRAINT "billing_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "payment" ADD CONSTRAINT "payment_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "usage" ADD CONSTRAINT "usage_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user" ADD CONSTRAINT "user_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "user_email" ON "user" USING btree ("workspace_id","email");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "slug" ON "workspace" USING btree ("slug");
|
||||
@@ -0,0 +1,89 @@
|
||||
CREATE TABLE `account` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`email` varchar(255) NOT NULL,
|
||||
CONSTRAINT `email` UNIQUE(`email`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `billing` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`workspace_id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`customer_id` varchar(255),
|
||||
`payment_method_id` varchar(255),
|
||||
`payment_method_last4` varchar(4),
|
||||
`balance` bigint NOT NULL,
|
||||
`reload` boolean,
|
||||
CONSTRAINT `billing_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `payment` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`workspace_id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`customer_id` varchar(255),
|
||||
`payment_id` varchar(255),
|
||||
`amount` bigint NOT NULL,
|
||||
CONSTRAINT `payment_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `usage` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`workspace_id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`model` varchar(255) NOT NULL,
|
||||
`input_tokens` int NOT NULL,
|
||||
`output_tokens` int NOT NULL,
|
||||
`reasoning_tokens` int,
|
||||
`cache_read_tokens` int,
|
||||
`cache_write_tokens` int,
|
||||
`cost` bigint NOT NULL,
|
||||
CONSTRAINT `usage_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `key` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`workspace_id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`user_id` text NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`key` varchar(255) NOT NULL,
|
||||
`time_used` timestamp(3),
|
||||
CONSTRAINT `key_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`),
|
||||
CONSTRAINT `global_key` UNIQUE(`key`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `user` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`workspace_id` varchar(30) NOT NULL,
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
`email` varchar(255) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`time_seen` timestamp(3),
|
||||
`color` int,
|
||||
CONSTRAINT `user_workspace_id_id_pk` PRIMARY KEY(`workspace_id`,`id`),
|
||||
CONSTRAINT `user_email` UNIQUE(`workspace_id`,`email`)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `workspace` (
|
||||
`id` varchar(30) NOT NULL,
|
||||
`slug` varchar(255),
|
||||
`name` varchar(255),
|
||||
`time_created` timestamp(3) NOT NULL DEFAULT (now()),
|
||||
`time_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
|
||||
`time_deleted` timestamp(3),
|
||||
CONSTRAINT `workspace_id` PRIMARY KEY(`id`),
|
||||
CONSTRAINT `slug` UNIQUE(`slug`)
|
||||
);
|
||||
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE `key` ADD `actor` json;--> statement-breakpoint
|
||||
ALTER TABLE `key` DROP COLUMN `user_id`;
|
||||
@@ -1,8 +0,0 @@
|
||||
CREATE TABLE "account" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"email" varchar(255) NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "email" ON "account" USING btree ("email");
|
||||
@@ -1,14 +0,0 @@
|
||||
CREATE TABLE "key" (
|
||||
"id" varchar(30) NOT NULL,
|
||||
"workspace_id" varchar(30) NOT NULL,
|
||||
"time_created" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"time_deleted" timestamp with time zone,
|
||||
"user_id" text NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"key" varchar(255) NOT NULL,
|
||||
"time_used" timestamp with time zone,
|
||||
CONSTRAINT "key_workspace_id_id_pk" PRIMARY KEY("workspace_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "key" ADD CONSTRAINT "key_workspace_id_workspace_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspace"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "global_key" ON "key" USING btree ("key");
|
||||
@@ -1 +0,0 @@
|
||||
ALTER TABLE "usage" DROP COLUMN "request_id";
|
||||
@@ -1,85 +1,142 @@
|
||||
{
|
||||
"id": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "aee779c5-db1d-4655-95ec-6451c18455be",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.billing": {
|
||||
"name": "billing",
|
||||
"schema": "",
|
||||
"account": {
|
||||
"name": "account",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
"email"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"billing": {
|
||||
"name": "billing",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_id": {
|
||||
"name": "payment_method_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_last4": {
|
||||
"name": "payment_method_last4",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"balance": {
|
||||
"name": "balance",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"billing_workspace_id_workspace_id_fk": {
|
||||
"name": "billing_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "billing",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
@@ -90,74 +147,72 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.payment": {
|
||||
"payment": {
|
||||
"name": "payment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payment_workspace_id_workspace_id_fk": {
|
||||
"name": "payment_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "payment",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
@@ -168,104 +223,100 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.usage": {
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"request_id": {
|
||||
"name": "request_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_tokens": {
|
||||
"name": "cache_write_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"usage_workspace_id_workspace_id_fk": {
|
||||
"name": "usage_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "usage",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
@@ -276,102 +327,179 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"key": {
|
||||
"name": "key",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "workspace_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"user_workspace_id_workspace_id_fk": {
|
||||
"name": "user_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "user",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
@@ -382,80 +510,86 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.workspace": {
|
||||
"workspace": {
|
||||
"name": "workspace",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "slug",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +1,142 @@
|
||||
{
|
||||
"id": "bf9e9084-4073-4ecb-8e56-5610816c9589",
|
||||
"prevId": "9b5cec8c-8b59-4d7a-bb5c-76ade1c83d6f",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"version": "5",
|
||||
"dialect": "mysql",
|
||||
"id": "79b7ee25-1c1c-41ff-9bbf-754af257102b",
|
||||
"prevId": "aee779c5-db1d-4655-95ec-6451c18455be",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
"email"
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.billing": {
|
||||
"billing": {
|
||||
"name": "billing",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_id": {
|
||||
"name": "payment_method_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_method_last4": {
|
||||
"name": "payment_method_last4",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"balance": {
|
||||
"name": "balance",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"billing_workspace_id_workspace_id_fk": {
|
||||
"name": "billing_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "billing",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
@@ -144,74 +147,72 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.payment": {
|
||||
"payment": {
|
||||
"name": "payment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payment_workspace_id_workspace_id_fk": {
|
||||
"name": "payment_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "payment",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
@@ -222,104 +223,100 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.usage": {
|
||||
"usage": {
|
||||
"name": "usage",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"request_id": {
|
||||
"name": "request_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cache_write_tokens": {
|
||||
"name": "cache_write_tokens",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"usage_workspace_id_workspace_id_fk": {
|
||||
"name": "usage_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "usage",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
@@ -330,102 +327,179 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"key": {
|
||||
"name": "key",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"actor": {
|
||||
"name": "actor",
|
||||
"type": "json",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
"key"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "integer",
|
||||
"type": "int",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "workspace_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
"workspace_id",
|
||||
"email"
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"user_workspace_id_workspace_id_fk": {
|
||||
"name": "user_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "user",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
@@ -436,80 +510,86 @@
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
},
|
||||
"public.workspace": {
|
||||
"workspace": {
|
||||
"name": "workspace",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
"autoincrement": false,
|
||||
"default": "(now())"
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"type": "timestamp(3)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "slug",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"columns": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
"checkConstraint": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"tables": {},
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -1,615 +0,0 @@
|
||||
{
|
||||
"id": "351e4956-74e0-4282-a23b-02f1a73fa38c",
|
||||
"prevId": "bf9e9084-4073-4ecb-8e56-5610816c9589",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.billing": {
|
||||
"name": "billing",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_method_id": {
|
||||
"name": "payment_method_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_method_last4": {
|
||||
"name": "payment_method_last4",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"balance": {
|
||||
"name": "balance",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"billing_workspace_id_workspace_id_fk": {
|
||||
"name": "billing_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "billing",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.payment": {
|
||||
"name": "payment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payment_workspace_id_workspace_id_fk": {
|
||||
"name": "payment_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "payment",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.usage": {
|
||||
"name": "usage",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"request_id": {
|
||||
"name": "request_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cache_write_tokens": {
|
||||
"name": "cache_write_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"usage_workspace_id_workspace_id_fk": {
|
||||
"name": "usage_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "usage",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.key": {
|
||||
"name": "key",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "key",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"key_workspace_id_workspace_id_fk": {
|
||||
"name": "key_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "key",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "workspace_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"user_workspace_id_workspace_id_fk": {
|
||||
"name": "user_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "user",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.workspace": {
|
||||
"name": "workspace",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "slug",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -1,609 +0,0 @@
|
||||
{
|
||||
"id": "fa935883-9e51-4811-90c7-8967eefe458c",
|
||||
"prevId": "351e4956-74e0-4282-a23b-02f1a73fa38c",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"email": {
|
||||
"name": "email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.billing": {
|
||||
"name": "billing",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_method_id": {
|
||||
"name": "payment_method_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_method_last4": {
|
||||
"name": "payment_method_last4",
|
||||
"type": "varchar(4)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"balance": {
|
||||
"name": "balance",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"reload": {
|
||||
"name": "reload",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"billing_workspace_id_workspace_id_fk": {
|
||||
"name": "billing_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "billing",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"billing_workspace_id_id_pk": {
|
||||
"name": "billing_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.payment": {
|
||||
"name": "payment",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"customer_id": {
|
||||
"name": "customer_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"payment_id": {
|
||||
"name": "payment_id",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"amount": {
|
||||
"name": "amount",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"payment_workspace_id_workspace_id_fk": {
|
||||
"name": "payment_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "payment",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"payment_workspace_id_id_pk": {
|
||||
"name": "payment_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.usage": {
|
||||
"name": "usage",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"reasoning_tokens": {
|
||||
"name": "reasoning_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cache_read_tokens": {
|
||||
"name": "cache_read_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cache_write_tokens": {
|
||||
"name": "cache_write_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cost": {
|
||||
"name": "cost",
|
||||
"type": "bigint",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"usage_workspace_id_workspace_id_fk": {
|
||||
"name": "usage_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "usage",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"usage_workspace_id_id_pk": {
|
||||
"name": "usage_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.key": {
|
||||
"name": "key",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_used": {
|
||||
"name": "time_used",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"global_key": {
|
||||
"name": "global_key",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "key",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"key_workspace_id_workspace_id_fk": {
|
||||
"name": "key_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "key",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"key_workspace_id_id_pk": {
|
||||
"name": "key_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"workspace_id": {
|
||||
"name": "workspace_id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"time_seen": {
|
||||
"name": "time_seen",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"user_email": {
|
||||
"name": "user_email",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "workspace_id",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
},
|
||||
{
|
||||
"expression": "email",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {
|
||||
"user_workspace_id_workspace_id_fk": {
|
||||
"name": "user_workspace_id_workspace_id_fk",
|
||||
"tableFrom": "user",
|
||||
"tableTo": "workspace",
|
||||
"columnsFrom": [
|
||||
"workspace_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "no action",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"user_workspace_id_id_pk": {
|
||||
"name": "user_workspace_id_id_pk",
|
||||
"columns": [
|
||||
"workspace_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.workspace": {
|
||||
"name": "workspace",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(30)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"time_deleted": {
|
||||
"name": "time_deleted",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "slug",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": true,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,19 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"dialect": "mysql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1754518198186,
|
||||
"tag": "0000_amused_mojo",
|
||||
"version": "5",
|
||||
"when": 1756796050935,
|
||||
"tag": "0000_fluffy_raza",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1754609655262,
|
||||
"tag": "0001_thankful_chat",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "7",
|
||||
"when": 1754627626945,
|
||||
"tag": "0002_stale_jackal",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 3,
|
||||
"version": "7",
|
||||
"when": 1754672464106,
|
||||
"tag": "0003_tranquil_spencer_smythe",
|
||||
"version": "5",
|
||||
"when": 1756871639102,
|
||||
"tag": "0001_serious_whistler",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode/cloud-core",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-sts": "3.782.0",
|
||||
"@opencode/cloud-resource": "workspace:*",
|
||||
"@planetscale/database": "1.19.0",
|
||||
"drizzle-orm": "0.41.0",
|
||||
"postgres": "3.4.7",
|
||||
"stripe": "18.0.0",
|
||||
@@ -16,9 +17,11 @@
|
||||
"./*": "./src/*"
|
||||
},
|
||||
"scripts": {
|
||||
"db": "sst shell drizzle-kit"
|
||||
"db": "sst shell drizzle-kit",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"drizzle-kit": "0.30.5"
|
||||
"drizzle-kit": "0.30.5",
|
||||
"mysql2": "3.14.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import { BillingTable, PaymentTable, UsageTable } from "./schema/billing.sql"
|
||||
import { Actor } from "./actor"
|
||||
import { fn } from "./util/fn"
|
||||
import { z } from "zod"
|
||||
import { Identifier } from "./identifier"
|
||||
import { centsToMicroCents } from "./util/price"
|
||||
import { User } from "./user"
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
|
||||
@@ -52,46 +50,6 @@ export namespace Billing {
|
||||
)
|
||||
}
|
||||
|
||||
export const consume = fn(
|
||||
z.object({
|
||||
requestID: z.string().optional(),
|
||||
model: z.string(),
|
||||
inputTokens: z.number(),
|
||||
outputTokens: z.number(),
|
||||
reasoningTokens: z.number().optional(),
|
||||
cacheReadTokens: z.number().optional(),
|
||||
cacheWriteTokens: z.number().optional(),
|
||||
costInCents: z.number(),
|
||||
}),
|
||||
async (input) => {
|
||||
const workspaceID = Actor.workspace()
|
||||
const cost = centsToMicroCents(input.costInCents)
|
||||
|
||||
return await Database.transaction(async (tx) => {
|
||||
await tx.insert(UsageTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("usage"),
|
||||
requestID: input.requestID,
|
||||
model: input.model,
|
||||
inputTokens: input.inputTokens,
|
||||
outputTokens: input.outputTokens,
|
||||
reasoningTokens: input.reasoningTokens,
|
||||
cacheReadTokens: input.cacheReadTokens,
|
||||
cacheWriteTokens: input.cacheWriteTokens,
|
||||
cost,
|
||||
})
|
||||
const [updated] = await tx
|
||||
.update(BillingTable)
|
||||
.set({
|
||||
balance: sql`${BillingTable.balance} - ${cost}`,
|
||||
})
|
||||
.where(eq(BillingTable.workspaceID, workspaceID))
|
||||
.returning()
|
||||
return updated.balance
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
export const generateCheckoutUrl = fn(
|
||||
z.object({
|
||||
successUrl: z.string(),
|
||||
@@ -112,7 +70,7 @@ export namespace Billing {
|
||||
product_data: {
|
||||
name: "opencode credits",
|
||||
},
|
||||
unit_amount: 2000, // $20 minimum
|
||||
unit_amount: 2123, // $20 minimum + Stripe fee 4.4% + $0.30
|
||||
},
|
||||
quantity: 1,
|
||||
},
|
||||
|
||||
@@ -1,41 +1,33 @@
|
||||
import { drizzle } from "drizzle-orm/postgres-js"
|
||||
import { drizzle } from "drizzle-orm/planetscale-serverless"
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
export * from "drizzle-orm"
|
||||
import postgres from "postgres"
|
||||
import { Client } from "@planetscale/database"
|
||||
|
||||
const init = () => {
|
||||
const client = postgres({
|
||||
idle_timeout: 30000,
|
||||
connect_timeout: 30000,
|
||||
host: Resource.Database.host,
|
||||
database: Resource.Database.database,
|
||||
user: Resource.Database.username,
|
||||
password: Resource.Database.password,
|
||||
port: Resource.Database.port,
|
||||
ssl: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
max: 1,
|
||||
})
|
||||
return drizzle(client, {})
|
||||
}
|
||||
|
||||
const createClient = "NODE_ENV" in process.env ? memo(init) : init
|
||||
|
||||
import { PgTransaction, type PgTransactionConfig } from "drizzle-orm/pg-core"
|
||||
import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core"
|
||||
import type { ExtractTablesWithRelations } from "drizzle-orm"
|
||||
import type { PostgresJsQueryResultHKT } from "drizzle-orm/postgres-js"
|
||||
import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless"
|
||||
import { Context } from "../context"
|
||||
import { memo } from "../util/memo"
|
||||
|
||||
export namespace Database {
|
||||
export type Transaction = PgTransaction<
|
||||
PostgresJsQueryResultHKT,
|
||||
Record<string, unknown>,
|
||||
ExtractTablesWithRelations<Record<string, unknown>>
|
||||
export type Transaction = MySqlTransaction<
|
||||
PlanetscaleQueryResultHKT,
|
||||
PlanetScalePreparedQueryHKT,
|
||||
Record<string, never>,
|
||||
ExtractTablesWithRelations<Record<string, never>>
|
||||
>
|
||||
|
||||
export type TxOrDb = Transaction | ReturnType<typeof createClient>
|
||||
const client = memo(() => {
|
||||
const result = new Client({
|
||||
host: Resource.Database.host,
|
||||
username: Resource.Database.username,
|
||||
password: Resource.Database.password,
|
||||
})
|
||||
const db = drizzle(result, {})
|
||||
return db
|
||||
})
|
||||
|
||||
export type TxOrDb = Transaction | ReturnType<typeof client>
|
||||
|
||||
const TransactionContext = Context.create<{
|
||||
tx: TxOrDb
|
||||
@@ -48,14 +40,13 @@ export namespace Database {
|
||||
return tx.transaction(callback)
|
||||
} catch (err) {
|
||||
if (err instanceof Context.NotFound) {
|
||||
const client = createClient()
|
||||
const effects: (() => void | Promise<void>)[] = []
|
||||
const result = await TransactionContext.provide(
|
||||
{
|
||||
effects,
|
||||
tx: client,
|
||||
tx: client(),
|
||||
},
|
||||
() => callback(client),
|
||||
() => callback(client()),
|
||||
)
|
||||
await Promise.all(effects.map((x) => x()))
|
||||
return result
|
||||
@@ -76,15 +67,14 @@ export namespace Database {
|
||||
}
|
||||
}
|
||||
|
||||
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: PgTransactionConfig) {
|
||||
export async function transaction<T>(callback: (tx: TxOrDb) => Promise<T>, config?: MySqlTransactionConfig) {
|
||||
try {
|
||||
const { tx } = TransactionContext.use()
|
||||
return callback(tx)
|
||||
} catch (err) {
|
||||
if (err instanceof Context.NotFound) {
|
||||
const client = createClient()
|
||||
const effects: (() => void | Promise<void>)[] = []
|
||||
const result = await client.transaction(async (tx) => {
|
||||
const result = await client().transaction(async (tx) => {
|
||||
return TransactionContext.provide({ tx, effects }, () => callback(tx))
|
||||
}, config)
|
||||
await Promise.all(effects.map((x) => x()))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { bigint, timestamp, varchar } from "drizzle-orm/pg-core"
|
||||
import { sql } from "drizzle-orm"
|
||||
import { bigint, timestamp, varchar } from "drizzle-orm/mysql-core"
|
||||
|
||||
export const ulid = (name: string) => varchar(name, { length: 30 })
|
||||
|
||||
@@ -15,7 +16,7 @@ export const id = () => ulid("id").notNull()
|
||||
|
||||
export const utc = (name: string) =>
|
||||
timestamp(name, {
|
||||
withTimezone: true,
|
||||
fsp: 3,
|
||||
})
|
||||
|
||||
export const currency = (name: string) =>
|
||||
@@ -25,5 +26,8 @@ export const currency = (name: string) =>
|
||||
|
||||
export const timestamps = {
|
||||
timeCreated: utc("time_created").notNull().defaultNow(),
|
||||
timeUpdated: utc("time_updated")
|
||||
.notNull()
|
||||
.default(sql`CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3)`),
|
||||
timeDeleted: utc("time_deleted"),
|
||||
}
|
||||
|
||||
+23
-47
@@ -7,73 +7,49 @@ import { KeyTable } from "./schema/key.sql"
|
||||
|
||||
export namespace Key {
|
||||
export const list = async () => {
|
||||
const user = Actor.assert("user")
|
||||
const workspace = Actor.workspace()
|
||||
const keys = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: KeyTable.id,
|
||||
name: KeyTable.name,
|
||||
key: KeyTable.key,
|
||||
userID: KeyTable.userID,
|
||||
timeCreated: KeyTable.timeCreated,
|
||||
timeUsed: KeyTable.timeUsed,
|
||||
})
|
||||
.select()
|
||||
.from(KeyTable)
|
||||
.where(eq(KeyTable.workspaceID, user.properties.workspaceID))
|
||||
.where(eq(KeyTable.workspaceID, workspace))
|
||||
.orderBy(sql`${KeyTable.timeCreated} DESC`),
|
||||
)
|
||||
return keys
|
||||
}
|
||||
|
||||
export const create = fn(z.object({ name: z.string().min(1).max(255) }), async (input) => {
|
||||
const user = Actor.assert("user")
|
||||
const workspaceID = Actor.workspace()
|
||||
const { name } = input
|
||||
|
||||
// Generate secret key: sk- + 64 random characters (upper, lower, numbers)
|
||||
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
let randomPart = ""
|
||||
for (let i = 0; i < 64; i++) {
|
||||
randomPart += chars.charAt(Math.floor(Math.random() * chars.length))
|
||||
let secretKey = "sk-"
|
||||
const array = new Uint32Array(64)
|
||||
crypto.getRandomValues(array)
|
||||
for (let i = 0, l = array.length; i < l; i++) {
|
||||
secretKey += chars[array[i] % chars.length]
|
||||
}
|
||||
const secretKey = `sk-${randomPart}`
|
||||
const keyID = Identifier.create("key")
|
||||
|
||||
const keyRecord = await Database.use((tx) =>
|
||||
tx
|
||||
.insert(KeyTable)
|
||||
.values({
|
||||
id: Identifier.create("key"),
|
||||
workspaceID: user.properties.workspaceID,
|
||||
userID: user.properties.userID,
|
||||
name,
|
||||
key: secretKey,
|
||||
timeUsed: null,
|
||||
})
|
||||
.returning(),
|
||||
await Database.use((tx) =>
|
||||
tx.insert(KeyTable).values({
|
||||
id: keyID,
|
||||
workspaceID,
|
||||
actor: Actor.use(),
|
||||
name,
|
||||
key: secretKey,
|
||||
timeUsed: null,
|
||||
}),
|
||||
)
|
||||
|
||||
return {
|
||||
key: secretKey,
|
||||
id: keyRecord[0].id,
|
||||
name: keyRecord[0].name,
|
||||
created: keyRecord[0].timeCreated,
|
||||
}
|
||||
return keyID
|
||||
})
|
||||
|
||||
export const remove = fn(z.object({ id: z.string() }), async (input) => {
|
||||
const user = Actor.assert("user")
|
||||
const { id } = input
|
||||
|
||||
const result = await Database.use((tx) =>
|
||||
tx
|
||||
.delete(KeyTable)
|
||||
.where(and(eq(KeyTable.id, id), eq(KeyTable.workspaceID, user.properties.workspaceID)))
|
||||
.returning({ id: KeyTable.id }),
|
||||
const workspace = Actor.workspace()
|
||||
await Database.use((tx) =>
|
||||
tx.delete(KeyTable).where(and(eq(KeyTable.id, input.id), eq(KeyTable.workspaceID, workspace))),
|
||||
)
|
||||
|
||||
if (result.length === 0) {
|
||||
throw new Error("Key not found")
|
||||
}
|
||||
|
||||
return { id: result[0].id }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
|
||||
import { mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { id, timestamps } from "../drizzle/types"
|
||||
|
||||
export const AccountTable = pgTable(
|
||||
export const AccountTable = mysqlTable(
|
||||
"account",
|
||||
{
|
||||
id: id(),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { bigint, boolean, integer, pgTable, varchar } from "drizzle-orm/pg-core"
|
||||
import { bigint, boolean, int, mysqlTable, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
|
||||
export const BillingTable = pgTable(
|
||||
export const BillingTable = mysqlTable(
|
||||
"billing",
|
||||
{
|
||||
...workspaceColumns,
|
||||
@@ -16,7 +16,7 @@ export const BillingTable = pgTable(
|
||||
(table) => [...workspaceIndexes(table)],
|
||||
)
|
||||
|
||||
export const PaymentTable = pgTable(
|
||||
export const PaymentTable = mysqlTable(
|
||||
"payment",
|
||||
{
|
||||
...workspaceColumns,
|
||||
@@ -28,17 +28,17 @@ export const PaymentTable = pgTable(
|
||||
(table) => [...workspaceIndexes(table)],
|
||||
)
|
||||
|
||||
export const UsageTable = pgTable(
|
||||
export const UsageTable = mysqlTable(
|
||||
"usage",
|
||||
{
|
||||
...workspaceColumns,
|
||||
...timestamps,
|
||||
model: varchar("model", { length: 255 }).notNull(),
|
||||
inputTokens: integer("input_tokens").notNull(),
|
||||
outputTokens: integer("output_tokens").notNull(),
|
||||
reasoningTokens: integer("reasoning_tokens"),
|
||||
cacheReadTokens: integer("cache_read_tokens"),
|
||||
cacheWriteTokens: integer("cache_write_tokens"),
|
||||
inputTokens: int("input_tokens").notNull(),
|
||||
outputTokens: int("output_tokens").notNull(),
|
||||
reasoningTokens: int("reasoning_tokens"),
|
||||
cacheReadTokens: int("cache_read_tokens"),
|
||||
cacheWriteTokens: int("cache_write_tokens"),
|
||||
cost: bigint("cost", { mode: "number" }).notNull(),
|
||||
},
|
||||
(table) => [...workspaceIndexes(table)],
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { text, pgTable, varchar, uniqueIndex } from "drizzle-orm/pg-core"
|
||||
import { mysqlTable, varchar, uniqueIndex, json } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
import { Actor } from "../actor"
|
||||
|
||||
export const KeyTable = pgTable(
|
||||
export const KeyTable = mysqlTable(
|
||||
"key",
|
||||
{
|
||||
...workspaceColumns,
|
||||
...timestamps,
|
||||
userID: text("user_id").notNull(),
|
||||
actor: json("actor").$type<Actor.Info>(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
key: varchar("key", { length: 255 }).notNull(),
|
||||
timeUsed: utc("time_used"),
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { text, pgTable, uniqueIndex, varchar, integer } from "drizzle-orm/pg-core"
|
||||
import { text, mysqlTable, uniqueIndex, varchar, int } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, utc, workspaceColumns } from "../drizzle/types"
|
||||
import { workspaceIndexes } from "./workspace.sql"
|
||||
|
||||
export const UserTable = pgTable(
|
||||
export const UserTable = mysqlTable(
|
||||
"user",
|
||||
{
|
||||
...workspaceColumns,
|
||||
...timestamps,
|
||||
email: text("email").notNull(),
|
||||
email: varchar("email", { length: 255 }).notNull(),
|
||||
name: varchar("name", { length: 255 }).notNull(),
|
||||
timeSeen: utc("time_seen"),
|
||||
color: integer("color"),
|
||||
color: int("color"),
|
||||
},
|
||||
(table) => [...workspaceIndexes(table), uniqueIndex("user_email").on(table.workspaceID, table.email)],
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { primaryKey, foreignKey, pgTable, uniqueIndex, varchar } from "drizzle-orm/pg-core"
|
||||
import { primaryKey, mysqlTable, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||
import { timestamps, ulid } from "../drizzle/types"
|
||||
|
||||
export const WorkspaceTable = pgTable(
|
||||
export const WorkspaceTable = mysqlTable(
|
||||
"workspace",
|
||||
{
|
||||
id: ulid("id").notNull().primaryKey(),
|
||||
@@ -17,9 +17,5 @@ export function workspaceIndexes(table: any) {
|
||||
primaryKey({
|
||||
columns: [table.workspaceID, table.id],
|
||||
}),
|
||||
foreignKey({
|
||||
foreignColumns: [WorkspaceTable.id],
|
||||
columns: [table.workspaceID],
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
export function memo<T>(fn: () => T) {
|
||||
export function memo<T>(fn: () => T, cleanup?: (input: T) => Promise<void>) {
|
||||
let value: T | undefined
|
||||
let loaded = false
|
||||
|
||||
return (): T => {
|
||||
const result = (): T => {
|
||||
if (loaded) return value as T
|
||||
loaded = true
|
||||
value = fn()
|
||||
return value as T
|
||||
}
|
||||
result.reset = async () => {
|
||||
if (cleanup && value) await cleanup(value)
|
||||
loaded = false
|
||||
value = undefined
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export namespace Workspace {
|
||||
await tx.insert(BillingTable).values({
|
||||
workspaceID,
|
||||
id: Identifier.create("billing"),
|
||||
balance: centsToMicroCents(100),
|
||||
balance: 0,
|
||||
})
|
||||
})
|
||||
await Actor.provide(
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"name": "@opencode/cloud-function",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "4.20250522.0",
|
||||
"@types/node": "catalog:",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { Account } from "@opencode/cloud-core/account.js"
|
||||
import { Workspace } from "@opencode/cloud-core/workspace.js"
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
import { Database } from "@opencode/cloud-core/drizzle/index.js"
|
||||
|
||||
type Env = {
|
||||
AuthStorage: KVNamespace
|
||||
@@ -33,7 +34,7 @@ const MY_THEME: Theme = {
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
||||
return issuer({
|
||||
const result = await issuer({
|
||||
theme: MY_THEME,
|
||||
providers: {
|
||||
github: GithubProvider({
|
||||
@@ -135,5 +136,6 @@ export default {
|
||||
return ctx.subject("account", accountID, { accountID, email })
|
||||
},
|
||||
}).fetch(request, env, ctx)
|
||||
return result
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,596 +0,0 @@
|
||||
import { Hono, MiddlewareHandler } from "hono"
|
||||
import { type ProviderMetadata, type LanguageModelUsage } from "ai"
|
||||
import { createAnthropic } from "@ai-sdk/anthropic"
|
||||
import { createOpenAI } from "@ai-sdk/openai"
|
||||
import { createOpenAICompatible } from "@ai-sdk/openai-compatible"
|
||||
import type { LanguageModelV2Prompt } from "@ai-sdk/provider"
|
||||
import { type ChatCompletionCreateParamsBase } from "openai/resources/chat/completions"
|
||||
import { Actor } from "@opencode/cloud-core/actor.js"
|
||||
import { Database, eq, sql } from "@opencode/cloud-core/drizzle/index.js"
|
||||
import { KeyTable } from "@opencode/cloud-core/schema/key.sql.js"
|
||||
import { Billing } from "@opencode/cloud-core/billing.js"
|
||||
import { Resource } from "@opencode/cloud-resource"
|
||||
|
||||
type Env = {}
|
||||
|
||||
const SUPPORTED_MODELS = {
|
||||
"anthropic/claude-sonnet-4": {
|
||||
input: 0.0000015,
|
||||
output: 0.000006,
|
||||
reasoning: 0.0000015,
|
||||
cacheRead: 0.0000001,
|
||||
cacheWrite: 0.0000001,
|
||||
model: () =>
|
||||
createAnthropic({
|
||||
apiKey: Resource.ANTHROPIC_API_KEY.value,
|
||||
})("claude-sonnet-4-20250514"),
|
||||
},
|
||||
"openai/gpt-4.1": {
|
||||
input: 0.0000015,
|
||||
output: 0.000006,
|
||||
reasoning: 0.0000015,
|
||||
cacheRead: 0.0000001,
|
||||
cacheWrite: 0.0000001,
|
||||
model: () =>
|
||||
createOpenAI({
|
||||
apiKey: Resource.OPENAI_API_KEY.value,
|
||||
})("gpt-4.1"),
|
||||
},
|
||||
"zhipuai/glm-4.5-flash": {
|
||||
input: 0,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
model: () =>
|
||||
createOpenAICompatible({
|
||||
name: "Zhipu AI",
|
||||
baseURL: "https://api.z.ai/api/paas/v4",
|
||||
apiKey: Resource.ZHIPU_API_KEY.value,
|
||||
})("glm-4.5-flash"),
|
||||
},
|
||||
}
|
||||
|
||||
const GatewayAuth: MiddlewareHandler = async (c, next) => {
|
||||
const authHeader = c.req.header("authorization")
|
||||
|
||||
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
||||
return c.json(
|
||||
{
|
||||
error: {
|
||||
message: "Missing API key.",
|
||||
type: "invalid_request_error",
|
||||
param: null,
|
||||
code: "unauthorized",
|
||||
},
|
||||
},
|
||||
401,
|
||||
)
|
||||
}
|
||||
|
||||
const apiKey = authHeader.split(" ")[1]
|
||||
|
||||
// Check against KeyTable
|
||||
const keyRecord = await Database.use((tx) =>
|
||||
tx
|
||||
.select({
|
||||
id: KeyTable.id,
|
||||
workspaceID: KeyTable.workspaceID,
|
||||
})
|
||||
.from(KeyTable)
|
||||
.where(eq(KeyTable.key, apiKey))
|
||||
.then((rows) => rows[0]),
|
||||
)
|
||||
|
||||
if (!keyRecord) {
|
||||
return c.json(
|
||||
{
|
||||
error: {
|
||||
message: "Invalid API key.",
|
||||
type: "invalid_request_error",
|
||||
param: null,
|
||||
code: "unauthorized",
|
||||
},
|
||||
},
|
||||
401,
|
||||
)
|
||||
}
|
||||
|
||||
c.set("keyRecord", keyRecord)
|
||||
await next()
|
||||
}
|
||||
|
||||
const app = new Hono<{ Bindings: Env; Variables: { keyRecord?: { id: string; workspaceID: string } } }>()
|
||||
.get("/", (c) => c.text("Hello, world!"))
|
||||
.post("/v1/chat/completions", GatewayAuth, async (c) => {
|
||||
const keyRecord = c.get("keyRecord")!
|
||||
|
||||
return await Actor.provide("system", { workspaceID: keyRecord.workspaceID }, async () => {
|
||||
try {
|
||||
// Check balance
|
||||
const customer = await Billing.get()
|
||||
if (customer.balance <= 0) {
|
||||
return c.json(
|
||||
{
|
||||
error: {
|
||||
message: "Insufficient balance",
|
||||
type: "insufficient_quota",
|
||||
param: null,
|
||||
code: "insufficient_quota",
|
||||
},
|
||||
},
|
||||
401,
|
||||
)
|
||||
}
|
||||
|
||||
const body = await c.req.json<ChatCompletionCreateParamsBase>()
|
||||
const model = SUPPORTED_MODELS[body.model as keyof typeof SUPPORTED_MODELS]?.model()
|
||||
if (!model) throw new Error(`Unsupported model: ${body.model}`)
|
||||
|
||||
const requestBody = transformOpenAIRequestToAiSDK()
|
||||
|
||||
return body.stream ? await handleStream() : await handleGenerate()
|
||||
|
||||
async function handleStream() {
|
||||
const result = await model.doStream({
|
||||
...requestBody,
|
||||
})
|
||||
|
||||
const encoder = new TextEncoder()
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const id = `chatcmpl-${Date.now()}`
|
||||
const created = Math.floor(Date.now() / 1000)
|
||||
|
||||
try {
|
||||
for await (const chunk of result.stream) {
|
||||
console.log("!!! CHUNK !!! : " + chunk.type)
|
||||
switch (chunk.type) {
|
||||
case "text-delta": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
content: chunk.delta,
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "reasoning-delta": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
reasoning_content: chunk.delta,
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "tool-call": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: chunk.toolCallId,
|
||||
type: "function",
|
||||
function: {
|
||||
name: chunk.toolName,
|
||||
arguments: chunk.input,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
break
|
||||
}
|
||||
|
||||
case "error": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason: "stop",
|
||||
},
|
||||
],
|
||||
error: {
|
||||
message: typeof chunk.error === "string" ? chunk.error : chunk.error,
|
||||
type: "server_error",
|
||||
},
|
||||
}
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
break
|
||||
}
|
||||
|
||||
case "finish": {
|
||||
const data = {
|
||||
id,
|
||||
object: "chat.completion.chunk",
|
||||
created,
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason:
|
||||
{
|
||||
stop: "stop",
|
||||
length: "length",
|
||||
"content-filter": "content_filter",
|
||||
"tool-calls": "tool_calls",
|
||||
error: "stop",
|
||||
other: "stop",
|
||||
unknown: "stop",
|
||||
}[chunk.finishReason] || "stop",
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: chunk.usage.inputTokens,
|
||||
completion_tokens: chunk.usage.outputTokens,
|
||||
total_tokens: chunk.usage.totalTokens,
|
||||
completion_tokens_details: {
|
||||
reasoning_tokens: chunk.usage.reasoningTokens,
|
||||
},
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: chunk.usage.cachedInputTokens,
|
||||
},
|
||||
},
|
||||
}
|
||||
await trackUsage(body.model, chunk.usage, chunk.providerMetadata)
|
||||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`))
|
||||
controller.enqueue(encoder.encode("data: [DONE]\n\n"))
|
||||
controller.close()
|
||||
break
|
||||
}
|
||||
|
||||
//case "stream-start":
|
||||
//case "response-metadata":
|
||||
case "text-start":
|
||||
case "text-end":
|
||||
case "reasoning-start":
|
||||
case "reasoning-end":
|
||||
case "tool-input-start":
|
||||
case "tool-input-delta":
|
||||
case "tool-input-end":
|
||||
case "raw":
|
||||
default:
|
||||
// Log unknown chunk types for debugging
|
||||
console.warn(`Unknown chunk type: ${(chunk as any).type}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
controller.error(error)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return new Response(stream, {
|
||||
headers: {
|
||||
"Content-Type": "text/plain; charset=utf-8",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function handleGenerate() {
|
||||
const response = await model.doGenerate({
|
||||
...requestBody,
|
||||
})
|
||||
await trackUsage(body.model, response.usage, response.providerMetadata)
|
||||
return c.json({
|
||||
id: `chatcmpl-${Date.now()}`,
|
||||
object: "chat.completion" as const,
|
||||
created: Math.floor(Date.now() / 1000),
|
||||
model: body.model,
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
message: {
|
||||
role: "assistant" as const,
|
||||
content: response.content?.find((c) => c.type === "text")?.text ?? "",
|
||||
reasoning_content: response.content?.find((c) => c.type === "reasoning")?.text,
|
||||
tool_calls: response.content
|
||||
?.filter((c) => c.type === "tool-call")
|
||||
.map((toolCall) => ({
|
||||
id: toolCall.toolCallId,
|
||||
type: "function" as const,
|
||||
function: {
|
||||
name: toolCall.toolName,
|
||||
arguments: toolCall.input,
|
||||
},
|
||||
})),
|
||||
},
|
||||
finish_reason:
|
||||
(
|
||||
{
|
||||
stop: "stop",
|
||||
length: "length",
|
||||
"content-filter": "content_filter",
|
||||
"tool-calls": "tool_calls",
|
||||
error: "stop",
|
||||
other: "stop",
|
||||
unknown: "stop",
|
||||
} as const
|
||||
)[response.finishReason] || "stop",
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: response.usage?.inputTokens,
|
||||
completion_tokens: response.usage?.outputTokens,
|
||||
total_tokens: response.usage?.totalTokens,
|
||||
completion_tokens_details: {
|
||||
reasoning_tokens: response.usage?.reasoningTokens,
|
||||
},
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: response.usage?.cachedInputTokens,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function transformOpenAIRequestToAiSDK() {
|
||||
const prompt = transformMessages()
|
||||
const tools = transformTools()
|
||||
|
||||
return {
|
||||
prompt,
|
||||
maxOutputTokens: body.max_tokens ?? body.max_completion_tokens ?? undefined,
|
||||
temperature: body.temperature ?? undefined,
|
||||
topP: body.top_p ?? undefined,
|
||||
frequencyPenalty: body.frequency_penalty ?? undefined,
|
||||
presencePenalty: body.presence_penalty ?? undefined,
|
||||
providerOptions: body.reasoning_effort
|
||||
? {
|
||||
anthropic: {
|
||||
reasoningEffort: body.reasoning_effort,
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
stopSequences: (typeof body.stop === "string" ? [body.stop] : body.stop) ?? undefined,
|
||||
responseFormat: (() => {
|
||||
if (!body.response_format) return { type: "text" as const }
|
||||
if (body.response_format.type === "json_schema")
|
||||
return {
|
||||
type: "json" as const,
|
||||
schema: body.response_format.json_schema.schema,
|
||||
name: body.response_format.json_schema.name,
|
||||
description: body.response_format.json_schema.description,
|
||||
}
|
||||
if (body.response_format.type === "json_object") return { type: "json" as const }
|
||||
throw new Error("Unsupported response format")
|
||||
})(),
|
||||
seed: body.seed ?? undefined,
|
||||
tools: tools.tools,
|
||||
toolChoice: tools.toolChoice,
|
||||
}
|
||||
|
||||
function transformTools() {
|
||||
const { tools, tool_choice } = body
|
||||
|
||||
if (!tools || tools.length === 0) {
|
||||
return { tools: undefined, toolChoice: undefined }
|
||||
}
|
||||
|
||||
const aiSdkTools = tools.map((tool) => {
|
||||
return {
|
||||
type: tool.type,
|
||||
name: tool.function.name,
|
||||
description: tool.function.description,
|
||||
inputSchema: tool.function.parameters!,
|
||||
}
|
||||
})
|
||||
|
||||
let aiSdkToolChoice
|
||||
if (tool_choice == null) {
|
||||
aiSdkToolChoice = undefined
|
||||
} else if (tool_choice === "auto") {
|
||||
aiSdkToolChoice = { type: "auto" as const }
|
||||
} else if (tool_choice === "none") {
|
||||
aiSdkToolChoice = { type: "none" as const }
|
||||
} else if (tool_choice === "required") {
|
||||
aiSdkToolChoice = { type: "required" as const }
|
||||
} else if (tool_choice.type === "function") {
|
||||
aiSdkToolChoice = {
|
||||
type: "tool" as const,
|
||||
toolName: tool_choice.function.name,
|
||||
}
|
||||
}
|
||||
|
||||
return { tools: aiSdkTools, toolChoice: aiSdkToolChoice }
|
||||
}
|
||||
|
||||
function transformMessages() {
|
||||
const { messages } = body
|
||||
const prompt: LanguageModelV2Prompt = []
|
||||
|
||||
for (const message of messages) {
|
||||
switch (message.role) {
|
||||
case "system": {
|
||||
prompt.push({
|
||||
role: "system",
|
||||
content: message.content as string,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "user": {
|
||||
if (typeof message.content === "string") {
|
||||
prompt.push({
|
||||
role: "user",
|
||||
content: [{ type: "text", text: message.content }],
|
||||
})
|
||||
} else {
|
||||
const content = message.content.map((part) => {
|
||||
switch (part.type) {
|
||||
case "text":
|
||||
return { type: "text" as const, text: part.text }
|
||||
case "image_url":
|
||||
return {
|
||||
type: "file" as const,
|
||||
mediaType: "image/jpeg" as const,
|
||||
data: part.image_url.url,
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported content part type: ${(part as any).type}`)
|
||||
}
|
||||
})
|
||||
prompt.push({
|
||||
role: "user",
|
||||
content,
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case "assistant": {
|
||||
const content: Array<
|
||||
| { type: "text"; text: string }
|
||||
| {
|
||||
type: "tool-call"
|
||||
toolCallId: string
|
||||
toolName: string
|
||||
input: any
|
||||
}
|
||||
> = []
|
||||
|
||||
if (message.content) {
|
||||
content.push({
|
||||
type: "text",
|
||||
text: message.content as string,
|
||||
})
|
||||
}
|
||||
|
||||
if (message.tool_calls) {
|
||||
for (const toolCall of message.tool_calls) {
|
||||
content.push({
|
||||
type: "tool-call",
|
||||
toolCallId: toolCall.id,
|
||||
toolName: toolCall.function.name,
|
||||
input: JSON.parse(toolCall.function.arguments),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
prompt.push({
|
||||
role: "assistant",
|
||||
content,
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
case "tool": {
|
||||
prompt.push({
|
||||
role: "tool",
|
||||
content: [
|
||||
{
|
||||
type: "tool-result",
|
||||
toolName: "placeholder",
|
||||
toolCallId: message.tool_call_id,
|
||||
output: {
|
||||
type: "text",
|
||||
value: message.content as string,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new Error(`Unsupported message role: ${message.role}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return prompt
|
||||
}
|
||||
}
|
||||
|
||||
async function trackUsage(model: string, usage: LanguageModelUsage, providerMetadata?: ProviderMetadata) {
|
||||
const modelData = SUPPORTED_MODELS[model as keyof typeof SUPPORTED_MODELS]
|
||||
if (!modelData) throw new Error(`Unsupported model: ${model}`)
|
||||
|
||||
const inputTokens = usage.inputTokens ?? 0
|
||||
const outputTokens = usage.outputTokens ?? 0
|
||||
const reasoningTokens = usage.reasoningTokens ?? 0
|
||||
const cacheReadTokens = usage.cachedInputTokens ?? 0
|
||||
const cacheWriteTokens =
|
||||
providerMetadata?.["anthropic"]?.["cacheCreationInputTokens"] ??
|
||||
// @ts-expect-error
|
||||
providerMetadata?.["bedrock"]?.["usage"]?.["cacheWriteInputTokens"] ??
|
||||
0
|
||||
|
||||
const inputCost = modelData.input * inputTokens
|
||||
const outputCost = modelData.output * outputTokens
|
||||
const reasoningCost = modelData.reasoning * reasoningTokens
|
||||
const cacheReadCost = modelData.cacheRead * cacheReadTokens
|
||||
const cacheWriteCost = modelData.cacheWrite * cacheWriteTokens
|
||||
const costInCents = (inputCost + outputCost + reasoningCost + cacheReadCost + cacheWriteCost) * 100
|
||||
|
||||
await Billing.consume({
|
||||
model,
|
||||
inputTokens,
|
||||
outputTokens,
|
||||
reasoningTokens,
|
||||
cacheReadTokens,
|
||||
cacheWriteTokens,
|
||||
costInCents,
|
||||
})
|
||||
|
||||
await Database.use((tx) =>
|
||||
tx
|
||||
.update(KeyTable)
|
||||
.set({ timeUsed: sql`now()` })
|
||||
.where(eq(KeyTable.id, keyRecord.id)),
|
||||
)
|
||||
}
|
||||
} catch (error: any) {
|
||||
return c.json({ error: { message: error.message } }, 500)
|
||||
}
|
||||
})
|
||||
})
|
||||
.all("*", (c) => c.text("Not Found"))
|
||||
|
||||
export type ApiType = typeof app
|
||||
|
||||
export default app
|
||||
Vendored
+5
-14
@@ -14,18 +14,14 @@ declare module "sst" {
|
||||
"type": "sst.sst.Linkable"
|
||||
"value": string
|
||||
}
|
||||
"BASETEN_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Console": {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DATABASE_PASSWORD": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DATABASE_USERNAME": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Database": {
|
||||
"database": string
|
||||
"host": string
|
||||
@@ -54,10 +50,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"OPENAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"STRIPE_SECRET_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -70,7 +62,7 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.Astro"
|
||||
"url": string
|
||||
}
|
||||
"ZHIPU_API_KEY": {
|
||||
"XAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
@@ -84,7 +76,6 @@ declare module "sst" {
|
||||
"AuthApi": cloudflare.Service
|
||||
"AuthStorage": cloudflare.KVNamespace
|
||||
"Bucket": cloudflare.R2Bucket
|
||||
"GatewayApi": cloudflare.Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+5
-14
@@ -14,18 +14,14 @@ declare module "sst" {
|
||||
"type": "sst.sst.Linkable"
|
||||
"value": string
|
||||
}
|
||||
"BASETEN_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Console": {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DATABASE_PASSWORD": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DATABASE_USERNAME": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Database": {
|
||||
"database": string
|
||||
"host": string
|
||||
@@ -54,10 +50,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"OPENAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"STRIPE_SECRET_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -70,7 +62,7 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.Astro"
|
||||
"url": string
|
||||
}
|
||||
"ZHIPU_API_KEY": {
|
||||
"XAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
@@ -84,7 +76,6 @@ declare module "sst" {
|
||||
"AuthApi": cloudflare.Service
|
||||
"AuthStorage": cloudflare.KVNamespace
|
||||
"Bucket": cloudflare.R2Bucket
|
||||
"GatewayApi": cloudflare.Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ export const api = new sst.cloudflare.Worker("Api", {
|
||||
},
|
||||
})
|
||||
|
||||
export const web = new sst.cloudflare.x.Astro("Web", {
|
||||
domain,
|
||||
new sst.cloudflare.x.Astro("Web", {
|
||||
domain: "docs." + domain,
|
||||
path: "packages/web",
|
||||
environment: {
|
||||
// For astro config
|
||||
|
||||
+36
-27
@@ -5,15 +5,38 @@ import { domain } from "./stage"
|
||||
// DATABASE
|
||||
////////////////
|
||||
|
||||
const DATABASE_USERNAME = new sst.Secret("DATABASE_USERNAME")
|
||||
const DATABASE_PASSWORD = new sst.Secret("DATABASE_PASSWORD")
|
||||
const cluster = planetscale.getDatabaseOutput({
|
||||
name: "opencode",
|
||||
organization: "sst",
|
||||
})
|
||||
|
||||
const branch =
|
||||
$app.stage === "production"
|
||||
? planetscale.getBranchOutput({
|
||||
name: "production",
|
||||
organization: cluster.organization,
|
||||
database: cluster.name,
|
||||
})
|
||||
: new planetscale.Branch("DatabaseBranch", {
|
||||
database: cluster.name,
|
||||
organization: cluster.organization,
|
||||
name: $app.stage,
|
||||
parentBranch: "production",
|
||||
})
|
||||
const password = new planetscale.Password("DatabasePassword", {
|
||||
name: $app.stage,
|
||||
database: cluster.name,
|
||||
organization: cluster.organization,
|
||||
branch: branch.name,
|
||||
})
|
||||
|
||||
export const database = new sst.Linkable("Database", {
|
||||
properties: {
|
||||
host: `aws-us-east-2-${$app.stage === "thdxr" ? "2" : "1"}.pg.psdb.cloud`,
|
||||
database: "postgres",
|
||||
username: DATABASE_USERNAME.value,
|
||||
password: DATABASE_PASSWORD.value,
|
||||
port: 5432,
|
||||
host: password.accessHostUrl,
|
||||
database: cluster.name,
|
||||
username: password.username,
|
||||
password: password.plaintext,
|
||||
port: 3306,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -77,8 +100,8 @@ export const stripeWebhook = new WebhookEndpoint("StripeWebhook", {
|
||||
})
|
||||
|
||||
const ANTHROPIC_API_KEY = new sst.Secret("ANTHROPIC_API_KEY")
|
||||
const OPENAI_API_KEY = new sst.Secret("OPENAI_API_KEY")
|
||||
const ZHIPU_API_KEY = new sst.Secret("ZHIPU_API_KEY")
|
||||
const XAI_API_KEY = new sst.Secret("XAI_API_KEY")
|
||||
const BASETEN_API_KEY = new sst.Secret("BASETEN_API_KEY")
|
||||
const STRIPE_SECRET_KEY = new sst.Secret("STRIPE_SECRET_KEY")
|
||||
const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
properties: { value: auth.url.apply((url) => url!) },
|
||||
@@ -86,27 +109,13 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
|
||||
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
|
||||
properties: { value: stripeWebhook.secret },
|
||||
})
|
||||
export const gateway = new sst.cloudflare.Worker("GatewayApi", {
|
||||
domain: `api.gateway.${domain}`,
|
||||
handler: "cloud/function/src/gateway.ts",
|
||||
url: true,
|
||||
link: [
|
||||
database,
|
||||
AUTH_API_URL,
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
STRIPE_SECRET_KEY,
|
||||
ANTHROPIC_API_KEY,
|
||||
OPENAI_API_KEY,
|
||||
ZHIPU_API_KEY,
|
||||
],
|
||||
})
|
||||
|
||||
////////////////
|
||||
// CONSOLE
|
||||
////////////////
|
||||
|
||||
export const console = new sst.cloudflare.x.SolidStart("Console", {
|
||||
domain: `console.${domain}`,
|
||||
new sst.cloudflare.x.SolidStart("Console", {
|
||||
domain,
|
||||
path: "cloud/app",
|
||||
link: [
|
||||
database,
|
||||
@@ -114,8 +123,8 @@ export const console = new sst.cloudflare.x.SolidStart("Console", {
|
||||
STRIPE_WEBHOOK_SECRET,
|
||||
STRIPE_SECRET_KEY,
|
||||
ANTHROPIC_API_KEY,
|
||||
OPENAI_API_KEY,
|
||||
ZHIPU_API_KEY,
|
||||
XAI_API_KEY,
|
||||
BASETEN_API_KEY,
|
||||
],
|
||||
environment: {
|
||||
//VITE_DOCS_URL: web.url.apply((url) => url!),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
|
||||
"mcp": {
|
||||
"weather": {
|
||||
"type": "local",
|
||||
|
||||
+3
-1
@@ -55,5 +55,7 @@
|
||||
"overrides": {
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"patchedDependencies": {}
|
||||
"patchedDependencies": {
|
||||
"@solidjs/start@1.1.7": "patches/@solidjs%2Fstart@1.1.7.patch"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@opencode/function",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
Vendored
+5
-14
@@ -14,18 +14,14 @@ declare module "sst" {
|
||||
"type": "sst.sst.Linkable"
|
||||
"value": string
|
||||
}
|
||||
"BASETEN_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Console": {
|
||||
"type": "sst.cloudflare.SolidStart"
|
||||
"url": string
|
||||
}
|
||||
"DATABASE_PASSWORD": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"DATABASE_USERNAME": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"Database": {
|
||||
"database": string
|
||||
"host": string
|
||||
@@ -54,10 +50,6 @@ declare module "sst" {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"OPENAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
"STRIPE_SECRET_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
@@ -70,7 +62,7 @@ declare module "sst" {
|
||||
"type": "sst.cloudflare.Astro"
|
||||
"url": string
|
||||
}
|
||||
"ZHIPU_API_KEY": {
|
||||
"XAI_API_KEY": {
|
||||
"type": "sst.sst.Secret"
|
||||
"value": string
|
||||
}
|
||||
@@ -84,7 +76,6 @@ declare module "sst" {
|
||||
"AuthApi": cloudflare.Service
|
||||
"AuthStorage": cloudflare.KVNamespace
|
||||
"Bucket": cloudflare.R2Bucket
|
||||
"GatewayApi": cloudflare.Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"name": "opencode",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
|
||||
@@ -103,12 +103,13 @@ export const AuthLoginCommand = cmd({
|
||||
await ModelsDev.refresh().catch(() => {})
|
||||
const providers = await ModelsDev.get()
|
||||
const priority: Record<string, number> = {
|
||||
anthropic: 0,
|
||||
"github-copilot": 1,
|
||||
openai: 2,
|
||||
google: 3,
|
||||
openrouter: 4,
|
||||
vercel: 5,
|
||||
opencode: 0,
|
||||
anthropic: 1,
|
||||
"github-copilot": 2,
|
||||
openai: 3,
|
||||
google: 4,
|
||||
openrouter: 5,
|
||||
vercel: 6,
|
||||
}
|
||||
let provider = await prompts.autocomplete({
|
||||
message: "Select provider",
|
||||
@@ -124,7 +125,7 @@ export const AuthLoginCommand = cmd({
|
||||
map((x) => ({
|
||||
label: x.name,
|
||||
value: x.id,
|
||||
hint: priority[x.id] === 0 ? "recommended" : undefined,
|
||||
hint: priority[x.id] <= 1 ? "recommended" : undefined,
|
||||
})),
|
||||
),
|
||||
{
|
||||
|
||||
@@ -93,10 +93,13 @@ export const GithubInstallCommand = cmd({
|
||||
|
||||
async function promptProvider() {
|
||||
const priority: Record<string, number> = {
|
||||
anthropic: 0,
|
||||
"github-copilot": 1,
|
||||
openai: 2,
|
||||
google: 3,
|
||||
opencode: 0,
|
||||
anthropic: 1,
|
||||
"github-copilot": 2,
|
||||
openai: 3,
|
||||
google: 4,
|
||||
openrouter: 5,
|
||||
vercel: 6,
|
||||
}
|
||||
let provider = await prompts.select({
|
||||
message: "Select provider",
|
||||
@@ -111,7 +114,7 @@ export const GithubInstallCommand = cmd({
|
||||
map((x) => ({
|
||||
label: x.name,
|
||||
value: x.id,
|
||||
hint: priority[x.id] === 0 ? "recommended" : undefined,
|
||||
hint: priority[x.id] <= 1 ? "recommended" : undefined,
|
||||
})),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -10,6 +10,7 @@ import { bootstrap } from "../bootstrap"
|
||||
import { MessageV2 } from "../../session/message-v2"
|
||||
import { Identifier } from "../../id/id"
|
||||
import { Agent } from "../../agent/agent"
|
||||
import { Command } from "../../command"
|
||||
|
||||
const TOOL: Record<string, [string, string]> = {
|
||||
todowrite: ["Todo", UI.Style.TEXT_WARNING_BOLD],
|
||||
@@ -35,6 +36,10 @@ export const RunCommand = cmd({
|
||||
array: true,
|
||||
default: [],
|
||||
})
|
||||
.option("command", {
|
||||
describe: "the command to run, use message for args",
|
||||
type: "string",
|
||||
})
|
||||
.option("continue", {
|
||||
alias: ["c"],
|
||||
describe: "continue the last session",
|
||||
@@ -64,12 +69,19 @@ export const RunCommand = cmd({
|
||||
|
||||
if (!process.stdin.isTTY) message += "\n" + (await Bun.stdin.text())
|
||||
|
||||
if (message.trim().length === 0) {
|
||||
UI.error("Message cannot be empty")
|
||||
if (message.trim().length === 0 && !args.command) {
|
||||
UI.error("You must provide a message or a command")
|
||||
return
|
||||
}
|
||||
|
||||
await bootstrap(process.cwd(), async () => {
|
||||
if (args.command) {
|
||||
const exists = await Command.get(args.command)
|
||||
if (!exists) {
|
||||
UI.error(`Command "${args.command}" not found`)
|
||||
return
|
||||
}
|
||||
}
|
||||
const session = await (async () => {
|
||||
if (args.continue) {
|
||||
const it = Session.list()
|
||||
@@ -172,12 +184,26 @@ export const RunCommand = cmd({
|
||||
UI.error(err)
|
||||
})
|
||||
|
||||
if (args.command) {
|
||||
await Session.command({
|
||||
messageID: Identifier.ascending("message"),
|
||||
sessionID: session.id,
|
||||
agent: agent.name,
|
||||
model: providerID + "/" + modelID,
|
||||
command: args.command,
|
||||
arguments: message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const messageID = Identifier.ascending("message")
|
||||
const result = await Session.chat({
|
||||
const result = await Session.prompt({
|
||||
sessionID: session.id,
|
||||
messageID,
|
||||
providerID,
|
||||
modelID,
|
||||
model: {
|
||||
providerID,
|
||||
modelID,
|
||||
},
|
||||
agent: agent.name,
|
||||
parts: [
|
||||
{
|
||||
@@ -190,7 +216,7 @@ export const RunCommand = cmd({
|
||||
|
||||
const isPiped = !process.stdout.isTTY
|
||||
if (isPiped) {
|
||||
const match = result.parts.findLast((x) => x.type === "text")
|
||||
const match = result.parts.findLast((x: any) => x.type === "text") as any
|
||||
if (match) process.stdout.write(UI.markdown(match.text))
|
||||
if (errorMsg) process.stdout.write(errorMsg)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,11 @@ export namespace Config {
|
||||
log.debug("loaded custom config", { path: Flag.OPENCODE_CONFIG })
|
||||
}
|
||||
|
||||
if (Flag.OPENCODE_CONFIG_CONTENT) {
|
||||
result = mergeDeep(result, JSON.parse(Flag.OPENCODE_CONFIG_CONTENT))
|
||||
log.debug("loaded custom config from OPENCODE_CONFIG_CONTENT")
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(auth)) {
|
||||
if (value.type === "wellknown") {
|
||||
process.env[value.key] = value.token
|
||||
@@ -45,7 +50,7 @@ export namespace Config {
|
||||
result.agent = result.agent || {}
|
||||
const markdownAgents = [
|
||||
...(await Filesystem.globUp("agent/**/*.md", Global.Path.config, Global.Path.config)),
|
||||
...(await Filesystem.globUp(".opencode/agent/*.md", Instance.directory, Instance.worktree)),
|
||||
...(await Filesystem.globUp(".opencode/agent/**/*.md", Instance.directory, Instance.worktree)),
|
||||
]
|
||||
for (const item of markdownAgents) {
|
||||
const content = await Bun.file(item).text()
|
||||
|
||||
@@ -154,13 +154,12 @@ export namespace File {
|
||||
if (exclude.includes(entry.name)) continue
|
||||
const fullPath = path.join(resolved, entry.name)
|
||||
const relativePath = path.relative(Instance.directory, fullPath)
|
||||
const relativeToRoot = path.relative(Instance.worktree, fullPath)
|
||||
const type = entry.isDirectory() ? "directory" : "file"
|
||||
nodes.push({
|
||||
name: entry.name,
|
||||
path: relativePath,
|
||||
type,
|
||||
ignored: ignored(type === "directory" ? relativeToRoot + "/" : relativeToRoot),
|
||||
ignored: ignored(type === "directory" ? relativePath + "/" : relativePath),
|
||||
})
|
||||
}
|
||||
return nodes.sort((a, b) => {
|
||||
|
||||
@@ -2,6 +2,7 @@ export namespace Flag {
|
||||
export const OPENCODE_AUTO_SHARE = truthy("OPENCODE_AUTO_SHARE")
|
||||
export const OPENCODE_DISABLE_WATCHER = truthy("OPENCODE_DISABLE_WATCHER")
|
||||
export const OPENCODE_CONFIG = process.env["OPENCODE_CONFIG"]
|
||||
export const OPENCODE_CONFIG_CONTENT = process.env["OPENCODE_CONFIG_CONTENT"]
|
||||
export const OPENCODE_DISABLE_AUTOUPDATE = truthy("OPENCODE_DISABLE_AUTOUPDATE")
|
||||
export const OPENCODE_PERMISSION = process.env["OPENCODE_PERMISSION"]
|
||||
export const OPENCODE_DISABLE_DEFAULT_PLUGINS = truthy("OPENCODE_DISABLE_DEFAULT_PLUGINS")
|
||||
|
||||
@@ -70,6 +70,7 @@ export namespace LSP {
|
||||
}
|
||||
servers[name] = {
|
||||
...existing,
|
||||
id: name,
|
||||
root: existing?.root ?? (async () => Instance.directory),
|
||||
extensions: item.extensions ?? existing.extensions,
|
||||
spawn: async (root) => {
|
||||
|
||||
@@ -193,7 +193,7 @@ export namespace LSPServer {
|
||||
}
|
||||
|
||||
export const Gopls: Info = {
|
||||
id: "golang",
|
||||
id: "gopls",
|
||||
root: async (file) => {
|
||||
const work = await NearestRoot(["go.work"])(file)
|
||||
if (work) return work
|
||||
|
||||
@@ -53,6 +53,17 @@ export namespace Project {
|
||||
.map((x) => x.trim())
|
||||
.toSorted(),
|
||||
)
|
||||
if (!id) {
|
||||
const project: Info = {
|
||||
id: "global",
|
||||
worktree: "/",
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
}
|
||||
await Storage.write<Info>(["project", "global"], project)
|
||||
return project
|
||||
}
|
||||
worktree = path.dirname(
|
||||
await $`git rev-parse --path-format=absolute --git-common-dir`
|
||||
.quiet()
|
||||
|
||||
@@ -153,7 +153,10 @@ export namespace Provider {
|
||||
options: Record<string, any>
|
||||
}
|
||||
} = {}
|
||||
const models = new Map<string, { info: ModelsDev.Model; language: LanguageModel }>()
|
||||
const models = new Map<
|
||||
string,
|
||||
{ providerID: string; modelID: string; info: ModelsDev.Model; language: LanguageModel }
|
||||
>()
|
||||
const sdk = new Map<string, SDK>()
|
||||
|
||||
log.info("init")
|
||||
@@ -362,10 +365,14 @@ export namespace Provider {
|
||||
const language = provider.getModel ? await provider.getModel(sdk, modelID) : sdk.languageModel(modelID)
|
||||
log.info("found", { providerID, modelID })
|
||||
s.models.set(key, {
|
||||
providerID,
|
||||
modelID,
|
||||
info,
|
||||
language,
|
||||
})
|
||||
return {
|
||||
modelID,
|
||||
providerID,
|
||||
info,
|
||||
language,
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ export namespace ProviderTransform {
|
||||
}
|
||||
|
||||
if (modelID.includes("gpt-5") && !modelID.includes("gpt-5-chat")) {
|
||||
result["reasoningEffort"] = "minimal"
|
||||
result["reasoningEffort"] = "high"
|
||||
if (providerID !== "azure") {
|
||||
result["textVerbosity"] = "low"
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ProjectRoute = new Hono()
|
||||
"/current",
|
||||
describeRoute({
|
||||
description: "Get the current project",
|
||||
operationId: "butt.current",
|
||||
operationId: "project.current",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Current project",
|
||||
|
||||
@@ -53,6 +53,9 @@ export namespace Server {
|
||||
const app = new Hono()
|
||||
export const App = app
|
||||
.onError((err, c) => {
|
||||
log.error("failed", {
|
||||
error: err,
|
||||
})
|
||||
if (err instanceof NamedError) {
|
||||
return c.json(err.toObject(), {
|
||||
status: 400,
|
||||
@@ -606,7 +609,7 @@ export namespace Server {
|
||||
"/session/:id/message",
|
||||
describeRoute({
|
||||
description: "Create and send a new message to a session",
|
||||
operationId: "session.chat",
|
||||
operationId: "session.prompt",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Created message",
|
||||
@@ -629,11 +632,11 @@ export namespace Server {
|
||||
id: z.string().openapi({ description: "Session ID" }),
|
||||
}),
|
||||
),
|
||||
zValidator("json", Session.ChatInput.omit({ sessionID: true })),
|
||||
zValidator("json", Session.PromptInput.omit({ sessionID: true })),
|
||||
async (c) => {
|
||||
const sessionID = c.req.valid("param").id
|
||||
const body = c.req.valid("json")
|
||||
const msg = await Session.chat({ ...body, sessionID })
|
||||
const msg = await Session.prompt({ ...body, sessionID })
|
||||
return c.json(msg)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from "path"
|
||||
import os from "os"
|
||||
import path from "path"
|
||||
import fs from "fs/promises"
|
||||
import { spawn } from "child_process"
|
||||
import { Decimal } from "decimal.js"
|
||||
import { z, ZodSchema } from "zod"
|
||||
@@ -364,11 +365,15 @@ export namespace Session {
|
||||
return part
|
||||
}
|
||||
|
||||
export const ChatInput = z.object({
|
||||
export const PromptInput = z.object({
|
||||
sessionID: Identifier.schema("session"),
|
||||
messageID: Identifier.schema("message").optional(),
|
||||
providerID: z.string(),
|
||||
modelID: z.string(),
|
||||
model: z
|
||||
.object({
|
||||
providerID: z.string(),
|
||||
modelID: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
agent: z.string().optional(),
|
||||
system: z.string().optional(),
|
||||
tools: z.record(z.boolean()).optional(),
|
||||
@@ -407,10 +412,10 @@ export namespace Session {
|
||||
]),
|
||||
),
|
||||
})
|
||||
export type ChatInput = z.infer<typeof ChatInput>
|
||||
export type ChatInput = z.infer<typeof PromptInput>
|
||||
|
||||
export async function chat(
|
||||
input: z.infer<typeof ChatInput>,
|
||||
export async function prompt(
|
||||
input: z.infer<typeof PromptInput>,
|
||||
): Promise<{ info: MessageV2.Assistant; parts: MessageV2.Part[] }> {
|
||||
const l = log.clone().tag("session", input.sessionID)
|
||||
l.info("chatting")
|
||||
@@ -652,7 +657,16 @@ export namespace Session {
|
||||
})
|
||||
}
|
||||
|
||||
const model = await Provider.getModel(input.providerID, input.modelID)
|
||||
const agent = await Agent.get(inputAgent)
|
||||
const model = await (async () => {
|
||||
if (input.model) {
|
||||
return input.model
|
||||
}
|
||||
if (agent.model) {
|
||||
return agent.model
|
||||
}
|
||||
return Provider.defaultModel()
|
||||
})().then((x) => Provider.getModel(x.providerID, x.modelID))
|
||||
let msgs = await messages(input.sessionID)
|
||||
|
||||
const previous = msgs.filter((x) => x.info.role === "assistant").at(-1)?.info as MessageV2.Assistant
|
||||
@@ -667,10 +681,10 @@ export namespace Session {
|
||||
|
||||
await summarize({
|
||||
sessionID: input.sessionID,
|
||||
providerID: input.providerID,
|
||||
modelID: input.modelID,
|
||||
providerID: model.providerID,
|
||||
modelID: model.info.id,
|
||||
})
|
||||
return chat(input)
|
||||
return prompt(input)
|
||||
}
|
||||
}
|
||||
using abort = lock(input.sessionID)
|
||||
@@ -679,17 +693,17 @@ export namespace Session {
|
||||
if (lastSummary) msgs = msgs.filter((msg) => msg.info.id >= lastSummary.info.id)
|
||||
|
||||
if (msgs.filter((m) => m.info.role === "user").length === 1 && !session.parentID && isDefaultTitle(session.title)) {
|
||||
const small = (await Provider.getSmallModel(input.providerID)) ?? model
|
||||
const small = (await Provider.getSmallModel(model.providerID)) ?? model
|
||||
generateText({
|
||||
maxOutputTokens: small.info.reasoning ? 1024 : 20,
|
||||
providerOptions: {
|
||||
[input.providerID]: {
|
||||
[model.providerID]: {
|
||||
...small.info.options,
|
||||
...ProviderTransform.options(input.providerID, small.info.id, input.sessionID),
|
||||
...ProviderTransform.options(small.providerID, small.modelID, input.sessionID),
|
||||
},
|
||||
},
|
||||
messages: [
|
||||
...SystemPrompt.title(input.providerID).map(
|
||||
...SystemPrompt.title(model.providerID).map(
|
||||
(x): ModelMessage => ({
|
||||
role: "system",
|
||||
content: x,
|
||||
@@ -724,7 +738,6 @@ export namespace Session {
|
||||
})
|
||||
}
|
||||
|
||||
const agent = await Agent.get(inputAgent)
|
||||
if (agent.name === "plan") {
|
||||
msgs.at(-1)?.parts.push({
|
||||
id: Identifier.ascending("part"),
|
||||
@@ -747,12 +760,12 @@ export namespace Session {
|
||||
synthetic: true,
|
||||
})
|
||||
}
|
||||
let system = SystemPrompt.header(input.providerID)
|
||||
let system = SystemPrompt.header(model.providerID)
|
||||
system.push(
|
||||
...(() => {
|
||||
if (input.system) return [input.system]
|
||||
if (agent.prompt) return [agent.prompt]
|
||||
return SystemPrompt.provider(input.modelID)
|
||||
return SystemPrompt.provider(model.modelID)
|
||||
})(),
|
||||
)
|
||||
system.push(...(await SystemPrompt.environment()))
|
||||
@@ -777,8 +790,8 @@ export namespace Session {
|
||||
reasoning: 0,
|
||||
cache: { read: 0, write: 0 },
|
||||
},
|
||||
modelID: input.modelID,
|
||||
providerID: input.providerID,
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
},
|
||||
@@ -796,10 +809,10 @@ export namespace Session {
|
||||
|
||||
const enabledTools = pipe(
|
||||
agent.tools,
|
||||
mergeDeep(await ToolRegistry.enabled(input.providerID, input.modelID, agent)),
|
||||
mergeDeep(await ToolRegistry.enabled(model.providerID, model.modelID, agent)),
|
||||
mergeDeep(input.tools ?? {}),
|
||||
)
|
||||
for (const item of await ToolRegistry.tools(input.providerID, input.modelID)) {
|
||||
for (const item of await ToolRegistry.tools(model.providerID, model.modelID)) {
|
||||
if (Wildcard.all(item.id, enabledTools) === false) continue
|
||||
tools[item.id] = tool({
|
||||
id: item.id as any,
|
||||
@@ -909,16 +922,16 @@ export namespace Session {
|
||||
"chat.params",
|
||||
{
|
||||
model: model.info,
|
||||
provider: await Provider.getProvider(input.providerID),
|
||||
provider: await Provider.getProvider(model.providerID),
|
||||
message: userMsg,
|
||||
},
|
||||
{
|
||||
temperature: model.info.temperature
|
||||
? (agent.temperature ?? ProviderTransform.temperature(input.providerID, input.modelID))
|
||||
? (agent.temperature ?? ProviderTransform.temperature(model.providerID, model.modelID))
|
||||
: undefined,
|
||||
topP: agent.topP ?? ProviderTransform.topP(input.providerID, input.modelID),
|
||||
topP: agent.topP ?? ProviderTransform.topP(model.providerID, model.modelID),
|
||||
options: {
|
||||
...ProviderTransform.options(input.providerID, input.modelID, input.sessionID),
|
||||
...ProviderTransform.options(model.providerID, model.modelID, input.sessionID),
|
||||
...model.info.options,
|
||||
...agent.options,
|
||||
},
|
||||
@@ -962,8 +975,8 @@ export namespace Session {
|
||||
reasoning: 0,
|
||||
cache: { read: 0, write: 0 },
|
||||
},
|
||||
modelID: input.modelID,
|
||||
providerID: input.providerID,
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
mode: inputAgent,
|
||||
time: {
|
||||
created: Date.now(),
|
||||
@@ -987,7 +1000,7 @@ export namespace Session {
|
||||
}
|
||||
},
|
||||
headers:
|
||||
input.providerID === "opencode"
|
||||
model.providerID === "opencode"
|
||||
? {
|
||||
"x-opencode-session": input.sessionID,
|
||||
"x-opencode-request": userMsg.id,
|
||||
@@ -1010,7 +1023,7 @@ export namespace Session {
|
||||
return false
|
||||
},
|
||||
providerOptions: {
|
||||
[input.providerID]: params.options,
|
||||
[model.providerID]: params.options,
|
||||
},
|
||||
temperature: params.temperature,
|
||||
topP: params.topP,
|
||||
@@ -1031,7 +1044,7 @@ export namespace Session {
|
||||
async transformParams(args) {
|
||||
if (args.type === "stream") {
|
||||
// @ts-expect-error
|
||||
args.params.prompt = ProviderTransform.message(args.params.prompt, input.providerID, input.modelID)
|
||||
args.params.prompt = ProviderTransform.message(args.params.prompt, model.providerID, model.modelID)
|
||||
}
|
||||
return args.params
|
||||
},
|
||||
@@ -1044,7 +1057,7 @@ export namespace Session {
|
||||
const unprocessed = queued.find((x) => !x.processed)
|
||||
if (unprocessed) {
|
||||
unprocessed.processed = true
|
||||
return chat(unprocessed.input)
|
||||
return prompt(unprocessed.input)
|
||||
}
|
||||
for (const item of queued) {
|
||||
item.callback(result)
|
||||
@@ -1217,26 +1230,20 @@ export namespace Session {
|
||||
})
|
||||
export type CommandInput = z.infer<typeof CommandInput>
|
||||
const bashRegex = /!`([^`]+)`/g
|
||||
const fileRegex = /@([^\s]+)/g
|
||||
/**
|
||||
* Regular expression to match @ file references in text
|
||||
* Matches @ followed by file paths, excluding commas, periods at end of sentences, and backticks
|
||||
* Does not match when preceded by word characters or backticks (to avoid email addresses and quoted references)
|
||||
*/
|
||||
export const fileRegex = /(?<![\w`])@(\.?[^\s`,.]*(?:\.[^\s`,.]+)*)/g
|
||||
|
||||
export async function command(input: CommandInput) {
|
||||
log.info("command", input)
|
||||
const command = await Command.get(input.command)
|
||||
const agent = command.agent ?? input.agent ?? "build"
|
||||
const fmtModel = (model: { providerID: string; modelID: string }) => `${model.providerID}/${model.modelID}`
|
||||
|
||||
const model =
|
||||
command.model ??
|
||||
(command.agent && (await Agent.get(command.agent).then((x) => (x.model ? fmtModel(x.model) : undefined)))) ??
|
||||
input.model ??
|
||||
(input.agent && (await Agent.get(input.agent).then((x) => (x.model ? fmtModel(x.model) : undefined)))) ??
|
||||
fmtModel(await Provider.defaultModel())
|
||||
|
||||
let template = command.template.replace("$ARGUMENTS", input.arguments)
|
||||
|
||||
// intentionally doing match regex doing bash regex replacements
|
||||
// this is because bash commands can output "@" references
|
||||
const fileMatches = template.matchAll(fileRegex)
|
||||
|
||||
const bash = Array.from(template.matchAll(bashRegex))
|
||||
if (bash.length > 0) {
|
||||
const results = await Promise.all(
|
||||
@@ -1259,24 +1266,49 @@ export namespace Session {
|
||||
},
|
||||
] as ChatInput["parts"]
|
||||
|
||||
for (const match of fileMatches) {
|
||||
const filename = match[1]
|
||||
const filepath = filename.startsWith("~/")
|
||||
? path.join(os.homedir(), filename.slice(2))
|
||||
: path.join(Instance.worktree, filename)
|
||||
const matches = Array.from(template.matchAll(fileRegex))
|
||||
await Promise.all(
|
||||
matches.map(async (match) => {
|
||||
const name = match[1]
|
||||
const filepath = name.startsWith("~/")
|
||||
? path.join(os.homedir(), name.slice(2))
|
||||
: path.resolve(Instance.worktree, name)
|
||||
|
||||
parts.push({
|
||||
type: "file",
|
||||
url: `file://${filepath}`,
|
||||
filename,
|
||||
mime: "text/plain",
|
||||
})
|
||||
}
|
||||
const stats = await fs.stat(filepath).catch(() => undefined)
|
||||
if (!stats) {
|
||||
const agent = await Agent.get(name)
|
||||
if (agent) {
|
||||
parts.push({
|
||||
type: "agent",
|
||||
name: agent.name,
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return chat({
|
||||
if (stats.isDirectory()) return
|
||||
|
||||
parts.push({
|
||||
type: "file",
|
||||
url: `file://${filepath}`,
|
||||
filename: name,
|
||||
mime: "text/plain",
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
return prompt({
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
...Provider.parseModel(model!),
|
||||
model: (() => {
|
||||
if (input.model) {
|
||||
return Provider.parseModel(input.model)
|
||||
}
|
||||
if (command.model) {
|
||||
return Provider.parseModel(command.model)
|
||||
}
|
||||
return undefined
|
||||
})(),
|
||||
agent,
|
||||
parts,
|
||||
})
|
||||
@@ -1643,7 +1675,7 @@ export namespace Session {
|
||||
const filtered = msgs.filter((msg) => !lastSummary || msg.info.id >= lastSummary.info.id)
|
||||
const model = await Provider.getModel(input.providerID, input.modelID)
|
||||
const system = [
|
||||
...SystemPrompt.summarize(input.providerID),
|
||||
...SystemPrompt.summarize(model.providerID),
|
||||
...(await SystemPrompt.environment()),
|
||||
...(await SystemPrompt.custom()),
|
||||
]
|
||||
@@ -1661,7 +1693,7 @@ export namespace Session {
|
||||
summary: true,
|
||||
cost: 0,
|
||||
modelID: input.modelID,
|
||||
providerID: input.providerID,
|
||||
providerID: model.providerID,
|
||||
tokens: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
@@ -1770,11 +1802,13 @@ export namespace Session {
|
||||
providerID: string
|
||||
messageID: string
|
||||
}) {
|
||||
await Session.chat({
|
||||
await Session.prompt({
|
||||
sessionID: input.sessionID,
|
||||
messageID: input.messageID,
|
||||
providerID: input.providerID,
|
||||
modelID: input.modelID,
|
||||
model: {
|
||||
providerID: input.providerID,
|
||||
modelID: input.modelID,
|
||||
},
|
||||
parts: [
|
||||
{
|
||||
id: Identifier.ascending("part"),
|
||||
|
||||
@@ -14,7 +14,7 @@ import PROMPT_GEMINI from "./prompt/gemini.txt"
|
||||
import PROMPT_ANTHROPIC_SPOOF from "./prompt/anthropic_spoof.txt"
|
||||
import PROMPT_SUMMARIZE from "./prompt/summarize.txt"
|
||||
import PROMPT_TITLE from "./prompt/title.txt"
|
||||
import PROMPT_COPILOT_GPT_5 from "./prompt/copilot-gpt-5.txt"
|
||||
import PROMPT_CODEX from "./prompt/codex.txt"
|
||||
|
||||
export namespace SystemPrompt {
|
||||
export function header(providerID: string) {
|
||||
@@ -23,7 +23,7 @@ export namespace SystemPrompt {
|
||||
}
|
||||
|
||||
export function provider(modelID: string) {
|
||||
if (modelID.includes("gpt-5")) return [PROMPT_COPILOT_GPT_5]
|
||||
if (modelID.includes("gpt-5")) return [PROMPT_CODEX]
|
||||
if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST]
|
||||
if (modelID.includes("gemini-")) return [PROMPT_GEMINI]
|
||||
if (modelID.includes("claude")) return [PROMPT_ANTHROPIC]
|
||||
|
||||
@@ -30,6 +30,7 @@ export namespace Storage {
|
||||
if (worktree) break
|
||||
}
|
||||
if (!worktree) continue
|
||||
if (!(await fs.exists(worktree))) continue
|
||||
const [id] = await $`git rev-list --max-parents=0 --all`
|
||||
.quiet()
|
||||
.nothrow()
|
||||
@@ -114,7 +115,9 @@ export namespace Storage {
|
||||
for (let index = migration; index < MIGRATIONS.length; index++) {
|
||||
log.info("running migration", { index })
|
||||
const migration = MIGRATIONS[index]
|
||||
await migration(dir)
|
||||
await migration(dir).catch((e) => {
|
||||
log.error("failed to run migration", { error: e, index })
|
||||
})
|
||||
await Bun.write(path.join(dir, "migration"), (index + 1).toString())
|
||||
}
|
||||
return {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Tool } from "./tool"
|
||||
import * as path from "path"
|
||||
import DESCRIPTION from "./ls.txt"
|
||||
import { Instance } from "../project/instance"
|
||||
import { Ripgrep } from "../file/ripgrep"
|
||||
|
||||
export const IGNORE_PATTERNS = [
|
||||
"node_modules/",
|
||||
@@ -42,15 +43,8 @@ export const ListTool = Tool.define("list", {
|
||||
async execute(params) {
|
||||
const searchPath = path.resolve(Instance.directory, params.path || ".")
|
||||
|
||||
const glob = new Bun.Glob("**/*")
|
||||
const files = []
|
||||
|
||||
for await (const file of glob.scan({ cwd: searchPath, dot: true })) {
|
||||
if (IGNORE_PATTERNS.some((p) => file.includes(p))) continue
|
||||
if (params.ignore?.some((pattern) => new Bun.Glob(pattern).match(file))) continue
|
||||
files.push(file)
|
||||
if (files.length >= LIMIT) break
|
||||
}
|
||||
const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || [])
|
||||
const files = await Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs, limit: LIMIT })
|
||||
|
||||
// Build directory structure
|
||||
const dirs = new Set<string>()
|
||||
|
||||
@@ -51,11 +51,13 @@ export const TaskTool = Tool.define("task", async () => {
|
||||
ctx.abort.addEventListener("abort", () => {
|
||||
Session.abort(session.id)
|
||||
})
|
||||
const result = await Session.chat({
|
||||
const result = await Session.prompt({
|
||||
messageID,
|
||||
sessionID: session.id,
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
model: {
|
||||
modelID: model.modelID,
|
||||
providerID: model.providerID,
|
||||
},
|
||||
agent: agent.name,
|
||||
tools: {
|
||||
todowrite: false,
|
||||
@@ -75,9 +77,9 @@ export const TaskTool = Tool.define("task", async () => {
|
||||
return {
|
||||
title: params.description,
|
||||
metadata: {
|
||||
summary: result.parts.filter((x) => x.type === "tool"),
|
||||
summary: result.parts.filter((x: any) => x.type === "tool"),
|
||||
},
|
||||
output: result.parts.findLast((x) => x.type === "text")?.text ?? "",
|
||||
output: (result.parts.findLast((x: any) => x.type === "text") as any)?.text ?? "",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { Session } from "../../src/session/index"
|
||||
|
||||
describe("fileRegex", () => {
|
||||
const template = `This is a @valid/path/to/a/file and it should also match at
|
||||
the beginning of a line:
|
||||
|
||||
@another-valid/path/to/a/file
|
||||
|
||||
but this is not:
|
||||
|
||||
- Adds a "Co-authored-by:" footer which clarifies which AI agent
|
||||
helped create this commit, using an appropriate \`noreply@...\`
|
||||
or \`noreply@anthropic.com\` email address.
|
||||
|
||||
We also need to deal with files followed by @commas, ones
|
||||
with @file-extensions.md, even @multiple.extensions.bak,
|
||||
hidden directorys like @.config/ or files like @.bashrc
|
||||
and ones at the end of a sentence like @foo.md.
|
||||
|
||||
Also shouldn't forget @/absolute/paths.txt with and @/without/extensions,
|
||||
as well as @~/home-files and @~/paths/under/home.txt.
|
||||
|
||||
If the reference is \`@quoted/in/backticks\` then it shouldn't match at all.`
|
||||
|
||||
const matches = Array.from(template.matchAll(Session.fileRegex))
|
||||
|
||||
test("should extract exactly 12 file references", () => {
|
||||
expect(matches.length).toBe(12)
|
||||
})
|
||||
|
||||
test("should extract valid/path/to/a/file", () => {
|
||||
expect(matches[0][1]).toBe("valid/path/to/a/file")
|
||||
})
|
||||
|
||||
test("should extract another-valid/path/to/a/file", () => {
|
||||
expect(matches[1][1]).toBe("another-valid/path/to/a/file")
|
||||
})
|
||||
|
||||
test("should extract paths ignoring comma after", () => {
|
||||
expect(matches[2][1]).toBe("commas")
|
||||
})
|
||||
|
||||
test("should extract a path with a file extension and comma after", () => {
|
||||
expect(matches[3][1]).toBe("file-extensions.md")
|
||||
})
|
||||
|
||||
test("should extract a path with multiple dots and comma after", () => {
|
||||
expect(matches[4][1]).toBe("multiple.extensions.bak")
|
||||
})
|
||||
|
||||
test("should extract hidden directory", () => {
|
||||
expect(matches[5][1]).toBe(".config/")
|
||||
})
|
||||
|
||||
test("should extract hidden file", () => {
|
||||
expect(matches[6][1]).toBe(".bashrc")
|
||||
})
|
||||
|
||||
test("should extract a file ignoring period at end of sentence", () => {
|
||||
expect(matches[7][1]).toBe("foo.md")
|
||||
})
|
||||
|
||||
test("should extract an absolute path with an extension", () => {
|
||||
expect(matches[8][1]).toBe("/absolute/paths.txt")
|
||||
})
|
||||
|
||||
test("should extract an absolute path without an extension", () => {
|
||||
expect(matches[9][1]).toBe("/without/extensions")
|
||||
})
|
||||
|
||||
test("should extract an absolute path in home directory", () => {
|
||||
expect(matches[10][1]).toBe("~/home-files")
|
||||
})
|
||||
|
||||
test("should extract an absolute path under home directory", () => {
|
||||
expect(matches[11][1]).toBe("~/paths/under/home.txt")
|
||||
})
|
||||
|
||||
test("should not match when preceded by backtick", () => {
|
||||
const backtickTest = "This `@should/not/match` should be ignored"
|
||||
const backtickMatches = Array.from(backtickTest.matchAll(Session.fileRegex))
|
||||
expect(backtickMatches.length).toBe(0)
|
||||
})
|
||||
|
||||
test("should not match email addresses", () => {
|
||||
const emailTest = "Contact user@example.com for help"
|
||||
const emailMatches = Array.from(emailTest.matchAll(Session.fileRegex))
|
||||
expect(emailMatches.length).toBe(0)
|
||||
})
|
||||
})
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/plugin",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { Plugin } from "./index"
|
||||
|
||||
export const ExamplePlugin: Plugin = async ({ client, $ }) => {
|
||||
export const ExamplePlugin: Plugin = async ({
|
||||
client: _client,
|
||||
$: _shell,
|
||||
project: _project,
|
||||
directory: _directory,
|
||||
worktree: _worktree,
|
||||
}) => {
|
||||
return {
|
||||
permission: {},
|
||||
async "chat.params"(input, output) {
|
||||
async "chat.params"(_input, output) {
|
||||
output.topP = 1
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
".": "0.5.0"
|
||||
".": "0.8.0"
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
configured_endpoints: 43
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-24d6bfcc66bba2e3a826fdad7e48a474c5aa9193a6bb89dc8ab1feb1f3d9bf72.yml
|
||||
openapi_spec_hash: db8b553192d9027e1f9254096406cfc2
|
||||
config_hash: 1b0d220e033fe9f683abf7048e7ad076
|
||||
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/opencode%2Fopencode-97b61518d8666ea7cb310af04248e00bcf8dc9753ba3c7e84471df72b3232004.yml
|
||||
openapi_spec_hash: a3500531973ad999c350b87c21aa3ab8
|
||||
config_hash: 026ef000d34bf2f930e7b41e77d2d3ff
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## 0.8.0 (2025-09-01)
|
||||
|
||||
Full Changelog: [v0.7.0...v0.8.0](https://github.com/sst/opencode-sdk-go/compare/v0.7.0...v0.8.0)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d))
|
||||
|
||||
## 0.7.0 (2025-09-01)
|
||||
|
||||
Full Changelog: [v0.6.0...v0.7.0](https://github.com/sst/opencode-sdk-go/compare/v0.6.0...v0.7.0)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4))
|
||||
|
||||
## 0.6.0 (2025-09-01)
|
||||
|
||||
Full Changelog: [v0.5.0...v0.6.0](https://github.com/sst/opencode-sdk-go/compare/v0.5.0...v0.6.0)
|
||||
|
||||
### Features
|
||||
|
||||
* **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8))
|
||||
|
||||
## 0.5.0 (2025-08-31)
|
||||
|
||||
Full Changelog: [v0.4.0...v0.5.0](https://github.com/sst/opencode-sdk-go/compare/v0.4.0...v0.5.0)
|
||||
|
||||
@@ -24,7 +24,7 @@ Or to pin the version:
|
||||
<!-- x-release-please-start-version -->
|
||||
|
||||
```sh
|
||||
go get -u 'github.com/sst/opencode-sdk-go@v0.5.0'
|
||||
go get -u 'github.com/sst/opencode-sdk-go@v0.8.0'
|
||||
```
|
||||
|
||||
<!-- x-release-please-end -->
|
||||
|
||||
@@ -143,10 +143,10 @@ Response Types:
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStatePending">ToolStatePending</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#ToolStateRunning">ToolStateRunning</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#UserMessage">UserMessage</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesResponse">SessionMessagesResponse</a>
|
||||
- <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionPromptResponse">SessionPromptResponse</a>
|
||||
|
||||
Methods:
|
||||
|
||||
@@ -155,13 +155,13 @@ Methods:
|
||||
- <code title="get /session">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.List">List</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionListParams">SessionListParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="delete /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Delete">Delete</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionDeleteParams">SessionDeleteParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/abort">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Abort">Abort</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionAbortParams">SessionAbortParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Chat">Chat</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatParams">SessionChatParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChatResponse">SessionChatResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /session/{id}/children">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Children">Children</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionChildrenParams">SessionChildrenParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/command">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Command">Command</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandParams">SessionCommandParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionCommandResponse">SessionCommandResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /session/{id}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Get">Get</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionGetParams">SessionGetParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/init">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Init">Init</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionInitParams">SessionInitParams</a>) (<a href="https://pkg.go.dev/builtin#bool">bool</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /session/{id}/message/{messageID}">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Message">Message</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, messageID <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageParams">SessionMessageParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessageResponse">SessionMessageResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="get /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Messages">Messages</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, query <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesParams">SessionMessagesParams</a>) ([]<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionMessagesResponse">SessionMessagesResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/message">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Prompt">Prompt</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionPromptParams">SessionPromptParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionPromptResponse">SessionPromptResponse</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/revert">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Revert">Revert</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionRevertParams">SessionRevertParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/share">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Share">Share</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, body <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionShareParams">SessionShareParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#Session">Session</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
- <code title="post /session/{id}/shell">client.Session.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionService.Shell">Shell</a>(ctx <a href="https://pkg.go.dev/context">context</a>.<a href="https://pkg.go.dev/context#Context">Context</a>, id <a href="https://pkg.go.dev/builtin#string">string</a>, params <a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#SessionShellParams">SessionShellParams</a>) (<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go">opencode</a>.<a href="https://pkg.go.dev/github.com/sst/opencode-sdk-go#AssistantMessage">AssistantMessage</a>, <a href="https://pkg.go.dev/builtin#error">error</a>)</code>
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/sst/opencode-sdk-go/internal/param"
|
||||
"github.com/sst/opencode-sdk-go/internal/requestconfig"
|
||||
"github.com/sst/opencode-sdk-go/option"
|
||||
"github.com/sst/opencode-sdk-go/shared"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
@@ -1650,10 +1651,13 @@ func (r configProviderModelsLimitJSON) RawJSON() string {
|
||||
}
|
||||
|
||||
type ConfigProviderOptions struct {
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
ExtraFields map[string]interface{} `json:"-,extras"`
|
||||
JSON configProviderOptionsJSON `json:"-"`
|
||||
APIKey string `json:"apiKey"`
|
||||
BaseURL string `json:"baseURL"`
|
||||
// Timeout in milliseconds for requests to this provider. Default is 300000 (5
|
||||
// minutes). Set to false to disable timeout.
|
||||
Timeout ConfigProviderOptionsTimeoutUnion `json:"timeout"`
|
||||
ExtraFields map[string]interface{} `json:"-,extras"`
|
||||
JSON configProviderOptionsJSON `json:"-"`
|
||||
}
|
||||
|
||||
// configProviderOptionsJSON contains the JSON metadata for the struct
|
||||
@@ -1661,6 +1665,7 @@ type ConfigProviderOptions struct {
|
||||
type configProviderOptionsJSON struct {
|
||||
APIKey apijson.Field
|
||||
BaseURL apijson.Field
|
||||
Timeout apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
@@ -1673,6 +1678,33 @@ func (r configProviderOptionsJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
// Timeout in milliseconds for requests to this provider. Default is 300000 (5
|
||||
// minutes). Set to false to disable timeout.
|
||||
//
|
||||
// Union satisfied by [shared.UnionInt] or [shared.UnionBool].
|
||||
type ConfigProviderOptionsTimeoutUnion interface {
|
||||
ImplementsConfigProviderOptionsTimeoutUnion()
|
||||
}
|
||||
|
||||
func init() {
|
||||
apijson.RegisterUnion(
|
||||
reflect.TypeOf((*ConfigProviderOptionsTimeoutUnion)(nil)).Elem(),
|
||||
"",
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.Number,
|
||||
Type: reflect.TypeOf(shared.UnionInt(0)),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.True,
|
||||
Type: reflect.TypeOf(shared.UnionBool(false)),
|
||||
},
|
||||
apijson.UnionVariant{
|
||||
TypeFilter: gjson.False,
|
||||
Type: reflect.TypeOf(shared.UnionBool(false)),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Control sharing behavior:'manual' allows manual sharing via commands, 'auto'
|
||||
// enables automatic sharing, 'disabled' disables all sharing
|
||||
type ConfigShare string
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
package internal
|
||||
|
||||
const PackageVersion = "0.5.0" // x-release-please-version
|
||||
const PackageVersion = "0.8.0" // x-release-please-version
|
||||
|
||||
@@ -42,15 +42,19 @@ func (r *PathService) Get(ctx context.Context, query PathGetParams, opts ...opti
|
||||
}
|
||||
|
||||
type Path struct {
|
||||
Config string `json:"config,required"`
|
||||
State string `json:"state,required"`
|
||||
JSON pathJSON `json:"-"`
|
||||
Config string `json:"config,required"`
|
||||
Directory string `json:"directory,required"`
|
||||
State string `json:"state,required"`
|
||||
Worktree string `json:"worktree,required"`
|
||||
JSON pathJSON `json:"-"`
|
||||
}
|
||||
|
||||
// pathJSON contains the JSON metadata for the struct [Path]
|
||||
type pathJSON struct {
|
||||
Config apijson.Field
|
||||
Directory apijson.Field
|
||||
State apijson.Field
|
||||
Worktree apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
+110
-102
@@ -92,18 +92,6 @@ func (r *SessionService) Abort(ctx context.Context, id string, body SessionAbort
|
||||
return
|
||||
}
|
||||
|
||||
// Create and send a new message to a session
|
||||
func (r *SessionService) Chat(ctx context.Context, id string, params SessionChatParams, opts ...option.RequestOption) (res *SessionChatResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("session/%s/message", id)
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
}
|
||||
|
||||
// Get a session's children
|
||||
func (r *SessionService) Children(ctx context.Context, id string, query SessionChildrenParams, opts ...option.RequestOption) (res *[]Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
@@ -180,6 +168,18 @@ func (r *SessionService) Messages(ctx context.Context, id string, query SessionM
|
||||
return
|
||||
}
|
||||
|
||||
// Create and send a new message to a session
|
||||
func (r *SessionService) Prompt(ctx context.Context, id string, params SessionPromptParams, opts ...option.RequestOption) (res *SessionPromptResponse, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
if id == "" {
|
||||
err = errors.New("missing required id parameter")
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("session/%s/message", id)
|
||||
err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, params, &res, opts...)
|
||||
return
|
||||
}
|
||||
|
||||
// Revert a message
|
||||
func (r *SessionService) Revert(ctx context.Context, id string, params SessionRevertParams, opts ...option.RequestOption) (res *Session, err error) {
|
||||
opts = append(r.Options[:], opts...)
|
||||
@@ -333,7 +333,7 @@ func (r AgentPartInputParam) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
func (r AgentPartInputParam) implementsSessionChatParamsPartUnion() {}
|
||||
func (r AgentPartInputParam) implementsSessionPromptParamsPartUnion() {}
|
||||
|
||||
type AgentPartInputType string
|
||||
|
||||
@@ -709,7 +709,7 @@ func (r FilePartInputParam) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
func (r FilePartInputParam) implementsSessionChatParamsPartUnion() {}
|
||||
func (r FilePartInputParam) implementsSessionPromptParamsPartUnion() {}
|
||||
|
||||
type FilePartInputType string
|
||||
|
||||
@@ -1820,7 +1820,7 @@ func (r TextPartInputParam) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
func (r TextPartInputParam) implementsSessionChatParamsPartUnion() {}
|
||||
func (r TextPartInputParam) implementsSessionPromptParamsPartUnion() {}
|
||||
|
||||
type TextPartInputType string
|
||||
|
||||
@@ -2296,29 +2296,6 @@ func (r userMessageTimeJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SessionChatResponse struct {
|
||||
Info AssistantMessage `json:"info,required"`
|
||||
Parts []Part `json:"parts,required"`
|
||||
JSON sessionChatResponseJSON `json:"-"`
|
||||
}
|
||||
|
||||
// sessionChatResponseJSON contains the JSON metadata for the struct
|
||||
// [SessionChatResponse]
|
||||
type sessionChatResponseJSON struct {
|
||||
Info apijson.Field
|
||||
Parts apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SessionChatResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r sessionChatResponseJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SessionCommandResponse struct {
|
||||
Info AssistantMessage `json:"info,required"`
|
||||
Parts []Part `json:"parts,required"`
|
||||
@@ -2388,6 +2365,29 @@ func (r sessionMessagesResponseJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SessionPromptResponse struct {
|
||||
Info AssistantMessage `json:"info,required"`
|
||||
Parts []Part `json:"parts,required"`
|
||||
JSON sessionPromptResponseJSON `json:"-"`
|
||||
}
|
||||
|
||||
// sessionPromptResponseJSON contains the JSON metadata for the struct
|
||||
// [SessionPromptResponse]
|
||||
type sessionPromptResponseJSON struct {
|
||||
Info apijson.Field
|
||||
Parts apijson.Field
|
||||
raw string
|
||||
ExtraFields map[string]apijson.Field
|
||||
}
|
||||
|
||||
func (r *SessionPromptResponse) UnmarshalJSON(data []byte) (err error) {
|
||||
return apijson.UnmarshalRoot(data, r)
|
||||
}
|
||||
|
||||
func (r sessionPromptResponseJSON) RawJSON() string {
|
||||
return r.raw
|
||||
}
|
||||
|
||||
type SessionNewParams struct {
|
||||
Directory param.Field[string] `query:"directory"`
|
||||
ParentID param.Field[string] `json:"parentID"`
|
||||
@@ -2459,70 +2459,6 @@ func (r SessionAbortParams) URLQuery() (v url.Values) {
|
||||
})
|
||||
}
|
||||
|
||||
type SessionChatParams struct {
|
||||
ModelID param.Field[string] `json:"modelID,required"`
|
||||
Parts param.Field[[]SessionChatParamsPartUnion] `json:"parts,required"`
|
||||
ProviderID param.Field[string] `json:"providerID,required"`
|
||||
Directory param.Field[string] `query:"directory"`
|
||||
Agent param.Field[string] `json:"agent"`
|
||||
MessageID param.Field[string] `json:"messageID"`
|
||||
System param.Field[string] `json:"system"`
|
||||
Tools param.Field[map[string]bool] `json:"tools"`
|
||||
}
|
||||
|
||||
func (r SessionChatParams) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
// URLQuery serializes [SessionChatParams]'s query parameters as `url.Values`.
|
||||
func (r SessionChatParams) URLQuery() (v url.Values) {
|
||||
return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{
|
||||
ArrayFormat: apiquery.ArrayQueryFormatComma,
|
||||
NestedFormat: apiquery.NestedQueryFormatBrackets,
|
||||
})
|
||||
}
|
||||
|
||||
type SessionChatParamsPart struct {
|
||||
Type param.Field[SessionChatParamsPartsType] `json:"type,required"`
|
||||
ID param.Field[string] `json:"id"`
|
||||
Filename param.Field[string] `json:"filename"`
|
||||
Mime param.Field[string] `json:"mime"`
|
||||
Name param.Field[string] `json:"name"`
|
||||
Source param.Field[interface{}] `json:"source"`
|
||||
Synthetic param.Field[bool] `json:"synthetic"`
|
||||
Text param.Field[string] `json:"text"`
|
||||
Time param.Field[interface{}] `json:"time"`
|
||||
URL param.Field[string] `json:"url"`
|
||||
}
|
||||
|
||||
func (r SessionChatParamsPart) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
func (r SessionChatParamsPart) implementsSessionChatParamsPartUnion() {}
|
||||
|
||||
// Satisfied by [TextPartInputParam], [FilePartInputParam], [AgentPartInputParam],
|
||||
// [SessionChatParamsPart].
|
||||
type SessionChatParamsPartUnion interface {
|
||||
implementsSessionChatParamsPartUnion()
|
||||
}
|
||||
|
||||
type SessionChatParamsPartsType string
|
||||
|
||||
const (
|
||||
SessionChatParamsPartsTypeText SessionChatParamsPartsType = "text"
|
||||
SessionChatParamsPartsTypeFile SessionChatParamsPartsType = "file"
|
||||
SessionChatParamsPartsTypeAgent SessionChatParamsPartsType = "agent"
|
||||
)
|
||||
|
||||
func (r SessionChatParamsPartsType) IsKnown() bool {
|
||||
switch r {
|
||||
case SessionChatParamsPartsTypeText, SessionChatParamsPartsTypeFile, SessionChatParamsPartsTypeAgent:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SessionChildrenParams struct {
|
||||
Directory param.Field[string] `query:"directory"`
|
||||
}
|
||||
@@ -2611,6 +2547,78 @@ func (r SessionMessagesParams) URLQuery() (v url.Values) {
|
||||
})
|
||||
}
|
||||
|
||||
type SessionPromptParams struct {
|
||||
Parts param.Field[[]SessionPromptParamsPartUnion] `json:"parts,required"`
|
||||
Directory param.Field[string] `query:"directory"`
|
||||
Agent param.Field[string] `json:"agent"`
|
||||
MessageID param.Field[string] `json:"messageID"`
|
||||
Model param.Field[SessionPromptParamsModel] `json:"model"`
|
||||
System param.Field[string] `json:"system"`
|
||||
Tools param.Field[map[string]bool] `json:"tools"`
|
||||
}
|
||||
|
||||
func (r SessionPromptParams) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
// URLQuery serializes [SessionPromptParams]'s query parameters as `url.Values`.
|
||||
func (r SessionPromptParams) URLQuery() (v url.Values) {
|
||||
return apiquery.MarshalWithSettings(r, apiquery.QuerySettings{
|
||||
ArrayFormat: apiquery.ArrayQueryFormatComma,
|
||||
NestedFormat: apiquery.NestedQueryFormatBrackets,
|
||||
})
|
||||
}
|
||||
|
||||
type SessionPromptParamsPart struct {
|
||||
Type param.Field[SessionPromptParamsPartsType] `json:"type,required"`
|
||||
ID param.Field[string] `json:"id"`
|
||||
Filename param.Field[string] `json:"filename"`
|
||||
Mime param.Field[string] `json:"mime"`
|
||||
Name param.Field[string] `json:"name"`
|
||||
Source param.Field[interface{}] `json:"source"`
|
||||
Synthetic param.Field[bool] `json:"synthetic"`
|
||||
Text param.Field[string] `json:"text"`
|
||||
Time param.Field[interface{}] `json:"time"`
|
||||
URL param.Field[string] `json:"url"`
|
||||
}
|
||||
|
||||
func (r SessionPromptParamsPart) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
func (r SessionPromptParamsPart) implementsSessionPromptParamsPartUnion() {}
|
||||
|
||||
// Satisfied by [TextPartInputParam], [FilePartInputParam], [AgentPartInputParam],
|
||||
// [SessionPromptParamsPart].
|
||||
type SessionPromptParamsPartUnion interface {
|
||||
implementsSessionPromptParamsPartUnion()
|
||||
}
|
||||
|
||||
type SessionPromptParamsPartsType string
|
||||
|
||||
const (
|
||||
SessionPromptParamsPartsTypeText SessionPromptParamsPartsType = "text"
|
||||
SessionPromptParamsPartsTypeFile SessionPromptParamsPartsType = "file"
|
||||
SessionPromptParamsPartsTypeAgent SessionPromptParamsPartsType = "agent"
|
||||
)
|
||||
|
||||
func (r SessionPromptParamsPartsType) IsKnown() bool {
|
||||
switch r {
|
||||
case SessionPromptParamsPartsTypeText, SessionPromptParamsPartsTypeFile, SessionPromptParamsPartsTypeAgent:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type SessionPromptParamsModel struct {
|
||||
ModelID param.Field[string] `json:"modelID,required"`
|
||||
ProviderID param.Field[string] `json:"providerID,required"`
|
||||
}
|
||||
|
||||
func (r SessionPromptParamsModel) MarshalJSON() (data []byte, err error) {
|
||||
return apijson.MarshalRoot(r)
|
||||
}
|
||||
|
||||
type SessionRevertParams struct {
|
||||
MessageID param.Field[string] `json:"messageID,required"`
|
||||
Directory param.Field[string] `query:"directory"`
|
||||
|
||||
@@ -148,52 +148,6 @@ func TestSessionAbortWithOptionalParams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionChatWithOptionalParams(t *testing.T) {
|
||||
t.Skip("Prism tests are disabled")
|
||||
baseURL := "http://localhost:4010"
|
||||
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
|
||||
baseURL = envURL
|
||||
}
|
||||
if !testutil.CheckTestServer(t, baseURL) {
|
||||
return
|
||||
}
|
||||
client := opencode.NewClient(
|
||||
option.WithBaseURL(baseURL),
|
||||
)
|
||||
_, err := client.Session.Chat(
|
||||
context.TODO(),
|
||||
"id",
|
||||
opencode.SessionChatParams{
|
||||
ModelID: opencode.F("modelID"),
|
||||
Parts: opencode.F([]opencode.SessionChatParamsPartUnion{opencode.TextPartInputParam{
|
||||
Text: opencode.F("text"),
|
||||
Type: opencode.F(opencode.TextPartInputTypeText),
|
||||
ID: opencode.F("id"),
|
||||
Synthetic: opencode.F(true),
|
||||
Time: opencode.F(opencode.TextPartInputTimeParam{
|
||||
Start: opencode.F(0.000000),
|
||||
End: opencode.F(0.000000),
|
||||
}),
|
||||
}}),
|
||||
ProviderID: opencode.F("providerID"),
|
||||
Directory: opencode.F("directory"),
|
||||
Agent: opencode.F("agent"),
|
||||
MessageID: opencode.F("msg"),
|
||||
System: opencode.F("system"),
|
||||
Tools: opencode.F(map[string]bool{
|
||||
"foo": true,
|
||||
}),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
var apierr *opencode.Error
|
||||
if errors.As(err, &apierr) {
|
||||
t.Log(string(apierr.DumpRequest(true)))
|
||||
}
|
||||
t.Fatalf("err should be nil: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionChildrenWithOptionalParams(t *testing.T) {
|
||||
t.Skip("Prism tests are disabled")
|
||||
baseURL := "http://localhost:4010"
|
||||
@@ -371,6 +325,54 @@ func TestSessionMessagesWithOptionalParams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionPromptWithOptionalParams(t *testing.T) {
|
||||
t.Skip("Prism tests are disabled")
|
||||
baseURL := "http://localhost:4010"
|
||||
if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
|
||||
baseURL = envURL
|
||||
}
|
||||
if !testutil.CheckTestServer(t, baseURL) {
|
||||
return
|
||||
}
|
||||
client := opencode.NewClient(
|
||||
option.WithBaseURL(baseURL),
|
||||
)
|
||||
_, err := client.Session.Prompt(
|
||||
context.TODO(),
|
||||
"id",
|
||||
opencode.SessionPromptParams{
|
||||
Parts: opencode.F([]opencode.SessionPromptParamsPartUnion{opencode.TextPartInputParam{
|
||||
Text: opencode.F("text"),
|
||||
Type: opencode.F(opencode.TextPartInputTypeText),
|
||||
ID: opencode.F("id"),
|
||||
Synthetic: opencode.F(true),
|
||||
Time: opencode.F(opencode.TextPartInputTimeParam{
|
||||
Start: opencode.F(0.000000),
|
||||
End: opencode.F(0.000000),
|
||||
}),
|
||||
}}),
|
||||
Directory: opencode.F("directory"),
|
||||
Agent: opencode.F("agent"),
|
||||
MessageID: opencode.F("msg"),
|
||||
Model: opencode.F(opencode.SessionPromptParamsModel{
|
||||
ModelID: opencode.F("modelID"),
|
||||
ProviderID: opencode.F("providerID"),
|
||||
}),
|
||||
System: opencode.F("system"),
|
||||
Tools: opencode.F(map[string]bool{
|
||||
"foo": true,
|
||||
}),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
var apierr *opencode.Error
|
||||
if errors.As(err, &apierr) {
|
||||
t.Log(string(apierr.DumpRequest(true)))
|
||||
}
|
||||
t.Fatalf("err should be nil: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionRevertWithOptionalParams(t *testing.T) {
|
||||
t.Skip("Prism tests are disabled")
|
||||
baseURL := "http://localhost:4010"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
|
||||
|
||||
package shared
|
||||
|
||||
type UnionBool bool
|
||||
|
||||
func (UnionBool) ImplementsConfigProviderOptionsTimeoutUnion() {}
|
||||
|
||||
type UnionInt int64
|
||||
|
||||
func (UnionInt) ImplementsConfigProviderOptionsTimeoutUnion() {}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk"
|
||||
|
||||
const server = await createOpencodeServer()
|
||||
const client = createOpencodeClient({ baseUrl: server.url })
|
||||
|
||||
const input = await Array.fromAsync(new Bun.Glob("packages/core/*.ts").scan())
|
||||
|
||||
const tasks: Promise<void>[] = []
|
||||
for await (const file of input) {
|
||||
console.log("processing", file)
|
||||
const session = await client.session.create()
|
||||
tasks.push(
|
||||
client.session.prompt({
|
||||
path: { id: session.data.id },
|
||||
body: {
|
||||
parts: [
|
||||
{
|
||||
type: "file",
|
||||
mime: "text/plain",
|
||||
url: `file://${file}`,
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
text: `Write tests for every public function in this file.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
)
|
||||
console.log("done", file)
|
||||
}
|
||||
|
||||
await Promise.all(
|
||||
input.map(async (file) => {
|
||||
const session = await client.session.create()
|
||||
console.log("processing", file)
|
||||
await client.session.prompt({
|
||||
path: { id: session.data.id },
|
||||
body: {
|
||||
parts: [
|
||||
{
|
||||
type: "file",
|
||||
mime: "text/plain",
|
||||
url: `file://${file}`,
|
||||
},
|
||||
{
|
||||
type: "text",
|
||||
text: `Write tests for every public function in this file.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
console.log("done", file)
|
||||
}),
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/package.json",
|
||||
"name": "@opencode-ai/sdk",
|
||||
"version": "0.5.29",
|
||||
"version": "0.6.4",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { Options as ClientOptions, TDataShape, Client } from "./client/inde
|
||||
import type {
|
||||
ProjectListData,
|
||||
ProjectListResponses,
|
||||
ProjectCurrentData,
|
||||
ProjectCurrentResponses,
|
||||
EventSubscribeData,
|
||||
EventSubscribeResponses,
|
||||
ConfigGetData,
|
||||
@@ -35,8 +37,8 @@ import type {
|
||||
SessionSummarizeResponses,
|
||||
SessionMessagesData,
|
||||
SessionMessagesResponses,
|
||||
SessionChatData,
|
||||
SessionChatResponses,
|
||||
SessionPromptData,
|
||||
SessionPromptResponses,
|
||||
SessionMessageData,
|
||||
SessionMessageResponses,
|
||||
SessionCommandData,
|
||||
@@ -130,6 +132,16 @@ class Project extends _HeyApiClient {
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current project
|
||||
*/
|
||||
public current<ThrowOnError extends boolean = false>(options?: Options<ProjectCurrentData, ThrowOnError>) {
|
||||
return (options?.client ?? this._client).get<ProjectCurrentResponses, unknown, ThrowOnError>({
|
||||
url: "/project/current",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Event extends _HeyApiClient {
|
||||
@@ -318,8 +330,8 @@ class Session extends _HeyApiClient {
|
||||
/**
|
||||
* Create and send a new message to a session
|
||||
*/
|
||||
public chat<ThrowOnError extends boolean = false>(options: Options<SessionChatData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).post<SessionChatResponses, unknown, ThrowOnError>({
|
||||
public prompt<ThrowOnError extends boolean = false>(options: Options<SessionPromptData, ThrowOnError>) {
|
||||
return (options.client ?? this._client).post<SessionPromptResponses, unknown, ThrowOnError>({
|
||||
url: "/session/{id}/message",
|
||||
...options,
|
||||
headers: {
|
||||
|
||||
@@ -645,7 +645,11 @@ export type Config = {
|
||||
options?: {
|
||||
apiKey?: string
|
||||
baseURL?: string
|
||||
[key: string]: unknown | string | undefined
|
||||
/**
|
||||
* Timeout in milliseconds for requests to this provider. Default is 300000 (5 minutes). Set to false to disable timeout.
|
||||
*/
|
||||
timeout?: number | false
|
||||
[key: string]: unknown | string | (number | false) | undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1056,8 @@ export type LayoutConfig = "auto" | "stretch"
|
||||
export type Path = {
|
||||
state: string
|
||||
config: string
|
||||
worktree: string
|
||||
directory: string
|
||||
}
|
||||
|
||||
export type _Error = {
|
||||
@@ -1196,6 +1202,24 @@ export type ProjectListResponses = {
|
||||
|
||||
export type ProjectListResponse = ProjectListResponses[keyof ProjectListResponses]
|
||||
|
||||
export type ProjectCurrentData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/project/current"
|
||||
}
|
||||
|
||||
export type ProjectCurrentResponses = {
|
||||
/**
|
||||
* Current project
|
||||
*/
|
||||
200: Project
|
||||
}
|
||||
|
||||
export type ProjectCurrentResponse = ProjectCurrentResponses[keyof ProjectCurrentResponses]
|
||||
|
||||
export type EventSubscribeData = {
|
||||
body?: never
|
||||
path?: never
|
||||
@@ -1519,11 +1543,13 @@ export type SessionMessagesResponses = {
|
||||
|
||||
export type SessionMessagesResponse = SessionMessagesResponses[keyof SessionMessagesResponses]
|
||||
|
||||
export type SessionChatData = {
|
||||
export type SessionPromptData = {
|
||||
body?: {
|
||||
messageID?: string
|
||||
providerID: string
|
||||
modelID: string
|
||||
model?: {
|
||||
providerID: string
|
||||
modelID: string
|
||||
}
|
||||
agent?: string
|
||||
system?: string
|
||||
tools?: {
|
||||
@@ -1553,7 +1579,7 @@ export type SessionChatData = {
|
||||
url: "/session/{id}/message"
|
||||
}
|
||||
|
||||
export type SessionChatResponses = {
|
||||
export type SessionPromptResponses = {
|
||||
/**
|
||||
* Created message
|
||||
*/
|
||||
@@ -1563,7 +1589,7 @@ export type SessionChatResponses = {
|
||||
}
|
||||
}
|
||||
|
||||
export type SessionChatResponse = SessionChatResponses[keyof SessionChatResponses]
|
||||
export type SessionPromptResponse = SessionPromptResponses[keyof SessionPromptResponses]
|
||||
|
||||
export type SessionMessageData = {
|
||||
body?: never
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
import { spawn } from "node:child_process"
|
||||
import { type Config } from "./gen/types.gen.js"
|
||||
|
||||
export type ServerConfig = {
|
||||
export type ServerOptions = {
|
||||
hostname?: string
|
||||
port?: number
|
||||
signal?: AbortSignal
|
||||
timeout?: number
|
||||
config?: Config
|
||||
}
|
||||
|
||||
export async function createOpencodeServer(config?: ServerConfig) {
|
||||
config = Object.assign(
|
||||
export async function createOpencodeServer(options?: ServerOptions) {
|
||||
options = Object.assign(
|
||||
{
|
||||
hostname: "127.0.0.1",
|
||||
port: 4096,
|
||||
timeout: 5000,
|
||||
},
|
||||
config ?? {},
|
||||
options ?? {},
|
||||
)
|
||||
|
||||
const proc = spawn(`opencode`, [`serve`, `--hostname=${config.hostname}`, `--port=${config.port}`], {
|
||||
signal: config.signal,
|
||||
const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], {
|
||||
signal: options.signal,
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}),
|
||||
},
|
||||
})
|
||||
|
||||
const url = await new Promise<string>((resolve, reject) => {
|
||||
const id = setTimeout(() => {
|
||||
reject(new Error(`Timeout waiting for server to start after ${config.timeout}ms`))
|
||||
}, config.timeout)
|
||||
reject(new Error(`Timeout waiting for server to start after ${options.timeout}ms`))
|
||||
}, options.timeout)
|
||||
let output = ""
|
||||
proc.stdout?.on("data", (chunk) => {
|
||||
output += chunk.toString()
|
||||
@@ -56,8 +62,8 @@ export async function createOpencodeServer(config?: ServerConfig) {
|
||||
clearTimeout(id)
|
||||
reject(error)
|
||||
})
|
||||
if (config.signal) {
|
||||
config.signal.addEventListener("abort", () => {
|
||||
if (options.signal) {
|
||||
options.signal.addEventListener("abort", () => {
|
||||
clearTimeout(id)
|
||||
reject(new Error("Aborted"))
|
||||
})
|
||||
|
||||
@@ -147,7 +147,7 @@ resources:
|
||||
summarize: post /session/{id}/summarize
|
||||
message: get /session/{id}/message/{messageID}
|
||||
messages: get /session/{id}/message
|
||||
chat: post /session/{id}/message
|
||||
prompt: post /session/{id}/message
|
||||
command: post /session/{id}/command
|
||||
shell: post /session/{id}/shell
|
||||
update: patch /session/{id}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user