@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,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automation client — drives the companion Chrome extension (extension/)
|
|
3
|
+
* over its WebSocket protocol. The manager side is the SERVER: it listens
|
|
4
|
+
* on the port the extension auto-connects to (ws://localhost:9876,
|
|
5
|
+
* reconnecting every 5s), then sends OMEGA_AUTOMATE commands and awaits
|
|
6
|
+
* OMEGA_AUTOMATE_RESULT responses by correlation id. The extension turns
|
|
7
|
+
* the commands into trusted CDP input events (chrome.debugger), so the
|
|
8
|
+
* automated pages can't tell a human from the manager.
|
|
9
|
+
*
|
|
10
|
+
* Port and timeouts are constructor-injectable (tests bind ephemeral
|
|
11
|
+
* ports; OMEGA_EXTENSION_PORT overrides the default when 9876 is taken —
|
|
12
|
+
* the extension side must be pointed at the same port).
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const client = new AutomationClient();
|
|
16
|
+
* await client.connect();
|
|
17
|
+
* await client.navigate('https://example.com');
|
|
18
|
+
* await client.click('#submit');
|
|
19
|
+
* await client.type('input', 'hello');
|
|
20
|
+
* const result = await client.evaluate('document.title');
|
|
21
|
+
* await client.disconnect();
|
|
22
|
+
*/
|
|
23
|
+
const { WebSocketServer } = require('ws');
|
|
24
|
+
|
|
25
|
+
const DEFAULT_PORT = 9876;
|
|
26
|
+
const CONNECT_TIMEOUT = 15000;
|
|
27
|
+
const COMMAND_TIMEOUT = 30000;
|
|
28
|
+
|
|
29
|
+
let cmdCounter = 0;
|
|
30
|
+
|
|
31
|
+
/** The port the extension protocol runs on (env-overridable). */
|
|
32
|
+
function resolveExtensionPort() {
|
|
33
|
+
return Number(process.env.OMEGA_EXTENSION_PORT) || DEFAULT_PORT;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
class AutomationClient {
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.port = options.port || resolveExtensionPort();
|
|
39
|
+
this.connectTimeout = options.connectTimeout || CONNECT_TIMEOUT;
|
|
40
|
+
this.commandTimeout = options.commandTimeout || COMMAND_TIMEOUT;
|
|
41
|
+
this.wss = null;
|
|
42
|
+
this.ws = null;
|
|
43
|
+
this.pending = new Map(); // id → { resolve, reject, timeout }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Start the WebSocket server and wait for the extension to connect.
|
|
48
|
+
*/
|
|
49
|
+
async connect() {
|
|
50
|
+
if (this.ws) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
this.wss = new WebSocketServer({ port: this.port });
|
|
56
|
+
|
|
57
|
+
const timeout = setTimeout(() => {
|
|
58
|
+
this.wss.close();
|
|
59
|
+
this.wss = null;
|
|
60
|
+
reject(new Error('Extension did not connect (timeout). Make sure the Omega extension is installed and enabled.'));
|
|
61
|
+
}, this.connectTimeout);
|
|
62
|
+
|
|
63
|
+
this.wss.on('error', (error) => {
|
|
64
|
+
clearTimeout(timeout);
|
|
65
|
+
if (error.code === 'EADDRINUSE') {
|
|
66
|
+
reject(new Error(`Port ${this.port} already in use`));
|
|
67
|
+
} else {
|
|
68
|
+
reject(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.wss.on('connection', (ws) => {
|
|
73
|
+
clearTimeout(timeout);
|
|
74
|
+
this.ws = ws;
|
|
75
|
+
|
|
76
|
+
ws.on('message', (data) => {
|
|
77
|
+
try {
|
|
78
|
+
const msg = JSON.parse(data.toString());
|
|
79
|
+
this._handleResponse(msg);
|
|
80
|
+
} catch {
|
|
81
|
+
// Ignore parse errors
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
ws.on('close', () => {
|
|
86
|
+
this.ws = null;
|
|
87
|
+
// Reject all pending commands
|
|
88
|
+
for (const [id, entry] of this.pending) {
|
|
89
|
+
clearTimeout(entry.timeout);
|
|
90
|
+
entry.reject(new Error('Extension disconnected'));
|
|
91
|
+
this.pending.delete(id);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
resolve();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Disconnect and clean up.
|
|
102
|
+
*/
|
|
103
|
+
async disconnect() {
|
|
104
|
+
if (this.ws) {
|
|
105
|
+
this.ws.terminate();
|
|
106
|
+
this.ws = null;
|
|
107
|
+
}
|
|
108
|
+
if (this.wss) {
|
|
109
|
+
for (const client of this.wss.clients) {
|
|
110
|
+
client.terminate();
|
|
111
|
+
}
|
|
112
|
+
await new Promise((resolve) => this.wss.close(resolve));
|
|
113
|
+
this.wss = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Send a single automation command and wait for the result.
|
|
119
|
+
*/
|
|
120
|
+
async send(command, params = {}, target = {}) {
|
|
121
|
+
if (!this.ws) {
|
|
122
|
+
throw new Error('Not connected to extension');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const id = `cmd_${++cmdCounter}_${Date.now()}`;
|
|
126
|
+
|
|
127
|
+
return new Promise((resolve, reject) => {
|
|
128
|
+
const timeout = setTimeout(() => {
|
|
129
|
+
this.pending.delete(id);
|
|
130
|
+
reject(new Error(`Command "${command}" timed out after ${this.commandTimeout}ms`));
|
|
131
|
+
}, this.commandTimeout);
|
|
132
|
+
|
|
133
|
+
this.pending.set(id, { resolve, reject, timeout });
|
|
134
|
+
|
|
135
|
+
this.ws.send(JSON.stringify({
|
|
136
|
+
type: 'OMEGA_AUTOMATE',
|
|
137
|
+
id,
|
|
138
|
+
command,
|
|
139
|
+
params,
|
|
140
|
+
target,
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Handle a response message from the extension.
|
|
147
|
+
*/
|
|
148
|
+
_handleResponse(msg) {
|
|
149
|
+
if (msg.type === 'OMEGA_AUTOMATE_PROGRESS') {
|
|
150
|
+
// Progress events during sequences — informational only
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (msg.type !== 'OMEGA_AUTOMATE_RESULT') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const entry = this.pending.get(msg.id);
|
|
159
|
+
if (!entry) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
clearTimeout(entry.timeout);
|
|
164
|
+
this.pending.delete(msg.id);
|
|
165
|
+
|
|
166
|
+
if (msg.success) {
|
|
167
|
+
entry.resolve(msg.result);
|
|
168
|
+
} else {
|
|
169
|
+
const err = new Error(msg.error?.message || 'Automation command failed');
|
|
170
|
+
err.code = msg.error?.code;
|
|
171
|
+
entry.reject(err);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// =========================================================================
|
|
176
|
+
// Convenience methods
|
|
177
|
+
// =========================================================================
|
|
178
|
+
|
|
179
|
+
/** Navigate to a URL (opens a new tab when target.create is true). */
|
|
180
|
+
async navigate(url, target = {}) {
|
|
181
|
+
return this.send('navigate', { url }, target);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Click an element by CSS selector. */
|
|
185
|
+
async click(selector, options = {}) {
|
|
186
|
+
return this.send('click', { selector, ...options });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Type text into an element with trusted key events. */
|
|
190
|
+
async type(selector, text, options = {}) {
|
|
191
|
+
return this.send('type', { selector, text, ...options });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Read DOM content. */
|
|
195
|
+
async read(params = {}) {
|
|
196
|
+
return this.send('read', params);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Wait for an element, condition, or delay. */
|
|
200
|
+
async wait(params = {}) {
|
|
201
|
+
return this.send('wait', params);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Evaluate arbitrary JavaScript in the page context. */
|
|
205
|
+
async evaluate(expression) {
|
|
206
|
+
return this.send('evaluate', { expression });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
module.exports = { AutomationClient, resolveExtensionPort, DEFAULT_PORT };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @omega.js/backend's marketing SSOT — the FIELDS + SEGMENTS dictionaries that
|
|
3
|
+
* define every custom field and segment @omega.js/backend expects in the marketing
|
|
4
|
+
* providers (SendGrid, Beehiiv), plus GROUP_KEYS, the unsubscribe groups its
|
|
5
|
+
* send path names. Required straight from the @omega.js/backend
|
|
6
|
+
* workspace package so the definitions can never drift from the code that
|
|
7
|
+
* consumes them at runtime (omega-manager climbed the filesystem into the
|
|
8
|
+
* sibling repo for the same reason — this is the same SSOT, resolved
|
|
9
|
+
* properly through the workspace).
|
|
10
|
+
*
|
|
11
|
+
* omega-manager also carried an OMEGA-owned `import_batch` field here; its
|
|
12
|
+
* only writer is the legacy-contact import script, which hasn't ported —
|
|
13
|
+
* the field rides that port.
|
|
14
|
+
*/
|
|
15
|
+
const { FIELDS, SEGMENTS, GROUP_KEYS, fieldsForProvider } = require('@omega.js/backend/dist/manager/libraries/email/constants.js');
|
|
16
|
+
|
|
17
|
+
// Array shapes for handlers that iterate
|
|
18
|
+
const BEM_FIELDS = Object.entries(FIELDS).map(([name, field]) => ({
|
|
19
|
+
name,
|
|
20
|
+
display: field.display,
|
|
21
|
+
type: field.type,
|
|
22
|
+
skip: field.skip || [],
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const BEM_SEGMENTS = Object.entries(SEGMENTS).map(([name, segment]) => ({
|
|
26
|
+
name,
|
|
27
|
+
display: segment.display,
|
|
28
|
+
conditions: segment.conditions,
|
|
29
|
+
logic: segment.logic || 'and',
|
|
30
|
+
skip: segment.skip || [],
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
const BEM_FIELDS_BY_NAME = Object.fromEntries(BEM_FIELDS.map((field) => [field.name, field]));
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Fields a given provider should provision — @omega.js/backend's OWN view of its
|
|
37
|
+
* catalog (#695), so what OMEGA creates and what the backend's contact sync
|
|
38
|
+
* writes can never be two different lists.
|
|
39
|
+
*/
|
|
40
|
+
function fieldsFor(provider) {
|
|
41
|
+
return fieldsForProvider(provider).map((name) => BEM_FIELDS_BY_NAME[name]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Segments a given provider should provision (honors each segment's skip list). */
|
|
45
|
+
function segmentsFor(provider) {
|
|
46
|
+
return BEM_SEGMENTS.filter((segment) => !segment.skip.includes(provider));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = { BACKEND_FIELDS_MAP: FIELDS, BEM_FIELDS, BEM_SEGMENTS, BEM_GROUP_KEYS: GROUP_KEYS, fieldsFor, segmentsFor };
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand-monorepo loading — resolve the brand root from any cwd inside it,
|
|
3
|
+
* load config/omega.json5 through @omega.js/config (whole-file merge, manager
|
|
4
|
+
* defaults as the lowest layer), enumerate enabled targets, and discover the
|
|
5
|
+
* target dirs under targets/.
|
|
6
|
+
*
|
|
7
|
+
* This replaces omega-manager's .brands/{id}/config.json + buildBrandConfig()
|
|
8
|
+
* pair: in the brand-monorepo world the brand's own omega.json5 IS the single
|
|
9
|
+
* source of user choices — the manager reads the same file every framework
|
|
10
|
+
* reads, no mirror to disperse.
|
|
11
|
+
*
|
|
12
|
+
* Target dir → target mapping, first match wins:
|
|
13
|
+
* 1. Declared — the dir's own config/omega.json5 lists exactly the target
|
|
14
|
+
* under `targets` (key presence = enabled).
|
|
15
|
+
* 2. Directory convention — targets/website* → web, targets/backend* → backend,
|
|
16
|
+
* targets/desktop* → desktop, targets/extension* → extension, targets/mobile* → mobile.
|
|
17
|
+
* Unmapped dirs surface as workspace-service warnings, never silent skips.
|
|
18
|
+
*
|
|
19
|
+
* A CUSTOM target (#603) is neither: the brand config declares it
|
|
20
|
+
* (`targets.<name>: { type: 'custom' }`) and its dir carries no framework. It
|
|
21
|
+
* is marked `custom: true` with `target` left NULL, which is what makes every
|
|
22
|
+
* framework service's `filter((entry) => entry.target)` skip it for free —
|
|
23
|
+
* only the ONE op that must see it (the workspace service) reads the flag.
|
|
24
|
+
*
|
|
25
|
+
* A backend entry also carries `projectType` (#584) — 'firebase' (Cloud
|
|
26
|
+
* Functions) or 'custom' (the same backend as its own server on a container
|
|
27
|
+
* host). That is a framework target either way; the mode only decides which
|
|
28
|
+
* verbs its framework can serve (framework-bin.js).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const fs = require('node:fs');
|
|
32
|
+
const path = require('node:path');
|
|
33
|
+
const JSON5 = require('json5');
|
|
34
|
+
|
|
35
|
+
// resolveBrandRoot moved into @omega.js/config (cp73c) — the hierarchy walk
|
|
36
|
+
// has ONE home there, alongside findBrandRoot and the env cascade.
|
|
37
|
+
const { loadConfig, resolveConfigPath, getEnabledTargets, resolveBrandRoot, backendProjectType, normalizeTargetInstances } = require('../vendor/config/index.js');
|
|
38
|
+
const { DEFAULTS, DIR_TARGETS, templateObject } = require('../config.js');
|
|
39
|
+
const { customTargetDirs } = require('./custom-target.js');
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Read a target's raw omega.json5 (no merge, no validation) purely to see which
|
|
43
|
+
* targets it declares. Discovery must never throw — validation is the
|
|
44
|
+
* workspace service's job.
|
|
45
|
+
*/
|
|
46
|
+
function readDeclaredTargets(targetPath) {
|
|
47
|
+
const configPath = resolveConfigPath(targetPath);
|
|
48
|
+
if (!configPath) return null;
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const raw = JSON5.parse(fs.readFileSync(configPath, 'utf8'));
|
|
52
|
+
const targets = getEnabledTargets(raw);
|
|
53
|
+
return targets.length > 0 ? targets : null;
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Map a target directory name to a target via the naming convention:
|
|
61
|
+
* exact match or `<name>-*` prefix (targets/website-docs → web).
|
|
62
|
+
*/
|
|
63
|
+
function targetFromDirName(dirName) {
|
|
64
|
+
for (const [prefix, target] of Object.entries(DIR_TARGETS)) {
|
|
65
|
+
if (dirName === prefix || dirName.startsWith(`${prefix}-`)) {
|
|
66
|
+
return target;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Discover the targets under {brandRoot}/targets.
|
|
74
|
+
*
|
|
75
|
+
* A brand still carrying `apps/` fails LOUDLY here (#443): the old shape is
|
|
76
|
+
* not a brand with zero targets, it is a brand that never ran the one-time
|
|
77
|
+
* rename, and walking it as empty would quietly do the wrong thing in every
|
|
78
|
+
* service downstream. The fix is a migration, run once, by hand — nothing
|
|
79
|
+
* heals it inside a run.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} brandRoot - Absolute brand-monorepo root
|
|
82
|
+
* @returns {Array<{ name, dir, path, target, projectType, declaredTargets }>} -
|
|
83
|
+
* One entry per target directory; `target` is null when unmapped (workspace
|
|
84
|
+
* warns), and `projectType` rides the backend entry only (#584).
|
|
85
|
+
* @throws {Error} when the brand is still on the pre-#443 `apps/` shape
|
|
86
|
+
*/
|
|
87
|
+
function discoverTargets(brandRoot) {
|
|
88
|
+
// The brand file's own raw view — read here (never through loadBrand, which
|
|
89
|
+
// calls US) purely to learn which dirs are declared custom. Discovery must
|
|
90
|
+
// never throw: an unreadable brand file simply declares no custom targets,
|
|
91
|
+
// and the workspace service reports the real problem.
|
|
92
|
+
let customDirs = new Set();
|
|
93
|
+
// How the backend RUNS (#584) — 'firebase' (Cloud Functions) or 'custom'
|
|
94
|
+
// (its own server on a container host). Read from the same raw view, since
|
|
95
|
+
// it steers which verbs the fan-outs may dispatch at that target.
|
|
96
|
+
let backendMode = 'firebase';
|
|
97
|
+
try {
|
|
98
|
+
const brandConfigPath = resolveConfigPath(brandRoot);
|
|
99
|
+
if (brandConfigPath) {
|
|
100
|
+
const raw = JSON5.parse(fs.readFileSync(brandConfigPath, 'utf8'));
|
|
101
|
+
customDirs = new Set(customTargetDirs(raw));
|
|
102
|
+
backendMode = backendProjectType(normalizeTargetInstances(raw.targets?.backend)[0]);
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
// Unreadable brand config — no custom declarations to honor
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const targetsDir = path.join(brandRoot, 'targets');
|
|
109
|
+
if (!fs.existsSync(targetsDir)) {
|
|
110
|
+
if (fs.existsSync(path.join(brandRoot, 'apps'))) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`${brandRoot} still carries apps/ instead of targets/ (#443) — `
|
|
113
|
+
+ 'run `npx omega manage --migration=targets-rename --execute` once, then `npm install`.',
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const targets = [];
|
|
121
|
+
|
|
122
|
+
for (const entry of fs.readdirSync(targetsDir, { withFileTypes: true })) {
|
|
123
|
+
if (!entry.isDirectory() || entry.name.startsWith('.')) continue;
|
|
124
|
+
|
|
125
|
+
const targetPath = path.join(targetsDir, entry.name);
|
|
126
|
+
|
|
127
|
+
// A custom target (#603) has no framework to map to — the brand's own
|
|
128
|
+
// declaration is the whole mapping, and `target` stays null on purpose
|
|
129
|
+
if (customDirs.has(entry.name)) {
|
|
130
|
+
targets.push({
|
|
131
|
+
name: entry.name,
|
|
132
|
+
dir: `targets/${entry.name}`,
|
|
133
|
+
path: targetPath,
|
|
134
|
+
target: null,
|
|
135
|
+
custom: true,
|
|
136
|
+
declaredTargets: null,
|
|
137
|
+
});
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const declaredTargets = readDeclaredTargets(targetPath);
|
|
142
|
+
|
|
143
|
+
// Declared target wins; a single declaration is unambiguous, multiple
|
|
144
|
+
// declarations fall back to the directory convention as the tiebreaker.
|
|
145
|
+
let target = null;
|
|
146
|
+
if (declaredTargets && declaredTargets.length === 1) {
|
|
147
|
+
target = declaredTargets[0];
|
|
148
|
+
} else {
|
|
149
|
+
target = targetFromDirName(entry.name);
|
|
150
|
+
if (!target && declaredTargets) target = declaredTargets[0];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
targets.push({
|
|
154
|
+
name: entry.name,
|
|
155
|
+
dir: `targets/${entry.name}`,
|
|
156
|
+
path: targetPath,
|
|
157
|
+
target,
|
|
158
|
+
// Backend only: the mode decides which verbs its framework can serve
|
|
159
|
+
// (#584). Every other target has one shape, so it carries no key.
|
|
160
|
+
...(target === 'backend' ? { projectType: backendMode } : {}),
|
|
161
|
+
declaredTargets,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return targets;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Load a brand monorepo: config (manager defaults ← [company ←] brand file,
|
|
170
|
+
* whole-file merge with `{ domain }` templating applied), enabled targets,
|
|
171
|
+
* discovered target dirs.
|
|
172
|
+
*
|
|
173
|
+
* The COMPANY layer is @omega.js/config's, not ours (#83): loadConfig reads
|
|
174
|
+
* the brand's `.omega/company.json` stamp itself and folds the company file
|
|
175
|
+
* (minus `brands`) between the defaults and the brand file. The manager keeps
|
|
176
|
+
* the company config only as PROVENANCE — runManage hangs it on the loaded
|
|
177
|
+
* brand as `companyRoot`/`companyConfig` for services that ask "did this come
|
|
178
|
+
* from the company?" — never as a second fold.
|
|
179
|
+
*
|
|
180
|
+
* Config load failures (parse error, secret-shaped keys, legacy targets
|
|
181
|
+
* array) do NOT throw here — they land in `configError` so the workspace
|
|
182
|
+
* service reports them through the normal status flow.
|
|
183
|
+
*
|
|
184
|
+
* @param {string} brandRoot - Absolute brand-monorepo root
|
|
185
|
+
* @returns {{ root, id, config, configError, configErrors, enabledTargets, targets, files }}
|
|
186
|
+
*/
|
|
187
|
+
function loadBrand(brandRoot) {
|
|
188
|
+
let loaded = null;
|
|
189
|
+
let configError = null;
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
loaded = loadConfig(brandRoot, undefined, { defaults: DEFAULTS });
|
|
193
|
+
} catch (error) {
|
|
194
|
+
configError = error.message;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let config = loaded ? loaded.config : { ...DEFAULTS, brand: { id: path.basename(brandRoot) } };
|
|
198
|
+
|
|
199
|
+
// Template `{ domain }` placeholders from brand.url (omega-manager parity)
|
|
200
|
+
const url = config.brand?.url || '';
|
|
201
|
+
const domain = url.replace(/^https?:\/\//, '');
|
|
202
|
+
if (domain) {
|
|
203
|
+
config = templateObject(config, {
|
|
204
|
+
domain,
|
|
205
|
+
domainDashed: domain.replace(/\./g, '-'),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
root: brandRoot,
|
|
211
|
+
id: config.brand?.id || path.basename(brandRoot),
|
|
212
|
+
config,
|
|
213
|
+
configError,
|
|
214
|
+
configErrors: loaded ? loaded.errors : [],
|
|
215
|
+
enabledTargets: loaded ? getEnabledTargets(loaded.config) : [],
|
|
216
|
+
targets: discoverTargets(brandRoot),
|
|
217
|
+
files: loaded ? loaded.files : null,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Resolve a brand image (brand.images.<key>) to an ABSOLUTE URL for external
|
|
223
|
+
* services (Stripe product images, Chatsy avatars, …). Config stores
|
|
224
|
+
* site-relative paths (served by the web build's static channel — the site
|
|
225
|
+
* itself absolutizes per environment); external surfaces can't resolve a
|
|
226
|
+
* relative path, so join against brand.url. Full URLs pass through.
|
|
227
|
+
* @param {object} brandConfig - merged brand config
|
|
228
|
+
* @param {string} key - images key ('brandmark' | 'social' | ...)
|
|
229
|
+
* @returns {string|null} absolute URL, or null when unresolvable
|
|
230
|
+
*/
|
|
231
|
+
function absoluteBrandImage(brandConfig, key) {
|
|
232
|
+
const value = brandConfig?.brand?.images?.[key] || null;
|
|
233
|
+
if (!value) return null;
|
|
234
|
+
if (/^https?:\/\//.test(value)) return value;
|
|
235
|
+
|
|
236
|
+
const base = String(brandConfig?.brand?.url || '').replace(/\/$/, '');
|
|
237
|
+
return base ? `${base}${value.startsWith('/') ? '' : '/'}${value}` : null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = { resolveBrandRoot, loadBrand, discoverTargets, targetFromDirName, absoluteBrandImage };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle-identifier policy — reverse-DNS ids derived from the brand's web
|
|
3
|
+
* identity (Ian 2026-07-14: the prefix is the reverse domain, autoset at
|
|
4
|
+
* onboarding from the parent company when one exists, else the brand's own
|
|
5
|
+
* url).
|
|
6
|
+
*
|
|
7
|
+
* deriveBundleIdPrefix('https://itwcreativeworks.com') → 'com.itwcreativeworks'
|
|
8
|
+
* deriveBundleIdPrefix('https://playground.omegajs.dev') → 'dev.omegajs.playground'
|
|
9
|
+
* composeBundleId('com.itwcreativeworks', 'omega-playground')
|
|
10
|
+
* → 'com.itwcreativeworks.omega.playground'
|
|
11
|
+
*
|
|
12
|
+
* Sanitization keeps ids legal on BOTH stores: Apple allows hyphens but
|
|
13
|
+
* Android package segments don't, so domain labels drop non-alphanumerics
|
|
14
|
+
* and brand-id hyphens become dots (each id word is its own segment).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Reverse-DNS prefix from a company/brand url, or null when the url does
|
|
19
|
+
* not parse. `www.` is dropped; every other hostname label survives (a
|
|
20
|
+
* subdomain brand derives a subdomain-scoped prefix).
|
|
21
|
+
*
|
|
22
|
+
* @param {string} url - http(s) url (brand.url shape)
|
|
23
|
+
* @returns {string|null}
|
|
24
|
+
*/
|
|
25
|
+
function deriveBundleIdPrefix(url) {
|
|
26
|
+
let hostname;
|
|
27
|
+
try {
|
|
28
|
+
hostname = new URL(url).hostname;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const labels = hostname
|
|
34
|
+
.replace(/^www\./, '')
|
|
35
|
+
.split('.')
|
|
36
|
+
.map((label) => label.toLowerCase().replace(/[^a-z0-9]/g, ''))
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
|
|
39
|
+
return labels.length > 0 ? labels.reverse().join('.') : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Full bundle identifier: prefix + the brand id with hyphens as dots.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} prefix - reverse-DNS prefix (config or derived)
|
|
46
|
+
* @param {string} brandId - brand.id ('omega-playground')
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
function composeBundleId(prefix, brandId) {
|
|
50
|
+
return `${prefix}.${String(brandId).toLowerCase().replace(/-+/g, '.')}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { deriveBundleIdPrefix, composeBundleId };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand Claude settings ([#62](https://github.com/Omega-JS-Stack/omega/issues/62)):
|
|
3
|
+
* a brand's committed `.claude/settings.json` registers the omega marketplace
|
|
4
|
+
* that the INSTALLED `@omega.js/manager` package carries (the plugin is
|
|
5
|
+
* vendored into it at prepare time) and enables the plugin, so every session
|
|
6
|
+
* in that brand — anyone's, on any machine — loads the omega skills and hooks.
|
|
7
|
+
*
|
|
8
|
+
* Gated on a PUBLISHED install: the manager package must carry the vendored
|
|
9
|
+
* `.claude-plugin/marketplace.json` AND be a real installed directory. In the
|
|
10
|
+
* local era the brand's `@omega.js/manager` is a SYMLINK into the monorepo —
|
|
11
|
+
* whose packages/manager carries that same generated marketplace — so the
|
|
12
|
+
* link itself is the signal, and this is a silent no-op —
|
|
13
|
+
* the developer's own user-scope install serves those sessions, and the brand
|
|
14
|
+
* file must never point at a machine-specific monorepo path.
|
|
15
|
+
*
|
|
16
|
+
* Non-clobbering: only the two omega keys are ever written; every other
|
|
17
|
+
* setting in the file is preserved, and an already-correct file is not
|
|
18
|
+
* rewritten.
|
|
19
|
+
*/
|
|
20
|
+
const fs = require('node:fs');
|
|
21
|
+
const { join } = require('node:path');
|
|
22
|
+
const { isDeepStrictEqual } = require('node:util');
|
|
23
|
+
const jetpack = require('fs-jetpack');
|
|
24
|
+
|
|
25
|
+
const SETTINGS_FILE = join('.claude', 'settings.json');
|
|
26
|
+
const MARKETPLACE_KEY = 'extraKnownMarketplaces';
|
|
27
|
+
const PLUGIN_KEY = 'enabledPlugins';
|
|
28
|
+
const MARKETPLACE_NAME = 'omega';
|
|
29
|
+
const PLUGIN_ID = 'omega@omega';
|
|
30
|
+
// Brand-root-relative on purpose: portable to every machine and every clone.
|
|
31
|
+
const MANAGER_PATH = './node_modules/@omega.js/manager';
|
|
32
|
+
const MANAGER_DIR = join('node_modules', '@omega.js', 'manager');
|
|
33
|
+
const VENDORED_MARKETPLACE = join(MANAGER_DIR, '.claude-plugin', 'marketplace.json');
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The marketplace entry a brand's settings must carry.
|
|
37
|
+
*
|
|
38
|
+
* @returns {object} - The `extraKnownMarketplaces.omega` value
|
|
39
|
+
*/
|
|
40
|
+
function marketplaceEntry() {
|
|
41
|
+
return { source: { source: 'directory', path: MANAGER_PATH } };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Does this brand have a PUBLISHED manager install (one carrying the vendored
|
|
46
|
+
* plugin marketplace)?
|
|
47
|
+
*
|
|
48
|
+
* A LOCALLY LINKED manager never counts, whatever it carries: the monorepo's
|
|
49
|
+
* own packages/manager grows the vendored marketplace on every prepare/pack,
|
|
50
|
+
* and `omega i local` symlinks the brand at it — so the file resolves through
|
|
51
|
+
* the link and the marketplace check alone would write settings pointing at
|
|
52
|
+
* one developer's machine. The symlink IS the local era.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} brandRoot - Absolute brand monorepo root
|
|
55
|
+
* @returns {boolean}
|
|
56
|
+
*/
|
|
57
|
+
function hasVendoredPlugin(brandRoot) {
|
|
58
|
+
let stats;
|
|
59
|
+
try {
|
|
60
|
+
stats = fs.lstatSync(join(brandRoot, MANAGER_DIR));
|
|
61
|
+
} catch {
|
|
62
|
+
return false; // nothing installed yet
|
|
63
|
+
}
|
|
64
|
+
if (stats.isSymbolicLink()) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return jetpack.exists(join(brandRoot, VENDORED_MARKETPLACE)) === 'file';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Ensure the brand's `.claude/settings.json` registers and enables the omega
|
|
73
|
+
* plugin from its installed manager package.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} brandRoot - Absolute brand monorepo root
|
|
76
|
+
* @returns {'skipped'|'present'|'created'|'healed'|'invalid'} - What happened
|
|
77
|
+
*/
|
|
78
|
+
function ensureClaudeSettings(brandRoot) {
|
|
79
|
+
if (!hasVendoredPlugin(brandRoot)) {
|
|
80
|
+
return 'skipped';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const file = join(brandRoot, SETTINGS_FILE);
|
|
84
|
+
const raw = jetpack.read(file);
|
|
85
|
+
|
|
86
|
+
let settings = {};
|
|
87
|
+
if (raw !== undefined) {
|
|
88
|
+
try {
|
|
89
|
+
settings = JSON.parse(raw);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
// A consumer's own malformed file — expected external condition, and
|
|
92
|
+
// never ours to overwrite. The caller warns.
|
|
93
|
+
return 'invalid';
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const marketplaces = settings[MARKETPLACE_KEY] || {};
|
|
98
|
+
const plugins = settings[PLUGIN_KEY] || {};
|
|
99
|
+
if (isDeepStrictEqual(marketplaces[MARKETPLACE_NAME], marketplaceEntry()) && plugins[PLUGIN_ID] === true) {
|
|
100
|
+
return 'present';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const updated = {
|
|
104
|
+
...settings,
|
|
105
|
+
[MARKETPLACE_KEY]: { ...marketplaces, [MARKETPLACE_NAME]: marketplaceEntry() },
|
|
106
|
+
[PLUGIN_KEY]: { ...plugins, [PLUGIN_ID]: true },
|
|
107
|
+
};
|
|
108
|
+
jetpack.write(file, `${JSON.stringify(updated, null, 2)}\n`);
|
|
109
|
+
|
|
110
|
+
return raw === undefined ? 'created' : 'healed';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
SETTINGS_FILE,
|
|
115
|
+
MARKETPLACE_KEY,
|
|
116
|
+
PLUGIN_KEY,
|
|
117
|
+
MARKETPLACE_NAME,
|
|
118
|
+
PLUGIN_ID,
|
|
119
|
+
MANAGER_PATH,
|
|
120
|
+
MANAGER_DIR,
|
|
121
|
+
VENDORED_MARKETPLACE,
|
|
122
|
+
marketplaceEntry,
|
|
123
|
+
hasVendoredPlugin,
|
|
124
|
+
ensureClaudeSettings,
|
|
125
|
+
};
|