@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,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TikTok pixel authorization (#448, Ian 2026-08-21) — the ONE pass that puts
|
|
3
|
+
* a TikTok Events API token in a brand's hands, as an instance of the shared
|
|
4
|
+
* setup contract (#608, lib/service-input.js): ask when needed, never demand
|
|
5
|
+
* a standing key.
|
|
6
|
+
*
|
|
7
|
+
* Meta's token is minted in a dashboard and pasted, so it rides the generic
|
|
8
|
+
* paste lane. TikTok's is minted by an OAuth exchange, so its acquire is its
|
|
9
|
+
* own flow — and the app secret is a MINT-TIME credential, not a standing one:
|
|
10
|
+
*
|
|
11
|
+
* 1. the app id is public config (`analytics.providers.tiktok.appId`);
|
|
12
|
+
* 2. the app secret is pasted ONCE, masked, and never persisted anywhere;
|
|
13
|
+
* 3. the portal authorization is OFFERED (Enter-gated open, house rule: ask
|
|
14
|
+
* permission, never auto-open) and the `auth_code` it lands is pasted back;
|
|
15
|
+
* 4. the exchange runs inline;
|
|
16
|
+
* 5. ONLY the long-lived TIKTOK_ACCESS_TOKEN reaches the brand .env — the
|
|
17
|
+
* name @omega.js/backend reads.
|
|
18
|
+
*
|
|
19
|
+
* A re-authorization asks for the secret again (a company-level home for it is
|
|
20
|
+
* #446). Nothing here ever fails a walk: a headless run, a declined gate, an
|
|
21
|
+
* empty paste, or a rejected exchange all return false, and the caller keeps
|
|
22
|
+
* its warned where-to-get guidance.
|
|
23
|
+
*/
|
|
24
|
+
const chalk = require('chalk').default;
|
|
25
|
+
|
|
26
|
+
const { writeEnvValue } = require('../../../lib/env-secret.js');
|
|
27
|
+
const { canPrompt } = require('../../../lib/run-gates.js');
|
|
28
|
+
const { confirmSetup, resolveConfigValue } = require('../../../lib/config-flow.js');
|
|
29
|
+
const { exchangeAuthCode, TIKTOK_APP_URL, TIKTOK_APPS_URL, TIKTOK_AUTH_BASE, isAuthUrlFor, parseAuthCode } = require('./tiktok-api.js');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Put a long-lived TikTok access token in the environment, walking the portal
|
|
33
|
+
* authorization when the run can ask.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} context - Handler context ({ brandRoot, brandConfig, options };
|
|
36
|
+
* `tiktokExchange` is the test seam that replaces the live exchange).
|
|
37
|
+
* @param {Object} spec - The TikTok pixel spec (envVar, label, key).
|
|
38
|
+
* @param {Object} [options]
|
|
39
|
+
* @param {boolean} [options.gate] - false = the caller already ran the
|
|
40
|
+
* Provide / Skip / Disable gate for this provider.
|
|
41
|
+
* @param {Object} [options.prompt] - Test seam over devkit/prompt members.
|
|
42
|
+
* @returns {Promise<boolean>} Whether the token is now in process.env.
|
|
43
|
+
*/
|
|
44
|
+
async function acquireTikTokToken(context, spec, options = {}) {
|
|
45
|
+
const { brandRoot, brandConfig, options: runOptions = {} } = context;
|
|
46
|
+
|
|
47
|
+
if (process.env[spec.envVar]) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!canPrompt(runOptions)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const provider = brandConfig.analytics?.providers?.[spec.key] || {};
|
|
56
|
+
|
|
57
|
+
// Whether this flow's Provide / Skip / Disable gate has already been opened.
|
|
58
|
+
let gated = options.gate === false;
|
|
59
|
+
|
|
60
|
+
let appId = provider.appId;
|
|
61
|
+
if (!appId) {
|
|
62
|
+
// The app id is the developer app's — public config, so it is ASKED for
|
|
63
|
+
// right here (#635) rather than warned about, and the mint continues into
|
|
64
|
+
// the same pass on the id just entered.
|
|
65
|
+
console.log(` ${chalk.dim('Create a TikTok developer app, then copy its App ID from the app\'s Basic Information')}`);
|
|
66
|
+
appId = await resolveConfigValue(context, {
|
|
67
|
+
path: `analytics.providers.${spec.key}.appId`,
|
|
68
|
+
label: 'TikTok developer app id',
|
|
69
|
+
instructions: [
|
|
70
|
+
'1. The next step opens the TikTok developer portal',
|
|
71
|
+
'2. Create (or open) the app, then paste back its App ID',
|
|
72
|
+
`3. The authorization and the exchange run here and only the long-lived token is saved (${chalk.cyan(spec.envVar)})`,
|
|
73
|
+
],
|
|
74
|
+
entry: { url: TIKTOK_APPS_URL, message: 'Paste the TikTok developer app id:' },
|
|
75
|
+
disablePath: `analytics.providers.${spec.key}`,
|
|
76
|
+
...(gated ? { gate: false } : {}),
|
|
77
|
+
});
|
|
78
|
+
if (!appId) {
|
|
79
|
+
// Skipped, disabled, or headless — the caller keeps its guidance
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
// That ask WAS this flow's gate: asking again for the same setup would
|
|
83
|
+
// put the same question twice in one pass.
|
|
84
|
+
gated = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!gated) {
|
|
88
|
+
const action = await confirmSetup(context, {
|
|
89
|
+
label: spec.label,
|
|
90
|
+
instructions: [
|
|
91
|
+
'1. The next step opens your app\'s page in the TikTok developer portal: paste its secret, then its "Advertiser authorization URL"',
|
|
92
|
+
'2. That URL opens next: approve the advertiser account, then paste back the redirect URL (it carries the auth_code)',
|
|
93
|
+
`3. The exchange runs here and only the long-lived token is saved (${chalk.cyan(spec.envVar)})`,
|
|
94
|
+
],
|
|
95
|
+
disablePath: `analytics.providers.${spec.key}`,
|
|
96
|
+
});
|
|
97
|
+
if (action !== 'yes') {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const prompt = { ...require('../../../vendor/devkit/prompt.js'), ...options.prompt };
|
|
103
|
+
|
|
104
|
+
// The app page shows the "Advertiser authorization URL" TikTok built for the
|
|
105
|
+
// app (app id, state, and the redirect URI configured ON the app). The secret
|
|
106
|
+
// lives there too, so the page opens FIRST; the link is pasted back here, so
|
|
107
|
+
// it is never wrong and never stored (Ian 2026-08-27).
|
|
108
|
+
await prompt.pressEnterToOpen(
|
|
109
|
+
TIKTOK_APP_URL(appId),
|
|
110
|
+
`the ${spec.label} app page`,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Mint-time only: masked, held in a local, never written to .env or config
|
|
114
|
+
const secret = (await prompt.password({ message: 'TikTok app secret (used once, never saved):' }) || '').trim();
|
|
115
|
+
if (!secret) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const authUrl = (await prompt.input({ message: 'Paste the "Advertiser authorization URL" from the app page:' }) || '').trim();
|
|
120
|
+
if (!authUrl) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
if (!isAuthUrlFor(authUrl, appId)) {
|
|
124
|
+
console.log(` ${chalk.yellow('⚠')} That is not the authorization URL for app ${chalk.cyan(appId)} ${chalk.dim(`(expected ${TIKTOK_AUTH_BASE}?app_id=${appId}&…)`)}`);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
await prompt.pressEnterToOpen(authUrl, `the ${spec.label} authorization portal`);
|
|
129
|
+
|
|
130
|
+
const authCode = parseAuthCode((await prompt.input({ message: 'Paste the redirect URL (or just the auth_code):' }) || '').trim());
|
|
131
|
+
if (!authCode) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const exchange = context.tiktokExchange || ((params) => exchangeAuthCode(params));
|
|
136
|
+
|
|
137
|
+
let token;
|
|
138
|
+
try {
|
|
139
|
+
token = await exchange({ appId, secret, authCode });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
// A rejected exchange is a normal outcome of a stale/mistyped code — the
|
|
142
|
+
// caller's guidance stands and the manage walk continues
|
|
143
|
+
console.log(` ${chalk.yellow('⚠')} TikTok token exchange failed${chalk.dim(`: ${error.message}`)}`);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
writeEnvValue(brandRoot, spec.envVar, token);
|
|
148
|
+
process.env[spec.envVar] = token;
|
|
149
|
+
console.log(` ${chalk.green('✓')} ${spec.envVar} minted and saved to the brand .env`);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = { acquireTikTokToken };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate the wordmark and combomark SVGs from the brandmark + brand
|
|
3
|
+
* name + brand.font — text rendered as path outlines via opentype.js, so
|
|
4
|
+
* the output SVGs are self-contained. Generation is missing-only: an
|
|
5
|
+
* existing (possibly hand-tuned) wordmark or combomark is never
|
|
6
|
+
* overwritten.
|
|
7
|
+
*
|
|
8
|
+
* `brand.font` names a font file (without extension) resolved from the
|
|
9
|
+
* brand repo's `assets/fonts/` or the system font dirs. No font
|
|
10
|
+
* configured → nothing to generate with, noted and moved past (a brand
|
|
11
|
+
* with hand-made logo files never needs one). Configured but not found →
|
|
12
|
+
* error, the config intent failed.
|
|
13
|
+
*/
|
|
14
|
+
const { join } = require('node:path');
|
|
15
|
+
const chalk = require('chalk').default;
|
|
16
|
+
const jetpack = require('fs-jetpack');
|
|
17
|
+
const { loadFont } = require('../lib/font-loader.js');
|
|
18
|
+
const { generateWordmark, generateCombomark } = require('../lib/svg-logo-generator.js');
|
|
19
|
+
|
|
20
|
+
module.exports = async function ensureLogoGen(context) {
|
|
21
|
+
const { brandConfig, brandRoot, logoDir, brandmarkPath, options } = context;
|
|
22
|
+
const dryRun = options?.dryRun || false;
|
|
23
|
+
|
|
24
|
+
const brandName = brandConfig.brand.name;
|
|
25
|
+
const fontName = brandConfig.brand.font;
|
|
26
|
+
|
|
27
|
+
const wordmarkPath = join(logoDir, 'wordmark.svg');
|
|
28
|
+
const combomarkPath = join(logoDir, 'combomark.svg');
|
|
29
|
+
|
|
30
|
+
const missing = [
|
|
31
|
+
['wordmark', wordmarkPath],
|
|
32
|
+
['combomark', combomarkPath],
|
|
33
|
+
].filter(([, path]) => !jetpack.exists(path));
|
|
34
|
+
|
|
35
|
+
if (missing.length === 0) {
|
|
36
|
+
console.log(` ${chalk.green('✓')} Logo sources ${chalk.dim('(wordmark + combomark)')} in sync`);
|
|
37
|
+
return { output: { logos: { synced: true } } };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!fontName) {
|
|
41
|
+
console.log(` ${chalk.dim(`⊘ No brand.font configured — ${missing.map(([name]) => name).join(' + ')} not generated (set brand.font + a font file in assets/fonts/, or add the SVGs by hand)`)}`);
|
|
42
|
+
return { output: { logos: { skipped: 'no brand.font' } } };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (dryRun) {
|
|
46
|
+
console.log(` ${chalk.yellow('[DRY RUN]')} Would generate ${missing.map(([name]) => chalk.cyan(`${name}.svg`)).join(' + ')}`);
|
|
47
|
+
return { output: { logos: { planned: missing.map(([name]) => name) } } };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const font = loadFont(fontName, brandRoot);
|
|
51
|
+
if (!font) {
|
|
52
|
+
console.log(` ${chalk.red('✗')} Font ${chalk.cyan(fontName)} not found in ${chalk.dim('assets/fonts/')} or the system font dirs`);
|
|
53
|
+
return { status: 'error', error: `font "${fontName}" not found` };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const generated = [];
|
|
57
|
+
|
|
58
|
+
for (const [name, path] of missing) {
|
|
59
|
+
const svg = name === 'wordmark'
|
|
60
|
+
? generateWordmark(brandName, font)
|
|
61
|
+
: generateCombomark(brandName, jetpack.read(brandmarkPath), font);
|
|
62
|
+
jetpack.write(path, svg);
|
|
63
|
+
console.log(` ${chalk.green('✓')} Generated ${chalk.cyan(`${name}.svg`)}`);
|
|
64
|
+
generated.push(name);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { output: { logos: { generated } } };
|
|
68
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assets service — the brand's derived visual collateral generated from
|
|
3
|
+
* its logo sources: wordmark/combomark SVGs from the brandmark +
|
|
4
|
+
* brand.font, color/black SVG variants + PNG size ladders, PSD templates
|
|
5
|
+
* (seeded from the company root, logo/text layers refreshed, PNGs
|
|
6
|
+
* exported), macOS/Windows app icons, social profile icons, and the web
|
|
7
|
+
* favicon set.
|
|
8
|
+
*
|
|
9
|
+
* Sources are committed brand collateral at `assets/logo/*.svg` and
|
|
10
|
+
* `assets/templates/*.psd` in the brand repo (omega-manager read
|
|
11
|
+
* `.brands/{id}/assets/`); derived files land in the gitignored
|
|
12
|
+
* `.omega/assets/` (omega-manager's `.output/{id}/assets/`). Every
|
|
13
|
+
* operation is mtime-diffed — only missing or stale outputs regenerate —
|
|
14
|
+
* which replaces omega-manager's `--onboarding` gate on the write
|
|
15
|
+
* operations (it regenerated blindly, so running every time was too
|
|
16
|
+
* expensive; it also had no dry-run guard). `--reset-assets` forces a
|
|
17
|
+
* rebuild the timestamps would skip by clearing the cache first
|
|
18
|
+
* (lib/reset.js).
|
|
19
|
+
*
|
|
20
|
+
* The brandmark is the root of every derived asset. When it's missing
|
|
21
|
+
* and MrLogo credentials are present (MRLOGO_SERVICE_ACCOUNT /
|
|
22
|
+
* MRLOGO_API_KEY / LOGO_API_ID_TOKEN — the product-service ladder,
|
|
23
|
+
* lib/brandmark-api.js; zero config options by design), the AI generation
|
|
24
|
+
* flow runs; otherwise the service skips with guidance. Not ported:
|
|
25
|
+
* omega-manager's social-images/store-images
|
|
26
|
+
* write operations — dead code whose template-name lists never matched
|
|
27
|
+
* TEMPLATE_CONFIG keys (they could never generate anything); their
|
|
28
|
+
* intended outputs are the templates operation's og-image and
|
|
29
|
+
* store/chrome exports.
|
|
30
|
+
*/
|
|
31
|
+
const { join } = require('node:path');
|
|
32
|
+
const jetpack = require('fs-jetpack');
|
|
33
|
+
const chalk = require('chalk').default;
|
|
34
|
+
const { createServiceRunner } = require('../../lib/service-runner.js');
|
|
35
|
+
const { input } = require('../../vendor/devkit/prompt.js');
|
|
36
|
+
const { withSpinner } = require('../../vendor/devkit/flows.js');
|
|
37
|
+
const { resolveLogoAuth, generateBrandmark, MRLOGO_URL } = require('./lib/brandmark-api.js');
|
|
38
|
+
const { resolveResetKinds, resetAssetsCache } = require('./lib/reset.js');
|
|
39
|
+
const { canPrompt } = require('../../lib/run-gates.js');
|
|
40
|
+
|
|
41
|
+
module.exports.run = createServiceRunner({
|
|
42
|
+
serviceDir: __dirname,
|
|
43
|
+
setup: async (context) => {
|
|
44
|
+
const config = context.brandConfig.assets;
|
|
45
|
+
|
|
46
|
+
if (config === false || config?.enabled === false) {
|
|
47
|
+
return { skip: true, reason: 'assets.enabled = false' };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const logoDir = join(context.brandRoot, 'assets', 'logo');
|
|
51
|
+
const brandmarkPath = join(logoDir, 'brandmark.svg');
|
|
52
|
+
|
|
53
|
+
// The brandmark is the root of every derived asset — when the brand has
|
|
54
|
+
// none and MrLogo credentials resolve (the product-service ladder:
|
|
55
|
+
// operator SA → api key → pasted ID token; setting a credential IS the
|
|
56
|
+
// consent to spend the API call), generate it. Interactive runs ask for
|
|
57
|
+
// optional art direction. Dry runs never mint.
|
|
58
|
+
if (!jetpack.exists(brandmarkPath)) {
|
|
59
|
+
if (!context.options?.dryRun) {
|
|
60
|
+
try {
|
|
61
|
+
const auth = await resolveLogoAuth({
|
|
62
|
+
brandConfig: context.brandConfig,
|
|
63
|
+
brandRoot: context.brandRoot,
|
|
64
|
+
db: context.mrlogoDb,
|
|
65
|
+
authAdmin: context.mrlogoAuthAdmin,
|
|
66
|
+
log: (line) => console.log(` ${line}`),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (auth) {
|
|
70
|
+
const direction = canPrompt(context.options)
|
|
71
|
+
? (await input({ message: 'Logo prompt (press Enter to skip):', default: '' })).trim()
|
|
72
|
+
: '';
|
|
73
|
+
await withSpinner('Generating brandmark via MrLogo', () =>
|
|
74
|
+
generateBrandmark({ brandConfig: context.brandConfig, brandmarkPath, direction, token: auth.token }));
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.log(` ${chalk.yellow('⚠')} Brandmark generation failed${chalk.dim(`: ${error.message}`)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!jetpack.exists(brandmarkPath)) {
|
|
82
|
+
return { skip: true, reason: `no assets/logo/brandmark.svg in the brand repo (add the brand's logo source, set MRLOGO_SERVICE_ACCOUNT / MRLOGO_API_KEY / LOGO_API_ID_TOKEN in the brand .env for AI generation, or make one at ${MRLOGO_URL})` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const outDir = join(context.brandRoot, '.omega', 'assets');
|
|
87
|
+
|
|
88
|
+
// The force refresh (#214): clear the asked kinds before the operations
|
|
89
|
+
// run, so the mtime gate below sees them missing and rebuilds them. Loud
|
|
90
|
+
// by design, because this is the one thing the service destroys and an
|
|
91
|
+
// operator who typed the flag by accident has to see what went.
|
|
92
|
+
const resetKinds = resolveResetKinds(context.options?.resetAssets);
|
|
93
|
+
if (resetKinds.length > 0) {
|
|
94
|
+
const dryRun = context.options?.dryRun || false;
|
|
95
|
+
const { removed } = resetAssetsCache({ outDir, kinds: resetKinds, dryRun });
|
|
96
|
+
const what = `${chalk.bold(resetKinds.join(' + '))} ${chalk.dim(`(${removed.length} path${removed.length === 1 ? '' : 's'} under .omega/assets/)`)}`;
|
|
97
|
+
|
|
98
|
+
console.log(dryRun
|
|
99
|
+
? ` ${chalk.yellow('[DRY RUN]')} Would reset ${what}`
|
|
100
|
+
: ` ${chalk.yellow('↺')} Reset ${what}; regenerating below`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
logoDir,
|
|
105
|
+
brandmarkPath,
|
|
106
|
+
outDir,
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assets processing rules — which logo sources produce which derived files.
|
|
3
|
+
*
|
|
4
|
+
* Sources live in the brand repo at `assets/logo/*.svg` (committed brand
|
|
5
|
+
* collateral — omega-manager read `.brands/{id}/assets/`); derived files
|
|
6
|
+
* land in `.omega/assets/` (gitignored, regenerable — omega-manager's
|
|
7
|
+
* `.output/{id}/assets/`).
|
|
8
|
+
*
|
|
9
|
+
* Logo naming conventions:
|
|
10
|
+
* - brandmark: Square icon/symbol (no text)
|
|
11
|
+
* - wordmark: Text-only logo
|
|
12
|
+
* - combomark: Icon + text combined
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const BRANDMARK_SIZES = [16, 32, 48, 64, 128, 256, 512, 1024, 2048];
|
|
16
|
+
const WIDE_LOGO_SIZES = [128, 256, 512, 1024, 2048];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Each logo source gets two variants — the SVG as-is (colors/gradients
|
|
20
|
+
* preserved) and an all-black conversion — exported as SVG + PNGs at all
|
|
21
|
+
* sizes.
|
|
22
|
+
*/
|
|
23
|
+
const PROCESSING_RULES = {
|
|
24
|
+
brandmark: {
|
|
25
|
+
source: 'assets/logo/brandmark.svg',
|
|
26
|
+
outputDir: 'logo/brandmark',
|
|
27
|
+
square: true,
|
|
28
|
+
sizes: BRANDMARK_SIZES,
|
|
29
|
+
},
|
|
30
|
+
wordmark: {
|
|
31
|
+
source: 'assets/logo/wordmark.svg',
|
|
32
|
+
outputDir: 'logo/wordmark',
|
|
33
|
+
square: false,
|
|
34
|
+
sizes: WIDE_LOGO_SIZES,
|
|
35
|
+
},
|
|
36
|
+
combomark: {
|
|
37
|
+
source: 'assets/logo/combomark.svg',
|
|
38
|
+
outputDir: 'logo/combomark',
|
|
39
|
+
square: false,
|
|
40
|
+
sizes: WIDE_LOGO_SIZES,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Social profile icons — square brandmark on a white background for
|
|
46
|
+
* profile pictures (YouTube, Twitter, …).
|
|
47
|
+
*/
|
|
48
|
+
const SOCIAL_ICON_CONFIG = {
|
|
49
|
+
outputDir: 'social/brandmark',
|
|
50
|
+
sizes: [512, 1024, 2048],
|
|
51
|
+
padding: 0.15, // 15% padding on each side
|
|
52
|
+
background: { r: 255, g: 255, b: 255, alpha: 1 },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* PSD templates — brand-editable Photoshop sources for app icons, social
|
|
57
|
+
* images, and store promos. Each `{key}.psd` lives in the brand repo at
|
|
58
|
+
* `assets/templates/` (seeded on first run from the company root's
|
|
59
|
+
* `assets/templates/` when the brand is company-managed — omega-manager
|
|
60
|
+
* baked ITW's binaries into its own `src/defaults/`); exports land in
|
|
61
|
+
* `.omega/assets/{outputDir}/`.
|
|
62
|
+
*
|
|
63
|
+
* Layer replacement types:
|
|
64
|
+
* - logo: replace the layer canvas with the brandmark SVG
|
|
65
|
+
* - path: slash-delimited layer path (e.g. 'Main/Logo/Logo')
|
|
66
|
+
* - size: logo size as a fraction of canvas width (0-1)
|
|
67
|
+
* - bounds: [left, top, right, bottom] fractions to center within (optional)
|
|
68
|
+
* - useBlackLogo: render the pure-black brandmark variant
|
|
69
|
+
* - text: replace a text layer's content with a brandConfig value
|
|
70
|
+
* - path: slash-delimited layer path
|
|
71
|
+
* - value: dot-notation config path (e.g. 'brand.name')
|
|
72
|
+
* - maxWidth: max text width as a fraction of canvas width — shrinks the
|
|
73
|
+
* font when the value overflows
|
|
74
|
+
*/
|
|
75
|
+
const TEMPLATE_CONFIG = {
|
|
76
|
+
// --- App: macOS ---
|
|
77
|
+
'app-macos-icon': {
|
|
78
|
+
dimensions: [1024, 1024],
|
|
79
|
+
outputDir: 'app/macos',
|
|
80
|
+
outputName: 'icon',
|
|
81
|
+
layers: [
|
|
82
|
+
{ type: 'logo', path: 'Main/Logo/Logo', size: 0.575, bounds: [0.078, 0.089, 0.922, 0.933] },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
'app-macos-tray': {
|
|
86
|
+
dimensions: [32, 32],
|
|
87
|
+
outputDir: 'app/macos',
|
|
88
|
+
// The desktop framework consumes this as `tray.png` and renames its dist
|
|
89
|
+
// output to `trayTemplate.png` itself (the `Template` suffix is a macOS
|
|
90
|
+
// magic marker for dark-mode auto-inversion). Native @2x size; the
|
|
91
|
+
// framework downscales the @1x sibling.
|
|
92
|
+
outputName: 'tray',
|
|
93
|
+
layers: [
|
|
94
|
+
// macOS tray template images MUST be pure black (with alpha) so the
|
|
95
|
+
// system can invert them in dark mode
|
|
96
|
+
{ type: 'logo', path: 'Main/Logo/Logo', size: 1.0, useBlackLogo: true },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
'app-macos-dmg': {
|
|
100
|
+
dimensions: [1080, 760],
|
|
101
|
+
outputDir: 'app/macos',
|
|
102
|
+
outputName: 'dmg',
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// --- App: Windows ---
|
|
106
|
+
'app-windows-icon': {
|
|
107
|
+
dimensions: [1024, 1024],
|
|
108
|
+
outputDir: 'app/windows',
|
|
109
|
+
outputName: 'icon',
|
|
110
|
+
layers: [
|
|
111
|
+
{ type: 'logo', path: 'Main/Logo/Logo', size: 0.7 },
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// --- Social ---
|
|
116
|
+
'social-og-image': {
|
|
117
|
+
dimensions: [1200, 630],
|
|
118
|
+
outputDir: 'social',
|
|
119
|
+
outputName: 'og-image',
|
|
120
|
+
layers: [
|
|
121
|
+
{ type: 'logo', path: 'Main/Logo/Logo', size: 0.25 },
|
|
122
|
+
{ type: 'text', path: 'Main/Text/Brand Name', value: 'brand.name', maxWidth: 0.8 },
|
|
123
|
+
{ type: 'text', path: 'Main/Text/Brand Tagline', value: 'brand.tagline', maxWidth: 0.7 },
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// --- Store: Chrome Web Store ---
|
|
128
|
+
'store-chrome-promo-small': {
|
|
129
|
+
dimensions: [440, 280],
|
|
130
|
+
outputDir: 'store/chrome',
|
|
131
|
+
outputName: 'promo-small',
|
|
132
|
+
},
|
|
133
|
+
'store-chrome-promo-large': {
|
|
134
|
+
dimensions: [920, 680],
|
|
135
|
+
outputDir: 'store/chrome',
|
|
136
|
+
outputName: 'promo-large',
|
|
137
|
+
},
|
|
138
|
+
'store-chrome-promo-marquee': {
|
|
139
|
+
dimensions: [1400, 560],
|
|
140
|
+
outputDir: 'store/chrome',
|
|
141
|
+
outputName: 'promo-marquee',
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* App icon formats per platform. The composited `icon.png` from the
|
|
147
|
+
* templates operation is the source when present, else the brandmark SVG.
|
|
148
|
+
*/
|
|
149
|
+
const ICON_PLATFORMS = {
|
|
150
|
+
macos: { format: 'icns' },
|
|
151
|
+
windows: { format: 'ico' },
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Web favicon set.
|
|
156
|
+
*/
|
|
157
|
+
const FAVICON_CONFIG = {
|
|
158
|
+
outputDir: 'favicon',
|
|
159
|
+
files: [
|
|
160
|
+
{ name: 'favicon-16x16.png', size: 16 },
|
|
161
|
+
{ name: 'favicon-32x32.png', size: 32 },
|
|
162
|
+
{ name: 'apple-touch-icon.png', size: 180 },
|
|
163
|
+
{ name: 'android-chrome-192x192.png', size: 192 },
|
|
164
|
+
{ name: 'android-chrome-512x512.png', size: 512 },
|
|
165
|
+
],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
module.exports = { PROCESSING_RULES, SOCIAL_ICON_CONFIG, TEMPLATE_CONFIG, ICON_PLATFORMS, FAVICON_CONFIG };
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI brandmark generation via MrLogo (mrlogo.ai — a sibling ITW product) —
|
|
3
|
+
* the fallback when a brand has no assets/logo/brandmark.svg yet. MrLogo is
|
|
4
|
+
* OURS, so the endpoint is a product constant (like the Ghostii client's
|
|
5
|
+
* write URL) and auth rides the same credential ladder as the other product
|
|
6
|
+
* services (forms/chat/email — Ian 2026-07-14: no config options,
|
|
7
|
+
* "use an API key or service account or whatever slapform, chatsy,
|
|
8
|
+
* replyify use"):
|
|
9
|
+
*
|
|
10
|
+
* 1. MRLOGO_SERVICE_ACCOUNT (path to MrLogo's service-account JSON,
|
|
11
|
+
* absolute or brand-root-relative) — full auto: ensures the brand's
|
|
12
|
+
* OWN MrLogo product user (email = brand.contact.email, canonical
|
|
13
|
+
* account shape via lib/product-create) and calls the API with that
|
|
14
|
+
* user's api.privateKey. New users land on MrLogo's basic plan
|
|
15
|
+
* (100 credits), plenty for brandmark generation.
|
|
16
|
+
* 2. MRLOGO_API_KEY — an existing MrLogo account's api.privateKey used
|
|
17
|
+
* directly. Verified against the live wire contract: BEM's
|
|
18
|
+
* authenticate() resolves a non-JWT Bearer value by querying
|
|
19
|
+
* users.api.privateKey, and MrLogo's logos route rides that auth.
|
|
20
|
+
* 3. LOGO_API_ID_TOKEN — a pasted Firebase ID token (manual escape
|
|
21
|
+
* hatch; ID tokens expire hourly, so the durable tiers rank first).
|
|
22
|
+
*
|
|
23
|
+
* No credentials → the assets service skips with guidance (generate at
|
|
24
|
+
* mrlogo.ai by hand and drop the SVG in assets/logo/). The API returns a
|
|
25
|
+
* monochrome SVG (preferred) or color SVG URL; the download lands at
|
|
26
|
+
* assets/logo/brandmark.svg as committed collateral.
|
|
27
|
+
*/
|
|
28
|
+
const { randomUUID } = require('node:crypto');
|
|
29
|
+
const chalk = require('chalk').default;
|
|
30
|
+
const jetpack = require('fs-jetpack');
|
|
31
|
+
const { FirestoreREST, loadServiceAccount } = require('../../../lib/firestore-rest.js');
|
|
32
|
+
const { createAuthAdmin } = require('../../../lib/auth-admin.js');
|
|
33
|
+
const { ensureProductUser } = require('../../../lib/product-create.js');
|
|
34
|
+
const { createPasswordResolver } = require('../../account/lib/resolve-password.js');
|
|
35
|
+
|
|
36
|
+
// MrLogo's LIVE production route (the old-world /backend-manager path IS its
|
|
37
|
+
// api). Env override is a test/staging seam, not brand config.
|
|
38
|
+
const MRLOGO_API_URL = 'https://api.mrlogo.ai/backend-manager/logos';
|
|
39
|
+
const MRLOGO_URL = 'https://mrlogo.ai';
|
|
40
|
+
|
|
41
|
+
function apiUrl() {
|
|
42
|
+
return process.env.MRLOGO_API_URL || MRLOGO_API_URL;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the logo API's Bearer credential down the product ladder, or null
|
|
47
|
+
* when no tier is configured. The SA tier ensures the brand's own MrLogo
|
|
48
|
+
* product user and uses ITS api.privateKey — BEM auth accepts a privateKey
|
|
49
|
+
* and an ID token through the same Authorization header.
|
|
50
|
+
*
|
|
51
|
+
* @param {object} spec
|
|
52
|
+
* @param {object} spec.brandConfig - Merged brand config
|
|
53
|
+
* @param {string} spec.brandRoot - Brand root (SA paths + password channels)
|
|
54
|
+
* @param {object} [spec.db] - FirestoreREST test seam (with authAdmin)
|
|
55
|
+
* @param {object} [spec.authAdmin] - auth-admin test seam
|
|
56
|
+
* @param {function} spec.log - line logger
|
|
57
|
+
* @returns {Promise<{ token: string, source: string }|null>}
|
|
58
|
+
*/
|
|
59
|
+
async function resolveLogoAuth({ brandConfig, brandRoot, db, authAdmin, log }) {
|
|
60
|
+
// Tier 1 — operator SA: mint/reuse the brand's own product user
|
|
61
|
+
if (!db) {
|
|
62
|
+
const envPath = process.env.MRLOGO_SERVICE_ACCOUNT;
|
|
63
|
+
if (envPath) {
|
|
64
|
+
const serviceAccount = loadServiceAccount(envPath, brandRoot);
|
|
65
|
+
db = new FirestoreREST(serviceAccount);
|
|
66
|
+
authAdmin = createAuthAdmin(serviceAccount);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (db && authAdmin) {
|
|
71
|
+
const brand = brandConfig.brand || {};
|
|
72
|
+
const email = brand.contact?.email;
|
|
73
|
+
if (!email) {
|
|
74
|
+
throw new Error('MRLOGO_SERVICE_ACCOUNT is set but brand.contact.email is missing — the brand\'s product user needs it');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const { uid } = await ensureProductUser({
|
|
78
|
+
db,
|
|
79
|
+
authAdmin,
|
|
80
|
+
email,
|
|
81
|
+
resolvePassword: createPasswordResolver({
|
|
82
|
+
brandRoot,
|
|
83
|
+
domain: new URL(brand.url || 'https://invalid.test').hostname,
|
|
84
|
+
brand,
|
|
85
|
+
dryRun: false,
|
|
86
|
+
}),
|
|
87
|
+
log,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const userDoc = await db.getDoc(`users/${uid}`);
|
|
91
|
+
const privateKey = userDoc?.api?.privateKey;
|
|
92
|
+
if (!privateKey) {
|
|
93
|
+
throw new Error(`MrLogo user ${email} (${uid}) has no api.privateKey in its user doc`);
|
|
94
|
+
}
|
|
95
|
+
return { token: privateKey, source: 'MRLOGO_SERVICE_ACCOUNT' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Tier 2 — an existing account's api.privateKey, used directly
|
|
99
|
+
if (process.env.MRLOGO_API_KEY) {
|
|
100
|
+
return { token: process.env.MRLOGO_API_KEY, source: 'MRLOGO_API_KEY' };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Tier 3 — pasted Firebase ID token (expires hourly)
|
|
104
|
+
if (process.env.LOGO_API_ID_TOKEN) {
|
|
105
|
+
return { token: process.env.LOGO_API_ID_TOKEN, source: 'LOGO_API_ID_TOKEN' };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Generate the brandmark via the logo API and write it to brandmarkPath.
|
|
113
|
+
*
|
|
114
|
+
* @returns {boolean} whether the SVG landed
|
|
115
|
+
*/
|
|
116
|
+
async function generateBrandmark({ brandConfig, brandmarkPath, direction, token }) {
|
|
117
|
+
const response = await fetch(apiUrl(), {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
headers: {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
'Authorization': `Bearer ${token}`,
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
brandName: brandConfig.brand.name,
|
|
125
|
+
mode: 'brandmark',
|
|
126
|
+
style: 'flat',
|
|
127
|
+
mood: 'professional',
|
|
128
|
+
industry: brandConfig.brand.tagline || '',
|
|
129
|
+
description: [brandConfig.brand.description || '', direction].filter(Boolean).join('. '),
|
|
130
|
+
colors: ['#000000'],
|
|
131
|
+
id: randomUUID(),
|
|
132
|
+
}),
|
|
133
|
+
signal: AbortSignal.timeout(120000),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
const text = await response.text();
|
|
138
|
+
throw new Error(`logo API responded ${response.status}: ${text}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const result = await response.json();
|
|
142
|
+
const files = result.iteration?.files || {};
|
|
143
|
+
const svgUrl = files.monoSvg?.url || files.colorSvg?.url;
|
|
144
|
+
|
|
145
|
+
if (!svgUrl) {
|
|
146
|
+
console.log(` ${chalk.yellow('⚠')} No SVG in the logo API response — check the provider's logs`);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const svgResponse = await fetch(svgUrl, { signal: AbortSignal.timeout(30000) });
|
|
151
|
+
if (!svgResponse.ok) {
|
|
152
|
+
throw new Error(`failed to download the SVG (${svgResponse.status})`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
jetpack.write(brandmarkPath, await svgResponse.text());
|
|
156
|
+
console.log(` ${chalk.green('✓')} Generated ${chalk.cyan('assets/logo/brandmark.svg')} via MrLogo`);
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = { resolveLogoAuth, generateBrandmark, MRLOGO_URL };
|