mcpmake 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/README.md +691 -0
  2. package/bin/mcpmake.mjs +2 -0
  3. package/dist/analyzer/auth-detector.d.ts +12 -0
  4. package/dist/analyzer/auth-detector.js +142 -0
  5. package/dist/analyzer/dom-parser.d.ts +10 -0
  6. package/dist/analyzer/dom-parser.js +259 -0
  7. package/dist/analyzer/goal-crawler.d.ts +25 -0
  8. package/dist/analyzer/goal-crawler.js +177 -0
  9. package/dist/analyzer/hybrid-detector.d.ts +28 -0
  10. package/dist/analyzer/hybrid-detector.js +96 -0
  11. package/dist/analyzer/index.d.ts +12 -0
  12. package/dist/analyzer/index.js +8 -0
  13. package/dist/analyzer/screenshot-capture.d.ts +29 -0
  14. package/dist/analyzer/screenshot-capture.js +42 -0
  15. package/dist/analyzer/selector-builder.d.ts +19 -0
  16. package/dist/analyzer/selector-builder.js +199 -0
  17. package/dist/analyzer/semantic-analyzer.d.ts +13 -0
  18. package/dist/analyzer/semantic-analyzer.js +145 -0
  19. package/dist/analyzer/site-crawler.d.ts +38 -0
  20. package/dist/analyzer/site-crawler.js +235 -0
  21. package/dist/cloud/billing/billing-engine.d.ts +44 -0
  22. package/dist/cloud/billing/billing-engine.js +81 -0
  23. package/dist/cloud/billing/credit-store.d.ts +64 -0
  24. package/dist/cloud/billing/credit-store.js +168 -0
  25. package/dist/cloud/billing/index.d.ts +4 -0
  26. package/dist/cloud/billing/index.js +2 -0
  27. package/dist/cloud/billing/usage-store.d.ts +42 -0
  28. package/dist/cloud/billing/usage-store.js +85 -0
  29. package/dist/cloud/billing/usage-tracker.d.ts +38 -0
  30. package/dist/cloud/billing/usage-tracker.js +95 -0
  31. package/dist/cloud/build-pipeline.d.ts +39 -0
  32. package/dist/cloud/build-pipeline.js +310 -0
  33. package/dist/cloud/build-queue.d.ts +30 -0
  34. package/dist/cloud/build-queue.js +70 -0
  35. package/dist/cloud/caddy-manager.d.ts +18 -0
  36. package/dist/cloud/caddy-manager.js +97 -0
  37. package/dist/cloud/container-backend.d.ts +62 -0
  38. package/dist/cloud/container-backend.js +59 -0
  39. package/dist/cloud/container-manager.d.ts +64 -0
  40. package/dist/cloud/container-manager.js +301 -0
  41. package/dist/cloud/crypto.d.ts +27 -0
  42. package/dist/cloud/crypto.js +63 -0
  43. package/dist/cloud/db/index.d.ts +27 -0
  44. package/dist/cloud/db/index.js +53 -0
  45. package/dist/cloud/db/migrations.d.ts +12 -0
  46. package/dist/cloud/db/migrations.js +329 -0
  47. package/dist/cloud/db/pg-store.d.ts +45 -0
  48. package/dist/cloud/db/pg-store.js +336 -0
  49. package/dist/cloud/failure-tracker.d.ts +51 -0
  50. package/dist/cloud/failure-tracker.js +102 -0
  51. package/dist/cloud/idle-monitor.d.ts +30 -0
  52. package/dist/cloud/idle-monitor.js +70 -0
  53. package/dist/cloud/mailer.d.ts +21 -0
  54. package/dist/cloud/mailer.js +193 -0
  55. package/dist/cloud/mcp-proxy.d.ts +58 -0
  56. package/dist/cloud/mcp-proxy.js +203 -0
  57. package/dist/cloud/metric-samples.d.ts +43 -0
  58. package/dist/cloud/metric-samples.js +85 -0
  59. package/dist/cloud/metrics.d.ts +26 -0
  60. package/dist/cloud/metrics.js +59 -0
  61. package/dist/cloud/multipart.d.ts +26 -0
  62. package/dist/cloud/multipart.js +132 -0
  63. package/dist/cloud/observability.d.ts +27 -0
  64. package/dist/cloud/observability.js +98 -0
  65. package/dist/cloud/rate-limiter.d.ts +31 -0
  66. package/dist/cloud/rate-limiter.js +58 -0
  67. package/dist/cloud/request-security.d.ts +5 -0
  68. package/dist/cloud/request-security.js +74 -0
  69. package/dist/cloud/resource-monitor.d.ts +69 -0
  70. package/dist/cloud/resource-monitor.js +130 -0
  71. package/dist/cloud/secret-store.d.ts +38 -0
  72. package/dist/cloud/secret-store.js +103 -0
  73. package/dist/cloud/security.d.ts +26 -0
  74. package/dist/cloud/security.js +142 -0
  75. package/dist/cloud/server.d.ts +21 -0
  76. package/dist/cloud/server.js +1079 -0
  77. package/dist/cloud/shared-state.d.ts +72 -0
  78. package/dist/cloud/shared-state.js +159 -0
  79. package/dist/cloud/ssrf.d.ts +43 -0
  80. package/dist/cloud/ssrf.js +150 -0
  81. package/dist/cloud/store.d.ts +41 -0
  82. package/dist/cloud/store.js +75 -0
  83. package/dist/cloud/stripe.d.ts +78 -0
  84. package/dist/cloud/stripe.js +317 -0
  85. package/dist/cloud/telemetry-store.d.ts +53 -0
  86. package/dist/cloud/telemetry-store.js +108 -0
  87. package/dist/cloud/web/auth.d.ts +225 -0
  88. package/dist/cloud/web/auth.js +555 -0
  89. package/dist/cloud/web/charts.d.ts +70 -0
  90. package/dist/cloud/web/charts.js +178 -0
  91. package/dist/cloud/web/csrf.d.ts +14 -0
  92. package/dist/cloud/web/csrf.js +22 -0
  93. package/dist/cloud/web/docs.d.ts +40 -0
  94. package/dist/cloud/web/docs.js +174 -0
  95. package/dist/cloud/web/router.d.ts +25 -0
  96. package/dist/cloud/web/router.js +1921 -0
  97. package/dist/cloud/web/static/alpine.min.js +5 -0
  98. package/dist/cloud/web/static/favicon.svg +4 -0
  99. package/dist/cloud/web/static/htmx-sse.js +290 -0
  100. package/dist/cloud/web/static/htmx.min.js +1 -0
  101. package/dist/cloud/web/static/style.css +2683 -0
  102. package/dist/cloud/web/static-server.d.ts +13 -0
  103. package/dist/cloud/web/static-server.js +73 -0
  104. package/dist/cloud/web/template-engine.d.ts +27 -0
  105. package/dist/cloud/web/template-engine.js +146 -0
  106. package/dist/cloud/web/templates/layouts/admin.hbs +57 -0
  107. package/dist/cloud/web/templates/layouts/auth.hbs +138 -0
  108. package/dist/cloud/web/templates/layouts/base.hbs +16 -0
  109. package/dist/cloud/web/templates/layouts/dashboard.hbs +39 -0
  110. package/dist/cloud/web/templates/layouts/landing.hbs +82 -0
  111. package/dist/cloud/web/templates/pages/admin/overview.hbs +123 -0
  112. package/dist/cloud/web/templates/pages/admin/servers.hbs +129 -0
  113. package/dist/cloud/web/templates/pages/admin/telemetry.hbs +39 -0
  114. package/dist/cloud/web/templates/pages/admin/user-edit.hbs +91 -0
  115. package/dist/cloud/web/templates/pages/admin/users.hbs +179 -0
  116. package/dist/cloud/web/templates/pages/auth/forgot-password.hbs +25 -0
  117. package/dist/cloud/web/templates/pages/auth/login.hbs +33 -0
  118. package/dist/cloud/web/templates/pages/auth/register.hbs +32 -0
  119. package/dist/cloud/web/templates/pages/auth/reset-password.hbs +34 -0
  120. package/dist/cloud/web/templates/pages/dashboard/billing.hbs +140 -0
  121. package/dist/cloud/web/templates/pages/dashboard/create.hbs +173 -0
  122. package/dist/cloud/web/templates/pages/dashboard/index.hbs +8 -0
  123. package/dist/cloud/web/templates/pages/dashboard/server-detail.hbs +280 -0
  124. package/dist/cloud/web/templates/pages/dashboard/server-logs.hbs +35 -0
  125. package/dist/cloud/web/templates/pages/dashboard/server-metrics.hbs +63 -0
  126. package/dist/cloud/web/templates/pages/dashboard/servers-partial.hbs +21 -0
  127. package/dist/cloud/web/templates/pages/dashboard/servers.hbs +44 -0
  128. package/dist/cloud/web/templates/pages/docs/show.hbs +16 -0
  129. package/dist/cloud/web/templates/pages/errors/404.hbs +9 -0
  130. package/dist/cloud/web/templates/pages/errors/500.hbs +8 -0
  131. package/dist/cloud/web/templates/pages/landing/index.hbs +223 -0
  132. package/dist/cloud/web/templates/pages/legal/privacy.hbs +71 -0
  133. package/dist/cloud/web/templates/pages/legal/terms.hbs +73 -0
  134. package/dist/cloud/web/templates/partials/admin-stats.hbs +52 -0
  135. package/dist/cloud/web/templates/partials/flash-message.hbs +6 -0
  136. package/dist/cloud/web/templates/partials/pricing-table.hbs +103 -0
  137. package/dist/cloud/web/templates/partials/server-card.hbs +19 -0
  138. package/dist/cloud/web/templates/partials/status-badge.hbs +1 -0
  139. package/dist/commands/bundle.d.ts +18 -0
  140. package/dist/commands/bundle.js +82 -0
  141. package/dist/commands/ci.d.ts +25 -0
  142. package/dist/commands/ci.js +149 -0
  143. package/dist/commands/deploy.d.ts +24 -0
  144. package/dist/commands/deploy.js +145 -0
  145. package/dist/commands/diff.d.ts +18 -0
  146. package/dist/commands/diff.js +185 -0
  147. package/dist/commands/from/describe.d.ts +65 -0
  148. package/dist/commands/from/describe.js +173 -0
  149. package/dist/commands/from/har.d.ts +81 -0
  150. package/dist/commands/from/har.js +255 -0
  151. package/dist/commands/from/openapi.d.ts +105 -0
  152. package/dist/commands/from/openapi.js +302 -0
  153. package/dist/commands/from/postman.d.ts +51 -0
  154. package/dist/commands/from/postman.js +146 -0
  155. package/dist/commands/from/target-support.d.ts +11 -0
  156. package/dist/commands/from/target-support.js +33 -0
  157. package/dist/commands/from/url.d.ts +75 -0
  158. package/dist/commands/from/url.js +244 -0
  159. package/dist/commands/from/website.d.ts +75 -0
  160. package/dist/commands/from/website.js +284 -0
  161. package/dist/commands/lint.d.ts +24 -0
  162. package/dist/commands/lint.js +184 -0
  163. package/dist/commands/merge.d.ts +18 -0
  164. package/dist/commands/merge.js +161 -0
  165. package/dist/commands/publish.d.ts +27 -0
  166. package/dist/commands/publish.js +334 -0
  167. package/dist/commands/rescan.d.ts +40 -0
  168. package/dist/commands/rescan.js +255 -0
  169. package/dist/commands/update.d.ts +14 -0
  170. package/dist/commands/update.js +87 -0
  171. package/dist/commands/verify.d.ts +14 -0
  172. package/dist/commands/verify.js +71 -0
  173. package/dist/config/configurable-command.d.ts +13 -0
  174. package/dist/config/configurable-command.js +70 -0
  175. package/dist/config/mcpmake-config.d.ts +68 -0
  176. package/dist/config/mcpmake-config.js +207 -0
  177. package/dist/docs/cli.md +400 -0
  178. package/dist/docs/mcp-2026-07-28-migration.md +78 -0
  179. package/dist/docs/migrate-from-stainless.md +94 -0
  180. package/dist/docs/quickstart.md +166 -0
  181. package/dist/docs/show-hn.md +26 -0
  182. package/dist/docs/website-servers.md +169 -0
  183. package/dist/emitter/code-writer.d.ts +8 -0
  184. package/dist/emitter/code-writer.js +25 -0
  185. package/dist/emitter/index.d.ts +32 -0
  186. package/dist/emitter/index.js +280 -0
  187. package/dist/emitter/mcpb-bundler.d.ts +31 -0
  188. package/dist/emitter/mcpb-bundler.js +172 -0
  189. package/dist/emitter/project-scaffolder.d.ts +4 -0
  190. package/dist/emitter/project-scaffolder.js +89 -0
  191. package/dist/emitter/python-template-loader.d.ts +4 -0
  192. package/dist/emitter/python-template-loader.js +30 -0
  193. package/dist/emitter/python-templates/dockerfile.hbs +14 -0
  194. package/dist/emitter/python-templates/env.example.hbs +6 -0
  195. package/dist/emitter/python-templates/requirements.txt.hbs +4 -0
  196. package/dist/emitter/python-templates/server.py.hbs +77 -0
  197. package/dist/emitter/site-scaffolder.d.ts +13 -0
  198. package/dist/emitter/site-scaffolder.js +70 -0
  199. package/dist/emitter/site-template-loader.d.ts +5 -0
  200. package/dist/emitter/site-template-loader.js +47 -0
  201. package/dist/emitter/site-templates/browser-manager.ts.hbs +233 -0
  202. package/dist/emitter/site-templates/config.ts.hbs +28 -0
  203. package/dist/emitter/site-templates/dockerfile.hbs +31 -0
  204. package/dist/emitter/site-templates/env.example.hbs +19 -0
  205. package/dist/emitter/site-templates/package.json.hbs +26 -0
  206. package/dist/emitter/site-templates/server-main-http.ts.hbs +108 -0
  207. package/dist/emitter/site-templates/server-main.ts.hbs +23 -0
  208. package/dist/emitter/site-templates/tool-handler-action.ts.hbs +86 -0
  209. package/dist/emitter/site-templates/tool-handler-form.ts.hbs +116 -0
  210. package/dist/emitter/site-templates/tool-handler-lifecycle.ts.hbs +146 -0
  211. package/dist/emitter/site-templates/tool-index.ts.hbs +11 -0
  212. package/dist/emitter/template-loader.d.ts +1 -0
  213. package/dist/emitter/template-loader.js +27 -0
  214. package/dist/emitter/templates/auth-provider.ts.hbs +57 -0
  215. package/dist/emitter/templates/config.ts.hbs +63 -0
  216. package/dist/emitter/templates/discovery.ts.hbs +301 -0
  217. package/dist/emitter/templates/dockerfile.hbs +34 -0
  218. package/dist/emitter/templates/env.example.hbs +28 -0
  219. package/dist/emitter/templates/gitignore.hbs +5 -0
  220. package/dist/emitter/templates/http-executor.ts.hbs +117 -0
  221. package/dist/emitter/templates/oauth.ts.hbs +188 -0
  222. package/dist/emitter/templates/package.json.hbs +25 -0
  223. package/dist/emitter/templates/prompts.ts.hbs +22 -0
  224. package/dist/emitter/templates/readme.md.hbs +123 -0
  225. package/dist/emitter/templates/resources.ts.hbs +63 -0
  226. package/dist/emitter/templates/server-main-http.ts.hbs +407 -0
  227. package/dist/emitter/templates/server-main.ts.hbs +40 -0
  228. package/dist/emitter/templates/task-handlers.ts.hbs +189 -0
  229. package/dist/emitter/templates/task-manager.ts.hbs +139 -0
  230. package/dist/emitter/templates/task-sse.ts.hbs +105 -0
  231. package/dist/emitter/templates/tool-handler.ts.hbs +124 -0
  232. package/dist/emitter/templates/tool-index.ts.hbs +11 -0
  233. package/dist/emitter/templates/tool-test.ts.hbs +57 -0
  234. package/dist/emitter/templates/trace.ts.hbs +79 -0
  235. package/dist/emitter/templates/tsconfig.json.hbs +16 -0
  236. package/dist/emitter/templates/types.ts.hbs +5 -0
  237. package/dist/emitter/worker-template-loader.d.ts +5 -0
  238. package/dist/emitter/worker-template-loader.js +33 -0
  239. package/dist/emitter/worker-templates/config.ts.hbs +54 -0
  240. package/dist/emitter/worker-templates/dev-vars.example.hbs +10 -0
  241. package/dist/emitter/worker-templates/gitignore.hbs +6 -0
  242. package/dist/emitter/worker-templates/package.json.hbs +24 -0
  243. package/dist/emitter/worker-templates/readme.md.hbs +53 -0
  244. package/dist/emitter/worker-templates/server.test.ts.hbs +20 -0
  245. package/dist/emitter/worker-templates/tool-handler.ts.hbs +85 -0
  246. package/dist/emitter/worker-templates/tool-index.ts.hbs +28 -0
  247. package/dist/emitter/worker-templates/tsconfig.json.hbs +17 -0
  248. package/dist/emitter/worker-templates/worker.ts.hbs +242 -0
  249. package/dist/emitter/worker-templates/wrangler.toml.hbs +19 -0
  250. package/dist/generator/spec-generator.d.ts +6 -0
  251. package/dist/generator/spec-generator.js +50 -0
  252. package/dist/index.d.ts +1 -0
  253. package/dist/index.js +64 -0
  254. package/dist/parser/har-filter.d.ts +8 -0
  255. package/dist/parser/har-filter.js +71 -0
  256. package/dist/parser/har-loader.d.ts +2 -0
  257. package/dist/parser/har-loader.js +14 -0
  258. package/dist/parser/har-normalizer.d.ts +20 -0
  259. package/dist/parser/har-normalizer.js +78 -0
  260. package/dist/parser/index.d.ts +10 -0
  261. package/dist/parser/index.js +6 -0
  262. package/dist/parser/openapi-loader.d.ts +6 -0
  263. package/dist/parser/openapi-loader.js +308 -0
  264. package/dist/parser/operation-extractor.d.ts +13 -0
  265. package/dist/parser/operation-extractor.js +155 -0
  266. package/dist/parser/overlay-loader.d.ts +10 -0
  267. package/dist/parser/overlay-loader.js +184 -0
  268. package/dist/parser/postman-loader.d.ts +9 -0
  269. package/dist/parser/postman-loader.js +106 -0
  270. package/dist/parser/schema-converter.d.ts +12 -0
  271. package/dist/parser/schema-converter.js +117 -0
  272. package/dist/plugins/adapter.d.ts +40 -0
  273. package/dist/plugins/adapter.js +15 -0
  274. package/dist/plugins/loader.d.ts +25 -0
  275. package/dist/plugins/loader.js +58 -0
  276. package/dist/pricing.d.ts +55 -0
  277. package/dist/pricing.js +133 -0
  278. package/dist/providers/index.d.ts +15 -0
  279. package/dist/providers/index.js +56 -0
  280. package/dist/recorder/browser-recorder.d.ts +22 -0
  281. package/dist/recorder/browser-recorder.js +205 -0
  282. package/dist/registry/official-registry.d.ts +90 -0
  283. package/dist/registry/official-registry.js +129 -0
  284. package/dist/rescan/diff-engine.d.ts +5 -0
  285. package/dist/rescan/diff-engine.js +312 -0
  286. package/dist/rescan/index.d.ts +3 -0
  287. package/dist/rescan/index.js +2 -0
  288. package/dist/rescan/rescan-runner.d.ts +42 -0
  289. package/dist/rescan/rescan-runner.js +69 -0
  290. package/dist/rescan/rescan-scheduler.d.ts +41 -0
  291. package/dist/rescan/rescan-scheduler.js +179 -0
  292. package/dist/site-transformer/browser-tools.d.ts +10 -0
  293. package/dist/site-transformer/browser-tools.js +59 -0
  294. package/dist/site-transformer/index.d.ts +2 -0
  295. package/dist/site-transformer/index.js +2 -0
  296. package/dist/site-transformer/selector-healer.d.ts +8 -0
  297. package/dist/site-transformer/selector-healer.js +106 -0
  298. package/dist/site-transformer/tool-generator.d.ts +13 -0
  299. package/dist/site-transformer/tool-generator.js +245 -0
  300. package/dist/transformer/auth-detector.d.ts +13 -0
  301. package/dist/transformer/auth-detector.js +90 -0
  302. package/dist/transformer/catalog-builder.d.ts +18 -0
  303. package/dist/transformer/catalog-builder.js +56 -0
  304. package/dist/transformer/client-compat.d.ts +6 -0
  305. package/dist/transformer/client-compat.js +44 -0
  306. package/dist/transformer/har-clusterer.d.ts +9 -0
  307. package/dist/transformer/har-clusterer.js +27 -0
  308. package/dist/transformer/har-dedup.d.ts +10 -0
  309. package/dist/transformer/har-dedup.js +81 -0
  310. package/dist/transformer/har-schema-inferrer.d.ts +15 -0
  311. package/dist/transformer/har-schema-inferrer.js +90 -0
  312. package/dist/transformer/har-to-operations.d.ts +13 -0
  313. package/dist/transformer/har-to-operations.js +192 -0
  314. package/dist/transformer/index.d.ts +8 -0
  315. package/dist/transformer/index.js +6 -0
  316. package/dist/transformer/llm-namer.d.ts +6 -0
  317. package/dist/transformer/llm-namer.js +59 -0
  318. package/dist/transformer/naming.d.ts +4 -0
  319. package/dist/transformer/naming.js +30 -0
  320. package/dist/transformer/operation-filter.d.ts +13 -0
  321. package/dist/transformer/operation-filter.js +52 -0
  322. package/dist/transformer/resource-builder.d.ts +12 -0
  323. package/dist/transformer/resource-builder.js +80 -0
  324. package/dist/transformer/schema-merger.d.ts +14 -0
  325. package/dist/transformer/schema-merger.js +65 -0
  326. package/dist/transformer/tool-builder.d.ts +3 -0
  327. package/dist/transformer/tool-builder.js +114 -0
  328. package/dist/types/index.d.ts +131 -0
  329. package/dist/types/index.js +1 -0
  330. package/dist/types/site.d.ts +284 -0
  331. package/dist/types/site.js +8 -0
  332. package/dist/utils/fail.d.ts +48 -0
  333. package/dist/utils/fail.js +204 -0
  334. package/dist/utils/fs.d.ts +5 -0
  335. package/dist/utils/fs.js +28 -0
  336. package/dist/utils/interactive.d.ts +6 -0
  337. package/dist/utils/interactive.js +30 -0
  338. package/dist/utils/logger.d.ts +1 -0
  339. package/dist/utils/logger.js +2 -0
  340. package/dist/utils/sanitize.d.ts +28 -0
  341. package/dist/utils/sanitize.js +44 -0
  342. package/dist/utils/watcher.d.ts +11 -0
  343. package/dist/utils/watcher.js +36 -0
  344. package/package.json +65 -0
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shared singletons used by both the API server and the web router.
3
+ * Extracted to break circular dependencies between server.ts and router.ts.
4
+ *
5
+ * Environment variables:
6
+ * DATABASE_URL — PostgreSQL connection string (optional; in-memory when unset)
7
+ * ENCRYPTION_KEY — Master key for secret encryption (>=32 chars in production)
8
+ * ADMIN_EMAIL — Email address auto-promoted to admin on registration
9
+ * ADMIN_TOKEN — Bearer token for API-level admin endpoints
10
+ * IDLE_THRESHOLD_MS — Idle timeout before containers are stopped (default: 3600000)
11
+ * TRUST_PROXY — Set to "true" to trust X-Forwarded-* headers
12
+ * STRIPE_SECRET_KEY — Stripe secret API key (optional; billing disabled when unset)
13
+ * STRIPE_WEBHOOK_SECRET — Stripe webhook signing secret for signature verification
14
+ * STRIPE_PRICE_HOBBYIST — Stripe Price ID for the Hobbyist plan ($9/mo)
15
+ * STRIPE_PRICE_PRO — Stripe Price ID for the Pro plan ($29/mo)
16
+ * STRIPE_PRICE_TEAM — Stripe Price ID for the Team plan ($99/mo)
17
+ */
18
+ import { ServerStore, PortAllocator } from './store.js';
19
+ import { MetricsStore } from './metrics.js';
20
+ import { RateLimiter } from './rate-limiter.js';
21
+ import { UsageTracker } from './billing/usage-tracker.js';
22
+ import { type UsageStore } from './billing/usage-store.js';
23
+ import { type CreditStore } from './billing/credit-store.js';
24
+ import { FailureTracker } from './failure-tracker.js';
25
+ import { type MetricSampleStore } from './metric-samples.js';
26
+ import { type TelemetryStore } from './telemetry-store.js';
27
+ import { SecretStore } from './secret-store.js';
28
+ import type { Database } from './db/index.js';
29
+ import type { PgServerStore } from './db/pg-store.js';
30
+ import type { PgUserStore, PgSessionStore } from './db/pg-store.js';
31
+ export declare const MAX_SPEC_SIZE: number;
32
+ export declare const MAX_NAME_LENGTH = 100;
33
+ export declare const UPLOAD_DIR = "/tmp/mcpmake-uploads";
34
+ export declare const store: ServerStore;
35
+ export declare const ports: PortAllocator;
36
+ export declare const uploadLimiter: RateLimiter;
37
+ export declare const loginLimiter: RateLimiter;
38
+ export declare const resetLimiter: RateLimiter;
39
+ export declare const telemetryLimiter: RateLimiter;
40
+ export declare const metrics: MetricsStore;
41
+ export declare const usageTracker: UsageTracker;
42
+ /** Rolling per-minute API failure-rate counter (in-memory, bounded). */
43
+ export declare const failureTracker: FailureTracker;
44
+ /** Returns the active Database instance, or null when running in-memory. */
45
+ export declare function getDb(): Database | null;
46
+ /** Returns the Pg-backed server store, or null when running in-memory. */
47
+ export declare function getPgServerStore(): PgServerStore | null;
48
+ /** Returns the Pg-backed user store, or null when running in-memory. */
49
+ export declare function getPgUserStore(): PgUserStore | null;
50
+ /** Returns the Pg-backed session store, or null when running in-memory. */
51
+ export declare function getPgSessionStore(): PgSessionStore | null;
52
+ /** Returns the active usage store (Pg-backed when a database is configured). */
53
+ export declare function getUsageStore(): UsageStore;
54
+ /** Returns the active credit store (Pg-backed when a database is configured). */
55
+ export declare function getCreditStore(): CreditStore;
56
+ /** Returns the active metric-sample store (Pg-backed when a database is configured). */
57
+ export declare function getMetricSampleStore(): MetricSampleStore;
58
+ /** Returns the active telemetry store (Pg-backed when a database is configured). */
59
+ export declare function getTelemetryStore(): TelemetryStore;
60
+ /** Returns the secret store. Lazily initialised on first access. */
61
+ export declare function getSecretStore(): SecretStore;
62
+ /**
63
+ * Initialise stores from environment.
64
+ *
65
+ * When `DATABASE_URL` is set, creates PostgreSQL-backed stores and runs
66
+ * pending migrations. The Pg stores are exposed via getter functions above
67
+ * so that callers can be migrated incrementally.
68
+ *
69
+ * The original in-memory `store`, `userStore`, and `sessionStore` remain
70
+ * available for code that hasn't been ported to async yet.
71
+ */
72
+ export declare function initStores(): Promise<void>;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Shared singletons used by both the API server and the web router.
3
+ * Extracted to break circular dependencies between server.ts and router.ts.
4
+ *
5
+ * Environment variables:
6
+ * DATABASE_URL — PostgreSQL connection string (optional; in-memory when unset)
7
+ * ENCRYPTION_KEY — Master key for secret encryption (>=32 chars in production)
8
+ * ADMIN_EMAIL — Email address auto-promoted to admin on registration
9
+ * ADMIN_TOKEN — Bearer token for API-level admin endpoints
10
+ * IDLE_THRESHOLD_MS — Idle timeout before containers are stopped (default: 3600000)
11
+ * TRUST_PROXY — Set to "true" to trust X-Forwarded-* headers
12
+ * STRIPE_SECRET_KEY — Stripe secret API key (optional; billing disabled when unset)
13
+ * STRIPE_WEBHOOK_SECRET — Stripe webhook signing secret for signature verification
14
+ * STRIPE_PRICE_HOBBYIST — Stripe Price ID for the Hobbyist plan ($9/mo)
15
+ * STRIPE_PRICE_PRO — Stripe Price ID for the Pro plan ($29/mo)
16
+ * STRIPE_PRICE_TEAM — Stripe Price ID for the Team plan ($99/mo)
17
+ */
18
+ import os from 'node:os';
19
+ import { ServerStore, PortAllocator } from './store.js';
20
+ import { MetricsStore } from './metrics.js';
21
+ import { RateLimiter } from './rate-limiter.js';
22
+ import { UsageTracker } from './billing/usage-tracker.js';
23
+ import { InMemoryUsageStore } from './billing/usage-store.js';
24
+ import { InMemoryCreditStore } from './billing/credit-store.js';
25
+ import { FailureTracker } from './failure-tracker.js';
26
+ import { InMemoryMetricSampleStore } from './metric-samples.js';
27
+ import { InMemoryTelemetryStore } from './telemetry-store.js';
28
+ import { SecretStore } from './secret-store.js';
29
+ import { logger } from '../utils/logger.js';
30
+ export const MAX_SPEC_SIZE = 5 * 1024 * 1024; // 5 MB
31
+ export const MAX_NAME_LENGTH = 100;
32
+ export const UPLOAD_DIR = '/tmp/mcpmake-uploads';
33
+ export const store = new ServerStore();
34
+ export const ports = new PortAllocator();
35
+ export const uploadLimiter = new RateLimiter({ maxRequests: 10, windowMs: 86_400_000 }); // 10/day
36
+ export const loginLimiter = new RateLimiter({ maxRequests: 10, windowMs: 3_600_000 }); // 10/hour per key
37
+ export const resetLimiter = new RateLimiter({ maxRequests: 3, windowMs: 3_600_000 }); // 3/hour per key
38
+ // Per-IP flood cap for the unauthenticated telemetry endpoint. NOTE: behind a
39
+ // reverse proxy without TRUST_PROXY=true every request appears to come from
40
+ // 127.0.0.1, so this degrades to a single global bucket (a coarse but still
41
+ // useful flood cap). Same limitation as uploadLimiter/loginLimiter.
42
+ export const telemetryLimiter = new RateLimiter({ maxRequests: 20, windowMs: 3_600_000 }); // 20/hour per key
43
+ export const metrics = new MetricsStore();
44
+ export const usageTracker = new UsageTracker();
45
+ /** Rolling per-minute API failure-rate counter (in-memory, bounded). */
46
+ export const failureTracker = new FailureTracker();
47
+ // ---------------------------------------------------------------------------
48
+ // PostgreSQL-backed stores (populated by initStores when DATABASE_URL is set)
49
+ // ---------------------------------------------------------------------------
50
+ let _db = null;
51
+ let _pgServerStore = null;
52
+ let _pgUserStore = null;
53
+ let _pgSessionStore = null;
54
+ let _secretStore = null;
55
+ let _usageStore = new InMemoryUsageStore();
56
+ let _creditStore = new InMemoryCreditStore();
57
+ let _metricSampleStore = new InMemoryMetricSampleStore();
58
+ let _telemetryStore = new InMemoryTelemetryStore();
59
+ /** Returns the active Database instance, or null when running in-memory. */
60
+ export function getDb() {
61
+ return _db;
62
+ }
63
+ /** Returns the Pg-backed server store, or null when running in-memory. */
64
+ export function getPgServerStore() {
65
+ return _pgServerStore;
66
+ }
67
+ /** Returns the Pg-backed user store, or null when running in-memory. */
68
+ export function getPgUserStore() {
69
+ return _pgUserStore;
70
+ }
71
+ /** Returns the Pg-backed session store, or null when running in-memory. */
72
+ export function getPgSessionStore() {
73
+ return _pgSessionStore;
74
+ }
75
+ /** Returns the active usage store (Pg-backed when a database is configured). */
76
+ export function getUsageStore() {
77
+ return _usageStore;
78
+ }
79
+ /** Returns the active credit store (Pg-backed when a database is configured). */
80
+ export function getCreditStore() {
81
+ return _creditStore;
82
+ }
83
+ /** Returns the active metric-sample store (Pg-backed when a database is configured). */
84
+ export function getMetricSampleStore() {
85
+ return _metricSampleStore;
86
+ }
87
+ /** Returns the active telemetry store (Pg-backed when a database is configured). */
88
+ export function getTelemetryStore() {
89
+ return _telemetryStore;
90
+ }
91
+ /** Returns the secret store. Lazily initialised on first access. */
92
+ export function getSecretStore() {
93
+ if (!_secretStore) {
94
+ _secretStore = createSecretStore();
95
+ }
96
+ return _secretStore;
97
+ }
98
+ /**
99
+ * Create a SecretStore from the ENCRYPTION_KEY env var.
100
+ * Falls back to a machine-specific derived key in development.
101
+ */
102
+ function createSecretStore(db) {
103
+ let masterKey = process.env.ENCRYPTION_KEY;
104
+ if (!masterKey || masterKey.length < 32) {
105
+ if (process.env.NODE_ENV === 'production' || process.env.DOMAIN) {
106
+ throw new Error('ENCRYPTION_KEY must be set to at least 32 characters in production');
107
+ }
108
+ const devKey = `dev-insecure-${os.hostname()}`;
109
+ logger.warn('ENCRYPTION_KEY is not set or is shorter than 32 characters. ' +
110
+ 'Using a derived dev key — NOT suitable for production.');
111
+ masterKey = devKey;
112
+ }
113
+ return new SecretStore(masterKey, db);
114
+ }
115
+ /**
116
+ * Initialise stores from environment.
117
+ *
118
+ * When `DATABASE_URL` is set, creates PostgreSQL-backed stores and runs
119
+ * pending migrations. The Pg stores are exposed via getter functions above
120
+ * so that callers can be migrated incrementally.
121
+ *
122
+ * The original in-memory `store`, `userStore`, and `sessionStore` remain
123
+ * available for code that hasn't been ported to async yet.
124
+ */
125
+ export async function initStores() {
126
+ const databaseUrl = process.env.DATABASE_URL;
127
+ if (!databaseUrl)
128
+ return;
129
+ try {
130
+ const { createDatabase } = await import('./db/index.js');
131
+ const { runMigrations } = await import('./db/migrations.js');
132
+ const { PgServerStore: PgSS, PgUserStore: PgUS, PgSessionStore: PgSesS, } = await import('./db/pg-store.js');
133
+ const db = await createDatabase(databaseUrl);
134
+ await runMigrations(db);
135
+ _db = db;
136
+ _pgServerStore = new PgSS(db);
137
+ _pgUserStore = new PgUS(db);
138
+ _pgSessionStore = new PgSesS(db);
139
+ const { PgUsageStore } = await import('./billing/usage-store.js');
140
+ _usageStore = new PgUsageStore(db);
141
+ const { PgCreditStore } = await import('./billing/credit-store.js');
142
+ _creditStore = new PgCreditStore(db);
143
+ const { PgMetricSampleStore } = await import('./metric-samples.js');
144
+ _metricSampleStore = new PgMetricSampleStore(db);
145
+ const { PgTelemetryStore } = await import('./telemetry-store.js');
146
+ _telemetryStore = new PgTelemetryStore(db);
147
+ // Wire the secret store to the database
148
+ if (_secretStore) {
149
+ _secretStore.setDatabase(db);
150
+ }
151
+ else {
152
+ _secretStore = createSecretStore(db);
153
+ }
154
+ logger.info('PostgreSQL stores initialized');
155
+ }
156
+ catch (err) {
157
+ logger.warn(`PostgreSQL initialization failed, using in-memory stores: ${err instanceof Error ? err.message : err}`);
158
+ }
159
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * SSRF protection for user-supplied URLs.
3
+ *
4
+ * Before the backend fetches a spec URL or crawls a website on behalf of a
5
+ * user, we must ensure the target is a genuinely public host. Otherwise an
6
+ * attacker could point us at internal services, the cloud metadata endpoint
7
+ * (169.254.169.254), or other private infrastructure.
8
+ *
9
+ * Guards:
10
+ * - protocol must be http/https
11
+ * - the hostname's resolved IP(s) must all be public (no loopback,
12
+ * private, link-local, CGNAT, unique-local, etc.)
13
+ * - redirects are followed manually and each hop is re-validated
14
+ */
15
+ export declare class SsrfError extends Error {
16
+ constructor(message: string);
17
+ }
18
+ /**
19
+ * Return true if an IP address string falls in a blocked (non-public) range.
20
+ */
21
+ export declare function isBlockedIp(ip: string): boolean;
22
+ /**
23
+ * Validate that a URL targets a public host. Resolves DNS and checks every
24
+ * returned address. Throws {@link SsrfError} on any violation.
25
+ *
26
+ * @returns the resolved public IP and address family for connection pinning.
27
+ */
28
+ export declare function assertPublicUrl(rawUrl: string): Promise<{
29
+ url: URL;
30
+ address: string;
31
+ family: number;
32
+ }>;
33
+ /**
34
+ * Fetch a URL with SSRF protection, following up to `maxRedirects` redirects
35
+ * and re-validating each hop. Returns the final Response.
36
+ *
37
+ * Note: Node's global fetch resolves DNS itself, so there is a small TOCTOU
38
+ * gap between our lookup and the connect. The network-level egress allowlist
39
+ * (see deploy/harden-egress.sh) is the backstop for that residual risk.
40
+ */
41
+ export declare function safeFetch(rawUrl: string, init?: RequestInit & {
42
+ timeoutMs?: number;
43
+ }, maxRedirects?: number): Promise<Response>;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * SSRF protection for user-supplied URLs.
3
+ *
4
+ * Before the backend fetches a spec URL or crawls a website on behalf of a
5
+ * user, we must ensure the target is a genuinely public host. Otherwise an
6
+ * attacker could point us at internal services, the cloud metadata endpoint
7
+ * (169.254.169.254), or other private infrastructure.
8
+ *
9
+ * Guards:
10
+ * - protocol must be http/https
11
+ * - the hostname's resolved IP(s) must all be public (no loopback,
12
+ * private, link-local, CGNAT, unique-local, etc.)
13
+ * - redirects are followed manually and each hop is re-validated
14
+ */
15
+ import { lookup } from 'node:dns/promises';
16
+ import { isIP } from 'node:net';
17
+ export class SsrfError extends Error {
18
+ constructor(message) {
19
+ super(message);
20
+ this.name = 'SsrfError';
21
+ }
22
+ }
23
+ /**
24
+ * Return true if an IP address string falls in a blocked (non-public) range.
25
+ */
26
+ export function isBlockedIp(ip) {
27
+ const family = isIP(ip);
28
+ if (family === 4)
29
+ return isBlockedIpv4(ip);
30
+ if (family === 6)
31
+ return isBlockedIpv6(ip);
32
+ return true; // not a valid IP — refuse
33
+ }
34
+ function isBlockedIpv4(ip) {
35
+ const parts = ip.split('.').map((p) => parseInt(p, 10));
36
+ if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255))
37
+ return true;
38
+ const [a, b] = parts;
39
+ if (a === 0)
40
+ return true; // 0.0.0.0/8 "this host"
41
+ if (a === 10)
42
+ return true; // 10.0.0.0/8 private
43
+ if (a === 127)
44
+ return true; // 127.0.0.0/8 loopback
45
+ if (a === 169 && b === 254)
46
+ return true; // 169.254.0.0/16 link-local incl. cloud metadata
47
+ if (a === 172 && b >= 16 && b <= 31)
48
+ return true; // 172.16.0.0/12 private
49
+ if (a === 192 && b === 168)
50
+ return true; // 192.168.0.0/16 private
51
+ if (a === 100 && b >= 64 && b <= 127)
52
+ return true; // 100.64.0.0/10 CGNAT
53
+ if (a === 192 && b === 0)
54
+ return true; // 192.0.0.0/24 + 192.0.2.0/24 special-use
55
+ if (a === 198 && (b === 18 || b === 19))
56
+ return true; // 198.18.0.0/15 benchmarking
57
+ if (a >= 224)
58
+ return true; // 224.0.0.0/4 multicast + 240.0.0.0/4 reserved
59
+ return false;
60
+ }
61
+ function isBlockedIpv6(ip) {
62
+ const lower = ip.toLowerCase();
63
+ // IPv4-mapped (::ffff:a.b.c.d) — validate the embedded IPv4
64
+ const mapped = lower.match(/^::ffff:(\d+\.\d+\.\d+\.\d+)$/);
65
+ if (mapped)
66
+ return isBlockedIpv4(mapped[1]);
67
+ if (lower === '::1' || lower === '::')
68
+ return true; // loopback / unspecified
69
+ if (lower.startsWith('fe80'))
70
+ return true; // link-local
71
+ if (lower.startsWith('fc') || lower.startsWith('fd'))
72
+ return true; // fc00::/7 unique-local
73
+ if (lower.startsWith('ff'))
74
+ return true; // multicast
75
+ if (lower.startsWith('::ffff:'))
76
+ return true; // other v4-mapped forms — refuse
77
+ if (lower.startsWith('2001:db8'))
78
+ return true; // documentation
79
+ return false;
80
+ }
81
+ /**
82
+ * Validate that a URL targets a public host. Resolves DNS and checks every
83
+ * returned address. Throws {@link SsrfError} on any violation.
84
+ *
85
+ * @returns the resolved public IP and address family for connection pinning.
86
+ */
87
+ export async function assertPublicUrl(rawUrl) {
88
+ let url;
89
+ try {
90
+ url = new URL(rawUrl);
91
+ }
92
+ catch {
93
+ throw new SsrfError('Invalid URL');
94
+ }
95
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
96
+ throw new SsrfError(`Unsupported protocol: ${url.protocol}`);
97
+ }
98
+ const hostname = url.hostname.replace(/^\[|\]$/g, ''); // strip IPv6 brackets
99
+ // Literal IP — check directly.
100
+ if (isIP(hostname)) {
101
+ if (isBlockedIp(hostname)) {
102
+ throw new SsrfError(`Blocked non-public address: ${hostname}`);
103
+ }
104
+ return { url, address: hostname, family: isIP(hostname) };
105
+ }
106
+ // Hostname — resolve ALL addresses and ensure every one is public.
107
+ let addresses;
108
+ try {
109
+ addresses = await lookup(hostname, { all: true });
110
+ }
111
+ catch {
112
+ throw new SsrfError(`Could not resolve host: ${hostname}`);
113
+ }
114
+ if (addresses.length === 0) {
115
+ throw new SsrfError(`Host did not resolve: ${hostname}`);
116
+ }
117
+ for (const addr of addresses) {
118
+ if (isBlockedIp(addr.address)) {
119
+ throw new SsrfError(`Host ${hostname} resolves to a blocked address: ${addr.address}`);
120
+ }
121
+ }
122
+ return { url, address: addresses[0].address, family: addresses[0].family };
123
+ }
124
+ /**
125
+ * Fetch a URL with SSRF protection, following up to `maxRedirects` redirects
126
+ * and re-validating each hop. Returns the final Response.
127
+ *
128
+ * Note: Node's global fetch resolves DNS itself, so there is a small TOCTOU
129
+ * gap between our lookup and the connect. The network-level egress allowlist
130
+ * (see deploy/harden-egress.sh) is the backstop for that residual risk.
131
+ */
132
+ export async function safeFetch(rawUrl, init = {}, maxRedirects = 5) {
133
+ const { timeoutMs = 15_000, ...rest } = init;
134
+ let current = rawUrl;
135
+ for (let hop = 0; hop <= maxRedirects; hop++) {
136
+ await assertPublicUrl(current);
137
+ const res = await fetch(current, {
138
+ ...rest,
139
+ redirect: 'manual',
140
+ signal: rest.signal ?? AbortSignal.timeout(timeoutMs),
141
+ });
142
+ if (res.status >= 300 && res.status < 400 && res.headers.has('location')) {
143
+ const location = res.headers.get('location');
144
+ current = new URL(location, current).toString();
145
+ continue;
146
+ }
147
+ return res;
148
+ }
149
+ throw new SsrfError(`Too many redirects (>${maxRedirects})`);
150
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * In-memory server metadata store (MVP).
3
+ * Will be replaced with Postgres in a later phase.
4
+ */
5
+ export interface ServerRecord {
6
+ slug: string;
7
+ userId?: string;
8
+ specHash: string;
9
+ containerId: string;
10
+ port: number;
11
+ bearerToken: string;
12
+ status: 'building' | 'running' | 'stopped' | 'error';
13
+ toolCount: number;
14
+ /** Type of MCP server: 'http' for API proxies, 'playwright' for website scrapers. Defaults to 'http'. */
15
+ serverType?: 'http' | 'playwright';
16
+ createdAt: string;
17
+ lastActiveAt: string;
18
+ }
19
+ export declare class ServerStore {
20
+ private servers;
21
+ create(record: ServerRecord): void;
22
+ get(slug: string): ServerRecord | undefined;
23
+ list(): ServerRecord[];
24
+ update(slug: string, updates: Partial<ServerRecord>): void;
25
+ delete(slug: string): void;
26
+ findByPort(port: number): ServerRecord | undefined;
27
+ }
28
+ /**
29
+ * Port allocator for assigning host ports to containers.
30
+ * Allocates from a configurable range (default 4000-4999).
31
+ */
32
+ export declare class PortAllocator {
33
+ private readonly minPort;
34
+ private readonly maxPort;
35
+ private allocated;
36
+ private nextCandidate;
37
+ constructor(minPort?: number, maxPort?: number);
38
+ allocate(): number;
39
+ release(port: number): void;
40
+ get allocatedCount(): number;
41
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * In-memory server metadata store (MVP).
3
+ * Will be replaced with Postgres in a later phase.
4
+ */
5
+ export class ServerStore {
6
+ servers = new Map();
7
+ create(record) {
8
+ if (this.servers.has(record.slug)) {
9
+ throw new Error(`Server with slug "${record.slug}" already exists`);
10
+ }
11
+ // Default serverType to 'http' for backwards compatibility
12
+ this.servers.set(record.slug, { serverType: 'http', ...record });
13
+ }
14
+ get(slug) {
15
+ const record = this.servers.get(slug);
16
+ return record ? { ...record } : undefined;
17
+ }
18
+ list() {
19
+ return [...this.servers.values()].map((r) => ({ ...r }));
20
+ }
21
+ update(slug, updates) {
22
+ const existing = this.servers.get(slug);
23
+ if (!existing) {
24
+ throw new Error(`Server with slug "${slug}" not found`);
25
+ }
26
+ // Prevent changing the slug via update
27
+ const { slug: _ignored, ...safeUpdates } = updates;
28
+ this.servers.set(slug, { ...existing, ...safeUpdates });
29
+ }
30
+ delete(slug) {
31
+ this.servers.delete(slug);
32
+ }
33
+ findByPort(port) {
34
+ for (const record of this.servers.values()) {
35
+ if (record.port === port) {
36
+ return { ...record };
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+ }
42
+ /**
43
+ * Port allocator for assigning host ports to containers.
44
+ * Allocates from a configurable range (default 4000-4999).
45
+ */
46
+ export class PortAllocator {
47
+ minPort;
48
+ maxPort;
49
+ allocated = new Set();
50
+ nextCandidate;
51
+ constructor(minPort = 4000, maxPort = 4999) {
52
+ this.minPort = minPort;
53
+ this.maxPort = maxPort;
54
+ this.nextCandidate = minPort;
55
+ }
56
+ allocate() {
57
+ const range = this.maxPort - this.minPort + 1;
58
+ // Try sequential allocation first for simplicity
59
+ for (let attempts = 0; attempts < range; attempts++) {
60
+ const port = this.nextCandidate;
61
+ this.nextCandidate = this.minPort + ((this.nextCandidate - this.minPort + 1) % range);
62
+ if (!this.allocated.has(port)) {
63
+ this.allocated.add(port);
64
+ return port;
65
+ }
66
+ }
67
+ throw new Error(`No available ports in range ${this.minPort}-${this.maxPort} (${this.allocated.size} allocated)`);
68
+ }
69
+ release(port) {
70
+ this.allocated.delete(port);
71
+ }
72
+ get allocatedCount() {
73
+ return this.allocated.size;
74
+ }
75
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Stripe payment integration.
3
+ *
4
+ * Optional module — if STRIPE_SECRET_KEY is not set, all functions return
5
+ * null or throw descriptive errors. Uses dynamic import for the `stripe`
6
+ * package to avoid a hard dependency (same pattern as `pg`).
7
+ *
8
+ * Environment variables:
9
+ * STRIPE_SECRET_KEY — Stripe secret API key
10
+ * STRIPE_WEBHOOK_SECRET — Webhook endpoint signing secret
11
+ * STRIPE_PRICE_HOBBYIST — Price ID for the Hobbyist plan
12
+ * STRIPE_PRICE_PRO — Price ID for the Pro plan
13
+ * STRIPE_PRICE_TEAM — Price ID for the Team plan
14
+ */
15
+ /** Minimal subset of the Stripe SDK we actually use. */
16
+ interface StripeClient {
17
+ checkout: {
18
+ sessions: {
19
+ create(params: Record<string, unknown>): Promise<{
20
+ url: string | null;
21
+ }>;
22
+ };
23
+ };
24
+ billingPortal: {
25
+ sessions: {
26
+ create(params: Record<string, unknown>): Promise<{
27
+ url: string;
28
+ }>;
29
+ };
30
+ };
31
+ webhooks: {
32
+ constructEvent(payload: Buffer | string, sig: string, secret: string): unknown;
33
+ };
34
+ }
35
+ export declare const PLAN_PRICE_IDS: Record<string, string>;
36
+ /**
37
+ * Get a configured Stripe client. Returns `null` when STRIPE_SECRET_KEY
38
+ * is not set. Caches the instance after first successful creation.
39
+ */
40
+ export declare function getStripeClient(): Promise<StripeClient | null>;
41
+ /**
42
+ * Returns true when Stripe is configured (secret key is present).
43
+ * Does NOT load the Stripe SDK — just checks the env var.
44
+ */
45
+ export declare function isStripeConfigured(): boolean;
46
+ /**
47
+ * Create a Stripe Checkout session for a plan upgrade.
48
+ *
49
+ * @returns The checkout session URL to redirect the user to.
50
+ * @throws If Stripe is not configured or the plan has no price ID.
51
+ */
52
+ export declare function createCheckoutSession(opts: {
53
+ userId: string;
54
+ email: string;
55
+ plan: string;
56
+ successUrl: string;
57
+ cancelUrl: string;
58
+ }): Promise<string>;
59
+ /**
60
+ * Create a Stripe billing portal session so the customer can manage
61
+ * their subscription (cancel, change payment method, view invoices).
62
+ *
63
+ * @returns The portal session URL to redirect the user to.
64
+ */
65
+ export declare function createPortalSession(customerId: string, returnUrl: string): Promise<string>;
66
+ /**
67
+ * Verify and handle an incoming Stripe webhook event.
68
+ *
69
+ * Handles:
70
+ * - checkout.session.completed — activate subscription, store customer ID
71
+ * - customer.subscription.updated — plan change / renewal
72
+ * - customer.subscription.deleted — cancellation, downgrade to free
73
+ *
74
+ * @param payload - The raw request body as a Buffer (required for signature verification)
75
+ * @param signature - The `stripe-signature` header value
76
+ */
77
+ export declare function handleWebhookEvent(payload: Buffer, signature: string): Promise<void>;
78
+ export {};