@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,374 @@
1
+ /**
2
+ * Ensure PayPal catalog products and billing plans exist for all paid
3
+ * products.
4
+ *
5
+ * Product resolution: config productId → state (paypalProducts map) →
6
+ * exact-name match against the catalog (self-heal when state is lost) →
7
+ * create. Created/matched IDs are written back into omega.json5
8
+ * (payment.products[id=…].paypal.productId — comment-preserving) and
9
+ * mirrored in state. Plans are managed by interval + amount + trial-cycle
10
+ * presence — matching active plans are kept, duplicates and stale plans
11
+ * deactivated, missing ones created; a product with trial days carries a TWIN
12
+ * pair per interval (with the trial cycle, and without it) so a buyer who is
13
+ * not owed a trial has a plan to land on. Legacy products (product.paypal.legacyProductIds)
14
+ * get their active plans deactivated so no new subscriptions land on them.
15
+ *
16
+ * In sandbox mode the ensured products are the payments-QA fixtures, so the
17
+ * step closes with the sandbox-buyer reminder (see below).
18
+ */
19
+ const chalk = require('chalk').default;
20
+ const { paidProducts, productDisplayName, productImage } = require('../lib/payment-utils.js');
21
+ const { writeBrandConfig } = require('../../../lib/config-write.js');
22
+
23
+ // Map config interval names to PayPal interval units
24
+ const FREQUENCY_TO_INTERVAL = {
25
+ annually: 'YEAR',
26
+ monthly: 'MONTH',
27
+ weekly: 'WEEK',
28
+ daily: 'DAY',
29
+ };
30
+
31
+ // PayPal Developer Dashboard → Sandbox → Accounts (the only surface that mints
32
+ // a sandbox buyer) and the drive runbook that documents the hoops
33
+ const SANDBOX_ACCOUNTS_URL = 'https://developer.paypal.com/dashboard/accounts';
34
+ const SANDBOX_QA_RUNBOOK = '@omega.js/backend/docs/paypal-sandbox-qa.md';
35
+
36
+ /**
37
+ * Build desired catalog-product details from brand/product config
38
+ */
39
+ function buildProductDetails(brandConfig, product) {
40
+ return {
41
+ name: productDisplayName(brandConfig, product),
42
+ description: (product.description || brandConfig.brand.description || '').slice(0, 256),
43
+ imageUrl: productImage(brandConfig),
44
+ homeUrl: brandConfig.brand.url,
45
+ };
46
+ }
47
+
48
+ /**
49
+ * Diff a PayPal catalog product against desired and return JSON Patch ops.
50
+ * Images are only managed when brand.images.brandmark is configured.
51
+ */
52
+ function getProductUpdates(paypalProduct, desired) {
53
+ const patches = [];
54
+
55
+ if (paypalProduct.name !== desired.name) {
56
+ patches.push({ op: 'replace', path: '/name', value: desired.name });
57
+ }
58
+ if ((paypalProduct.description || '') !== desired.description) {
59
+ patches.push({ op: 'replace', path: '/description', value: desired.description });
60
+ }
61
+ if (desired.imageUrl && paypalProduct.image_url !== desired.imageUrl) {
62
+ patches.push({ op: 'replace', path: '/image_url', value: desired.imageUrl });
63
+ }
64
+ if (paypalProduct.home_url !== desired.homeUrl) {
65
+ patches.push({ op: 'replace', path: '/home_url', value: desired.homeUrl });
66
+ }
67
+
68
+ return patches.length > 0 ? patches : null;
69
+ }
70
+
71
+ /**
72
+ * Check if a plan's billing cycles match the expected config.
73
+ * Matches on: interval unit, amount, and trial days.
74
+ *
75
+ * `expectedTrialDays` carries the twin dimension: a positive value matches the
76
+ * trial twin (a TRIAL cycle of exactly that many days ahead of the REGULAR
77
+ * one), 0 matches the skip-trial twin (a REGULAR cycle and nothing else), so
78
+ * neither twin ever matches the other's row ([#761]).
79
+ */
80
+ function isPlanMatch(plan, intervalUnit, expectedAmount, expectedTrialDays) {
81
+ if (plan.status !== 'ACTIVE') {
82
+ return false;
83
+ }
84
+
85
+ const regularCycle = plan.billing_cycles?.find((c) => c.tenure_type === 'REGULAR');
86
+ if (!regularCycle) {
87
+ return false;
88
+ }
89
+
90
+ if (regularCycle.frequency?.interval_unit !== intervalUnit) {
91
+ return false;
92
+ }
93
+
94
+ const planAmount = parseFloat(regularCycle.pricing_scheme?.fixed_price?.value || '0');
95
+ if (planAmount !== expectedAmount) {
96
+ return false;
97
+ }
98
+
99
+ const trialCycle = plan.billing_cycles?.find((c) => c.tenure_type === 'TRIAL');
100
+ if (expectedTrialDays > 0) {
101
+ if (!trialCycle || trialCycle.total_cycles !== expectedTrialDays) {
102
+ return false;
103
+ }
104
+ } else if (trialCycle) {
105
+ return false;
106
+ }
107
+
108
+ return true;
109
+ }
110
+
111
+ /**
112
+ * Ensure billing plans on a PayPal product match config.
113
+ * Creates new plans if missing, deactivates duplicates and stale plans.
114
+ *
115
+ * A product that configures trial days carries TWIN plans per paid interval:
116
+ * the trial twin (TRIAL cycle + REGULAR) and the skip-trial twin (REGULAR
117
+ * only). PayPal puts trials on the PLAN, never on the subscribe call, so a
118
+ * returning buyer who is not owed a trial has to subscribe to a plan that has
119
+ * no free cycle on it at all — the backend picks the twin by the same
120
+ * interval + amount + trial-presence rule this walk matches on
121
+ * ([#761](https://github.com/Omega-JS-Stack/omega/issues/761)). A product
122
+ * without trial days carries one plan, as before. Neither twin is a duplicate
123
+ * or a stale copy of the other, so a rerun creates and deactivates nothing.
124
+ */
125
+ async function ensurePlans(api, paypalProductId, product, brandConfig, dryRun) {
126
+ const plans = await api.listPlansForProduct(paypalProductId);
127
+ const trialDays = product.trial?.days || 0;
128
+
129
+ // The twins to ensure, in dashboard order: with a trial cycle, then without
130
+ const twins = trialDays > 0 ? [trialDays, 0] : [0];
131
+
132
+ // Only a twinned product has plans to tell apart — a single-plan product's
133
+ // names and log lines stay exactly as they were
134
+ const isSkipTrialTwin = (twinTrialDays) => trialDays > 0 && twinTrialDays === 0;
135
+ const twinLabel = (twinTrialDays) => {
136
+ if (trialDays === 0) {
137
+ return '';
138
+ }
139
+ return isSkipTrialTwin(twinTrialDays) ? ' (no trial)' : ' (trial)';
140
+ };
141
+
142
+ for (const interval of Object.keys(product.prices)) {
143
+ const expectedAmount = product.prices[interval];
144
+
145
+ if (!expectedAmount) {
146
+ continue;
147
+ }
148
+
149
+ const intervalUnit = FREQUENCY_TO_INTERVAL[interval];
150
+
151
+ // Skip 'once' — one-time products don't have plans
152
+ if (!intervalUnit) {
153
+ continue;
154
+ }
155
+
156
+ // Every active plan on this interval, split ONCE across the twins: a keeper
157
+ // per twin, its extra matches are duplicates, and what no twin claims is
158
+ // stale (splitting per twin instead would read one twin as the other's
159
+ // stale plan and deactivate it every run)
160
+ const intervalPlans = plans.filter((plan) => {
161
+ if (plan.status !== 'ACTIVE') {
162
+ return false;
163
+ }
164
+ const regularCycle = plan.billing_cycles?.find((c) => c.tenure_type === 'REGULAR');
165
+ return regularCycle?.frequency?.interval_unit === intervalUnit;
166
+ });
167
+
168
+ const claimed = new Set();
169
+ const duplicates = [];
170
+ const missing = [];
171
+
172
+ for (const twinTrialDays of twins) {
173
+ const matches = intervalPlans.filter((plan) => isPlanMatch(plan, intervalUnit, expectedAmount, twinTrialDays));
174
+
175
+ for (const plan of matches) {
176
+ claimed.add(plan);
177
+ }
178
+
179
+ if (matches.length === 0) {
180
+ missing.push(twinTrialDays);
181
+ continue;
182
+ }
183
+
184
+ console.log(` ${chalk.green('✓')} ${interval}${twinLabel(twinTrialDays)}: $${expectedAmount} ${chalk.dim(matches[0].id)}`);
185
+ duplicates.push(...matches.slice(1));
186
+ }
187
+
188
+ const stale = intervalPlans.filter((plan) => !claimed.has(plan));
189
+
190
+ for (const dup of duplicates) {
191
+ if (dryRun) {
192
+ console.log(` ${chalk.yellow('↓')} ${interval}: would deactivate duplicate ${chalk.dim(dup.id)} ${chalk.yellow('[DRY RUN]')}`);
193
+ } else {
194
+ await api.deactivatePlan(dup.id);
195
+ console.log(` ${chalk.yellow('↓')} ${interval}: deactivated duplicate ${chalk.dim(dup.id)}`);
196
+ }
197
+ }
198
+
199
+ for (const old of stale) {
200
+ if (dryRun) {
201
+ console.log(` ${chalk.yellow('↓')} ${interval}: would deactivate ${chalk.dim(old.id)} ${chalk.yellow('[DRY RUN]')}`);
202
+ } else {
203
+ await api.deactivatePlan(old.id);
204
+ console.log(` ${chalk.yellow('↓')} ${interval}: deactivated ${chalk.dim(old.id)}`);
205
+ }
206
+ }
207
+
208
+ // Create the twins nothing matched
209
+ for (const twinTrialDays of missing) {
210
+ const intervalName = interval.charAt(0).toUpperCase() + interval.slice(1);
211
+ const planName = `${productDisplayName(brandConfig, product)} (${intervalName}${isSkipTrialTwin(twinTrialDays) ? ', no trial' : ''})`;
212
+
213
+ if (dryRun) {
214
+ console.log(` ${chalk.cyan('+')} ${interval}: would create $${expectedAmount} plan "${planName}" ${chalk.yellow('[DRY RUN]')}`);
215
+ } else {
216
+ const newPlan = await api.createPlan({
217
+ productId: paypalProductId,
218
+ name: planName,
219
+ interval,
220
+ amount: expectedAmount,
221
+ trialDays: twinTrialDays,
222
+ });
223
+ console.log(` ${chalk.green('✓')} ${interval}${twinLabel(twinTrialDays)}: created $${expectedAmount} ${chalk.cyan(newPlan.id)}`);
224
+ }
225
+ }
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Deactivate active plans on legacy PayPal products
231
+ * (product.paypal.legacyProductIds). Existing subscribers keep their plans;
232
+ * no new subscriptions can be created on them.
233
+ */
234
+ async function deactivateLegacyPlans(api, products, dryRun) {
235
+ const legacyIds = new Set();
236
+ for (const product of products) {
237
+ for (const id of product.paypal?.legacyProductIds || []) {
238
+ legacyIds.add(id);
239
+ }
240
+ }
241
+
242
+ if (legacyIds.size === 0) {
243
+ return;
244
+ }
245
+
246
+ console.log(` ${chalk.dim('Legacy PayPal products:')}`);
247
+
248
+ for (const legacyProductId of legacyIds) {
249
+ const plans = await api.listPlansForProduct(legacyProductId);
250
+ const activePlans = plans.filter((p) => p.status === 'ACTIVE');
251
+
252
+ if (activePlans.length === 0) {
253
+ console.log(` ${chalk.green('✓')} ${chalk.dim(legacyProductId)}: no active plans`);
254
+ continue;
255
+ }
256
+
257
+ for (const plan of activePlans) {
258
+ if (dryRun) {
259
+ console.log(` ${chalk.yellow('↓')} ${chalk.dim(legacyProductId)}: would deactivate ${plan.name} ${chalk.dim(plan.id)} ${chalk.yellow('[DRY RUN]')}`);
260
+ } else {
261
+ await api.deactivatePlan(plan.id);
262
+ console.log(` ${chalk.yellow('↓')} ${chalk.dim(legacyProductId)}: deactivated ${plan.name} ${chalk.dim(plan.id)}`);
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Sandbox only: the fixtures just ensured can be bought only by a sandbox
270
+ * BUYER account, and PayPal exposes no API to mint one (probed 2026-08-18,
271
+ * #348 — the app-credential token carries no sandbox-account scope), so the
272
+ * walk reminds the operator instead of precreating it. Live runs say nothing.
273
+ */
274
+ function remindSandboxBuyer(api) {
275
+ if (api.getAccountInfo().environment !== 'sandbox') {
276
+ return;
277
+ }
278
+
279
+ console.log(` ${chalk.yellow('⚠')} Sandbox — buying the QA fixture (proof-press) through guest checkout needs a sandbox BUYER account (no API mints one)`);
280
+ console.log(` ${chalk.dim('→')} Sandbox → Accounts: ${chalk.cyan(SANDBOX_ACCOUNTS_URL)} ${chalk.dim(`· runbook: ${SANDBOX_QA_RUNBOOK}`)}`);
281
+ }
282
+
283
+ module.exports = async function ensurePayPalProducts(context) {
284
+ const { brandConfig, paypalApi: api, options } = context;
285
+ const dryRun = options?.dryRun || false;
286
+
287
+ if (!api) {
288
+ console.log(` ${chalk.dim('⊘ PayPal not configured')}`);
289
+ return {};
290
+ }
291
+
292
+ if (dryRun) {
293
+ console.log(` ${chalk.yellow('[DRY RUN]')} No changes will be made`);
294
+ }
295
+
296
+ const products = paidProducts(brandConfig);
297
+ const configEdits = {};
298
+ let catalog = null; // Lazy-listed only when a product has no configured ID
299
+
300
+ for (const product of products) {
301
+ console.log(` ${chalk.bold(product.name)} (${product.id}):`);
302
+
303
+ const desired = buildProductDetails(brandConfig, product);
304
+
305
+ // --- Resolve the PayPal catalog product: config → name match → create ---
306
+ let paypalId = product.paypal?.productId || null;
307
+ let paypalProduct = null;
308
+
309
+ if (!paypalId) {
310
+ // Self-heal: the catalog list is summarized, so a name match still
311
+ // needs a full fetch before diffing
312
+ catalog = catalog || await api.listProducts();
313
+ const match = catalog.find((p) => p.name === desired.name);
314
+ if (match) {
315
+ paypalId = match.id;
316
+ console.log(` ${chalk.green('✓')} Matched existing product by name: ${chalk.dim(match.id)}`);
317
+ }
318
+ }
319
+
320
+ if (paypalId) {
321
+ paypalProduct = await api.getProduct(paypalId);
322
+ console.log(` ${chalk.green('✓')} Product exists: ${chalk.dim(paypalProduct.id)}`);
323
+
324
+ const patches = getProductUpdates(paypalProduct, desired);
325
+ if (patches) {
326
+ const fields = patches.map((p) => p.path.slice(1)).join(', ');
327
+ if (dryRun) {
328
+ console.log(` ${chalk.cyan('~')} Would update: ${chalk.dim(fields)} ${chalk.yellow('[DRY RUN]')}`);
329
+ } else {
330
+ await api.updateProduct(paypalProduct.id, patches);
331
+ console.log(` ${chalk.green('✓')} Updated: ${chalk.dim(fields)}`);
332
+ }
333
+ }
334
+ } else {
335
+ if (dryRun) {
336
+ console.log(` ${chalk.cyan('+')} Would create product: ${chalk.dim(desired.name)} ${chalk.yellow('[DRY RUN]')}`);
337
+ // Can't manage plans without a real product ID
338
+ continue;
339
+ }
340
+
341
+ paypalProduct = await api.createProduct({
342
+ name: desired.name,
343
+ description: desired.description,
344
+ type: product.type === 'subscription' ? 'SERVICE' : 'DIGITAL',
345
+ imageUrl: desired.imageUrl,
346
+ homeUrl: desired.homeUrl,
347
+ });
348
+ console.log(` ${chalk.green('✓')} Created product: ${chalk.cyan(paypalProduct.id)}`);
349
+ }
350
+
351
+ if (paypalProduct.id !== product.paypal?.productId) {
352
+ configEdits[`payment.products[id=${product.id}].paypal.productId`] = paypalProduct.id;
353
+ }
354
+
355
+ // --- Ensure billing plans match config (subscriptions only) ---
356
+ if (product.type === 'subscription') {
357
+ await ensurePlans(api, paypalProduct.id, product, brandConfig, dryRun);
358
+ } else {
359
+ console.log(` ${chalk.dim('⊘ One-time — no plans needed (Orders API)')}`);
360
+ }
361
+ }
362
+
363
+ if (Object.keys(configEdits).length > 0) {
364
+ writeBrandConfig(context, configEdits);
365
+ }
366
+
367
+ // --- Deactivate plans on legacy products ---
368
+ await deactivateLegacyPlans(api, products, dryRun);
369
+
370
+ // --- The fixtures exist; a sandbox drive still needs a buyer ---
371
+ remindSandboxBuyer(api);
372
+
373
+ return { output: { paypalSync: { productsProcessed: products.length } } };
374
+ };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Ensure a PayPal 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), diffs event_types, PATCHes when drifted, creates when missing.
7
+ * Skipped for shared Firebase projects (no api.{domain} backend to receive
8
+ * events). Requires OMEGA_WEBHOOK_KEY.
9
+ */
10
+ const chalk = require('chalk').default;
11
+ const { buildWebhookUrl, diffEventSets, redactWebhookUrl, staleWebhookEndpoints } = require('../lib/payment-utils.js');
12
+
13
+ // Events the backend handles for PayPal subscription/payment processing
14
+ const ENABLED_EVENTS = [
15
+ // Subscriptions
16
+ 'BILLING.SUBSCRIPTION.CREATED',
17
+ 'BILLING.SUBSCRIPTION.ACTIVATED',
18
+ 'BILLING.SUBSCRIPTION.UPDATED',
19
+ 'BILLING.SUBSCRIPTION.CANCELLED',
20
+ 'BILLING.SUBSCRIPTION.SUSPENDED',
21
+ 'BILLING.SUBSCRIPTION.EXPIRED',
22
+ 'BILLING.SUBSCRIPTION.PAYMENT.FAILED',
23
+ 'BILLING.SUBSCRIPTION.RE-ACTIVATED',
24
+
25
+ // One-time purchases (Orders/Checkout)
26
+ 'CHECKOUT.ORDER.COMPLETED',
27
+ 'CHECKOUT.ORDER.APPROVED',
28
+
29
+ // Payment captures and sales
30
+ 'PAYMENT.SALE.COMPLETED',
31
+ 'PAYMENT.SALE.DENIED',
32
+ 'PAYMENT.SALE.REFUNDED',
33
+ 'PAYMENT.CAPTURE.COMPLETED',
34
+ 'PAYMENT.CAPTURE.DENIED',
35
+ 'PAYMENT.CAPTURE.REFUNDED',
36
+
37
+ // Invoices
38
+ 'INVOICING.INVOICE.CREATED',
39
+ 'INVOICING.INVOICE.PAID',
40
+ 'INVOICING.INVOICE.CANCELLED',
41
+ ];
42
+
43
+ module.exports = async function ensurePayPalWebhook(context) {
44
+ const { brandConfig, paypalApi: api, options } = context;
45
+ const dryRun = options?.dryRun || false;
46
+
47
+ if (!api) {
48
+ console.log(` ${chalk.dim('⊘ PayPal not configured')}`);
49
+ return {};
50
+ }
51
+
52
+ // Shared Firebase project — no backend deploys from this brand, so api.{domain} doesn't exist
53
+ if (brandConfig.cloud?.shared === true) {
54
+ console.log(` ${chalk.dim('⊘ Shared Firebase project — no brand API for webhooks')}`);
55
+ return {};
56
+ }
57
+
58
+ if (!process.env.OMEGA_WEBHOOK_KEY) {
59
+ // Unreachable by design (#635): the key is OMEGA's own (`generated:` in
60
+ // the env schema) and the payment service's setup mints it through the
61
+ // shared contract before any operation runs. Absent here = broken wiring.
62
+ throw new Error('OMEGA_WEBHOOK_KEY absent after the payment setup ran — its REQUIRES entry or its requestServiceInput call is missing');
63
+ }
64
+
65
+ const desiredUrl = buildWebhookUrl(brandConfig, 'paypal');
66
+
67
+ const { webhooks = [] } = await api.listWebhooks();
68
+
69
+ // Exactly ONE endpoint on the brand's API host: the desired URL. Legacy twins
70
+ // there receive every event and answer 400 forever (#570).
71
+ for (const stale of staleWebhookEndpoints(webhooks, desiredUrl)) {
72
+ if (dryRun) {
73
+ console.log(` ${chalk.cyan('-')} Would delete stale webhook ${chalk.dim(redactWebhookUrl(stale.url))} ${chalk.yellow('[DRY RUN]')}`);
74
+ continue;
75
+ }
76
+
77
+ await api.deleteWebhook(stale.id);
78
+ console.log(` ${chalk.yellow('⚠')} Deleted stale webhook ${chalk.dim(`(${stale.id})`)} ${redactWebhookUrl(stale.url)}`);
79
+ }
80
+
81
+ const existing = webhooks.find((wh) => wh.url === desiredUrl);
82
+
83
+ if (existing) {
84
+ const currentEvents = (existing.event_types || []).map((e) => e.name || e);
85
+ const diff = diffEventSets(currentEvents, ENABLED_EVENTS);
86
+
87
+ if (!diff) {
88
+ console.log(` ${chalk.green('✓')} Webhook up to date ${chalk.dim(`(${existing.id})`)}`);
89
+ return { output: { paypalWebhook: { id: existing.id, updated: false } } };
90
+ }
91
+
92
+ if (diff.missing.length > 0) {
93
+ console.log(` Adding events: ${chalk.cyan(diff.missing.join(', '))}`);
94
+ }
95
+ if (diff.extra.length > 0) {
96
+ console.log(` Removing events: ${chalk.dim(diff.extra.join(', '))}`);
97
+ }
98
+
99
+ if (dryRun) {
100
+ console.log(` ${chalk.cyan('~')} Would update webhook events ${chalk.yellow('[DRY RUN]')}`);
101
+ return { output: { paypalWebhook: { id: existing.id, planned: 'update' } } };
102
+ }
103
+
104
+ // PayPal webhook update uses JSON Patch to replace event_types
105
+ await api.updateWebhook(existing.id, [
106
+ {
107
+ op: 'replace',
108
+ path: '/event_types',
109
+ value: ENABLED_EVENTS.map((name) => ({ name })),
110
+ },
111
+ ]);
112
+ console.log(` ${chalk.green('✓')} Webhook events updated ${chalk.dim(`(${existing.id})`)}`);
113
+
114
+ return { output: { paypalWebhook: { id: existing.id, updated: true } } };
115
+ }
116
+
117
+ if (dryRun) {
118
+ console.log(` ${chalk.cyan('+')} Would create webhook ${chalk.yellow('[DRY RUN]')}`);
119
+ console.log(` ${chalk.dim(redactWebhookUrl(desiredUrl))}`);
120
+ return { output: { paypalWebhook: { planned: 'create' } } };
121
+ }
122
+
123
+ const webhook = await api.createWebhook(desiredUrl, ENABLED_EVENTS);
124
+ console.log(` ${chalk.green('✓')} Webhook created: ${chalk.dim(webhook.id)}`);
125
+ console.log(` ${chalk.dim(redactWebhookUrl(desiredUrl))}`);
126
+
127
+ return { output: { paypalWebhook: { id: webhook.id, created: true } } };
128
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Ensure the Stripe account's business profile matches brand config
3
+ * (name, url, support email, support url). Diff-then-update; account
4
+ * updates aren't supported on every account type (org sub-accounts), so an
5
+ * update failure warns instead of erroring. Disable entirely with
6
+ * payment.providers.stripe.updateAccountInfo = false.
7
+ */
8
+ const chalk = require('chalk').default;
9
+
10
+ /**
11
+ * Build desired account settings from brand config
12
+ */
13
+ function buildDesiredAccount(brandConfig) {
14
+ const domain = brandConfig.brand.url.replace(/^https?:\/\//, '');
15
+
16
+ return {
17
+ business_profile: {
18
+ name: brandConfig.brand.name,
19
+ url: brandConfig.brand.url,
20
+ support_email: brandConfig.brand.contact?.email || `support@${domain}`,
21
+ support_url: brandConfig.brand.url,
22
+ },
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Diff current account against desired and return updates
28
+ */
29
+ function getAccountUpdates(account, desired) {
30
+ const bp = account.business_profile || {};
31
+ const desiredBp = desired.business_profile;
32
+
33
+ const bpUpdates = {};
34
+ for (const key of ['name', 'url', 'support_email', 'support_url']) {
35
+ if (bp[key] !== desiredBp[key]) {
36
+ bpUpdates[key] = desiredBp[key];
37
+ }
38
+ }
39
+
40
+ return Object.keys(bpUpdates).length > 0 ? { business_profile: bpUpdates } : null;
41
+ }
42
+
43
+ module.exports = async function ensureStripeAccount(context) {
44
+ const { brandConfig, stripeApi: api, options } = context;
45
+ const dryRun = options?.dryRun || false;
46
+
47
+ if (!api) {
48
+ console.log(` ${chalk.dim('⊘ Stripe not configured')}`);
49
+ return {};
50
+ }
51
+
52
+ if (brandConfig.payment?.providers?.stripe?.updateAccountInfo === false) {
53
+ console.log(` ${chalk.dim('⊘ Account updates disabled (updateAccountInfo = false)')}`);
54
+ return {};
55
+ }
56
+
57
+ let account;
58
+ try {
59
+ account = await api.getAccount();
60
+ } catch (error) {
61
+ console.log(` ${chalk.yellow('⚠')} Could not read account${chalk.dim(`: ${error.message}`)}`);
62
+ return { status: 'warned', reason: 'could not read the Stripe account', output: { stripeAccount: { error: error.message } } };
63
+ }
64
+
65
+ const desired = buildDesiredAccount(brandConfig);
66
+ const updates = getAccountUpdates(account, desired);
67
+
68
+ if (!updates) {
69
+ console.log(` ${chalk.green('✓')} Account settings up to date`);
70
+ return { output: { stripeAccount: { updated: false } } };
71
+ }
72
+
73
+ // Log what's changing
74
+ for (const [key, value] of Object.entries(updates.business_profile)) {
75
+ const current = account.business_profile?.[key] || '(none)';
76
+ console.log(` ${key}: "${current}" ${chalk.dim('→')} "${chalk.cyan(value)}"`);
77
+ }
78
+
79
+ if (dryRun) {
80
+ console.log(` ${chalk.cyan('~')} Would update account settings ${chalk.yellow('[DRY RUN]')}`);
81
+ return { output: { stripeAccount: { planned: 'update' } } };
82
+ }
83
+
84
+ try {
85
+ await api.updateAccount(account.id, updates);
86
+ console.log(` ${chalk.green('✓')} Account settings updated`);
87
+ return { output: { stripeAccount: { updated: true } } };
88
+ } catch (error) {
89
+ // Account updates may not be supported for this account type
90
+ console.log(` ${chalk.yellow('⚠')} Could not update account${chalk.dim(`: ${error.message}`)}`);
91
+ return { status: 'warned', reason: 'could not update the Stripe account settings', output: { stripeAccount: { error: error.message } } };
92
+ }
93
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Report Stripe Enhanced Dispute Protection status.
3
+ *
4
+ * The Stripe API has no dispute-protection management (Dashboard-only), so
5
+ * this follows the no-API manual-action pattern: print the settings
6
+ * deep-link, confirm interactively, and stay warned until activation is
7
+ * 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.disputesConfirmed`. Cannot mutate by
10
+ * construction.
11
+ */
12
+ const chalk = require('chalk').default;
13
+ const { confirm, pressEnterToOpen } = require('../../../vendor/devkit/prompt.js');
14
+ const { writeBrandConfig } = require('../../../lib/config-write.js');
15
+ const { canPrompt } = require('../../../lib/run-gates.js');
16
+
17
+ const CONFIG_PATH = 'payment.providers.stripe.disputesConfirmed';
18
+
19
+ module.exports = async function ensureStripeDisputes(context) {
20
+ const { brandConfig, stripeApi: api, serviceData, options = {} } = context;
21
+
22
+ if (!api) {
23
+ console.log(` ${chalk.dim('⊘ Stripe not configured')}`);
24
+ return {};
25
+ }
26
+
27
+ if (brandConfig.payment?.providers?.stripe?.disputesConfirmed === true) {
28
+ console.log(` ${chalk.green('✓')} Enhanced Dispute Protection confirmed`);
29
+ return {};
30
+ }
31
+
32
+ const disputesUrl = serviceData.stripeAccountId
33
+ ? `https://dashboard.stripe.com/${serviceData.stripeAccountId}/settings/disputes`
34
+ : 'https://dashboard.stripe.com/settings/disputes';
35
+
36
+ console.log(` ${chalk.yellow('⚠')} No API for dispute protection — activate ${chalk.bold('Enhanced Dispute Protection')} in the Dashboard`);
37
+
38
+ if (canPrompt(options)) {
39
+ await pressEnterToOpen(disputesUrl, 'the Stripe dispute settings');
40
+ const done = await confirm({ message: 'Enhanced Dispute Protection activated in the Dashboard?', default: false });
41
+ if (done) {
42
+ writeBrandConfig(context, { [CONFIG_PATH]: true });
43
+ console.log(` ${chalk.green('✓')} Enhanced Dispute Protection confirmed`);
44
+ return {};
45
+ }
46
+ } else {
47
+ console.log(` ${chalk.dim('→')} Dispute settings: ${chalk.cyan(disputesUrl)}`);
48
+ console.log(` ${chalk.dim('→')} (rerun in an interactive terminal to confirm)`);
49
+ }
50
+
51
+ return {
52
+ status: 'warned',
53
+ reason: 'Enhanced Dispute Protection not confirmed — activate it in the Stripe Dashboard',
54
+ output: { stripeDisputes: { disputesUrl } },
55
+ };
56
+ };