@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,245 @@
1
+ /**
2
+ * Ensure Stripe products and prices exist for all paid products.
3
+ *
4
+ * Product resolution: config productId → state (stripeProducts map) →
5
+ * metadata match against the account's products (self-heal when state is
6
+ * lost — every product we create carries { brandId, productId } metadata) →
7
+ * create. Created/matched IDs are written back into omega.json5
8
+ * (payment.products[id=…].stripe.productId — comment-preserving) and
9
+ * mirrored in state. Prices are managed by interval + amount — matching
10
+ * active prices are kept, stale ones archived (Stripe can't delete prices),
11
+ * missing ones created. omega-manager's version had no dry-run guard and no
12
+ * archived-product skip here; both are standard now.
13
+ */
14
+ const chalk = require('chalk').default;
15
+ const { paidProducts, productDisplayName, productImage } = require('../lib/payment-utils.js');
16
+ const { writeBrandConfig } = require('../../../lib/config-write.js');
17
+
18
+ // Map config interval names to Stripe price intervals
19
+ const FREQUENCY_TO_INTERVAL = {
20
+ annually: 'year',
21
+ monthly: 'month',
22
+ weekly: 'week',
23
+ daily: 'day',
24
+ };
25
+
26
+ /**
27
+ * Build desired product details from brand/product config.
28
+ * Images are only managed when brand.images.brandmark is configured.
29
+ */
30
+ function buildProductDetails(brandConfig, brandId, product) {
31
+ return {
32
+ name: productDisplayName(brandConfig, product),
33
+ description: product.description || brandConfig.brand.description || '',
34
+ images: productImage(brandConfig) ? [productImage(brandConfig)] : null,
35
+ url: brandConfig.brand.url,
36
+ metadata: { brandId, productId: product.id },
37
+ };
38
+ }
39
+
40
+ /**
41
+ * Diff a Stripe product against desired and return the update payload
42
+ */
43
+ function getProductUpdates(stripeProduct, desired) {
44
+ const updates = {};
45
+
46
+ if (stripeProduct.name !== desired.name) {
47
+ updates.name = desired.name;
48
+ }
49
+ if ((stripeProduct.description || '') !== desired.description) {
50
+ updates.description = desired.description;
51
+ }
52
+ if (stripeProduct.url !== desired.url) {
53
+ updates.url = desired.url;
54
+ }
55
+
56
+ if (desired.images && JSON.stringify(stripeProduct.images || []) !== JSON.stringify(desired.images)) {
57
+ updates.images = desired.images;
58
+ }
59
+
60
+ // Metadata — ensure all desired keys exist with correct values
61
+ const currentMeta = stripeProduct.metadata || {};
62
+ const metadataUpdates = {};
63
+ for (const [key, value] of Object.entries(desired.metadata)) {
64
+ if (currentMeta[key] !== value) {
65
+ metadataUpdates[key] = value;
66
+ }
67
+ }
68
+ if (Object.keys(metadataUpdates).length > 0) {
69
+ updates.metadata = { ...currentMeta, ...metadataUpdates };
70
+ }
71
+
72
+ return Object.keys(updates).length > 0 ? updates : null;
73
+ }
74
+
75
+ /**
76
+ * Ensure active prices on a Stripe product match config amounts.
77
+ * Creates missing prices, archives actives at the wrong amount.
78
+ */
79
+ async function ensurePrices(api, stripeProductId, product, dryRun) {
80
+ const activePrices = await api.listPricesForProduct(stripeProductId);
81
+
82
+ if (product.type === 'subscription') {
83
+ for (const interval of Object.keys(product.prices)) {
84
+ const expectedAmount = product.prices[interval];
85
+
86
+ if (!expectedAmount) {
87
+ continue;
88
+ }
89
+
90
+ const expectedCents = Math.round(expectedAmount * 100);
91
+ const stripeInterval = FREQUENCY_TO_INTERVAL[interval] || 'month';
92
+
93
+ const match = activePrices.find((p) =>
94
+ p.active
95
+ && p.recurring?.interval === stripeInterval
96
+ && p.unit_amount === expectedCents
97
+ );
98
+
99
+ if (match) {
100
+ console.log(` ${chalk.green('✓')} ${interval}: $${expectedAmount} ${chalk.dim(match.id)}`);
101
+ continue;
102
+ }
103
+
104
+ // Archive active prices at the wrong amount for this interval
105
+ const stale = activePrices.filter((p) =>
106
+ p.active
107
+ && p.recurring?.interval === stripeInterval
108
+ );
109
+
110
+ for (const old of stale) {
111
+ if (dryRun) {
112
+ console.log(` ${chalk.yellow('↓')} ${interval}: would archive $${old.unit_amount / 100} ${chalk.dim(old.id)} ${chalk.yellow('[DRY RUN]')}`);
113
+ } else {
114
+ await api.archivePrice(old.id);
115
+ console.log(` ${chalk.yellow('↓')} ${interval}: archived $${old.unit_amount / 100} ${chalk.dim(old.id)}`);
116
+ }
117
+ }
118
+
119
+ if (dryRun) {
120
+ console.log(` ${chalk.cyan('+')} ${interval}: would create $${expectedAmount} ${chalk.yellow('[DRY RUN]')}`);
121
+ } else {
122
+ const newPrice = await api.createRecurringPrice(stripeProductId, expectedAmount, interval);
123
+ console.log(` ${chalk.green('✓')} ${interval}: created $${expectedAmount} ${chalk.cyan(newPrice.id)}`);
124
+ }
125
+ }
126
+ }
127
+
128
+ if (product.type === 'one-time') {
129
+ const expectedAmount = product.prices.once;
130
+
131
+ if (expectedAmount) {
132
+ const expectedCents = Math.round(expectedAmount * 100);
133
+ const match = activePrices.find((p) => p.active && !p.recurring && p.unit_amount === expectedCents);
134
+
135
+ if (match) {
136
+ console.log(` ${chalk.green('✓')} once: $${expectedAmount} ${chalk.dim(match.id)}`);
137
+ } else {
138
+ const stale = activePrices.filter((p) => p.active && !p.recurring);
139
+ for (const old of stale) {
140
+ if (dryRun) {
141
+ console.log(` ${chalk.yellow('↓')} once: would archive $${old.unit_amount / 100} ${chalk.dim(old.id)} ${chalk.yellow('[DRY RUN]')}`);
142
+ } else {
143
+ await api.archivePrice(old.id);
144
+ console.log(` ${chalk.yellow('↓')} once: archived $${old.unit_amount / 100} ${chalk.dim(old.id)}`);
145
+ }
146
+ }
147
+
148
+ if (dryRun) {
149
+ console.log(` ${chalk.cyan('+')} once: would create $${expectedAmount} ${chalk.yellow('[DRY RUN]')}`);
150
+ } else {
151
+ const newPrice = await api.createOneTimePrice(stripeProductId, expectedAmount);
152
+ console.log(` ${chalk.green('✓')} once: created $${expectedAmount} ${chalk.cyan(newPrice.id)}`);
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ module.exports = async function ensureStripeProducts(context) {
160
+ const { brandConfig, brandId, stripeApi: api, options } = context;
161
+ const dryRun = options?.dryRun || false;
162
+
163
+ if (!api) {
164
+ console.log(` ${chalk.dim('⊘ Stripe not configured')}`);
165
+ return {};
166
+ }
167
+
168
+ if (dryRun) {
169
+ console.log(` ${chalk.yellow('[DRY RUN]')} No changes will be made`);
170
+ }
171
+
172
+ const products = paidProducts(brandConfig);
173
+ const configEdits = {};
174
+ let catalog = null; // Lazy-listed only when a product has no configured ID
175
+
176
+ for (const product of products) {
177
+ console.log(` ${chalk.bold(product.name)} (${product.id}):`);
178
+
179
+ const desired = buildProductDetails(brandConfig, brandId, product);
180
+
181
+ // --- Resolve the Stripe product: config → metadata match → create ---
182
+ const stripeId = product.stripe?.productId || null;
183
+ let stripeProduct = null;
184
+
185
+ if (stripeId) {
186
+ stripeProduct = await api.getProduct(stripeId);
187
+ } else {
188
+ // Self-heal: our products carry { brandId, productId } metadata
189
+ catalog = catalog || await api.listAllProducts();
190
+ stripeProduct = catalog.find((p) =>
191
+ p.active !== false
192
+ && p.metadata?.brandId === brandId
193
+ && p.metadata?.productId === product.id
194
+ ) || null;
195
+
196
+ if (stripeProduct) {
197
+ console.log(` ${chalk.green('✓')} Matched existing product by metadata: ${chalk.dim(stripeProduct.id)}`);
198
+ }
199
+ }
200
+
201
+ if (stripeProduct) {
202
+ console.log(` ${chalk.green('✓')} Product exists: ${chalk.dim(stripeProduct.id)}`);
203
+
204
+ const updates = getProductUpdates(stripeProduct, desired);
205
+ if (updates) {
206
+ const fields = Object.keys(updates).join(', ');
207
+ if (dryRun) {
208
+ console.log(` ${chalk.cyan('~')} Would update: ${chalk.dim(fields)} ${chalk.yellow('[DRY RUN]')}`);
209
+ } else {
210
+ await api.updateProduct(stripeProduct.id, updates);
211
+ console.log(` ${chalk.green('✓')} Updated: ${chalk.dim(fields)}`);
212
+ }
213
+ }
214
+ } else {
215
+ if (dryRun) {
216
+ console.log(` ${chalk.cyan('+')} Would create product: ${chalk.dim(desired.name)} ${chalk.yellow('[DRY RUN]')}`);
217
+ // Can't manage prices without a real product ID
218
+ continue;
219
+ }
220
+
221
+ stripeProduct = await api.createProduct({
222
+ name: desired.name,
223
+ brandId,
224
+ productId: product.id,
225
+ description: desired.description,
226
+ images: desired.images || undefined,
227
+ url: desired.url,
228
+ });
229
+ console.log(` ${chalk.green('✓')} Created product: ${chalk.cyan(stripeProduct.id)}`);
230
+ }
231
+
232
+ if (stripeProduct.id !== product.stripe?.productId) {
233
+ configEdits[`payment.products[id=${product.id}].stripe.productId`] = stripeProduct.id;
234
+ }
235
+
236
+ // --- Ensure prices match config amounts ---
237
+ await ensurePrices(api, stripeProduct.id, product, dryRun);
238
+ }
239
+
240
+ if (Object.keys(configEdits).length > 0) {
241
+ writeBrandConfig(context, configEdits);
242
+ }
243
+
244
+ return { output: { stripeSync: { productsProcessed: products.length } } };
245
+ };
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Report the desired Stripe Radar rules.
3
+ *
4
+ * Stripe has no Radar rules API (Dashboard-only), so this follows the
5
+ * no-API manual-action pattern (the search service's ga-link): print the desired
6
+ * rules + the Dashboard deep-link, confirm interactively, and stay warned
7
+ * until confirmed. A confirmation nothing can re-check is the one kind of
8
+ * reconcile flag config keeps (#434): it lands at
9
+ * `payment.providers.stripe.radarConfirmed`. Rules come from
10
+ * payment.providers.stripe.radar — manager defaults with per-brand
11
+ * override. Cannot mutate by construction.
12
+ */
13
+ const chalk = require('chalk').default;
14
+ const { confirm, pressEnterToOpen } = require('../../../vendor/devkit/prompt.js');
15
+ const { writeBrandConfig } = require('../../../lib/config-write.js');
16
+ const { canPrompt } = require('../../../lib/run-gates.js');
17
+
18
+ const CONFIG_PATH = 'payment.providers.stripe.radarConfirmed';
19
+
20
+ module.exports = async function ensureStripeRadar(context) {
21
+ const { brandConfig, stripeApi: api, serviceData, options = {} } = context;
22
+
23
+ if (!api) {
24
+ console.log(` ${chalk.dim('⊘ Stripe not configured')}`);
25
+ return {};
26
+ }
27
+
28
+ const desiredRules = brandConfig.payment?.providers?.stripe?.radar || [];
29
+ if (desiredRules.length === 0) {
30
+ console.log(` ${chalk.dim('⊘ No Radar rules configured')}`);
31
+ return {};
32
+ }
33
+
34
+ if (brandConfig.payment?.providers?.stripe?.radarConfirmed === true) {
35
+ console.log(` ${chalk.green('✓')} Radar rules confirmed (${desiredRules.length} rules)`);
36
+ return {};
37
+ }
38
+
39
+ const radarUrl = serviceData.stripeAccountId
40
+ ? `https://dashboard.stripe.com/${serviceData.stripeAccountId}/radar/rules`
41
+ : 'https://dashboard.stripe.com/radar/rules';
42
+
43
+ console.log(` ${chalk.yellow('⚠')} Stripe has no Radar rules API — add these in the Dashboard:`);
44
+ console.log('');
45
+ for (const rule of desiredRules) {
46
+ const actionColor = rule.action === 'block' ? chalk.red
47
+ : rule.action === 'review' ? chalk.yellow
48
+ : chalk.cyan;
49
+ console.log(` ${actionColor(rule.action.padEnd(24))} ${chalk.dim(rule.predicate)}`);
50
+ if (rule.description) {
51
+ console.log(` ${' '.repeat(24)} ${chalk.dim(`↳ ${rule.description}`)}`);
52
+ }
53
+ }
54
+ console.log('');
55
+
56
+ if (canPrompt(options)) {
57
+ await pressEnterToOpen(radarUrl, 'the Stripe Radar rules page');
58
+ const done = await confirm({ message: 'Radar rules added in the Dashboard?', default: false });
59
+ if (done) {
60
+ writeBrandConfig(context, { [CONFIG_PATH]: true });
61
+ console.log(` ${chalk.green('✓')} Radar rules confirmed (${desiredRules.length} rules)`);
62
+ return {};
63
+ }
64
+ } else {
65
+ console.log(` ${chalk.dim('→')} Radar rules: ${chalk.cyan(radarUrl)}`);
66
+ console.log(` ${chalk.dim('→')} (rerun in an interactive terminal to confirm)`);
67
+ }
68
+
69
+ return {
70
+ status: 'warned',
71
+ reason: 'Radar rules not confirmed — add them in the Stripe Dashboard',
72
+ output: { stripeRadar: { rules: desiredRules.length, radarUrl } },
73
+ };
74
+ };
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Ensure a Stripe webhook endpoint exists for this brand's backend.
3
+ *
4
+ * Matches by exact URL, deletes every OTHER endpoint on the brand's API host
5
+ * (stale twins — one endpoint per host, and endpoints on other hosts are never
6
+ * touched), re-enables endpoints Stripe auto-disabled after repeated failures,
7
+ * diffs enabled_events, updates when drifted, creates when missing. Skipped for
8
+ * shared Firebase projects (no api.{domain} backend to receive events).
9
+ * Requires OMEGA_WEBHOOK_KEY.
10
+ */
11
+ const chalk = require('chalk').default;
12
+ const { buildWebhookUrl, diffEventSets, redactWebhookUrl, staleWebhookEndpoints } = require('../lib/payment-utils.js');
13
+
14
+ // Events the backend handles for subscription/payment processing
15
+ const ENABLED_EVENTS = [
16
+ // Subscriptions
17
+ 'customer.subscription.created',
18
+ 'customer.subscription.updated',
19
+ 'customer.subscription.deleted',
20
+ 'customer.subscription.paused',
21
+ 'customer.subscription.resumed',
22
+ 'customer.subscription.trial_will_end',
23
+
24
+ // Invoices (subscription renewals + failed payments)
25
+ 'invoice.created',
26
+ 'invoice.paid',
27
+ 'invoice.payment_succeeded',
28
+ 'invoice.payment_failed',
29
+ 'invoice.payment_action_required',
30
+
31
+ // One-time purchases (Checkout + charges)
32
+ 'checkout.session.completed',
33
+ 'charge.succeeded',
34
+ 'charge.failed',
35
+ 'charge.refunded',
36
+
37
+ // Payment intents (covers both subscription and one-time)
38
+ 'payment_intent.succeeded',
39
+ 'payment_intent.payment_failed',
40
+ ];
41
+
42
+ module.exports = async function ensureStripeWebhook(context) {
43
+ const { brandConfig, stripeApi: api, options } = context;
44
+ const dryRun = options?.dryRun || false;
45
+
46
+ if (!api) {
47
+ console.log(` ${chalk.dim('⊘ Stripe not configured')}`);
48
+ return {};
49
+ }
50
+
51
+ // Shared Firebase project — no backend deploys from this brand, so api.{domain} doesn't exist
52
+ if (brandConfig.cloud?.shared === true) {
53
+ console.log(` ${chalk.dim('⊘ Shared Firebase project — no brand API for webhooks')}`);
54
+ return {};
55
+ }
56
+
57
+ if (!process.env.OMEGA_WEBHOOK_KEY) {
58
+ // Unreachable by design (#635): the key is OMEGA's own (`generated:` in
59
+ // the env schema) and the payment service's setup mints it through the
60
+ // shared contract before any operation runs. Absent here = broken wiring.
61
+ throw new Error('OMEGA_WEBHOOK_KEY absent after the payment setup ran — its REQUIRES entry or its requestServiceInput call is missing');
62
+ }
63
+
64
+ const desiredUrl = buildWebhookUrl(brandConfig, 'stripe');
65
+
66
+ const { data: webhooks = [] } = await api.listWebhookEndpoints();
67
+
68
+ // Exactly ONE endpoint on the brand's API host: the desired URL. Legacy twins
69
+ // there receive every event and answer 400 forever (#570).
70
+ for (const stale of staleWebhookEndpoints(webhooks, desiredUrl)) {
71
+ if (dryRun) {
72
+ console.log(` ${chalk.cyan('-')} Would delete stale webhook ${chalk.dim(redactWebhookUrl(stale.url))} ${chalk.yellow('[DRY RUN]')}`);
73
+ continue;
74
+ }
75
+
76
+ await api.deleteWebhookEndpoint(stale.id);
77
+ console.log(` ${chalk.yellow('⚠')} Deleted stale webhook ${chalk.dim(`(${stale.id})`)} ${redactWebhookUrl(stale.url)}`);
78
+ }
79
+
80
+ const existing = webhooks.find((wh) => wh.url === desiredUrl);
81
+
82
+ if (existing) {
83
+ // Re-enable if disabled (Stripe auto-disables after too many failures)
84
+ if (existing.status === 'disabled') {
85
+ if (dryRun) {
86
+ console.log(` ${chalk.cyan('~')} Would re-enable disabled webhook ${chalk.yellow('[DRY RUN]')}`);
87
+ } else {
88
+ console.log(` ${chalk.yellow('⚠')} Webhook is disabled — re-enabling...`);
89
+ await api.updateWebhookEndpoint(existing.id, { disabled: false });
90
+ console.log(` ${chalk.green('✓')} Webhook re-enabled`);
91
+ }
92
+ }
93
+
94
+ const diff = diffEventSets(existing.enabled_events || [], ENABLED_EVENTS);
95
+
96
+ if (!diff) {
97
+ console.log(` ${chalk.green('✓')} Webhook up to date ${chalk.dim(`(${existing.id})`)}`);
98
+ return { output: { stripeWebhook: { id: existing.id, updated: false } } };
99
+ }
100
+
101
+ if (diff.missing.length > 0) {
102
+ console.log(` Adding events: ${chalk.cyan(diff.missing.join(', '))}`);
103
+ }
104
+ if (diff.extra.length > 0) {
105
+ console.log(` Removing events: ${chalk.dim(diff.extra.join(', '))}`);
106
+ }
107
+
108
+ if (dryRun) {
109
+ console.log(` ${chalk.cyan('~')} Would update webhook events ${chalk.yellow('[DRY RUN]')}`);
110
+ return { output: { stripeWebhook: { id: existing.id, planned: 'update' } } };
111
+ }
112
+
113
+ await api.updateWebhookEndpoint(existing.id, { enabled_events: ENABLED_EVENTS });
114
+ console.log(` ${chalk.green('✓')} Webhook events updated ${chalk.dim(`(${existing.id})`)}`);
115
+
116
+ return { output: { stripeWebhook: { id: existing.id, updated: true } } };
117
+ }
118
+
119
+ if (dryRun) {
120
+ console.log(` ${chalk.cyan('+')} Would create webhook ${chalk.yellow('[DRY RUN]')}`);
121
+ console.log(` ${chalk.dim(redactWebhookUrl(desiredUrl))}`);
122
+ return { output: { stripeWebhook: { planned: 'create' } } };
123
+ }
124
+
125
+ const webhook = await api.createWebhookEndpoint(desiredUrl, ENABLED_EVENTS);
126
+ console.log(` ${chalk.green('✓')} Webhook created: ${chalk.dim(webhook.id)}`);
127
+ console.log(` ${chalk.dim(redactWebhookUrl(desiredUrl))}`);
128
+
129
+ return { output: { stripeWebhook: { id: webhook.id, created: true } } };
130
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Payment service — reconciles the brand's payment providers to
3
+ * payment.products: Stripe (account settings, products + prices, webhook,
4
+ * plus manual-guidance Radar/dispute checks), PayPal (catalog products +
5
+ * billing plans, webhook), and Chargebee (item family → items → item prices,
6
+ * webhook). Product IDs are written back to omega.json5
7
+ * (payment.products[id=…] — comment-preserving) and mirrored in state.
8
+ *
9
+ * Coinbase Commerce (crypto, #642) reconciles NOTHING — no catalog, no webhook
10
+ * API — so it has no operations and no client; its whole surface here is the
11
+ * credential ask, gated on payment.providers.coinbase.enabled.
12
+ *
13
+ * Auth is per provider, from config + the brand .env:
14
+ * - Stripe: STRIPE_SECRET_KEY
15
+ * - PayPal: payment.providers.paypal.clientId + PAYPAL_CLIENT_SECRET
16
+ * - Chargebee: payment.providers.chargebee.site + CHARGEBEE_API_KEY
17
+ * - Coinbase: COINBASE_COMMERCE_API_KEY (no public half at all)
18
+ * When an enabled provider's credentials are missing, interactive runs
19
+ * offer the setup flow (lib/provider-setup.js — dashboard browser open,
20
+ * public keys land in omega.json5, secrets in the brand .env); otherwise
21
+ * the provider is skipped per-operation with a dim note. The service only
22
+ * skips when NO provider is configured. The webhook operations additionally
23
+ * need OMEGA_WEBHOOK_KEY, which the setup MINTS through the shared contract
24
+ * (it is OMEGA's own key, #635) rather than asking anyone for.
25
+ *
26
+ * --provider=stripe|paypal|chargebee|coinbase narrows the run to one provider's
27
+ * operations (omega-manager's flag, unchanged).
28
+ */
29
+ const { serviceInputSpec } = require('../../config.js');
30
+ const { createServiceRunner } = require('../../lib/service-runner.js');
31
+ const { requestServiceInput } = require('../../lib/service-input.js');
32
+ const { StripeAPI } = require('./lib/stripe-api.js');
33
+ const { PayPalAPI } = require('./lib/paypal-api.js');
34
+ const { ChargebeeAPI } = require('./lib/chargebee-api.js');
35
+ const { paidProducts } = require('./lib/payment-utils.js');
36
+ const { providerSetupFlow } = require('./lib/provider-setup.js');
37
+
38
+ const PROVIDERS = ['paypal', 'stripe', 'chargebee', 'coinbase'];
39
+
40
+ module.exports.run = createServiceRunner({
41
+ serviceDir: __dirname,
42
+ setup: async (context) => {
43
+ const payment = context.brandConfig.payment || {};
44
+
45
+ if (payment.enabled === false) {
46
+ return { skip: true, reason: 'payment.enabled = false' };
47
+ }
48
+
49
+ const url = (context.brandConfig.brand?.url || '').replace(/^https?:\/\//, '').replace(/\/$/, '');
50
+ if (!url) {
51
+ return { skip: true, reason: 'no brand.url configured' };
52
+ }
53
+
54
+ if (paidProducts(context.brandConfig).length === 0) {
55
+ return { skip: true, reason: 'no paid products in payment.products' };
56
+ }
57
+
58
+ const providerFilter = context.options?.provider || null;
59
+ if (providerFilter && !PROVIDERS.includes(providerFilter)) {
60
+ return { skip: true, reason: `unknown --provider "${providerFilter}" (paypal | stripe | chargebee | coinbase)` };
61
+ }
62
+ const wanted = (name) => !providerFilter || providerFilter === name;
63
+
64
+ // Resolve each provider; when an enabled one is missing credentials,
65
+ // interactive runs offer the setup flow (which lands them in
66
+ // omega.json5 + the brand .env + process.env) before giving up.
67
+ // Unconfigured ones stay null and their operations print a dim note.
68
+ // Tests inject fake clients via context.stripeApi / paypalApi /
69
+ // chargebeeApi (injected clients never trigger the flow).
70
+ const providers = () => context.brandConfig.payment.providers || {};
71
+
72
+ let stripeApi = null;
73
+ if (wanted('stripe') && providers().stripe !== false) {
74
+ if (!context.stripeApi && (!process.env.STRIPE_SECRET_KEY || !providers().stripe?.publishableKey)) {
75
+ await providerSetupFlow(context, 'stripe');
76
+ }
77
+ if (providers().stripe !== false) {
78
+ stripeApi = context.stripeApi
79
+ || (process.env.STRIPE_SECRET_KEY ? new StripeAPI(process.env.STRIPE_SECRET_KEY) : null);
80
+ }
81
+ }
82
+
83
+ let paypalApi = null;
84
+ if (wanted('paypal') && providers().paypal !== false) {
85
+ if (!context.paypalApi && (!providers().paypal?.clientId || !process.env.PAYPAL_CLIENT_SECRET)) {
86
+ await providerSetupFlow(context, 'paypal');
87
+ }
88
+ const clientId = providers().paypal !== false ? providers().paypal?.clientId : null;
89
+ paypalApi = context.paypalApi
90
+ || (clientId && process.env.PAYPAL_CLIENT_SECRET ? new PayPalAPI(clientId, process.env.PAYPAL_CLIENT_SECRET) : null);
91
+ }
92
+
93
+ let chargebeeApi = null;
94
+ if (wanted('chargebee') && providers().chargebee !== false) {
95
+ if (!context.chargebeeApi && (!providers().chargebee?.site || !process.env.CHARGEBEE_API_KEY)) {
96
+ await providerSetupFlow(context, 'chargebee');
97
+ }
98
+ const site = providers().chargebee !== false ? providers().chargebee?.site : null;
99
+ chargebeeApi = context.chargebeeApi
100
+ || (site && process.env.CHARGEBEE_API_KEY ? new ChargebeeAPI(site, process.env.CHARGEBEE_API_KEY) : null);
101
+ }
102
+
103
+ // Coinbase Commerce (crypto, [#642](https://github.com/Omega-JS-Stack/omega/issues/642))
104
+ // has NO operations and therefore no API client: a charge is created ad hoc
105
+ // at checkout, there is no product catalog to reconcile, and the webhook
106
+ // endpoint is set by hand in its dashboard (the API manages none). Its whole
107
+ // manage-time surface is the CREDENTIAL, so the shared setup contract asks
108
+ // for it right here (#608) and nothing below reads it.
109
+ //
110
+ // Gated on the provider being switched ON, because `enabled` IS the whole
111
+ // switch for this one (there is no public datum to gate on — the API key is
112
+ // the entire credential): a brand that never turned crypto on is never asked
113
+ // for a key it has no use for.
114
+ if (wanted('coinbase') && providers().coinbase?.enabled === true && !process.env.COINBASE_COMMERCE_API_KEY) {
115
+ await providerSetupFlow(context, 'coinbase');
116
+ }
117
+
118
+ if (!stripeApi && !paypalApi && !chargebeeApi) {
119
+ return {
120
+ skip: true,
121
+ reason: providerFilter === 'coinbase'
122
+ // Asked for above and that is all there is: crypto reconciles nothing
123
+ ? 'coinbase has nothing to reconcile — Coinbase Commerce has no product catalog and no webhook API; its key is the whole surface'
124
+ : providerFilter
125
+ ? `provider "${providerFilter}" not configured`
126
+ : 'no payment provider configured (STRIPE_SECRET_KEY, paypal.clientId + PAYPAL_CLIENT_SECRET, or chargebee.site + CHARGEBEE_API_KEY)',
127
+ };
128
+ }
129
+
130
+ // The webhook key is OMEGA's OWN (#635): the shared helper mints it in
131
+ // place when this service runs before the workspace one did, so every
132
+ // webhook operation below can read it unconditionally.
133
+ const gate = await requestServiceInput(context, serviceInputSpec('payment', { names: ['OMEGA_WEBHOOK_KEY'] }));
134
+ if (gate) return gate;
135
+
136
+ // Stripe account ID → state (dashboard deep-links for the radar/disputes
137
+ // guidance; omega-manager's bookmark service reads it too). Non-fatal.
138
+ let serviceData;
139
+ if (stripeApi) {
140
+ try {
141
+ const account = await stripeApi.getAccount();
142
+ serviceData = { stripeAccountId: account.id };
143
+ } catch {
144
+ // Deep-links fall back to the account-less dashboard URLs
145
+ }
146
+ }
147
+
148
+ // Narrow operations to the filtered provider (operation names are prefixed)
149
+ let operations;
150
+ if (providerFilter) {
151
+ operations = context.operations.filter((op) => op.name.startsWith(providerFilter));
152
+ }
153
+
154
+ return {
155
+ stripeApi,
156
+ paypalApi,
157
+ chargebeeApi,
158
+ domain: url,
159
+ serviceData,
160
+ operations,
161
+ };
162
+ },
163
+ });