diff --git a/bun.lock b/bun.lock
index cb66b9b8e..a4d0ecbe3 100644
--- a/bun.lock
+++ b/bun.lock
@@ -11,7 +11,7 @@
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
- "sst": "3.17.22",
+ "sst": "3.17.23",
"turbo": "2.5.6",
},
},
@@ -106,7 +106,10 @@
"@cloudflare/workers-types": "catalog:",
},
"devDependencies": {
+ "@cloudflare/workers-types": "catalog:",
"@tsconfig/node22": "22.0.2",
+ "@types/node": "catalog:",
+ "cloudflare": "5.2.0",
},
},
"packages/desktop": {
@@ -1432,6 +1435,8 @@
"@types/node": ["@types/node@22.13.9", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw=="],
+ "@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
+
"@types/promise.allsettled": ["@types/promise.allsettled@1.0.6", "", {}, "sha512-wA0UT0HeT2fGHzIFV9kWpYz5mdoyLxKrTgMdZQM++5h6pYAFH73HXcQhefg24nD1yivUFEn5KU+EF4b+CXJ4Wg=="],
"@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
@@ -1518,6 +1523,8 @@
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
+ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
+
"ai": ["ai@5.0.8", "", { "dependencies": { "@ai-sdk/gateway": "1.0.4", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.1", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4" } }, "sha512-qbnhj046UvG30V1S5WhjBn+RBGEAmi8PSZWqMhRsE3EPxvO5BcePXTZFA23e9MYyWS9zr4Vm8Mv3wQXwLmtIBw=="],
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
@@ -1742,6 +1749,8 @@
"clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="],
+ "cloudflare": ["cloudflare@5.2.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-dVzqDpPFYR9ApEC9e+JJshFJZXcw4HzM8W+3DHzO5oy9+8rLC53G7x6fEf9A7/gSuSCxuvndzui5qJKftfIM9A=="],
+
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
@@ -2068,7 +2077,11 @@
"foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="],
- "form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
+ "form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
+
+ "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
+
+ "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
"forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="],
@@ -2258,6 +2271,8 @@
"human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="],
+ "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
+
"husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="],
"i18next": ["i18next@23.16.8", "", { "dependencies": { "@babel/runtime": "^7.23.2" } }, "sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg=="],
@@ -2716,6 +2731,8 @@
"node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="],
+ "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
+
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-fetch-native": ["node-fetch-native@1.6.7", "", {}, "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q=="],
@@ -3178,23 +3195,23 @@
"sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="],
- "sst": ["sst@3.17.22", "", { "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.22", "sst-darwin-x64": "3.17.22", "sst-linux-arm64": "3.17.22", "sst-linux-x64": "3.17.22", "sst-linux-x86": "3.17.22", "sst-win32-arm64": "3.17.22", "sst-win32-x64": "3.17.22", "sst-win32-x86": "3.17.22" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-C+XMTbm6fx+7eT+ESAMATqG7qV7+pyVfxYQb6osdH3jd4u91QW1VU/xlEru+RU1rs1ZE58ixXdRP75UGPn+gog=="],
+ "sst": ["sst@3.17.23", "", { "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.23", "sst-darwin-x64": "3.17.23", "sst-linux-arm64": "3.17.23", "sst-linux-x64": "3.17.23", "sst-linux-x86": "3.17.23", "sst-win32-arm64": "3.17.23", "sst-win32-x64": "3.17.23", "sst-win32-x86": "3.17.23" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-TwKgUgDnZdc1Swe+bvCNeyO4dQnYz5cTodMpYj3jlXZdK9/KNz0PVxT1f0u5E76i1pmilXrUBL/f7iiMPw4RDg=="],
- "sst-darwin-arm64": ["sst-darwin-arm64@3.17.22", "", { "os": "darwin", "cpu": "arm64" }, "sha512-B2pKq1dWc60+7HfXQ6/9etskxxNv9axxlQKveCLQAuG2a3mmtv2/jcR0Ch3mvSTGtW+KfhzUXda2kj7nZ/phBA=="],
+ "sst-darwin-arm64": ["sst-darwin-arm64@3.17.23", "", { "os": "darwin", "cpu": "arm64" }, "sha512-R6kvmF+rUideOoU7KBs2SdvrIupoE+b+Dor/eq9Uo4Dojj7KvYDZI/EDm8sSCbbcx/opiWeyNqKtlnLEdCxE6g=="],
- "sst-darwin-x64": ["sst-darwin-x64@3.17.22", "", { "os": "darwin", "cpu": "x64" }, "sha512-flikYqXvhwwrS6x2FDOde+MQODHaZCIbUkVHYO3/gYo99rbAMQ8VpC/3LXnmnPEQkLOwWCSzLp4S4F9nG/PW2g=="],
+ "sst-darwin-x64": ["sst-darwin-x64@3.17.23", "", { "os": "darwin", "cpu": "x64" }, "sha512-WW4P1S35iYCifQXxD+sE3wuzcN+LHLpuKMaNoaBqEcWGZnH3IPaDJ7rpLF0arkDAo/z3jZmWWzOCkr0JuqJ8vQ=="],
- "sst-linux-arm64": ["sst-linux-arm64@3.17.22", "", { "os": "linux", "cpu": "arm64" }, "sha512-+pyD8Oej9js8XeCCebiEIde02vC5hc+bLl2/jR02K+9gYkGVJ6n5bkT8AlR8zWdS4FJKPyeJYUfjliT1T33j+g=="],
+ "sst-linux-arm64": ["sst-linux-arm64@3.17.23", "", { "os": "linux", "cpu": "arm64" }, "sha512-TjtNqgIh7RlAWgPLFCAt0mXvIB+J7WjmRvIRrAdX0mXsndOiBJ/DMOgXSLVsIWHCfPj8MIEot/hWpnJgXgIeag=="],
- "sst-linux-x64": ["sst-linux-x64@3.17.22", "", { "os": "linux", "cpu": "x64" }, "sha512-A5p941edP9wgfgsbLUMeEPvi9JExj0OSaxgtFAC6/6BYoW4zruGAPzq206Ln6dNYP3gRdo5TJbSjio3F0ot8qg=="],
+ "sst-linux-x64": ["sst-linux-x64@3.17.23", "", { "os": "linux", "cpu": "x64" }, "sha512-qdqJiEbYfCjZlI3F/TA6eoIU7JXVkEEI/UMILNf2JWhky0KQdCW2Xyz+wb6c0msVJCWdUM/uj+1DaiP2eXvghw=="],
- "sst-linux-x86": ["sst-linux-x86@3.17.22", "", { "os": "linux", "cpu": "none" }, "sha512-pFDIi+ZwH8GOvy5He9wsbAjRGf/sTGhGE/V480w0A6itb9BC4jQ9sblJkk3Jx/fP2g27pKN2RNz+ifOU+GrUYQ=="],
+ "sst-linux-x86": ["sst-linux-x86@3.17.23", "", { "os": "linux", "cpu": "none" }, "sha512-aGmUujIvoNlmAABEGsOgfY1rxD9koC6hN8bnTLbDI+oI/u/zjHYh50jsbL0p3TlaHpwF/lxP3xFSuT6IKp+KgA=="],
- "sst-win32-arm64": ["sst-win32-arm64@3.17.22", "", { "os": "win32", "cpu": "arm64" }, "sha512-9KaIrk+Z6hLDNi9GShf9NLrZi9jC/NNGpUAn6HvTXr8c6HUyQzg6takMH8nrISGCPn92y+IYWqdglaqbgnJTog=="],
+ "sst-win32-arm64": ["sst-win32-arm64@3.17.23", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZxdkGqYDrrZGz98rijDCN+m5yuCcwD6Bc9/6hubLsvdpNlVorUqzpg801Ec97xSK0nIC9g6pNiRyxAcsQQstUg=="],
- "sst-win32-x64": ["sst-win32-x64@3.17.22", "", { "os": "win32", "cpu": "x64" }, "sha512-cvzyet4octGHK7w05jPUSPmUdlAWyh8IzjB8Pcs873K9AUGJEtQCftOKZjXaFdIG9DTvFWCCBi9zdzClxT9jJg=="],
+ "sst-win32-x64": ["sst-win32-x64@3.17.23", "", { "os": "win32", "cpu": "x64" }, "sha512-yc9cor4MS49Ccy2tQCF1tf6M81yLeSGzGL+gjhUxpVKo2pN3bxl3w70eyU/mTXSEeyAmG9zEfbt6FNu4sy5cUA=="],
- "sst-win32-x86": ["sst-win32-x86@3.17.22", "", { "os": "win32", "cpu": "none" }, "sha512-ol5icDJuHzG+AjbGbCIQoF8z3oiikTF9CtccdK/udqEF861DnngWzM99IY5TJvmJlN+38yOV0MY4XI5hM6SEQA=="],
+ "sst-win32-x86": ["sst-win32-x86@3.17.23", "", { "os": "win32", "cpu": "none" }, "sha512-DIp3s54IpNAfdYjSRt6McvkbEPQDMxUu6RUeRAd2C+FcTJgTloon/ghAPQBaDgu2VoVgymjcJARO/XyfKcCLOQ=="],
"stackframe": ["stackframe@1.3.4", "", {}, "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="],
@@ -3480,6 +3497,8 @@
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
+ "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
+
"web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
@@ -3754,6 +3773,8 @@
"@slack/web-api/eventemitter3": ["eventemitter3@3.1.2", "", {}, "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q=="],
+ "@slack/web-api/form-data": ["form-data@2.5.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.35", "safe-buffer": "^5.2.1" } }, "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A=="],
+
"@slack/web-api/p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
"@solidjs/start/shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="],
@@ -3814,8 +3835,6 @@
"astro/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
- "axios/form-data": ["form-data@4.0.4", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow=="],
-
"babel-plugin-jsx-dom-expressions/@babel/helper-module-imports": ["@babel/helper-module-imports@7.18.6", "", { "dependencies": { "@babel/types": "^7.18.6" } }, "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA=="],
"babel-plugin-module-resolver/glob": ["glob@9.3.5", "", { "dependencies": { "fs.realpath": "^1.0.0", "minimatch": "^8.0.2", "minipass": "^4.2.4", "path-scurry": "^1.6.1" } }, "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q=="],
@@ -4350,6 +4369,8 @@
"@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="],
+ "@slack/web-api/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
+
"@slack/web-api/p-queue/eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
"@slack/web-api/p-queue/p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
@@ -4402,8 +4423,6 @@
"astro/shiki/@shikijs/types": ["@shikijs/types@3.14.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-bQGgC6vrY8U/9ObG1Z/vTro+uclbjjD/uG58RvfxKZVD5p9Yc1ka3tVyEFy7BNJLzxuWyHH5NWynP9zZZS59eQ=="],
- "axios/form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
-
"babel-plugin-module-resolver/glob/minimatch": ["minimatch@8.0.4", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA=="],
"babel-plugin-module-resolver/glob/minipass": ["minipass@4.2.8", "", {}, "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ=="],
@@ -4700,6 +4719,8 @@
"@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="],
+ "@slack/web-api/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
+
"@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="],
"@vercel/nft/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
@@ -4708,8 +4729,6 @@
"archiver-utils/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
- "axios/form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
-
"babel-plugin-module-resolver/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
"babel-plugin-module-resolver/glob/path-scurry/minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
diff --git a/github/sst-env.d.ts b/github/sst-env.d.ts
index 6b69016e7..f742a1200 100644
--- a/github/sst-env.d.ts
+++ b/github/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/infra/console.ts b/infra/console.ts
index 98cc4c3f0..fabc86587 100644
--- a/infra/console.ts
+++ b/infra/console.ts
@@ -106,6 +106,7 @@ const AUTH_API_URL = new sst.Linkable("AUTH_API_URL", {
const STRIPE_WEBHOOK_SECRET = new sst.Linkable("STRIPE_WEBHOOK_SECRET", {
properties: { value: stripeWebhook.secret },
})
+const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
////////////////
// CONSOLE
@@ -136,6 +137,16 @@ new sst.cloudflare.x.SolidStart("Console", {
EMAILOCTOPUS_API_KEY,
AWS_SES_ACCESS_KEY_ID,
AWS_SES_SECRET_ACCESS_KEY,
+ ...($dev
+ ? [
+ new sst.Secret(
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID",
+ process.env.CLOUDFLARE_DEFAULT_ACCOUNT_ID!,
+ ),
+ new sst.Secret("CLOUDFLARE_API_TOKEN", process.env.CLOUDFLARE_API_TOKEN!),
+ ]
+ : []),
+ gatewayKv,
],
environment: {
//VITE_DOCS_URL: web.url.apply((url) => url!),
diff --git a/package.json b/package.json
index 41b74dae9..0e6ab4c68 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"@tsconfig/bun": "catalog:",
"husky": "9.1.7",
"prettier": "3.6.2",
- "sst": "3.17.22",
+ "sst": "3.17.23",
"turbo": "2.5.6"
},
"dependencies": {
diff --git a/packages/console/app/src/routes/zen/util/error.ts b/packages/console/app/src/routes/zen/util/error.ts
index dfc7e9fcd..f1e131468 100644
--- a/packages/console/app/src/routes/zen/util/error.ts
+++ b/packages/console/app/src/routes/zen/util/error.ts
@@ -3,3 +3,4 @@ export class CreditsError extends Error {}
export class MonthlyLimitError extends Error {}
export class UserLimitError extends Error {}
export class ModelError extends Error {}
+export class RateLimitError extends Error {}
diff --git a/packages/console/app/src/routes/zen/util/handler.ts b/packages/console/app/src/routes/zen/util/handler.ts
index 194a7c71e..edaac3a7b 100644
--- a/packages/console/app/src/routes/zen/util/handler.ts
+++ b/packages/console/app/src/routes/zen/util/handler.ts
@@ -12,14 +12,14 @@ import { UserTable } from "@opencode-ai/console-core/schema/user.sql.js"
import { ModelTable } from "@opencode-ai/console-core/schema/model.sql.js"
import { ProviderTable } from "@opencode-ai/console-core/schema/provider.sql.js"
import { logger } from "./logger"
-import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError } from "./error"
+import { AuthError, CreditsError, MonthlyLimitError, UserLimitError, ModelError, RateLimitError } from "./error"
import { createBodyConverter, createStreamPartConverter, createResponseConverter } from "./provider/provider"
import { anthropicHelper } from "./provider/anthropic"
import { openaiHelper } from "./provider/openai"
import { oaCompatHelper } from "./provider/openai-compatible"
+import { createRateLimiter } from "./rateLimiter"
type ZenData = Awaited>
-type Model = ZenData["models"][string]
export async function handler(
input: APIEvent,
@@ -28,6 +28,10 @@ export async function handler(
parseApiKey: (headers: Headers) => string | undefined
},
) {
+ type AuthInfo = Awaited>
+ type ModelInfo = Awaited>
+ type ProviderInfo = Awaited>
+
const FREE_WORKSPACES = [
"wrk_01K46JDFR0E75SG2Q8K172KF3Y", // frank
"wrk_01K6W1A3VE0KMNVSCQT43BG2SX", // opencode bench
@@ -35,6 +39,7 @@ export async function handler(
try {
const body = await input.request.json()
+ const ip = input.request.headers.get("x-real-ip") ?? ""
logger.metric({
is_tream: !!body.stream,
session: input.request.headers.get("x-opencode-session"),
@@ -42,9 +47,11 @@ export async function handler(
})
const zenData = ZenData.list()
const modelInfo = validateModel(zenData, body.model)
- const providerInfo = selectProvider(zenData, modelInfo, input.request.headers.get("x-real-ip") ?? "")
+ const providerInfo = selectProvider(zenData, modelInfo, ip)
const authInfo = await authenticate(modelInfo, providerInfo)
- validateBilling(modelInfo, authInfo)
+ const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
+ await rateLimiter?.check()
+ validateBilling(authInfo, modelInfo)
validateModelSettings(authInfo)
updateProviderKey(authInfo, providerInfo)
logger.metric({ provider: providerInfo.id })
@@ -59,7 +66,7 @@ export async function handler(
}),
)
logger.debug("REQUEST URL: " + reqUrl)
- logger.debug("REQUEST: " + reqBody)
+ logger.debug("REQUEST: " + reqBody.substring(0, 300) + "...")
const res = await fetch(reqUrl, {
method: "POST",
headers: (() => {
@@ -84,9 +91,6 @@ export async function handler(
}
}
logger.debug("STATUS: " + res.status + " " + res.statusText)
- if (res.status === 400 || res.status === 503) {
- logger.debug("RESPONSE: " + (await res.text()))
- }
// Handle non-streaming response
if (!body.stream) {
@@ -95,6 +99,7 @@ export async function handler(
const body = JSON.stringify(responseConverter(json))
logger.metric({ response_length: body.length })
logger.debug("RESPONSE: " + body)
+ await rateLimiter?.track()
await trackUsage(authInfo, modelInfo, providerInfo, json.usage)
await reload(authInfo)
return new Response(body, {
@@ -123,6 +128,7 @@ export async function handler(
response_length: responseLength,
"timestamp.last_byte": Date.now(),
})
+ await rateLimiter?.track()
const usage = usageParser.retrieve()
if (usage) {
await trackUsage(authInfo, modelInfo, providerInfo, usage)
@@ -197,6 +203,15 @@ export async function handler(
{ status: 401 },
)
+ if (error instanceof RateLimitError)
+ return new Response(
+ JSON.stringify({
+ type: "error",
+ error: { type: error.constructor.name, message: error.message },
+ }),
+ { status: 429 },
+ )
+
return new Response(
JSON.stringify({
type: "error",
@@ -221,8 +236,8 @@ export async function handler(
return { id: modelId, ...modelData }
}
- function selectProvider(zenData: ZenData, model: Awaited>, ip: string) {
- const providers = model.providers
+ function selectProvider(zenData: ZenData, modelInfo: ModelInfo, ip: string) {
+ const providers = modelInfo.providers
.filter((provider) => !provider.disabled)
.flatMap((provider) => Array(provider.weight ?? 1).fill(provider))
@@ -235,22 +250,22 @@ export async function handler(
throw new ModelError(`Provider ${provider.id} not supported`)
}
- const format = zenData.providers[provider.id].format
-
return {
...provider,
...zenData.providers[provider.id],
- ...(format === "anthropic" ? anthropicHelper : format === "openai" ? openaiHelper : oaCompatHelper),
+ ...(() => {
+ const format = zenData.providers[provider.id].format
+ if (format === "anthropic") return anthropicHelper
+ if (format === "openai") return openaiHelper
+ return oaCompatHelper
+ })(),
}
}
- async function authenticate(
- model: Awaited>,
- providerInfo: Awaited>,
- ) {
+ async function authenticate(modelInfo: ModelInfo, providerInfo: ProviderInfo) {
const apiKey = opts.parseApiKey(input.request.headers)
if (!apiKey) {
- if (model.allowAnonymous) return
+ if (modelInfo.allowAnonymous) return
throw new AuthError("Missing API key.")
}
@@ -282,7 +297,7 @@ export async function handler(
.innerJoin(WorkspaceTable, eq(WorkspaceTable.id, KeyTable.workspaceID))
.innerJoin(BillingTable, eq(BillingTable.workspaceID, KeyTable.workspaceID))
.innerJoin(UserTable, and(eq(UserTable.workspaceID, KeyTable.workspaceID), eq(UserTable.id, KeyTable.userID)))
- .leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, model.id)))
+ .leftJoin(ModelTable, and(eq(ModelTable.workspaceID, KeyTable.workspaceID), eq(ModelTable.model, modelInfo.id)))
.leftJoin(
ProviderTable,
and(eq(ProviderTable.workspaceID, KeyTable.workspaceID), eq(ProviderTable.provider, providerInfo.id)),
@@ -308,11 +323,11 @@ export async function handler(
}
}
- function validateBilling(model: Model, authInfo: Awaited>) {
+ function validateBilling(authInfo: AuthInfo, modelInfo: ModelInfo) {
if (!authInfo) return
if (authInfo.provider?.credentials) return
if (authInfo.isFree) return
- if (model.allowAnonymous) return
+ if (modelInfo.allowAnonymous) return
const billing = authInfo.billing
if (!billing.paymentMethodID)
@@ -356,26 +371,18 @@ export async function handler(
}
}
- function validateModelSettings(authInfo: Awaited>) {
+ function validateModelSettings(authInfo: AuthInfo) {
if (!authInfo) return
if (authInfo.isDisabled) throw new ModelError("Model is disabled")
}
- function updateProviderKey(
- authInfo: Awaited>,
- providerInfo: Awaited>,
- ) {
+ function updateProviderKey(authInfo: AuthInfo, providerInfo: ProviderInfo) {
if (!authInfo) return
if (!authInfo.provider?.credentials) return
providerInfo.apiKey = authInfo.provider.credentials
}
- async function trackUsage(
- authInfo: Awaited>,
- modelInfo: ReturnType,
- providerInfo: Awaited>,
- usage: any,
- ) {
+ async function trackUsage(authInfo: AuthInfo, modelInfo: ModelInfo, providerInfo: ProviderInfo, usage: any) {
const { inputTokens, outputTokens, reasoningTokens, cacheReadTokens, cacheWrite5mTokens, cacheWrite1hTokens } =
providerInfo.normalizeUsage(usage)
@@ -483,7 +490,7 @@ export async function handler(
)
}
- async function reload(authInfo: Awaited>) {
+ async function reload(authInfo: AuthInfo) {
if (!authInfo) return
if (authInfo.isFree) return
if (authInfo.provider?.credentials) return
diff --git a/packages/console/app/src/routes/zen/util/rateLimiter.ts b/packages/console/app/src/routes/zen/util/rateLimiter.ts
new file mode 100644
index 000000000..84cd5253e
--- /dev/null
+++ b/packages/console/app/src/routes/zen/util/rateLimiter.ts
@@ -0,0 +1,36 @@
+import { Resource } from "@opencode-ai/console-resource"
+import { RateLimitError } from "./error"
+import { logger } from "./logger"
+
+export function createRateLimiter(model: string, limit: number | undefined, ip: string) {
+ if (!limit) return
+
+ const now = Date.now()
+ const currKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now)}`
+ const prevKey = `usage:${ip}:${model}:${buildYYYYMMDDHH(now - 3_600_000)}`
+ let currRate: number
+ let prevRate: number
+
+ return {
+ track: async () => {
+ await Resource.GatewayKv.put(currKey, currRate + 1, { expirationTtl: 3600 })
+ },
+ check: async () => {
+ const values = await Resource.GatewayKv.get([currKey, prevKey])
+ const prevValue = values?.get(prevKey)
+ const currValue = values?.get(currKey)
+ prevRate = prevValue ? parseInt(prevValue) : 0
+ currRate = currValue ? parseInt(currValue) : 0
+ logger.debug(`rate limit ${model} prev/curr: ${prevRate}/${currRate}`)
+ if (prevRate + currRate >= limit)
+ throw new RateLimitError(`Rate limit exceeded. Please try again later.`)
+ },
+ }
+}
+
+function buildYYYYMMDDHH(timestamp: number) {
+ return new Date(timestamp)
+ .toISOString()
+ .replace(/[^0-9]/g, "")
+ .substring(0, 10)
+}
diff --git a/packages/console/app/sst-env.d.ts b/packages/console/app/sst-env.d.ts
index bd5588217..9b9de7327 100644
--- a/packages/console/app/sst-env.d.ts
+++ b/packages/console/app/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/core/script/update-models.ts b/packages/console/core/script/update-models.ts
index e7a245515..807d57826 100755
--- a/packages/console/core/script/update-models.ts
+++ b/packages/console/core/script/update-models.ts
@@ -7,7 +7,6 @@ import { ZenData } from "../src/model"
const root = path.resolve(process.cwd(), "..", "..", "..")
const models = await $`bun sst secret list`.cwd(root).text()
-console.log("models", models)
// read the line starting with "ZEN_MODELS"
const oldValue1 = models
diff --git a/packages/console/core/src/model.ts b/packages/console/core/src/model.ts
index ea719534d..46b2aa557 100644
--- a/packages/console/core/src/model.ts
+++ b/packages/console/core/src/model.ts
@@ -24,6 +24,7 @@ export namespace ZenData {
cost: ModelCostSchema,
cost200K: ModelCostSchema.optional(),
allowAnonymous: z.boolean().optional(),
+ rateLimit: z.number().optional(),
providers: z.array(
z.object({
id: z.string(),
diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts
index ba4c5a623..63c2af977 100644
--- a/packages/console/core/sst-env.d.ts
+++ b/packages/console/core/sst-env.d.ts
@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
- ADMIN_SECRET: {
- type: "sst.sst.Secret"
- value: string
+ "ADMIN_SECRET": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AWS_SES_ACCESS_KEY_ID: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_ACCESS_KEY_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AWS_SES_SECRET_ACCESS_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_SECRET_ACCESS_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "CLOUDFLARE_API_TOKEN": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Desktop: {
- type: "sst.cloudflare.StaticSite"
- url: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- EMAILOCTOPUS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "Desktop": {
+ "type": "sst.cloudflare.StaticSite"
+ "url": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "EMAILOCTOPUS_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_SECRET_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_WEBHOOK_SECRET: {
- type: "sst.sst.Linkable"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- ZEN_MODELS1: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- ZEN_MODELS2: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
+ }
+ "ZEN_MODELS1": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "ZEN_MODELS2": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "GatewayKv": cloudflare.KVNamespace
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts
index ba4c5a623..63c2af977 100644
--- a/packages/console/function/sst-env.d.ts
+++ b/packages/console/function/sst-env.d.ts
@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
- ADMIN_SECRET: {
- type: "sst.sst.Secret"
- value: string
+ "ADMIN_SECRET": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AWS_SES_ACCESS_KEY_ID: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_ACCESS_KEY_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AWS_SES_SECRET_ACCESS_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_SECRET_ACCESS_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "CLOUDFLARE_API_TOKEN": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Desktop: {
- type: "sst.cloudflare.StaticSite"
- url: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- EMAILOCTOPUS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "Desktop": {
+ "type": "sst.cloudflare.StaticSite"
+ "url": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "EMAILOCTOPUS_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_SECRET_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_WEBHOOK_SECRET: {
- type: "sst.sst.Linkable"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- ZEN_MODELS1: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- ZEN_MODELS2: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
+ }
+ "ZEN_MODELS1": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "ZEN_MODELS2": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "GatewayKv": cloudflare.KVNamespace
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/mail/sst-env.d.ts b/packages/console/mail/sst-env.d.ts
index bd5588217..9b9de7327 100644
--- a/packages/console/mail/sst-env.d.ts
+++ b/packages/console/mail/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/console/resource/package.json b/packages/console/resource/package.json
index 6553feed1..f110f6c2a 100644
--- a/packages/console/resource/package.json
+++ b/packages/console/resource/package.json
@@ -13,6 +13,9 @@
}
},
"devDependencies": {
- "@tsconfig/node22": "22.0.2"
+ "@cloudflare/workers-types": "catalog:",
+ "@tsconfig/node22": "22.0.2",
+ "@types/node": "catalog:",
+ "cloudflare": "5.2.0"
}
}
diff --git a/packages/console/resource/resource.node.ts b/packages/console/resource/resource.node.ts
index d7dbb6c6d..2369eab98 100644
--- a/packages/console/resource/resource.node.ts
+++ b/packages/console/resource/resource.node.ts
@@ -1 +1,64 @@
-export { Resource } from "sst"
+import type {
+ KVNamespaceListOptions,
+ KVNamespaceListResult,
+ KVNamespacePutOptions,
+} from "@cloudflare/workers-types"
+import { Resource as ResourceBase } from "sst"
+import Cloudflare from "cloudflare"
+
+export const Resource = new Proxy(
+ {},
+ {
+ get(_target, prop: keyof typeof ResourceBase) {
+ const value = ResourceBase[prop]
+ // @ts-ignore
+ if ("type" in value && value.type === "sst.cloudflare.Kv") {
+ const client = new Cloudflare({
+ apiToken: ResourceBase.CLOUDFLARE_API_TOKEN.value,
+ })
+ // @ts-ignore
+ const namespaceId = value.namespaceId
+ const accountId = ResourceBase.CLOUDFLARE_DEFAULT_ACCOUNT_ID.value
+ return {
+ get: (k: string | string[]) => {
+ const isMulti = Array.isArray(k)
+ return client.kv.namespaces
+ .bulkGet(namespaceId, {
+ keys: Array.isArray(k) ? k : [k],
+ account_id: accountId,
+ })
+ .then((result) =>
+ isMulti ? new Map(Object.entries(result?.values ?? {})) : result?.values?.[k],
+ )
+ },
+ put: (k: string, v: string, opts?: KVNamespacePutOptions) =>
+ client.kv.namespaces.values.update(namespaceId, k, {
+ account_id: accountId,
+ value: v,
+ expiration: opts?.expiration,
+ expiration_ttl: opts?.expirationTtl,
+ metadata: opts?.metadata,
+ }),
+ delete: (k: string) =>
+ client.kv.namespaces.values.delete(namespaceId, k, {
+ account_id: accountId,
+ }),
+ list: (opts?: KVNamespaceListOptions): Promise> =>
+ client.kv.namespaces.keys
+ .list(namespaceId, {
+ account_id: accountId,
+ prefix: opts?.prefix ?? undefined,
+ })
+ .then((result) => {
+ return {
+ keys: result.result,
+ list_complete: true,
+ cacheStatus: null,
+ }
+ }),
+ }
+ }
+ return value
+ },
+ },
+) as Record
diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts
index ba4c5a623..63c2af977 100644
--- a/packages/console/resource/sst-env.d.ts
+++ b/packages/console/resource/sst-env.d.ts
@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
- ADMIN_SECRET: {
- type: "sst.sst.Secret"
- value: string
+ "ADMIN_SECRET": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AWS_SES_ACCESS_KEY_ID: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_ACCESS_KEY_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AWS_SES_SECRET_ACCESS_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_SECRET_ACCESS_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "CLOUDFLARE_API_TOKEN": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Desktop: {
- type: "sst.cloudflare.StaticSite"
- url: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- EMAILOCTOPUS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "Desktop": {
+ "type": "sst.cloudflare.StaticSite"
+ "url": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "EMAILOCTOPUS_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_SECRET_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_WEBHOOK_SECRET: {
- type: "sst.sst.Linkable"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- ZEN_MODELS1: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- ZEN_MODELS2: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
+ }
+ "ZEN_MODELS1": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "ZEN_MODELS2": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "GatewayKv": cloudflare.KVNamespace
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/desktop/src/sst-env.d.ts b/packages/desktop/src/sst-env.d.ts
index 1b1683a1e..47a8fbec7 100644
--- a/packages/desktop/src/sst-env.d.ts
+++ b/packages/desktop/src/sst-env.d.ts
@@ -2,7 +2,9 @@
/* tslint:disable */
/* eslint-disable */
///
-interface ImportMetaEnv {}
+interface ImportMetaEnv {
+
+}
interface ImportMeta {
readonly env: ImportMetaEnv
-}
+}
\ No newline at end of file
diff --git a/packages/desktop/sst-env.d.ts b/packages/desktop/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/desktop/sst-env.d.ts
+++ b/packages/desktop/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts
index ba4c5a623..63c2af977 100644
--- a/packages/function/sst-env.d.ts
+++ b/packages/function/sst-env.d.ts
@@ -6,99 +6,108 @@
import "sst"
declare module "sst" {
export interface Resource {
- ADMIN_SECRET: {
- type: "sst.sst.Secret"
- value: string
+ "ADMIN_SECRET": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AWS_SES_ACCESS_KEY_ID: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_ACCESS_KEY_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AWS_SES_SECRET_ACCESS_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_SECRET_ACCESS_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "CLOUDFLARE_API_TOKEN": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Desktop: {
- type: "sst.cloudflare.StaticSite"
- url: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- EMAILOCTOPUS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "Desktop": {
+ "type": "sst.cloudflare.StaticSite"
+ "url": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "EMAILOCTOPUS_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_SECRET_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_WEBHOOK_SECRET: {
- type: "sst.sst.Linkable"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- ZEN_MODELS1: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- ZEN_MODELS2: {
- type: "sst.sst.Secret"
- value: string
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
+ }
+ "ZEN_MODELS1": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "ZEN_MODELS2": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types"
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types";
declare module "sst" {
export interface Resource {
- Api: cloudflare.Service
- AuthApi: cloudflare.Service
- AuthStorage: cloudflare.KVNamespace
- Bucket: cloudflare.R2Bucket
- LogProcessor: cloudflare.Service
+ "Api": cloudflare.Service
+ "AuthApi": cloudflare.Service
+ "AuthStorage": cloudflare.KVNamespace
+ "Bucket": cloudflare.R2Bucket
+ "GatewayKv": cloudflare.KVNamespace
+ "LogProcessor": cloudflare.Service
}
}
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/opencode/sst-env.d.ts
+++ b/packages/opencode/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/plugin/sst-env.d.ts b/packages/plugin/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/plugin/sst-env.d.ts
+++ b/packages/plugin/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/script/sst-env.d.ts b/packages/script/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/script/sst-env.d.ts
+++ b/packages/script/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/sdk/js/sst-env.d.ts b/packages/sdk/js/sst-env.d.ts
index bd5588217..9b9de7327 100644
--- a/packages/sdk/js/sst-env.d.ts
+++ b/packages/sdk/js/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/sdk/python/sst.pyi b/packages/sdk/python/sst.pyi
index c64110c5b..b9353f7f7 100644
--- a/packages/sdk/python/sst.pyi
+++ b/packages/sdk/python/sst.pyi
@@ -25,10 +25,17 @@ class Resource:
type: str
url: str
class AuthStorage:
+ namespaceId: str
type: str
class Bucket:
name: str
type: str
+ class CLOUDFLARE_API_TOKEN:
+ type: str
+ value: str
+ class CLOUDFLARE_DEFAULT_ACCOUNT_ID:
+ type: str
+ value: str
class Console:
type: str
url: str
@@ -60,6 +67,9 @@ class Resource:
class GOOGLE_CLIENT_ID:
type: str
value: str
+ class GatewayKv:
+ namespaceId: str
+ type: str
class HONEYCOMB_API_KEY:
type: str
value: str
diff --git a/packages/slack/sst-env.d.ts b/packages/slack/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/slack/sst-env.d.ts
+++ b/packages/slack/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/ui/sst-env.d.ts b/packages/ui/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/ui/sst-env.d.ts
+++ b/packages/ui/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/packages/web/sst-env.d.ts b/packages/web/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/packages/web/sst-env.d.ts
+++ b/packages/web/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/sdks/vscode/sst-env.d.ts b/sdks/vscode/sst-env.d.ts
index 0397645b5..b6a7e9066 100644
--- a/sdks/vscode/sst-env.d.ts
+++ b/sdks/vscode/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
+export {}
\ No newline at end of file
diff --git a/sst-env.d.ts b/sst-env.d.ts
index b80f971ab..876f05228 100644
--- a/sst-env.d.ts
+++ b/sst-env.d.ts
@@ -5,107 +5,120 @@
declare module "sst" {
export interface Resource {
- ADMIN_SECRET: {
- type: "sst.sst.Secret"
- value: string
+ "ADMIN_SECRET": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AUTH_API_URL: {
- type: "sst.sst.Linkable"
- value: string
+ "AUTH_API_URL": {
+ "type": "sst.sst.Linkable"
+ "value": string
}
- AWS_SES_ACCESS_KEY_ID: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_ACCESS_KEY_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- AWS_SES_SECRET_ACCESS_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "AWS_SES_SECRET_ACCESS_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Api: {
- type: "sst.cloudflare.Worker"
- url: string
+ "Api": {
+ "type": "sst.cloudflare.Worker"
+ "url": string
}
- AuthApi: {
- type: "sst.cloudflare.Worker"
- url: string
+ "AuthApi": {
+ "type": "sst.cloudflare.Worker"
+ "url": string
}
- AuthStorage: {
- type: "sst.cloudflare.Kv"
+ "AuthStorage": {
+ "namespaceId": string
+ "type": "sst.cloudflare.Kv"
}
- Bucket: {
- name: string
- type: "sst.cloudflare.Bucket"
+ "Bucket": {
+ "name": string
+ "type": "sst.cloudflare.Bucket"
}
- Console: {
- type: "sst.cloudflare.SolidStart"
- url: string
+ "CLOUDFLARE_API_TOKEN": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Database: {
- database: string
- host: string
- password: string
- port: number
- type: "sst.sst.Linkable"
- username: string
+ "CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Desktop: {
- type: "sst.cloudflare.StaticSite"
- url: string
+ "Console": {
+ "type": "sst.cloudflare.SolidStart"
+ "url": string
}
- EMAILOCTOPUS_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "Database": {
+ "database": string
+ "host": string
+ "password": string
+ "port": number
+ "type": "sst.sst.Linkable"
+ "username": string
}
- GITHUB_APP_ID: {
- type: "sst.sst.Secret"
- value: string
+ "Desktop": {
+ "type": "sst.cloudflare.StaticSite"
+ "url": string
}
- GITHUB_APP_PRIVATE_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "EMAILOCTOPUS_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_ID_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GITHUB_CLIENT_SECRET_CONSOLE: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_APP_PRIVATE_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- GOOGLE_CLIENT_ID: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_ID_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- HONEYCOMB_API_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GITHUB_CLIENT_SECRET_CONSOLE": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- LogProcessor: {
- type: "sst.cloudflare.Worker"
+ "GOOGLE_CLIENT_ID": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- STRIPE_SECRET_KEY: {
- type: "sst.sst.Secret"
- value: string
+ "GatewayKv": {
+ "namespaceId": string
+ "type": "sst.cloudflare.Kv"
}
- STRIPE_WEBHOOK_SECRET: {
- type: "sst.sst.Linkable"
- value: string
+ "HONEYCOMB_API_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- Web: {
- type: "sst.cloudflare.Astro"
- url: string
+ "LogProcessor": {
+ "type": "sst.cloudflare.Worker"
}
- ZEN_MODELS1: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_SECRET_KEY": {
+ "type": "sst.sst.Secret"
+ "value": string
}
- ZEN_MODELS2: {
- type: "sst.sst.Secret"
- value: string
+ "STRIPE_WEBHOOK_SECRET": {
+ "type": "sst.sst.Linkable"
+ "value": string
+ }
+ "Web": {
+ "type": "sst.cloudflare.Astro"
+ "url": string
+ }
+ "ZEN_MODELS1": {
+ "type": "sst.sst.Secret"
+ "value": string
+ }
+ "ZEN_MODELS2": {
+ "type": "sst.sst.Secret"
+ "value": string
}
}
}
///
import "sst"
-export {}
+export {}
\ No newline at end of file