@omega.js/manager 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/.claude-plugin/marketplace.json +14 -0
- package/LICENSE +98 -0
- package/README.md +165 -0
- package/bin/omega +2 -0
- package/bin/omg +2 -0
- package/claude-plugin/.claude-plugin/plugin.json +11 -0
- package/claude-plugin/.mcp.json +9 -0
- package/claude-plugin/README.md +139 -0
- package/claude-plugin/hooks/gate/mark.sh +58 -0
- package/claude-plugin/hooks/gate/run.sh +97 -0
- package/claude-plugin/hooks/guard/run.sh +249 -0
- package/claude-plugin/hooks/hooks.json +63 -0
- package/claude-plugin/hooks/inject/run.sh +127 -0
- package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
- package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
- package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
- package/claude-plugin/hooks/quality/run.sh +132 -0
- package/claude-plugin/hooks/shape/run.sh +45 -0
- package/claude-plugin/mcp-router-launch.js +29 -0
- package/claude-plugin/skills/README.md +48 -0
- package/claude-plugin/skills/accessibility/SKILL.md +31 -0
- package/claude-plugin/skills/analytics/SKILL.md +30 -0
- package/claude-plugin/skills/backend/SKILL.md +25 -0
- package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
- package/claude-plugin/skills/browser/SKILL.md +64 -0
- package/claude-plugin/skills/client/SKILL.md +24 -0
- package/claude-plugin/skills/desktop/SKILL.md +25 -0
- package/claude-plugin/skills/extension/SKILL.md +25 -0
- package/claude-plugin/skills/main/SKILL.md +54 -0
- package/claude-plugin/skills/manager/SKILL.md +25 -0
- package/claude-plugin/skills/seo/SKILL.md +36 -0
- package/claude-plugin/skills/theme/SKILL.md +33 -0
- package/claude-plugin/skills/web/SKILL.md +29 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +29 -0
- package/dist/commands/build.js +17 -0
- package/dist/commands/clean.js +16 -0
- package/dist/commands/company.js +46 -0
- package/dist/commands/deploy.js +143 -0
- package/dist/commands/dev.js +437 -0
- package/dist/commands/devlog.js +25 -0
- package/dist/commands/help.js +48 -0
- package/dist/commands/manage.js +53 -0
- package/dist/commands/migrate.js +76 -0
- package/dist/commands/onboard.js +31 -0
- package/dist/commands/pipeline.js +319 -0
- package/dist/commands/test.js +380 -0
- package/dist/commands/update.js +128 -0
- package/dist/commands/version.js +8 -0
- package/dist/company-init.js +167 -0
- package/dist/company.js +283 -0
- package/dist/config.js +1359 -0
- package/dist/devlog/index.js +226 -0
- package/dist/devlog/lib/collect-commits.js +136 -0
- package/dist/devlog/lib/generate-post.js +201 -0
- package/dist/devlog/lib/ghostii.js +122 -0
- package/dist/devlog/lib/project-map.js +98 -0
- package/dist/devlog/lib/publish-website.js +88 -0
- package/dist/index.js +45 -0
- package/dist/lib/agents-md.js +201 -0
- package/dist/lib/analytics-secret.js +22 -0
- package/dist/lib/argv.js +21 -0
- package/dist/lib/auth-admin.js +120 -0
- package/dist/lib/automation-client.js +210 -0
- package/dist/lib/backend-marketing.js +49 -0
- package/dist/lib/brand.js +240 -0
- package/dist/lib/bundle-id.js +53 -0
- package/dist/lib/claude-settings.js +125 -0
- package/dist/lib/company-scaffold.js +213 -0
- package/dist/lib/company.js +227 -0
- package/dist/lib/config-flow.js +350 -0
- package/dist/lib/config-write.js +41 -0
- package/dist/lib/custom-target.js +75 -0
- package/dist/lib/domain-utils.js +19 -0
- package/dist/lib/duration.js +39 -0
- package/dist/lib/env-order.js +236 -0
- package/dist/lib/env-secret.js +78 -0
- package/dist/lib/firestore-rest.js +326 -0
- package/dist/lib/framework-bin.js +156 -0
- package/dist/lib/gitignore.js +63 -0
- package/dist/lib/google-auth.js +495 -0
- package/dist/lib/google-token.js +66 -0
- package/dist/lib/jwt.js +31 -0
- package/dist/lib/legacy-oauth.js +59 -0
- package/dist/lib/node-version.js +127 -0
- package/dist/lib/owner-plan.js +78 -0
- package/dist/lib/package-scripts.js +152 -0
- package/dist/lib/preflight.js +470 -0
- package/dist/lib/product-create.js +144 -0
- package/dist/lib/run-command.js +74 -0
- package/dist/lib/run-gates.js +70 -0
- package/dist/lib/run-output.js +42 -0
- package/dist/lib/run-summary.js +384 -0
- package/dist/lib/scaffold.js +427 -0
- package/dist/lib/service-input.js +207 -0
- package/dist/lib/service-runner.js +466 -0
- package/dist/lib/stale.js +24 -0
- package/dist/lib/target-selection.js +155 -0
- package/dist/lib/verb-fanout.js +128 -0
- package/dist/lib/verify-live.js +188 -0
- package/dist/manage.js +311 -0
- package/dist/omega-bin.js +7 -0
- package/dist/onboard.js +532 -0
- package/dist/services/account/ensure/users.js +259 -0
- package/dist/services/account/index.js +97 -0
- package/dist/services/account/lib/backend-client.js +107 -0
- package/dist/services/account/lib/password.js +36 -0
- package/dist/services/account/lib/resolve-password.js +100 -0
- package/dist/services/advertising/ensure/sites.js +101 -0
- package/dist/services/advertising/index.js +101 -0
- package/dist/services/advertising/lib/adsense-api.js +41 -0
- package/dist/services/ai/ensure/keys.js +21 -0
- package/dist/services/ai/index.js +36 -0
- package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
- package/dist/services/analytics/ensure/google-streams.js +256 -0
- package/dist/services/analytics/ensure/meta-pixel.js +22 -0
- package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
- package/dist/services/analytics/index.js +134 -0
- package/dist/services/analytics/lib/analytics-api.js +156 -0
- package/dist/services/analytics/lib/meta-api.js +83 -0
- package/dist/services/analytics/lib/pixel-account.js +78 -0
- package/dist/services/analytics/lib/pixel-provision.js +152 -0
- package/dist/services/analytics/lib/pixel-specs.js +57 -0
- package/dist/services/analytics/lib/pixel-token.js +88 -0
- package/dist/services/analytics/lib/property-flow.js +77 -0
- package/dist/services/analytics/lib/tiktok-api.js +157 -0
- package/dist/services/analytics/lib/tiktok-auth.js +153 -0
- package/dist/services/assets/ensure/logo-gen.js +68 -0
- package/dist/services/assets/index.js +109 -0
- package/dist/services/assets/lib/assets-config.js +168 -0
- package/dist/services/assets/lib/brandmark-api.js +160 -0
- package/dist/services/assets/lib/derived.js +113 -0
- package/dist/services/assets/lib/font-loader.js +67 -0
- package/dist/services/assets/lib/reconcile.js +122 -0
- package/dist/services/assets/lib/reset.js +118 -0
- package/dist/services/assets/lib/svg-logo-generator.js +154 -0
- package/dist/services/assets/lib/svg-to-black.js +32 -0
- package/dist/services/assets/write/favicons.js +82 -0
- package/dist/services/assets/write/icons.js +64 -0
- package/dist/services/assets/write/process.js +96 -0
- package/dist/services/assets/write/reconcile.js +30 -0
- package/dist/services/assets/write/social-icons.js +108 -0
- package/dist/services/assets/write/templates.js +345 -0
- package/dist/services/bookmark/ensure/sync.js +255 -0
- package/dist/services/bookmark/index.js +17 -0
- package/dist/services/campaigns/ensure/contact-person.js +40 -0
- package/dist/services/campaigns/ensure/custom-fields.js +84 -0
- package/dist/services/campaigns/ensure/domain-auth.js +92 -0
- package/dist/services/campaigns/ensure/event-webhook.js +89 -0
- package/dist/services/campaigns/ensure/link-branding.js +187 -0
- package/dist/services/campaigns/ensure/list.js +50 -0
- package/dist/services/campaigns/ensure/segments.js +109 -0
- package/dist/services/campaigns/ensure/sender-identity.js +153 -0
- package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
- package/dist/services/campaigns/index.js +79 -0
- package/dist/services/campaigns/lib/dns-sync.js +81 -0
- package/dist/services/campaigns/lib/segment-query.js +138 -0
- package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
- package/dist/services/captcha/ensure/site-key.js +83 -0
- package/dist/services/captcha/index.js +78 -0
- package/dist/services/captcha/lib/console-url.js +45 -0
- package/dist/services/captcha/lib/recaptcha-api.js +36 -0
- package/dist/services/certificates/ensure/api-key.js +22 -0
- package/dist/services/certificates/ensure/bundle-ids.js +115 -0
- package/dist/services/certificates/ensure/certificates.js +187 -0
- package/dist/services/certificates/ensure/profiles.js +143 -0
- package/dist/services/certificates/index.js +225 -0
- package/dist/services/certificates/lib/apple-api.js +183 -0
- package/dist/services/certificates/lib/certificate-manager.js +252 -0
- package/dist/services/certificates/lib/identifier-manager.js +107 -0
- package/dist/services/certificates/lib/keychain.js +135 -0
- package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
- package/dist/services/certificates/lib/profile-manager.js +115 -0
- package/dist/services/chat/data/baseline-knowledge.md +96 -0
- package/dist/services/chat/ensure/chat.js +106 -0
- package/dist/services/chat/ensure/user.js +45 -0
- package/dist/services/chat/index.js +139 -0
- package/dist/services/chat/lib/baseline-knowledge.js +137 -0
- package/dist/services/cloud/ensure/authentication.js +267 -0
- package/dist/services/cloud/ensure/billing.js +120 -0
- package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
- package/dist/services/cloud/ensure/database.js +60 -0
- package/dist/services/cloud/ensure/firestore.js +79 -0
- package/dist/services/cloud/ensure/functions.js +49 -0
- package/dist/services/cloud/ensure/hosting.js +398 -0
- package/dist/services/cloud/ensure/oauth-consent.js +248 -0
- package/dist/services/cloud/ensure/project-settings.js +72 -0
- package/dist/services/cloud/ensure/sdk-config.js +95 -0
- package/dist/services/cloud/ensure/service-account.js +152 -0
- package/dist/services/cloud/ensure/services.js +103 -0
- package/dist/services/cloud/ensure/storage.js +47 -0
- package/dist/services/cloud/index.js +130 -0
- package/dist/services/cloud/lib/access-heal.js +130 -0
- package/dist/services/cloud/lib/firebase-api.js +662 -0
- package/dist/services/cloud/lib/project-flow.js +102 -0
- package/dist/services/directory/ensure/entry.js +48 -0
- package/dist/services/directory/index.js +64 -0
- package/dist/services/directory/lib/blocks.js +104 -0
- package/dist/services/disperse/index.js +45 -0
- package/dist/services/disperse/write/certs.js +117 -0
- package/dist/services/domain/ensure/nameservers.js +179 -0
- package/dist/services/domain/index.js +69 -0
- package/dist/services/domain/lib/namecheap-api.js +167 -0
- package/dist/services/domain/lib/registrars.js +46 -0
- package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
- package/dist/services/edge/ensure/cache-rules.js +105 -0
- package/dist/services/edge/ensure/dns-records.js +304 -0
- package/dist/services/edge/ensure/email-routing.js +292 -0
- package/dist/services/edge/ensure/rules-configuration.js +98 -0
- package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
- package/dist/services/edge/ensure/rules-redirect.js +107 -0
- package/dist/services/edge/ensure/rules-response-headers.js +90 -0
- package/dist/services/edge/ensure/rules-security.js +142 -0
- package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
- package/dist/services/edge/ensure/workers.js +219 -0
- package/dist/services/edge/ensure/zone-settings.js +108 -0
- package/dist/services/edge/ensure/zone.js +183 -0
- package/dist/services/edge/index.js +73 -0
- package/dist/services/edge/lib/cloudflare-api.js +84 -0
- package/dist/services/edge/lib/dns-records-helpers.js +518 -0
- package/dist/services/edge/lib/read-cache.js +16 -0
- package/dist/services/edge/lib/ruleset-helper.js +108 -0
- package/dist/services/edge/workers/omega-api-proxy.js +43 -0
- package/dist/services/email/data/baseline-filter.md +4 -0
- package/dist/services/email/data/baseline-knowledge.md +86 -0
- package/dist/services/email/ensure/agent.js +107 -0
- package/dist/services/email/ensure/user.js +45 -0
- package/dist/services/email/index.js +138 -0
- package/dist/services/email/lib/baseline.js +82 -0
- package/dist/services/email/lib/knowledge-file.js +53 -0
- package/dist/services/forms/ensure/form.js +46 -0
- package/dist/services/forms/ensure/user.js +45 -0
- package/dist/services/forms/index.js +138 -0
- package/dist/services/migrations/ensure/notifications.js +189 -0
- package/dist/services/migrations/ensure/orders.js +34 -0
- package/dist/services/migrations/ensure/payment-provider.js +132 -0
- package/dist/services/migrations/ensure/payments-intents.js +34 -0
- package/dist/services/migrations/ensure/state-retirement.js +266 -0
- package/dist/services/migrations/ensure/targets-rename.js +124 -0
- package/dist/services/migrations/ensure/users.js +1176 -0
- package/dist/services/migrations/index.js +101 -0
- package/dist/services/migrations/lib/attribution-touch.js +64 -0
- package/dist/services/migrations/lib/ensure-metadata.js +144 -0
- package/dist/services/migrations/lib/migration-runner.js +473 -0
- package/dist/services/migrations/lib/sanitize-strings.js +112 -0
- package/dist/services/migrations/lib/schema-validator.js +154 -0
- package/dist/services/monitoring/ensure/dsn.js +54 -0
- package/dist/services/monitoring/ensure/projects.js +135 -0
- package/dist/services/monitoring/index.js +54 -0
- package/dist/services/monitoring/lib/sentry-api.js +94 -0
- package/dist/services/newsletter/ensure/custom-fields.js +90 -0
- package/dist/services/newsletter/ensure/publication.js +94 -0
- package/dist/services/newsletter/ensure/segments.js +140 -0
- package/dist/services/newsletter/ensure/webhook.js +108 -0
- package/dist/services/newsletter/index.js +64 -0
- package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
- package/dist/services/newsletter/lib/segment-automation.js +577 -0
- package/dist/services/payment/ensure/chargebee-account.js +30 -0
- package/dist/services/payment/ensure/chargebee-products.js +359 -0
- package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
- package/dist/services/payment/ensure/paypal-account.js +36 -0
- package/dist/services/payment/ensure/paypal-products.js +374 -0
- package/dist/services/payment/ensure/paypal-webhook.js +128 -0
- package/dist/services/payment/ensure/stripe-account.js +93 -0
- package/dist/services/payment/ensure/stripe-disputes.js +56 -0
- package/dist/services/payment/ensure/stripe-products.js +245 -0
- package/dist/services/payment/ensure/stripe-radar.js +74 -0
- package/dist/services/payment/ensure/stripe-webhook.js +130 -0
- package/dist/services/payment/index.js +163 -0
- package/dist/services/payment/lib/chargebee-api.js +338 -0
- package/dist/services/payment/lib/payment-utils.js +116 -0
- package/dist/services/payment/lib/paypal-api.js +354 -0
- package/dist/services/payment/lib/provider-setup.js +134 -0
- package/dist/services/payment/lib/stripe-api.js +152 -0
- package/dist/services/repo/ensure/org.js +70 -0
- package/dist/services/repo/ensure/pages.js +72 -0
- package/dist/services/repo/ensure/repo.js +90 -0
- package/dist/services/repo/index.js +52 -0
- package/dist/services/repo/lib/github-api.js +196 -0
- package/dist/services/search/ensure/ga-link.js +57 -0
- package/dist/services/search/ensure/property.js +137 -0
- package/dist/services/search/ensure/sitemaps.js +70 -0
- package/dist/services/search/index.js +61 -0
- package/dist/services/search/lib/search-console-api.js +92 -0
- package/dist/services/seo/ensure/github-repos.js +271 -0
- package/dist/services/seo/index.js +55 -0
- package/dist/services/seo/lib/gh-api.js +161 -0
- package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
- package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
- package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
- package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
- package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
- package/dist/services/seo/templates/index.js +57 -0
- package/dist/services/server/ensure/brands.js +63 -0
- package/dist/services/server/index.js +42 -0
- package/dist/services/testing/ensure/target-checks.js +97 -0
- package/dist/services/testing/index.js +11 -0
- package/dist/services/testing/lib/checks.js +467 -0
- package/dist/services/update/index.js +20 -0
- package/dist/services/update/lib/cache.js +59 -0
- package/dist/services/update/lib/fingerprint.js +157 -0
- package/dist/services/update/write/targets.js +206 -0
- package/dist/services/workspace/ensure/agents.js +42 -0
- package/dist/services/workspace/ensure/claude-settings.js +34 -0
- package/dist/services/workspace/ensure/config.js +54 -0
- package/dist/services/workspace/ensure/defaults.js +60 -0
- package/dist/services/workspace/ensure/env-keys.js +55 -0
- package/dist/services/workspace/ensure/env-order.js +66 -0
- package/dist/services/workspace/ensure/env-rules.js +97 -0
- package/dist/services/workspace/ensure/gitignore.js +18 -0
- package/dist/services/workspace/ensure/scripts.js +126 -0
- package/dist/services/workspace/ensure/structure.js +97 -0
- package/dist/services/workspace/ensure/translation-sdk.js +148 -0
- package/dist/services/workspace/ensure/workflows.js +54 -0
- package/dist/services/workspace/index.js +8 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/flows.js +317 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/translate/cache.js +84 -0
- package/dist/vendor/devkit/translate/engine.js +243 -0
- package/dist/vendor/devkit/translate/index.js +49 -0
- package/dist/vendor/devkit/translate/languages.js +134 -0
- package/dist/vendor/devkit/translate/providers.js +188 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/docs/AGENTS.md +200 -0
- package/docs/account.md +45 -0
- package/docs/advertising.md +46 -0
- package/docs/ai.md +34 -0
- package/docs/analytics.md +69 -0
- package/docs/assets.md +57 -0
- package/docs/bookmark.md +26 -0
- package/docs/brand.md +151 -0
- package/docs/campaigns.md +96 -0
- package/docs/captcha.md +46 -0
- package/docs/certificates.md +68 -0
- package/docs/chat.md +47 -0
- package/docs/cloud.md +110 -0
- package/docs/company.md +64 -0
- package/docs/directory.md +140 -0
- package/docs/disperse.md +46 -0
- package/docs/domain.md +56 -0
- package/docs/edge.md +234 -0
- package/docs/email.md +42 -0
- package/docs/forms.md +48 -0
- package/docs/index.md +109 -0
- package/docs/migration.md +203 -0
- package/docs/migrations.md +56 -0
- package/docs/monitoring.md +41 -0
- package/docs/newsletter.md +48 -0
- package/docs/payment.md +83 -0
- package/docs/repo.md +48 -0
- package/docs/search.md +44 -0
- package/docs/seo.md +34 -0
- package/docs/server.md +37 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/testing.md +34 -0
- package/docs/update.md +36 -0
- package/docs/workspace.md +64 -0
- package/package.json +88 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand-monorepo scaffolding — the file plan the onboard wizard writes.
|
|
3
|
+
*
|
|
4
|
+
* buildScaffoldPlan() turns the wizard's answers into the plan-§0 skeleton:
|
|
5
|
+
* config/omega.json5, root package.json (targets/* workspaces), .gitignore, the
|
|
6
|
+
* .env credential stub, README.md, and a minimal package.json per enabled
|
|
7
|
+
* target's own dir. applyScaffoldPlan() writes it with fill-missing
|
|
8
|
+
* semantics: existing files are NEVER touched, so onboarding is idempotent
|
|
9
|
+
* and re-running it into a partial brand only fills the gaps.
|
|
10
|
+
*
|
|
11
|
+
* Target package.jsons carry their framework dep, so install → setup works
|
|
12
|
+
* without hand-editing; each framework's own setup still owns the consumer
|
|
13
|
+
* INTERIOR (scripts, config, scaffolded files). The backend's framework is a
|
|
14
|
+
* RUNTIME dependency (it rides the staged dist/package.json — src/dist
|
|
15
|
+
* pillar); every other target declares its framework as a devDependency
|
|
16
|
+
* (build-time only).
|
|
17
|
+
*
|
|
18
|
+
* Every `@omega.js/*` spec is an EXACT PIN at the manager's own version
|
|
19
|
+
* (#794): the family ships lockstep, one number for the whole set, so a
|
|
20
|
+
* brand can never install a backend from one release beside a client from
|
|
21
|
+
* another. A caret (or the old `*`) let one target float ahead alone on an
|
|
22
|
+
* `npm update`; pinned, only `omega update --apply` at the brand root moves it,
|
|
23
|
+
* and it moves every target together. The local era is untouched — an
|
|
24
|
+
* existing `file:` spec is never rewritten, because applyScaffoldPlan()
|
|
25
|
+
* leaves every file that already exists exactly as it is.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const jetpack = require('fs-jetpack');
|
|
30
|
+
const chalk = require('chalk').default;
|
|
31
|
+
|
|
32
|
+
const { TARGET_DIRS, TARGET_FRAMEWORKS } = require('../config.js');
|
|
33
|
+
// The environment vocabulary is @omega.js/config's — the same three names the
|
|
34
|
+
// overlay files are suffixed with and every runtime's environment() answers
|
|
35
|
+
const { ENV_ENVIRONMENTS } = require('../vendor/config/index.js');
|
|
36
|
+
// The canonical group list + renderer live in env-order.js (the ordering
|
|
37
|
+
// SSOT, cp137) — the stub is just a canonical render with generated Omega
|
|
38
|
+
// keys, so a scaffolded .env and a reordered one have the same shape.
|
|
39
|
+
const { renderCanonicalEnv, envLine } = require('./env-order.js');
|
|
40
|
+
// What gets generated (the env schema's `generated` entries) — with
|
|
41
|
+
// env-order.js's serializer above, the same two SSOTs writeEnvValue rides.
|
|
42
|
+
const { generatedEnvKeys } = require('../vendor/config/index.js');
|
|
43
|
+
// The heal's value is the SSOT for the manage script — a scaffolded brand
|
|
44
|
+
// must never be born needing the migration the heal just learned (#229)
|
|
45
|
+
const { MANAGE_SCRIPT } = require('./package-scripts.js');
|
|
46
|
+
|
|
47
|
+
// The backend framework is a Cloud Functions RUNTIME dependency — the stage
|
|
48
|
+
// step derives dist/package.json from the target manifest's `dependencies`
|
|
49
|
+
// (src/dist pillar). Every other target's framework is build-time only.
|
|
50
|
+
const RUNTIME_DEP_TARGETS = ['backend'];
|
|
51
|
+
|
|
52
|
+
// The pin every scaffolded @omega.js/* spec carries — the manager's OWN
|
|
53
|
+
// version, read at run time (#794). The family releases lockstep, so the
|
|
54
|
+
// manager's number IS the family's number; nothing here keeps a copy of it.
|
|
55
|
+
const FAMILY_VERSION = require('../../package.json').version;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Render the brand-level config/omega.json5 (fresh brands only — an existing
|
|
59
|
+
* config is never regenerated). String values go through JSON.stringify, so
|
|
60
|
+
* user input can't break the file.
|
|
61
|
+
*/
|
|
62
|
+
function renderOmegaConfig(answers) {
|
|
63
|
+
const lines = [
|
|
64
|
+
`// ${answers.name} — brand-level omega.json5: the shared config layer every target`,
|
|
65
|
+
'// under targets/ inherits. Local files override any key per-surface; key presence',
|
|
66
|
+
'// under `targets` = this brand supports that target. Secrets NEVER live here —',
|
|
67
|
+
'// they go in the gitignored .env (the loader hard-fails on secret-shaped keys).',
|
|
68
|
+
'{',
|
|
69
|
+
' brand: {',
|
|
70
|
+
` id: ${JSON.stringify(answers.id)},`,
|
|
71
|
+
` name: ${JSON.stringify(answers.name)},`,
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
if (answers.description) {
|
|
75
|
+
lines.push(` description: ${JSON.stringify(answers.description)},`);
|
|
76
|
+
}
|
|
77
|
+
if (answers.tagline) {
|
|
78
|
+
lines.push(` tagline: ${JSON.stringify(answers.tagline)},`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
lines.push(
|
|
82
|
+
` url: ${JSON.stringify(answers.url)},`,
|
|
83
|
+
' contact: {',
|
|
84
|
+
` email: ${JSON.stringify(answers.email)},`,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// The human who signs the personal sends (welcome, nudge, checkup) — asked
|
|
88
|
+
// for at onboarding, never derived, so an unanswered person writes no key
|
|
89
|
+
// at all instead of a placeholder identity (#770).
|
|
90
|
+
if (answers.person) {
|
|
91
|
+
lines.push(' person: {');
|
|
92
|
+
for (const [key, value] of Object.entries(answers.person)) {
|
|
93
|
+
lines.push(` ${key}: ${JSON.stringify(value)},`);
|
|
94
|
+
}
|
|
95
|
+
lines.push(' },');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lines.push(
|
|
99
|
+
' },',
|
|
100
|
+
' },',
|
|
101
|
+
'',
|
|
102
|
+
' // Social handles (platform: "handle") — each entry lights its footer icon, its JSON-LD sameAs entry, and a /<platform> shortlink.',
|
|
103
|
+
' socials: {},',
|
|
104
|
+
'',
|
|
105
|
+
' // Project-owned theme — seeded at onboarding, yours to change.',
|
|
106
|
+
' theme: {',
|
|
107
|
+
' id: "classy",',
|
|
108
|
+
' appearance: "system", // "system" | "light" | "dark"',
|
|
109
|
+
' },',
|
|
110
|
+
'',
|
|
111
|
+
' // AI translation (web /{lang}/ pages, extension _locales). Provider',
|
|
112
|
+
' // "claude" (default) rides the local Claude Code install — no API key;',
|
|
113
|
+
' // "chatgpt" needs OPENAI_API_KEY in .env. Uncomment to enable:',
|
|
114
|
+
' // translation: {',
|
|
115
|
+
' // languages: ["es", "fr", "de"],',
|
|
116
|
+
' // },',
|
|
117
|
+
'',
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// Only a CUSTOMIZED admin list lands here — an inherited one (company
|
|
121
|
+
// config or the built-in support@{domain} default) stays unwritten so the
|
|
122
|
+
// source layer keeps owning it.
|
|
123
|
+
if (answers.accountAdmins) {
|
|
124
|
+
lines.push(
|
|
125
|
+
' // Managed Firebase Auth accounts (account service). Passwords never live',
|
|
126
|
+
' // here — OMEGA_ACCOUNT_PASSWORD__* env vars, config/hooks/account/password.js,',
|
|
127
|
+
' // or the generated ACCOUNT_PASSWORD_SEED.',
|
|
128
|
+
' account: {',
|
|
129
|
+
' admins: [',
|
|
130
|
+
);
|
|
131
|
+
for (const entry of answers.accountAdmins) {
|
|
132
|
+
lines.push(` { email: ${JSON.stringify(entry.email)}, account: ${!!entry.account}, marketing: ${!!entry.marketing} },`);
|
|
133
|
+
}
|
|
134
|
+
lines.push(' ],', ' },', '');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Backend brands get a bootable cloud project out of the box: demo-* ids
|
|
138
|
+
// are the emulator-only convention (never touch live Firebase), so the
|
|
139
|
+
// emulator boots before any real project exists (dogfood friction #3).
|
|
140
|
+
if (answers.targets.includes('backend')) {
|
|
141
|
+
lines.push(
|
|
142
|
+
' // Cloud project (backend target). demo-* ids are EMULATOR-ONLY — the',
|
|
143
|
+
' // emulators boot against this immediately; swap in a real Firebase project',
|
|
144
|
+
' // id at launch (the cloud service can create one).',
|
|
145
|
+
' cloud: {',
|
|
146
|
+
' provider: "firebase",',
|
|
147
|
+
' config: {',
|
|
148
|
+
` projectId: ${JSON.stringify(`demo-${answers.id}`)},`,
|
|
149
|
+
' },',
|
|
150
|
+
' },',
|
|
151
|
+
'',
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
lines.push(
|
|
156
|
+
' // Payment catalog — pricing pages render THESE products (no products =',
|
|
157
|
+
' // honest empty state). Uncomment + edit to start selling; ids are',
|
|
158
|
+
' // permanent once live. Provider id fields (stripe/paypal/chargebee) are',
|
|
159
|
+
' // filled by the payment service — leave them null. Presentation fields',
|
|
160
|
+
' // (tagline, popular, features) are optional card garnish.',
|
|
161
|
+
' // payment: {',
|
|
162
|
+
' // products: [',
|
|
163
|
+
' // {',
|
|
164
|
+
' // id: "premium",',
|
|
165
|
+
' // name: "Premium",',
|
|
166
|
+
' // type: "subscription",',
|
|
167
|
+
' // tagline: "best for teams",',
|
|
168
|
+
' // popular: true,',
|
|
169
|
+
' // prices: { monthly: 9.99, annually: 99.99 },',
|
|
170
|
+
' // trial: { days: 14 },',
|
|
171
|
+
' // limits: { requests: 10000 },',
|
|
172
|
+
' // features: [{ id: "requests", name: "Requests", icon: "sparkles" }],',
|
|
173
|
+
' // },',
|
|
174
|
+
' // { id: "launch-kit", name: "Launch Kit", type: "one-time", prices: { once: 49.99 } },',
|
|
175
|
+
' // ],',
|
|
176
|
+
' // },',
|
|
177
|
+
'',
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// App-signing brands get their reverse-DNS bundle prefix derived at
|
|
181
|
+
// onboarding (parent company's domain when one exists, else the brand's)
|
|
182
|
+
if (answers.targets.includes('desktop') && answers.bundleIdPrefix) {
|
|
183
|
+
lines.push(
|
|
184
|
+
' // Apple signing (certificates service): reverse-DNS prefix derived from',
|
|
185
|
+
' // the company/brand domain at onboarding. Bundle IDs mint as',
|
|
186
|
+
' // <prefix>.<brand id with dashes as dots>.',
|
|
187
|
+
' certificates: {',
|
|
188
|
+
' providers: {',
|
|
189
|
+
' apple: {',
|
|
190
|
+
` bundleIdPrefix: ${JSON.stringify(answers.bundleIdPrefix)},`,
|
|
191
|
+
' },',
|
|
192
|
+
' },',
|
|
193
|
+
' },',
|
|
194
|
+
'',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
lines.push(
|
|
199
|
+
' // Key presence = target enabled; the value is that target\'s type-wide',
|
|
200
|
+
' // config (any shared key inside overrides it for that surface).',
|
|
201
|
+
' targets: {',
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
for (const target of answers.targets) {
|
|
205
|
+
lines.push(` ${target}: {},`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
lines.push(' },', '}');
|
|
209
|
+
|
|
210
|
+
return `${lines.join('\n')}\n`;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function renderRootPackageJson(answers) {
|
|
214
|
+
return `${JSON.stringify({
|
|
215
|
+
name: answers.id,
|
|
216
|
+
private: true,
|
|
217
|
+
...(answers.description ? { description: answers.description } : {}),
|
|
218
|
+
workspaces: ['targets/*'],
|
|
219
|
+
// npm scripts put node_modules/.bin on PATH, so plain `omega` (the
|
|
220
|
+
// context-aware dispatcher) resolves — never the retired omega-manager name
|
|
221
|
+
// `npm start` boots the dev stack (`dev` stays as its alias); the bare
|
|
222
|
+
// manage cycle is `npm run manage`
|
|
223
|
+
scripts: {
|
|
224
|
+
start: 'omega dev',
|
|
225
|
+
dev: 'omega dev',
|
|
226
|
+
manage: MANAGE_SCRIPT,
|
|
227
|
+
deploy: 'omega deploy',
|
|
228
|
+
},
|
|
229
|
+
// Brand-level verbs (`omega dev`, manage, the `start` script above) resolve
|
|
230
|
+
// @omega.js/manager FROM THE BRAND ROOT (omega-bin dispatch) — without this
|
|
231
|
+
// declaration nothing installs it outside the monorepo and every brand-root
|
|
232
|
+
// command dies (cp195 journey catch). Pinned exactly: the root and every
|
|
233
|
+
// target ride ONE family version (#794).
|
|
234
|
+
devDependencies: {
|
|
235
|
+
'@omega.js/manager': FAMILY_VERSION,
|
|
236
|
+
},
|
|
237
|
+
}, null, 2)}\n`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function renderGitignore() {
|
|
241
|
+
return [
|
|
242
|
+
'# Dependencies',
|
|
243
|
+
'node_modules/',
|
|
244
|
+
'',
|
|
245
|
+
'# Build output',
|
|
246
|
+
'dist/',
|
|
247
|
+
'',
|
|
248
|
+
'# OMEGA manager state (derived data — never committed)',
|
|
249
|
+
'.omega/',
|
|
250
|
+
'',
|
|
251
|
+
'# Run logs (truncated on every launch — never committed)',
|
|
252
|
+
'logs/',
|
|
253
|
+
'',
|
|
254
|
+
'# Secrets',
|
|
255
|
+
'.env',
|
|
256
|
+
'.env.*',
|
|
257
|
+
'',
|
|
258
|
+
'# OS',
|
|
259
|
+
'.DS_Store',
|
|
260
|
+
'',
|
|
261
|
+
].join('\n');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function renderEnvStub(answers) {
|
|
265
|
+
return renderCanonicalEnv({
|
|
266
|
+
header: [
|
|
267
|
+
`# ${answers.name} — brand secrets (gitignored; loaded before every omega run).`,
|
|
268
|
+
'# Uncomment and fill what this brand uses. Services without their credentials',
|
|
269
|
+
'# skip cleanly, so add these as the brand adopts each service.',
|
|
270
|
+
],
|
|
271
|
+
// The generated keys and their line form come from the SSOTs the
|
|
272
|
+
// manage-time mint uses too (#569), so a brand born here and a brand
|
|
273
|
+
// healed by the env-keys op carry byte-identical shapes.
|
|
274
|
+
entries: new Map(Object.entries(generatedEnvKeys()).map(([key, generate]) => [key, { raw: envLine(key, generate()) }])),
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* An environment overlay stub — a header comment and NOTHING else
|
|
280
|
+
* ([#586](https://github.com/Omega-JS-Stack/omega/issues/586)). The base `.env`
|
|
281
|
+
* stays the one file that documents every key (it carries the canonical
|
|
282
|
+
* placeholder list); an overlay only ever holds the handful a brand wants
|
|
283
|
+
* different for one environment, so pre-listing anything here would be a second
|
|
284
|
+
* inventory to drift.
|
|
285
|
+
*
|
|
286
|
+
* @param {string} environment - `development` | `testing` | `production`.
|
|
287
|
+
* @returns {string} The file contents.
|
|
288
|
+
*/
|
|
289
|
+
function renderEnvOverlayStub(environment) {
|
|
290
|
+
return [
|
|
291
|
+
`# .env.${environment} — overlays .env when this brand runs in ${environment} (gitignored).`,
|
|
292
|
+
'# Only the keys that differ — anything not set here falls through to .env.',
|
|
293
|
+
'',
|
|
294
|
+
].join('\n');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function renderReadme(answers) {
|
|
298
|
+
const targetList = answers.targets
|
|
299
|
+
.map((target) => {
|
|
300
|
+
const dir = TARGET_DIRS[target] || target;
|
|
301
|
+
const framework = TARGET_FRAMEWORKS[target];
|
|
302
|
+
return `- \`targets/${dir}/\` — the ${target} target${framework ? ` (framework: \`${framework}\`)` : ''}`;
|
|
303
|
+
})
|
|
304
|
+
.join('\n');
|
|
305
|
+
|
|
306
|
+
return `# ${answers.name}
|
|
307
|
+
|
|
308
|
+
${answers.description || 'An OMEGA brand monorepo.'}
|
|
309
|
+
|
|
310
|
+
One repo, every surface of the brand. \`config/omega.json5\` is the single
|
|
311
|
+
source of user choices; \`omega\` reconciles every external service
|
|
312
|
+
to it, idempotently.
|
|
313
|
+
|
|
314
|
+
## Structure
|
|
315
|
+
|
|
316
|
+
- \`config/omega.json5\` — brand-level shared config (targets inherit + override)
|
|
317
|
+
${targetList}
|
|
318
|
+
- \`.env\` — credentials (gitignored; see the stub for every service's keys)
|
|
319
|
+
- \`.env.<environment>\` — the per-environment overlay (\`development\`, \`testing\`, \`production\`): only the keys that differ there
|
|
320
|
+
- \`.omega/\` — manager state + run output (gitignored, machine-owned)
|
|
321
|
+
|
|
322
|
+
## Next steps
|
|
323
|
+
|
|
324
|
+
1. \`npm install\` — each target declares its framework (workspace link in a
|
|
325
|
+
monorepo; standalone pre-publish: \`npx mgr i local\` inside each target).
|
|
326
|
+
2. Fill in \`.env\` as the brand adopts external services.
|
|
327
|
+
3. \`npm run manage\` — reconcile everything; rerun any time.
|
|
328
|
+
4. \`npm start\` (\`npx omega dev\`) — boot the local stack; each target's verbs
|
|
329
|
+
scaffold its consumer interior on first run.
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function renderTargetPackageJson(answers, target, dir) {
|
|
334
|
+
const framework = TARGET_FRAMEWORKS[target];
|
|
335
|
+
// The exact family pin (#794): satisfied by a workspace link in-monorepo,
|
|
336
|
+
// `mgr i local` pre-publish (which flips the spec to `file:`), and the npm
|
|
337
|
+
// registry once @omega.js/* publish — where the pin is what keeps every
|
|
338
|
+
// target on ONE framework version.
|
|
339
|
+
const depKey = RUNTIME_DEP_TARGETS.includes(target) ? 'dependencies' : 'devDependencies';
|
|
340
|
+
|
|
341
|
+
// version + author: electron-builder hard-requires version and warns on
|
|
342
|
+
// author (the cp142 rehearsal catch) — every target gets both, they're healthy
|
|
343
|
+
return `${JSON.stringify({
|
|
344
|
+
name: `${answers.id}-${dir}`,
|
|
345
|
+
version: '0.0.1',
|
|
346
|
+
author: answers.name,
|
|
347
|
+
private: true,
|
|
348
|
+
description: `${answers.name} ${target} target`,
|
|
349
|
+
...(framework ? { [depKey]: { [framework]: FAMILY_VERSION } } : {}),
|
|
350
|
+
}, null, 2)}\n`;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Build the scaffold plan for a brand.
|
|
355
|
+
*
|
|
356
|
+
* @param {Object} answers - { id, name, description, tagline, url, email, person, targets }
|
|
357
|
+
* @returns {Array<{ path: string, contents: string }>} - Brand-root-relative file plan
|
|
358
|
+
*/
|
|
359
|
+
function buildScaffoldPlan(answers) {
|
|
360
|
+
const plan = [
|
|
361
|
+
{ path: 'config/omega.json5', contents: renderOmegaConfig(answers) },
|
|
362
|
+
{ path: 'package.json', contents: renderRootPackageJson(answers) },
|
|
363
|
+
{ path: '.gitignore', contents: renderGitignore() },
|
|
364
|
+
{ path: '.env', contents: renderEnvStub(answers) },
|
|
365
|
+
...ENV_ENVIRONMENTS.map((environment) => ({ path: `.env.${environment}`, contents: renderEnvOverlayStub(environment) })),
|
|
366
|
+
{ path: 'README.md', contents: renderReadme(answers) },
|
|
367
|
+
];
|
|
368
|
+
|
|
369
|
+
for (const target of answers.targets) {
|
|
370
|
+
const dir = TARGET_DIRS[target] || target;
|
|
371
|
+
plan.push({ path: `targets/${dir}/package.json`, contents: renderTargetPackageJson(answers, target, dir) });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return plan;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Write a scaffold plan with fill-missing semantics: files that already
|
|
379
|
+
* exist are never touched (rerunning onboard converges instead of clobbering).
|
|
380
|
+
*
|
|
381
|
+
* @param {string} brandRoot - Absolute brand root to scaffold into
|
|
382
|
+
* @param {Array<{ path, contents }>} plan - From buildScaffoldPlan()
|
|
383
|
+
* @param {Object} [options] - { dryRun } — plan only, zero writes
|
|
384
|
+
* @returns {{ created: string[], kept: string[], planned: string[] }}
|
|
385
|
+
*/
|
|
386
|
+
function applyScaffoldPlan(brandRoot, plan, { dryRun = false } = {}) {
|
|
387
|
+
const created = [];
|
|
388
|
+
const kept = [];
|
|
389
|
+
const planned = [];
|
|
390
|
+
|
|
391
|
+
for (const file of plan) {
|
|
392
|
+
const destination = path.join(brandRoot, file.path);
|
|
393
|
+
|
|
394
|
+
if (jetpack.exists(destination)) {
|
|
395
|
+
kept.push(file.path);
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (dryRun) {
|
|
400
|
+
planned.push(file.path);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
jetpack.write(destination, file.contents);
|
|
405
|
+
created.push(file.path);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return { created, kept, planned };
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Print what a plan did — the one rendering of created/kept/planned, shared
|
|
413
|
+
* by every scaffolding verb (onboard's brand plan, company init's).
|
|
414
|
+
*/
|
|
415
|
+
function printPlanResults({ created, kept, planned }) {
|
|
416
|
+
for (const file of planned) {
|
|
417
|
+
console.log(` ${chalk.dim('⊘')} would create ${chalk.cyan(file)}`);
|
|
418
|
+
}
|
|
419
|
+
for (const file of created) {
|
|
420
|
+
console.log(` ${chalk.green('✓')} created ${chalk.cyan(file)}`);
|
|
421
|
+
}
|
|
422
|
+
for (const file of kept) {
|
|
423
|
+
console.log(` ${chalk.dim('•')} kept ${chalk.dim(file)} ${chalk.dim('(exists)')}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
module.exports = { buildScaffoldPlan, applyScaffoldPlan, printPlanResults };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE setup contract every service asks its inputs through (#608, Ian
|
|
3
|
+
* 2026-08-25) — the env-side twin of config-flow's resolveConfigValue, and the
|
|
4
|
+
* successor to cp114's two-outcome ensureEnvSecrets.
|
|
5
|
+
*
|
|
6
|
+
* When a service runs and an input it needs is missing, it asks RIGHT THEN,
|
|
7
|
+
* with the same three outcomes everywhere:
|
|
8
|
+
*
|
|
9
|
+
* Provide — the Enter-gated open of the exact page that mints
|
|
10
|
+
* the value, a masked paste, persisted to the brand
|
|
11
|
+
* .env (lib/env-secret.js) and exported for THIS run,
|
|
12
|
+
* so a fresh brand configures itself mid-walk.
|
|
13
|
+
* Skip for now — this run steps aside; the next one asks again.
|
|
14
|
+
* Disable permanently — `<service>.enabled: false` lands in omega.json5
|
|
15
|
+
* (the tri-state opt-out, #33) and nothing ever asks
|
|
16
|
+
* again until the line is deleted.
|
|
17
|
+
*
|
|
18
|
+
* Non-interactive runs (CI, a piped `omega dev` boot, `--dry-run`) NEVER
|
|
19
|
+
* prompt: the missing keys print as a loud skip line and ride back as the
|
|
20
|
+
* machine-readable `missingEnv` list the run summary's 🔑 section aggregates.
|
|
21
|
+
*
|
|
22
|
+
* One class of input is never PASTED: a key OMEGA mints for ITSELF (the env
|
|
23
|
+
* schema's `generated:` set) has nobody to ask, so it is minted in place and
|
|
24
|
+
* never appears in `missingEnv` (#635). When it is the only thing missing no
|
|
25
|
+
* gate opens at all, headless runs included; when a pasted key is missing
|
|
26
|
+
* beside it the gate runs FIRST and the mint happens only past it, so
|
|
27
|
+
* "Disable permanently" never leaves a minted secret behind.
|
|
28
|
+
*
|
|
29
|
+
* WHAT each service needs is the REQUIRES registry's to say (src/config.js —
|
|
30
|
+
* one home, checked up front by lib/preflight.js and asked for here through
|
|
31
|
+
* `serviceInputSpec(name)`); the env schema (@omega.js/config, #581) is what
|
|
32
|
+
* says which of those keys OMEGA mints for itself and which a human acquires,
|
|
33
|
+
* and the sweep test holds the registry to it.
|
|
34
|
+
*
|
|
35
|
+
* Values are never printed — names, labels and mint URLs only.
|
|
36
|
+
*/
|
|
37
|
+
const chalk = require('chalk').default;
|
|
38
|
+
|
|
39
|
+
const { generatedEnvKeys } = require('../vendor/config/index.js');
|
|
40
|
+
const { canPrompt, dryRunPlan } = require('./run-gates.js');
|
|
41
|
+
const { writeEnvValue, mintGeneratedKey } = require('./env-secret.js');
|
|
42
|
+
const { confirmSetup, readTriState } = require('./config-flow.js');
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Ask for a service's missing inputs, with the three-outcome gate.
|
|
46
|
+
*
|
|
47
|
+
* @param {object} context - Service context ({ brandRoot, brandConfig, brandId, options }).
|
|
48
|
+
* @param {object} spec - The input spec (serviceInputSpec(name) builds one from
|
|
49
|
+
* the REQUIRES registry):
|
|
50
|
+
* @param {string} spec.service - Service name (names the rerun hint).
|
|
51
|
+
* @param {string} spec.label - Human name the gate opens with ("Cloudflare").
|
|
52
|
+
* @param {string} spec.disablePath - Where "Disable permanently" writes `false`.
|
|
53
|
+
* @param {string[]} [spec.instructions] - Guidance lines shown before the gate.
|
|
54
|
+
* @param {boolean} [spec.gate] - false = the caller already ran the gate.
|
|
55
|
+
* @param {Array<{ name, label?, url?, hint?, when? }>} spec.inputs - The env
|
|
56
|
+
* vars the service needs; `when(brandConfig)` drops the ones this brand
|
|
57
|
+
* doesn't, `url` is the page that mints one, `hint` says what to make there.
|
|
58
|
+
* @param {object} [deps] - Test seam: { prompt } overrides devkit/prompt members.
|
|
59
|
+
* @returns {Promise<object|null>} null to proceed, or the setup skip shape
|
|
60
|
+
* ({ skip, reason, missingEnv, disabled? }).
|
|
61
|
+
*/
|
|
62
|
+
async function requestServiceInput(context, spec, deps = {}) {
|
|
63
|
+
const { brandConfig = {}, options = {} } = context;
|
|
64
|
+
|
|
65
|
+
const wanted = spec.inputs.filter((input) => !input.when || input.when(brandConfig));
|
|
66
|
+
const missing = wanted.filter((input) => !process.env[input.name]);
|
|
67
|
+
if (missing.length === 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Already opted out (#33): the value — or any ancestor section — is `false`.
|
|
72
|
+
// Every service gates on its own key too; this is the backstop that keeps a
|
|
73
|
+
// disabled service from ever reaching a prompt.
|
|
74
|
+
if (readTriState(brandConfig, spec.disablePath).optedOut) {
|
|
75
|
+
return {
|
|
76
|
+
skip: true,
|
|
77
|
+
reason: `${spec.disablePath} is disabled — delete the line in omega.json5 to be asked again`,
|
|
78
|
+
missingEnv: missing.map((input) => input.name),
|
|
79
|
+
disabled: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// A key OMEGA mints for itself (the env schema's `generated:` set, #581) is
|
|
84
|
+
// MINTED, not pasted: there is nobody to ask for it, so it never joins the
|
|
85
|
+
// 🔑 "add these to your .env" list — the two kinds of missing input split
|
|
86
|
+
// here and are handled apart (#635).
|
|
87
|
+
const generated = generatedEnvKeys();
|
|
88
|
+
const mintable = missing.filter((input) => generated[input.name]);
|
|
89
|
+
const pending = missing.filter((input) => !generated[input.name]);
|
|
90
|
+
|
|
91
|
+
const mintAll = () => {
|
|
92
|
+
for (const input of mintable) {
|
|
93
|
+
mintGeneratedKey(context.brandRoot, input.name, { indent: ' ' });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// A dry run says what a real run would mint and writes nothing, exactly as
|
|
98
|
+
// the workspace env-keys op does.
|
|
99
|
+
if (options.dryRun) {
|
|
100
|
+
for (const input of mintable) {
|
|
101
|
+
dryRunPlan(`mint ${input.name} into the brand .env`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Nothing a human could supply is missing: mint and proceed, no gate, on a
|
|
106
|
+
// headless run too.
|
|
107
|
+
if (pending.length === 0) {
|
|
108
|
+
if (options.dryRun) {
|
|
109
|
+
// Named, but NOT as missingEnv — telling someone to paste a key only
|
|
110
|
+
// OMEGA can produce would be a lie the 🔑 section then repeats.
|
|
111
|
+
return {
|
|
112
|
+
skip: true,
|
|
113
|
+
reason: `${mintable.map((input) => input.name).join(', ')} will be minted on a real run`,
|
|
114
|
+
missingEnv: [],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
mintAll();
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const names = pending.map((input) => input.name);
|
|
123
|
+
|
|
124
|
+
if (!canPrompt(options)) {
|
|
125
|
+
// Loud: a run that cannot ask still says exactly which keys it wants and
|
|
126
|
+
// where they go, instead of a bare "skipped".
|
|
127
|
+
console.log(` ${chalk.yellow('🔑')} ${spec.label} needs ${chalk.bold(names.join(', '))} ${chalk.dim('— not in the brand .env')}`);
|
|
128
|
+
for (const input of pending) {
|
|
129
|
+
if (input.url) {
|
|
130
|
+
console.log(` ${chalk.dim(`→ ${input.name}: mint it at ${input.url}`)}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
skip: true,
|
|
136
|
+
reason: `missing ${names.join(', ')} — add to the brand .env, or rerun interactively to paste`,
|
|
137
|
+
missingEnv: names,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// The uniform gate — its wording lives once, in config-flow's confirmSetup,
|
|
142
|
+
// and its Disable lands the tri-state `false` for us. `gate: false` is the
|
|
143
|
+
// chained ask (resolveConfigValue's convention): the caller already opened
|
|
144
|
+
// this flow's gate, so asking twice would be the same question.
|
|
145
|
+
const action = spec.gate === false
|
|
146
|
+
? 'yes'
|
|
147
|
+
: await confirmSetup(context, {
|
|
148
|
+
label: spec.label,
|
|
149
|
+
instructions: spec.instructions,
|
|
150
|
+
disablePath: spec.disablePath,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
if (action === 'disable') {
|
|
154
|
+
return {
|
|
155
|
+
skip: true,
|
|
156
|
+
reason: `${spec.disablePath} disabled in omega.json5 — delete the line to be asked again`,
|
|
157
|
+
missingEnv: names,
|
|
158
|
+
disabled: true,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (action !== 'yes') {
|
|
163
|
+
return {
|
|
164
|
+
skip: true,
|
|
165
|
+
reason: `skipped this run — ${spec.service} still needs ${names.join(', ')}`,
|
|
166
|
+
missingEnv: names,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Past the gate: only NOW are OMEGA's own keys minted, so a "Disable
|
|
171
|
+
// permanently" never leaves a freshly minted secret in a brand that just
|
|
172
|
+
// said it wants none of this.
|
|
173
|
+
mintAll();
|
|
174
|
+
|
|
175
|
+
const prompt = { ...require('../vendor/devkit/prompt.js'), ...deps.prompt };
|
|
176
|
+
|
|
177
|
+
for (const input of pending) {
|
|
178
|
+
const label = input.label || input.name;
|
|
179
|
+
if (input.hint) {
|
|
180
|
+
console.log(` ${chalk.dim(input.hint)}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// The exact-page guide: Enter opens the page that mints the value
|
|
184
|
+
// (pressEnterToOpen always prints the URL, so it stays clickable when the
|
|
185
|
+
// user would rather not open a browser).
|
|
186
|
+
if (input.url) {
|
|
187
|
+
await prompt.pressEnterToOpen(input.url, `the ${label} page`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const value = (await prompt.password({ message: `Paste ${input.name}:` }) || '').trim();
|
|
191
|
+
if (!value) {
|
|
192
|
+
return {
|
|
193
|
+
skip: true,
|
|
194
|
+
reason: `nothing entered for ${input.name} — ${spec.service} still needs ${names.join(', ')}`,
|
|
195
|
+
missingEnv: names,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
writeEnvValue(context.brandRoot, input.name, value);
|
|
200
|
+
process.env[input.name] = value;
|
|
201
|
+
console.log(` ${chalk.green('✓')} ${input.name} saved to the brand .env`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
module.exports = { requestServiceInput };
|