@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.
Files changed (416) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/LICENSE +98 -0
  3. package/README.md +165 -0
  4. package/bin/omega +2 -0
  5. package/bin/omg +2 -0
  6. package/claude-plugin/.claude-plugin/plugin.json +11 -0
  7. package/claude-plugin/.mcp.json +9 -0
  8. package/claude-plugin/README.md +139 -0
  9. package/claude-plugin/hooks/gate/mark.sh +58 -0
  10. package/claude-plugin/hooks/gate/run.sh +97 -0
  11. package/claude-plugin/hooks/guard/run.sh +249 -0
  12. package/claude-plugin/hooks/hooks.json +63 -0
  13. package/claude-plugin/hooks/inject/run.sh +127 -0
  14. package/claude-plugin/hooks/lib/omega-gate.sh +20 -0
  15. package/claude-plugin/hooks/lib/omega-scope.sh +41 -0
  16. package/claude-plugin/hooks/lib/omega-skills.sh +209 -0
  17. package/claude-plugin/hooks/quality/run.sh +132 -0
  18. package/claude-plugin/hooks/shape/run.sh +45 -0
  19. package/claude-plugin/mcp-router-launch.js +29 -0
  20. package/claude-plugin/skills/README.md +48 -0
  21. package/claude-plugin/skills/accessibility/SKILL.md +31 -0
  22. package/claude-plugin/skills/analytics/SKILL.md +30 -0
  23. package/claude-plugin/skills/backend/SKILL.md +25 -0
  24. package/claude-plugin/skills/brandcheck/SKILL.md +29 -0
  25. package/claude-plugin/skills/browser/SKILL.md +64 -0
  26. package/claude-plugin/skills/client/SKILL.md +24 -0
  27. package/claude-plugin/skills/desktop/SKILL.md +25 -0
  28. package/claude-plugin/skills/extension/SKILL.md +25 -0
  29. package/claude-plugin/skills/main/SKILL.md +54 -0
  30. package/claude-plugin/skills/manager/SKILL.md +25 -0
  31. package/claude-plugin/skills/seo/SKILL.md +36 -0
  32. package/claude-plugin/skills/theme/SKILL.md +33 -0
  33. package/claude-plugin/skills/web/SKILL.md +29 -0
  34. package/dist/cli-run.js +34 -0
  35. package/dist/cli.js +29 -0
  36. package/dist/commands/build.js +17 -0
  37. package/dist/commands/clean.js +16 -0
  38. package/dist/commands/company.js +46 -0
  39. package/dist/commands/deploy.js +143 -0
  40. package/dist/commands/dev.js +437 -0
  41. package/dist/commands/devlog.js +25 -0
  42. package/dist/commands/help.js +48 -0
  43. package/dist/commands/manage.js +53 -0
  44. package/dist/commands/migrate.js +76 -0
  45. package/dist/commands/onboard.js +31 -0
  46. package/dist/commands/pipeline.js +319 -0
  47. package/dist/commands/test.js +380 -0
  48. package/dist/commands/update.js +128 -0
  49. package/dist/commands/version.js +8 -0
  50. package/dist/company-init.js +167 -0
  51. package/dist/company.js +283 -0
  52. package/dist/config.js +1359 -0
  53. package/dist/devlog/index.js +226 -0
  54. package/dist/devlog/lib/collect-commits.js +136 -0
  55. package/dist/devlog/lib/generate-post.js +201 -0
  56. package/dist/devlog/lib/ghostii.js +122 -0
  57. package/dist/devlog/lib/project-map.js +98 -0
  58. package/dist/devlog/lib/publish-website.js +88 -0
  59. package/dist/index.js +45 -0
  60. package/dist/lib/agents-md.js +201 -0
  61. package/dist/lib/analytics-secret.js +22 -0
  62. package/dist/lib/argv.js +21 -0
  63. package/dist/lib/auth-admin.js +120 -0
  64. package/dist/lib/automation-client.js +210 -0
  65. package/dist/lib/backend-marketing.js +49 -0
  66. package/dist/lib/brand.js +240 -0
  67. package/dist/lib/bundle-id.js +53 -0
  68. package/dist/lib/claude-settings.js +125 -0
  69. package/dist/lib/company-scaffold.js +213 -0
  70. package/dist/lib/company.js +227 -0
  71. package/dist/lib/config-flow.js +350 -0
  72. package/dist/lib/config-write.js +41 -0
  73. package/dist/lib/custom-target.js +75 -0
  74. package/dist/lib/domain-utils.js +19 -0
  75. package/dist/lib/duration.js +39 -0
  76. package/dist/lib/env-order.js +236 -0
  77. package/dist/lib/env-secret.js +78 -0
  78. package/dist/lib/firestore-rest.js +326 -0
  79. package/dist/lib/framework-bin.js +156 -0
  80. package/dist/lib/gitignore.js +63 -0
  81. package/dist/lib/google-auth.js +495 -0
  82. package/dist/lib/google-token.js +66 -0
  83. package/dist/lib/jwt.js +31 -0
  84. package/dist/lib/legacy-oauth.js +59 -0
  85. package/dist/lib/node-version.js +127 -0
  86. package/dist/lib/owner-plan.js +78 -0
  87. package/dist/lib/package-scripts.js +152 -0
  88. package/dist/lib/preflight.js +470 -0
  89. package/dist/lib/product-create.js +144 -0
  90. package/dist/lib/run-command.js +74 -0
  91. package/dist/lib/run-gates.js +70 -0
  92. package/dist/lib/run-output.js +42 -0
  93. package/dist/lib/run-summary.js +384 -0
  94. package/dist/lib/scaffold.js +427 -0
  95. package/dist/lib/service-input.js +207 -0
  96. package/dist/lib/service-runner.js +466 -0
  97. package/dist/lib/stale.js +24 -0
  98. package/dist/lib/target-selection.js +155 -0
  99. package/dist/lib/verb-fanout.js +128 -0
  100. package/dist/lib/verify-live.js +188 -0
  101. package/dist/manage.js +311 -0
  102. package/dist/omega-bin.js +7 -0
  103. package/dist/onboard.js +532 -0
  104. package/dist/services/account/ensure/users.js +259 -0
  105. package/dist/services/account/index.js +97 -0
  106. package/dist/services/account/lib/backend-client.js +107 -0
  107. package/dist/services/account/lib/password.js +36 -0
  108. package/dist/services/account/lib/resolve-password.js +100 -0
  109. package/dist/services/advertising/ensure/sites.js +101 -0
  110. package/dist/services/advertising/index.js +101 -0
  111. package/dist/services/advertising/lib/adsense-api.js +41 -0
  112. package/dist/services/ai/ensure/keys.js +21 -0
  113. package/dist/services/ai/index.js +36 -0
  114. package/dist/services/analytics/ensure/google-firebase-link.js +230 -0
  115. package/dist/services/analytics/ensure/google-streams.js +256 -0
  116. package/dist/services/analytics/ensure/meta-pixel.js +22 -0
  117. package/dist/services/analytics/ensure/tiktok-pixel.js +22 -0
  118. package/dist/services/analytics/index.js +134 -0
  119. package/dist/services/analytics/lib/analytics-api.js +156 -0
  120. package/dist/services/analytics/lib/meta-api.js +83 -0
  121. package/dist/services/analytics/lib/pixel-account.js +78 -0
  122. package/dist/services/analytics/lib/pixel-provision.js +152 -0
  123. package/dist/services/analytics/lib/pixel-specs.js +57 -0
  124. package/dist/services/analytics/lib/pixel-token.js +88 -0
  125. package/dist/services/analytics/lib/property-flow.js +77 -0
  126. package/dist/services/analytics/lib/tiktok-api.js +157 -0
  127. package/dist/services/analytics/lib/tiktok-auth.js +153 -0
  128. package/dist/services/assets/ensure/logo-gen.js +68 -0
  129. package/dist/services/assets/index.js +109 -0
  130. package/dist/services/assets/lib/assets-config.js +168 -0
  131. package/dist/services/assets/lib/brandmark-api.js +160 -0
  132. package/dist/services/assets/lib/derived.js +113 -0
  133. package/dist/services/assets/lib/font-loader.js +67 -0
  134. package/dist/services/assets/lib/reconcile.js +122 -0
  135. package/dist/services/assets/lib/reset.js +118 -0
  136. package/dist/services/assets/lib/svg-logo-generator.js +154 -0
  137. package/dist/services/assets/lib/svg-to-black.js +32 -0
  138. package/dist/services/assets/write/favicons.js +82 -0
  139. package/dist/services/assets/write/icons.js +64 -0
  140. package/dist/services/assets/write/process.js +96 -0
  141. package/dist/services/assets/write/reconcile.js +30 -0
  142. package/dist/services/assets/write/social-icons.js +108 -0
  143. package/dist/services/assets/write/templates.js +345 -0
  144. package/dist/services/bookmark/ensure/sync.js +255 -0
  145. package/dist/services/bookmark/index.js +17 -0
  146. package/dist/services/campaigns/ensure/contact-person.js +40 -0
  147. package/dist/services/campaigns/ensure/custom-fields.js +84 -0
  148. package/dist/services/campaigns/ensure/domain-auth.js +92 -0
  149. package/dist/services/campaigns/ensure/event-webhook.js +89 -0
  150. package/dist/services/campaigns/ensure/link-branding.js +187 -0
  151. package/dist/services/campaigns/ensure/list.js +50 -0
  152. package/dist/services/campaigns/ensure/segments.js +109 -0
  153. package/dist/services/campaigns/ensure/sender-identity.js +153 -0
  154. package/dist/services/campaigns/ensure/unsubscribe-groups.js +116 -0
  155. package/dist/services/campaigns/index.js +79 -0
  156. package/dist/services/campaigns/lib/dns-sync.js +81 -0
  157. package/dist/services/campaigns/lib/segment-query.js +138 -0
  158. package/dist/services/campaigns/lib/sendgrid-api.js +287 -0
  159. package/dist/services/captcha/ensure/site-key.js +83 -0
  160. package/dist/services/captcha/index.js +78 -0
  161. package/dist/services/captcha/lib/console-url.js +45 -0
  162. package/dist/services/captcha/lib/recaptcha-api.js +36 -0
  163. package/dist/services/certificates/ensure/api-key.js +22 -0
  164. package/dist/services/certificates/ensure/bundle-ids.js +115 -0
  165. package/dist/services/certificates/ensure/certificates.js +187 -0
  166. package/dist/services/certificates/ensure/profiles.js +143 -0
  167. package/dist/services/certificates/index.js +225 -0
  168. package/dist/services/certificates/lib/apple-api.js +183 -0
  169. package/dist/services/certificates/lib/certificate-manager.js +252 -0
  170. package/dist/services/certificates/lib/identifier-manager.js +107 -0
  171. package/dist/services/certificates/lib/keychain.js +135 -0
  172. package/dist/services/certificates/lib/manual-walkthrough.js +165 -0
  173. package/dist/services/certificates/lib/profile-manager.js +115 -0
  174. package/dist/services/chat/data/baseline-knowledge.md +96 -0
  175. package/dist/services/chat/ensure/chat.js +106 -0
  176. package/dist/services/chat/ensure/user.js +45 -0
  177. package/dist/services/chat/index.js +139 -0
  178. package/dist/services/chat/lib/baseline-knowledge.js +137 -0
  179. package/dist/services/cloud/ensure/authentication.js +267 -0
  180. package/dist/services/cloud/ensure/billing.js +120 -0
  181. package/dist/services/cloud/ensure/cloud-messaging.js +105 -0
  182. package/dist/services/cloud/ensure/database.js +60 -0
  183. package/dist/services/cloud/ensure/firestore.js +79 -0
  184. package/dist/services/cloud/ensure/functions.js +49 -0
  185. package/dist/services/cloud/ensure/hosting.js +398 -0
  186. package/dist/services/cloud/ensure/oauth-consent.js +248 -0
  187. package/dist/services/cloud/ensure/project-settings.js +72 -0
  188. package/dist/services/cloud/ensure/sdk-config.js +95 -0
  189. package/dist/services/cloud/ensure/service-account.js +152 -0
  190. package/dist/services/cloud/ensure/services.js +103 -0
  191. package/dist/services/cloud/ensure/storage.js +47 -0
  192. package/dist/services/cloud/index.js +130 -0
  193. package/dist/services/cloud/lib/access-heal.js +130 -0
  194. package/dist/services/cloud/lib/firebase-api.js +662 -0
  195. package/dist/services/cloud/lib/project-flow.js +102 -0
  196. package/dist/services/directory/ensure/entry.js +48 -0
  197. package/dist/services/directory/index.js +64 -0
  198. package/dist/services/directory/lib/blocks.js +104 -0
  199. package/dist/services/disperse/index.js +45 -0
  200. package/dist/services/disperse/write/certs.js +117 -0
  201. package/dist/services/domain/ensure/nameservers.js +179 -0
  202. package/dist/services/domain/index.js +69 -0
  203. package/dist/services/domain/lib/namecheap-api.js +167 -0
  204. package/dist/services/domain/lib/registrars.js +46 -0
  205. package/dist/services/domain/lib/whitelist-walkthrough.js +76 -0
  206. package/dist/services/edge/ensure/cache-rules.js +105 -0
  207. package/dist/services/edge/ensure/dns-records.js +304 -0
  208. package/dist/services/edge/ensure/email-routing.js +292 -0
  209. package/dist/services/edge/ensure/rules-configuration.js +98 -0
  210. package/dist/services/edge/ensure/rules-managed-transforms.js +101 -0
  211. package/dist/services/edge/ensure/rules-redirect.js +107 -0
  212. package/dist/services/edge/ensure/rules-response-headers.js +90 -0
  213. package/dist/services/edge/ensure/rules-security.js +142 -0
  214. package/dist/services/edge/ensure/speed-scheduled-tests.js +98 -0
  215. package/dist/services/edge/ensure/workers.js +219 -0
  216. package/dist/services/edge/ensure/zone-settings.js +108 -0
  217. package/dist/services/edge/ensure/zone.js +183 -0
  218. package/dist/services/edge/index.js +73 -0
  219. package/dist/services/edge/lib/cloudflare-api.js +84 -0
  220. package/dist/services/edge/lib/dns-records-helpers.js +518 -0
  221. package/dist/services/edge/lib/read-cache.js +16 -0
  222. package/dist/services/edge/lib/ruleset-helper.js +108 -0
  223. package/dist/services/edge/workers/omega-api-proxy.js +43 -0
  224. package/dist/services/email/data/baseline-filter.md +4 -0
  225. package/dist/services/email/data/baseline-knowledge.md +86 -0
  226. package/dist/services/email/ensure/agent.js +107 -0
  227. package/dist/services/email/ensure/user.js +45 -0
  228. package/dist/services/email/index.js +138 -0
  229. package/dist/services/email/lib/baseline.js +82 -0
  230. package/dist/services/email/lib/knowledge-file.js +53 -0
  231. package/dist/services/forms/ensure/form.js +46 -0
  232. package/dist/services/forms/ensure/user.js +45 -0
  233. package/dist/services/forms/index.js +138 -0
  234. package/dist/services/migrations/ensure/notifications.js +189 -0
  235. package/dist/services/migrations/ensure/orders.js +34 -0
  236. package/dist/services/migrations/ensure/payment-provider.js +132 -0
  237. package/dist/services/migrations/ensure/payments-intents.js +34 -0
  238. package/dist/services/migrations/ensure/state-retirement.js +266 -0
  239. package/dist/services/migrations/ensure/targets-rename.js +124 -0
  240. package/dist/services/migrations/ensure/users.js +1176 -0
  241. package/dist/services/migrations/index.js +101 -0
  242. package/dist/services/migrations/lib/attribution-touch.js +64 -0
  243. package/dist/services/migrations/lib/ensure-metadata.js +144 -0
  244. package/dist/services/migrations/lib/migration-runner.js +473 -0
  245. package/dist/services/migrations/lib/sanitize-strings.js +112 -0
  246. package/dist/services/migrations/lib/schema-validator.js +154 -0
  247. package/dist/services/monitoring/ensure/dsn.js +54 -0
  248. package/dist/services/monitoring/ensure/projects.js +135 -0
  249. package/dist/services/monitoring/index.js +54 -0
  250. package/dist/services/monitoring/lib/sentry-api.js +94 -0
  251. package/dist/services/newsletter/ensure/custom-fields.js +90 -0
  252. package/dist/services/newsletter/ensure/publication.js +94 -0
  253. package/dist/services/newsletter/ensure/segments.js +140 -0
  254. package/dist/services/newsletter/ensure/webhook.js +108 -0
  255. package/dist/services/newsletter/index.js +64 -0
  256. package/dist/services/newsletter/lib/beehiiv-api.js +124 -0
  257. package/dist/services/newsletter/lib/segment-automation.js +577 -0
  258. package/dist/services/payment/ensure/chargebee-account.js +30 -0
  259. package/dist/services/payment/ensure/chargebee-products.js +359 -0
  260. package/dist/services/payment/ensure/chargebee-webhook.js +113 -0
  261. package/dist/services/payment/ensure/paypal-account.js +36 -0
  262. package/dist/services/payment/ensure/paypal-products.js +374 -0
  263. package/dist/services/payment/ensure/paypal-webhook.js +128 -0
  264. package/dist/services/payment/ensure/stripe-account.js +93 -0
  265. package/dist/services/payment/ensure/stripe-disputes.js +56 -0
  266. package/dist/services/payment/ensure/stripe-products.js +245 -0
  267. package/dist/services/payment/ensure/stripe-radar.js +74 -0
  268. package/dist/services/payment/ensure/stripe-webhook.js +130 -0
  269. package/dist/services/payment/index.js +163 -0
  270. package/dist/services/payment/lib/chargebee-api.js +338 -0
  271. package/dist/services/payment/lib/payment-utils.js +116 -0
  272. package/dist/services/payment/lib/paypal-api.js +354 -0
  273. package/dist/services/payment/lib/provider-setup.js +134 -0
  274. package/dist/services/payment/lib/stripe-api.js +152 -0
  275. package/dist/services/repo/ensure/org.js +70 -0
  276. package/dist/services/repo/ensure/pages.js +72 -0
  277. package/dist/services/repo/ensure/repo.js +90 -0
  278. package/dist/services/repo/index.js +52 -0
  279. package/dist/services/repo/lib/github-api.js +196 -0
  280. package/dist/services/search/ensure/ga-link.js +57 -0
  281. package/dist/services/search/ensure/property.js +137 -0
  282. package/dist/services/search/ensure/sitemaps.js +70 -0
  283. package/dist/services/search/index.js +61 -0
  284. package/dist/services/search/lib/search-console-api.js +92 -0
  285. package/dist/services/seo/ensure/github-repos.js +271 -0
  286. package/dist/services/seo/index.js +55 -0
  287. package/dist/services/seo/lib/gh-api.js +161 -0
  288. package/dist/services/seo/templates/developer-tool/.github/workflows/maintenance.yml +25 -0
  289. package/dist/services/seo/templates/developer-tool/.nvmrc +1 -0
  290. package/dist/services/seo/templates/developer-tool/_README.md.js +121 -0
  291. package/dist/services/seo/templates/developer-tool/_package.json.js +33 -0
  292. package/dist/services/seo/templates/developer-tool/src/index.js +120 -0
  293. package/dist/services/seo/templates/index.js +57 -0
  294. package/dist/services/server/ensure/brands.js +63 -0
  295. package/dist/services/server/index.js +42 -0
  296. package/dist/services/testing/ensure/target-checks.js +97 -0
  297. package/dist/services/testing/index.js +11 -0
  298. package/dist/services/testing/lib/checks.js +467 -0
  299. package/dist/services/update/index.js +20 -0
  300. package/dist/services/update/lib/cache.js +59 -0
  301. package/dist/services/update/lib/fingerprint.js +157 -0
  302. package/dist/services/update/write/targets.js +206 -0
  303. package/dist/services/workspace/ensure/agents.js +42 -0
  304. package/dist/services/workspace/ensure/claude-settings.js +34 -0
  305. package/dist/services/workspace/ensure/config.js +54 -0
  306. package/dist/services/workspace/ensure/defaults.js +60 -0
  307. package/dist/services/workspace/ensure/env-keys.js +55 -0
  308. package/dist/services/workspace/ensure/env-order.js +66 -0
  309. package/dist/services/workspace/ensure/env-rules.js +97 -0
  310. package/dist/services/workspace/ensure/gitignore.js +18 -0
  311. package/dist/services/workspace/ensure/scripts.js +126 -0
  312. package/dist/services/workspace/ensure/structure.js +97 -0
  313. package/dist/services/workspace/ensure/translation-sdk.js +148 -0
  314. package/dist/services/workspace/ensure/workflows.js +54 -0
  315. package/dist/services/workspace/index.js +8 -0
  316. package/dist/vendor/account/engine.js +182 -0
  317. package/dist/vendor/account/features.js +220 -0
  318. package/dist/vendor/account/index.js +53 -0
  319. package/dist/vendor/account/schema.js +272 -0
  320. package/dist/vendor/account/subscription.js +38 -0
  321. package/dist/vendor/config/company.js +31 -0
  322. package/dist/vendor/config/defaults.js +173 -0
  323. package/dist/vendor/config/demo.js +18 -0
  324. package/dist/vendor/config/desktop-artifacts.js +110 -0
  325. package/dist/vendor/config/edit.js +769 -0
  326. package/dist/vendor/config/env-delivery.js +145 -0
  327. package/dist/vendor/config/env-rules.js +93 -0
  328. package/dist/vendor/config/env-schema.js +1078 -0
  329. package/dist/vendor/config/env.js +445 -0
  330. package/dist/vendor/config/hooks.js +97 -0
  331. package/dist/vendor/config/index.js +237 -0
  332. package/dist/vendor/config/instances.js +208 -0
  333. package/dist/vendor/config/load.js +490 -0
  334. package/dist/vendor/config/merge.js +68 -0
  335. package/dist/vendor/config/order.js +139 -0
  336. package/dist/vendor/config/ports.js +374 -0
  337. package/dist/vendor/config/providers.js +32 -0
  338. package/dist/vendor/config/repo.js +142 -0
  339. package/dist/vendor/config/retired-keys.js +430 -0
  340. package/dist/vendor/config/schema.js +1610 -0
  341. package/dist/vendor/config/secrets.js +50 -0
  342. package/dist/vendor/config/seed.js +34 -0
  343. package/dist/vendor/config/site-global.js +205 -0
  344. package/dist/vendor/config/validate.js +554 -0
  345. package/dist/vendor/config/winback.js +61 -0
  346. package/dist/vendor/devkit/attach-log-file.js +262 -0
  347. package/dist/vendor/devkit/certs.js +199 -0
  348. package/dist/vendor/devkit/ci-workflows.js +520 -0
  349. package/dist/vendor/devkit/cli-router.js +156 -0
  350. package/dist/vendor/devkit/command-path.js +46 -0
  351. package/dist/vendor/devkit/deploy-record.js +180 -0
  352. package/dist/vendor/devkit/flows.js +317 -0
  353. package/dist/vendor/devkit/local-https.js +360 -0
  354. package/dist/vendor/devkit/local.js +1905 -0
  355. package/dist/vendor/devkit/logger.js +128 -0
  356. package/dist/vendor/devkit/omega-bin.js +345 -0
  357. package/dist/vendor/devkit/prompt.js +187 -0
  358. package/dist/vendor/devkit/safe-install.js +18 -0
  359. package/dist/vendor/devkit/stop-signals.js +28 -0
  360. package/dist/vendor/devkit/test/scope.js +162 -0
  361. package/dist/vendor/devkit/translate/cache.js +84 -0
  362. package/dist/vendor/devkit/translate/engine.js +243 -0
  363. package/dist/vendor/devkit/translate/index.js +49 -0
  364. package/dist/vendor/devkit/translate/languages.js +134 -0
  365. package/dist/vendor/devkit/translate/providers.js +188 -0
  366. package/dist/vendor/devkit/update.js +569 -0
  367. package/docs/AGENTS.md +200 -0
  368. package/docs/account.md +45 -0
  369. package/docs/advertising.md +46 -0
  370. package/docs/ai.md +34 -0
  371. package/docs/analytics.md +69 -0
  372. package/docs/assets.md +57 -0
  373. package/docs/bookmark.md +26 -0
  374. package/docs/brand.md +151 -0
  375. package/docs/campaigns.md +96 -0
  376. package/docs/captcha.md +46 -0
  377. package/docs/certificates.md +68 -0
  378. package/docs/chat.md +47 -0
  379. package/docs/cloud.md +110 -0
  380. package/docs/company.md +64 -0
  381. package/docs/directory.md +140 -0
  382. package/docs/disperse.md +46 -0
  383. package/docs/domain.md +56 -0
  384. package/docs/edge.md +234 -0
  385. package/docs/email.md +42 -0
  386. package/docs/forms.md +48 -0
  387. package/docs/index.md +109 -0
  388. package/docs/migration.md +203 -0
  389. package/docs/migrations.md +56 -0
  390. package/docs/monitoring.md +41 -0
  391. package/docs/newsletter.md +48 -0
  392. package/docs/payment.md +83 -0
  393. package/docs/repo.md +48 -0
  394. package/docs/search.md +44 -0
  395. package/docs/seo.md +34 -0
  396. package/docs/server.md +37 -0
  397. package/docs/shared/agent-docs.md +89 -0
  398. package/docs/shared/analytics.md +612 -0
  399. package/docs/shared/brands.md +51 -0
  400. package/docs/shared/breaking-changes.md +497 -0
  401. package/docs/shared/config.md +1387 -0
  402. package/docs/shared/deploys.md +215 -0
  403. package/docs/shared/icons.md +201 -0
  404. package/docs/shared/local-dev.md +147 -0
  405. package/docs/shared/logging.md +202 -0
  406. package/docs/shared/monitoring.md +153 -0
  407. package/docs/shared/publishing.md +183 -0
  408. package/docs/shared/rulings.md +34 -0
  409. package/docs/shared/testing.md +147 -0
  410. package/docs/shared/theming.md +604 -0
  411. package/docs/shared/translation.md +291 -0
  412. package/docs/shared/updates.md +61 -0
  413. package/docs/testing.md +34 -0
  414. package/docs/update.md +36 -0
  415. package/docs/workspace.md +64 -0
  416. package/package.json +88 -0
@@ -0,0 +1,577 @@
1
+ /**
2
+ * Beehiiv segment creation via the companion Chrome extension — Beehiiv
3
+ * has NO segment-create API, so the manager drives the dashboard UI
4
+ * itself through trusted CDP events (AutomationClient ↔ extension/).
5
+ * The evaluate() bodies are the battle-tested omega-manager selectors:
6
+ * Beehiiv duplicates IDs across condition rows, so every lookup targets
7
+ * the LAST matching element; React inputs are set through the native
8
+ * value setter + synthetic input/change events.
9
+ *
10
+ * Also home to the segment display helpers (OP_DISPLAY/formatCondition)
11
+ * shared by the verify output in ensure/segments.js.
12
+ */
13
+ const chalk = require('chalk').default;
14
+ const { BACKEND_FIELDS_MAP } = require('../../../lib/backend-marketing.js');
15
+
16
+ // Operator → human-readable phrasing for the dashboard instructions
17
+ const OP_DISPLAY = {
18
+ '==': 'is',
19
+ '!=': 'is not',
20
+ 'within': 'within last',
21
+ 'not_within': 'NOT within last',
22
+ 'email_is': 'email is',
23
+ 'email_like': 'email matches',
24
+ 'opened_or_clicked': 'opened or clicked within last',
25
+ 'not_opened': 'not opened in last',
26
+ 'not_opened_or_clicked': 'not opened or clicked in last',
27
+ 'not_clicked': 'not clicked in last',
28
+ 'received_gte': 'received at least',
29
+ };
30
+
31
+ /**
32
+ * Format a single condition as a human-readable string.
33
+ */
34
+ function formatCondition(condition) {
35
+ if (condition.type === 'engagement' || condition.type === 'contact') {
36
+ return `${OP_DISPLAY[condition.op] || condition.op} ${condition.value}`;
37
+ }
38
+
39
+ const fieldDef = BACKEND_FIELDS_MAP[condition.field];
40
+ const fieldName = fieldDef?.display || condition.field;
41
+ return `${fieldName} ${OP_DISPLAY[condition.op] || condition.op} "${condition.value}"`;
42
+ }
43
+
44
+ /**
45
+ * Map a @omega.js/backend operator to the Beehiiv dropdown text.
46
+ */
47
+ function beehiivOperator(op) {
48
+ switch (op) {
49
+ case '==': return 'is';
50
+ case '!=': return 'is not';
51
+ default: return null;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Create a single segment in Beehiiv via browser automation.
57
+ *
58
+ * Flow:
59
+ * 1. Navigate to /segments/new, pick the "dynamic" type
60
+ * 2. Type the segment name, proceed to "Define conditions"
61
+ * 3. For each condition: + Condition → type → attribute/engagement
62
+ * selectors → operator → value (engagement time windows via Refine)
63
+ * 4. Save the segment
64
+ *
65
+ * Throws on any step it can't complete (including not_within conditions,
66
+ * which need a manual "Signup date" attribute — the throw carries the
67
+ * exact dashboard steps).
68
+ *
69
+ * @param {AutomationClient} client - Connected automation client
70
+ * @param {Object} segment - @omega.js/backend segment ({ name, display, conditions, logic })
71
+ */
72
+ async function automateCreateSegment(client, segment) {
73
+ const log = (msg) => console.log(` ${chalk.dim(msg)}`);
74
+
75
+ // Step 1: Navigate to new segment page
76
+ log('Navigating to /segments/new...');
77
+ await client.navigate('https://app.beehiiv.com/segments/new');
78
+ await client.wait({ delay: 2000 });
79
+
80
+ // Step 2: Select "dynamic" segment type (page shows static/dynamic/manual selection first)
81
+ log('Selecting dynamic segment type...');
82
+ const typeResult = await client.evaluate(`(() => {
83
+ // Click the "dynamic" button by its value attribute
84
+ const dynamicBtn = document.querySelector('button[value="dynamic"]');
85
+ if (dynamicBtn) { dynamicBtn.click(); return { clicked: true, method: 'value' }; }
86
+ // Fallback: find by text content
87
+ const buttons = [...document.querySelectorAll('button')];
88
+ const match = buttons.find(b => b.textContent.toLowerCase().includes('dynamic'));
89
+ if (match) { match.click(); return { clicked: true, method: 'text' }; }
90
+ return { clicked: false, buttons: buttons.map(b => b.textContent.trim().slice(0, 30)) };
91
+ })()`);
92
+ log(`Segment type: ${JSON.stringify(typeResult.value)}`);
93
+
94
+ if (!typeResult.value?.clicked) {
95
+ throw new Error('Could not find "dynamic" segment type button');
96
+ }
97
+ await client.wait({ delay: 1000 });
98
+
99
+ // Step 3: Type segment name using trusted key events
100
+ log('Typing segment name...');
101
+ const nameSelector = '#text-input-name';
102
+ await client.wait({ selector: nameSelector, timeout: 5000, visible: true });
103
+ await client.click(nameSelector);
104
+ await client.type(nameSelector, segment.name, { clear: true, delay: 20 });
105
+ log(`Typed name: ${segment.name}`);
106
+ await client.wait({ delay: 1000 });
107
+
108
+ // Step 4: Click "Define conditions" to proceed to conditions step
109
+ log('Clicking "Define conditions"...');
110
+ const defineResult = await client.evaluate(`(() => {
111
+ const buttons = [...document.querySelectorAll('button')];
112
+ const btn = buttons.find(b => b.textContent.trim().includes('Define conditions'));
113
+ if (!btn) return { found: false, buttons: buttons.map(b => b.textContent.trim()).filter(t => t.length < 40 && t.length > 0) };
114
+ if (btn.disabled) return { found: true, disabled: true };
115
+ btn.click();
116
+ return { clicked: true };
117
+ })()`);
118
+ log(`Define conditions: ${JSON.stringify(defineResult.value)}`);
119
+
120
+ if (defineResult.value?.disabled) {
121
+ throw new Error('"Define conditions" button is disabled — name input may not have registered');
122
+ }
123
+ if (!defineResult.value?.clicked) {
124
+ throw new Error(`Could not find "Define conditions" button. Available: ${JSON.stringify(defineResult.value?.buttons)}`);
125
+ }
126
+ await client.wait({ delay: 1500 });
127
+
128
+ // Expand compound conditions — not_opened_or_clicked becomes two Beehiiv rows
129
+ const expandedConditions = segment.conditions.flatMap((c) => {
130
+ if (c.type === 'engagement' && c.op === 'not_opened_or_clicked') {
131
+ return [
132
+ { ...c, op: 'not_opened' },
133
+ { ...c, op: 'not_clicked' },
134
+ ];
135
+ }
136
+ return [c];
137
+ });
138
+
139
+ // Step 5: Add each condition
140
+ // All selectors target the LAST matching element because Beehiiv
141
+ // duplicates IDs and selectors across condition rows.
142
+ for (let i = 0; i < expandedConditions.length; i++) {
143
+ const condition = expandedConditions[i];
144
+
145
+ log(`Adding condition ${i + 1}: ${formatCondition(condition)}`);
146
+
147
+ // Beehiiv's condition builder has no relative "NOT within" operator on
148
+ // custom date fields — it needs the built-in "Signup date" attribute,
149
+ // which the automation can't reach reliably. Throw with the manual steps.
150
+ if (condition.op === 'not_within') {
151
+ const days = parseInt(condition.value, 10);
152
+ throw new Error(
153
+ `"${segment.display}" needs a manual condition in Beehiiv:\n`
154
+ + ` 1. Subscriber data → Attribute → Signup date\n`
155
+ + ` 2. Operator: is on or before\n`
156
+ + ` 3. Value: a relative date → ${days} days ago`,
157
+ );
158
+ }
159
+
160
+ const isEngagement = condition.type === 'engagement';
161
+ const isDateCondition = condition.op === 'within';
162
+
163
+ // 5a: Click "+ Condition"
164
+ const addResult = await client.evaluate(`(() => {
165
+ const els = [...document.querySelectorAll('button, div[role="button"], span, a')];
166
+ const addBtn = els.find(b => {
167
+ const t = b.textContent.trim();
168
+ return t === '+ Condition' || t === 'Condition' || t === '+ Add Condition';
169
+ }) || els.find(b => {
170
+ const t = b.textContent.trim();
171
+ return t.includes('Condition') && !t.includes('group') && t.length < 25;
172
+ });
173
+ if (addBtn) { addBtn.click(); return { clicked: true, text: addBtn.textContent.trim() }; }
174
+ return { clicked: false };
175
+ })()`);
176
+ log(`+ Condition: ${JSON.stringify(addResult.value)}`);
177
+ if (!addResult.value?.clicked) throw new Error('Could not find "+ Condition" button');
178
+ await client.wait({ delay: 1500 });
179
+
180
+ // 5b: Select condition type from dropdown
181
+ const conditionType = isEngagement ? 'Engagement' : 'Attribute';
182
+ const typePickResult = await client.evaluate(`(() => {
183
+ const items = [...document.querySelectorAll('li[role="option"]')];
184
+ const matches = items.filter(i => i.textContent.trim() === ${JSON.stringify(conditionType)});
185
+ const match = matches[matches.length - 1];
186
+ if (match) { match.click(); return { clicked: true }; }
187
+ return { clicked: false, options: items.map(i => i.textContent.trim()) };
188
+ })()`);
189
+ log(`${conditionType}: ${JSON.stringify(typePickResult.value)}`);
190
+ if (!typePickResult.value?.clicked) throw new Error(`Could not find "${conditionType}"`);
191
+ await client.wait({ delay: 1500 });
192
+
193
+ if (isEngagement) {
194
+ // === ENGAGEMENT CONDITION FLOW ===
195
+ // Parameterized: metric, operator, value, and optional time window (Refine)
196
+ let metric, engOp, engValue, refineDays;
197
+
198
+ switch (condition.op) {
199
+ case 'not_opened':
200
+ metric = 'Unique opens';
201
+ engOp = 'is exactly';
202
+ engValue = '0';
203
+ refineDays = parseInt(condition.value, 10);
204
+ break;
205
+ case 'not_clicked':
206
+ metric = 'Unique clicks';
207
+ engOp = 'is exactly';
208
+ engValue = '0';
209
+ refineDays = parseInt(condition.value, 10);
210
+ break;
211
+ case 'opened_or_clicked':
212
+ metric = 'Unique opens';
213
+ engOp = 'is greater than';
214
+ engValue = '0';
215
+ refineDays = parseInt(condition.value, 10);
216
+ break;
217
+ case 'received_gte': {
218
+ metric = 'Unique sends';
219
+ engOp = 'is greater than';
220
+ engValue = String(parseInt(condition.value, 10) - 1);
221
+ refineDays = null;
222
+ break;
223
+ }
224
+ default:
225
+ throw new Error(`Unsupported Beehiiv engagement op "${condition.op}"`);
226
+ }
227
+
228
+ // 5c-eng: Open "Select engagement" dropdown → select metric
229
+ log(`Opening "Select engagement" dropdown → ${metric}...`);
230
+ await client.evaluate(`(() => {
231
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
232
+ const matches = buttons.filter(b => b.textContent.includes('Select engagement'));
233
+ const btn = matches[matches.length - 1];
234
+ if (btn) btn.click();
235
+ return { clicked: !!btn };
236
+ })()`);
237
+ await client.wait({ delay: 1000 });
238
+
239
+ await client.evaluate(`(() => {
240
+ const items = [...document.querySelectorAll('li[role="option"]')];
241
+ const match = items.find(i => i.textContent.trim() === ${JSON.stringify(metric)});
242
+ if (match) match.click();
243
+ return { clicked: !!match, options: items.map(i => i.textContent.trim()) };
244
+ })()`);
245
+ log(`Selected "${metric}"`);
246
+ await client.wait({ delay: 1500 });
247
+
248
+ // 5d-eng: Set operator
249
+ log(`Setting operator "${engOp}"...`);
250
+ await client.evaluate(`(() => {
251
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
252
+ const matches = buttons.filter(b => {
253
+ const t = b.textContent.trim().toLowerCase();
254
+ return t.includes('is exactly') || t.includes('is greater') || t.includes('is less') || t.includes('is not');
255
+ });
256
+ const btn = matches[matches.length - 1];
257
+ if (btn) btn.click();
258
+ return { clicked: !!btn, text: btn?.textContent?.trim() };
259
+ })()`);
260
+ await client.wait({ delay: 1000 });
261
+
262
+ await client.evaluate(`(() => {
263
+ const items = [...document.querySelectorAll('li[role="option"]')];
264
+ const match = items.find(i => i.textContent.trim() === ${JSON.stringify(engOp)});
265
+ if (match) match.click();
266
+ return { clicked: !!match };
267
+ })()`);
268
+ log(`Operator set to "${engOp}"`);
269
+ await client.wait({ delay: 1000 });
270
+
271
+ // 5e-eng: Type value
272
+ log(`Typing value "${engValue}"...`);
273
+ await client.evaluate(`(() => {
274
+ const inputs = [...document.querySelectorAll('input[name="value"], #text-input-value, input[type="number"]')];
275
+ const target = inputs[inputs.length - 1];
276
+ if (!target) return { typed: false };
277
+ target.focus();
278
+ const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
279
+ nativeSetter.call(target, ${JSON.stringify(engValue)});
280
+ target.dispatchEvent(new Event('input', { bubbles: true }));
281
+ target.dispatchEvent(new Event('change', { bubbles: true }));
282
+ return { typed: true };
283
+ })()`);
284
+ await client.wait({ delay: 1000 });
285
+
286
+ // 5f-eng: Click "Refine" and set time window (skip for all-time conditions like received_gte)
287
+ if (refineDays) {
288
+ log(`Clicking Refine and setting time window: ${refineDays} days...`);
289
+ await client.evaluate(`(() => {
290
+ const buttons = [...document.querySelectorAll('button[title="Refine"]')];
291
+ const btn = buttons[buttons.length - 1];
292
+ if (btn) btn.click();
293
+ return { clicked: !!btn };
294
+ })()`);
295
+ await client.wait({ delay: 1500 });
296
+
297
+ await client.evaluate(`(() => {
298
+ const inputs = [...document.querySelectorAll('input[type="number"][name="value"], #text-input-value')];
299
+ const target = inputs[inputs.length - 1];
300
+ if (!target) return { typed: false };
301
+ target.focus();
302
+ const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
303
+ nativeSetter.call(target, '${refineDays}');
304
+ target.dispatchEvent(new Event('input', { bubbles: true }));
305
+ target.dispatchEvent(new Event('change', { bubbles: true }));
306
+ return { typed: true };
307
+ })()`);
308
+ log(`Time window set: ${refineDays} days`);
309
+ await client.wait({ delay: 1000 });
310
+ }
311
+
312
+ } else if (condition.type === 'contact') {
313
+ // === CONTACT CONDITION FLOW (e.g., email_is) ===
314
+ // Select "Email" from the "Select attribute" dropdown, then operator + value
315
+
316
+ // 5c-contact: Open "Select attribute" dropdown → select "Email"
317
+ log('Selecting Email attribute...');
318
+ await client.evaluate(`(() => {
319
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
320
+ const matches = buttons.filter(b => b.textContent.includes('Select attribute'));
321
+ const btn = matches[matches.length - 1];
322
+ if (btn) btn.click();
323
+ return { clicked: !!btn };
324
+ })()`);
325
+ await client.wait({ delay: 1000 });
326
+
327
+ await client.evaluate(`(() => {
328
+ const items = [...document.querySelectorAll('li[role="option"]')];
329
+ const matches = items.filter(i => i.textContent.trim() === 'Email');
330
+ const match = matches[matches.length - 1];
331
+ if (match) match.click();
332
+ return { clicked: !!match };
333
+ })()`);
334
+ log('Selected "Email"');
335
+ await client.wait({ delay: 1500 });
336
+
337
+ // 5d-contact: Set operator if needed (default is "is")
338
+ const contactOp = condition.op === 'email_is' ? 'is' : 'is not';
339
+ if (contactOp !== 'is') {
340
+ log(`Changing operator to "${contactOp}"...`);
341
+ await client.evaluate(`(() => {
342
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
343
+ const matches = buttons.filter(b => {
344
+ const t = b.textContent.trim().toLowerCase();
345
+ return t === 'is' || t === 'is not' || t === 'contains';
346
+ });
347
+ const btn = matches[matches.length - 1];
348
+ if (btn) btn.click();
349
+ return { clicked: !!btn };
350
+ })()`);
351
+ await client.wait({ delay: 1000 });
352
+
353
+ await client.evaluate(`(() => {
354
+ const items = [...document.querySelectorAll('li[role="option"]')];
355
+ const match = items.find(i => i.textContent.trim().toLowerCase() === ${JSON.stringify(contactOp)});
356
+ if (match) match.click();
357
+ return { clicked: !!match };
358
+ })()`);
359
+ await client.wait({ delay: 1000 });
360
+ }
361
+
362
+ // 5e-contact: Type the value
363
+ log(`Typing value "${condition.value}"...`);
364
+ await client.evaluate(`(() => {
365
+ const inputs = [...document.querySelectorAll('input[name="value"][placeholder="Set a value"], #text-input-value')];
366
+ const target = inputs[inputs.length - 1];
367
+ if (!target) return { typed: false };
368
+ target.focus();
369
+ const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
370
+ nativeSetter.call(target, ${JSON.stringify(condition.value)});
371
+ target.dispatchEvent(new Event('input', { bubbles: true }));
372
+ target.dispatchEvent(new Event('change', { bubbles: true }));
373
+ return { typed: true };
374
+ })()`);
375
+ log('Value typed');
376
+ await client.wait({ delay: 1000 });
377
+
378
+ } else {
379
+ // === ATTRIBUTE CONDITION FLOW (Custom field) ===
380
+
381
+ // 5c: Open "Select attribute" dropdown → select "Custom field"
382
+ log('Selecting Custom field...');
383
+ await client.evaluate(`(() => {
384
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
385
+ const matches = buttons.filter(b => b.textContent.includes('Select attribute'));
386
+ const btn = matches[matches.length - 1];
387
+ if (btn) btn.click();
388
+ return { clicked: !!btn };
389
+ })()`);
390
+ await client.wait({ delay: 1000 });
391
+
392
+ const customFieldResult = await client.evaluate(`(() => {
393
+ const items = [...document.querySelectorAll('li[role="option"]')];
394
+ const matches = items.filter(i => i.textContent.trim() === 'Custom field');
395
+ const cf = matches[matches.length - 1];
396
+ if (cf) { cf.click(); return { clicked: true }; }
397
+ return { clicked: false, options: items.map(i => i.textContent.trim()) };
398
+ })()`);
399
+ log(`Custom field: ${JSON.stringify(customFieldResult.value)}`);
400
+ if (!customFieldResult.value?.clicked) throw new Error('Could not find "Custom field"');
401
+ await client.wait({ delay: 1500 });
402
+
403
+ // 5d: Open "Select a Custom Field" dropdown → select the field by display name
404
+ const fieldDef = BACKEND_FIELDS_MAP[condition.field];
405
+ const fieldDisplay = fieldDef?.display || condition.field;
406
+ log(`Selecting "${fieldDisplay}"...`);
407
+
408
+ await client.evaluate(`(() => {
409
+ const inputs = [...document.querySelectorAll('input[name="custom_field"]')];
410
+ const target = inputs[inputs.length - 1];
411
+ if (target) {
412
+ const wrapper = target.closest('div[role="button"]');
413
+ if (wrapper) wrapper.click(); else { target.click(); target.focus(); }
414
+ }
415
+ return { clicked: !!target };
416
+ })()`);
417
+ await client.wait({ delay: 2000 });
418
+
419
+ const fieldResult = await client.evaluate(`(() => {
420
+ const items = [...document.querySelectorAll('li[role="option"]')];
421
+ const match = items.find(i => i.textContent.trim() === ${JSON.stringify(fieldDisplay)});
422
+ if (match) { match.click(); return { clicked: true }; }
423
+ return { clicked: false, options: items.map(i => i.textContent.trim()).slice(0, 20) };
424
+ })()`);
425
+ log(`Field: ${JSON.stringify(fieldResult.value)}`);
426
+ if (!fieldResult.value?.clicked) throw new Error(`Could not find field "${fieldDisplay}"`);
427
+ await client.wait({ delay: 1500 });
428
+
429
+ if (isDateCondition) {
430
+ // === DATE CONDITION FLOW ===
431
+ // After selecting a date field, Beehiiv shows:
432
+ // 1. Operator dropdown (is after / is on or after / is before / is on or before)
433
+ // 2. Date type dropdown (a specific date / a relative date)
434
+ // 3. Value input (number) + unit dropdown (days/weeks/months)
435
+
436
+ // 5e-date: Select operator "is on or after" (LAST operator dropdown)
437
+ log('Setting date operator "is on or after"...');
438
+ await client.evaluate(`(() => {
439
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
440
+ const matches = buttons.filter(b => {
441
+ const t = b.textContent.trim().toLowerCase();
442
+ return t.includes('is after') || t.includes('is before') || t.includes('is on or') || t === 'select an option';
443
+ });
444
+ const btn = matches[matches.length - 1];
445
+ if (btn) btn.click();
446
+ return { clicked: !!btn };
447
+ })()`);
448
+ await client.wait({ delay: 1000 });
449
+
450
+ await client.evaluate(`(() => {
451
+ const items = [...document.querySelectorAll('li[role="option"]')];
452
+ const match = items.find(i => i.textContent.trim() === 'is on or after');
453
+ if (match) match.click();
454
+ return { clicked: !!match };
455
+ })()`);
456
+ await client.wait({ delay: 1500 });
457
+
458
+ // 5f-date: Select "a relative date" (LAST date type dropdown)
459
+ log('Selecting "a relative date"...');
460
+ await client.evaluate(`(() => {
461
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
462
+ const matches = buttons.filter(b => {
463
+ const t = b.textContent.trim().toLowerCase();
464
+ return t.includes('specific date') || t.includes('relative date');
465
+ });
466
+ const btn = matches[matches.length - 1];
467
+ if (btn) btn.click();
468
+ return { clicked: !!btn };
469
+ })()`);
470
+ await client.wait({ delay: 1000 });
471
+
472
+ await client.evaluate(`(() => {
473
+ const items = [...document.querySelectorAll('li[role="option"]')];
474
+ const match = items.find(i => i.textContent.trim() === 'a relative date');
475
+ if (match) match.click();
476
+ return { clicked: !!match };
477
+ })()`);
478
+ await client.wait({ delay: 1500 });
479
+
480
+ // 5g-date: Parse days from value (e.g., "90d" → 90) and type into number input
481
+ const days = parseInt(condition.value, 10);
482
+ log(`Typing relative days: ${days}...`);
483
+ await client.evaluate(`(() => {
484
+ const inputs = [...document.querySelectorAll('input[type="number"], input[type="text"]')];
485
+ // Find the last number-like input (for relative date value)
486
+ const target = inputs.filter(i => i.placeholder?.toLowerCase().includes('number') || i.type === 'number').pop()
487
+ || inputs[inputs.length - 1];
488
+ if (target) {
489
+ target.focus();
490
+ const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
491
+ nativeSetter.call(target, '${days}');
492
+ target.dispatchEvent(new Event('input', { bubbles: true }));
493
+ target.dispatchEvent(new Event('change', { bubbles: true }));
494
+ }
495
+ return { typed: !!target };
496
+ })()`);
497
+ await client.wait({ delay: 1000 });
498
+
499
+ } else {
500
+ // === TEXT FIELD CONDITION FLOW ===
501
+
502
+ // 5e: Set operator if we need "is not" (default is "is")
503
+ const opText = beehiivOperator(condition.op);
504
+ if (opText && opText !== 'is') {
505
+ log(`Changing operator to "${opText}"...`);
506
+ await client.evaluate(`(() => {
507
+ const buttons = [...document.querySelectorAll('button[aria-haspopup="listbox"]')];
508
+ const matches = buttons.filter(b => {
509
+ const t = b.textContent.trim().toLowerCase();
510
+ return t === 'is' || t === 'is not';
511
+ });
512
+ const opBtn = matches[matches.length - 1];
513
+ if (opBtn) opBtn.click();
514
+ return { clicked: !!opBtn };
515
+ })()`);
516
+ await client.wait({ delay: 1000 });
517
+
518
+ await client.evaluate(`(() => {
519
+ const items = [...document.querySelectorAll('li[role="option"]')];
520
+ const match = items.find(i => i.textContent.trim().toLowerCase() === ${JSON.stringify(opText)});
521
+ if (match) match.click();
522
+ return { clicked: !!match };
523
+ })()`);
524
+ await client.wait({ delay: 1000 });
525
+ }
526
+
527
+ // 5f: Type value into the LAST value input
528
+ log(`Typing value "${condition.value}"...`);
529
+ await client.evaluate(`(() => {
530
+ const inputs = [...document.querySelectorAll('input[name="value"][placeholder="Set a value"], #text-input-value')];
531
+ const target = inputs[inputs.length - 1];
532
+ if (target) { target.focus(); target.click(); }
533
+ return { focused: !!target };
534
+ })()`);
535
+ await client.wait({ delay: 300 });
536
+ await client.evaluate(`(() => {
537
+ const inputs = [...document.querySelectorAll('input[name="value"][placeholder="Set a value"], #text-input-value')];
538
+ const target = inputs[inputs.length - 1];
539
+ if (!target) return { typed: false };
540
+ const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set;
541
+ nativeSetter.call(target, ${JSON.stringify(condition.value)});
542
+ target.dispatchEvent(new Event('input', { bubbles: true }));
543
+ target.dispatchEvent(new Event('change', { bubbles: true }));
544
+ return { typed: true };
545
+ })()`);
546
+ log('Value typed');
547
+ }
548
+
549
+ } // end condition-type branches
550
+
551
+ await client.wait({ delay: 1000 });
552
+ }
553
+
554
+ // Step 6: Click "Save segment"
555
+ log('Clicking "Save segment"...');
556
+ await client.wait({ delay: 1500 });
557
+ const saveResult = await client.evaluate(`(() => {
558
+ const buttons = [...document.querySelectorAll('button')];
559
+ const saveBtn = buttons.find(b => {
560
+ const t = b.textContent.trim().toLowerCase();
561
+ return t.includes('save segment') || t.includes('create segment');
562
+ });
563
+ if (saveBtn && !saveBtn.disabled) { saveBtn.click(); return { clicked: true, text: saveBtn.textContent.trim() }; }
564
+ if (saveBtn) return { found: true, disabled: true, text: saveBtn.textContent.trim() };
565
+ return { clicked: false, buttons: buttons.map(b => b.textContent.trim()).filter(t => t.length < 30 && t.length > 0) };
566
+ })()`);
567
+ log(`Save button: ${JSON.stringify(saveResult.value)}`);
568
+
569
+ if (!saveResult.value?.clicked) {
570
+ throw new Error(`Could not click save. ${JSON.stringify(saveResult.value)}`);
571
+ }
572
+
573
+ // Wait for save to complete
574
+ await client.wait({ delay: 3000 });
575
+ }
576
+
577
+ module.exports = { OP_DISPLAY, formatCondition, automateCreateSegment };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Validate Chargebee API access and report site info.
3
+ *
4
+ * A lightweight read (list item families, limit 1) proves the site is
5
+ * reachable and CHARGEBEE_API_KEY works before the webhook/products
6
+ * operations rely on it. Pure read — dry-run is identical by construction.
7
+ */
8
+ const chalk = require('chalk').default;
9
+
10
+ module.exports = async function ensureChargebeeAccount(context) {
11
+ const { chargebeeApi: api } = context;
12
+
13
+ if (!api) {
14
+ console.log(` ${chalk.dim('⊘ Chargebee not configured')}`);
15
+ return {};
16
+ }
17
+
18
+ try {
19
+ await api.makeRequest('GET', '/item_families', { limit: 1 });
20
+ } catch (error) {
21
+ console.log(` ${chalk.yellow('⚠')} Could not connect to Chargebee${chalk.dim(`: ${error.message}`)}`);
22
+ return { status: 'warned', reason: 'could not connect to Chargebee', output: { chargebeeAccount: { connected: false, error: error.message } } };
23
+ }
24
+
25
+ console.log(` site: ${chalk.cyan(api.site)}`);
26
+ console.log(` url: ${chalk.dim(`https://${api.site}.chargebee.com`)}`);
27
+ console.log(` API: ${chalk.green('connected')}`);
28
+
29
+ return { output: { chargebeeAccount: { connected: true, site: api.site } } };
30
+ };