@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,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure Workers + routes match `edge.providers.cloudflare.workers`
|
|
3
|
+
* [{ name, route, script, env }] — script is a filename in this service's
|
|
4
|
+
* workers/ dir; route supports `{ domain }` templating.
|
|
5
|
+
*
|
|
6
|
+
* 1. Reads existing workers + zone routes + account ID.
|
|
7
|
+
* 2. For each configured worker: load script from disk, mark create/update;
|
|
8
|
+
* for each route: ensure it exists and points at the right script.
|
|
9
|
+
* Unconfigured workers/routes are removed.
|
|
10
|
+
* 3. PUTs worker scripts as multipart form data, POSTs/PUTs/DELETEs routes.
|
|
11
|
+
*/
|
|
12
|
+
const { join } = require('node:path');
|
|
13
|
+
const chalk = require('chalk').default;
|
|
14
|
+
const jetpack = require('fs-jetpack');
|
|
15
|
+
const { templateObject } = require('../../../config.js');
|
|
16
|
+
const { cacheRead } = require('../lib/read-cache.js');
|
|
17
|
+
const { getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
|
|
18
|
+
const { dryRunPlan } = require('../../../lib/run-gates.js');
|
|
19
|
+
|
|
20
|
+
async function getAccountId(api, zoneId) {
|
|
21
|
+
const zone = await api.makeRequest(`/zones/${zoneId}`);
|
|
22
|
+
return zone.result.account.id;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function buildScriptFormData(scriptContent, env) {
|
|
26
|
+
const formData = new FormData();
|
|
27
|
+
formData.append('metadata', JSON.stringify({
|
|
28
|
+
main_module: 'index.js',
|
|
29
|
+
bindings: Object.entries(env || {}).map(([key, value]) => ({
|
|
30
|
+
type: 'plain_text', name: key, text: value,
|
|
31
|
+
})),
|
|
32
|
+
}));
|
|
33
|
+
formData.append('index.js', new Blob([scriptContent], { type: 'application/javascript+module' }), 'index.js');
|
|
34
|
+
return formData;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = async function ensureWorkers(context) {
|
|
38
|
+
const { cloudflareApi: api, brandRoot, brandConfig, domain, options = {} } = context;
|
|
39
|
+
const zoneId = getZoneId(context);
|
|
40
|
+
const gated = zoneGate(context, 'workers');
|
|
41
|
+
if (gated) return gated;
|
|
42
|
+
|
|
43
|
+
// === DIFF config presence first — reads are pointless without any config ===
|
|
44
|
+
const workersConfig = brandConfig?.edge?.providers?.cloudflare?.workers;
|
|
45
|
+
if (!workersConfig || workersConfig.length === 0) {
|
|
46
|
+
console.log(` ${chalk.dim('⊘ No workers configured')}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// === READ ===
|
|
51
|
+
const accountId = await getAccountId(api, zoneId);
|
|
52
|
+
const workersResponse = await api.makeRequest(`/accounts/${accountId}/workers/scripts`);
|
|
53
|
+
const existingWorkers = workersResponse.result || [];
|
|
54
|
+
const routesResponse = await api.makeRequest(`/zones/${zoneId}/workers/routes`);
|
|
55
|
+
const existingRoutes = routesResponse.result || [];
|
|
56
|
+
|
|
57
|
+
console.log(` ${chalk.green('✓')} Read ${chalk.dim(`(${existingWorkers.length} items)`)}`);
|
|
58
|
+
cacheRead(brandRoot, 'workers', {
|
|
59
|
+
count: existingWorkers.length,
|
|
60
|
+
workers: existingWorkers,
|
|
61
|
+
routes: existingRoutes,
|
|
62
|
+
accountId,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// === DIFF ===
|
|
66
|
+
const workersToCreate = [];
|
|
67
|
+
const workersToUpdate = [];
|
|
68
|
+
const routesToCreate = [];
|
|
69
|
+
const routesToUpdate = [];
|
|
70
|
+
|
|
71
|
+
for (const workerConfig of workersConfig) {
|
|
72
|
+
const route = templateObject(workerConfig.route, { domain });
|
|
73
|
+
const scriptPath = join(__dirname, '..', 'workers', workerConfig.script);
|
|
74
|
+
const scriptContent = jetpack.read(scriptPath);
|
|
75
|
+
|
|
76
|
+
if (!scriptContent) {
|
|
77
|
+
console.error(` ${chalk.red('✗')} Worker script not found: ${chalk.cyan(scriptPath)}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const existingWorker = existingWorkers.find((w) => w.id === workerConfig.name);
|
|
82
|
+
if (existingWorker) {
|
|
83
|
+
workersToUpdate.push({ name: workerConfig.name, script: scriptContent, env: workerConfig.env || {} });
|
|
84
|
+
} else {
|
|
85
|
+
console.log(` ${chalk.dim('·')} create worker ${chalk.cyan(workerConfig.name)}`);
|
|
86
|
+
workersToCreate.push({ name: workerConfig.name, script: scriptContent, env: workerConfig.env || {} });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const existingRoute = existingRoutes.find((r) => r.pattern === route);
|
|
90
|
+
if (existingRoute) {
|
|
91
|
+
if (existingRoute.script !== workerConfig.name) {
|
|
92
|
+
console.log(` ${chalk.dim('·')} update route ${chalk.cyan(route)} -> ${chalk.cyan(workerConfig.name)}`);
|
|
93
|
+
routesToUpdate.push({ id: existingRoute.id, pattern: route, script: workerConfig.name });
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
console.log(` ${chalk.dim('·')} create route ${chalk.cyan(route)} -> ${chalk.cyan(workerConfig.name)}`);
|
|
97
|
+
routesToCreate.push({ pattern: route, script: workerConfig.name });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Detect stale routes and workers (in Cloudflare but not in config)
|
|
102
|
+
const configRoutePatterns = new Set(workersConfig.map((w) => templateObject(w.route, { domain })));
|
|
103
|
+
const configWorkerNames = new Set(workersConfig.map((w) => w.name));
|
|
104
|
+
|
|
105
|
+
const routesToDelete = existingRoutes.filter((r) => !configRoutePatterns.has(r.pattern));
|
|
106
|
+
const workersToDelete = existingWorkers.filter((w) => !configWorkerNames.has(w.id));
|
|
107
|
+
|
|
108
|
+
for (const route of routesToDelete) {
|
|
109
|
+
console.log(` ${chalk.dim('·')} remove route ${chalk.cyan(route.pattern)}`);
|
|
110
|
+
}
|
|
111
|
+
for (const worker of workersToDelete) {
|
|
112
|
+
console.log(` ${chalk.dim('·')} remove worker ${chalk.cyan(worker.id)}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (workersToCreate.length === 0 && workersToUpdate.length === 0
|
|
116
|
+
&& routesToCreate.length === 0 && routesToUpdate.length === 0
|
|
117
|
+
&& routesToDelete.length === 0 && workersToDelete.length === 0) {
|
|
118
|
+
console.log(` ${chalk.dim('⊘ No changes needed')}`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (options.dryRun) {
|
|
123
|
+
dryRunPlan('write the worker + routes');
|
|
124
|
+
return {
|
|
125
|
+
status: 'success',
|
|
126
|
+
output: {
|
|
127
|
+
workers: {
|
|
128
|
+
planned: {
|
|
129
|
+
createWorkers: workersToCreate.length,
|
|
130
|
+
updateWorkers: workersToUpdate.length,
|
|
131
|
+
createRoutes: routesToCreate.length,
|
|
132
|
+
updateRoutes: routesToUpdate.length,
|
|
133
|
+
deleteRoutes: routesToDelete.length,
|
|
134
|
+
deleteWorkers: workersToDelete.length,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// === WRITE ===
|
|
142
|
+
const output = {
|
|
143
|
+
workersCreated: [], workersUpdated: [], routesCreated: [], routesUpdated: [],
|
|
144
|
+
routesDeleted: [], workersDeleted: [], errors: [],
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
for (const worker of [...workersToCreate, ...workersToUpdate]) {
|
|
148
|
+
const isCreate = workersToCreate.includes(worker);
|
|
149
|
+
try {
|
|
150
|
+
await api.makeRequest(`/accounts/${accountId}/workers/scripts/${worker.name}`, {
|
|
151
|
+
method: 'PUT',
|
|
152
|
+
body: buildScriptFormData(worker.script, worker.env),
|
|
153
|
+
});
|
|
154
|
+
(isCreate ? output.workersCreated : output.workersUpdated).push(worker.name);
|
|
155
|
+
console.log(` ${chalk.green('✓')} ${isCreate ? 'Created' : 'Updated'} worker: ${chalk.cyan(worker.name)}`);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
console.error(` ${chalk.red('✗')} Failed to ${isCreate ? 'create' : 'update'} worker ${chalk.cyan(worker.name)}${chalk.dim(`: ${error.message}`)}`);
|
|
158
|
+
output.errors.push({ worker: worker.name, error: error.message });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const route of routesToCreate) {
|
|
163
|
+
try {
|
|
164
|
+
await api.makeRequest(`/zones/${zoneId}/workers/routes`, {
|
|
165
|
+
method: 'POST',
|
|
166
|
+
body: JSON.stringify({ pattern: route.pattern, script: route.script }),
|
|
167
|
+
});
|
|
168
|
+
output.routesCreated.push(route.pattern);
|
|
169
|
+
console.log(` ${chalk.green('✓')} Created route: ${chalk.cyan(route.pattern)} -> ${chalk.cyan(route.script)}`);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error(` ${chalk.red('✗')} Failed to create route ${chalk.cyan(route.pattern)}${chalk.dim(`: ${error.message}`)}`);
|
|
172
|
+
output.errors.push({ route: route.pattern, error: error.message });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const route of routesToUpdate) {
|
|
177
|
+
try {
|
|
178
|
+
await api.makeRequest(`/zones/${zoneId}/workers/routes/${route.id}`, {
|
|
179
|
+
method: 'PUT',
|
|
180
|
+
body: JSON.stringify({ pattern: route.pattern, script: route.script }),
|
|
181
|
+
});
|
|
182
|
+
output.routesUpdated.push(route.pattern);
|
|
183
|
+
console.log(` ${chalk.green('✓')} Updated route: ${chalk.cyan(route.pattern)} -> ${chalk.cyan(route.script)}`);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error(` ${chalk.red('✗')} Failed to update route ${chalk.cyan(route.pattern)}${chalk.dim(`: ${error.message}`)}`);
|
|
186
|
+
output.errors.push({ route: route.pattern, error: error.message });
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Delete stale routes (before workers — routes reference worker scripts)
|
|
191
|
+
for (const route of routesToDelete) {
|
|
192
|
+
try {
|
|
193
|
+
await api.makeRequest(`/zones/${zoneId}/workers/routes/${route.id}`, { method: 'DELETE' });
|
|
194
|
+
output.routesDeleted.push(route.pattern);
|
|
195
|
+
console.log(` ${chalk.green('✓')} Removed route: ${chalk.cyan(route.pattern)}`);
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(` ${chalk.red('✗')} Failed to remove route ${chalk.cyan(route.pattern)}${chalk.dim(`: ${error.message}`)}`);
|
|
198
|
+
output.errors.push({ route: route.pattern, error: error.message });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Delete stale workers
|
|
203
|
+
for (const worker of workersToDelete) {
|
|
204
|
+
try {
|
|
205
|
+
await api.makeRequest(`/accounts/${accountId}/workers/scripts/${worker.id}`, { method: 'DELETE' });
|
|
206
|
+
output.workersDeleted.push(worker.id);
|
|
207
|
+
console.log(` ${chalk.green('✓')} Removed worker: ${chalk.cyan(worker.id)}`);
|
|
208
|
+
} catch (error) {
|
|
209
|
+
console.error(` ${chalk.red('✗')} Failed to remove worker ${chalk.cyan(worker.id)}${chalk.dim(`: ${error.message}`)}`);
|
|
210
|
+
output.errors.push({ worker: worker.id, error: error.message });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
status: output.errors.length > 0 ? 'warned' : 'success',
|
|
216
|
+
...(output.errors.length > 0 ? { reason: `${output.errors.length} worker(s) failed: ${output.errors.map((e) => e.worker).join(', ')}` } : {}),
|
|
217
|
+
output: { workers: output },
|
|
218
|
+
};
|
|
219
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure zone settings match `edge.providers.cloudflare.settings`.
|
|
3
|
+
*
|
|
4
|
+
* 1. Reads all settings in one bulk call + any addon settings individually.
|
|
5
|
+
* 2. Diffs against desired settings — skips read-only and absent ones.
|
|
6
|
+
* 3. Patches each changed setting (one API call per setting — Cloudflare has no bulk PATCH).
|
|
7
|
+
*
|
|
8
|
+
* Failures on individual settings don't block the rest.
|
|
9
|
+
*/
|
|
10
|
+
const chalk = require('chalk').default;
|
|
11
|
+
const { DEFAULTS } = require('../../../config.js');
|
|
12
|
+
const { cacheRead } = require('../lib/read-cache.js');
|
|
13
|
+
const { getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
|
|
14
|
+
const { dryRunPlan } = require('../../../lib/run-gates.js');
|
|
15
|
+
|
|
16
|
+
// Settings not returned by /zones/{id}/settings — each needs its own GET.
|
|
17
|
+
const ADDON_SETTINGS = ['speed_brain', 'fonts'];
|
|
18
|
+
|
|
19
|
+
module.exports = async function ensureZoneSettings(context) {
|
|
20
|
+
const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
|
|
21
|
+
const zoneId = getZoneId(context);
|
|
22
|
+
const gated = zoneGate(context, 'zoneSettings');
|
|
23
|
+
if (gated) return gated;
|
|
24
|
+
|
|
25
|
+
// === READ ===
|
|
26
|
+
const bulkResponse = await api.makeRequest(`/zones/${zoneId}/settings`);
|
|
27
|
+
const settings = {};
|
|
28
|
+
for (const s of bulkResponse.result) {
|
|
29
|
+
settings[s.id] = { value: s.value, editable: s.editable !== false };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const targetAddons = ADDON_SETTINGS.filter((id) => Object.keys(DEFAULTS.edge.providers.cloudflare.settings).includes(id));
|
|
33
|
+
for (const id of targetAddons) {
|
|
34
|
+
if (settings[id]) continue;
|
|
35
|
+
try {
|
|
36
|
+
const response = await api.makeRequest(`/zones/${zoneId}/settings/${id}`);
|
|
37
|
+
settings[id] = { value: response.result.value, editable: response.result.editable !== false };
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(` ${chalk.yellow('⚠')} addon setting ${chalk.bold(id)} not available${chalk.dim(`: ${error.message}`)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
console.log(` ${chalk.green('✓')} Read ${chalk.dim(`(${Object.keys(settings).length} items)`)}`);
|
|
44
|
+
cacheRead(brandRoot, 'zone-settings', { count: Object.keys(settings).length, settings });
|
|
45
|
+
|
|
46
|
+
// === DIFF ===
|
|
47
|
+
const desired = brandConfig?.edge?.providers?.cloudflare?.settings;
|
|
48
|
+
if (!desired || typeof desired !== 'object') {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const updates = {};
|
|
53
|
+
const skipped = [];
|
|
54
|
+
|
|
55
|
+
for (const [key, desiredValue] of Object.entries(desired)) {
|
|
56
|
+
const current = settings[key];
|
|
57
|
+
if (!current) continue; // not on this plan
|
|
58
|
+
if (!current.editable) {
|
|
59
|
+
skipped.push(key);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const isObject = typeof desiredValue === 'object' && desiredValue !== null;
|
|
64
|
+
const changed = isObject
|
|
65
|
+
? JSON.stringify(current.value) !== JSON.stringify(desiredValue)
|
|
66
|
+
: current.value !== desiredValue;
|
|
67
|
+
if (!changed) continue;
|
|
68
|
+
|
|
69
|
+
updates[key] = desiredValue;
|
|
70
|
+
const currentDisplay = isObject ? '(object)' : String(current.value);
|
|
71
|
+
const desiredDisplay = isObject ? '(object)' : String(desiredValue);
|
|
72
|
+
console.log(` ${chalk.dim('·')} ${key}: ${chalk.dim(currentDisplay)} => ${chalk.cyan(desiredDisplay)}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (skipped.length > 0) {
|
|
76
|
+
console.log(` ${chalk.yellow('⚠')} ${skipped.length} read-only setting(s) skipped: ${chalk.dim(skipped.join(', '))}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (Object.keys(updates).length === 0) {
|
|
80
|
+
console.log(` ${chalk.dim('⊘ No changes needed')}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (options.dryRun) {
|
|
85
|
+
return dryRunPlan(`patch ${Object.keys(updates).length} setting(s)`, { status: 'success', output: { settings: { planned: Object.keys(updates) } } });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// === WRITE ===
|
|
89
|
+
const output = { updated: true, success: [], failed: [] };
|
|
90
|
+
|
|
91
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
92
|
+
try {
|
|
93
|
+
await api.makeRequest(`/zones/${zoneId}/settings/${key}`, {
|
|
94
|
+
method: 'PATCH',
|
|
95
|
+
body: JSON.stringify({ value }),
|
|
96
|
+
});
|
|
97
|
+
output.success.push(key);
|
|
98
|
+
const display = typeof value === 'object' ? '(object)' : String(value);
|
|
99
|
+
console.log(` ${chalk.green('✓')} ${key} => ${chalk.cyan(display)}`);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
output.failed.push({ key, error: error.message });
|
|
102
|
+
console.error(` ${chalk.red('✗')} ${key}${chalk.dim(`: ${error.message}`)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
output.count = output.success.length;
|
|
107
|
+
return { output: { settings: output } };
|
|
108
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the zone exists in Cloudflare.
|
|
3
|
+
*
|
|
4
|
+
* Creates the zone when missing — for subdomain projects that means the
|
|
5
|
+
* PARENT zone (playground.omegajs.dev → creates omegajs.dev): the zone is
|
|
6
|
+
* a prerequisite resource the brand's records live in, and creating it is
|
|
7
|
+
* inert until the registrar's nameservers point at it (which the domain
|
|
8
|
+
* service automates for API registrars, right after this service). A
|
|
9
|
+
* pending zone reports its nameservers; when the registrar is a manual
|
|
10
|
+
* one, interactive runs open its nameserver page and poll until the zone
|
|
11
|
+
* activates. Non-interactive/dry runs just report — a later run picks the
|
|
12
|
+
* zone up once it activates.
|
|
13
|
+
*
|
|
14
|
+
* The resolved id goes two places: `state: { zoneId }` for the later
|
|
15
|
+
* operations in THIS run (they read it via getZoneId), and CONFIG, at
|
|
16
|
+
* `edge.providers.cloudflare.zone` (#434) — the id `omega purge` targets
|
|
17
|
+
* from a build, where no Cloudflare token is in play. The resolved value
|
|
18
|
+
* always wins: a stale hand-set id is drift and gets patched.
|
|
19
|
+
*/
|
|
20
|
+
const chalk = require('chalk').default;
|
|
21
|
+
const { openBrowserAndPoll, pollWithSpinner } = require('../../../vendor/devkit/flows.js');
|
|
22
|
+
const { cacheRead } = require('../lib/read-cache.js');
|
|
23
|
+
const { API_PROVIDERS, REGISTRAR_NAMESERVER_URLS, resolveRegistrar } = require('../../domain/lib/registrars.js');
|
|
24
|
+
const { writeBrandConfig } = require('../../../lib/config-write.js');
|
|
25
|
+
const { canPrompt, dryRunPlan } = require('../../../lib/run-gates.js');
|
|
26
|
+
|
|
27
|
+
const CONFIG_PATH = 'edge.providers.cloudflare.zone';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Land the resolved zone id in omega.json5 (drift-gated) and hand back the
|
|
31
|
+
* within-run carry every later operation reads.
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} context - Handler context
|
|
34
|
+
* @param {Object} zone - The resolved Cloudflare zone
|
|
35
|
+
* @returns {Object} The handler's `state` block
|
|
36
|
+
*/
|
|
37
|
+
function landZoneId(context, zone) {
|
|
38
|
+
if (context.brandConfig.edge?.providers?.cloudflare?.zone !== zone.id) {
|
|
39
|
+
writeBrandConfig(context, { [CONFIG_PATH]: zone.id });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { zoneId: zone.id };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function reportPending(zone) {
|
|
46
|
+
console.log(` ${chalk.yellow('⚠')} Zone pending — set these nameservers at your domain registrar:`);
|
|
47
|
+
for (const ns of zone.name_servers || []) {
|
|
48
|
+
console.log(` ${chalk.cyan(ns)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Interactive wait for a pending zone: opens the registrar's nameserver page
|
|
54
|
+
* (when domain.providers names one with a known dashboard) and re-reads the
|
|
55
|
+
* zone until Cloudflare reports it active.
|
|
56
|
+
*
|
|
57
|
+
* @returns {Object|null} - The active zone, or null (deferred/skipped —
|
|
58
|
+
* the caller returns its pending shape)
|
|
59
|
+
*/
|
|
60
|
+
async function waitForActiveZone(context, zone) {
|
|
61
|
+
const { cloudflareApi: api, brandConfig, zoneDomain, options = {} } = context;
|
|
62
|
+
const provider = resolveRegistrar(brandConfig);
|
|
63
|
+
|
|
64
|
+
// API registrars: the domain service sets the nameservers right after this
|
|
65
|
+
// service. No provider yet: nothing to open (fresh brand). Non-interactive
|
|
66
|
+
// and dry runs never sit in a poll.
|
|
67
|
+
if (!canPrompt(options) || !provider || API_PROVIDERS.has(provider)) {
|
|
68
|
+
console.log(` ${chalk.dim('Rerun after the nameservers propagate (the domain service automates this for API registrars).')}`);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const check = async () => {
|
|
73
|
+
const fresh = (await api.makeRequest(`/zones/${zone.id}`)).result;
|
|
74
|
+
return fresh.status === 'active' ? { done: true, result: fresh } : { done: false };
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// provider is user config — unknown registrars poll without a browser step
|
|
78
|
+
const registrarUrl = REGISTRAR_NAMESERVER_URLS[provider]?.(zoneDomain);
|
|
79
|
+
const result = registrarUrl
|
|
80
|
+
? await openBrowserAndPoll({
|
|
81
|
+
url: registrarUrl,
|
|
82
|
+
label: `the ${provider} nameserver settings`,
|
|
83
|
+
promptMessage: `Set the nameservers above at ${provider}.`,
|
|
84
|
+
waitMessage: 'Checking nameservers',
|
|
85
|
+
check,
|
|
86
|
+
intervalMs: 10000,
|
|
87
|
+
indent: ' ',
|
|
88
|
+
})
|
|
89
|
+
: await pollWithSpinner({
|
|
90
|
+
check,
|
|
91
|
+
intervalMs: 10000,
|
|
92
|
+
message: 'Checking nameservers',
|
|
93
|
+
indent: ' ',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (result.success) {
|
|
97
|
+
console.log(` ${chalk.green('✓')} Nameservers configured — zone is active`);
|
|
98
|
+
return result.result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log(` ${chalk.dim('⊘ Zone still pending — rerun to check again later')}`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
module.exports = async function ensureZone(context) {
|
|
106
|
+
const { cloudflareApi: api, brandRoot, zoneDomain, isSubdomainProject, options = {} } = context;
|
|
107
|
+
|
|
108
|
+
// === READ ===
|
|
109
|
+
const accounts = await api.makeRequest('/accounts');
|
|
110
|
+
if (!accounts.result || accounts.result.length === 0) {
|
|
111
|
+
throw new Error('No Cloudflare account found for this token');
|
|
112
|
+
}
|
|
113
|
+
const accountId = accounts.result[0].id;
|
|
114
|
+
|
|
115
|
+
const allZones = await api.getAllZones();
|
|
116
|
+
let zone = allZones.find((z) => z.name === zoneDomain);
|
|
117
|
+
cacheRead(brandRoot, 'zone', { accountId, zone: zone || null });
|
|
118
|
+
|
|
119
|
+
if (zone) {
|
|
120
|
+
console.log(` ${chalk.green('✓')} Zone exists: ${chalk.cyan(zoneDomain)} ${chalk.dim(`(${zone.status})`)}`);
|
|
121
|
+
console.log(` ${chalk.dim(`Dashboard: https://dash.cloudflare.com/${accountId}/${zoneDomain}`)}`);
|
|
122
|
+
|
|
123
|
+
if (zone.status === 'pending' && !isSubdomainProject) {
|
|
124
|
+
reportPending(zone);
|
|
125
|
+
const activeZone = await waitForActiveZone(context, zone);
|
|
126
|
+
if (!activeZone) {
|
|
127
|
+
return {
|
|
128
|
+
state: landZoneId(context, zone),
|
|
129
|
+
output: { zone: { status: 'pending', nameservers: zone.name_servers } },
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
zone = activeZone;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
state: landZoneId(context, zone),
|
|
137
|
+
output: { zone: { status: zone.status } },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// === WRITE: create the zone (the PARENT zone for subdomain projects —
|
|
142
|
+
// inert until the registrar points at it, so records under the subdomain
|
|
143
|
+
// can land in the same run) ===
|
|
144
|
+
if (isSubdomainProject) {
|
|
145
|
+
console.log(` ${chalk.yellow('⚠')} Parent zone ${chalk.cyan(zoneDomain)} not in Cloudflare yet — creating it (subdomain project)`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (options.dryRun) {
|
|
149
|
+
return dryRunPlan(`add zone ${zoneDomain} to Cloudflare`, { status: 'success', output: { zone: { planned: 'create' } } });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
console.log(` Adding zone ${chalk.cyan(zoneDomain)} to Cloudflare...`);
|
|
153
|
+
|
|
154
|
+
const response = await api.makeRequest('/zones', {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
body: JSON.stringify({
|
|
157
|
+
account: { id: accountId },
|
|
158
|
+
name: zoneDomain,
|
|
159
|
+
type: 'full',
|
|
160
|
+
}),
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
zone = response.result;
|
|
164
|
+
|
|
165
|
+
console.log(` ${chalk.green('✓')} Zone added ${chalk.dim(`(${zone.id})`)}`);
|
|
166
|
+
|
|
167
|
+
if (zone.status === 'pending') {
|
|
168
|
+
reportPending(zone);
|
|
169
|
+
const activeZone = await waitForActiveZone(context, zone);
|
|
170
|
+
if (!activeZone) {
|
|
171
|
+
return {
|
|
172
|
+
state: landZoneId(context, zone),
|
|
173
|
+
output: { zone: { created: true, status: 'pending', nameservers: zone.name_servers } },
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
zone = activeZone;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
state: landZoneId(context, zone),
|
|
181
|
+
output: { zone: { created: true, status: zone.status } },
|
|
182
|
+
};
|
|
183
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge service — reconciles the brand's zone to `edge.providers.cloudflare`
|
|
3
|
+
* in omega.json5: zone existence, DNS records, email routing, zone settings,
|
|
4
|
+
* rulesets (cache/redirect/configuration/response-headers/security/managed-
|
|
5
|
+
* transforms), speed tests, and workers. Ported from omega-manager; every
|
|
6
|
+
* handler follows read → diff → write because Cloudflare requires fetching
|
|
7
|
+
* current state before patching.
|
|
8
|
+
*
|
|
9
|
+
* Auth: CLOUDFLARE_TOKEN in the brand .env. No token → the service skips.
|
|
10
|
+
*
|
|
11
|
+
* Subdomain projects (brand.url is not an apex domain) use the parent zone
|
|
12
|
+
* and only run zone + dns-records — zone-level settings belong to the parent
|
|
13
|
+
* brand.
|
|
14
|
+
*
|
|
15
|
+
* Each handler caches its read step to .omega/cache/cloudflare/{op}.json.
|
|
16
|
+
*/
|
|
17
|
+
const chalk = require('chalk').default;
|
|
18
|
+
const { serviceInputSpec } = require('../../config.js');
|
|
19
|
+
const { createServiceRunner } = require('../../lib/service-runner.js');
|
|
20
|
+
const { CloudflareAPI } = require('./lib/cloudflare-api.js');
|
|
21
|
+
const { getApexDomain } = require('../../lib/domain-utils.js');
|
|
22
|
+
const { requestServiceInput } = require('../../lib/service-input.js');
|
|
23
|
+
|
|
24
|
+
// Operations allowed for subdomain projects (zone-level settings are skipped)
|
|
25
|
+
const SUBDOMAIN_OPERATIONS = new Set(['zone', 'dns-records']);
|
|
26
|
+
|
|
27
|
+
module.exports.run = createServiceRunner({
|
|
28
|
+
serviceDir: __dirname,
|
|
29
|
+
setup: async (context) => {
|
|
30
|
+
const cloudflare = context.brandConfig.edge?.providers?.cloudflare || {};
|
|
31
|
+
|
|
32
|
+
if (cloudflare.enabled === false) {
|
|
33
|
+
return { skip: true, reason: 'edge.providers.cloudflare.enabled = false' };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!context.cloudflareApi) {
|
|
37
|
+
const gate = await requestServiceInput(context, serviceInputSpec('edge'));
|
|
38
|
+
if (gate) return gate;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const domain = (context.brandConfig.brand?.url || '').replace(/^https?:\/\//, '');
|
|
42
|
+
if (!domain) {
|
|
43
|
+
return { skip: true, reason: 'no brand.url configured' };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Tests inject a fake client via context.cloudflareApi
|
|
47
|
+
const api = context.cloudflareApi || new CloudflareAPI();
|
|
48
|
+
|
|
49
|
+
const apexDomain = getApexDomain(domain);
|
|
50
|
+
const isSubdomainProject = domain !== apexDomain;
|
|
51
|
+
const zoneDomain = apexDomain;
|
|
52
|
+
const zone = await api.getZoneByName(zoneDomain);
|
|
53
|
+
|
|
54
|
+
if (isSubdomainProject) {
|
|
55
|
+
console.log(` Subdomain project detected: ${chalk.cyan(domain)}`);
|
|
56
|
+
console.log(` Using parent zone: ${chalk.cyan(zoneDomain)}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const filteredOperations = isSubdomainProject
|
|
60
|
+
? context.operations.filter((op) => SUBDOMAIN_OPERATIONS.has(op.name))
|
|
61
|
+
: context.operations;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
cloudflareApi: api,
|
|
65
|
+
domain, // Full domain (e.g. app.mybrand.com)
|
|
66
|
+
zoneDomain, // Apex/zone domain (e.g. mybrand.com)
|
|
67
|
+
isSubdomainProject,
|
|
68
|
+
zoneId: zone?.id || null,
|
|
69
|
+
zone,
|
|
70
|
+
operations: filteredOperations,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare API client — omega-manager's fetch wrapper, ported verbatim.
|
|
3
|
+
* Token comes from CLOUDFLARE_TOKEN in the brand .env (loaded before services
|
|
4
|
+
* run) or is passed in directly.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const CLOUDFLARE_API_BASE = 'https://api.cloudflare.com/client/v4';
|
|
8
|
+
|
|
9
|
+
class CloudflareAPI {
|
|
10
|
+
constructor(apiToken = null) {
|
|
11
|
+
this.apiToken = apiToken || process.env.CLOUDFLARE_TOKEN;
|
|
12
|
+
|
|
13
|
+
if (!this.apiToken) {
|
|
14
|
+
throw new Error('Cloudflare API token not configured. Set CLOUDFLARE_TOKEN in the brand .env');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.headers = {
|
|
18
|
+
'Authorization': `Bearer ${this.apiToken}`,
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async makeRequest(endpoint, options = {}) {
|
|
24
|
+
const url = `${CLOUDFLARE_API_BASE}${endpoint}`;
|
|
25
|
+
|
|
26
|
+
// FormData bodies (worker uploads) must not carry a JSON Content-Type —
|
|
27
|
+
// fetch sets the multipart boundary itself
|
|
28
|
+
const isFormData = options.body instanceof FormData;
|
|
29
|
+
const headers = isFormData
|
|
30
|
+
? { 'Authorization': `Bearer ${this.apiToken}`, ...options.headers }
|
|
31
|
+
: { ...this.headers, ...options.headers };
|
|
32
|
+
|
|
33
|
+
const response = await fetch(url, { ...options, headers });
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
|
|
36
|
+
if (!data.success) {
|
|
37
|
+
throw new Error(`Cloudflare API Error: ${JSON.stringify(data.errors)}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return data;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find a zone by its exact name (null if not found)
|
|
45
|
+
*/
|
|
46
|
+
async getZoneByName(domain) {
|
|
47
|
+
const data = await this.makeRequest(`/zones?name=${domain}`);
|
|
48
|
+
|
|
49
|
+
if (!data.result || data.result.length === 0) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return data.result[0];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List every zone on the account (paginated)
|
|
58
|
+
*/
|
|
59
|
+
async getAllZones() {
|
|
60
|
+
const zones = [];
|
|
61
|
+
let page = 1;
|
|
62
|
+
const perPage = 50;
|
|
63
|
+
|
|
64
|
+
while (true) {
|
|
65
|
+
const data = await this.makeRequest(`/zones?page=${page}&per_page=${perPage}`);
|
|
66
|
+
|
|
67
|
+
if (!data.result || data.result.length === 0) {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
zones.push(...data.result);
|
|
72
|
+
|
|
73
|
+
if (data.result.length < perPage) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
page++;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return zones;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
module.exports = { CloudflareAPI };
|