vibecarbon 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 (495) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +188 -0
  3. package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
  4. package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
  5. package/carbon/.claude/settings.json +29 -0
  6. package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
  7. package/carbon/.dockerignore +57 -0
  8. package/carbon/.env.example +219 -0
  9. package/carbon/.github/copilot-instructions.md +3 -0
  10. package/carbon/.github/workflows/deploy.yml +346 -0
  11. package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
  12. package/carbon/.windsurfrules +74 -0
  13. package/carbon/AGENTS.md +422 -0
  14. package/carbon/CLAUDE.md +3 -0
  15. package/carbon/DEVELOPMENT.md +187 -0
  16. package/carbon/Dockerfile +98 -0
  17. package/carbon/LICENSE +21 -0
  18. package/carbon/PRODUCTION.md +364 -0
  19. package/carbon/README.md +193 -0
  20. package/carbon/TESTING.md +138 -0
  21. package/carbon/backup/Dockerfile +75 -0
  22. package/carbon/backup/backup.sh +95 -0
  23. package/carbon/backup/compose-backup.sh +140 -0
  24. package/carbon/biome.json +83 -0
  25. package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
  26. package/carbon/cloud-init/k3s/master-init.sh +215 -0
  27. package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
  28. package/carbon/cloud-init/k3s/worker-init.sh +147 -0
  29. package/carbon/components.json +24 -0
  30. package/carbon/content/blog/authentication-guide.mdx +34 -0
  31. package/carbon/content/blog/getting-started.mdx +41 -0
  32. package/carbon/content/changelog/v0-1-0.mdx +40 -0
  33. package/carbon/content/docs/analytics.mdx +90 -0
  34. package/carbon/content/docs/authentication.mdx +231 -0
  35. package/carbon/content/docs/background-jobs.mdx +116 -0
  36. package/carbon/content/docs/cli.mdx +630 -0
  37. package/carbon/content/docs/database.mdx +236 -0
  38. package/carbon/content/docs/deployment.mdx +227 -0
  39. package/carbon/content/docs/development.mdx +238 -0
  40. package/carbon/content/docs/environments.mdx +84 -0
  41. package/carbon/content/docs/getting-started.mdx +112 -0
  42. package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
  43. package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
  44. package/carbon/content/docs/optional-services.mdx +160 -0
  45. package/carbon/db/Dockerfile +23 -0
  46. package/carbon/docker-compose.dev-init.yml +5 -0
  47. package/carbon/docker-compose.metabase.override.yml +14 -0
  48. package/carbon/docker-compose.metabase.prod.yml +28 -0
  49. package/carbon/docker-compose.metabase.yml +84 -0
  50. package/carbon/docker-compose.n8n.override.yml +14 -0
  51. package/carbon/docker-compose.n8n.prod.yml +31 -0
  52. package/carbon/docker-compose.n8n.yml +97 -0
  53. package/carbon/docker-compose.observability.override.yml +18 -0
  54. package/carbon/docker-compose.observability.prod.yml +28 -0
  55. package/carbon/docker-compose.observability.yml +125 -0
  56. package/carbon/docker-compose.override.yml +28 -0
  57. package/carbon/docker-compose.prod.yml +294 -0
  58. package/carbon/docker-compose.yml +508 -0
  59. package/carbon/docker-entrypoint.sh +12 -0
  60. package/carbon/ha/activate-standby.sh +52 -0
  61. package/carbon/ha/primary-init.sql +36 -0
  62. package/carbon/ha/standby-init.sh +74 -0
  63. package/carbon/k8s/LICENSE +99 -0
  64. package/carbon/k8s/README.md +272 -0
  65. package/carbon/k8s/base/app/deployment.yaml +130 -0
  66. package/carbon/k8s/base/app/hpa.yaml +44 -0
  67. package/carbon/k8s/base/app/kustomization.yaml +10 -0
  68. package/carbon/k8s/base/app/network-policy.yaml +124 -0
  69. package/carbon/k8s/base/app/pdb.yaml +14 -0
  70. package/carbon/k8s/base/app/rbac.yaml +36 -0
  71. package/carbon/k8s/base/app/service.yaml +16 -0
  72. package/carbon/k8s/base/backup/cronjob.yaml +120 -0
  73. package/carbon/k8s/base/backup/kustomization.yaml +7 -0
  74. package/carbon/k8s/base/backup/network-policy.yaml +31 -0
  75. package/carbon/k8s/base/backup/rbac.yaml +7 -0
  76. package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
  77. package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
  78. package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
  79. package/carbon/k8s/base/config/configmap.yaml +48 -0
  80. package/carbon/k8s/base/config/kustomization.yaml +8 -0
  81. package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
  82. package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
  83. package/carbon/k8s/base/kustomization.yaml +43 -0
  84. package/carbon/k8s/base/namespace.yaml +7 -0
  85. package/carbon/k8s/base/network-policies.yaml +95 -0
  86. package/carbon/k8s/base/registry/kustomization.yaml +5 -0
  87. package/carbon/k8s/base/registry/local-registry.yaml +193 -0
  88. package/carbon/k8s/base/traefik/certificate.yaml +21 -0
  89. package/carbon/k8s/base/traefik/configmap.yaml +13 -0
  90. package/carbon/k8s/base/traefik/deployment.yaml +165 -0
  91. package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
  92. package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
  93. package/carbon/k8s/base/traefik/middleware.yaml +109 -0
  94. package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
  95. package/carbon/k8s/base/traefik/service.yaml +38 -0
  96. package/carbon/k8s/flux/README.md +49 -0
  97. package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
  98. package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
  99. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
  100. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
  101. package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
  102. package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
  103. package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
  104. package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
  105. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
  106. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
  107. package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
  108. package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
  109. package/carbon/k8s/infra/kustomization.yaml +21 -0
  110. package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
  111. package/carbon/k8s/overlays/local/configmap.yaml +23 -0
  112. package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
  113. package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
  114. package/carbon/k8s/overlays/local/namespace.yaml +8 -0
  115. package/carbon/k8s/overlays/local/secrets.yaml +32 -0
  116. package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
  117. package/carbon/k8s/test-local.sh +318 -0
  118. package/carbon/k8s/values/supabase.values.yaml +133 -0
  119. package/carbon/package.json +154 -0
  120. package/carbon/runtime.Dockerfile +17 -0
  121. package/carbon/scripts/_dev-jwt.mjs +45 -0
  122. package/carbon/scripts/check-rls.ts +53 -0
  123. package/carbon/scripts/dev-init.js +191 -0
  124. package/carbon/scripts/dev.js +191 -0
  125. package/carbon/scripts/docker-down.js +63 -0
  126. package/carbon/scripts/docker-logs.js +59 -0
  127. package/carbon/scripts/docker-up.js +222 -0
  128. package/carbon/scripts/generate-dev-configs.sh +131 -0
  129. package/carbon/scripts/generate-rss.ts +116 -0
  130. package/carbon/scripts/generate-sitemap.ts +102 -0
  131. package/carbon/scripts/k8s-apply.js +71 -0
  132. package/carbon/scripts/k8s-delete.js +75 -0
  133. package/carbon/scripts/lib/manifest.js +176 -0
  134. package/carbon/scripts/secret-scan.mjs +278 -0
  135. package/carbon/scripts/validate-dev-configs.sh +101 -0
  136. package/carbon/src/client/App.tsx +202 -0
  137. package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
  138. package/carbon/src/client/assets/hyperformant-light.svg +29 -0
  139. package/carbon/src/client/assets/logos/aider.svg +1 -0
  140. package/carbon/src/client/assets/logos/antigravity.svg +1 -0
  141. package/carbon/src/client/assets/logos/bolt.svg +1 -0
  142. package/carbon/src/client/assets/logos/claude-code.svg +1 -0
  143. package/carbon/src/client/assets/logos/copilot.svg +1 -0
  144. package/carbon/src/client/assets/logos/cursor.svg +1 -0
  145. package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
  146. package/carbon/src/client/assets/logos/lovable.svg +1 -0
  147. package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
  148. package/carbon/src/client/assets/logos/v0.svg +1 -0
  149. package/carbon/src/client/assets/logos/windsurf.svg +1 -0
  150. package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
  151. package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
  152. package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
  153. package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
  154. package/carbon/src/client/components/AppSidebar.tsx +760 -0
  155. package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
  156. package/carbon/src/client/components/CTAFooter.tsx +59 -0
  157. package/carbon/src/client/components/ComparisonSection.tsx +132 -0
  158. package/carbon/src/client/components/ContentPanel.tsx +66 -0
  159. package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
  160. package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
  161. package/carbon/src/client/components/FAQSection.tsx +76 -0
  162. package/carbon/src/client/components/FileUpload.tsx +210 -0
  163. package/carbon/src/client/components/HeaderActions.tsx +17 -0
  164. package/carbon/src/client/components/Hero.tsx +608 -0
  165. package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
  166. package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
  167. package/carbon/src/client/components/Logo.tsx +87 -0
  168. package/carbon/src/client/components/LogoStrip.tsx +76 -0
  169. package/carbon/src/client/components/MetricsStrip.tsx +48 -0
  170. package/carbon/src/client/components/Nav.tsx +195 -0
  171. package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
  172. package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
  173. package/carbon/src/client/components/PageHeader.tsx +24 -0
  174. package/carbon/src/client/components/PlanGate.tsx +36 -0
  175. package/carbon/src/client/components/PricingSection.tsx +371 -0
  176. package/carbon/src/client/components/SEO.tsx +34 -0
  177. package/carbon/src/client/components/SmoothScroll.tsx +45 -0
  178. package/carbon/src/client/components/TechStackSection.tsx +165 -0
  179. package/carbon/src/client/components/WorkflowSection.tsx +604 -0
  180. package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
  181. package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
  182. package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
  183. package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
  184. package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
  185. package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
  186. package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
  187. package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
  188. package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
  189. package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
  190. package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
  191. package/carbon/src/client/components/effects/index.ts +2 -0
  192. package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
  193. package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
  194. package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
  195. package/carbon/src/client/components/scroll/index.ts +2 -0
  196. package/carbon/src/client/components/ui/accordion.tsx +71 -0
  197. package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
  198. package/carbon/src/client/components/ui/alert.tsx +73 -0
  199. package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
  200. package/carbon/src/client/components/ui/avatar.tsx +91 -0
  201. package/carbon/src/client/components/ui/badge.tsx +50 -0
  202. package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
  203. package/carbon/src/client/components/ui/button-group.tsx +78 -0
  204. package/carbon/src/client/components/ui/button.tsx +120 -0
  205. package/carbon/src/client/components/ui/calendar.tsx +182 -0
  206. package/carbon/src/client/components/ui/card.tsx +85 -0
  207. package/carbon/src/client/components/ui/carousel.tsx +227 -0
  208. package/carbon/src/client/components/ui/chart.tsx +357 -0
  209. package/carbon/src/client/components/ui/checkbox.tsx +27 -0
  210. package/carbon/src/client/components/ui/collapsible.tsx +15 -0
  211. package/carbon/src/client/components/ui/command.tsx +178 -0
  212. package/carbon/src/client/components/ui/context-menu.tsx +233 -0
  213. package/carbon/src/client/components/ui/dialog.tsx +132 -0
  214. package/carbon/src/client/components/ui/drawer.tsx +118 -0
  215. package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
  216. package/carbon/src/client/components/ui/empty.tsx +94 -0
  217. package/carbon/src/client/components/ui/field.tsx +226 -0
  218. package/carbon/src/client/components/ui/hover-card.tsx +44 -0
  219. package/carbon/src/client/components/ui/input-group.tsx +146 -0
  220. package/carbon/src/client/components/ui/input-otp.tsx +83 -0
  221. package/carbon/src/client/components/ui/input.tsx +20 -0
  222. package/carbon/src/client/components/ui/item.tsx +188 -0
  223. package/carbon/src/client/components/ui/kbd.tsx +26 -0
  224. package/carbon/src/client/components/ui/label.tsx +21 -0
  225. package/carbon/src/client/components/ui/menubar.tsx +250 -0
  226. package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
  227. package/carbon/src/client/components/ui/pagination.tsx +102 -0
  228. package/carbon/src/client/components/ui/popover.tsx +75 -0
  229. package/carbon/src/client/components/ui/progress.tsx +66 -0
  230. package/carbon/src/client/components/ui/radio-group.tsx +36 -0
  231. package/carbon/src/client/components/ui/resizable.tsx +46 -0
  232. package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
  233. package/carbon/src/client/components/ui/select.tsx +186 -0
  234. package/carbon/src/client/components/ui/separator.tsx +21 -0
  235. package/carbon/src/client/components/ui/sheet.tsx +124 -0
  236. package/carbon/src/client/components/ui/sidebar.tsx +702 -0
  237. package/carbon/src/client/components/ui/skeleton.tsx +13 -0
  238. package/carbon/src/client/components/ui/slider.tsx +57 -0
  239. package/carbon/src/client/components/ui/sonner.tsx +45 -0
  240. package/carbon/src/client/components/ui/spinner.tsx +15 -0
  241. package/carbon/src/client/components/ui/switch.tsx +30 -0
  242. package/carbon/src/client/components/ui/table.tsx +89 -0
  243. package/carbon/src/client/components/ui/tabs.tsx +73 -0
  244. package/carbon/src/client/components/ui/textarea.tsx +18 -0
  245. package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
  246. package/carbon/src/client/components/ui/toggle.tsx +44 -0
  247. package/carbon/src/client/components/ui/tooltip.tsx +56 -0
  248. package/carbon/src/client/hooks/api/index.ts +14 -0
  249. package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
  250. package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
  251. package/carbon/src/client/hooks/use-mobile.ts +21 -0
  252. package/carbon/src/client/hooks/useMousePosition.ts +91 -0
  253. package/carbon/src/client/hooks/useNotifications.tsx +124 -0
  254. package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
  255. package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
  256. package/carbon/src/client/hooks/usePackageManager.ts +69 -0
  257. package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
  258. package/carbon/src/client/hooks/useRunningServices.ts +114 -0
  259. package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
  260. package/carbon/src/client/index.css +467 -0
  261. package/carbon/src/client/index.html +56 -0
  262. package/carbon/src/client/lib/admin-services.ts +151 -0
  263. package/carbon/src/client/lib/api.ts +32 -0
  264. package/carbon/src/client/lib/blog.ts +35 -0
  265. package/carbon/src/client/lib/changelog.ts +33 -0
  266. package/carbon/src/client/lib/docs-search.ts +101 -0
  267. package/carbon/src/client/lib/docs.ts +37 -0
  268. package/carbon/src/client/lib/i18n.ts +32 -0
  269. package/carbon/src/client/lib/supabase.ts +72 -0
  270. package/carbon/src/client/lib/tailwind-colors.ts +357 -0
  271. package/carbon/src/client/lib/theme.ts +117 -0
  272. package/carbon/src/client/lib/utils.ts +22 -0
  273. package/carbon/src/client/locales/de.json +529 -0
  274. package/carbon/src/client/locales/en.json +461 -0
  275. package/carbon/src/client/locales/es.json +529 -0
  276. package/carbon/src/client/locales/fr.json +529 -0
  277. package/carbon/src/client/locales/pt.json +529 -0
  278. package/carbon/src/client/main.tsx +56 -0
  279. package/carbon/src/client/mdx.d.ts +13 -0
  280. package/carbon/src/client/pages/ApiDocs.tsx +76 -0
  281. package/carbon/src/client/pages/AuthCallback.tsx +34 -0
  282. package/carbon/src/client/pages/Blog.tsx +167 -0
  283. package/carbon/src/client/pages/Changelog.tsx +171 -0
  284. package/carbon/src/client/pages/Charts.tsx +388 -0
  285. package/carbon/src/client/pages/Checkout.tsx +227 -0
  286. package/carbon/src/client/pages/Contact.tsx +174 -0
  287. package/carbon/src/client/pages/Dashboard.tsx +368 -0
  288. package/carbon/src/client/pages/Docs.tsx +372 -0
  289. package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
  290. package/carbon/src/client/pages/Home.tsx +187 -0
  291. package/carbon/src/client/pages/Legal.tsx +100 -0
  292. package/carbon/src/client/pages/Login.tsx +408 -0
  293. package/carbon/src/client/pages/MFAVerify.tsx +156 -0
  294. package/carbon/src/client/pages/NotFound.tsx +21 -0
  295. package/carbon/src/client/pages/Onboarding.tsx +246 -0
  296. package/carbon/src/client/pages/ResetPassword.tsx +200 -0
  297. package/carbon/src/client/pages/UIComponents.tsx +390 -0
  298. package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
  299. package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
  300. package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
  301. package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
  302. package/carbon/src/client/pages/admin/Logs.tsx +18 -0
  303. package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
  304. package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
  305. package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
  306. package/carbon/src/client/pages/admin/Settings.tsx +314 -0
  307. package/carbon/src/client/pages/admin/Theme.tsx +465 -0
  308. package/carbon/src/client/pages/admin/Users.tsx +365 -0
  309. package/carbon/src/client/pages/api-docs.css +156 -0
  310. package/carbon/src/client/pages/organizations/Details.tsx +200 -0
  311. package/carbon/src/client/pages/organizations/Members.tsx +402 -0
  312. package/carbon/src/client/pages/settings/Billing.tsx +473 -0
  313. package/carbon/src/client/pages/settings/Profile.tsx +160 -0
  314. package/carbon/src/client/pages/settings/Security.tsx +341 -0
  315. package/carbon/src/client/public/favicon.svg +19 -0
  316. package/carbon/src/client/public/robots.txt +8 -0
  317. package/carbon/src/server/billing/index.ts +104 -0
  318. package/carbon/src/server/billing/provider.ts +81 -0
  319. package/carbon/src/server/billing/providers/paddle.ts +314 -0
  320. package/carbon/src/server/billing/providers/polar.ts +325 -0
  321. package/carbon/src/server/billing/providers/stripe.ts +233 -0
  322. package/carbon/src/server/emails/templates.ts +116 -0
  323. package/carbon/src/server/index.ts +554 -0
  324. package/carbon/src/server/lib/auth.ts +6 -0
  325. package/carbon/src/server/lib/email.ts +64 -0
  326. package/carbon/src/server/lib/env.ts +112 -0
  327. package/carbon/src/server/lib/errors.ts +21 -0
  328. package/carbon/src/server/lib/logger.ts +17 -0
  329. package/carbon/src/server/lib/rate-limiter.ts +288 -0
  330. package/carbon/src/server/lib/request.ts +34 -0
  331. package/carbon/src/server/lib/stripe.ts +42 -0
  332. package/carbon/src/server/lib/supabase.ts +65 -0
  333. package/carbon/src/server/middleware/requirePlan.ts +80 -0
  334. package/carbon/src/server/routes/_internal/services-status.ts +958 -0
  335. package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
  336. package/carbon/src/server/routes/health.ts +48 -0
  337. package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
  338. package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
  339. package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
  340. package/carbon/src/server/routes/v1/auth.ts +390 -0
  341. package/carbon/src/server/routes/v1/billing.ts +718 -0
  342. package/carbon/src/server/routes/v1/contact.ts +93 -0
  343. package/carbon/src/server/routes/v1/index.ts +1333 -0
  344. package/carbon/src/server/routes/v1/newsletter.ts +181 -0
  345. package/carbon/src/server/routes/v1/performance.ts +157 -0
  346. package/carbon/src/server/routes/v1/stats.ts +170 -0
  347. package/carbon/src/server/routes/v1/theme.ts +106 -0
  348. package/carbon/src/server/routes/webhooks/billing.ts +376 -0
  349. package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
  350. package/carbon/src/server/types.ts +11 -0
  351. package/carbon/src/shared/pricing.ts +155 -0
  352. package/carbon/src/shared/types.ts +338 -0
  353. package/carbon/supabase/migrations/00001_init.sql +717 -0
  354. package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
  355. package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
  356. package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
  357. package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
  358. package/carbon/supabase/seed.sql +16 -0
  359. package/carbon/tests/_helpers/app.ts +45 -0
  360. package/carbon/tests/_helpers/env.ts +37 -0
  361. package/carbon/tests/_helpers/factories.ts +69 -0
  362. package/carbon/tests/_helpers/jwt.ts +23 -0
  363. package/carbon/tests/_helpers/setup-integration.ts +20 -0
  364. package/carbon/tests/_helpers/setup-rtl.ts +12 -0
  365. package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
  366. package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
  367. package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
  368. package/carbon/tests/integration/server/routes/health.test.ts +61 -0
  369. package/carbon/tests/structural/i18n-parity.test.ts +42 -0
  370. package/carbon/tests/unit/client/utils.test.ts +49 -0
  371. package/carbon/tests/unit/shared/pricing.test.ts +93 -0
  372. package/carbon/tsconfig.json +27 -0
  373. package/carbon/tsconfig.server.json +27 -0
  374. package/carbon/tsconfig.test.json +9 -0
  375. package/carbon/vite.config.ts +110 -0
  376. package/carbon/vitest.config.ts +74 -0
  377. package/carbon/volumes/db/jwt.sql +57 -0
  378. package/carbon/volumes/db/metabase-init.sh +29 -0
  379. package/carbon/volumes/db/n8n-init.sh +25 -0
  380. package/carbon/volumes/db/realtime.sql +33 -0
  381. package/carbon/volumes/db/roles.sql +93 -0
  382. package/carbon/volumes/db/set-passwords.sh +12 -0
  383. package/carbon/volumes/db/super-admin.dev.sql +113 -0
  384. package/carbon/volumes/db/super-admin.generated.sql +113 -0
  385. package/carbon/volumes/db/super-admin.sql +114 -0
  386. package/carbon/volumes/grafana/dashboards/logs.json +179 -0
  387. package/carbon/volumes/grafana/dashboards/overview.json +523 -0
  388. package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
  389. package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
  390. package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
  391. package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
  392. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
  393. package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
  394. package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
  395. package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
  396. package/carbon/volumes/kong/kong.yml +208 -0
  397. package/carbon/volumes/loki/loki-config.yml +58 -0
  398. package/carbon/volumes/n8n/hooks.js +131 -0
  399. package/carbon/volumes/n8n/scripts/setup.sh +66 -0
  400. package/carbon/volumes/prometheus/prometheus.yml +43 -0
  401. package/carbon/volumes/promtail/promtail-config.yml +64 -0
  402. package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
  403. package/carbon/volumes/traefik/middlewares.yml +95 -0
  404. package/carbon/volumes/traefik/vite-dev.yml +20 -0
  405. package/package.json +95 -0
  406. package/src/access.js +354 -0
  407. package/src/activate.js +187 -0
  408. package/src/add.js +718 -0
  409. package/src/backup.js +786 -0
  410. package/src/cli.js +350 -0
  411. package/src/configure.js +967 -0
  412. package/src/console.js +155 -0
  413. package/src/create.js +1499 -0
  414. package/src/deploy.js +311 -0
  415. package/src/destroy.js +2033 -0
  416. package/src/diagnose.js +735 -0
  417. package/src/down.js +80 -0
  418. package/src/failover.js +1032 -0
  419. package/src/lib/backup-s3.js +179 -0
  420. package/src/lib/build.js +33 -0
  421. package/src/lib/checksum.js +28 -0
  422. package/src/lib/ci-setup.js +666 -0
  423. package/src/lib/cli/help.js +129 -0
  424. package/src/lib/cli/parse-flags.js +160 -0
  425. package/src/lib/cli/select-action.js +65 -0
  426. package/src/lib/cli/select-environment.js +108 -0
  427. package/src/lib/cli/tty-guard.js +75 -0
  428. package/src/lib/cloudflare.js +447 -0
  429. package/src/lib/colors.js +52 -0
  430. package/src/lib/command.js +361 -0
  431. package/src/lib/config.js +359 -0
  432. package/src/lib/cost.js +103 -0
  433. package/src/lib/deploy/bundle.js +231 -0
  434. package/src/lib/deploy/compose/acme-verify.js +121 -0
  435. package/src/lib/deploy/compose/build-args.js +78 -0
  436. package/src/lib/deploy/compose/ha.js +1874 -0
  437. package/src/lib/deploy/compose/index.js +1294 -0
  438. package/src/lib/deploy/compose/reconcile.js +74 -0
  439. package/src/lib/deploy/github.js +382 -0
  440. package/src/lib/deploy/image.js +191 -0
  441. package/src/lib/deploy/index.js +119 -0
  442. package/src/lib/deploy/k8s/LICENSE +99 -0
  443. package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
  444. package/src/lib/deploy/k8s/ha/index.js +1000 -0
  445. package/src/lib/deploy/k8s/index.js +62 -0
  446. package/src/lib/deploy/k8s/k3s.js +2515 -0
  447. package/src/lib/deploy/orchestrator.js +1401 -0
  448. package/src/lib/deploy/prompts.js +545 -0
  449. package/src/lib/deploy/remote-build.js +130 -0
  450. package/src/lib/deploy/state.js +120 -0
  451. package/src/lib/deploy/utils.js +328 -0
  452. package/src/lib/deploy-logger.js +93 -0
  453. package/src/lib/dns-propagation.js +48 -0
  454. package/src/lib/environment.js +58 -0
  455. package/src/lib/fetch-retry.js +103 -0
  456. package/src/lib/github-environments.js +335 -0
  457. package/src/lib/hetzner-dns.js +377 -0
  458. package/src/lib/hetzner-guided-setup.js +275 -0
  459. package/src/lib/host-keys.js +37 -0
  460. package/src/lib/iac/cloud-init.js +52 -0
  461. package/src/lib/iac/index.js +325 -0
  462. package/src/lib/iac/programs/hetzner-compose.js +130 -0
  463. package/src/lib/iac/programs/hetzner-k8s.js +320 -0
  464. package/src/lib/kubectl.js +81 -0
  465. package/src/lib/licensing/index.js +259 -0
  466. package/src/lib/licensing/tiers.js +181 -0
  467. package/src/lib/licensing/validator.js +171 -0
  468. package/src/lib/merge-package-json.js +90 -0
  469. package/src/lib/operator-ip.js +381 -0
  470. package/src/lib/package-manager.js +111 -0
  471. package/src/lib/perf.js +71 -0
  472. package/src/lib/project-guard.js +39 -0
  473. package/src/lib/project.js +334 -0
  474. package/src/lib/providers/base.js +276 -0
  475. package/src/lib/providers/hetzner-s3.js +656 -0
  476. package/src/lib/providers/hetzner.js +755 -0
  477. package/src/lib/providers/index.js +164 -0
  478. package/src/lib/s3.js +510 -0
  479. package/src/lib/secret-scan.js +583 -0
  480. package/src/lib/secrets.js +63 -0
  481. package/src/lib/server-types.js +195 -0
  482. package/src/lib/shell.js +91 -0
  483. package/src/lib/ssh.js +241 -0
  484. package/src/lib/tracker.js +242 -0
  485. package/src/lib/upgrade-policy.js +170 -0
  486. package/src/lib/validators.js +105 -0
  487. package/src/lib/version.js +5 -0
  488. package/src/remove.js +292 -0
  489. package/src/reset.js +97 -0
  490. package/src/restore.js +871 -0
  491. package/src/scale.js +1734 -0
  492. package/src/shell.js +222 -0
  493. package/src/status.js +981 -0
  494. package/src/up.js +264 -0
  495. package/src/upgrade.js +721 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * Command Tracker — step-level timing and persistent log files for CLI commands
3
+ *
4
+ * Usage:
5
+ * const tracker = createTracker('deploy', { environment: 'prod' });
6
+ * const s = tracker.spinner();
7
+ * s.start('Provisioning cluster');
8
+ * s.stop('Cluster provisioned');
9
+ * const { formatted } = tracker.finish();
10
+ */
11
+
12
+ import { createWriteStream, existsSync, mkdirSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import * as p from '@clack/prompts';
15
+
16
+ /**
17
+ * Format milliseconds into a human-readable duration string.
18
+ * @param {number} ms
19
+ * @returns {string} e.g. "3s", "2m 14s"
20
+ */
21
+ function formatDuration(ms) {
22
+ const totalSecs = Math.round(ms / 1000);
23
+ const mins = Math.floor(totalSecs / 60);
24
+ const secs = totalSecs % 60;
25
+ return mins > 0 ? `${mins}m ${secs}s` : `${secs}s`;
26
+ }
27
+
28
+ /**
29
+ * Format a timestamp for log lines (HH:MM:SS)
30
+ * @param {Date} date
31
+ * @returns {string}
32
+ */
33
+ function formatTime(date) {
34
+ return date.toISOString().slice(11, 19);
35
+ }
36
+
37
+ /**
38
+ * Right-pad a duration string to a fixed width for aligned output.
39
+ * @param {string} str
40
+ * @param {number} width
41
+ * @returns {string}
42
+ */
43
+ function padDuration(str, width = 8) {
44
+ return str.padStart(width);
45
+ }
46
+
47
+ /**
48
+ * Create a CommandTracker for a CLI command.
49
+ *
50
+ * @param {string} command - Command name (e.g. 'deploy', 'destroy')
51
+ * @param {object} [meta] - Key/value metadata written to the log header
52
+ * @returns {CommandTracker}
53
+ */
54
+ export function createTracker(command, meta = {}) {
55
+ return new CommandTracker(command, meta);
56
+ }
57
+
58
+ /**
59
+ * Create a prefixed tracker for parallel operations (e.g., HA deploys).
60
+ * Instead of real @clack spinners (which corrupt when two run concurrently),
61
+ * this uses p.log.info with a region/label prefix for each step.
62
+ *
63
+ * Duck-type compatible with CommandTracker — can be passed as options.tracker.
64
+ *
65
+ * @param {string} label - Prefix label (e.g., "fsn1", "nbg1")
66
+ * @param {object} [opts] - Options
67
+ * @param {CommandTracker} [opts.parent] - Parent tracker for log file writes
68
+ * @returns {object} Tracker-compatible object
69
+ */
70
+ export function createPrefixedTracker(label, opts = {}) {
71
+ const parent = opts.parent;
72
+ const dim = (s) => `\x1b[2m${s}\x1b[22m`;
73
+ const prefix = dim(`[${label}]`);
74
+
75
+ return {
76
+ spinner() {
77
+ let stepStart = null;
78
+ let stepLabel = null;
79
+
80
+ return {
81
+ start(msg) {
82
+ stepStart = performance.now();
83
+ stepLabel = msg;
84
+ p.log.step(`${prefix} ${msg}`);
85
+ parent?.log?.(`[${label}] START ${msg}`);
86
+ },
87
+ stop(msg) {
88
+ const elapsed = stepStart ? formatDuration(performance.now() - stepStart) : '';
89
+ const display = msg || stepLabel;
90
+ p.log.info(`${prefix} ${display} ${dim(`(${elapsed})`)}`);
91
+ parent?.log?.(`[${label}] DONE ${display} (${elapsed})`);
92
+ stepStart = null;
93
+ stepLabel = null;
94
+ },
95
+ message(msg) {
96
+ p.log.info(`${prefix} ${msg}`);
97
+ parent?.log?.(`[${label}] UPDATE ${msg}`);
98
+ },
99
+ };
100
+ },
101
+ log(msg) {
102
+ parent?.log?.(`[${label}] ${msg}`);
103
+ },
104
+ };
105
+ }
106
+
107
+ class CommandTracker {
108
+ constructor(command, meta) {
109
+ this.command = command;
110
+ this.meta = meta;
111
+ this.startTime = performance.now();
112
+ this.steps = []; // { label, durationMs }
113
+ this._currentStep = null; // { label, startTime }
114
+ this._stream = null;
115
+
116
+ // Open log file
117
+ const logsDir = join(process.cwd(), '.vibecarbon', 'logs');
118
+ try {
119
+ if (!existsSync(logsDir)) {
120
+ mkdirSync(logsDir, { recursive: true });
121
+ }
122
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
123
+ const envSuffix = meta.environment ? `-${meta.environment}` : '';
124
+ const filename = `${command}${envSuffix}-${timestamp}.log`;
125
+ this.logPath = join(logsDir, filename);
126
+ this._stream = createWriteStream(this.logPath, { flags: 'a' });
127
+
128
+ // Write header
129
+ this._write(`=== vibecarbon ${command} ===`);
130
+ this._write(`Started: ${new Date().toISOString()}`);
131
+ for (const [key, value] of Object.entries(meta)) {
132
+ if (value != null) {
133
+ this._write(` ${key}: ${value}`);
134
+ }
135
+ }
136
+ this._write('');
137
+ } catch {
138
+ // If we can't create the log file (e.g. no .vibecarbon dir), continue without logging
139
+ this.logPath = null;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Create a spinner wrapper that records timing and writes to the log.
145
+ * Duck-type compatible with p.spinner().
146
+ * @returns {{ start(msg: string): void, stop(msg: string, code?: number): void, message(msg: string): void }}
147
+ */
148
+ spinner() {
149
+ let realSpinner = p.spinner();
150
+ const tracker = this;
151
+
152
+ return {
153
+ start(msg) {
154
+ // If a previous step wasn't stopped, record it as-is
155
+ if (tracker._currentStep) {
156
+ const elapsed = performance.now() - tracker._currentStep.startTime;
157
+ tracker.steps.push({ label: tracker._currentStep.label, durationMs: elapsed });
158
+ }
159
+
160
+ tracker._currentStep = { label: msg, startTime: performance.now() };
161
+ tracker._write(`[${formatTime(new Date())}] START ${msg}`);
162
+ // Create a fresh spinner — clack spinners don't cleanly restart after stop()
163
+ realSpinner = p.spinner();
164
+ realSpinner.start(msg);
165
+ },
166
+
167
+ stop(msg, code) {
168
+ if (tracker._currentStep) {
169
+ const elapsed = performance.now() - tracker._currentStep.startTime;
170
+ tracker.steps.push({ label: msg || tracker._currentStep.label, durationMs: elapsed });
171
+ tracker._write(
172
+ `[${formatTime(new Date())}] DONE ${msg || tracker._currentStep.label} (${formatDuration(elapsed)})`,
173
+ );
174
+ tracker._currentStep = null;
175
+ }
176
+ realSpinner.stop(msg, code);
177
+ },
178
+
179
+ message(msg) {
180
+ tracker._write(`[${formatTime(new Date())}] UPDATE ${msg}`);
181
+ realSpinner.message(msg);
182
+ },
183
+ };
184
+ }
185
+
186
+ /**
187
+ * Write an arbitrary line to the log file.
188
+ * @param {string} msg
189
+ */
190
+ log(msg) {
191
+ this._write(`[${formatTime(new Date())}] LOG ${msg}`);
192
+ }
193
+
194
+ /**
195
+ * Finalize tracking: write summary to log, print timing table to console.
196
+ * @returns {{ elapsed: number, formatted: string, logPath: string | null }}
197
+ */
198
+ finish() {
199
+ const totalMs = performance.now() - this.startTime;
200
+ const formatted = formatDuration(totalMs);
201
+
202
+ // Write step summary to log
203
+ if (this.steps.length > 0) {
204
+ this._write('');
205
+ this._write('=== Step Summary ===');
206
+ for (const step of this.steps) {
207
+ this._write(` ${padDuration(formatDuration(step.durationMs))} ${step.label}`);
208
+ }
209
+ this._write(` ${padDuration(formatted)} TOTAL`);
210
+ }
211
+ this._write('');
212
+ this._write(`Finished: ${new Date().toISOString()}`);
213
+
214
+ // Close stream
215
+ if (this._stream) {
216
+ this._stream.end();
217
+ this._stream = null;
218
+ }
219
+
220
+ // Print timing stats table to console (only if >1 step)
221
+ if (this.steps.length > 1) {
222
+ const lines = this.steps.map(
223
+ (step) => `${padDuration(formatDuration(step.durationMs))} ${step.label}`,
224
+ );
225
+ lines.push(`${padDuration(formatted)} Total`);
226
+ p.note(lines.join('\n'), 'Stats');
227
+ }
228
+
229
+ return { elapsed: totalMs, formatted, logPath: this.logPath };
230
+ }
231
+
232
+ /**
233
+ * Write a line to the log file stream.
234
+ * @param {string} line
235
+ * @private
236
+ */
237
+ _write(line) {
238
+ if (this._stream) {
239
+ this._stream.write(`${line}\n`);
240
+ }
241
+ }
242
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Upgrade policy — classifies template files into safe / merge / never categories
3
+ */
4
+
5
+ import { readdirSync } from 'node:fs';
6
+ import { join, relative } from 'node:path';
7
+
8
+ // Files that can be auto-replaced when unmodified by the user
9
+ const SAFE_PATTERNS = [
10
+ 'Dockerfile',
11
+ 'docker-entrypoint.sh',
12
+ '.dockerignore',
13
+ 'docker-compose.prod.yml',
14
+ 'docker-compose.observability.yml',
15
+ 'docker-compose.observability.prod.yml',
16
+ 'docker-compose.observability.override.yml',
17
+ 'docker-compose.metabase.yml',
18
+ 'docker-compose.metabase.override.yml',
19
+ 'docker-compose.n8n.yml',
20
+ 'docker-compose.n8n.override.yml',
21
+ 'docker-compose.dev-init.yml',
22
+ 'k8s/base/**',
23
+ 'k8s/infra/**',
24
+ 'k8s/flux/**',
25
+ 'cloud-init/**',
26
+ '.github/workflows/**',
27
+ 'biome.json',
28
+ 'tsconfig.json',
29
+ 'tsconfig.server.json',
30
+ 'components.json',
31
+ 'backup/**',
32
+ 'scripts/**',
33
+ 'volumes/kong/**',
34
+ 'volumes/grafana/**',
35
+ 'volumes/prometheus/**',
36
+ 'volumes/loki/**',
37
+ 'volumes/promtail/**',
38
+ 'volumes/traefik/**',
39
+ 'AGENTS.md',
40
+ 'CLAUDE.md',
41
+ 'PRODUCTION.md',
42
+ 'DEVELOPMENT.md',
43
+ '.windsurfrules',
44
+ '.github/copilot-instructions.md',
45
+ '.cursor/rules/**',
46
+ '.claude/settings.json',
47
+ '.claude/hooks/**',
48
+ '.claude/agents/**',
49
+ ];
50
+
51
+ // Files that always require user review
52
+ const MERGE_PATTERNS = [
53
+ 'docker-compose.yml',
54
+ 'docker-compose.override.yml',
55
+ 'package.json',
56
+ 'vite.config.ts',
57
+ 'k8s/overlays/**',
58
+ '.gitignore',
59
+ ];
60
+
61
+ // Files that should never be touched
62
+ const NEVER_PATTERNS = [
63
+ 'src/**',
64
+ 'content/**',
65
+ 'supabase/**',
66
+ '.env',
67
+ '.env.local',
68
+ '.env.example',
69
+ 'README.md',
70
+ 'volumes/db/**',
71
+ 'node_modules/**',
72
+ 'dist/**',
73
+ 'pnpm-lock.yaml',
74
+ 'package-lock.json',
75
+ 'bun.lock',
76
+ 'bun.lockb',
77
+ '.git/**',
78
+ '.claude/agent-memory/**',
79
+ ];
80
+
81
+ /**
82
+ * Convert a simple glob pattern to a RegExp.
83
+ * Supports `**` (any path depth) and `*` (any filename chars).
84
+ *
85
+ * @param {string} pattern
86
+ * @returns {RegExp}
87
+ */
88
+ function globToRegex(pattern) {
89
+ const re = pattern
90
+ // Escape regex-special chars except * and /
91
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
92
+ // ** matches any number of path segments (including zero)
93
+ .replace(/\*\*/g, '{{GLOBSTAR}}')
94
+ // * matches anything except path separator
95
+ .replace(/\*/g, '[^/]*')
96
+ .replace(/\{\{GLOBSTAR\}\}/g, '.*');
97
+ return new RegExp(`^${re}$`);
98
+ }
99
+
100
+ /**
101
+ * Test if a path matches any pattern in a list
102
+ *
103
+ * @param {string} filePath
104
+ * @param {string[]} patterns
105
+ * @returns {boolean}
106
+ */
107
+ function matchesAny(filePath, patterns) {
108
+ return patterns.some((p) => globToRegex(p).test(filePath));
109
+ }
110
+
111
+ /**
112
+ * Classify a file path into an upgrade policy category
113
+ *
114
+ * @param {string} relativePath - Path relative to project root
115
+ * @returns {'safe' | 'merge' | 'never'}
116
+ */
117
+ export function getFilePolicy(relativePath) {
118
+ if (matchesAny(relativePath, NEVER_PATTERNS)) return 'never';
119
+ if (matchesAny(relativePath, MERGE_PATTERNS)) return 'merge';
120
+ if (matchesAny(relativePath, SAFE_PATTERNS)) return 'safe';
121
+ // Default: files not explicitly classified are never touched
122
+ return 'never';
123
+ }
124
+
125
+ /**
126
+ * Walk a directory tree and return all file paths relative to rootDir
127
+ *
128
+ * @param {string} rootDir - Root directory to walk
129
+ * @returns {string[]} - Array of relative file paths
130
+ */
131
+ function walkDir(rootDir) {
132
+ const results = [];
133
+
134
+ function walk(dir) {
135
+ let entries;
136
+ try {
137
+ entries = readdirSync(dir, { withFileTypes: true });
138
+ } catch {
139
+ return;
140
+ }
141
+ for (const entry of entries) {
142
+ const fullPath = join(dir, entry.name);
143
+ if (entry.isDirectory()) {
144
+ // Skip node_modules and .git in template
145
+ if (entry.name === 'node_modules' || entry.name === '.git') continue;
146
+ walk(fullPath);
147
+ } else {
148
+ results.push(relative(rootDir, fullPath));
149
+ }
150
+ }
151
+ }
152
+
153
+ walk(rootDir);
154
+ return results;
155
+ }
156
+
157
+ /**
158
+ * Get all upgradeable files from the template directory
159
+ * Returns files that are classified as 'safe' or 'merge'
160
+ *
161
+ * @param {string} templateDir - Path to the carbon/ template directory
162
+ * @returns {string[]} - Array of relative file paths
163
+ */
164
+ export function getUpgradeableFiles(templateDir) {
165
+ const allFiles = walkDir(templateDir);
166
+ return allFiles.filter((f) => {
167
+ const policy = getFilePolicy(f);
168
+ return policy === 'safe' || policy === 'merge';
169
+ });
170
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Input validators shared across create, cli, restore, backup, configure.
3
+ *
4
+ * Every validator returns `undefined` on success and a human-readable error
5
+ * string on failure. This matches @clack/prompts' `validate` contract and
6
+ * lets the same function work for both interactive prompts and non-interactive
7
+ * CLI argument validation.
8
+ */
9
+
10
+ import { basename } from 'node:path';
11
+
12
+ const RESERVED_PROJECT_NAMES = new Set(['node_modules', 'dist', 'build', 'coverage', '__tests__']);
13
+
14
+ const PROJECT_NAME_RE = /^[a-z]([a-z0-9-]*[a-z0-9])?$/;
15
+
16
+ /**
17
+ * Validate a project name. Must be a bare basename, lowercase DNS-safe,
18
+ * not reserved, max 63 characters.
19
+ * Returns undefined on success, error string on failure.
20
+ */
21
+ export function validateProjectName(name) {
22
+ if (!name) return 'Project name is required';
23
+ if (name !== basename(name) || name.startsWith('.')) {
24
+ return 'Project name must be a basename (no slashes, no ../, no dot-names)';
25
+ }
26
+ if (name.length > 63) return 'Project name must be 63 characters or fewer';
27
+ if (RESERVED_PROJECT_NAMES.has(name.toLowerCase())) {
28
+ return `"${name}" is a reserved name`;
29
+ }
30
+ if (!PROJECT_NAME_RE.test(name)) {
31
+ return 'Project name must be lowercase, start with a letter, and contain only letters, digits, and hyphens';
32
+ }
33
+ return undefined;
34
+ }
35
+
36
+ const EMAIL_RE = /^[A-Za-z0-9._+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/;
37
+
38
+ /**
39
+ * Validate an admin email address.
40
+ * Returns undefined on success, error string on failure.
41
+ */
42
+ export function validateAdminEmail(email) {
43
+ if (!email) return 'Email is required';
44
+ if (!EMAIL_RE.test(email)) {
45
+ return 'Email must be a simple address (letters, digits, . _ + -, single @)';
46
+ }
47
+ return undefined;
48
+ }
49
+
50
+ const PRINTABLE_ASCII_RE = /^[\x20-\x7e]+$/;
51
+ // Block chars that break single-quote escaping or cause expansion if quoting is
52
+ // accidentally skipped. Other shell-special chars (| ; & etc.) are safe inside
53
+ // shEscape's single-quoted output, so they're intentionally not blocked here.
54
+ const SHELL_METACHARS_RE = /['"`$\\]/;
55
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: intentional — rejecting control chars
56
+ const CONTROL_CHARS_RE = /[\u0000-\u001f\u007f]/;
57
+
58
+ /**
59
+ * Validate an admin password. Must be printable ASCII, no shell metacharacters.
60
+ * Returns undefined on success, error string on failure.
61
+ */
62
+ export function validateAdminPassword(password) {
63
+ if (!password || password.length < 8) return 'Password must be at least 8 characters';
64
+ if (CONTROL_CHARS_RE.test(password)) return 'Password must not contain control characters';
65
+ if (SHELL_METACHARS_RE.test(password)) {
66
+ return `Password must not contain ', ", backtick, $, or \\`;
67
+ }
68
+ if (!PRINTABLE_ASCII_RE.test(password)) return 'Password must be printable ASCII only';
69
+ return undefined;
70
+ }
71
+
72
+ const DOMAIN_LABEL_RE = /^[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/;
73
+
74
+ /**
75
+ * Validate a domain name. Must be a valid DNS hostname, max 253 characters.
76
+ * Returns undefined on success, error string on failure.
77
+ */
78
+ export function validateDomain(domain) {
79
+ if (!domain) return 'Domain is required';
80
+ if (domain.length > 253) return 'Domain must be 253 characters or fewer';
81
+ if (domain.startsWith('.') || domain.endsWith('.')) {
82
+ return 'Domain must not start or end with a dot';
83
+ }
84
+ const labels = domain.split('.');
85
+ if (labels.length < 2) return 'Domain must contain at least one dot';
86
+ for (const label of labels) {
87
+ if (!DOMAIN_LABEL_RE.test(label)) return `"${label}" is not a valid domain label`;
88
+ }
89
+ return undefined;
90
+ }
91
+
92
+ const BACKUP_FILENAME_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*\.(tar\.gz|sql\.gz|tar|sql)$/;
93
+
94
+ /**
95
+ * Validate a backup filename. Must be a safe basename with a recognised extension.
96
+ * Returns undefined on success, error string on failure.
97
+ */
98
+ export function validateBackupFilename(name) {
99
+ if (!name) return 'Backup filename is required';
100
+ if (name !== basename(name)) return 'Backup filename must be a basename (no paths)';
101
+ if (!BACKUP_FILENAME_RE.test(name)) {
102
+ return 'Backup filename must match <safe-name>.(tar|sql)[.gz]';
103
+ }
104
+ return undefined;
105
+ }
@@ -0,0 +1,5 @@
1
+ import { createRequire } from 'node:module';
2
+
3
+ const require = createRequire(import.meta.url);
4
+ const { version } = require('../../package.json');
5
+ export const VERSION = version;