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.
- package/LICENSE +663 -0
- package/README.md +188 -0
- package/carbon/.claude/hooks/task-completed-gate.sh +19 -0
- package/carbon/.claude/hooks/teammate-idle-gate.sh +29 -0
- package/carbon/.claude/settings.json +29 -0
- package/carbon/.cursor/rules/vibecarbon.mdc +6 -0
- package/carbon/.dockerignore +57 -0
- package/carbon/.env.example +219 -0
- package/carbon/.github/copilot-instructions.md +3 -0
- package/carbon/.github/workflows/deploy.yml +346 -0
- package/carbon/.github/workflows/vibecarbon-build.yml +81 -0
- package/carbon/.windsurfrules +74 -0
- package/carbon/AGENTS.md +422 -0
- package/carbon/CLAUDE.md +3 -0
- package/carbon/DEVELOPMENT.md +187 -0
- package/carbon/Dockerfile +98 -0
- package/carbon/LICENSE +21 -0
- package/carbon/PRODUCTION.md +364 -0
- package/carbon/README.md +193 -0
- package/carbon/TESTING.md +138 -0
- package/carbon/backup/Dockerfile +75 -0
- package/carbon/backup/backup.sh +95 -0
- package/carbon/backup/compose-backup.sh +140 -0
- package/carbon/biome.json +83 -0
- package/carbon/cloud-init/docker-ce-setup.yaml +55 -0
- package/carbon/cloud-init/k3s/master-init.sh +215 -0
- package/carbon/cloud-init/k3s/supabase-init.sh +167 -0
- package/carbon/cloud-init/k3s/worker-init.sh +147 -0
- package/carbon/components.json +24 -0
- package/carbon/content/blog/authentication-guide.mdx +34 -0
- package/carbon/content/blog/getting-started.mdx +41 -0
- package/carbon/content/changelog/v0-1-0.mdx +40 -0
- package/carbon/content/docs/analytics.mdx +90 -0
- package/carbon/content/docs/authentication.mdx +231 -0
- package/carbon/content/docs/background-jobs.mdx +116 -0
- package/carbon/content/docs/cli.mdx +630 -0
- package/carbon/content/docs/database.mdx +236 -0
- package/carbon/content/docs/deployment.mdx +227 -0
- package/carbon/content/docs/development.mdx +238 -0
- package/carbon/content/docs/environments.mdx +84 -0
- package/carbon/content/docs/getting-started.mdx +112 -0
- package/carbon/content/docs/legal/privacy-policy.mdx +89 -0
- package/carbon/content/docs/legal/terms-of-service.mdx +99 -0
- package/carbon/content/docs/optional-services.mdx +160 -0
- package/carbon/db/Dockerfile +23 -0
- package/carbon/docker-compose.dev-init.yml +5 -0
- package/carbon/docker-compose.metabase.override.yml +14 -0
- package/carbon/docker-compose.metabase.prod.yml +28 -0
- package/carbon/docker-compose.metabase.yml +84 -0
- package/carbon/docker-compose.n8n.override.yml +14 -0
- package/carbon/docker-compose.n8n.prod.yml +31 -0
- package/carbon/docker-compose.n8n.yml +97 -0
- package/carbon/docker-compose.observability.override.yml +18 -0
- package/carbon/docker-compose.observability.prod.yml +28 -0
- package/carbon/docker-compose.observability.yml +125 -0
- package/carbon/docker-compose.override.yml +28 -0
- package/carbon/docker-compose.prod.yml +294 -0
- package/carbon/docker-compose.yml +508 -0
- package/carbon/docker-entrypoint.sh +12 -0
- package/carbon/ha/activate-standby.sh +52 -0
- package/carbon/ha/primary-init.sql +36 -0
- package/carbon/ha/standby-init.sh +74 -0
- package/carbon/k8s/LICENSE +99 -0
- package/carbon/k8s/README.md +272 -0
- package/carbon/k8s/base/app/deployment.yaml +130 -0
- package/carbon/k8s/base/app/hpa.yaml +44 -0
- package/carbon/k8s/base/app/kustomization.yaml +10 -0
- package/carbon/k8s/base/app/network-policy.yaml +124 -0
- package/carbon/k8s/base/app/pdb.yaml +14 -0
- package/carbon/k8s/base/app/rbac.yaml +36 -0
- package/carbon/k8s/base/app/service.yaml +16 -0
- package/carbon/k8s/base/backup/cronjob.yaml +120 -0
- package/carbon/k8s/base/backup/kustomization.yaml +7 -0
- package/carbon/k8s/base/backup/network-policy.yaml +31 -0
- package/carbon/k8s/base/backup/rbac.yaml +7 -0
- package/carbon/k8s/base/cluster-autoscaler/deployment.yaml +103 -0
- package/carbon/k8s/base/cluster-autoscaler/kustomization.yaml +17 -0
- package/carbon/k8s/base/cluster-autoscaler/rbac.yaml +109 -0
- package/carbon/k8s/base/config/configmap.yaml +48 -0
- package/carbon/k8s/base/config/kustomization.yaml +8 -0
- package/carbon/k8s/base/hetzner-ccm/kustomization.yaml +9 -0
- package/carbon/k8s/base/hetzner-csi/kustomization.yaml +9 -0
- package/carbon/k8s/base/kustomization.yaml +43 -0
- package/carbon/k8s/base/namespace.yaml +7 -0
- package/carbon/k8s/base/network-policies.yaml +95 -0
- package/carbon/k8s/base/registry/kustomization.yaml +5 -0
- package/carbon/k8s/base/registry/local-registry.yaml +193 -0
- package/carbon/k8s/base/traefik/certificate.yaml +21 -0
- package/carbon/k8s/base/traefik/configmap.yaml +13 -0
- package/carbon/k8s/base/traefik/deployment.yaml +165 -0
- package/carbon/k8s/base/traefik/ingressroute.yaml +141 -0
- package/carbon/k8s/base/traefik/kustomization.yaml +11 -0
- package/carbon/k8s/base/traefik/middleware.yaml +109 -0
- package/carbon/k8s/base/traefik/network-policy.yaml +92 -0
- package/carbon/k8s/base/traefik/service.yaml +38 -0
- package/carbon/k8s/flux/README.md +49 -0
- package/carbon/k8s/flux/clusters/primary/vibecarbon.yaml +128 -0
- package/carbon/k8s/flux/clusters/standby/vibecarbon.yaml +83 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-release.yaml +38 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/helm-repository.yaml +16 -0
- package/carbon/k8s/gitops/cert-manager-webhook-hetzner/kustomization.yaml +10 -0
- package/carbon/k8s/gitops/supabase/helm-release.yaml +66 -0
- package/carbon/k8s/gitops/supabase/helm-repository.yaml +18 -0
- package/carbon/k8s/gitops/supabase/kustomization.yaml +33 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-cloudflare.yaml +51 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-hetzner.yaml +59 -0
- package/carbon/k8s/infra/cert-manager-resources/cluster-issuers-manual.yaml +43 -0
- package/carbon/k8s/infra/cert-manager-resources/kustomization.yaml +15 -0
- package/carbon/k8s/infra/kustomization.yaml +21 -0
- package/carbon/k8s/infra/traefik-crds/kustomization.yaml +10 -0
- package/carbon/k8s/overlays/local/configmap.yaml +23 -0
- package/carbon/k8s/overlays/local/ingressroute-studio.yaml +104 -0
- package/carbon/k8s/overlays/local/kustomization.yaml +215 -0
- package/carbon/k8s/overlays/local/namespace.yaml +8 -0
- package/carbon/k8s/overlays/local/secrets.yaml +32 -0
- package/carbon/k8s/overlays/production/kustomization.yaml +34 -0
- package/carbon/k8s/test-local.sh +318 -0
- package/carbon/k8s/values/supabase.values.yaml +133 -0
- package/carbon/package.json +154 -0
- package/carbon/runtime.Dockerfile +17 -0
- package/carbon/scripts/_dev-jwt.mjs +45 -0
- package/carbon/scripts/check-rls.ts +53 -0
- package/carbon/scripts/dev-init.js +191 -0
- package/carbon/scripts/dev.js +191 -0
- package/carbon/scripts/docker-down.js +63 -0
- package/carbon/scripts/docker-logs.js +59 -0
- package/carbon/scripts/docker-up.js +222 -0
- package/carbon/scripts/generate-dev-configs.sh +131 -0
- package/carbon/scripts/generate-rss.ts +116 -0
- package/carbon/scripts/generate-sitemap.ts +102 -0
- package/carbon/scripts/k8s-apply.js +71 -0
- package/carbon/scripts/k8s-delete.js +75 -0
- package/carbon/scripts/lib/manifest.js +176 -0
- package/carbon/scripts/secret-scan.mjs +278 -0
- package/carbon/scripts/validate-dev-configs.sh +101 -0
- package/carbon/src/client/App.tsx +202 -0
- package/carbon/src/client/assets/hyperformant-dark.svg +29 -0
- package/carbon/src/client/assets/hyperformant-light.svg +29 -0
- package/carbon/src/client/assets/logos/aider.svg +1 -0
- package/carbon/src/client/assets/logos/antigravity.svg +1 -0
- package/carbon/src/client/assets/logos/bolt.svg +1 -0
- package/carbon/src/client/assets/logos/claude-code.svg +1 -0
- package/carbon/src/client/assets/logos/copilot.svg +1 -0
- package/carbon/src/client/assets/logos/cursor.svg +1 -0
- package/carbon/src/client/assets/logos/gemini-cli.svg +1 -0
- package/carbon/src/client/assets/logos/lovable.svg +1 -0
- package/carbon/src/client/assets/logos/openai-codex.svg +1 -0
- package/carbon/src/client/assets/logos/v0.svg +1 -0
- package/carbon/src/client/assets/logos/windsurf.svg +1 -0
- package/carbon/src/client/assets/vibecarbon-icon.svg +19 -0
- package/carbon/src/client/assets/vibecarbon-logo-dark.svg +29 -0
- package/carbon/src/client/assets/vibecarbon-logo-light.svg +29 -0
- package/carbon/src/client/components/AIIntegrationSection.tsx +120 -0
- package/carbon/src/client/components/AppSidebar.tsx +760 -0
- package/carbon/src/client/components/ArchitectureSection.tsx +46 -0
- package/carbon/src/client/components/CTAFooter.tsx +59 -0
- package/carbon/src/client/components/ComparisonSection.tsx +132 -0
- package/carbon/src/client/components/ContentPanel.tsx +66 -0
- package/carbon/src/client/components/ContentSkeleton.tsx +21 -0
- package/carbon/src/client/components/ErrorBoundary.tsx +46 -0
- package/carbon/src/client/components/FAQSection.tsx +76 -0
- package/carbon/src/client/components/FileUpload.tsx +210 -0
- package/carbon/src/client/components/HeaderActions.tsx +17 -0
- package/carbon/src/client/components/Hero.tsx +608 -0
- package/carbon/src/client/components/ImpersonationBanner.tsx +31 -0
- package/carbon/src/client/components/LanguageSwitcher.tsx +67 -0
- package/carbon/src/client/components/Logo.tsx +87 -0
- package/carbon/src/client/components/LogoStrip.tsx +76 -0
- package/carbon/src/client/components/MetricsStrip.tsx +48 -0
- package/carbon/src/client/components/Nav.tsx +195 -0
- package/carbon/src/client/components/NewsletterSignup.tsx +147 -0
- package/carbon/src/client/components/NotificationDrawer.tsx +171 -0
- package/carbon/src/client/components/PageHeader.tsx +24 -0
- package/carbon/src/client/components/PlanGate.tsx +36 -0
- package/carbon/src/client/components/PricingSection.tsx +371 -0
- package/carbon/src/client/components/SEO.tsx +34 -0
- package/carbon/src/client/components/SmoothScroll.tsx +45 -0
- package/carbon/src/client/components/TechStackSection.tsx +165 -0
- package/carbon/src/client/components/WorkflowSection.tsx +604 -0
- package/carbon/src/client/components/admin/DockerLogs.tsx +276 -0
- package/carbon/src/client/components/admin/PerformanceMetrics.tsx +230 -0
- package/carbon/src/client/components/admin/ServiceCard.tsx +45 -0
- package/carbon/src/client/components/admin/ServicesStatus.tsx +296 -0
- package/carbon/src/client/components/architecture/ArchitectureDiagram.tsx +413 -0
- package/carbon/src/client/components/auth/AuthProvider.tsx +466 -0
- package/carbon/src/client/components/effects/FilmGrainOverlay.tsx +35 -0
- package/carbon/src/client/components/effects/FresnelEdge.tsx +49 -0
- package/carbon/src/client/components/effects/GlowTracker.tsx +46 -0
- package/carbon/src/client/components/effects/SparkBurst.tsx +145 -0
- package/carbon/src/client/components/effects/VGlowEffect.tsx +83 -0
- package/carbon/src/client/components/effects/index.ts +2 -0
- package/carbon/src/client/components/layouts/SidebarLayout.tsx +20 -0
- package/carbon/src/client/components/scroll/ScrollSection.tsx +76 -0
- package/carbon/src/client/components/scroll/ScrollytellingProvider.tsx +81 -0
- package/carbon/src/client/components/scroll/index.ts +2 -0
- package/carbon/src/client/components/ui/accordion.tsx +71 -0
- package/carbon/src/client/components/ui/alert-dialog.tsx +162 -0
- package/carbon/src/client/components/ui/alert.tsx +73 -0
- package/carbon/src/client/components/ui/aspect-ratio.tsx +22 -0
- package/carbon/src/client/components/ui/avatar.tsx +91 -0
- package/carbon/src/client/components/ui/badge.tsx +50 -0
- package/carbon/src/client/components/ui/breadcrumb.tsx +100 -0
- package/carbon/src/client/components/ui/button-group.tsx +78 -0
- package/carbon/src/client/components/ui/button.tsx +120 -0
- package/carbon/src/client/components/ui/calendar.tsx +182 -0
- package/carbon/src/client/components/ui/card.tsx +85 -0
- package/carbon/src/client/components/ui/carousel.tsx +227 -0
- package/carbon/src/client/components/ui/chart.tsx +357 -0
- package/carbon/src/client/components/ui/checkbox.tsx +27 -0
- package/carbon/src/client/components/ui/collapsible.tsx +15 -0
- package/carbon/src/client/components/ui/command.tsx +178 -0
- package/carbon/src/client/components/ui/context-menu.tsx +233 -0
- package/carbon/src/client/components/ui/dialog.tsx +132 -0
- package/carbon/src/client/components/ui/drawer.tsx +118 -0
- package/carbon/src/client/components/ui/dropdown-menu.tsx +242 -0
- package/carbon/src/client/components/ui/empty.tsx +94 -0
- package/carbon/src/client/components/ui/field.tsx +226 -0
- package/carbon/src/client/components/ui/hover-card.tsx +44 -0
- package/carbon/src/client/components/ui/input-group.tsx +146 -0
- package/carbon/src/client/components/ui/input-otp.tsx +83 -0
- package/carbon/src/client/components/ui/input.tsx +20 -0
- package/carbon/src/client/components/ui/item.tsx +188 -0
- package/carbon/src/client/components/ui/kbd.tsx +26 -0
- package/carbon/src/client/components/ui/label.tsx +21 -0
- package/carbon/src/client/components/ui/menubar.tsx +250 -0
- package/carbon/src/client/components/ui/navigation-menu.tsx +155 -0
- package/carbon/src/client/components/ui/pagination.tsx +102 -0
- package/carbon/src/client/components/ui/popover.tsx +75 -0
- package/carbon/src/client/components/ui/progress.tsx +66 -0
- package/carbon/src/client/components/ui/radio-group.tsx +36 -0
- package/carbon/src/client/components/ui/resizable.tsx +46 -0
- package/carbon/src/client/components/ui/scroll-area.tsx +48 -0
- package/carbon/src/client/components/ui/select.tsx +186 -0
- package/carbon/src/client/components/ui/separator.tsx +21 -0
- package/carbon/src/client/components/ui/sheet.tsx +124 -0
- package/carbon/src/client/components/ui/sidebar.tsx +702 -0
- package/carbon/src/client/components/ui/skeleton.tsx +13 -0
- package/carbon/src/client/components/ui/slider.tsx +57 -0
- package/carbon/src/client/components/ui/sonner.tsx +45 -0
- package/carbon/src/client/components/ui/spinner.tsx +15 -0
- package/carbon/src/client/components/ui/switch.tsx +30 -0
- package/carbon/src/client/components/ui/table.tsx +89 -0
- package/carbon/src/client/components/ui/tabs.tsx +73 -0
- package/carbon/src/client/components/ui/textarea.tsx +18 -0
- package/carbon/src/client/components/ui/toggle-group.tsx +87 -0
- package/carbon/src/client/components/ui/toggle.tsx +44 -0
- package/carbon/src/client/components/ui/tooltip.tsx +56 -0
- package/carbon/src/client/hooks/api/index.ts +14 -0
- package/carbon/src/client/hooks/api/useAuthSettings.ts +80 -0
- package/carbon/src/client/hooks/api/useSubscription.ts +87 -0
- package/carbon/src/client/hooks/use-mobile.ts +21 -0
- package/carbon/src/client/hooks/useMousePosition.ts +91 -0
- package/carbon/src/client/hooks/useNotifications.tsx +124 -0
- package/carbon/src/client/hooks/useOrganizationMembers.ts +127 -0
- package/carbon/src/client/hooks/useOrganizations.tsx +230 -0
- package/carbon/src/client/hooks/usePackageManager.ts +69 -0
- package/carbon/src/client/hooks/useReducedMotion.ts +20 -0
- package/carbon/src/client/hooks/useRunningServices.ts +114 -0
- package/carbon/src/client/hooks/useScrollProgress.ts +56 -0
- package/carbon/src/client/index.css +467 -0
- package/carbon/src/client/index.html +56 -0
- package/carbon/src/client/lib/admin-services.ts +151 -0
- package/carbon/src/client/lib/api.ts +32 -0
- package/carbon/src/client/lib/blog.ts +35 -0
- package/carbon/src/client/lib/changelog.ts +33 -0
- package/carbon/src/client/lib/docs-search.ts +101 -0
- package/carbon/src/client/lib/docs.ts +37 -0
- package/carbon/src/client/lib/i18n.ts +32 -0
- package/carbon/src/client/lib/supabase.ts +72 -0
- package/carbon/src/client/lib/tailwind-colors.ts +357 -0
- package/carbon/src/client/lib/theme.ts +117 -0
- package/carbon/src/client/lib/utils.ts +22 -0
- package/carbon/src/client/locales/de.json +529 -0
- package/carbon/src/client/locales/en.json +461 -0
- package/carbon/src/client/locales/es.json +529 -0
- package/carbon/src/client/locales/fr.json +529 -0
- package/carbon/src/client/locales/pt.json +529 -0
- package/carbon/src/client/main.tsx +56 -0
- package/carbon/src/client/mdx.d.ts +13 -0
- package/carbon/src/client/pages/ApiDocs.tsx +76 -0
- package/carbon/src/client/pages/AuthCallback.tsx +34 -0
- package/carbon/src/client/pages/Blog.tsx +167 -0
- package/carbon/src/client/pages/Changelog.tsx +171 -0
- package/carbon/src/client/pages/Charts.tsx +388 -0
- package/carbon/src/client/pages/Checkout.tsx +227 -0
- package/carbon/src/client/pages/Contact.tsx +174 -0
- package/carbon/src/client/pages/Dashboard.tsx +368 -0
- package/carbon/src/client/pages/Docs.tsx +372 -0
- package/carbon/src/client/pages/ForgotPassword.tsx +111 -0
- package/carbon/src/client/pages/Home.tsx +187 -0
- package/carbon/src/client/pages/Legal.tsx +100 -0
- package/carbon/src/client/pages/Login.tsx +408 -0
- package/carbon/src/client/pages/MFAVerify.tsx +156 -0
- package/carbon/src/client/pages/NotFound.tsx +21 -0
- package/carbon/src/client/pages/Onboarding.tsx +246 -0
- package/carbon/src/client/pages/ResetPassword.tsx +200 -0
- package/carbon/src/client/pages/UIComponents.tsx +390 -0
- package/carbon/src/client/pages/admin/ContactSubmissions.tsx +220 -0
- package/carbon/src/client/pages/admin/Dashboard.tsx +24 -0
- package/carbon/src/client/pages/admin/Infrastructure.tsx +257 -0
- package/carbon/src/client/pages/admin/Jobs.tsx +225 -0
- package/carbon/src/client/pages/admin/Logs.tsx +18 -0
- package/carbon/src/client/pages/admin/Newsletter.tsx +300 -0
- package/carbon/src/client/pages/admin/Notifications.tsx +603 -0
- package/carbon/src/client/pages/admin/Organizations.tsx +306 -0
- package/carbon/src/client/pages/admin/Settings.tsx +314 -0
- package/carbon/src/client/pages/admin/Theme.tsx +465 -0
- package/carbon/src/client/pages/admin/Users.tsx +365 -0
- package/carbon/src/client/pages/api-docs.css +156 -0
- package/carbon/src/client/pages/organizations/Details.tsx +200 -0
- package/carbon/src/client/pages/organizations/Members.tsx +402 -0
- package/carbon/src/client/pages/settings/Billing.tsx +473 -0
- package/carbon/src/client/pages/settings/Profile.tsx +160 -0
- package/carbon/src/client/pages/settings/Security.tsx +341 -0
- package/carbon/src/client/public/favicon.svg +19 -0
- package/carbon/src/client/public/robots.txt +8 -0
- package/carbon/src/server/billing/index.ts +104 -0
- package/carbon/src/server/billing/provider.ts +81 -0
- package/carbon/src/server/billing/providers/paddle.ts +314 -0
- package/carbon/src/server/billing/providers/polar.ts +325 -0
- package/carbon/src/server/billing/providers/stripe.ts +233 -0
- package/carbon/src/server/emails/templates.ts +116 -0
- package/carbon/src/server/index.ts +554 -0
- package/carbon/src/server/lib/auth.ts +6 -0
- package/carbon/src/server/lib/email.ts +64 -0
- package/carbon/src/server/lib/env.ts +112 -0
- package/carbon/src/server/lib/errors.ts +21 -0
- package/carbon/src/server/lib/logger.ts +17 -0
- package/carbon/src/server/lib/rate-limiter.ts +288 -0
- package/carbon/src/server/lib/request.ts +34 -0
- package/carbon/src/server/lib/stripe.ts +42 -0
- package/carbon/src/server/lib/supabase.ts +65 -0
- package/carbon/src/server/middleware/requirePlan.ts +80 -0
- package/carbon/src/server/routes/_internal/services-status.ts +958 -0
- package/carbon/src/server/routes/_internal/verify-role.ts +185 -0
- package/carbon/src/server/routes/health.ts +48 -0
- package/carbon/src/server/routes/v1/admin/contact.ts +128 -0
- package/carbon/src/server/routes/v1/admin/jobs.ts +171 -0
- package/carbon/src/server/routes/v1/admin/newsletter.ts +237 -0
- package/carbon/src/server/routes/v1/auth.ts +390 -0
- package/carbon/src/server/routes/v1/billing.ts +718 -0
- package/carbon/src/server/routes/v1/contact.ts +93 -0
- package/carbon/src/server/routes/v1/index.ts +1333 -0
- package/carbon/src/server/routes/v1/newsletter.ts +181 -0
- package/carbon/src/server/routes/v1/performance.ts +157 -0
- package/carbon/src/server/routes/v1/stats.ts +170 -0
- package/carbon/src/server/routes/v1/theme.ts +106 -0
- package/carbon/src/server/routes/webhooks/billing.ts +376 -0
- package/carbon/src/server/routes/webhooks/stripe.ts +276 -0
- package/carbon/src/server/types.ts +11 -0
- package/carbon/src/shared/pricing.ts +155 -0
- package/carbon/src/shared/types.ts +338 -0
- package/carbon/supabase/migrations/00001_init.sql +717 -0
- package/carbon/supabase/migrations/00002_theme_settings.sql +13 -0
- package/carbon/supabase/migrations/00003_pg_cron.sql +121 -0
- package/carbon/supabase/migrations/00004_contact_newsletter.sql +81 -0
- package/carbon/supabase/migrations/00005_localization_languages.sql +22 -0
- package/carbon/supabase/seed.sql +16 -0
- package/carbon/tests/_helpers/app.ts +45 -0
- package/carbon/tests/_helpers/env.ts +37 -0
- package/carbon/tests/_helpers/factories.ts +69 -0
- package/carbon/tests/_helpers/jwt.ts +23 -0
- package/carbon/tests/_helpers/setup-integration.ts +20 -0
- package/carbon/tests/_helpers/setup-rtl.ts +12 -0
- package/carbon/tests/component/ErrorBoundary.test.tsx +53 -0
- package/carbon/tests/component/use-auth-settings.test.tsx +119 -0
- package/carbon/tests/integration/server/routes/contact.test.ts +162 -0
- package/carbon/tests/integration/server/routes/health.test.ts +61 -0
- package/carbon/tests/structural/i18n-parity.test.ts +42 -0
- package/carbon/tests/unit/client/utils.test.ts +49 -0
- package/carbon/tests/unit/shared/pricing.test.ts +93 -0
- package/carbon/tsconfig.json +27 -0
- package/carbon/tsconfig.server.json +27 -0
- package/carbon/tsconfig.test.json +9 -0
- package/carbon/vite.config.ts +110 -0
- package/carbon/vitest.config.ts +74 -0
- package/carbon/volumes/db/jwt.sql +57 -0
- package/carbon/volumes/db/metabase-init.sh +29 -0
- package/carbon/volumes/db/n8n-init.sh +25 -0
- package/carbon/volumes/db/realtime.sql +33 -0
- package/carbon/volumes/db/roles.sql +93 -0
- package/carbon/volumes/db/set-passwords.sh +12 -0
- package/carbon/volumes/db/super-admin.dev.sql +113 -0
- package/carbon/volumes/db/super-admin.generated.sql +113 -0
- package/carbon/volumes/db/super-admin.sql +114 -0
- package/carbon/volumes/grafana/dashboards/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards/overview.json +523 -0
- package/carbon/volumes/grafana/dashboards/postgresql.json +337 -0
- package/carbon/volumes/grafana/dashboards.dev/logs.json +179 -0
- package/carbon/volumes/grafana/dashboards.dev/overview.json +156 -0
- package/carbon/volumes/grafana/dashboards.dev/postgresql.json +337 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.dev.yml +16 -0
- package/carbon/volumes/grafana/provisioning/dashboards/dashboards.yml +16 -0
- package/carbon/volumes/grafana/provisioning/datasources/datasources.yml +43 -0
- package/carbon/volumes/kong/docker-entrypoint.sh +9 -0
- package/carbon/volumes/kong/kong.yml +208 -0
- package/carbon/volumes/loki/loki-config.yml +58 -0
- package/carbon/volumes/n8n/hooks.js +131 -0
- package/carbon/volumes/n8n/scripts/setup.sh +66 -0
- package/carbon/volumes/prometheus/prometheus.yml +43 -0
- package/carbon/volumes/promtail/promtail-config.yml +64 -0
- package/carbon/volumes/traefik/middlewares.dev.yml +83 -0
- package/carbon/volumes/traefik/middlewares.yml +95 -0
- package/carbon/volumes/traefik/vite-dev.yml +20 -0
- package/package.json +95 -0
- package/src/access.js +354 -0
- package/src/activate.js +187 -0
- package/src/add.js +718 -0
- package/src/backup.js +786 -0
- package/src/cli.js +350 -0
- package/src/configure.js +967 -0
- package/src/console.js +155 -0
- package/src/create.js +1499 -0
- package/src/deploy.js +311 -0
- package/src/destroy.js +2033 -0
- package/src/diagnose.js +735 -0
- package/src/down.js +80 -0
- package/src/failover.js +1032 -0
- package/src/lib/backup-s3.js +179 -0
- package/src/lib/build.js +33 -0
- package/src/lib/checksum.js +28 -0
- package/src/lib/ci-setup.js +666 -0
- package/src/lib/cli/help.js +129 -0
- package/src/lib/cli/parse-flags.js +160 -0
- package/src/lib/cli/select-action.js +65 -0
- package/src/lib/cli/select-environment.js +108 -0
- package/src/lib/cli/tty-guard.js +75 -0
- package/src/lib/cloudflare.js +447 -0
- package/src/lib/colors.js +52 -0
- package/src/lib/command.js +361 -0
- package/src/lib/config.js +359 -0
- package/src/lib/cost.js +103 -0
- package/src/lib/deploy/bundle.js +231 -0
- package/src/lib/deploy/compose/acme-verify.js +121 -0
- package/src/lib/deploy/compose/build-args.js +78 -0
- package/src/lib/deploy/compose/ha.js +1874 -0
- package/src/lib/deploy/compose/index.js +1294 -0
- package/src/lib/deploy/compose/reconcile.js +74 -0
- package/src/lib/deploy/github.js +382 -0
- package/src/lib/deploy/image.js +191 -0
- package/src/lib/deploy/index.js +119 -0
- package/src/lib/deploy/k8s/LICENSE +99 -0
- package/src/lib/deploy/k8s/gitops-deploy.js +298 -0
- package/src/lib/deploy/k8s/ha/index.js +1000 -0
- package/src/lib/deploy/k8s/index.js +62 -0
- package/src/lib/deploy/k8s/k3s.js +2515 -0
- package/src/lib/deploy/orchestrator.js +1401 -0
- package/src/lib/deploy/prompts.js +545 -0
- package/src/lib/deploy/remote-build.js +130 -0
- package/src/lib/deploy/state.js +120 -0
- package/src/lib/deploy/utils.js +328 -0
- package/src/lib/deploy-logger.js +93 -0
- package/src/lib/dns-propagation.js +48 -0
- package/src/lib/environment.js +58 -0
- package/src/lib/fetch-retry.js +103 -0
- package/src/lib/github-environments.js +335 -0
- package/src/lib/hetzner-dns.js +377 -0
- package/src/lib/hetzner-guided-setup.js +275 -0
- package/src/lib/host-keys.js +37 -0
- package/src/lib/iac/cloud-init.js +52 -0
- package/src/lib/iac/index.js +325 -0
- package/src/lib/iac/programs/hetzner-compose.js +130 -0
- package/src/lib/iac/programs/hetzner-k8s.js +320 -0
- package/src/lib/kubectl.js +81 -0
- package/src/lib/licensing/index.js +259 -0
- package/src/lib/licensing/tiers.js +181 -0
- package/src/lib/licensing/validator.js +171 -0
- package/src/lib/merge-package-json.js +90 -0
- package/src/lib/operator-ip.js +381 -0
- package/src/lib/package-manager.js +111 -0
- package/src/lib/perf.js +71 -0
- package/src/lib/project-guard.js +39 -0
- package/src/lib/project.js +334 -0
- package/src/lib/providers/base.js +276 -0
- package/src/lib/providers/hetzner-s3.js +656 -0
- package/src/lib/providers/hetzner.js +755 -0
- package/src/lib/providers/index.js +164 -0
- package/src/lib/s3.js +510 -0
- package/src/lib/secret-scan.js +583 -0
- package/src/lib/secrets.js +63 -0
- package/src/lib/server-types.js +195 -0
- package/src/lib/shell.js +91 -0
- package/src/lib/ssh.js +241 -0
- package/src/lib/tracker.js +242 -0
- package/src/lib/upgrade-policy.js +170 -0
- package/src/lib/validators.js +105 -0
- package/src/lib/version.js +5 -0
- package/src/remove.js +292 -0
- package/src/reset.js +97 -0
- package/src/restore.js +871 -0
- package/src/scale.js +1734 -0
- package/src/shell.js +222 -0
- package/src/status.js +981 -0
- package/src/up.js +264 -0
- package/src/upgrade.js +721 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared help-text renderer for vibecarbon commands.
|
|
3
|
+
*
|
|
4
|
+
* Each command exports a `CommandSpec` (see `parse-flags.js` for the
|
|
5
|
+
* type) and this renderer produces the human help body from it. One
|
|
6
|
+
* authoritative source for usage info means flags can't drift between
|
|
7
|
+
* the parser and the help output the way they did when each command
|
|
8
|
+
* hand-rolled both — a common failure mode in the old code where a
|
|
9
|
+
* flag would be parsed but missing from `--help`, or vice-versa.
|
|
10
|
+
*
|
|
11
|
+
* The output style mirrors the existing tone: bold section headers,
|
|
12
|
+
* cyan flag/value names, dim descriptions. Sections are skipped
|
|
13
|
+
* silently when their data is empty (no flags? no FLAGS section).
|
|
14
|
+
*
|
|
15
|
+
* Vibecarbon is single-dash-only — flag names render as `-name`,
|
|
16
|
+
* never `--name`. See memory:feedback_cli_single_dash_flags.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { c } from '../colors.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {object} HelpExample
|
|
23
|
+
* @property {string} command - the literal invocation (without
|
|
24
|
+
* the leading `$ ` shell prompt).
|
|
25
|
+
* @property {string} [description] - one-line context shown above
|
|
26
|
+
* the command in dim text.
|
|
27
|
+
*
|
|
28
|
+
* @typedef {import('./parse-flags.js').CommandSpec & {
|
|
29
|
+
* examples?: HelpExample[],
|
|
30
|
+
* description?: string,
|
|
31
|
+
* }} HelpSpec
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Render a command's help body. Returns a string ending in a newline,
|
|
36
|
+
* suitable for `console.log()` or `process.stdout.write()`.
|
|
37
|
+
*
|
|
38
|
+
* @param {HelpSpec} spec
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
export function renderHelp(spec) {
|
|
42
|
+
const lines = [];
|
|
43
|
+
|
|
44
|
+
// Title line: "Vibecarbon Backup - Create or manage backups"
|
|
45
|
+
const title = `${c.bold('Vibecarbon')} ${c.bold(capitalize(spec.name))}`;
|
|
46
|
+
if (spec.summary) {
|
|
47
|
+
lines.push(`${title} - ${spec.summary}`);
|
|
48
|
+
} else {
|
|
49
|
+
lines.push(title);
|
|
50
|
+
}
|
|
51
|
+
lines.push('');
|
|
52
|
+
|
|
53
|
+
if (spec.description) {
|
|
54
|
+
lines.push(spec.description);
|
|
55
|
+
lines.push('');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// USAGE section.
|
|
59
|
+
lines.push(c.bold('USAGE'));
|
|
60
|
+
lines.push(` ${formatUsage(spec)}`);
|
|
61
|
+
lines.push('');
|
|
62
|
+
|
|
63
|
+
// ARGUMENTS section (only if there are positionals).
|
|
64
|
+
const positionals = spec.positional ?? [];
|
|
65
|
+
if (positionals.length > 0) {
|
|
66
|
+
lines.push(c.bold('ARGUMENTS'));
|
|
67
|
+
for (const p of positionals) {
|
|
68
|
+
const name = p.optional ? `[${p.name}]` : `<${p.name}>`;
|
|
69
|
+
const desc = p.description ?? '';
|
|
70
|
+
lines.push(` ${c.info(name.padEnd(16))} ${c.dim(desc)}`);
|
|
71
|
+
}
|
|
72
|
+
lines.push('');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// FLAGS section.
|
|
76
|
+
const flags = spec.flags ?? [];
|
|
77
|
+
if (flags.length > 0) {
|
|
78
|
+
lines.push(c.bold('FLAGS'));
|
|
79
|
+
for (const f of flags) {
|
|
80
|
+
const display = f.value ? `-${f.name} ${f.value}` : `-${f.name}`;
|
|
81
|
+
const desc = f.description ?? '';
|
|
82
|
+
const enumNote = f.enum ? `${desc ? ' ' : ''}(${f.enum.join('|')})` : '';
|
|
83
|
+
lines.push(` ${c.info(display.padEnd(20))} ${c.dim(desc + enumNote)}`);
|
|
84
|
+
}
|
|
85
|
+
lines.push('');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// EXAMPLES section.
|
|
89
|
+
const examples = spec.examples ?? [];
|
|
90
|
+
if (examples.length > 0) {
|
|
91
|
+
lines.push(c.bold('EXAMPLES'));
|
|
92
|
+
for (const ex of examples) {
|
|
93
|
+
if (ex.description) {
|
|
94
|
+
lines.push(` ${c.dim(`# ${ex.description}`)}`);
|
|
95
|
+
}
|
|
96
|
+
lines.push(` ${ex.command}`);
|
|
97
|
+
lines.push('');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return `${lines.join('\n').trimEnd()}\n`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Build the one-line usage signature.
|
|
106
|
+
* @param {HelpSpec} spec
|
|
107
|
+
*/
|
|
108
|
+
function formatUsage(spec) {
|
|
109
|
+
const parts = ['vibecarbon', spec.name];
|
|
110
|
+
for (const p of spec.positional ?? []) {
|
|
111
|
+
if (p.variadic) {
|
|
112
|
+
parts.push(p.optional ? `[${p.name}...]` : `<${p.name}...>`);
|
|
113
|
+
} else {
|
|
114
|
+
parts.push(p.optional ? `[${p.name}]` : `<${p.name}>`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if ((spec.flags ?? []).length > 0) {
|
|
118
|
+
parts.push('[flags]');
|
|
119
|
+
}
|
|
120
|
+
return parts.join(' ');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {string} s
|
|
125
|
+
* @returns {string}
|
|
126
|
+
*/
|
|
127
|
+
function capitalize(s) {
|
|
128
|
+
return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
|
|
129
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-dash spec-driven flag parser.
|
|
3
|
+
*
|
|
4
|
+
* Vibecarbon's CLI uses single-dash flags only (`-h`, `-env prod`,
|
|
5
|
+
* `-mode k8s`). Double-dash long flags (`--help`) are rejected — see
|
|
6
|
+
* memory:feedback_cli_single_dash_flags. Each command exports a spec
|
|
7
|
+
* declaring its flags + positionals; this parser walks argv against
|
|
8
|
+
* that spec and returns `{ values, positional, errors }`.
|
|
9
|
+
*
|
|
10
|
+
* Why a shared parser:
|
|
11
|
+
* - Each command file used to hand-roll a `parseArgs(args)` loop.
|
|
12
|
+
* The result was inconsistent error handling, drift between long
|
|
13
|
+
* and short forms, and silent acceptance of typos.
|
|
14
|
+
* - With a shared parser driven by an explicit spec, every command
|
|
15
|
+
* gets the same diagnostics, and help-text generation has a single
|
|
16
|
+
* authoritative source (this spec, consumed by `cli/help.js`).
|
|
17
|
+
*
|
|
18
|
+
* @typedef {object} FlagSpec
|
|
19
|
+
* @property {string} name - canonical flag name (used as `-${name}` in argv).
|
|
20
|
+
* @property {boolean} [boolean] - true for presence-only flags (`-l`, `-y`).
|
|
21
|
+
* Boolean and value are mutually exclusive; pick one.
|
|
22
|
+
* @property {string} [value] - placeholder shown in help (e.g. `<name>`).
|
|
23
|
+
* When set, the parser consumes the next argv element as the value.
|
|
24
|
+
* @property {string[]} [enum] - constrain a value flag to an allowed set.
|
|
25
|
+
* The parser emits an error if the supplied value isn't in this list.
|
|
26
|
+
* @property {string} [description] - human-readable description for help.
|
|
27
|
+
*
|
|
28
|
+
* @typedef {object} PositionalSpec
|
|
29
|
+
* @property {string} name - identifier (used as the key in the result).
|
|
30
|
+
* @property {boolean} [optional=false] - if false, missing this positional
|
|
31
|
+
* produces an error.
|
|
32
|
+
* @property {boolean} [variadic=false] - if true, all remaining positionals
|
|
33
|
+
* are collected into an array under this name.
|
|
34
|
+
* @property {string} [description] - human-readable description for help.
|
|
35
|
+
*
|
|
36
|
+
* @typedef {object} CommandSpec
|
|
37
|
+
* @property {string} name - the command (e.g. `backup`).
|
|
38
|
+
* @property {string} [summary] - one-line description for help.
|
|
39
|
+
* @property {FlagSpec[]} [flags]
|
|
40
|
+
* @property {PositionalSpec[]} [positional]
|
|
41
|
+
*
|
|
42
|
+
* @typedef {object} ParseResult
|
|
43
|
+
* @property {Record<string, string|boolean|null>} values - flag values
|
|
44
|
+
* keyed by canonical name. Boolean flags default to false; value flags
|
|
45
|
+
* default to null.
|
|
46
|
+
* @property {Record<string, string|string[]|undefined>} positional -
|
|
47
|
+
* positional values keyed by their declared name. Variadic positionals
|
|
48
|
+
* produce an array.
|
|
49
|
+
* @property {string[]} errors - human-readable diagnostics. Empty when
|
|
50
|
+
* parsing succeeded; the caller decides whether to print and exit.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {string[]} argv - process.argv slice (without node + script).
|
|
55
|
+
* @param {CommandSpec} spec
|
|
56
|
+
* @returns {ParseResult}
|
|
57
|
+
*/
|
|
58
|
+
export function parseFlags(argv, spec) {
|
|
59
|
+
/** @type {Record<string, string|boolean|null>} */
|
|
60
|
+
const values = {};
|
|
61
|
+
/** @type {Record<string, string|string[]|undefined>} */
|
|
62
|
+
const positional = {};
|
|
63
|
+
/** @type {string[]} */
|
|
64
|
+
const errors = [];
|
|
65
|
+
|
|
66
|
+
const flagSpecs = spec.flags ?? [];
|
|
67
|
+
const positionalSpecs = spec.positional ?? [];
|
|
68
|
+
|
|
69
|
+
for (const f of flagSpecs) {
|
|
70
|
+
if (f.boolean && f.value !== undefined) {
|
|
71
|
+
throw new Error(`flag spec "${f.name}" has both \`boolean\` and \`value\`; pick one`);
|
|
72
|
+
}
|
|
73
|
+
values[f.name] = f.boolean ? false : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** @type {string[]} */
|
|
77
|
+
const collectedPositionals = [];
|
|
78
|
+
|
|
79
|
+
for (let i = 0; i < argv.length; i++) {
|
|
80
|
+
const arg = argv[i];
|
|
81
|
+
|
|
82
|
+
if (!arg.startsWith('-')) {
|
|
83
|
+
collectedPositionals.push(arg);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Reject the empty `-` (probably a typo) and double-dash forms
|
|
88
|
+
// (`--help`, `--env=prod`). Both fall into the "unknown flag" path
|
|
89
|
+
// — vibecarbon is pre-release and intentionally has no migration
|
|
90
|
+
// shim for the dropped POSIX-long convention.
|
|
91
|
+
if (arg === '-' || arg.startsWith('--')) {
|
|
92
|
+
errors.push(`unknown flag: ${arg}`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const name = arg.slice(1);
|
|
97
|
+
const flagSpec = flagSpecs.find((f) => f.name === name);
|
|
98
|
+
|
|
99
|
+
if (!flagSpec) {
|
|
100
|
+
errors.push(`unknown flag: ${arg}`);
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (flagSpec.boolean) {
|
|
105
|
+
values[name] = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Value flag — consume the next argv element. Reject if the next
|
|
110
|
+
// element is missing or itself a flag (the latter usually means the
|
|
111
|
+
// operator forgot the value: `vibecarbon backup -env -y` is almost
|
|
112
|
+
// certainly a mistake, not "use literal `-y` as the env name").
|
|
113
|
+
const next = argv[i + 1];
|
|
114
|
+
if (next === undefined || next.startsWith('-')) {
|
|
115
|
+
errors.push(`flag -${name} requires a value`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (flagSpec.enum && !flagSpec.enum.includes(next)) {
|
|
119
|
+
errors.push(`-${name} must be one of: ${flagSpec.enum.join(', ')} (got: ${next})`);
|
|
120
|
+
// Still consume next so we don't double-report it as a stray positional.
|
|
121
|
+
i++;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
values[name] = next;
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Distribute collected positionals across positional specs in order.
|
|
129
|
+
// A variadic spec consumes all remaining positionals, so it must be
|
|
130
|
+
// the last entry; we guard against mid-list variadics in the schema.
|
|
131
|
+
let cursor = 0;
|
|
132
|
+
for (let p = 0; p < positionalSpecs.length; p++) {
|
|
133
|
+
const ps = positionalSpecs[p];
|
|
134
|
+
if (ps.variadic) {
|
|
135
|
+
if (p !== positionalSpecs.length - 1) {
|
|
136
|
+
throw new Error(`positional "${ps.name}" is variadic but not the last entry in spec`);
|
|
137
|
+
}
|
|
138
|
+
positional[ps.name] = collectedPositionals.slice(cursor);
|
|
139
|
+
cursor = collectedPositionals.length;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
if (cursor < collectedPositionals.length) {
|
|
143
|
+
positional[ps.name] = collectedPositionals[cursor];
|
|
144
|
+
cursor++;
|
|
145
|
+
} else {
|
|
146
|
+
positional[ps.name] = undefined;
|
|
147
|
+
if (!ps.optional) {
|
|
148
|
+
errors.push(`missing required argument: ${ps.name}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Excess positionals (operator passed more than the spec declares).
|
|
154
|
+
if (cursor < collectedPositionals.length) {
|
|
155
|
+
const extra = collectedPositionals.slice(cursor);
|
|
156
|
+
errors.push(`unexpected argument${extra.length === 1 ? '' : 's'}: ${extra.join(' ')}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { values, positional, errors };
|
|
160
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action-picker prompt for commands that branch on a verb.
|
|
3
|
+
*
|
|
4
|
+
* Some commands have multiple modes — `backup` can create, list, or
|
|
5
|
+
* download; `restore` can restore from S3 or from a local file. Under
|
|
6
|
+
* the new interactive-default model, these don't surface as flags;
|
|
7
|
+
* the bare command opens an action menu after the env is selected.
|
|
8
|
+
*
|
|
9
|
+
* The seed parameter (from `-action <verb>`) lets power users skip
|
|
10
|
+
* the prompt for repeatable invocations. Validation against the
|
|
11
|
+
* options list catches typos loudly instead of silently dropping
|
|
12
|
+
* into the prompt.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as p from '@clack/prompts';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} ActionOption
|
|
19
|
+
* @property {string} value - canonical identifier; what the seed
|
|
20
|
+
* matches against and what this helper returns.
|
|
21
|
+
* @property {string} label - what the operator sees in the picker.
|
|
22
|
+
* @property {string} [hint] - secondary text in the picker.
|
|
23
|
+
*
|
|
24
|
+
* @param {object} options
|
|
25
|
+
* @param {string} options.message - prompt text (e.g. "What do you
|
|
26
|
+
* want to do?").
|
|
27
|
+
* @param {ActionOption[]} options.choices
|
|
28
|
+
* @param {string|null} [options.seed] - operator-supplied verb (from
|
|
29
|
+
* the `-action` flag). When set, validated against choices and
|
|
30
|
+
* returned directly. When null/undefined, prompt.
|
|
31
|
+
* @returns {Promise<string>} the chosen `value`.
|
|
32
|
+
*/
|
|
33
|
+
export async function selectAction(options) {
|
|
34
|
+
const { message, choices, seed } = options;
|
|
35
|
+
|
|
36
|
+
if (choices.length === 0) {
|
|
37
|
+
throw new Error('selectAction called with no choices — programming error');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (seed) {
|
|
41
|
+
const known = choices.find((c) => c.value === seed);
|
|
42
|
+
if (!known) {
|
|
43
|
+
const valid = choices.map((c) => c.value).join(', ');
|
|
44
|
+
p.log.error(`Action '${seed}' is not valid. Choose one of: ${valid}.`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
return known.value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (choices.length === 1) {
|
|
51
|
+
return choices[0].value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const choice = await p.select({
|
|
55
|
+
message,
|
|
56
|
+
options: choices,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (p.isCancel(choice)) {
|
|
60
|
+
p.cancel('Operation cancelled.');
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return /** @type {string} */ (choice);
|
|
65
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-picker prompt for env-scoped commands.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the older `lib/environment.js#resolveEnvironment` with a
|
|
5
|
+
* verb-aware variant suited to the new interactive-default CLI:
|
|
6
|
+
* - The prompt + empty-state error speak in terms of the operation
|
|
7
|
+
* ("back up", "deploy to", "destroy") so the message reads as
|
|
8
|
+
* guidance, not a generic env picker.
|
|
9
|
+
* - The picker hint includes the deploy status, server IP, and
|
|
10
|
+
* primary region so multi-env operators can pick by metadata
|
|
11
|
+
* instead of memorized name.
|
|
12
|
+
* - The seed (positional or flag) is passed in explicitly rather
|
|
13
|
+
* than read from a parsed-args struct, so callers using either
|
|
14
|
+
* the new `parse-flags.js` or hand-rolled argv can both consume
|
|
15
|
+
* it without an adapter.
|
|
16
|
+
*
|
|
17
|
+
* Returns `{ envName, envConfig }`. On empty state, missing seed
|
|
18
|
+
* with no TTY, cancel, or invalid seed, exits the process with a
|
|
19
|
+
* canonical message — same model the rest of the CLI uses for
|
|
20
|
+
* unrecoverable conditions.
|
|
21
|
+
*
|
|
22
|
+
* The old `resolveEnvironment` is retained so PR 1 doesn't change
|
|
23
|
+
* any command behavior. PR 2 migrates backup/restore to this helper
|
|
24
|
+
* and PR 6 deletes the old one.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import * as p from '@clack/prompts';
|
|
28
|
+
import { c } from '../colors.js';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {object} projectConfig - parsed `.vibecarbon.json`.
|
|
32
|
+
* @param {object} options
|
|
33
|
+
* @param {string} options.actionVerb - imperative phrase describing
|
|
34
|
+
* the operation, e.g. "back up", "deploy to", "restore", "destroy".
|
|
35
|
+
* Used in prompts and empty-state messaging.
|
|
36
|
+
* @param {string|null} [options.seed] - operator-supplied env name
|
|
37
|
+
* (from positional or flag). When set, skip the prompt and use it
|
|
38
|
+
* directly. When null/undefined, prompt (or use the single env if
|
|
39
|
+
* only one exists).
|
|
40
|
+
* @returns {Promise<{ envName: string, envConfig: object }>}
|
|
41
|
+
*/
|
|
42
|
+
export async function selectEnvironment(projectConfig, options) {
|
|
43
|
+
const { actionVerb, seed } = options;
|
|
44
|
+
const environments = projectConfig.environments || {};
|
|
45
|
+
const envNames = Object.keys(environments);
|
|
46
|
+
|
|
47
|
+
if (envNames.length === 0) {
|
|
48
|
+
p.log.error(`No deployed environments to ${actionVerb}.`);
|
|
49
|
+
p.log.info(`Run ${c.info('vibecarbon deploy')} first.`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Seed from caller — validate against known envs, fail loudly if
|
|
54
|
+
// the operator typed a name that doesn't exist (so a typo doesn't
|
|
55
|
+
// silently fall through to a prompt that gets misinterpreted).
|
|
56
|
+
if (seed) {
|
|
57
|
+
if (!environments[seed]) {
|
|
58
|
+
p.log.error(`Environment '${seed}' not found.`);
|
|
59
|
+
p.log.info(`Available: ${envNames.join(', ')}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
return { envName: seed, envConfig: environments[seed] };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Single-env shortcut: no point prompting when there's only one
|
|
66
|
+
// answer. The operator might still want to see which env they're
|
|
67
|
+
// operating on; surface that as a log line so the prompt isn't
|
|
68
|
+
// silently bypassed.
|
|
69
|
+
if (envNames.length === 1) {
|
|
70
|
+
const only = envNames[0];
|
|
71
|
+
p.log.info(`Using environment: ${c.bold(only)} (only deployed env)`);
|
|
72
|
+
return { envName: only, envConfig: environments[only] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Multi-env: render a picker with metadata-rich hints.
|
|
76
|
+
const choice = await p.select({
|
|
77
|
+
message: `Which environment to ${actionVerb}?`,
|
|
78
|
+
options: envNames.map((name) => ({
|
|
79
|
+
value: name,
|
|
80
|
+
label: name,
|
|
81
|
+
hint: formatEnvHint(environments[name]),
|
|
82
|
+
})),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (p.isCancel(choice)) {
|
|
86
|
+
p.cancel('Operation cancelled.');
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { envName: /** @type {string} */ (choice), envConfig: environments[choice] };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build the hint string shown next to each env in the picker.
|
|
95
|
+
* Combines status + region + first server IP so operators can
|
|
96
|
+
* disambiguate by deploy state at a glance.
|
|
97
|
+
*
|
|
98
|
+
* @param {object} envConfig
|
|
99
|
+
* @returns {string}
|
|
100
|
+
*/
|
|
101
|
+
function formatEnvHint(envConfig) {
|
|
102
|
+
const parts = [];
|
|
103
|
+
if (envConfig.status) parts.push(envConfig.status);
|
|
104
|
+
if (envConfig.region) parts.push(envConfig.region);
|
|
105
|
+
const ip = envConfig.servers?.[0]?.ip;
|
|
106
|
+
if (ip) parts.push(ip);
|
|
107
|
+
return parts.join(' · ');
|
|
108
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TTY-detection guard for interactive-by-default commands.
|
|
3
|
+
*
|
|
4
|
+
* Vibecarbon's CLI is interactive-first: bare commands open a clack
|
|
5
|
+
* prompt flow. That model breaks when stdin isn't a TTY (CI, piped
|
|
6
|
+
* input, `< /dev/null` smoke tests). Rather than letting clack hang or
|
|
7
|
+
* silently misbehave, every command that intends to prompt calls
|
|
8
|
+
* `requireTTYOrFlags()` after parsing argv. If we're on a TTY, the call
|
|
9
|
+
* is a no-op. Off a TTY, the guard checks whether the operator supplied
|
|
10
|
+
* enough flags to skip every prompt the command would otherwise issue;
|
|
11
|
+
* if they did, fine — proceed. If they didn't, exit 1 with a message
|
|
12
|
+
* naming exactly which flags would unblock this invocation.
|
|
13
|
+
*
|
|
14
|
+
* Why this shape (and not "always require flags off-TTY"): some
|
|
15
|
+
* commands are no-prompt by nature (e.g. `vibecarbon backup -env prod
|
|
16
|
+
* -l` is a pure read). Demanding flags from those operators would be
|
|
17
|
+
* theatre. The guard takes a `requirements` array describing which
|
|
18
|
+
* prompts would otherwise run, and only complains about the ones the
|
|
19
|
+
* operator hasn't already short-circuited.
|
|
20
|
+
*
|
|
21
|
+
* @typedef {object} PromptRequirement
|
|
22
|
+
* @property {string} flag - the flag name that pre-fills this prompt
|
|
23
|
+
* (e.g. "env", "action").
|
|
24
|
+
* @property {string} description - what this prompt would ask, in
|
|
25
|
+
* imperative form (e.g. "select an environment").
|
|
26
|
+
* @property {boolean} satisfied - true if argv already supplies a
|
|
27
|
+
* value (operator pre-filled via flag or positional). When false,
|
|
28
|
+
* the command would have opened a prompt.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { c } from '../colors.js';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Verify we're either on an interactive TTY OR the operator supplied
|
|
35
|
+
* every flag needed to skip the prompts. Exits 1 with a canonical
|
|
36
|
+
* "needs TTY or these flags" message otherwise. Returns void on
|
|
37
|
+
* success.
|
|
38
|
+
*
|
|
39
|
+
* @param {object} options
|
|
40
|
+
* @param {PromptRequirement[]} options.requirements - prompts the
|
|
41
|
+
* command would issue without flag short-circuits.
|
|
42
|
+
* @param {NodeJS.ReadStream} [options.stdin=process.stdin] - injectable
|
|
43
|
+
* for testing.
|
|
44
|
+
* @param {NodeJS.WriteStream} [options.stderr=process.stderr] -
|
|
45
|
+
* injectable for testing.
|
|
46
|
+
* @param {(code: number) => never} [options.exit=process.exit] -
|
|
47
|
+
* injectable for testing.
|
|
48
|
+
*/
|
|
49
|
+
export function requireTTYOrFlags(options) {
|
|
50
|
+
const {
|
|
51
|
+
requirements,
|
|
52
|
+
stdin = process.stdin,
|
|
53
|
+
stderr = process.stderr,
|
|
54
|
+
exit = /** @type {(code: number) => never} */ (process.exit.bind(process)),
|
|
55
|
+
} = options;
|
|
56
|
+
|
|
57
|
+
if (stdin.isTTY) return;
|
|
58
|
+
|
|
59
|
+
const missing = requirements.filter((r) => !r.satisfied);
|
|
60
|
+
if (missing.length === 0) return;
|
|
61
|
+
|
|
62
|
+
// Format: one line per missing prompt with the flag that would
|
|
63
|
+
// unblock it, so the operator can copy-paste a working invocation.
|
|
64
|
+
const lines = [
|
|
65
|
+
'',
|
|
66
|
+
`${c.error('✗')} This command needs an interactive terminal, or these flags to skip prompts:`,
|
|
67
|
+
'',
|
|
68
|
+
...missing.map((m) => ` -${m.flag.padEnd(10)} ${m.description}`),
|
|
69
|
+
'',
|
|
70
|
+
'Run again with -h for the full flag reference.',
|
|
71
|
+
'',
|
|
72
|
+
];
|
|
73
|
+
stderr.write(`${lines.join('\n')}\n`);
|
|
74
|
+
exit(1);
|
|
75
|
+
}
|