@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,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Captcha service (`captcha.providers.recaptcha`) — validates the brand's
|
|
3
|
+
* OWN classic reCAPTCHA keys (de-ITW: every brand mints its own key in its
|
|
4
|
+
* own GCP project — never a company-shared key; interactive runs ASK for the
|
|
5
|
+
* keys via the cp114 paste flow, pointing at the GCP reCAPTCHA console).
|
|
6
|
+
*
|
|
7
|
+
* Classic reCAPTCHA has no key-management API, so the key's domain list
|
|
8
|
+
* can't be reconciled; this service proves what it can (the secret key
|
|
9
|
+
* in .env is valid, via the documented siteverify endpoint) and prints the
|
|
10
|
+
* console link + domains for the manual half. Interactive runs open the
|
|
11
|
+
* console and confirm the domain list once, stamping config (#434).
|
|
12
|
+
*
|
|
13
|
+
* It also owns the ORPHAN-SECRET warning (#507): the secret in .env without
|
|
14
|
+
* `captcha.providers.recaptcha.siteKey` in config — see the guard below. The
|
|
15
|
+
* reverse direction is the env schema's shared `requiredWhen` rule (#626).
|
|
16
|
+
*
|
|
17
|
+
* Auth: RECAPTCHA_SITE_KEY + RECAPTCHA_SECRET_KEY in the brand .env.
|
|
18
|
+
* Missing keys → interactive runs ask; non-interactive runs skip with guidance.
|
|
19
|
+
*/
|
|
20
|
+
const chalk = require('chalk').default;
|
|
21
|
+
|
|
22
|
+
const { serviceInputSpec } = require('../../config.js');
|
|
23
|
+
const { createServiceRunner } = require('../../lib/service-runner.js');
|
|
24
|
+
const { requestServiceInput } = require('../../lib/service-input.js');
|
|
25
|
+
const { RecaptchaAPI } = require('./lib/recaptcha-api.js');
|
|
26
|
+
const { recaptchaConsoleUrl } = require('./lib/console-url.js');
|
|
27
|
+
|
|
28
|
+
module.exports.run = createServiceRunner({
|
|
29
|
+
serviceDir: __dirname,
|
|
30
|
+
setup: async (context) => {
|
|
31
|
+
const recaptchaConfig = context.brandConfig.captcha?.providers?.recaptcha || {};
|
|
32
|
+
|
|
33
|
+
if (recaptchaConfig.enabled === false) {
|
|
34
|
+
return { skip: true, reason: 'captcha.providers.recaptcha.enabled = false' };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The ORPHAN SECRET half (#507): RECAPTCHA_SECRET_KEY set with no
|
|
38
|
+
// captcha.providers.recaptcha.siteKey in config is a silent 403 on every
|
|
39
|
+
// protected POST — the backend enforces verification the moment the secret
|
|
40
|
+
// exists, and the client has no key to mint a token with (the playground's
|
|
41
|
+
// live checkout, 2026-08-22). This service is the ONE place that sees both
|
|
42
|
+
// halves, so it still says this direction out loud.
|
|
43
|
+
//
|
|
44
|
+
// The REVERSE direction — a site key requiring its secret — is the env
|
|
45
|
+
// schema's `requiredWhen` rule now
|
|
46
|
+
// ([#626](https://github.com/Omega-JS-Stack/omega/issues/626)): warned
|
|
47
|
+
// brand-wide by the workspace env-rules op, and refused by a production
|
|
48
|
+
// backend boot. Neither half = the sanctioned unkeyed brand (#17), which
|
|
49
|
+
// stays green and skips below.
|
|
50
|
+
const configuredSiteKey = recaptchaConfig.siteKey || null;
|
|
51
|
+
const configuredSecret = process.env.RECAPTCHA_SECRET_KEY || null;
|
|
52
|
+
if (configuredSecret && !configuredSiteKey) {
|
|
53
|
+
console.log(` ${chalk.yellow('⚠')} RECAPTCHA_SECRET_KEY is set but captcha.providers.recaptcha.siteKey is missing from config — the client sends an empty token and every protected POST 403s. Paste the site key into config/omega.json5: ${recaptchaConsoleUrl(context.brandConfig, configuredSiteKey)}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const url = (context.brandConfig.brand?.url || '').replace(/^https?:\/\//, '');
|
|
57
|
+
if (!url) {
|
|
58
|
+
return { skip: true, reason: 'no brand.url configured' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The site key is handler data (console link, domain guidance), not just
|
|
62
|
+
// auth — both keys must come from the .env even when tests inject the api
|
|
63
|
+
// De-ITW: the ask points at the GCP reCAPTCHA console (the brand's OWN
|
|
64
|
+
// project) via the registry entries — the paste flow is the ONLY path to
|
|
65
|
+
// a key; no default value exists anywhere
|
|
66
|
+
const gate = await requestServiceInput(context, serviceInputSpec('captcha'));
|
|
67
|
+
if (gate) return gate;
|
|
68
|
+
const siteKey = process.env.RECAPTCHA_SITE_KEY;
|
|
69
|
+
const secretKey = process.env.RECAPTCHA_SECRET_KEY;
|
|
70
|
+
|
|
71
|
+
// Tests inject a fake client via context.recaptchaApi
|
|
72
|
+
return {
|
|
73
|
+
recaptchaApi: context.recaptchaApi || new RecaptchaAPI({ secretKey }),
|
|
74
|
+
siteKey,
|
|
75
|
+
domain: url,
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reCAPTCHA console deep-link (#444) — the ONE place the guidance URL is
|
|
3
|
+
* built, shared by the setup's half-key guard (#507) and the site-key ensure
|
|
4
|
+
* handler's printed guidance.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The project hosting the key. captcha.providers.recaptcha.project only exists
|
|
9
|
+
* to point at a project that ISN'T the brand's own (a key minted elsewhere);
|
|
10
|
+
* with no override the key lives in the brand's cloud project, whose ONE home
|
|
11
|
+
* is cloud.config.projectId (#23) — resolved config already carries the
|
|
12
|
+
* company/brand merge.
|
|
13
|
+
*
|
|
14
|
+
* @param {object} brandConfig - Resolved brand config.
|
|
15
|
+
* @returns {string|null} Project id, or null when no layer names one.
|
|
16
|
+
*/
|
|
17
|
+
function resolveProject(brandConfig) {
|
|
18
|
+
return brandConfig.captcha?.providers?.recaptcha?.project
|
|
19
|
+
|| brandConfig.cloud?.config?.projectId
|
|
20
|
+
|| null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Deep-link to the key's settings page.
|
|
25
|
+
*
|
|
26
|
+
* omega-manager hardcoded the company GCP project here; the project resolves
|
|
27
|
+
* from config now, so the deep link builds by default. Only a project unknown
|
|
28
|
+
* at EVERY layer (or an unknown site key) falls back to the classic admin
|
|
29
|
+
* console, which lists your keys (it deep-links by internal numeric ID, not
|
|
30
|
+
* site key, so no key in the URL).
|
|
31
|
+
*
|
|
32
|
+
* @param {object} brandConfig - Resolved brand config.
|
|
33
|
+
* @param {string|null} siteKey - The key to deep-link, when known.
|
|
34
|
+
* @returns {string} Console URL.
|
|
35
|
+
*/
|
|
36
|
+
function recaptchaConsoleUrl(brandConfig, siteKey) {
|
|
37
|
+
const project = resolveProject(brandConfig);
|
|
38
|
+
|
|
39
|
+
if (project && siteKey) {
|
|
40
|
+
return `https://console.cloud.google.com/security/recaptcha/${siteKey}/overview?from=keysList&project=${project}`;
|
|
41
|
+
}
|
|
42
|
+
return 'https://www.google.com/recaptcha/admin';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module.exports = { recaptchaConsoleUrl };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Classic reCAPTCHA client. Classic reCAPTCHA has no key-management API —
|
|
3
|
+
* the only documented endpoint is siteverify, which doubles as a secret-key
|
|
4
|
+
* validity check: verifying a throwaway token answers invalid-input-response
|
|
5
|
+
* for a good secret and invalid-input-secret for a bad one.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
|
|
9
|
+
|
|
10
|
+
class RecaptchaAPI {
|
|
11
|
+
constructor({ secretKey = process.env.RECAPTCHA_SECRET_KEY } = {}) {
|
|
12
|
+
this.secretKey = secretKey;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Verify a response token against the secret key.
|
|
17
|
+
*
|
|
18
|
+
* @param {string} response - The reCAPTCHA response token to verify
|
|
19
|
+
* @returns {Promise<Object>} - Parsed siteverify JSON ({ success, 'error-codes' })
|
|
20
|
+
*/
|
|
21
|
+
async verify(response) {
|
|
22
|
+
const res = await fetch(VERIFY_URL, {
|
|
23
|
+
method: 'POST',
|
|
24
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
25
|
+
body: new URLSearchParams({ secret: this.secretKey, response }),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
throw new Error(`reCAPTCHA siteverify failed: HTTP ${res.status}`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return await res.json();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = { RecaptchaAPI };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanity checkpoint — the App Store Connect client was built in setup();
|
|
3
|
+
* this prints what's configured so the run log shows which Apple account
|
|
4
|
+
* the downstream operations act on.
|
|
5
|
+
*/
|
|
6
|
+
const chalk = require('chalk').default;
|
|
7
|
+
const { catchAgreements } = require('../lib/apple-api.js');
|
|
8
|
+
|
|
9
|
+
module.exports = catchAgreements(async (context) => {
|
|
10
|
+
const { appleClient, appleSecrets } = context;
|
|
11
|
+
|
|
12
|
+
if (!appleClient || !appleSecrets) {
|
|
13
|
+
return { status: 'error', error: 'App Store Connect client not initialized' };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
console.log(` ${chalk.green('✓')} API client ready`);
|
|
17
|
+
console.log(` ${chalk.dim(`Issuer: ${appleSecrets.issuerId}`)}`);
|
|
18
|
+
console.log(` ${chalk.dim(`Team: ${appleSecrets.teamId}`)}`);
|
|
19
|
+
console.log(` ${chalk.dim(`Key: ${appleSecrets.keyId}`)}`);
|
|
20
|
+
|
|
21
|
+
return {};
|
|
22
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the brand's bundle ID exists with the required capabilities.
|
|
3
|
+
*
|
|
4
|
+
* Bundle ID = composeBundleId(certificates.providers.apple.bundleIdPrefix, brand.id)
|
|
5
|
+
* — reverse-DNS prefix (config; the onboard wizard derives it from the
|
|
6
|
+
* company/brand domain, and a brand without one is ASKED, #635) + the brand
|
|
7
|
+
* id with hyphens as dots, e.g.
|
|
8
|
+
* com.itwcreativeworks.omega.playground. Platforms derive from the enabled
|
|
9
|
+
* targets (desktop → MACOS, mobile → IOS) and ride the returned state for
|
|
10
|
+
* the profiles handler.
|
|
11
|
+
*/
|
|
12
|
+
const chalk = require('chalk').default;
|
|
13
|
+
const { catchAgreements } = require('../lib/apple-api.js');
|
|
14
|
+
const { composeBundleId } = require('../../../lib/bundle-id.js');
|
|
15
|
+
const { resolveConfigValue } = require('../../../lib/config-flow.js');
|
|
16
|
+
|
|
17
|
+
// Where the brand's identifiers live — the page a prefix is read off of
|
|
18
|
+
const APPLE_PORTAL_IDENTIFIERS_URL = 'https://developer.apple.com/account/resources/identifiers/list';
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
listBundleIds,
|
|
22
|
+
createBundleId,
|
|
23
|
+
findBundleId,
|
|
24
|
+
listBundleIdCapabilities,
|
|
25
|
+
enableCapabilities,
|
|
26
|
+
} = require('../lib/identifier-manager.js');
|
|
27
|
+
|
|
28
|
+
module.exports = catchAgreements(async (context) => {
|
|
29
|
+
const { appleClient, brandConfig, brandId } = context;
|
|
30
|
+
const dryRun = context.options?.dryRun || false;
|
|
31
|
+
|
|
32
|
+
const appleConfig = brandConfig.certificates.providers?.apple || {};
|
|
33
|
+
|
|
34
|
+
// A value the brand must supply is ASKED for, not demanded (#635). The
|
|
35
|
+
// service already gated on its App Store Connect credentials, hence
|
|
36
|
+
// `gate: false`; a skip/disable/headless run steps aside warned, because a
|
|
37
|
+
// brand that never set a prefix is an unconfigured brand, not a broken one.
|
|
38
|
+
const prefix = appleConfig.bundleIdPrefix || await resolveConfigValue(context, {
|
|
39
|
+
path: 'certificates.providers.apple.bundleIdPrefix',
|
|
40
|
+
label: 'Apple bundle id prefix',
|
|
41
|
+
entry: {
|
|
42
|
+
url: APPLE_PORTAL_IDENTIFIERS_URL,
|
|
43
|
+
message: 'Apple bundle id prefix (reverse-DNS, e.g. com.yourcompany):',
|
|
44
|
+
},
|
|
45
|
+
disablePath: 'certificates.enabled',
|
|
46
|
+
gate: false,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (!prefix) {
|
|
50
|
+
console.log(` ${chalk.yellow('⚠')} No ${chalk.cyan('certificates.providers.apple.bundleIdPrefix')} — the bundle ID is <prefix>.<brand.id with dashes as dots>`);
|
|
51
|
+
return { status: 'warned', reason: 'no certificates.providers.apple.bundleIdPrefix', output: { bundleIds: { skipped: 'no bundleIdPrefix' } } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const bundleIdentifier = composeBundleId(prefix, brandId);
|
|
55
|
+
const targets = brandConfig.targets || {};
|
|
56
|
+
const platforms = [
|
|
57
|
+
...(targets.mobile ? ['IOS'] : []),
|
|
58
|
+
...(targets.desktop ? ['MACOS'] : []),
|
|
59
|
+
];
|
|
60
|
+
const capabilities = appleConfig.capabilities || [];
|
|
61
|
+
const brandName = brandConfig.brand?.name || brandId;
|
|
62
|
+
|
|
63
|
+
const existingBundleIds = await listBundleIds(appleClient);
|
|
64
|
+
console.log(` ${chalk.green('✓')} Found ${existingBundleIds.length} existing bundle IDs`);
|
|
65
|
+
console.log(` ${chalk.dim('•')} ${chalk.cyan(bundleIdentifier)} ${chalk.dim(`(${brandName})`)}`);
|
|
66
|
+
|
|
67
|
+
let record = findBundleId(existingBundleIds, bundleIdentifier);
|
|
68
|
+
let output;
|
|
69
|
+
|
|
70
|
+
if (record) {
|
|
71
|
+
console.log(` ${chalk.green('✓')} Bundle ID exists ${chalk.dim(`(${record.attributes.platform})`)}`);
|
|
72
|
+
output = { synced: true };
|
|
73
|
+
|
|
74
|
+
if (capabilities.length > 0) {
|
|
75
|
+
const existingCaps = await listBundleIdCapabilities(appleClient, record.id);
|
|
76
|
+
const existingCapTypes = existingCaps.map((cap) => cap.attributes.capabilityType);
|
|
77
|
+
const missing = capabilities.filter((cap) => !existingCapTypes.includes(cap));
|
|
78
|
+
|
|
79
|
+
if (missing.length > 0) {
|
|
80
|
+
if (dryRun) {
|
|
81
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would enable: ${missing.join(', ')}`);
|
|
82
|
+
output = { planned: missing.map((cap) => `enable ${cap}`) };
|
|
83
|
+
} else {
|
|
84
|
+
const results = await enableCapabilities(appleClient, record.id, missing);
|
|
85
|
+
for (const result of results) {
|
|
86
|
+
if (result.success) {
|
|
87
|
+
console.log(` ${chalk.green('✓')} ${result.capability}`);
|
|
88
|
+
} else {
|
|
89
|
+
console.log(` ${chalk.yellow('⚠')} ${result.capability} — ${chalk.gray(result.error)}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
output = { capabilitiesAdded: results.filter((r) => r.success).length };
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
console.log(` ${chalk.dim(`✓ All ${capabilities.length} capability(ies) enabled`)}`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
if (dryRun) {
|
|
100
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would create ${bundleIdentifier} with ${capabilities.join(', ') || 'no capabilities'}`);
|
|
101
|
+
return { output: { bundleIds: { planned: [`create ${bundleIdentifier}`] } } };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
record = await createBundleId(appleClient, bundleIdentifier, brandName, 'IOS', capabilities);
|
|
105
|
+
console.log(` ${chalk.green('✓')} Created bundle ID`);
|
|
106
|
+
output = { created: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
state: {
|
|
111
|
+
bundleId: { identifier: bundleIdentifier, id: record.id, platforms },
|
|
112
|
+
},
|
|
113
|
+
output: { bundleIds: output },
|
|
114
|
+
};
|
|
115
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure the account's signing certificates exist and are on disk. One set
|
|
3
|
+
* of certificates signs all of an Apple Developer account's apps, so this
|
|
4
|
+
* reconciles the account, not the brand.
|
|
5
|
+
*
|
|
6
|
+
* Per cert type in certificates.providers.apple.certificates:
|
|
7
|
+
* - valid cert on the account → download the .cer if the local file is
|
|
8
|
+
* missing (delete it to force a re-download), refresh the .p12 if stale
|
|
9
|
+
* - no valid cert + automatable → create via CSR (reused across runs —
|
|
10
|
+
* the private key pairs with the issued cert) + download + export
|
|
11
|
+
* - no valid cert + manual (DEVELOPER_ID_*) → validate the local .cer;
|
|
12
|
+
* missing/expired prints the portal URL + save path and warns (Apple
|
|
13
|
+
* requires Account Holder login to issue these — no API path)
|
|
14
|
+
*
|
|
15
|
+
* After the loop, exported .p12 files import into the macOS login keychain
|
|
16
|
+
* (best-effort, config-listed types only, never in dry-run).
|
|
17
|
+
*/
|
|
18
|
+
const { join } = require('node:path');
|
|
19
|
+
const jetpack = require('fs-jetpack');
|
|
20
|
+
const chalk = require('chalk').default;
|
|
21
|
+
const { catchAgreements } = require('../lib/apple-api.js');
|
|
22
|
+
const { canPrompt } = require('../../../lib/run-gates.js');
|
|
23
|
+
const { runManualCertWalkthrough } = require('../lib/manual-walkthrough.js');
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
listCertificates,
|
|
27
|
+
downloadCertificate,
|
|
28
|
+
createCertificate,
|
|
29
|
+
getCSRContent,
|
|
30
|
+
exportToP12,
|
|
31
|
+
findValidCertificate,
|
|
32
|
+
validateManualCertificate,
|
|
33
|
+
} = require('../lib/certificate-manager.js');
|
|
34
|
+
|
|
35
|
+
const APPLE_PORTAL_CERTIFICATES_URL = 'https://developer.apple.com/account/resources/certificates/list';
|
|
36
|
+
|
|
37
|
+
/** Trim an API cert record to the fields the later operations need. */
|
|
38
|
+
function trimCert(cert) {
|
|
39
|
+
return {
|
|
40
|
+
id: cert.id,
|
|
41
|
+
certificateType: cert.attributes.certificateType,
|
|
42
|
+
expirationDate: cert.attributes.expirationDate,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = catchAgreements(async (context) => {
|
|
47
|
+
const { appleClient, appleSecrets, appleDir, brandConfig, keychainImport } = context;
|
|
48
|
+
const dryRun = context.options?.dryRun || false;
|
|
49
|
+
|
|
50
|
+
const appleConfig = brandConfig.certificates.providers?.apple || {};
|
|
51
|
+
const certConfigs = appleConfig.certificates || [];
|
|
52
|
+
const certificatePassword = appleSecrets.certificatePassword || '';
|
|
53
|
+
|
|
54
|
+
// List once; tolerate API failure when manual certs can still be checked
|
|
55
|
+
// locally (accounts without admin API access hit this).
|
|
56
|
+
let existingCerts;
|
|
57
|
+
try {
|
|
58
|
+
existingCerts = await listCertificates(appleClient);
|
|
59
|
+
console.log(` ${chalk.green('✓')} Found ${existingCerts.length} existing certificates`);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (certConfigs.some((c) => c.manual)) {
|
|
62
|
+
console.log(` ${chalk.yellow('⚠')} Could not list certificates via API: ${chalk.gray(error.message)}`);
|
|
63
|
+
existingCerts = [];
|
|
64
|
+
} else {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const certificateMap = {};
|
|
70
|
+
const planned = [];
|
|
71
|
+
let downloaded = 0;
|
|
72
|
+
let created = 0;
|
|
73
|
+
let synced = 0;
|
|
74
|
+
let manualMissing = 0;
|
|
75
|
+
|
|
76
|
+
for (const certConfig of certConfigs) {
|
|
77
|
+
const { type, manual } = certConfig;
|
|
78
|
+
const certPath = join(appleDir, 'certificates', `${type}.cer`);
|
|
79
|
+
|
|
80
|
+
console.log(` ${chalk.dim('•')} ${chalk.cyan(type)}`);
|
|
81
|
+
|
|
82
|
+
const validCert = findValidCertificate(existingCerts, type);
|
|
83
|
+
|
|
84
|
+
if (validCert) {
|
|
85
|
+
certificateMap[type] = trimCert(validCert);
|
|
86
|
+
|
|
87
|
+
if (!jetpack.exists(certPath)) {
|
|
88
|
+
if (dryRun) {
|
|
89
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would download .cer + export .p12`);
|
|
90
|
+
planned.push(`download ${type}`);
|
|
91
|
+
} else {
|
|
92
|
+
await downloadCertificate(appleClient, validCert.id, certPath);
|
|
93
|
+
exportToP12(type, appleDir, certificatePassword);
|
|
94
|
+
downloaded++;
|
|
95
|
+
}
|
|
96
|
+
} else {
|
|
97
|
+
console.log(` ${chalk.green('✓')} Valid cert exists ${chalk.dim(`(expires ${validCert.attributes.expirationDate})`)}`);
|
|
98
|
+
if (!dryRun) {
|
|
99
|
+
exportToP12(type, appleDir, certificatePassword); // no-op unless the .p12 is stale
|
|
100
|
+
}
|
|
101
|
+
synced++;
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (manual) {
|
|
107
|
+
let validation = validateManualCertificate(certPath);
|
|
108
|
+
|
|
109
|
+
// Interactive rescue: only the Account Holder can CREATE Developer ID
|
|
110
|
+
// certs (portal-only — no API path), but the CSR, the download, and
|
|
111
|
+
// the install are ours to automate. Non-interactive runs keep the
|
|
112
|
+
// print-and-warn + converge-on-rerun contract below.
|
|
113
|
+
if (!validation.valid && canPrompt(context.options)) {
|
|
114
|
+
const { installed } = await runManualCertWalkthrough({
|
|
115
|
+
type,
|
|
116
|
+
certPath,
|
|
117
|
+
appleDir,
|
|
118
|
+
teamId: appleSecrets.teamId,
|
|
119
|
+
...(context.downloadsDir ? { downloadsDir: context.downloadsDir } : {}),
|
|
120
|
+
});
|
|
121
|
+
if (installed) {
|
|
122
|
+
validation = validateManualCertificate(certPath);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (validation.valid) {
|
|
127
|
+
const expDate = validation.expirationDate.toISOString().split('T')[0];
|
|
128
|
+
console.log(` ${chalk.green('✓')} Local file valid ${chalk.dim(`(expires ${expDate})`)}`);
|
|
129
|
+
if (!dryRun) {
|
|
130
|
+
exportToP12(type, appleDir, certificatePassword);
|
|
131
|
+
}
|
|
132
|
+
certificateMap[type] = {
|
|
133
|
+
id: 'manual',
|
|
134
|
+
certificateType: type,
|
|
135
|
+
expirationDate: validation.expirationDate.toISOString(),
|
|
136
|
+
};
|
|
137
|
+
synced++;
|
|
138
|
+
} else {
|
|
139
|
+
console.log(` ${chalk.red('✗')} ${validation.reason} — Apple requires Account Holder login to issue this type`);
|
|
140
|
+
console.log(` ${chalk.gray('→')} Download the .cer from ${chalk.cyan(APPLE_PORTAL_CERTIFICATES_URL)}`);
|
|
141
|
+
console.log(` ${chalk.gray('→')} Save it to ${chalk.gray(certPath)} and re-run`);
|
|
142
|
+
manualMissing++;
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (dryRun) {
|
|
148
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would create via CSR + download + export .p12`);
|
|
149
|
+
planned.push(`create ${type}`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const csrContent = getCSRContent(type, appleDir, appleSecrets.teamId);
|
|
154
|
+
const newCert = await createCertificate(appleClient, type, csrContent);
|
|
155
|
+
console.log(` ${chalk.green('✓')} Created ${chalk.dim(`(expires ${newCert.attributes.expirationDate})`)}`);
|
|
156
|
+
await downloadCertificate(appleClient, newCert.id, certPath);
|
|
157
|
+
exportToP12(type, appleDir, certificatePassword);
|
|
158
|
+
certificateMap[type] = trimCert(newCert);
|
|
159
|
+
created++;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Import config-listed .p12 files into the login keychain (macOS only)
|
|
163
|
+
if (!dryRun) {
|
|
164
|
+
keychainImport({
|
|
165
|
+
certificatesDir: join(appleDir, 'certificates'),
|
|
166
|
+
keychainPassword: appleSecrets.keychainPassword,
|
|
167
|
+
certificatePassword,
|
|
168
|
+
allowedTypes: certConfigs.map((c) => c.type),
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const summary = dryRun
|
|
173
|
+
? { planned }
|
|
174
|
+
: { synced, downloaded, created };
|
|
175
|
+
|
|
176
|
+
if (manualMissing > 0) {
|
|
177
|
+
console.log(` ${chalk.yellow('⚠')} ${manualMissing} manual cert(s) missing — download from the Apple Developer portal and re-run`);
|
|
178
|
+
return {
|
|
179
|
+
state: { certificateMap },
|
|
180
|
+
output: { certificates: { ...summary, manualMissing } },
|
|
181
|
+
status: 'warned',
|
|
182
|
+
reason: `${manualMissing} manual cert(s) missing — download them from the Apple Developer portal`,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return { state: { certificateMap }, output: { certificates: summary } };
|
|
187
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure provisioning profiles exist for the brand's bundle ID × every
|
|
3
|
+
* applicable cert type/platform combo, downloading each to
|
|
4
|
+
* .omega/certificates/apple/profiles/{BRAND_ID}/{CERT_TYPE}/{PLATFORM}.mobileprovision
|
|
5
|
+
* (delete the local file to force a re-download). The brand segment matters:
|
|
6
|
+
* the signing tree is shared at companyRoot, but a profile binds ONE bundle
|
|
7
|
+
* ID — without the segment, sibling brands overwrite each other's profiles.
|
|
8
|
+
*
|
|
9
|
+
* Reads the bundle ID + certificateMap the previous handlers stashed in
|
|
10
|
+
* state. Device lists are fetched lazily — only when a development
|
|
11
|
+
* profile actually needs creating (omega-manager fetched them every run).
|
|
12
|
+
*/
|
|
13
|
+
const { join } = require('node:path');
|
|
14
|
+
const jetpack = require('fs-jetpack');
|
|
15
|
+
const chalk = require('chalk').default;
|
|
16
|
+
const { catchAgreements } = require('../lib/apple-api.js');
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
listProfiles,
|
|
20
|
+
listDevices,
|
|
21
|
+
downloadProfile,
|
|
22
|
+
createProfile,
|
|
23
|
+
findValidProfile,
|
|
24
|
+
getProfileTypeForCertType,
|
|
25
|
+
certAppliesToPlatform,
|
|
26
|
+
} = require('../lib/profile-manager.js');
|
|
27
|
+
|
|
28
|
+
module.exports = catchAgreements(async (context) => {
|
|
29
|
+
const { appleClient, appleDir, brandConfig, brandId, serviceData } = context;
|
|
30
|
+
const dryRun = context.options?.dryRun || false;
|
|
31
|
+
|
|
32
|
+
const bundleId = serviceData?.bundleId;
|
|
33
|
+
if (!bundleId?.id) {
|
|
34
|
+
if (dryRun) {
|
|
35
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Bundle ID not created yet — profiles follow once it exists`);
|
|
36
|
+
return { output: { profiles: { planned: 'after bundle ID exists' } } };
|
|
37
|
+
}
|
|
38
|
+
console.log(` ${chalk.yellow('⚠')} No bundle ID resolved — skipping`);
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const certificateMap = serviceData?.certificateMap || {};
|
|
43
|
+
if (Object.keys(certificateMap).length === 0) {
|
|
44
|
+
console.log(` ${chalk.yellow('⚠')} No certificates available — skipping`);
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const profilesDir = join(appleDir, 'profiles');
|
|
49
|
+
const profileCertTypes = brandConfig.certificates.providers?.apple?.profiles || [];
|
|
50
|
+
const brandName = brandConfig.brand?.name || brandId;
|
|
51
|
+
|
|
52
|
+
const existingProfiles = await listProfiles(appleClient);
|
|
53
|
+
console.log(` ${chalk.green('✓')} Found ${existingProfiles.length} existing profiles`);
|
|
54
|
+
|
|
55
|
+
const profileMap = {};
|
|
56
|
+
const planned = [];
|
|
57
|
+
let downloaded = 0;
|
|
58
|
+
let created = 0;
|
|
59
|
+
let synced = 0;
|
|
60
|
+
let deviceIds = null;
|
|
61
|
+
|
|
62
|
+
for (const platform of bundleId.platforms) {
|
|
63
|
+
for (const certType of profileCertTypes) {
|
|
64
|
+
if (!certAppliesToPlatform(certType, platform)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const profileType = getProfileTypeForCertType(certType);
|
|
69
|
+
if (!profileType) {
|
|
70
|
+
continue; // e.g. MAC_INSTALLER_DISTRIBUTION — no profile needed
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const cert = certificateMap[certType];
|
|
74
|
+
if (!cert) {
|
|
75
|
+
console.log(` ${chalk.yellow('⚠')} Skipping ${chalk.cyan(certType)} (${platform}) — cert not available`);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const profileName = `${brandName} - ${certType} (${platform})`;
|
|
80
|
+
const profileKey = `${certType.toLowerCase()}-${platform.toLowerCase()}`;
|
|
81
|
+
const profilePath = join(profilesDir, brandId, certType, `${platform}.mobileprovision`);
|
|
82
|
+
|
|
83
|
+
console.log(` ${chalk.dim('•')} ${chalk.cyan(profileName)}`);
|
|
84
|
+
|
|
85
|
+
const validProfile = findValidProfile(existingProfiles, profileType, profileName);
|
|
86
|
+
|
|
87
|
+
if (validProfile) {
|
|
88
|
+
profileMap[profileKey] = { id: validProfile.id, expirationDate: validProfile.attributes.expirationDate };
|
|
89
|
+
|
|
90
|
+
if (!jetpack.exists(profilePath)) {
|
|
91
|
+
if (dryRun) {
|
|
92
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would download`);
|
|
93
|
+
planned.push(`download ${profileName}`);
|
|
94
|
+
} else {
|
|
95
|
+
await downloadProfile(appleClient, validProfile.id, profilePath);
|
|
96
|
+
downloaded++;
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
console.log(` ${chalk.green('✓')} Valid profile exists ${chalk.dim(`(expires ${validProfile.attributes.expirationDate})`)}`);
|
|
100
|
+
synced++;
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// A cert known only from its local file has no API id to link the
|
|
106
|
+
// profile to — the account's API key lacks permission to list it.
|
|
107
|
+
if (cert.id === 'manual') {
|
|
108
|
+
console.log(` ${chalk.yellow('⚠')} Cert known only from the local file (API didn't list it) — cannot create the profile; check the API key's permissions`);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (dryRun) {
|
|
113
|
+
console.log(` ${chalk.cyan('[DRY RUN]')} Would create + download`);
|
|
114
|
+
planned.push(`create ${profileName}`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Development profiles bind devices; distribution profiles don't
|
|
119
|
+
const requiresDevices = profileType === 'IOS_APP_DEVELOPMENT' || profileType === 'MAC_APP_DEVELOPMENT';
|
|
120
|
+
if (requiresDevices && deviceIds === null) {
|
|
121
|
+
const devices = await listDevices(appleClient);
|
|
122
|
+
deviceIds = devices.map((d) => d.id);
|
|
123
|
+
console.log(` ${chalk.green('✓')} Found ${devices.length} registered devices`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const newProfile = await createProfile(
|
|
127
|
+
appleClient,
|
|
128
|
+
profileName,
|
|
129
|
+
profileType,
|
|
130
|
+
bundleId.id,
|
|
131
|
+
[cert.id],
|
|
132
|
+
requiresDevices ? deviceIds : [],
|
|
133
|
+
);
|
|
134
|
+
console.log(` ${chalk.green('✓')} Created ${chalk.dim(`(expires ${newProfile.attributes.expirationDate})`)}`);
|
|
135
|
+
await downloadProfile(appleClient, newProfile.id, profilePath);
|
|
136
|
+
profileMap[profileKey] = { id: newProfile.id, expirationDate: newProfile.attributes.expirationDate };
|
|
137
|
+
created++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const summary = dryRun ? { planned } : { synced, downloaded, created };
|
|
142
|
+
return { state: { profiles: profileMap }, output: { profiles: summary } };
|
|
143
|
+
});
|