@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
package/dist/onboard.js
ADDED
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `omega onboard` — the brand-creation wizard: collect the brand's
|
|
3
|
+
* identity (flags win, prompts fill the gaps in a TTY, derivation covers the
|
|
4
|
+
* rest), scaffold the plan-§0 brand-monorepo skeleton, prove the config
|
|
5
|
+
* loads, then optionally hand off to manage.
|
|
6
|
+
*
|
|
7
|
+
* Context-sensitive on where it runs, mirroring manage:
|
|
8
|
+
* inside a company workspace → the new brand lands under brands.roots[0]
|
|
9
|
+
* (or a selected root) + gets the company stamp
|
|
10
|
+
* inside an existing brand → resume: no wizard, answers come from the
|
|
11
|
+
* brand's own config, only MISSING files fill in
|
|
12
|
+
* anywhere else → in-place: the cwd becomes the brand root
|
|
13
|
+
* (the "Use this template" story — clone, onboard)
|
|
14
|
+
*
|
|
15
|
+
* Non-interactive runs derive everything derivable from --id (name, url,
|
|
16
|
+
* email) and use the web+backend target default; only a missing, underivable
|
|
17
|
+
* id is an error. --dry-run never prompts and never writes — it prints the
|
|
18
|
+
* file plan. Scaffolding never overwrites: rerunning onboard converges.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const { spawn, spawnSync } = require('node:child_process');
|
|
23
|
+
const chalk = require('chalk').default;
|
|
24
|
+
const jetpack = require('fs-jetpack');
|
|
25
|
+
|
|
26
|
+
const { input, select, checkbox, confirm } = require('./vendor/devkit/prompt.js');
|
|
27
|
+
const { TARGETS, hasOmegaConfig } = require('./vendor/config/index.js');
|
|
28
|
+
|
|
29
|
+
const { TARGET_DIRS, TARGET_FRAMEWORKS, DEFAULTS } = require('./config.js');
|
|
30
|
+
const { DEFAULT_BRAND_ROOTS, resolveManageRoot, readRawConfig, stampCompanyMarker } = require('./lib/company.js');
|
|
31
|
+
const { loadBrand } = require('./lib/brand.js');
|
|
32
|
+
const { buildScaffoldPlan, applyScaffoldPlan, printPlanResults } = require('./lib/scaffold.js');
|
|
33
|
+
const { canPrompt } = require('./lib/run-gates.js');
|
|
34
|
+
const { deriveBundleIdPrefix } = require('./lib/bundle-id.js');
|
|
35
|
+
const { convertLegacyOAuthSecret } = require('./lib/legacy-oauth.js');
|
|
36
|
+
|
|
37
|
+
// New-brand ids are a conservative subset of the schema's brand.id pattern —
|
|
38
|
+
// always dir-, repo-, and URL-scheme-safe.
|
|
39
|
+
const ID_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
40
|
+
const ID_HINT = 'lowercase letters, numbers, dashes; starts with a letter';
|
|
41
|
+
|
|
42
|
+
const DEFAULT_TARGETS = ['web', 'backend'];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Derive a valid brand id from a directory name ('My Brand 2' → 'my-brand-2');
|
|
46
|
+
* null when nothing valid survives.
|
|
47
|
+
*/
|
|
48
|
+
function deriveId(dirName) {
|
|
49
|
+
const id = String(dirName)
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
52
|
+
.replace(/-+/g, '-')
|
|
53
|
+
.replace(/^[^a-z]+|-+$/g, '');
|
|
54
|
+
|
|
55
|
+
return ID_PATTERN.test(id) ? id : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 'acme-demo' → 'Acme Demo' */
|
|
59
|
+
function deriveName(id) {
|
|
60
|
+
return id
|
|
61
|
+
.split('-')
|
|
62
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
63
|
+
.join(' ');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** omega-manager's onboarding default: dashes drop out of the domain. */
|
|
67
|
+
function deriveUrl(id) {
|
|
68
|
+
return `https://${id.replace(/-/g, '')}.com`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function deriveEmail(url) {
|
|
72
|
+
const host = url.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
|
|
73
|
+
return `support@${host}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The brand.contact.person block from the wizard's answers (#770): the NAME
|
|
78
|
+
* is the whole block's condition — a headshot or a link with nobody to sign
|
|
79
|
+
* is not a person, and no name at all writes nothing at all, because a human
|
|
80
|
+
* fact can never be derived (the manage walk's own gate is what catches the
|
|
81
|
+
* gap later, rather than a framework identity signing the brand's email).
|
|
82
|
+
*
|
|
83
|
+
* @returns {Object|null} - { name, image?, url? }, or null when unanswered
|
|
84
|
+
*/
|
|
85
|
+
function buildPerson({ name, image, url }) {
|
|
86
|
+
const clean = (value) => (typeof value === 'string' ? value.trim() : '');
|
|
87
|
+
|
|
88
|
+
if (!clean(name)) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
name: clean(name),
|
|
94
|
+
...(clean(image) ? { image: clean(image) } : {}),
|
|
95
|
+
...(clean(url) ? { url: clean(url) } : {}),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Normalize a --targets flag value ('web,backend' or array) and validate
|
|
101
|
+
* every entry against the canonical target list.
|
|
102
|
+
*/
|
|
103
|
+
function parseTargetsFlag(value) {
|
|
104
|
+
const targets = Array.isArray(value) ? value : String(value).split(',');
|
|
105
|
+
const cleaned = targets.map((t) => t.trim()).filter(Boolean);
|
|
106
|
+
|
|
107
|
+
const unknown = cleaned.filter((t) => !TARGETS.includes(t));
|
|
108
|
+
if (unknown.length > 0) {
|
|
109
|
+
throw new Error(`Unknown target(s): ${unknown.join(', ')} — valid targets: ${TARGETS.join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
if (cleaned.length === 0) {
|
|
112
|
+
throw new Error(`--targets needs at least one of: ${TARGETS.join(', ')}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return cleaned;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The admin-account list a fresh brand would inherit without writing
|
|
120
|
+
* anything: the company config's account.admins when onboarding into a
|
|
121
|
+
* company workspace, else the manager's built-in default.
|
|
122
|
+
*/
|
|
123
|
+
function inheritedAdmins(companyRoot) {
|
|
124
|
+
const companyAdmins = companyRoot ? readRawConfig(companyRoot)?.account?.admins : null;
|
|
125
|
+
|
|
126
|
+
return Array.isArray(companyAdmins) && companyAdmins.length > 0
|
|
127
|
+
? { list: companyAdmins, source: 'company config' }
|
|
128
|
+
: { list: DEFAULTS.account.admins, source: 'built-in default' };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The accounts step: show the list the brand inherits and let the owner
|
|
133
|
+
* keep it (nothing written — the source layer keeps owning it) or define
|
|
134
|
+
* the brand's own, which lands as account.admins in its omega.json5.
|
|
135
|
+
*
|
|
136
|
+
* @returns {Array|null} Customized entries, or null to inherit
|
|
137
|
+
*/
|
|
138
|
+
async function collectAccountAdmins(inherited, interactive) {
|
|
139
|
+
if (!interactive) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
console.log('');
|
|
144
|
+
console.log(` Managed accounts the brand inherits ${chalk.dim(`(${inherited.source})`)}:`);
|
|
145
|
+
for (const entry of inherited.list) {
|
|
146
|
+
const flags = [entry.account && 'account', entry.marketing && 'marketing'].filter(Boolean).join(' + ');
|
|
147
|
+
console.log(` ${chalk.dim('•')} ${chalk.cyan(entry.email)} ${chalk.dim(`(${flags || 'nothing managed'})`)}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const keep = await confirm({
|
|
151
|
+
message: 'Keep this account list? (customizing writes account.admins into the brand config)',
|
|
152
|
+
default: true,
|
|
153
|
+
});
|
|
154
|
+
if (keep) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const entries = [];
|
|
159
|
+
do {
|
|
160
|
+
const email = (await input({
|
|
161
|
+
message: `Account email (${'{domain}'} = brand domain):`,
|
|
162
|
+
...(entries.length === 0 ? { default: 'support@{domain}' } : {}),
|
|
163
|
+
validate: (value) => (value.trim().includes('@') ? true : 'Must be an email address'),
|
|
164
|
+
})).trim();
|
|
165
|
+
const account = await confirm({ message: 'Manage the Firebase Auth account (create + admin role + top plan)?', default: true });
|
|
166
|
+
const marketing = await confirm({ message: 'Sync the contact to marketing providers?', default: true });
|
|
167
|
+
entries.push({ email, account, marketing });
|
|
168
|
+
} while (await confirm({ message: 'Add another account?', default: false }));
|
|
169
|
+
|
|
170
|
+
return entries;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Collect the wizard answers for a FRESH brand: flags win, prompts fill the
|
|
175
|
+
* gaps interactively, derivation covers the rest. `defaultId` comes from the
|
|
176
|
+
* in-place directory name (null in company mode); `companyRoot` (when
|
|
177
|
+
* onboarding into a company workspace) supplies the inherited account list.
|
|
178
|
+
*/
|
|
179
|
+
async function collectAnswers(options, defaultId, interactive, companyRoot = null) {
|
|
180
|
+
// id — the only field with no universal derivation
|
|
181
|
+
let id = options.id ?? null;
|
|
182
|
+
if (id != null && !ID_PATTERN.test(String(id))) {
|
|
183
|
+
throw new Error(`Invalid brand id ${JSON.stringify(String(id))} — ${ID_HINT}`);
|
|
184
|
+
}
|
|
185
|
+
if (!id) {
|
|
186
|
+
if (interactive) {
|
|
187
|
+
id = await input({
|
|
188
|
+
message: 'Brand ID:',
|
|
189
|
+
...(defaultId ? { default: defaultId } : {}),
|
|
190
|
+
validate: (value) => (ID_PATTERN.test(value.trim()) ? true : `Must be ${ID_HINT}`),
|
|
191
|
+
});
|
|
192
|
+
id = id.trim();
|
|
193
|
+
} else if (defaultId) {
|
|
194
|
+
id = defaultId;
|
|
195
|
+
} else {
|
|
196
|
+
throw new Error('Brand id required — pass --id=<id> (or run in an interactive terminal)');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// name / url — derivable from the id
|
|
201
|
+
let name = options.name ?? null;
|
|
202
|
+
if (!name) {
|
|
203
|
+
name = interactive
|
|
204
|
+
? (await input({
|
|
205
|
+
message: 'Brand name:',
|
|
206
|
+
default: deriveName(id),
|
|
207
|
+
validate: (value) => (value.trim().length > 0 ? true : 'Required'),
|
|
208
|
+
})).trim()
|
|
209
|
+
: deriveName(id);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let url = options.url ?? null;
|
|
213
|
+
if (url != null && !/^https?:\/\//.test(String(url))) {
|
|
214
|
+
throw new Error(`Invalid brand url ${JSON.stringify(String(url))} — must start with http(s)://`);
|
|
215
|
+
}
|
|
216
|
+
if (!url) {
|
|
217
|
+
url = interactive
|
|
218
|
+
? (await input({
|
|
219
|
+
message: 'Brand URL:',
|
|
220
|
+
default: deriveUrl(id),
|
|
221
|
+
validate: (value) => (/^https?:\/\//.test(value.trim()) ? true : 'Must start with http(s)://'),
|
|
222
|
+
})).trim()
|
|
223
|
+
: deriveUrl(id);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// description / tagline — optional; empty answers stay out of the config
|
|
227
|
+
let description = options.description ?? null;
|
|
228
|
+
if (description == null && interactive) {
|
|
229
|
+
description = (await input({ message: 'Brand description (optional):' })).trim();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let tagline = options.tagline ?? null;
|
|
233
|
+
if (tagline == null && interactive) {
|
|
234
|
+
tagline = (await input({ message: 'Brand tagline (optional, ~3 words):' })).trim();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// contact person — the human who signs the personal sends. The support
|
|
238
|
+
// email derives from the url; a NAME cannot, so the wizard asks for it and
|
|
239
|
+
// requires an answer, while a non-interactive run without --contactName
|
|
240
|
+
// writes no person at all rather than inventing one (#770). The headshot
|
|
241
|
+
// and the link are optional garnish on the same signoff.
|
|
242
|
+
let contactName = options.contactName ?? null;
|
|
243
|
+
if (contactName != null && (typeof contactName !== 'string' || !contactName.trim())) {
|
|
244
|
+
throw new Error('Invalid --contactName — pass the person\'s name (--contactName="Jane Doe")');
|
|
245
|
+
}
|
|
246
|
+
if (contactName == null && !interactive && (options.contactImage != null || options.contactUrl != null)) {
|
|
247
|
+
throw new Error('--contactImage/--contactUrl need a --contactName to belong to — nothing would be written');
|
|
248
|
+
}
|
|
249
|
+
if (contactName == null && interactive) {
|
|
250
|
+
contactName = await input({
|
|
251
|
+
message: 'Contact person (signs the personal emails: welcome, nudges, checkups):',
|
|
252
|
+
validate: (value) => (value.trim().length > 0 ? true : 'Required'),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let contactImage = options.contactImage ?? null;
|
|
257
|
+
if (contactImage == null && interactive && contactName) {
|
|
258
|
+
contactImage = await input({ message: 'Contact person headshot URL (optional):' });
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
let contactUrl = options.contactUrl ?? null;
|
|
262
|
+
if (contactUrl == null && interactive && contactName) {
|
|
263
|
+
contactUrl = await input({ message: 'Contact person link URL (optional):' });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// targets — checkbox in a TTY, web+backend otherwise
|
|
267
|
+
let targets = options.targets != null ? parseTargetsFlag(options.targets) : null;
|
|
268
|
+
if (!targets) {
|
|
269
|
+
targets = interactive
|
|
270
|
+
? await checkbox({
|
|
271
|
+
message: 'Targets (key presence in omega.json5 = enabled):',
|
|
272
|
+
choices: TARGETS.map((target) => ({
|
|
273
|
+
name: `${target} — targets/${TARGET_DIRS[target] || target}${TARGET_FRAMEWORKS[target] ? ` (${TARGET_FRAMEWORKS[target]})` : ' (reserved — MAM overhaul pending)'}`,
|
|
274
|
+
value: target,
|
|
275
|
+
checked: DEFAULT_TARGETS.includes(target),
|
|
276
|
+
})),
|
|
277
|
+
validate: (selection) => (selection.length > 0 ? true : 'Select at least one target'),
|
|
278
|
+
})
|
|
279
|
+
: DEFAULT_TARGETS;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// accounts — inherit by default (null writes nothing); customizing lands
|
|
283
|
+
// the brand's own account.admins
|
|
284
|
+
const accountAdmins = await collectAccountAdmins(inheritedAdmins(companyRoot), interactive);
|
|
285
|
+
|
|
286
|
+
// Bundle-id prefix — reverse-DNS of the parent company's domain when
|
|
287
|
+
// onboarding into a company workspace, else the brand's own (seeded into
|
|
288
|
+
// config only for targets that sign apps)
|
|
289
|
+
const companyUrl = companyRoot ? readRawConfig(companyRoot)?.brand?.url : null;
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
id,
|
|
293
|
+
name,
|
|
294
|
+
url,
|
|
295
|
+
description: description || null,
|
|
296
|
+
tagline: tagline || null,
|
|
297
|
+
email: deriveEmail(url),
|
|
298
|
+
person: buildPerson({ name: contactName, image: contactImage, url: contactUrl }),
|
|
299
|
+
targets,
|
|
300
|
+
accountAdmins,
|
|
301
|
+
bundleIdPrefix: deriveBundleIdPrefix(companyUrl || url),
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Resolve where the new brand lives when onboarding from a company root:
|
|
307
|
+
* brands.roots[0], or a selected root when several are configured and the
|
|
308
|
+
* run is interactive.
|
|
309
|
+
*/
|
|
310
|
+
async function resolveCompanyBrandsDir(companyRoot, interactive) {
|
|
311
|
+
const roots = readRawConfig(companyRoot)?.brands?.roots || DEFAULT_BRAND_ROOTS;
|
|
312
|
+
|
|
313
|
+
let rootSpec = roots[0];
|
|
314
|
+
if (roots.length > 1 && interactive) {
|
|
315
|
+
rootSpec = await select({
|
|
316
|
+
message: 'Where should the new brand live?',
|
|
317
|
+
choices: roots.map((r) => ({ value: r })),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (path.isAbsolute(rootSpec)) {
|
|
322
|
+
throw new Error(`brands.roots entries must be RELATIVE to the company root — got absolute path: ${rootSpec}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return path.resolve(companyRoot, rootSpec);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Answers for an EXISTING brand (resume): no wizard — the brand's own config
|
|
330
|
+
* is the source, and the scaffold only fills missing files around it.
|
|
331
|
+
*/
|
|
332
|
+
function answersFromBrand(brandRoot) {
|
|
333
|
+
const brand = loadBrand(brandRoot);
|
|
334
|
+
if (brand.configError) {
|
|
335
|
+
throw new Error(`This brand's config/omega.json5 does not load: ${brand.configError}`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const id = brand.id;
|
|
339
|
+
const url = brand.config.brand?.url || deriveUrl(id);
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
id,
|
|
343
|
+
name: brand.config.brand?.name || deriveName(id),
|
|
344
|
+
url,
|
|
345
|
+
description: brand.config.brand?.description || null,
|
|
346
|
+
tagline: brand.config.brand?.tagline || null,
|
|
347
|
+
email: brand.config.brand?.contact?.email || deriveEmail(url),
|
|
348
|
+
// The brand's own answer, kept as it stands — the scaffold never rewrites
|
|
349
|
+
// an existing config, so a rerun leaves the person byte-identical (#770)
|
|
350
|
+
person: brand.config.brand?.contact?.person || null,
|
|
351
|
+
targets: brand.enabledTargets.length > 0 ? brand.enabledTargets : DEFAULT_TARGETS,
|
|
352
|
+
bundleIdPrefix: brand.config.certificates?.providers?.apple?.bundleIdPrefix || deriveBundleIdPrefix(url),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Ensure the born brand is a git repository: init + stage + first commit
|
|
358
|
+
* when the brand root isn't already inside one (Ian 2026-07-17 — a brand
|
|
359
|
+
* should be committable from minute one, and the extension package task's
|
|
360
|
+
* source-zip step wants a repo). The scaffolded .gitignore is on disk
|
|
361
|
+
* before this runs, so the first commit can never capture .env/.omega.
|
|
362
|
+
* Inside an existing work tree ("Use this template" clones, brands in a
|
|
363
|
+
* monorepo) this is a clean skip.
|
|
364
|
+
*
|
|
365
|
+
* @param {string} brandRoot - The brand root directory
|
|
366
|
+
* @param {string} brandName - Display name for the first-commit message
|
|
367
|
+
* @returns {{ initialized: boolean, committed?: boolean, reason?: string }}
|
|
368
|
+
*/
|
|
369
|
+
function ensureGitRepo(brandRoot, brandName) {
|
|
370
|
+
const git = (args) => spawnSync('git', args, { cwd: brandRoot, stdio: 'ignore' });
|
|
371
|
+
|
|
372
|
+
if (git(['rev-parse', '--is-inside-work-tree']).status === 0) {
|
|
373
|
+
return { initialized: false, reason: 'existing repository' };
|
|
374
|
+
}
|
|
375
|
+
if (git(['init']).status !== 0) {
|
|
376
|
+
return { initialized: false, reason: 'git init failed (is git installed?)' };
|
|
377
|
+
}
|
|
378
|
+
git(['add', '-A']);
|
|
379
|
+
|
|
380
|
+
// Commit with the owner's identity; machines without one get a neutral
|
|
381
|
+
// fallback so the first commit never blocks onboarding
|
|
382
|
+
const hasIdentity = git(['config', 'user.email']).status === 0;
|
|
383
|
+
const identity = hasIdentity ? [] : ['-c', 'user.name=OMEGA Onboard', '-c', 'user.email=onboard@localhost'];
|
|
384
|
+
const commit = git([...identity, 'commit', '-m', `Initial commit — ${brandName} born via omega onboard`]);
|
|
385
|
+
|
|
386
|
+
return { initialized: true, committed: commit.status === 0 };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** Run manage in the new brand exactly as a user would — a real child process. */
|
|
390
|
+
function spawnManage(brandRoot) {
|
|
391
|
+
return new Promise((resolve) => {
|
|
392
|
+
// cli-run.js self-executes when spawned as main — spawning it directly
|
|
393
|
+
// deliberately bypasses the bin's dispatcher (#276): this child must run
|
|
394
|
+
// THIS manager, never re-dispatch on the child's cwd. The verb is
|
|
395
|
+
// explicit (#229): a bare invocation prints help and walks nothing.
|
|
396
|
+
const entry = path.join(__dirname, 'cli-run.js');
|
|
397
|
+
const child = spawn(process.execPath, [entry, 'manage'], { cwd: brandRoot, stdio: 'inherit' });
|
|
398
|
+
child.on('close', (code) => resolve(code ?? 1));
|
|
399
|
+
child.on('error', () => resolve(1));
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function printNextSteps(answers) {
|
|
404
|
+
console.log('');
|
|
405
|
+
console.log(chalk.bold('Next steps'));
|
|
406
|
+
|
|
407
|
+
const frameworks = answers.targets
|
|
408
|
+
.filter((target) => TARGET_FRAMEWORKS[target])
|
|
409
|
+
.map((target) => `${TARGET_FRAMEWORKS[target]} → targets/${TARGET_DIRS[target]}`);
|
|
410
|
+
if (frameworks.length > 0) {
|
|
411
|
+
console.log(` 1. Install each target's framework and run its setup: ${frameworks.join(', ')}`);
|
|
412
|
+
}
|
|
413
|
+
console.log(` ${frameworks.length > 0 ? 2 : 1}. Fill in .env as the brand adopts external services (the stub lists every key)`);
|
|
414
|
+
console.log(` ${frameworks.length > 0 ? 3 : 2}. Run ${chalk.cyan('npm run manage')} to reconcile everything — rerun any time`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Main onboard runner.
|
|
419
|
+
*
|
|
420
|
+
* @param {string} cwd - Where the command ran
|
|
421
|
+
* @param {Object} options - { id, name, url, description, tagline, contactName, contactImage, contactUrl, targets, dryRun, manage }
|
|
422
|
+
* @returns {Object} - { brandRoot, mode, created, kept, planned, valid, manageExitCode }
|
|
423
|
+
*/
|
|
424
|
+
async function runOnboard(cwd, options = {}) {
|
|
425
|
+
console.log(chalk.bold.cyan('OMEGA Manager — Onboarding'));
|
|
426
|
+
console.log('');
|
|
427
|
+
|
|
428
|
+
const interactive = canPrompt(options);
|
|
429
|
+
const resolved = resolveManageRoot(cwd);
|
|
430
|
+
|
|
431
|
+
// Resolve the brand root + answers per context
|
|
432
|
+
let brandRoot;
|
|
433
|
+
let answers;
|
|
434
|
+
let mode;
|
|
435
|
+
let companyRoot = null;
|
|
436
|
+
|
|
437
|
+
if (resolved?.isCompany) {
|
|
438
|
+
companyRoot = resolved.root;
|
|
439
|
+
console.log(`${chalk.dim('→')} Company workspace: ${chalk.cyan(companyRoot)}`);
|
|
440
|
+
|
|
441
|
+
const brandsDir = await resolveCompanyBrandsDir(companyRoot, interactive);
|
|
442
|
+
answers = await collectAnswers(options, null, interactive, companyRoot);
|
|
443
|
+
brandRoot = path.join(brandsDir, answers.id);
|
|
444
|
+
|
|
445
|
+
if (hasOmegaConfig(brandRoot)) {
|
|
446
|
+
console.log(`${chalk.dim('→')} Brand ${chalk.cyan(answers.id)} already exists — filling missing files only`);
|
|
447
|
+
answers = answersFromBrand(brandRoot);
|
|
448
|
+
mode = 'resume';
|
|
449
|
+
} else {
|
|
450
|
+
mode = 'fresh';
|
|
451
|
+
}
|
|
452
|
+
} else if (resolved) {
|
|
453
|
+
brandRoot = resolved.root;
|
|
454
|
+
console.log(`${chalk.dim('→')} Existing brand: ${chalk.cyan(brandRoot)} — filling missing files only`);
|
|
455
|
+
answers = answersFromBrand(brandRoot);
|
|
456
|
+
mode = 'resume';
|
|
457
|
+
} else {
|
|
458
|
+
brandRoot = cwd;
|
|
459
|
+
answers = await collectAnswers(options, deriveId(path.basename(cwd)), interactive);
|
|
460
|
+
mode = 'fresh';
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
if (mode === 'fresh') {
|
|
464
|
+
console.log(`${chalk.dim('→')} New brand: ${chalk.cyan(answers.name)} ${chalk.dim(`(${answers.id})`)} → ${chalk.cyan(brandRoot)}`);
|
|
465
|
+
}
|
|
466
|
+
console.log('');
|
|
467
|
+
|
|
468
|
+
// Scaffold (fill-missing; dry-run plans only)
|
|
469
|
+
const plan = buildScaffoldPlan(answers);
|
|
470
|
+
const results = applyScaffoldPlan(brandRoot, plan, { dryRun: options.dryRun });
|
|
471
|
+
printPlanResults(results);
|
|
472
|
+
|
|
473
|
+
if (options.dryRun) {
|
|
474
|
+
console.log('');
|
|
475
|
+
console.log(chalk.dim('⊘ Dry run — nothing written'));
|
|
476
|
+
return { brandRoot, mode, ...results, valid: true };
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Port-time conversion: a carried legacy oauth.json becomes the canonical
|
|
480
|
+
// google-oauth.json once, here, so nothing downstream dual-reads it (#501)
|
|
481
|
+
const legacyOAuth = convertLegacyOAuthSecret(brandRoot);
|
|
482
|
+
if (legacyOAuth.converted) {
|
|
483
|
+
console.log(` ${chalk.green('✓')} google-oauth.json ${chalk.dim('← the carried legacy oauth.json (converted, legacy file removed)')}`);
|
|
484
|
+
} else if (legacyOAuth.reason) {
|
|
485
|
+
console.log(` ${chalk.yellow('⚠')} legacy oauth.json not converted ${chalk.dim(`(${legacyOAuth.reason})`)}`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Company link — brand-local runs now layer the company defaults
|
|
489
|
+
if (companyRoot) {
|
|
490
|
+
if (stampCompanyMarker(brandRoot, companyRoot)) {
|
|
491
|
+
console.log(` ${chalk.green('✓')} company stamp ${chalk.dim(`→ .omega/company.json (${companyRoot})`)}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Prove the brand actually loads before calling it done
|
|
496
|
+
const brand = loadBrand(brandRoot);
|
|
497
|
+
const valid = !brand.configError && brand.configErrors.length === 0;
|
|
498
|
+
console.log('');
|
|
499
|
+
if (valid) {
|
|
500
|
+
console.log(`${chalk.green('✓')} Config loads and validates ${chalk.dim(`(targets: ${brand.enabledTargets.join(', ')})`)}`);
|
|
501
|
+
} else {
|
|
502
|
+
console.log(`${chalk.red('✗')} Config problem: ${brand.configError || brand.configErrors.join('; ')}`);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Git — a born brand is committable from minute one
|
|
506
|
+
const git = ensureGitRepo(brandRoot, answers.name);
|
|
507
|
+
if (git.initialized) {
|
|
508
|
+
console.log(`${chalk.green('✓')} git repository initialized ${chalk.dim(git.committed ? '(first commit made)' : '(commit skipped — set your git identity and commit)')}`);
|
|
509
|
+
} else {
|
|
510
|
+
console.log(`${chalk.dim('•')} git ${chalk.dim(`(${git.reason})`)}`);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
printNextSteps(answers);
|
|
514
|
+
|
|
515
|
+
// Manage handoff — never in a dry run, never implicitly without a TTY
|
|
516
|
+
let manageExitCode = null;
|
|
517
|
+
const runNow = options.manage
|
|
518
|
+
?? (interactive && valid
|
|
519
|
+
? await confirm({ message: 'Run manage now?', default: true })
|
|
520
|
+
: false);
|
|
521
|
+
|
|
522
|
+
if (runNow) {
|
|
523
|
+
console.log('');
|
|
524
|
+
console.log(`${chalk.dim('→')} Starting management…`);
|
|
525
|
+
console.log(chalk.dim('━'.repeat(70)));
|
|
526
|
+
manageExitCode = await spawnManage(brandRoot);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return { brandRoot, mode, ...results, valid, git, legacyOAuth, manageExitCode };
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
module.exports = { runOnboard, deriveId, deriveName, deriveUrl };
|