@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,98 @@
1
+ /**
2
+ * Ensure configuration rules match `edge.providers.cloudflare.rules.configuration`.
3
+ *
4
+ * 1. Reads the http_config_settings entrypoint ruleset (or notes it needs creating).
5
+ * 2. Diffs each configured rule by description — creates missing, updates stale, removes unconfigured.
6
+ * 3. POSTs a new ruleset if missing; PUTs the merged ruleset if updating.
7
+ */
8
+ const chalk = require('chalk').default;
9
+ const { cacheRead } = require('../lib/read-cache.js');
10
+ const { fetchRuleset, applyRuleset, getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
11
+
12
+ module.exports = async function ensureRulesConfiguration(context) {
13
+ const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
14
+ const zoneId = getZoneId(context);
15
+ const gated = zoneGate(context, 'rulesConfiguration');
16
+ if (gated) return gated;
17
+
18
+ // === READ ===
19
+ const { ruleset, needsCreate } = await fetchRuleset(api, zoneId, 'http_config_settings');
20
+ const readCount = ruleset?.rules?.length || 0;
21
+ console.log(` ${chalk.green('✓')} Read ${chalk.dim(`(${readCount} items)`)}`);
22
+ cacheRead(brandRoot, 'rules-configuration', { count: readCount, ruleset, needsCreate });
23
+
24
+ // === DIFF ===
25
+ const configRulesConfig = brandConfig?.edge?.providers?.cloudflare?.rules?.configuration;
26
+ if (!configRulesConfig || !Array.isArray(configRulesConfig) || configRulesConfig.length === 0) {
27
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
28
+ return;
29
+ }
30
+
31
+ const modifiedRules = ruleset?.rules ? [...ruleset.rules] : [];
32
+ let hasChanges = false;
33
+
34
+ for (const configRule of configRulesConfig) {
35
+ const ruleName = configRule.name;
36
+ const ruleIndex = modifiedRules.findIndex((rule) => rule.description === ruleName);
37
+ const currentRule = ruleIndex >= 0 ? modifiedRules[ruleIndex] : null;
38
+
39
+ const expectedRule = {
40
+ ...(currentRule || {}),
41
+ action: 'set_config',
42
+ action_parameters: configRule.settings || {},
43
+ expression: configRule.expression || currentRule?.expression,
44
+ description: ruleName,
45
+ enabled: configRule.enabled !== undefined ? configRule.enabled : (currentRule?.enabled ?? true),
46
+ };
47
+
48
+ if (!currentRule) {
49
+ console.log(` ${chalk.dim('·')} create ${chalk.cyan(`"${ruleName}"`)}`);
50
+ modifiedRules.push(expectedRule);
51
+ hasChanges = true;
52
+ continue;
53
+ }
54
+
55
+ const currentParams = JSON.stringify(currentRule.action_parameters || {});
56
+ const needsUpdate =
57
+ currentParams !== JSON.stringify(expectedRule.action_parameters)
58
+ || currentRule.expression !== expectedRule.expression
59
+ || currentRule.enabled !== expectedRule.enabled;
60
+
61
+ if (needsUpdate) {
62
+ console.log(` ${chalk.dim('·')} update ${chalk.cyan(`"${ruleName}"`)}`);
63
+ modifiedRules[ruleIndex] = expectedRule;
64
+ hasChanges = true;
65
+ }
66
+ }
67
+
68
+ // Remove stale rules (in Cloudflare but not in config)
69
+ const configNames = new Set(configRulesConfig.map((r) => r.name));
70
+ const staleIndices = [];
71
+ for (let i = modifiedRules.length - 1; i >= 0; i--) {
72
+ if (modifiedRules[i].description && !configNames.has(modifiedRules[i].description)) {
73
+ console.log(` ${chalk.dim('·')} remove ${chalk.cyan(`"${modifiedRules[i].description}"`)}`);
74
+ staleIndices.push(i);
75
+ hasChanges = true;
76
+ }
77
+ }
78
+ for (const i of staleIndices) modifiedRules.splice(i, 1);
79
+
80
+ if (!hasChanges && !needsCreate) {
81
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
82
+ return;
83
+ }
84
+
85
+ // === WRITE ===
86
+ const output = await applyRuleset(api, zoneId, {
87
+ ruleset,
88
+ needsCreate,
89
+ rules: modifiedRules,
90
+ phase: 'http_config_settings',
91
+ name: 'Configuration Rules',
92
+ description: 'Configuration rules managed by Omega Manager',
93
+ label: 'Configuration rules',
94
+ dryRun: options.dryRun,
95
+ });
96
+
97
+ return { output: { configuration: output } };
98
+ };
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Ensure Managed Transforms match `edge.providers.cloudflare.rules.managedTransforms`.
3
+ *
4
+ * 1. Reads the current managed_request_headers + managed_response_headers state.
5
+ * 2. Diffs against the config (enable/disable each Cloudflare-named transform).
6
+ * 3. PATCHes the combined updated lists in one call.
7
+ */
8
+ const chalk = require('chalk').default;
9
+ const { cacheRead } = require('../lib/read-cache.js');
10
+ const { getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
11
+ const { dryRunPlan } = require('../../../lib/run-gates.js');
12
+
13
+ const REQUEST_HEADERS_MAP = {
14
+ addClientCertificateHeaders: 'add_client_certificate_headers',
15
+ addVisitorLocationHeaders: 'add_visitor_location_headers',
16
+ removeVisitorIpHeaders: 'remove_visitor_ip_headers',
17
+ addWafCredentialCheckStatusHeader: 'add_waf_credential_check_status_header',
18
+ };
19
+
20
+ const RESPONSE_HEADERS_MAP = {
21
+ removeXPoweredByHeader: 'remove_x-powered-by_header',
22
+ addSecurityHeaders: 'add_security_headers',
23
+ };
24
+
25
+ module.exports = async function ensureManagedTransforms(context) {
26
+ const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
27
+ const zoneId = getZoneId(context);
28
+ const gated = zoneGate(context, 'rulesManagedTransforms');
29
+ if (gated) return gated;
30
+
31
+ // === READ ===
32
+ const data = await api.makeRequest(`/zones/${zoneId}/managed_headers`);
33
+ const managedRequestHeaders = data.result.managed_request_headers || [];
34
+ const managedResponseHeaders = data.result.managed_response_headers || [];
35
+
36
+ console.log(` ${chalk.green('✓')} Read`);
37
+ cacheRead(brandRoot, 'rules-managed-transforms', { managedRequestHeaders, managedResponseHeaders });
38
+
39
+ // === DIFF ===
40
+ const managedTransformsConfig = brandConfig?.edge?.providers?.cloudflare?.rules?.managedTransforms;
41
+ if (!managedTransformsConfig) {
42
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
43
+ return;
44
+ }
45
+
46
+ let hasChanges = false;
47
+ const updatedRequestHeaders = [...managedRequestHeaders];
48
+ const updatedResponseHeaders = [...managedResponseHeaders];
49
+
50
+ const applyDiff = (configSection, map, headersList) => {
51
+ if (!configSection) return;
52
+ for (const [configKey, apiId] of Object.entries(map)) {
53
+ const targetEnabled = configSection[configKey];
54
+ if (targetEnabled === null || targetEnabled === undefined) continue;
55
+
56
+ const headerIndex = headersList.findIndex((h) => h.id === apiId);
57
+ const currentEnabled = headerIndex >= 0 ? headersList[headerIndex].enabled : false;
58
+
59
+ if (currentEnabled !== targetEnabled) {
60
+ console.log(` ${chalk.dim('·')} ${apiId}: ${chalk.dim(currentEnabled)} => ${chalk.cyan(targetEnabled)}`);
61
+ hasChanges = true;
62
+ if (headerIndex >= 0) {
63
+ headersList[headerIndex] = { ...headersList[headerIndex], enabled: targetEnabled };
64
+ } else {
65
+ headersList.push({ id: apiId, enabled: targetEnabled });
66
+ }
67
+ }
68
+ }
69
+ };
70
+
71
+ applyDiff(managedTransformsConfig.request, REQUEST_HEADERS_MAP, updatedRequestHeaders);
72
+ applyDiff(managedTransformsConfig.response, RESPONSE_HEADERS_MAP, updatedResponseHeaders);
73
+
74
+ if (!hasChanges) {
75
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
76
+ return;
77
+ }
78
+
79
+ if (options.dryRun) {
80
+ return dryRunPlan('update Managed Transforms', { status: 'success', output: { managedTransforms: { planned: true } } });
81
+ }
82
+
83
+ // === WRITE ===
84
+ const output = { updated: false };
85
+ try {
86
+ await api.makeRequest(`/zones/${zoneId}/managed_headers`, {
87
+ method: 'PATCH',
88
+ body: JSON.stringify({
89
+ managed_request_headers: updatedRequestHeaders,
90
+ managed_response_headers: updatedResponseHeaders,
91
+ }),
92
+ });
93
+ output.updated = true;
94
+ console.log(` ${chalk.green('✓')} Managed Transforms updated`);
95
+ } catch (error) {
96
+ output.error = error.message;
97
+ console.error(` ${chalk.red('✗')} Managed Transforms failed${chalk.dim(`: ${error.message}`)}`);
98
+ }
99
+
100
+ return { output: { managedTransforms: output } };
101
+ };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Ensure redirect rules match `edge.providers.cloudflare.rules.redirect`.
3
+ *
4
+ * 1. Reads the http_request_dynamic_redirect entrypoint ruleset (or notes it needs creating).
5
+ * 2. Diffs each configured rule by description — creates missing, updates stale, removes unconfigured.
6
+ * 3. POSTs a new ruleset if missing; PUTs the merged ruleset if updating.
7
+ */
8
+ const chalk = require('chalk').default;
9
+ const { cacheRead } = require('../lib/read-cache.js');
10
+ const { fetchRuleset, applyRuleset, getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
11
+
12
+ module.exports = async function ensureRulesRedirect(context) {
13
+ const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
14
+ const zoneId = getZoneId(context);
15
+ const gated = zoneGate(context, 'rulesRedirect');
16
+ if (gated) return gated;
17
+
18
+ // === READ ===
19
+ const { ruleset, needsCreate } = await fetchRuleset(api, zoneId, 'http_request_dynamic_redirect');
20
+ const readCount = ruleset?.rules?.length || 0;
21
+ console.log(` ${chalk.green('✓')} Read ${chalk.dim(`(${readCount} items)`)}`);
22
+ cacheRead(brandRoot, 'rules-redirect', { count: readCount, ruleset, needsCreate });
23
+
24
+ // === DIFF ===
25
+ const redirectRulesConfig = brandConfig?.edge?.providers?.cloudflare?.rules?.redirect;
26
+ if (!redirectRulesConfig || !Array.isArray(redirectRulesConfig) || redirectRulesConfig.length === 0) {
27
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
28
+ return;
29
+ }
30
+
31
+ const modifiedRules = ruleset?.rules ? [...ruleset.rules] : [];
32
+ let hasChanges = false;
33
+
34
+ for (const configRule of redirectRulesConfig) {
35
+ const ruleName = configRule.name;
36
+ const ruleIndex = modifiedRules.findIndex((rule) => rule.description === ruleName);
37
+ const currentRule = ruleIndex >= 0 ? modifiedRules[ruleIndex] : null;
38
+
39
+ const expectedRule = {
40
+ ...(currentRule || {}),
41
+ action: 'redirect',
42
+ action_parameters: {
43
+ from_value: {
44
+ status_code: configRule.statusCode || 301,
45
+ preserve_query_string: configRule.preserveQueryString !== undefined ? configRule.preserveQueryString : true,
46
+ target_url: configRule.targetUrl || currentRule?.action_parameters?.from_value?.target_url,
47
+ },
48
+ },
49
+ expression: configRule.expression || currentRule?.expression,
50
+ description: ruleName,
51
+ enabled: configRule.enabled !== undefined ? configRule.enabled : (currentRule?.enabled ?? true),
52
+ };
53
+
54
+ if (!currentRule) {
55
+ console.log(` ${chalk.dim('·')} create ${chalk.cyan(`"${ruleName}"`)}`);
56
+ modifiedRules.push(expectedRule);
57
+ hasChanges = true;
58
+ continue;
59
+ }
60
+
61
+ const currentParams = currentRule.action_parameters?.from_value || {};
62
+ const expectedParams = expectedRule.action_parameters.from_value;
63
+ const needsUpdate =
64
+ currentParams.status_code !== expectedParams.status_code
65
+ || currentParams.preserve_query_string !== expectedParams.preserve_query_string
66
+ || JSON.stringify(currentParams.target_url) !== JSON.stringify(expectedParams.target_url)
67
+ || currentRule.expression !== expectedRule.expression
68
+ || currentRule.enabled !== expectedRule.enabled;
69
+
70
+ if (needsUpdate) {
71
+ console.log(` ${chalk.dim('·')} update ${chalk.cyan(`"${ruleName}"`)}`);
72
+ modifiedRules[ruleIndex] = expectedRule;
73
+ hasChanges = true;
74
+ }
75
+ }
76
+
77
+ // Remove stale rules (in Cloudflare but not in config)
78
+ const configNames = new Set(redirectRulesConfig.map((r) => r.name));
79
+ const staleIndices = [];
80
+ for (let i = modifiedRules.length - 1; i >= 0; i--) {
81
+ if (modifiedRules[i].description && !configNames.has(modifiedRules[i].description)) {
82
+ console.log(` ${chalk.dim('·')} remove ${chalk.cyan(`"${modifiedRules[i].description}"`)}`);
83
+ staleIndices.push(i);
84
+ hasChanges = true;
85
+ }
86
+ }
87
+ for (const i of staleIndices) modifiedRules.splice(i, 1);
88
+
89
+ if (!hasChanges && !needsCreate) {
90
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
91
+ return;
92
+ }
93
+
94
+ // === WRITE ===
95
+ const output = await applyRuleset(api, zoneId, {
96
+ ruleset,
97
+ needsCreate,
98
+ rules: modifiedRules,
99
+ phase: 'http_request_dynamic_redirect',
100
+ name: 'Redirect Rules',
101
+ description: 'Redirect rules managed by Omega Manager',
102
+ label: 'Redirect rules',
103
+ dryRun: options.dryRun,
104
+ });
105
+
106
+ return { output: { redirect: output } };
107
+ };
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Ensure response header transform rules match `edge.providers.cloudflare.rules.responseHeaders`.
3
+ *
4
+ * Different from other ruleset operations: always uses PUT, but targets either
5
+ * the existing ruleset's ID or the phase's entrypoint URL when it doesn't yet
6
+ * exist.
7
+ */
8
+ const chalk = require('chalk').default;
9
+ const { cacheRead } = require('../lib/read-cache.js');
10
+ const { fetchRuleset, getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
11
+ const { dryRunPlan } = require('../../../lib/run-gates.js');
12
+
13
+ const PHASE = 'http_response_headers_transform';
14
+
15
+ module.exports = async function ensureRulesResponseHeaders(context) {
16
+ const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
17
+ const zoneId = getZoneId(context);
18
+ const gated = zoneGate(context, 'rulesResponseHeaders');
19
+ if (gated) return gated;
20
+
21
+ // === READ ===
22
+ const { ruleset } = await fetchRuleset(api, zoneId, PHASE);
23
+ const readCount = ruleset?.rules?.length || 0;
24
+ console.log(` ${chalk.green('✓')} Read`);
25
+ cacheRead(brandRoot, 'rules-response-headers', {
26
+ responseTransform: { count: readCount, ruleset },
27
+ });
28
+
29
+ // === DIFF ===
30
+ const responseHeadersConfig = brandConfig?.edge?.providers?.cloudflare?.rules?.responseHeaders;
31
+ if (!responseHeadersConfig || responseHeadersConfig.length === 0) {
32
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
33
+ return;
34
+ }
35
+
36
+ const existingRules = ruleset?.rules || [];
37
+ const configuredRules = responseHeadersConfig.map((rule) => ({
38
+ description: rule.name,
39
+ expression: rule.expression,
40
+ action: 'rewrite',
41
+ action_parameters: {
42
+ headers: Object.fromEntries(
43
+ Object.entries(rule.headers).map(([name, value]) => [name, { operation: 'set', value }]),
44
+ ),
45
+ },
46
+ enabled: rule.enabled,
47
+ }));
48
+
49
+ // Compare ignoring Cloudflare metadata (id, ref, version, last_updated)
50
+ const normalize = (rule) => ({
51
+ description: rule.description,
52
+ expression: rule.expression,
53
+ action: rule.action,
54
+ action_parameters: rule.action_parameters,
55
+ enabled: rule.enabled,
56
+ });
57
+ const rulesChanged = JSON.stringify(existingRules.map(normalize)) !== JSON.stringify(configuredRules.map(normalize));
58
+
59
+ if (!rulesChanged) {
60
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
61
+ return;
62
+ }
63
+
64
+ console.log(` ${chalk.dim('·')} ${chalk.bold(configuredRules.length)} rules`);
65
+
66
+ if (options.dryRun) {
67
+ return dryRunPlan('update response header rules', { status: 'success', output: { responseHeaders: { planned: configuredRules.length } } });
68
+ }
69
+
70
+ // === WRITE ===
71
+ const output = { updated: false };
72
+ try {
73
+ const endpoint = ruleset?.id
74
+ ? `/zones/${zoneId}/rulesets/${ruleset.id}`
75
+ : `/zones/${zoneId}/rulesets/phases/${PHASE}/entrypoint`;
76
+
77
+ const body = ruleset?.id
78
+ ? { rules: configuredRules, phase: PHASE }
79
+ : { rules: configuredRules };
80
+
81
+ await api.makeRequest(endpoint, { method: 'PUT', body: JSON.stringify(body) });
82
+ output.updated = true;
83
+ console.log(` ${chalk.green('✓')} Response header transform rules updated`);
84
+ } catch (error) {
85
+ output.error = error.message;
86
+ console.error(` ${chalk.red('✗')} Response header transform rules failed${chalk.dim(`: ${error.message}`)}`);
87
+ }
88
+
89
+ return { output: { responseHeaders: output } };
90
+ };
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Ensure custom firewall rules match `edge.providers.cloudflare.rules.security`.
3
+ *
4
+ * 1. Reads the http_request_firewall_custom entrypoint ruleset (and managed for cache only).
5
+ * 2. Diffs each configured rule by description (name) — creates/updates `skip`-style rules.
6
+ * 3. PUTs the merged ruleset, or PUT to the phase entrypoint if it doesn't exist yet.
7
+ */
8
+ const chalk = require('chalk').default;
9
+ const { cacheRead } = require('../lib/read-cache.js');
10
+ const { fetchRuleset, getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
11
+ const { dryRunPlan } = require('../../../lib/run-gates.js');
12
+
13
+ const PHASE = 'http_request_firewall_custom';
14
+ const MANAGED_PHASE = 'http_request_firewall_managed';
15
+
16
+ module.exports = async function ensureRulesSecurity(context) {
17
+ const { cloudflareApi: api, brandRoot, brandConfig, options = {} } = context;
18
+ const zoneId = getZoneId(context);
19
+ const gated = zoneGate(context, 'rulesSecurity');
20
+ if (gated) return gated;
21
+
22
+ // === READ ===
23
+ const { ruleset: customRuleset } = await fetchRuleset(api, zoneId, PHASE);
24
+ const { ruleset: managedRuleset } = await fetchRuleset(api, zoneId, MANAGED_PHASE);
25
+ console.log(` ${chalk.green('✓')} Read`);
26
+ cacheRead(brandRoot, 'rules-security', {
27
+ customRules: { count: customRuleset?.rules?.length || 0, ruleset: customRuleset },
28
+ managedRules: { count: managedRuleset?.rules?.length || 0, ruleset: managedRuleset },
29
+ });
30
+
31
+ // === DIFF ===
32
+ const securityRulesConfig = brandConfig?.edge?.providers?.cloudflare?.rules?.security;
33
+ if (!securityRulesConfig || !Array.isArray(securityRulesConfig) || securityRulesConfig.length === 0) {
34
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
35
+ return;
36
+ }
37
+
38
+ const modifiedRules = customRuleset?.rules ? [...customRuleset.rules] : [];
39
+ let hasChanges = false;
40
+
41
+ for (const configRule of securityRulesConfig) {
42
+ const ruleName = configRule.name;
43
+ const ruleIndex = modifiedRules.findIndex((rule) => rule.description === ruleName);
44
+ const currentRule = ruleIndex >= 0 ? modifiedRules[ruleIndex] : null;
45
+
46
+ const actionParameters = {
47
+ products: configRule.skipProducts || currentRule?.action_parameters?.products || [],
48
+ };
49
+ if (configRule.skipPhases) {
50
+ actionParameters.phases = configRule.skipPhases;
51
+ } else if (currentRule?.action_parameters?.phases) {
52
+ actionParameters.phases = currentRule.action_parameters.phases;
53
+ }
54
+ if (configRule.skipRuleset) {
55
+ actionParameters.ruleset = configRule.skipRuleset;
56
+ } else if (currentRule?.action_parameters?.ruleset) {
57
+ actionParameters.ruleset = currentRule.action_parameters.ruleset;
58
+ }
59
+
60
+ const expectedRule = {
61
+ ...(currentRule || {}),
62
+ action: configRule.action || 'skip',
63
+ action_parameters: actionParameters,
64
+ expression: configRule.expression || currentRule?.expression,
65
+ description: ruleName,
66
+ enabled: configRule.enabled !== undefined ? configRule.enabled : (currentRule?.enabled ?? true),
67
+ logging: {
68
+ enabled: configRule.logging !== undefined ? configRule.logging : (currentRule?.logging?.enabled ?? false),
69
+ },
70
+ };
71
+
72
+ if (!currentRule) {
73
+ console.log(` ${chalk.dim('·')} create ${chalk.cyan(`"${ruleName}"`)}`);
74
+ modifiedRules.push(expectedRule);
75
+ hasChanges = true;
76
+ continue;
77
+ }
78
+
79
+ const currentProducts = JSON.stringify(currentRule.action_parameters?.products || []);
80
+ const currentPhases = JSON.stringify(currentRule.action_parameters?.phases || []);
81
+ const needsUpdate =
82
+ currentRule.action !== expectedRule.action
83
+ || currentProducts !== JSON.stringify(expectedRule.action_parameters.products)
84
+ || currentPhases !== JSON.stringify(expectedRule.action_parameters.phases || [])
85
+ || currentRule.action_parameters?.ruleset !== expectedRule.action_parameters.ruleset
86
+ || currentRule.expression !== expectedRule.expression
87
+ || currentRule.enabled !== expectedRule.enabled
88
+ || (currentRule.logging?.enabled ?? false) !== expectedRule.logging.enabled;
89
+
90
+ if (needsUpdate) {
91
+ console.log(` ${chalk.dim('·')} update ${chalk.cyan(`"${ruleName}"`)}`);
92
+ modifiedRules[ruleIndex] = expectedRule;
93
+ hasChanges = true;
94
+ }
95
+ }
96
+
97
+ // Remove stale rules (in Cloudflare but not in config)
98
+ const configNames = new Set(securityRulesConfig.map((r) => r.name));
99
+ const staleIndices = [];
100
+ for (let i = modifiedRules.length - 1; i >= 0; i--) {
101
+ if (modifiedRules[i].description && !configNames.has(modifiedRules[i].description)) {
102
+ console.log(` ${chalk.dim('·')} remove ${chalk.cyan(`"${modifiedRules[i].description}"`)}`);
103
+ staleIndices.push(i);
104
+ hasChanges = true;
105
+ }
106
+ }
107
+ for (const i of staleIndices) modifiedRules.splice(i, 1);
108
+
109
+ if (!hasChanges) {
110
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
111
+ return;
112
+ }
113
+
114
+ if (options.dryRun) {
115
+ return dryRunPlan('update security rules', { status: 'success', output: { security: { planned: modifiedRules.length } } });
116
+ }
117
+
118
+ // === WRITE ===
119
+ const output = { updated: false };
120
+ try {
121
+ const description = customRuleset?.description || '';
122
+ if (customRuleset?.id) {
123
+ await api.makeRequest(`/zones/${zoneId}/rulesets/${customRuleset.id}`, {
124
+ method: 'PUT',
125
+ body: JSON.stringify({ rules: modifiedRules, description }),
126
+ });
127
+ console.log(` ${chalk.green('✓')} Security rules updated`);
128
+ } else {
129
+ await api.makeRequest(`/zones/${zoneId}/rulesets/phases/${PHASE}/entrypoint`, {
130
+ method: 'PUT',
131
+ body: JSON.stringify({ rules: modifiedRules, description }),
132
+ });
133
+ console.log(` ${chalk.green('✓')} Security rules created`);
134
+ }
135
+ output.updated = true;
136
+ } catch (error) {
137
+ output.error = error.message;
138
+ console.error(` ${chalk.red('✗')} Security rules failed${chalk.dim(`: ${error.message}`)}`);
139
+ }
140
+
141
+ return { output: { security: output } };
142
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Ensure a Cloudflare Speed Test schedule for the brand homepage matches
3
+ * `edge.providers.cloudflare.speedTest`.
4
+ *
5
+ * 1. Reads all currently-tested pages + their schedules.
6
+ * 2. Locates the homepage schedule (compared loosely — Cloudflare returns mixed URL formats).
7
+ * 3. POSTs the schedule (create-or-replace endpoint).
8
+ */
9
+ const chalk = require('chalk').default;
10
+ const { cacheRead } = require('../lib/read-cache.js');
11
+ const { getZoneId, zoneGate } = require('../lib/ruleset-helper.js');
12
+ const { dryRunPlan } = require('../../../lib/run-gates.js');
13
+
14
+ module.exports = async function ensureSpeedScheduledTests(context) {
15
+ const { cloudflareApi: api, brandRoot, brandConfig, domain, options = {} } = context;
16
+ const zoneId = getZoneId(context);
17
+ const gated = zoneGate(context, 'speedScheduledTests');
18
+ if (gated) return gated;
19
+
20
+ // === READ ===
21
+ // The Speed API rejects zones that aren't active yet (nameservers pending)
22
+ // with 401 speed.errors.zone_not_active — skip until the zone activates
23
+ let pagesData;
24
+ try {
25
+ pagesData = await api.makeRequest(`/zones/${zoneId}/speed_api/pages`);
26
+ } catch (error) {
27
+ if (error.message.includes('zone_not_active')) {
28
+ console.log(` ${chalk.dim('⊘ Zone not active yet (nameservers pending) — skipping until activation')}`);
29
+ return { status: 'skipped' };
30
+ }
31
+ throw error;
32
+ }
33
+ const pages = pagesData.result || [];
34
+
35
+ const schedules = [];
36
+ for (const page of pages) {
37
+ try {
38
+ const scheduleData = await api.makeRequest(`/zones/${zoneId}/speed_api/schedule/${encodeURIComponent(page.url)}`);
39
+ if (scheduleData.result) {
40
+ schedules.push({ url: page.url, schedule: scheduleData.result });
41
+ }
42
+ } catch (error) {
43
+ if (!error.message.includes('404')) {
44
+ console.warn(` ${chalk.yellow('⚠')} Failed to get schedule for ${chalk.cyan(page.url)}${chalk.dim(`: ${error.message}`)}`);
45
+ }
46
+ }
47
+ }
48
+
49
+ console.log(` ${chalk.green('✓')} Read`);
50
+ cacheRead(brandRoot, 'speed-scheduled-tests', { schedules });
51
+
52
+ // === DIFF ===
53
+ const speedTestConfig = brandConfig?.edge?.providers?.cloudflare?.speedTest;
54
+ if (!speedTestConfig) {
55
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
56
+ return;
57
+ }
58
+
59
+ const homepageUrl = `https://${domain}`;
60
+ const normalizeUrl = (url) => url.replace(/^https?:\/\//, '').replace(/\/$/, '');
61
+ const normalizedHomepage = normalizeUrl(homepageUrl);
62
+ const existingSchedule = schedules.find((s) => normalizeUrl(s.url) === normalizedHomepage);
63
+
64
+ const targetFrequency = speedTestConfig.frequency;
65
+ const targetRegion = speedTestConfig.region;
66
+
67
+ if (existingSchedule) {
68
+ const current = existingSchedule.schedule;
69
+ if (current.frequency === targetFrequency && current.region === targetRegion) {
70
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
71
+ return;
72
+ }
73
+ console.log(` ${chalk.dim('·')} update ${chalk.cyan(homepageUrl)}: ${chalk.dim(`${current.frequency}/${current.region}`)} => ${chalk.cyan(`${targetFrequency}/${targetRegion}`)}`);
74
+ } else {
75
+ console.log(` ${chalk.dim('·')} create ${chalk.cyan(homepageUrl)}: ${chalk.cyan(`${targetFrequency} in ${targetRegion}`)}`);
76
+ }
77
+
78
+ if (options.dryRun) {
79
+ return dryRunPlan(`${existingSchedule ? 'update' : 'create'} the speed test schedule`, { status: 'success', output: { speedTest: { planned: existingSchedule ? 'update' : 'create' } } });
80
+ }
81
+
82
+ // === WRITE ===
83
+ const output = { updated: false };
84
+ const action = existingSchedule ? 'updated' : 'created';
85
+ try {
86
+ await api.makeRequest(`/zones/${zoneId}/speed_api/schedule/${encodeURIComponent(homepageUrl)}`, {
87
+ method: 'POST',
88
+ body: JSON.stringify({ frequency: targetFrequency, region: targetRegion }),
89
+ });
90
+ output.updated = true;
91
+ console.log(` ${chalk.green('✓')} Speed Test Schedule ${action}: ${chalk.cyan(homepageUrl)} (${chalk.cyan(`${targetFrequency}, ${targetRegion}`)})`);
92
+ } catch (error) {
93
+ output.error = error.message;
94
+ console.error(` ${chalk.red('✗')} Speed Test Schedule failed for ${chalk.cyan(homepageUrl)}${chalk.dim(`: ${error.message}`)}`);
95
+ }
96
+
97
+ return { output: { speedTest: output } };
98
+ };