@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,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Is this tool installed?" — the ONE cross-platform PATH probe.
|
|
3
|
+
*
|
|
4
|
+
* Every framework asks it of an external CLI it shells out to (mkcert,
|
|
5
|
+
* nodemon, the Stripe CLI) and the answer decides whether a lane runs or
|
|
6
|
+
* degrades. The probe itself is platform-specific: Unix has `which`, Windows
|
|
7
|
+
* has `where`, and each host has only its own — a bare `which` on Windows is
|
|
8
|
+
* not "not installed", it's a crash the caller reads as "not installed".
|
|
9
|
+
*/
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Is a command installed? An EXISTENCE probe.
|
|
14
|
+
*
|
|
15
|
+
* **Never spawn what this returns.** The path is for REPORTING — a log line, a
|
|
16
|
+
* diagnostic, a "which one did we find". Running the tool means naming it BARE
|
|
17
|
+
* and letting the host resolve it (`spawnOnPath` in @omega.js/backend does
|
|
18
|
+
* exactly that), because on Windows `where` prints the extensionless shell shim
|
|
19
|
+
* FIRST, and neither that shim nor the `.cmd` beside it is something Node's
|
|
20
|
+
* CreateProcess can execute — only a shell can, and only a shell applies
|
|
21
|
+
* PATHEXT. Truthiness is the answer this exists to give.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} name - Command name, e.g. 'mkcert'
|
|
24
|
+
* @param {object} [options]
|
|
25
|
+
* @param {string} [options.platform] - Host platform (test seam)
|
|
26
|
+
* @param {function} [options.exec] - execSync (test seam)
|
|
27
|
+
* @returns {string|null} The first path the probe reported, or null when it is not installed
|
|
28
|
+
*/
|
|
29
|
+
function commandOnPath(name, { platform = process.platform, exec = execSync } = {}) {
|
|
30
|
+
const probe = platform === 'win32' ? 'where' : 'which';
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const found = exec(`${probe} ${name}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
34
|
+
|
|
35
|
+
// `where` prints EVERY match, one per line (`which` prints one); the first
|
|
36
|
+
// is the one the shell would pick — on Windows that is the extensionless
|
|
37
|
+
// shim, which is why this answer is reported and not spawned.
|
|
38
|
+
return String(found).split(/\r?\n/)[0].trim() || null;
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// Both probes exit nonzero when the name isn't found — the expected answer,
|
|
41
|
+
// not a failure.
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = { commandOnPath };
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The per-brand deploy record — `<target>` under the `deploy` section of
|
|
3
|
+
* `.omega/state.json` (gitignored, per-machine). Multi-instance targets key
|
|
4
|
+
* per target: the primary stays `<target>`, other instances record under
|
|
5
|
+
* `<target>:<id>` (see deployKey).
|
|
6
|
+
*
|
|
7
|
+
* Written by every framework's deploy verb on success; read by the
|
|
8
|
+
* manager's testing service to tell "never deployed" (live-URL checks skip
|
|
9
|
+
* with a nudge) from "deployed but down" (an honest error). A record-less
|
|
10
|
+
* brand whose live URL answers gets ADOPTED — recorded on the spot — so
|
|
11
|
+
* fresh clones of long-deployed brands self-heal on their first manage run
|
|
12
|
+
* (Ian 2026-07-17).
|
|
13
|
+
*
|
|
14
|
+
* state.json is the ONE per-machine record file, sectioned per fact kind
|
|
15
|
+
* (#479): future record-shaped facts join it as sibling top-level sections, so
|
|
16
|
+
* this module reads and writes ONLY `deploy` and passes every other section
|
|
17
|
+
* through verbatim — including the config-shaped keys an unmigrated brand
|
|
18
|
+
* still carries from the retired state CONTENT (#434), which belong to the
|
|
19
|
+
* state-retirement migration and to nothing here.
|
|
20
|
+
*
|
|
21
|
+
* The record spent 0.45.0 in a `.omega/deploys.json` of its own (#449); a
|
|
22
|
+
* brand still carrying that file has it adopted here, once and loudly.
|
|
23
|
+
*/
|
|
24
|
+
const fs = require('node:fs');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
const jetpack = require('fs-jetpack');
|
|
27
|
+
|
|
28
|
+
const Logger = require('./logger.js');
|
|
29
|
+
const { findBrandRoot } = require('./local.js');
|
|
30
|
+
|
|
31
|
+
const logger = new Logger('deploy-record');
|
|
32
|
+
|
|
33
|
+
function stateFile(dir) {
|
|
34
|
+
return path.join(findBrandRoot(dir), '.omega', 'state.json');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Read the machine state with its `deploy` section resolved, folding in the
|
|
39
|
+
* interim `.omega/deploys.json` (#449) on the way — one-time and LOUD, so a
|
|
40
|
+
* brand that deployed on 0.45.0 keeps its stamps and the operator sees where
|
|
41
|
+
* they went. Records already in state.json WIN: newest write, newest home.
|
|
42
|
+
*
|
|
43
|
+
* A brand whose state.json already carries a `deploy` key is home — the
|
|
44
|
+
* records are read in place, silently, whatever else the file still holds.
|
|
45
|
+
*
|
|
46
|
+
* Rewrites the files it touches, so callers hold the lock.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} file - The state.json path.
|
|
49
|
+
* @returns {Object} The machine state, `deploy` section included.
|
|
50
|
+
*/
|
|
51
|
+
function loadState(file) {
|
|
52
|
+
const state = jetpack.read(file, 'json') || {};
|
|
53
|
+
const records = state.deploy || {};
|
|
54
|
+
|
|
55
|
+
const deploysFile = path.join(path.dirname(file), 'deploys.json');
|
|
56
|
+
const deploys = jetpack.read(deploysFile, 'json');
|
|
57
|
+
|
|
58
|
+
state.deploy = deploys ? { ...deploys, ...records } : records;
|
|
59
|
+
if (!deploys) return state;
|
|
60
|
+
|
|
61
|
+
// The new home lands before the old file goes: a crash between the two
|
|
62
|
+
// writes must never leave the records with no file at all.
|
|
63
|
+
jetpack.write(file, state, { jsonIndent: 2 });
|
|
64
|
+
jetpack.remove(deploysFile);
|
|
65
|
+
|
|
66
|
+
if (Object.keys(deploys).length > 0) {
|
|
67
|
+
logger.log(`Adopted ${describe(deploys)} from .omega/deploys.json into .omega/state.json — the old file was removed.`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return state;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* "1 deploy record" / "3 deploy records" — the adoption line's subject.
|
|
75
|
+
* @param {Object} records - key → record.
|
|
76
|
+
* @returns {string}
|
|
77
|
+
*/
|
|
78
|
+
function describe(records) {
|
|
79
|
+
const count = Object.keys(records).length;
|
|
80
|
+
return `${count} deploy record${count === 1 ? '' : 's'}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Record key for a target's instance (multi-instance targets): the primary
|
|
85
|
+
* keeps today's bare target key (zero breaking change — existing records
|
|
86
|
+
* stay valid); any other instance keys `<target>:<id>`, matching the
|
|
87
|
+
* per-target deploy model (each target deploys its own instance).
|
|
88
|
+
* @param {string} target - Target key (web/backend/desktop/extension)
|
|
89
|
+
* @param {string} [instance] - Instance id ('main' or absent = the primary)
|
|
90
|
+
* @returns {string} The deploy-record key
|
|
91
|
+
*/
|
|
92
|
+
function deployKey(target, instance) {
|
|
93
|
+
return instance && instance !== 'main' ? `${target}:${instance}` : target;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Run fn under a best-effort cross-process lock on the records file, so two
|
|
98
|
+
* targets deploying in parallel can't drop each other's record in the
|
|
99
|
+
* read-modify-write. Lock contention waits briefly; a stale lock (owner
|
|
100
|
+
* crashed) is stolen after 5s; on timeout we proceed unlocked — a deploy
|
|
101
|
+
* must never fail over its bookkeeping.
|
|
102
|
+
* @param {string} file - Records file path.
|
|
103
|
+
* @param {function} fn - Critical section.
|
|
104
|
+
* @returns {*} fn's result.
|
|
105
|
+
*/
|
|
106
|
+
function withStateLock(file, fn) {
|
|
107
|
+
const lockDir = `${file}.lock`;
|
|
108
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
109
|
+
|
|
110
|
+
const deadline = Date.now() + 2000;
|
|
111
|
+
let locked = false;
|
|
112
|
+
while (Date.now() < deadline) {
|
|
113
|
+
try {
|
|
114
|
+
fs.mkdirSync(lockDir);
|
|
115
|
+
locked = true;
|
|
116
|
+
break;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
try {
|
|
119
|
+
if (Date.now() - fs.statSync(lockDir).mtimeMs > 5000) {
|
|
120
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
} catch (statError) {
|
|
124
|
+
continue; // Lock vanished between mkdir and stat — retry immediately
|
|
125
|
+
}
|
|
126
|
+
// Synchronous 25ms wait (no async surface here by design)
|
|
127
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
return fn();
|
|
133
|
+
} finally {
|
|
134
|
+
if (locked) {
|
|
135
|
+
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Record a successful deploy for a target.
|
|
142
|
+
*
|
|
143
|
+
* @param {Object} options
|
|
144
|
+
* @param {string} options.dir - Any directory inside the brand (target or root)
|
|
145
|
+
* @param {string} options.target - Target key (web/backend/desktop/extension)
|
|
146
|
+
* @param {string} [options.instance] - Instance id; non-main instances record
|
|
147
|
+
* under their own `<target>:<id>` key (per-target deploy records)
|
|
148
|
+
* @param {Object} [options.detail] - Extra fields (method, adopted, …)
|
|
149
|
+
* @returns {Object} The written record
|
|
150
|
+
*/
|
|
151
|
+
function recordDeploy(options) {
|
|
152
|
+
const file = stateFile(options.dir);
|
|
153
|
+
const key = deployKey(options.target, options.instance);
|
|
154
|
+
|
|
155
|
+
return withStateLock(file, () => {
|
|
156
|
+
const state = loadState(file);
|
|
157
|
+
|
|
158
|
+
state.deploy[key] = { at: new Date().toISOString(), ...(options.detail || {}) };
|
|
159
|
+
jetpack.write(file, state, { jsonIndent: 2 });
|
|
160
|
+
|
|
161
|
+
return state.deploy[key];
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The recorded deploy for a target's instance — null when the brand has
|
|
167
|
+
* never deployed it from this machine (and no manage run has adopted a live
|
|
168
|
+
* site yet).
|
|
169
|
+
*
|
|
170
|
+
* @param {Object} options - { dir, target, instance? }
|
|
171
|
+
* @returns {Object|null}
|
|
172
|
+
*/
|
|
173
|
+
function readDeployRecord(options) {
|
|
174
|
+
const file = stateFile(options.dir);
|
|
175
|
+
// Under the lock because the read is also where a legacy record is adopted
|
|
176
|
+
const state = withStateLock(file, () => loadState(file));
|
|
177
|
+
return state.deploy[deployKey(options.target, options.instance)] || null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = { recordDeploy, readDeployRecord, deployKey };
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive terminal flows — browser-open and wait/poll loops, TTY-safe
|
|
3
|
+
* like prompt.js. omega-manager's terminal-utils ported: openBrowserAndPoll
|
|
4
|
+
* drives the "do something in a browser, poll the API until it shows up"
|
|
5
|
+
* onboarding flows; pollWithSpinner is the wait half on its own; withSpinner
|
|
6
|
+
* wraps a slow call in a live spinner.
|
|
7
|
+
*
|
|
8
|
+
* Degradation without a TTY (CI, company-mode children, node --test):
|
|
9
|
+
* nothing prompts, nothing draws — openBrowserAndPoll prints the URL and
|
|
10
|
+
* skips; pollWithSpinner polls quietly (or skips when manualOnly);
|
|
11
|
+
* withSpinner prints one line instead of animating.
|
|
12
|
+
*
|
|
13
|
+
* Test seams: streams ride prompt.js's setPromptStreams (keypresses in,
|
|
14
|
+
* spinner + prompt output out — real keystrokes, no mocks), and
|
|
15
|
+
* setBrowserOpener(fn) replaces the platform browser launcher so tests
|
|
16
|
+
* never open a real browser.
|
|
17
|
+
*/
|
|
18
|
+
const { spawn } = require('node:child_process');
|
|
19
|
+
const readline = require('node:readline');
|
|
20
|
+
const chalk = require('chalk').default;
|
|
21
|
+
const { input, isInteractive, getPromptStreams, enterToOpenMessage } = require('./prompt.js');
|
|
22
|
+
|
|
23
|
+
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
24
|
+
|
|
25
|
+
let _opener = null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Replace the platform browser launcher (test seam). Pass null to restore.
|
|
29
|
+
*/
|
|
30
|
+
function setBrowserOpener(fn) {
|
|
31
|
+
_opener = fn || null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function platformOpener(url) {
|
|
35
|
+
const [command, args] = process.platform === 'darwin' ? ['open', [url]]
|
|
36
|
+
: process.platform === 'win32' ? ['cmd', ['/c', 'start', '', url]]
|
|
37
|
+
: ['xdg-open', [url]];
|
|
38
|
+
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const child = spawn(command, args, { stdio: 'ignore', detached: true });
|
|
41
|
+
child.once('spawn', () => {
|
|
42
|
+
child.unref();
|
|
43
|
+
resolve(true);
|
|
44
|
+
});
|
|
45
|
+
child.once('error', () => resolve(false));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Open a URL in the default browser. Resolves false when no launcher is
|
|
51
|
+
* available (callers always print the URL first, so failure just means
|
|
52
|
+
* the user clicks it themselves).
|
|
53
|
+
*
|
|
54
|
+
* @param {string} url - URL to open
|
|
55
|
+
* @returns {Promise<boolean>} - Whether a browser launch was started
|
|
56
|
+
*/
|
|
57
|
+
function openBrowser(url) {
|
|
58
|
+
return (_opener || platformOpener)(url);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Run an async function with a live spinner (one static line without a TTY).
|
|
63
|
+
*
|
|
64
|
+
* @param {string} message - Message to display while waiting
|
|
65
|
+
* @param {Function} fn - Async function to execute
|
|
66
|
+
* @param {Object} [options]
|
|
67
|
+
* @param {string} [options.indent] - Indentation prefix
|
|
68
|
+
* @returns {any} - Result of fn()
|
|
69
|
+
*/
|
|
70
|
+
async function withSpinner(message, fn, options = {}) {
|
|
71
|
+
const { indent = ' ' } = options;
|
|
72
|
+
const { output } = getPromptStreams();
|
|
73
|
+
|
|
74
|
+
if (!isInteractive()) {
|
|
75
|
+
output.write(`${indent}${message}...\n`);
|
|
76
|
+
return fn();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let spinnerIndex = 0;
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
|
|
82
|
+
const interval = setInterval(() => {
|
|
83
|
+
const frame = SPINNER_FRAMES[spinnerIndex % SPINNER_FRAMES.length];
|
|
84
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
85
|
+
output.write(`\r${indent}${frame} ${message}... (${elapsed}s)`);
|
|
86
|
+
spinnerIndex++;
|
|
87
|
+
}, 100);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
return await fn();
|
|
91
|
+
} finally {
|
|
92
|
+
clearInterval(interval);
|
|
93
|
+
output.write('\r' + ' '.repeat(70) + '\r');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Poll until a condition is met, with a spinner and keyboard controls
|
|
99
|
+
* (ENTER = check now — or "I'm done" when manualOnly; S = skip waiting).
|
|
100
|
+
*
|
|
101
|
+
* Without a TTY there are no keys and no spinner: manualOnly returns
|
|
102
|
+
* skipped immediately (nobody can press ENTER), API-checked polls run
|
|
103
|
+
* quietly on the interval.
|
|
104
|
+
*
|
|
105
|
+
* @param {Object} options
|
|
106
|
+
* @param {Function} options.check - Async () => { done: boolean, result?: any, error?: string }
|
|
107
|
+
* @param {number} [options.intervalMs] - Interval between checks (default: 10000)
|
|
108
|
+
* @param {string} [options.message] - Message to display while waiting
|
|
109
|
+
* @param {string} [options.indent] - Indentation prefix
|
|
110
|
+
* @param {boolean} [options.manualOnly] - No API check available; just wait for ENTER
|
|
111
|
+
* @returns {Promise<{ success: boolean, result?: any, error?: string, skipped?: boolean }>}
|
|
112
|
+
*/
|
|
113
|
+
async function pollWithSpinner(options) {
|
|
114
|
+
const {
|
|
115
|
+
check,
|
|
116
|
+
intervalMs = 10000,
|
|
117
|
+
message = 'Waiting',
|
|
118
|
+
indent = ' ',
|
|
119
|
+
manualOnly = false,
|
|
120
|
+
} = options;
|
|
121
|
+
|
|
122
|
+
const { input, output } = getPromptStreams();
|
|
123
|
+
|
|
124
|
+
// Non-interactive: no keys, no spinner
|
|
125
|
+
if (!isInteractive()) {
|
|
126
|
+
if (manualOnly) {
|
|
127
|
+
output.write(`${indent}${chalk.dim('⊘ Non-interactive — cannot wait for manual confirmation')}\n`);
|
|
128
|
+
return { success: false, skipped: true };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
output.write(`${indent}${message}... ${chalk.dim(`(checking every ${Math.round(intervalMs / 1000)}s)`)}\n`);
|
|
132
|
+
while (true) {
|
|
133
|
+
try {
|
|
134
|
+
const result = await check();
|
|
135
|
+
if (result.done) {
|
|
136
|
+
if (result.error) {
|
|
137
|
+
return { success: false, error: result.error };
|
|
138
|
+
}
|
|
139
|
+
return { success: true, result: result.result };
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
return { success: false, error: error.message };
|
|
143
|
+
}
|
|
144
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
output.write(`\n${indent}${chalk.dim(`(enter)=${manualOnly ? 'done' : 'check now'}, (s)=skip`)}\n\n`);
|
|
149
|
+
|
|
150
|
+
let spinnerIndex = 0;
|
|
151
|
+
let lastCheck = Date.now();
|
|
152
|
+
let checkNow = false;
|
|
153
|
+
let skip = false;
|
|
154
|
+
|
|
155
|
+
// Keyboard input on the effective stream (fake TTYs in tests lack
|
|
156
|
+
// setRawMode — a stream-level capability, not something we own)
|
|
157
|
+
readline.emitKeypressEvents(input);
|
|
158
|
+
input.setRawMode?.(true);
|
|
159
|
+
input.resume();
|
|
160
|
+
|
|
161
|
+
const keypressHandler = (ch, key) => {
|
|
162
|
+
if (key?.name === 'return') {
|
|
163
|
+
checkNow = true;
|
|
164
|
+
} else if (ch?.toLowerCase() === 's') {
|
|
165
|
+
skip = true;
|
|
166
|
+
} else if (key?.ctrl && key?.name === 'c') {
|
|
167
|
+
process.exit();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
input.on('keypress', keypressHandler);
|
|
171
|
+
|
|
172
|
+
const startTime = Date.now();
|
|
173
|
+
const spinnerInterval = setInterval(() => {
|
|
174
|
+
const frame = SPINNER_FRAMES[spinnerIndex % SPINNER_FRAMES.length];
|
|
175
|
+
if (manualOnly) {
|
|
176
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
177
|
+
output.write(`\r${indent}${frame} ${message}... (${elapsed}s)`);
|
|
178
|
+
} else {
|
|
179
|
+
const elapsed = Math.floor((Date.now() - lastCheck) / 1000);
|
|
180
|
+
const remaining = Math.max(0, intervalMs / 1000 - elapsed);
|
|
181
|
+
output.write(`\r${indent}${frame} ${message}... (${remaining}s)`);
|
|
182
|
+
}
|
|
183
|
+
spinnerIndex++;
|
|
184
|
+
}, 100);
|
|
185
|
+
|
|
186
|
+
const cleanup = () => {
|
|
187
|
+
clearInterval(spinnerInterval);
|
|
188
|
+
output.write('\r' + ' '.repeat(70) + '\r');
|
|
189
|
+
// Only remove OUR keypress listener (inquirer manages its own)
|
|
190
|
+
input.removeListener('keypress', keypressHandler);
|
|
191
|
+
input.setRawMode?.(false);
|
|
192
|
+
input.pause();
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
try {
|
|
196
|
+
while (true) {
|
|
197
|
+
if (skip) {
|
|
198
|
+
cleanup();
|
|
199
|
+
return { success: false, skipped: true };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// In manual mode, ENTER means "I'm done" — success immediately
|
|
203
|
+
if (manualOnly && checkNow) {
|
|
204
|
+
cleanup();
|
|
205
|
+
return { success: true };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!manualOnly) {
|
|
209
|
+
try {
|
|
210
|
+
const result = await check();
|
|
211
|
+
if (result.done) {
|
|
212
|
+
cleanup();
|
|
213
|
+
if (result.error) {
|
|
214
|
+
return { success: false, error: result.error };
|
|
215
|
+
}
|
|
216
|
+
return { success: true, result: result.result };
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
cleanup();
|
|
220
|
+
return { success: false, error: error.message };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Wait for the next interval — or until a key cuts it short
|
|
225
|
+
lastCheck = Date.now();
|
|
226
|
+
checkNow = false;
|
|
227
|
+
|
|
228
|
+
await new Promise((resolve) => {
|
|
229
|
+
let timer = null;
|
|
230
|
+
const keyPoll = setInterval(() => {
|
|
231
|
+
if (checkNow || skip) {
|
|
232
|
+
clearInterval(keyPoll);
|
|
233
|
+
clearTimeout(timer);
|
|
234
|
+
resolve();
|
|
235
|
+
}
|
|
236
|
+
}, 100);
|
|
237
|
+
// In manual mode there's no auto-check — only user input resolves
|
|
238
|
+
if (!manualOnly) {
|
|
239
|
+
timer = setTimeout(() => {
|
|
240
|
+
clearInterval(keyPoll);
|
|
241
|
+
resolve();
|
|
242
|
+
}, intervalMs);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
} catch (error) {
|
|
247
|
+
cleanup();
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Print instructions + a URL, open the browser (Enter-gated), and poll
|
|
254
|
+
* until the condition is met. The core onboarding-flow shape: "create the
|
|
255
|
+
* thing in this dashboard; I'll wait until I can see it via the API."
|
|
256
|
+
*
|
|
257
|
+
* Without a TTY the URL is printed and the flow returns skipped.
|
|
258
|
+
*
|
|
259
|
+
* The open is gated behind Enter — the ONE synonymous walkthrough gate
|
|
260
|
+
* (prompt.js enterToOpenMessage); skipping the step happens at the poll
|
|
261
|
+
* via (s), never by declining the open.
|
|
262
|
+
*
|
|
263
|
+
* @param {Object} options
|
|
264
|
+
* @param {string} options.url - URL to open
|
|
265
|
+
* @param {string} options.promptMessage - What needs to be done there
|
|
266
|
+
* @param {string} [options.label] - What the Enter gate calls the page
|
|
267
|
+
* @param {string} options.waitMessage - Message shown while polling
|
|
268
|
+
* @param {Function} [options.check] - Async condition (see pollWithSpinner)
|
|
269
|
+
* @param {number} [options.intervalMs] - Poll interval (default: 10000)
|
|
270
|
+
* @param {string} [options.indent] - Indentation prefix
|
|
271
|
+
* @param {boolean} [options.manualOnly] - No API check; wait for ENTER
|
|
272
|
+
* @returns {Promise<{ success: boolean, result?: any, error?: string, skipped?: boolean }>}
|
|
273
|
+
*/
|
|
274
|
+
async function openBrowserAndPoll(options) {
|
|
275
|
+
const {
|
|
276
|
+
url,
|
|
277
|
+
promptMessage,
|
|
278
|
+
label = 'this page',
|
|
279
|
+
waitMessage,
|
|
280
|
+
check,
|
|
281
|
+
intervalMs = 10000,
|
|
282
|
+
indent = ' ',
|
|
283
|
+
manualOnly = false,
|
|
284
|
+
} = options;
|
|
285
|
+
|
|
286
|
+
const { output } = getPromptStreams();
|
|
287
|
+
|
|
288
|
+
output.write(`\n${indent}${promptMessage}\n${indent}URL: ${chalk.cyan(url)}\n\n`);
|
|
289
|
+
|
|
290
|
+
if (!isInteractive()) {
|
|
291
|
+
output.write(`${indent}${chalk.dim('⊘ Non-interactive — skipping browser open + poll')}\n`);
|
|
292
|
+
return { success: false, skipped: true };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
await input({ message: enterToOpenMessage(label) });
|
|
296
|
+
|
|
297
|
+
const opened = await openBrowser(url);
|
|
298
|
+
if (!opened) {
|
|
299
|
+
output.write(`${indent}${chalk.dim("(couldn't launch a browser — open the URL above manually)")}\n`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return pollWithSpinner({
|
|
303
|
+
check,
|
|
304
|
+
intervalMs,
|
|
305
|
+
message: waitMessage,
|
|
306
|
+
indent,
|
|
307
|
+
manualOnly,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = {
|
|
312
|
+
openBrowser,
|
|
313
|
+
setBrowserOpener,
|
|
314
|
+
withSpinner,
|
|
315
|
+
pollWithSpinner,
|
|
316
|
+
openBrowserAndPoll,
|
|
317
|
+
};
|