@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,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The brand-root VERB fan-out — one target-independent verb run over every
|
|
3
|
+
* target the brand has, framework and custom alike (#603).
|
|
4
|
+
*
|
|
5
|
+
* `omega build` and `omega clean` are the two verbs a brand root had for no
|
|
6
|
+
* target type at all: start, test and deploy each fanned out already, so a
|
|
7
|
+
* brand could compile or wipe its targets only one `cd` at a time. Both are the
|
|
8
|
+
* SAME walk with a different word, so they share this one implementation —
|
|
9
|
+
* commands/build.js and commands/clean.js are its two callers.
|
|
10
|
+
*
|
|
11
|
+
* The shape is the deploy fan-out's, deliberately: the same target discovery,
|
|
12
|
+
* the same `--target=` picker and the same dependency ORDER
|
|
13
|
+
* (selectTargets — backend first, custom targets last), the same flag
|
|
14
|
+
* forwarding, and `resolveTargetRun` as the one place a verb becomes a command
|
|
15
|
+
* (a framework's own `omega <verb>`, or the target's `npm run <verb>`).
|
|
16
|
+
*
|
|
17
|
+
* What differs from deploy: targets are INDEPENDENT here. Neither verb
|
|
18
|
+
* publishes anything, so a failure never stops the walk — knowing every broken
|
|
19
|
+
* target after one run beats discovering them one build at a time — and any
|
|
20
|
+
* failure still exits 1. A target that declares no such script steps aside
|
|
21
|
+
* LOUDLY, naming the target and the verb, and is never counted a failure. And
|
|
22
|
+
* --dry-run is CONSUMED here rather than forwarded: no framework's build/clean
|
|
23
|
+
* honors it, so every target prints the command it would have run and the walk
|
|
24
|
+
* executes nothing.
|
|
25
|
+
*
|
|
26
|
+
* The brand-root log tee lives HERE, not in the two verb files (#623): every
|
|
27
|
+
* other fan-out attaches at the top of its own command, but build and clean
|
|
28
|
+
* share this one walk and this is the only place that knows the brand root, so
|
|
29
|
+
* one attach keyed by the verb serves both — logs/build.log and logs/clean.log.
|
|
30
|
+
*/
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
const chalk = require('chalk').default;
|
|
33
|
+
const attachLogFile = require('../vendor/devkit/attach-log-file.js');
|
|
34
|
+
|
|
35
|
+
const { resolveBrandRoot, discoverTargets } = require('./brand.js');
|
|
36
|
+
const { resolveTargetRun } = require('./framework-bin.js');
|
|
37
|
+
const { runCommand } = require('./run-command.js');
|
|
38
|
+
const { PICKER_FLAG, assertPickerFlags, selectTargets, buildForwardedFlags } = require('./target-selection.js');
|
|
39
|
+
|
|
40
|
+
// Both yargs spellings of the flag this fan-out consumes (see the header)
|
|
41
|
+
const DRY_RUN_KEYS = ['dry-run', 'dryRun'];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Run one verb across the brand's targets.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} verb - The verb ('build' | 'clean').
|
|
47
|
+
* @param {object} [options] - The parsed CLI options (--target= consumed, the rest forwarded).
|
|
48
|
+
* @param {object} [deps] - Test seam: { run } replaces runCommand.
|
|
49
|
+
* @returns {Promise<void>} - Failures land on process.exitCode; only a retired
|
|
50
|
+
* picker (#780) throws, and it throws before any work.
|
|
51
|
+
*/
|
|
52
|
+
async function runVerbFanout(verb, options = {}, deps = {}) {
|
|
53
|
+
assertPickerFlags(options);
|
|
54
|
+
|
|
55
|
+
const run = deps.run || runCommand;
|
|
56
|
+
|
|
57
|
+
const brandRoot = resolveBrandRoot(process.cwd());
|
|
58
|
+
if (!brandRoot) {
|
|
59
|
+
console.error(chalk.red(`✗ Not inside a brand monorepo (no config/omega.json5 up the tree) — run inside a brand, or inside a target for that target's ${verb}.`));
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Tee the whole fan-out to <brandRoot>/logs/<verb>.log (#623) — the walk's
|
|
65
|
+
// OWN verdict on disk (a target's output rides its own log: runCommand
|
|
66
|
+
// inherits the terminal, so a child never passes through these writers).
|
|
67
|
+
attachLogFile(path.join(brandRoot, 'logs', `${verb}.log`));
|
|
68
|
+
|
|
69
|
+
const targets = discoverTargets(brandRoot).filter((entry) => entry.target || entry.custom);
|
|
70
|
+
const { selected } = selectTargets({ targets, target: options[PICKER_FLAG] });
|
|
71
|
+
|
|
72
|
+
if (selected.length === 0) {
|
|
73
|
+
console.error(chalk.red(`✗ This brand has no targets under targets/ — nothing ${verb === 'clean' ? 'cleaned' : 'built'}.`));
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const forwarded = buildForwardedFlags(options, DRY_RUN_KEYS);
|
|
79
|
+
const dryRun = !!(options['dry-run'] || options.dryRun);
|
|
80
|
+
|
|
81
|
+
console.log(chalk.bold(`\nOMEGA brand ${verb} — ${path.basename(brandRoot)}${dryRun ? chalk.dim(' (dry run)') : ''} ${chalk.dim(`(${selected.map((entry) => entry.target || entry.name).join(' → ')})`)}`));
|
|
82
|
+
|
|
83
|
+
const summary = [];
|
|
84
|
+
|
|
85
|
+
for (const [index, entry] of selected.entries()) {
|
|
86
|
+
const label = `[${index + 1}/${selected.length}] ${entry.name}`;
|
|
87
|
+
const resolved = resolveTargetRun(entry, verb, forwarded, { dryRun });
|
|
88
|
+
|
|
89
|
+
// A target that declares no such script has nothing to do and nothing
|
|
90
|
+
// broken — the skip NAMES the target and the verb, because a verb that
|
|
91
|
+
// quietly did nothing is indistinguishable from one that worked (#603)
|
|
92
|
+
if (resolved.kind === 'skip') {
|
|
93
|
+
console.log(chalk.dim(`\n⊘ ${label}: ${resolved.detail} — skipped`));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Under --dry-run the run IS the plan line — nothing executes on either
|
|
98
|
+
// lane, and a target that would have been skipped still says so above
|
|
99
|
+
if (resolved.kind === 'plan') {
|
|
100
|
+
console.log(chalk.dim(`\n⊘ ${label}: dry run — ${resolved.detail}`));
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (resolved.kind === 'error') {
|
|
105
|
+
console.log(chalk.red(`\n✗ ${label}: ${resolved.detail}`));
|
|
106
|
+
summary.push({ name: entry.name, ok: false, detail: resolved.detail });
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
console.log(chalk.cyan(`${`\n─── ${label} ${chalk.dim(`(${resolved.framework || 'custom'})`)} — ${resolved.label}`.trimEnd()} ───`));
|
|
111
|
+
const result = await run(resolved.command, resolved.args, entry.path);
|
|
112
|
+
|
|
113
|
+
summary.push({ name: entry.name, ok: result.success, detail: result.error });
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log(chalk.bold(`\n${verb.charAt(0).toUpperCase()}${verb.slice(1)} summary`));
|
|
117
|
+
for (const entry of summary) {
|
|
118
|
+
console.log(entry.ok
|
|
119
|
+
? ` ${chalk.green('✓')} ${entry.name}`
|
|
120
|
+
: ` ${chalk.red('✗')} ${entry.name} ${chalk.dim(`— ${entry.detail}`)}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (summary.some((entry) => !entry.ok)) {
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = { runVerbFanout };
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live-launch verification — the last mile of the one-command launch
|
|
3
|
+
* (`omega pipeline --deploy=web`): after the deploy legs push, these checks
|
|
4
|
+
* confirm the launch surface is actually LIVE from the outside.
|
|
5
|
+
*
|
|
6
|
+
* - site: HTTPS GET on the brand's canonical URL → 200 + text/html +
|
|
7
|
+
* a non-trivial body (a 200 empty shell is not a live site).
|
|
8
|
+
* - domain: the apex (and www, when the brand owns the apex) resolve.
|
|
9
|
+
* - cloudflare: the response arrives THROUGH Cloudflare (cf-ray, or
|
|
10
|
+
* server: cloudflare).
|
|
11
|
+
*
|
|
12
|
+
* Both transports are seams: `{ fetch, resolve }` default to the real
|
|
13
|
+
* globals and tests pass fakes, so no test ever touches the network.
|
|
14
|
+
*
|
|
15
|
+
* Gating mirrors the cloud service: a demo-* (emulator-only) brand has no
|
|
16
|
+
* live surface to check, so every leg records a gated skip with its reason
|
|
17
|
+
* and NO transport is called.
|
|
18
|
+
*/
|
|
19
|
+
const dns = require('node:dns').promises;
|
|
20
|
+
|
|
21
|
+
const { isDemoProject } = require('../vendor/config/index.js');
|
|
22
|
+
const { getApexDomain } = require('./domain-utils.js');
|
|
23
|
+
|
|
24
|
+
// deploy target → the checks that prove THAT target's surface is live. Only
|
|
25
|
+
// the web target has an outside-observable launch surface; backend/desktop/
|
|
26
|
+
// extension prove themselves through their own deploy legs.
|
|
27
|
+
const VERIFY_LEGS = { web: ['site', 'domain', 'cloudflare'] };
|
|
28
|
+
|
|
29
|
+
// A live page is bigger than this; a 200 carrying less is an empty shell
|
|
30
|
+
// (a hosting placeholder, a stub 404 page served with the wrong status).
|
|
31
|
+
const MIN_BODY_BYTES = 500;
|
|
32
|
+
|
|
33
|
+
// Live checks talk to the internet — never let one hang the pipeline.
|
|
34
|
+
const REQUEST_TIMEOUT_MS = 15_000;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve what a brand's live surface IS, and whether it may be checked.
|
|
38
|
+
*
|
|
39
|
+
* @param {object} brandConfig - Merged brand config (omega.json5).
|
|
40
|
+
* @returns {{ url: string|null, hostnames: string[], gated: string|null }} - `gated` is the skip reason when live checks must not run.
|
|
41
|
+
*/
|
|
42
|
+
function resolveVerifySurface(brandConfig) {
|
|
43
|
+
const projectId = brandConfig?.cloud?.config?.projectId;
|
|
44
|
+
if (!projectId) {
|
|
45
|
+
return { url: null, hostnames: [], gated: 'no cloud project configured — no live surface to verify' };
|
|
46
|
+
}
|
|
47
|
+
if (isDemoProject(projectId)) {
|
|
48
|
+
return { url: null, hostnames: [], gated: `${projectId} is a demo-* (emulator-only) project — no live surface to verify` };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const url = (brandConfig?.brand?.url || '').replace(/\/$/, '');
|
|
52
|
+
if (!url) {
|
|
53
|
+
return { url: null, hostnames: [], gated: 'no brand.url configured' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const hostname = new URL(url).hostname;
|
|
57
|
+
// www is a platform DNS record on the apex only — a subdomain brand's www
|
|
58
|
+
// belongs to the parent brand, so it is not this brand's to verify.
|
|
59
|
+
const hostnames = getApexDomain(hostname) === hostname ? [hostname, `www.${hostname}`] : [hostname];
|
|
60
|
+
|
|
61
|
+
return { url, hostnames, gated: null };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Real-fetch wrapper carrying the timeout; fakes ignore the options. */
|
|
65
|
+
function request(surface, transports) {
|
|
66
|
+
const fetchImpl = transports.fetch || globalThis.fetch;
|
|
67
|
+
return fetchImpl(surface.url, { redirect: 'follow', signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The canonical URL serves a real page.
|
|
72
|
+
*
|
|
73
|
+
* @param {object} surface - From resolveVerifySurface().
|
|
74
|
+
* @param {object} transports - { fetch }.
|
|
75
|
+
* @returns {Promise<{ status: string, error: string|null }>}
|
|
76
|
+
*/
|
|
77
|
+
async function checkSite(surface, transports) {
|
|
78
|
+
let response;
|
|
79
|
+
try {
|
|
80
|
+
response = await request(surface, transports);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return { status: 'error', error: `${surface.url} unreachable — ${error.message}` };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (response.status !== 200) {
|
|
86
|
+
return { status: 'error', error: `${surface.url} returned ${response.status} (expected 200)` };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const contentType = response.headers.get('content-type') || '(none)';
|
|
90
|
+
if (!contentType.includes('text/html')) {
|
|
91
|
+
return { status: 'error', error: `${surface.url} served content-type ${contentType} (expected text/html)` };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const body = await response.text();
|
|
95
|
+
if (body.length < MIN_BODY_BYTES) {
|
|
96
|
+
return { status: 'error', error: `${surface.url} served a ${body.length}-byte body (expected ≥ ${MIN_BODY_BYTES} — a 200 empty shell is not a live site)` };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return { status: 'success', error: null };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Every hostname the brand owns resolves in DNS.
|
|
104
|
+
*
|
|
105
|
+
* @param {object} surface - From resolveVerifySurface().
|
|
106
|
+
* @param {object} transports - { resolve }.
|
|
107
|
+
* @returns {Promise<{ status: string, error: string|null }>}
|
|
108
|
+
*/
|
|
109
|
+
async function checkDomain(surface, transports) {
|
|
110
|
+
const resolve = transports.resolve || dns.resolve4;
|
|
111
|
+
|
|
112
|
+
for (const hostname of surface.hostnames) {
|
|
113
|
+
try {
|
|
114
|
+
await resolve(hostname);
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return { status: 'error', error: `${hostname} does not resolve — ${error.message}` };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { status: 'success', error: null };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The response arrives through Cloudflare (proxied, not origin-direct).
|
|
125
|
+
*
|
|
126
|
+
* @param {object} surface - From resolveVerifySurface().
|
|
127
|
+
* @param {object} transports - { fetch }.
|
|
128
|
+
* @returns {Promise<{ status: string, error: string|null }>}
|
|
129
|
+
*/
|
|
130
|
+
async function checkCloudflare(surface, transports) {
|
|
131
|
+
let response;
|
|
132
|
+
try {
|
|
133
|
+
response = await request(surface, transports);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return { status: 'error', error: `${surface.url} unreachable — ${error.message}` };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const ray = response.headers.get('cf-ray');
|
|
139
|
+
const server = response.headers.get('server') || '(none)';
|
|
140
|
+
if (!ray && !/cloudflare/i.test(server)) {
|
|
141
|
+
return { status: 'error', error: `${surface.url} did not arrive through Cloudflare (no cf-ray, server: ${server})` };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return { status: 'success', error: null };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const CHECKS = { site: checkSite, domain: checkDomain, cloudflare: checkCloudflare };
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Run the verify sweep for a set of deploy targets.
|
|
151
|
+
*
|
|
152
|
+
* @param {string[]} targets - Deploy targets to verify (web, backend, …).
|
|
153
|
+
* @param {object} brandConfig - Merged brand config.
|
|
154
|
+
* @param {object} [transports] - { fetch, resolve } seams; real globals by default.
|
|
155
|
+
* @param {object} [options] - { dryRun }: a plan-only pass records the legs it WOULD run and touches nothing.
|
|
156
|
+
* @returns {Promise<Array<object>>} - Run-record rows (`verify:<name>`), judged exactly like deploy legs.
|
|
157
|
+
*/
|
|
158
|
+
async function runVerifyLegs(targets, brandConfig, transports = {}, options = {}) {
|
|
159
|
+
const names = [];
|
|
160
|
+
for (const target of targets) {
|
|
161
|
+
for (const name of VERIFY_LEGS[target] || []) {
|
|
162
|
+
if (!names.includes(name)) names.push(name);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (names.length === 0) {
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const surface = resolveVerifySurface(brandConfig);
|
|
171
|
+
|
|
172
|
+
// A dry run is plan-only everywhere else in the manager — the live sweep is
|
|
173
|
+
// no exception: it reports the legs it would run and sends no traffic.
|
|
174
|
+
const gated = options.dryRun ? 'dry-run — the live sweep is plan-only here' : surface.gated;
|
|
175
|
+
if (gated) {
|
|
176
|
+
return names.map((name) => ({ service: `verify:${name}`, status: 'skipped', output: null, error: null, reason: gated }));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const rows = [];
|
|
180
|
+
for (const name of names) {
|
|
181
|
+
const result = await CHECKS[name](surface, transports);
|
|
182
|
+
rows.push({ service: `verify:${name}`, status: result.status, output: null, error: result.error });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return rows;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = { VERIFY_LEGS, resolveVerifySurface, checkSite, checkDomain, checkCloudflare, runVerifyLegs };
|
package/dist/manage.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The manage orchestrator — omega-manager's src/manage/index.js reborn for
|
|
3
|
+
* brand monorepos. Resolves the brand root from the cwd, loads the brand
|
|
4
|
+
* (omega.json5 + enabled targets + target dirs), then walks SERVICE_ORDER running
|
|
5
|
+
* each service's operations idempotently. Every provisioned fact a service
|
|
6
|
+
* resolves lands in config/omega.json5 (secrets in .env) as it goes;
|
|
7
|
+
* transient output lands in .omega/runs/{timestamp}.json; the summary
|
|
8
|
+
* prints last.
|
|
9
|
+
*
|
|
10
|
+
* Company mode (many brands from one workspace — Ian's omega-manager case)
|
|
11
|
+
* layers on top of this later: runManage() is already per-brand, so the
|
|
12
|
+
* company runner is a loop + the parallel logger, both ported when that
|
|
13
|
+
* mode lands.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const chalk = require('chalk').default;
|
|
17
|
+
const { loadEnvRoots } = require('./vendor/config/index.js');
|
|
18
|
+
|
|
19
|
+
const { SERVICE_ORDER, BOOT_SERVICES, OPERATIONS } = require('./config.js');
|
|
20
|
+
const { resolveBrandRoot, loadBrand } = require('./lib/brand.js');
|
|
21
|
+
const { readCompanyMarker, loadCompanyConfig } = require('./lib/company.js');
|
|
22
|
+
const { writeRunOutput } = require('./lib/run-output.js');
|
|
23
|
+
const { formatDuration } = require('./lib/duration.js');
|
|
24
|
+
const { runPreflight, assertFamilyVersions } = require('./lib/preflight.js');
|
|
25
|
+
const ensureTargetsRename = require('./services/migrations/ensure/targets-rename.js');
|
|
26
|
+
const { CONSENT_REQUIRED } = require('./lib/google-auth.js');
|
|
27
|
+
const { RunSummary } = require('./lib/run-summary.js');
|
|
28
|
+
|
|
29
|
+
// Timestamp for this process run — used in .omega/runs/{RUN_TIMESTAMP}.json
|
|
30
|
+
const RUN_TIMESTAMP = new Date().toISOString().replace(/[:.]/g, '-');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Run a single service for the brand.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} serviceName - Service to run
|
|
36
|
+
* @param {Object} brand - Loaded brand (from loadBrand)
|
|
37
|
+
* @param {Object} options - Run options
|
|
38
|
+
* @returns {Object} - { status, error?, state?, output? }
|
|
39
|
+
*/
|
|
40
|
+
async function runService(serviceName, brand, options = {}) {
|
|
41
|
+
const operations = OPERATIONS[serviceName] || [];
|
|
42
|
+
|
|
43
|
+
if (operations.length === 0) {
|
|
44
|
+
return { status: 'skipped', reason: 'no operations' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const serviceModule = require(`./services/${serviceName}/index.js`);
|
|
49
|
+
|
|
50
|
+
if (!serviceModule.run) {
|
|
51
|
+
console.log(` Service ${serviceName} has no run() export`);
|
|
52
|
+
return { status: 'skipped', reason: 'no run export' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// No cross-run seed: a service reads what it needs from brandConfig (the
|
|
56
|
+
// one home for provisioned facts) or re-derives it from the platform, and
|
|
57
|
+
// a setup() may still seed serviceData for its own operations (#434)
|
|
58
|
+
return await serviceModule.run({
|
|
59
|
+
brandId: brand.id,
|
|
60
|
+
brandRoot: brand.root,
|
|
61
|
+
// Company workspace root when the brand carries a valid company marker
|
|
62
|
+
// — company-shared resources (Apple signing material) live there
|
|
63
|
+
companyRoot: brand.companyRoot || null,
|
|
64
|
+
// The company config layer (already merged into brandConfig) for
|
|
65
|
+
// provenance checks — null for standalone brands
|
|
66
|
+
companyConfig: brand.companyConfig || null,
|
|
67
|
+
brandConfig: brand.config,
|
|
68
|
+
brand,
|
|
69
|
+
targets: brand.targets,
|
|
70
|
+
operations,
|
|
71
|
+
options,
|
|
72
|
+
});
|
|
73
|
+
} catch (error) {
|
|
74
|
+
// A consent gate thrown at setup time is a pending HUMAN step, not a
|
|
75
|
+
// failure (#228) — warned keeps the walk going and lands the item in the
|
|
76
|
+
// summary's ⚑ list instead of stopping the cycle (and any dev boot on it)
|
|
77
|
+
if (error?.code === CONSENT_REQUIRED) {
|
|
78
|
+
console.log(` ${chalk.yellow('⚑')} ${serviceName}: ${error.message}`);
|
|
79
|
+
return {
|
|
80
|
+
status: 'warned',
|
|
81
|
+
warned: [{ operation: 'auth', reason: 'needs an interactive run' }],
|
|
82
|
+
output: { auth: { needsInteractive: error.message } },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
console.error(` Error in ${serviceName}: ${error.message}`);
|
|
87
|
+
return { status: 'error', error: error.message };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Run all services (in order) against the brand monorepo containing startDir.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} startDir - Any directory inside the brand monorepo
|
|
95
|
+
* @param {Object} options - { service?, lane? ('boot' = the local slice a dev
|
|
96
|
+
* boot needs; omitted = every service), continueOnError?, dryRun?, verbose?,
|
|
97
|
+
* strict? (preflight failures fail hard instead of skipping),
|
|
98
|
+
* migration? (true = all, string = one), execute? (the migrations write
|
|
99
|
+
* gate — without it the run only audits), limit?, ids? (migrations service),
|
|
100
|
+
* resetAssets? (true = both kinds, string = 'logos'/'templates'; assets
|
|
101
|
+
* service), force? (ignore the update service's incremental cache and
|
|
102
|
+
* rebuild every target) }
|
|
103
|
+
* @returns {{ hasErrors: boolean, results: Object, brand: Object }}
|
|
104
|
+
*/
|
|
105
|
+
async function runManage(startDir, options = {}) {
|
|
106
|
+
const brandRoot = resolveBrandRoot(startDir);
|
|
107
|
+
|
|
108
|
+
if (!brandRoot) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`No brand monorepo found at or above ${startDir} — `
|
|
111
|
+
+ `expected a config/omega.json5 at the brand root (see docs/shared/config.md).`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// One vocabulary (#443): the apps/ → targets/ rename is a ONE-TIME
|
|
116
|
+
// migration, never healing inside a run. The brand it fixes cannot be
|
|
117
|
+
// walked — discovery fails loud on the old shape and every service below
|
|
118
|
+
// reads targets/ — so this ONE migration runs ALONE, ahead of the load that
|
|
119
|
+
// would throw on it, and stops there. It is registered like every other
|
|
120
|
+
// migration too (config.js OPERATIONS.migrations), so a bare `--migration`
|
|
121
|
+
// walk re-checks it on a converged brand.
|
|
122
|
+
if (options.migration === ensureTargetsRename.MIGRATION_NAME) {
|
|
123
|
+
console.log('');
|
|
124
|
+
console.log(chalk.bold.cyan(`🚀 Omega Manager ${chalk.dim(`— migration: ${ensureTargetsRename.MIGRATION_NAME}`)}`));
|
|
125
|
+
console.log(` ${chalk.dim('Root:')} ${brandRoot}`);
|
|
126
|
+
|
|
127
|
+
const result = await ensureTargetsRename({ brandRoot, options });
|
|
128
|
+
|
|
129
|
+
return { hasErrors: false, results: { migrations: result }, brand: null };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Company layer: a company-managed brand carries a .omega/company.json
|
|
133
|
+
// stamp (written idempotently by company runs) pointing at its company
|
|
134
|
+
// root — its config becomes the layer between manager DEFAULTS and the
|
|
135
|
+
// brand file, and its .env fills the gaps under the brand .env.
|
|
136
|
+
const marker = readCompanyMarker(brandRoot);
|
|
137
|
+
let companyConfig = null;
|
|
138
|
+
|
|
139
|
+
if (marker?.stale) {
|
|
140
|
+
console.log(chalk.yellow(`⚠ .omega/company.json points at ${marker.companyRoot}, which is no longer a company workspace — running standalone (delete the file to silence this)`));
|
|
141
|
+
} else if (marker) {
|
|
142
|
+
companyConfig = loadCompanyConfig(marker.companyRoot);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Secrets chain: shell env > brand .env > company .env — the shared
|
|
146
|
+
// cascade (files load strongest-first, never overriding what's set), each
|
|
147
|
+
// layer overlaid by its own `.env.<environment>` file
|
|
148
|
+
// ([#586](https://github.com/Omega-JS-Stack/omega/issues/586)). The manage
|
|
149
|
+
// walk judges every key on the RESOLVED cascade, so a value that lives only
|
|
150
|
+
// in an overlay must be visible here or the mint lane would re-mint it.
|
|
151
|
+
//
|
|
152
|
+
// `production` is PINNED, never the shell's answer: manage reconciles REAL
|
|
153
|
+
// infrastructure with production credentials, and the mint lane has to see
|
|
154
|
+
// the key it would otherwise re-mint.
|
|
155
|
+
loadEnvRoots([
|
|
156
|
+
brandRoot,
|
|
157
|
+
companyConfig ? marker.companyRoot : null,
|
|
158
|
+
], { environment: 'production' });
|
|
159
|
+
|
|
160
|
+
// The company layer itself is folded by @omega.js/config off the same stamp
|
|
161
|
+
// (#83) — loadBrand takes no company argument; what we resolved here is
|
|
162
|
+
// provenance and the .env chain.
|
|
163
|
+
const brand = loadBrand(brandRoot);
|
|
164
|
+
|
|
165
|
+
// Company-shared resources (Apple signing material) live at the company
|
|
166
|
+
// workspace root — services resolve it via context.companyRoot
|
|
167
|
+
brand.companyRoot = companyConfig ? marker.companyRoot : null;
|
|
168
|
+
|
|
169
|
+
// The inheritable company layer itself rides along for provenance: its
|
|
170
|
+
// values are already merged into brand.config, but flows that want to say
|
|
171
|
+
// "this came from the company" (e.g. the GA account default) compare
|
|
172
|
+
// against it via context.companyConfig
|
|
173
|
+
brand.companyConfig = companyConfig;
|
|
174
|
+
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log(chalk.bold.cyan('🚀 Omega Manager'));
|
|
177
|
+
console.log('');
|
|
178
|
+
|
|
179
|
+
// Skip disabled brands
|
|
180
|
+
if (brand.config.enabled === false) {
|
|
181
|
+
console.log(chalk.dim('━'.repeat(70)));
|
|
182
|
+
console.log(` ${chalk.bold(brand.config.brand?.name || brand.id)} ${chalk.yellow('DISABLED')}`);
|
|
183
|
+
console.log(chalk.dim('━'.repeat(70)));
|
|
184
|
+
return { hasErrors: false, results: {}, brand, skipped: true };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Determine which services to run. --service is the most specific ask and
|
|
188
|
+
// wins outright; a lane narrows the walk to its slice (#228) — 'boot' is the
|
|
189
|
+
// local one `omega dev` runs before its legs, in SERVICE_ORDER order.
|
|
190
|
+
if (options.lane && options.lane !== 'boot') {
|
|
191
|
+
throw new Error(`Unknown lane: ${options.lane}. Available: boot`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const servicesToRun = options.service
|
|
195
|
+
? [options.service]
|
|
196
|
+
: (options.lane === 'boot'
|
|
197
|
+
? SERVICE_ORDER.filter((serviceName) => BOOT_SERVICES.includes(serviceName))
|
|
198
|
+
: SERVICE_ORDER);
|
|
199
|
+
|
|
200
|
+
if (options.service && !SERVICE_ORDER.includes(options.service)) {
|
|
201
|
+
throw new Error(`Unknown service: ${options.service}. Available: ${SERVICE_ORDER.join(', ')}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
console.log(chalk.cyan('━'.repeat(70)));
|
|
205
|
+
console.log(` ${chalk.bold.white(brand.config.brand?.name || brand.id)} ${chalk.cyan(brand.config.brand?.url || '')} ${chalk.dim(`@ ${new Date().toLocaleTimeString()}`)}`);
|
|
206
|
+
console.log(chalk.cyan('━'.repeat(70)));
|
|
207
|
+
console.log(` ${chalk.dim('Root:')} ${brand.root}`);
|
|
208
|
+
if (companyConfig) {
|
|
209
|
+
console.log(` ${chalk.dim('Company:')} ${marker.companyRoot}`);
|
|
210
|
+
}
|
|
211
|
+
console.log(` ${chalk.dim('Enabled:')} ${brand.enabledTargets.join(', ') || chalk.yellow('none enabled')}`);
|
|
212
|
+
console.log(` ${chalk.dim('Targets:')} ${brand.targets.map((entry) => `${entry.name}${entry.target ? chalk.dim(`→${entry.target}`) : chalk.yellow('→?')}`).join(', ') || chalk.yellow('none')}`);
|
|
213
|
+
console.log(` ${chalk.dim('Services:')} ${options.service || servicesToRun.join(chalk.dim(' → '))}`);
|
|
214
|
+
|
|
215
|
+
// Lockstep (#794): the @omega.js family ships ONE version, so a brand
|
|
216
|
+
// running a framework from one release beside a client from another is
|
|
217
|
+
// refused HERE — before any service reconciles anything on top of two
|
|
218
|
+
// copies of the runtime and two copies of the config validator. `file:`
|
|
219
|
+
// links (the local era) and targets with nothing installed are exempt.
|
|
220
|
+
assertFamilyVersions({ brandRoot, targets: brand.targets });
|
|
221
|
+
|
|
222
|
+
// Preflight (the REQUIRES registry): check the enabled services' declared
|
|
223
|
+
// env vars + Google scopes up front — ONE consolidated fix walkthrough
|
|
224
|
+
// instead of N mid-run skips. Failing services skip (the cycle continues —
|
|
225
|
+
// absorb, never crash) unless the run can collect the fix itself
|
|
226
|
+
// (interactive paste/consent flows) or --strict makes them fail hard.
|
|
227
|
+
// A lane narrows the WALK, never the preflight (#228): preflight is local
|
|
228
|
+
// (env presence + token-store read, zero network), and its walkthrough is
|
|
229
|
+
// how a boot names what the full setup still owes. Gates are only consulted
|
|
230
|
+
// for the services this run actually walks, so an unwalked finding records
|
|
231
|
+
// nothing and fails nothing.
|
|
232
|
+
const preflight = runPreflight({
|
|
233
|
+
services: options.service ? [options.service] : SERVICE_ORDER,
|
|
234
|
+
brandConfig: brand.config,
|
|
235
|
+
brandRoot,
|
|
236
|
+
options,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const summary = new RunSummary();
|
|
240
|
+
const results = {};
|
|
241
|
+
|
|
242
|
+
// Run each service in order
|
|
243
|
+
for (const serviceName of servicesToRun) {
|
|
244
|
+
console.log('');
|
|
245
|
+
console.log(` ${chalk.bold.magenta(`[${serviceName.toUpperCase()}]`)}`);
|
|
246
|
+
|
|
247
|
+
// Preflight verdicts: 'skip' steps the service aside with the printed
|
|
248
|
+
// walkthrough (missingEnv rides along for the 🔑 aggregate); 'error' is
|
|
249
|
+
// the --strict hard failure; 'run' (or no gate) proceeds normally
|
|
250
|
+
const gate = preflight.gates[serviceName];
|
|
251
|
+
let result;
|
|
252
|
+
if (gate?.action === 'error') {
|
|
253
|
+
console.log(` ${chalk.red(`✗ Preflight failed (${gate.reason})`)}`);
|
|
254
|
+
result = {
|
|
255
|
+
status: 'error',
|
|
256
|
+
error: gate.reason,
|
|
257
|
+
...(gate.missingEnv.length > 0 ? { missingEnv: gate.missingEnv } : {}),
|
|
258
|
+
};
|
|
259
|
+
} else if (gate?.action === 'skip') {
|
|
260
|
+
console.log(` ${chalk.dim(`⊘ Skipped (${gate.reason})`)}`);
|
|
261
|
+
result = {
|
|
262
|
+
status: 'skipped',
|
|
263
|
+
reason: gate.reason,
|
|
264
|
+
...(gate.missingEnv.length > 0 ? { missingEnv: gate.missingEnv } : {}),
|
|
265
|
+
// A consent/scope gap is a pending HUMAN step: it rides the ⚑
|
|
266
|
+
// aggregate with its rerun hint (#228), not just the walkthrough
|
|
267
|
+
// scroll-back a dev boot's leg output buries
|
|
268
|
+
...(gate.needsInteractive ? { output: { preflight: { needsInteractive: gate.needsInteractive } } } : {}),
|
|
269
|
+
};
|
|
270
|
+
} else {
|
|
271
|
+
const startedAt = Date.now();
|
|
272
|
+
result = await runService(serviceName, brand, options);
|
|
273
|
+
// Wall time for the services that actually ran — the gated branches
|
|
274
|
+
// above step aside instantly, so timing them is noise
|
|
275
|
+
result.durationMs = Date.now() - startedAt;
|
|
276
|
+
// Printed only for the services that did work — a service that stepped
|
|
277
|
+
// aside already said so on its own line, and 20 `⏱ 0ms` lines under a
|
|
278
|
+
// mostly-skipping walk is noise (the run record keeps every number)
|
|
279
|
+
if (result.status !== 'skipped') {
|
|
280
|
+
console.log(` ${chalk.dim(`⏱ ${formatDuration(result.durationMs)}`)}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
results[serviceName] = result;
|
|
284
|
+
|
|
285
|
+
summary.add(brand.id, brand.config.brand?.name || brand.id, serviceName, result);
|
|
286
|
+
|
|
287
|
+
if (result.status === 'error' && !options.continueOnError) {
|
|
288
|
+
console.log(` ${chalk.red(`Stopping due to error in ${serviceName}`)} ${chalk.dim('(--continue-on-error to keep going)')}`);
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Persist run output — this run's transients, for post-mortem debugging
|
|
294
|
+
writeRunOutput(brandRoot, RUN_TIMESTAMP, brand.id, Object.entries(results).map(([service, result]) => ({
|
|
295
|
+
service,
|
|
296
|
+
status: result.status,
|
|
297
|
+
// Absent for services the preflight gated aside — they never ran
|
|
298
|
+
...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),
|
|
299
|
+
output: result.output ?? null,
|
|
300
|
+
error: result.error ?? null,
|
|
301
|
+
// Machine-readable WHY for skips — the pipeline command asserts on it
|
|
302
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
303
|
+
...(result.missingEnv ? { missingEnv: result.missingEnv } : {}),
|
|
304
|
+
})));
|
|
305
|
+
|
|
306
|
+
summary.printSummary();
|
|
307
|
+
|
|
308
|
+
return { hasErrors: summary.hasErrors(), results, brand };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = { runManage, runService, RUN_TIMESTAMP };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Entry for the `omega`/`omg`/`mgr` bins — dispatches to the framework that owns
|
|
2
|
+
// the cwd's target; a brand root runs the manager's CLI, and so does a fresh clone
|
|
3
|
+
// with nothing installed yet (`npx omega onboard`, the bootstrap case).
|
|
4
|
+
require('./vendor/devkit/omega-bin.js').run({
|
|
5
|
+
hostName: '@omega.js/manager',
|
|
6
|
+
hostRun: () => require('./cli-run.js').run(),
|
|
7
|
+
});
|