@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,78 @@
1
+ /**
2
+ * Captcha service (`captcha.providers.recaptcha`) — validates the brand's
3
+ * OWN classic reCAPTCHA keys (de-ITW: every brand mints its own key in its
4
+ * own GCP project — never a company-shared key; interactive runs ASK for the
5
+ * keys via the cp114 paste flow, pointing at the GCP reCAPTCHA console).
6
+ *
7
+ * Classic reCAPTCHA has no key-management API, so the key's domain list
8
+ * can't be reconciled; this service proves what it can (the secret key
9
+ * in .env is valid, via the documented siteverify endpoint) and prints the
10
+ * console link + domains for the manual half. Interactive runs open the
11
+ * console and confirm the domain list once, stamping config (#434).
12
+ *
13
+ * It also owns the ORPHAN-SECRET warning (#507): the secret in .env without
14
+ * `captcha.providers.recaptcha.siteKey` in config — see the guard below. The
15
+ * reverse direction is the env schema's shared `requiredWhen` rule (#626).
16
+ *
17
+ * Auth: RECAPTCHA_SITE_KEY + RECAPTCHA_SECRET_KEY in the brand .env.
18
+ * Missing keys → interactive runs ask; non-interactive runs skip with guidance.
19
+ */
20
+ const chalk = require('chalk').default;
21
+
22
+ const { serviceInputSpec } = require('../../config.js');
23
+ const { createServiceRunner } = require('../../lib/service-runner.js');
24
+ const { requestServiceInput } = require('../../lib/service-input.js');
25
+ const { RecaptchaAPI } = require('./lib/recaptcha-api.js');
26
+ const { recaptchaConsoleUrl } = require('./lib/console-url.js');
27
+
28
+ module.exports.run = createServiceRunner({
29
+ serviceDir: __dirname,
30
+ setup: async (context) => {
31
+ const recaptchaConfig = context.brandConfig.captcha?.providers?.recaptcha || {};
32
+
33
+ if (recaptchaConfig.enabled === false) {
34
+ return { skip: true, reason: 'captcha.providers.recaptcha.enabled = false' };
35
+ }
36
+
37
+ // The ORPHAN SECRET half (#507): RECAPTCHA_SECRET_KEY set with no
38
+ // captcha.providers.recaptcha.siteKey in config is a silent 403 on every
39
+ // protected POST — the backend enforces verification the moment the secret
40
+ // exists, and the client has no key to mint a token with (the playground's
41
+ // live checkout, 2026-08-22). This service is the ONE place that sees both
42
+ // halves, so it still says this direction out loud.
43
+ //
44
+ // The REVERSE direction — a site key requiring its secret — is the env
45
+ // schema's `requiredWhen` rule now
46
+ // ([#626](https://github.com/Omega-JS-Stack/omega/issues/626)): warned
47
+ // brand-wide by the workspace env-rules op, and refused by a production
48
+ // backend boot. Neither half = the sanctioned unkeyed brand (#17), which
49
+ // stays green and skips below.
50
+ const configuredSiteKey = recaptchaConfig.siteKey || null;
51
+ const configuredSecret = process.env.RECAPTCHA_SECRET_KEY || null;
52
+ if (configuredSecret && !configuredSiteKey) {
53
+ console.log(` ${chalk.yellow('⚠')} RECAPTCHA_SECRET_KEY is set but captcha.providers.recaptcha.siteKey is missing from config — the client sends an empty token and every protected POST 403s. Paste the site key into config/omega.json5: ${recaptchaConsoleUrl(context.brandConfig, configuredSiteKey)}`);
54
+ }
55
+
56
+ const url = (context.brandConfig.brand?.url || '').replace(/^https?:\/\//, '');
57
+ if (!url) {
58
+ return { skip: true, reason: 'no brand.url configured' };
59
+ }
60
+
61
+ // The site key is handler data (console link, domain guidance), not just
62
+ // auth — both keys must come from the .env even when tests inject the api
63
+ // De-ITW: the ask points at the GCP reCAPTCHA console (the brand's OWN
64
+ // project) via the registry entries — the paste flow is the ONLY path to
65
+ // a key; no default value exists anywhere
66
+ const gate = await requestServiceInput(context, serviceInputSpec('captcha'));
67
+ if (gate) return gate;
68
+ const siteKey = process.env.RECAPTCHA_SITE_KEY;
69
+ const secretKey = process.env.RECAPTCHA_SECRET_KEY;
70
+
71
+ // Tests inject a fake client via context.recaptchaApi
72
+ return {
73
+ recaptchaApi: context.recaptchaApi || new RecaptchaAPI({ secretKey }),
74
+ siteKey,
75
+ domain: url,
76
+ };
77
+ },
78
+ });
@@ -0,0 +1,45 @@
1
+ /**
2
+ * The reCAPTCHA console deep-link (#444) — the ONE place the guidance URL is
3
+ * built, shared by the setup's half-key guard (#507) and the site-key ensure
4
+ * handler's printed guidance.
5
+ */
6
+
7
+ /**
8
+ * The project hosting the key. captcha.providers.recaptcha.project only exists
9
+ * to point at a project that ISN'T the brand's own (a key minted elsewhere);
10
+ * with no override the key lives in the brand's cloud project, whose ONE home
11
+ * is cloud.config.projectId (#23) — resolved config already carries the
12
+ * company/brand merge.
13
+ *
14
+ * @param {object} brandConfig - Resolved brand config.
15
+ * @returns {string|null} Project id, or null when no layer names one.
16
+ */
17
+ function resolveProject(brandConfig) {
18
+ return brandConfig.captcha?.providers?.recaptcha?.project
19
+ || brandConfig.cloud?.config?.projectId
20
+ || null;
21
+ }
22
+
23
+ /**
24
+ * Deep-link to the key's settings page.
25
+ *
26
+ * omega-manager hardcoded the company GCP project here; the project resolves
27
+ * from config now, so the deep link builds by default. Only a project unknown
28
+ * at EVERY layer (or an unknown site key) falls back to the classic admin
29
+ * console, which lists your keys (it deep-links by internal numeric ID, not
30
+ * site key, so no key in the URL).
31
+ *
32
+ * @param {object} brandConfig - Resolved brand config.
33
+ * @param {string|null} siteKey - The key to deep-link, when known.
34
+ * @returns {string} Console URL.
35
+ */
36
+ function recaptchaConsoleUrl(brandConfig, siteKey) {
37
+ const project = resolveProject(brandConfig);
38
+
39
+ if (project && siteKey) {
40
+ return `https://console.cloud.google.com/security/recaptcha/${siteKey}/overview?from=keysList&project=${project}`;
41
+ }
42
+ return 'https://www.google.com/recaptcha/admin';
43
+ }
44
+
45
+ module.exports = { recaptchaConsoleUrl };
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Classic reCAPTCHA client. Classic reCAPTCHA has no key-management API —
3
+ * the only documented endpoint is siteverify, which doubles as a secret-key
4
+ * validity check: verifying a throwaway token answers invalid-input-response
5
+ * for a good secret and invalid-input-secret for a bad one.
6
+ */
7
+
8
+ const VERIFY_URL = 'https://www.google.com/recaptcha/api/siteverify';
9
+
10
+ class RecaptchaAPI {
11
+ constructor({ secretKey = process.env.RECAPTCHA_SECRET_KEY } = {}) {
12
+ this.secretKey = secretKey;
13
+ }
14
+
15
+ /**
16
+ * Verify a response token against the secret key.
17
+ *
18
+ * @param {string} response - The reCAPTCHA response token to verify
19
+ * @returns {Promise<Object>} - Parsed siteverify JSON ({ success, 'error-codes' })
20
+ */
21
+ async verify(response) {
22
+ const res = await fetch(VERIFY_URL, {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
25
+ body: new URLSearchParams({ secret: this.secretKey, response }),
26
+ });
27
+
28
+ if (!res.ok) {
29
+ throw new Error(`reCAPTCHA siteverify failed: HTTP ${res.status}`);
30
+ }
31
+
32
+ return await res.json();
33
+ }
34
+ }
35
+
36
+ module.exports = { RecaptchaAPI };
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Sanity checkpoint — the App Store Connect client was built in setup();
3
+ * this prints what's configured so the run log shows which Apple account
4
+ * the downstream operations act on.
5
+ */
6
+ const chalk = require('chalk').default;
7
+ const { catchAgreements } = require('../lib/apple-api.js');
8
+
9
+ module.exports = catchAgreements(async (context) => {
10
+ const { appleClient, appleSecrets } = context;
11
+
12
+ if (!appleClient || !appleSecrets) {
13
+ return { status: 'error', error: 'App Store Connect client not initialized' };
14
+ }
15
+
16
+ console.log(` ${chalk.green('✓')} API client ready`);
17
+ console.log(` ${chalk.dim(`Issuer: ${appleSecrets.issuerId}`)}`);
18
+ console.log(` ${chalk.dim(`Team: ${appleSecrets.teamId}`)}`);
19
+ console.log(` ${chalk.dim(`Key: ${appleSecrets.keyId}`)}`);
20
+
21
+ return {};
22
+ });
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Ensure the brand's bundle ID exists with the required capabilities.
3
+ *
4
+ * Bundle ID = composeBundleId(certificates.providers.apple.bundleIdPrefix, brand.id)
5
+ * — reverse-DNS prefix (config; the onboard wizard derives it from the
6
+ * company/brand domain, and a brand without one is ASKED, #635) + the brand
7
+ * id with hyphens as dots, e.g.
8
+ * com.itwcreativeworks.omega.playground. Platforms derive from the enabled
9
+ * targets (desktop → MACOS, mobile → IOS) and ride the returned state for
10
+ * the profiles handler.
11
+ */
12
+ const chalk = require('chalk').default;
13
+ const { catchAgreements } = require('../lib/apple-api.js');
14
+ const { composeBundleId } = require('../../../lib/bundle-id.js');
15
+ const { resolveConfigValue } = require('../../../lib/config-flow.js');
16
+
17
+ // Where the brand's identifiers live — the page a prefix is read off of
18
+ const APPLE_PORTAL_IDENTIFIERS_URL = 'https://developer.apple.com/account/resources/identifiers/list';
19
+
20
+ const {
21
+ listBundleIds,
22
+ createBundleId,
23
+ findBundleId,
24
+ listBundleIdCapabilities,
25
+ enableCapabilities,
26
+ } = require('../lib/identifier-manager.js');
27
+
28
+ module.exports = catchAgreements(async (context) => {
29
+ const { appleClient, brandConfig, brandId } = context;
30
+ const dryRun = context.options?.dryRun || false;
31
+
32
+ const appleConfig = brandConfig.certificates.providers?.apple || {};
33
+
34
+ // A value the brand must supply is ASKED for, not demanded (#635). The
35
+ // service already gated on its App Store Connect credentials, hence
36
+ // `gate: false`; a skip/disable/headless run steps aside warned, because a
37
+ // brand that never set a prefix is an unconfigured brand, not a broken one.
38
+ const prefix = appleConfig.bundleIdPrefix || await resolveConfigValue(context, {
39
+ path: 'certificates.providers.apple.bundleIdPrefix',
40
+ label: 'Apple bundle id prefix',
41
+ entry: {
42
+ url: APPLE_PORTAL_IDENTIFIERS_URL,
43
+ message: 'Apple bundle id prefix (reverse-DNS, e.g. com.yourcompany):',
44
+ },
45
+ disablePath: 'certificates.enabled',
46
+ gate: false,
47
+ });
48
+
49
+ if (!prefix) {
50
+ console.log(` ${chalk.yellow('⚠')} No ${chalk.cyan('certificates.providers.apple.bundleIdPrefix')} — the bundle ID is <prefix>.<brand.id with dashes as dots>`);
51
+ return { status: 'warned', reason: 'no certificates.providers.apple.bundleIdPrefix', output: { bundleIds: { skipped: 'no bundleIdPrefix' } } };
52
+ }
53
+
54
+ const bundleIdentifier = composeBundleId(prefix, brandId);
55
+ const targets = brandConfig.targets || {};
56
+ const platforms = [
57
+ ...(targets.mobile ? ['IOS'] : []),
58
+ ...(targets.desktop ? ['MACOS'] : []),
59
+ ];
60
+ const capabilities = appleConfig.capabilities || [];
61
+ const brandName = brandConfig.brand?.name || brandId;
62
+
63
+ const existingBundleIds = await listBundleIds(appleClient);
64
+ console.log(` ${chalk.green('✓')} Found ${existingBundleIds.length} existing bundle IDs`);
65
+ console.log(` ${chalk.dim('•')} ${chalk.cyan(bundleIdentifier)} ${chalk.dim(`(${brandName})`)}`);
66
+
67
+ let record = findBundleId(existingBundleIds, bundleIdentifier);
68
+ let output;
69
+
70
+ if (record) {
71
+ console.log(` ${chalk.green('✓')} Bundle ID exists ${chalk.dim(`(${record.attributes.platform})`)}`);
72
+ output = { synced: true };
73
+
74
+ if (capabilities.length > 0) {
75
+ const existingCaps = await listBundleIdCapabilities(appleClient, record.id);
76
+ const existingCapTypes = existingCaps.map((cap) => cap.attributes.capabilityType);
77
+ const missing = capabilities.filter((cap) => !existingCapTypes.includes(cap));
78
+
79
+ if (missing.length > 0) {
80
+ if (dryRun) {
81
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would enable: ${missing.join(', ')}`);
82
+ output = { planned: missing.map((cap) => `enable ${cap}`) };
83
+ } else {
84
+ const results = await enableCapabilities(appleClient, record.id, missing);
85
+ for (const result of results) {
86
+ if (result.success) {
87
+ console.log(` ${chalk.green('✓')} ${result.capability}`);
88
+ } else {
89
+ console.log(` ${chalk.yellow('⚠')} ${result.capability} — ${chalk.gray(result.error)}`);
90
+ }
91
+ }
92
+ output = { capabilitiesAdded: results.filter((r) => r.success).length };
93
+ }
94
+ } else {
95
+ console.log(` ${chalk.dim(`✓ All ${capabilities.length} capability(ies) enabled`)}`);
96
+ }
97
+ }
98
+ } else {
99
+ if (dryRun) {
100
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would create ${bundleIdentifier} with ${capabilities.join(', ') || 'no capabilities'}`);
101
+ return { output: { bundleIds: { planned: [`create ${bundleIdentifier}`] } } };
102
+ }
103
+
104
+ record = await createBundleId(appleClient, bundleIdentifier, brandName, 'IOS', capabilities);
105
+ console.log(` ${chalk.green('✓')} Created bundle ID`);
106
+ output = { created: true };
107
+ }
108
+
109
+ return {
110
+ state: {
111
+ bundleId: { identifier: bundleIdentifier, id: record.id, platforms },
112
+ },
113
+ output: { bundleIds: output },
114
+ };
115
+ });
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Ensure the account's signing certificates exist and are on disk. One set
3
+ * of certificates signs all of an Apple Developer account's apps, so this
4
+ * reconciles the account, not the brand.
5
+ *
6
+ * Per cert type in certificates.providers.apple.certificates:
7
+ * - valid cert on the account → download the .cer if the local file is
8
+ * missing (delete it to force a re-download), refresh the .p12 if stale
9
+ * - no valid cert + automatable → create via CSR (reused across runs —
10
+ * the private key pairs with the issued cert) + download + export
11
+ * - no valid cert + manual (DEVELOPER_ID_*) → validate the local .cer;
12
+ * missing/expired prints the portal URL + save path and warns (Apple
13
+ * requires Account Holder login to issue these — no API path)
14
+ *
15
+ * After the loop, exported .p12 files import into the macOS login keychain
16
+ * (best-effort, config-listed types only, never in dry-run).
17
+ */
18
+ const { join } = require('node:path');
19
+ const jetpack = require('fs-jetpack');
20
+ const chalk = require('chalk').default;
21
+ const { catchAgreements } = require('../lib/apple-api.js');
22
+ const { canPrompt } = require('../../../lib/run-gates.js');
23
+ const { runManualCertWalkthrough } = require('../lib/manual-walkthrough.js');
24
+
25
+ const {
26
+ listCertificates,
27
+ downloadCertificate,
28
+ createCertificate,
29
+ getCSRContent,
30
+ exportToP12,
31
+ findValidCertificate,
32
+ validateManualCertificate,
33
+ } = require('../lib/certificate-manager.js');
34
+
35
+ const APPLE_PORTAL_CERTIFICATES_URL = 'https://developer.apple.com/account/resources/certificates/list';
36
+
37
+ /** Trim an API cert record to the fields the later operations need. */
38
+ function trimCert(cert) {
39
+ return {
40
+ id: cert.id,
41
+ certificateType: cert.attributes.certificateType,
42
+ expirationDate: cert.attributes.expirationDate,
43
+ };
44
+ }
45
+
46
+ module.exports = catchAgreements(async (context) => {
47
+ const { appleClient, appleSecrets, appleDir, brandConfig, keychainImport } = context;
48
+ const dryRun = context.options?.dryRun || false;
49
+
50
+ const appleConfig = brandConfig.certificates.providers?.apple || {};
51
+ const certConfigs = appleConfig.certificates || [];
52
+ const certificatePassword = appleSecrets.certificatePassword || '';
53
+
54
+ // List once; tolerate API failure when manual certs can still be checked
55
+ // locally (accounts without admin API access hit this).
56
+ let existingCerts;
57
+ try {
58
+ existingCerts = await listCertificates(appleClient);
59
+ console.log(` ${chalk.green('✓')} Found ${existingCerts.length} existing certificates`);
60
+ } catch (error) {
61
+ if (certConfigs.some((c) => c.manual)) {
62
+ console.log(` ${chalk.yellow('⚠')} Could not list certificates via API: ${chalk.gray(error.message)}`);
63
+ existingCerts = [];
64
+ } else {
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ const certificateMap = {};
70
+ const planned = [];
71
+ let downloaded = 0;
72
+ let created = 0;
73
+ let synced = 0;
74
+ let manualMissing = 0;
75
+
76
+ for (const certConfig of certConfigs) {
77
+ const { type, manual } = certConfig;
78
+ const certPath = join(appleDir, 'certificates', `${type}.cer`);
79
+
80
+ console.log(` ${chalk.dim('•')} ${chalk.cyan(type)}`);
81
+
82
+ const validCert = findValidCertificate(existingCerts, type);
83
+
84
+ if (validCert) {
85
+ certificateMap[type] = trimCert(validCert);
86
+
87
+ if (!jetpack.exists(certPath)) {
88
+ if (dryRun) {
89
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would download .cer + export .p12`);
90
+ planned.push(`download ${type}`);
91
+ } else {
92
+ await downloadCertificate(appleClient, validCert.id, certPath);
93
+ exportToP12(type, appleDir, certificatePassword);
94
+ downloaded++;
95
+ }
96
+ } else {
97
+ console.log(` ${chalk.green('✓')} Valid cert exists ${chalk.dim(`(expires ${validCert.attributes.expirationDate})`)}`);
98
+ if (!dryRun) {
99
+ exportToP12(type, appleDir, certificatePassword); // no-op unless the .p12 is stale
100
+ }
101
+ synced++;
102
+ }
103
+ continue;
104
+ }
105
+
106
+ if (manual) {
107
+ let validation = validateManualCertificate(certPath);
108
+
109
+ // Interactive rescue: only the Account Holder can CREATE Developer ID
110
+ // certs (portal-only — no API path), but the CSR, the download, and
111
+ // the install are ours to automate. Non-interactive runs keep the
112
+ // print-and-warn + converge-on-rerun contract below.
113
+ if (!validation.valid && canPrompt(context.options)) {
114
+ const { installed } = await runManualCertWalkthrough({
115
+ type,
116
+ certPath,
117
+ appleDir,
118
+ teamId: appleSecrets.teamId,
119
+ ...(context.downloadsDir ? { downloadsDir: context.downloadsDir } : {}),
120
+ });
121
+ if (installed) {
122
+ validation = validateManualCertificate(certPath);
123
+ }
124
+ }
125
+
126
+ if (validation.valid) {
127
+ const expDate = validation.expirationDate.toISOString().split('T')[0];
128
+ console.log(` ${chalk.green('✓')} Local file valid ${chalk.dim(`(expires ${expDate})`)}`);
129
+ if (!dryRun) {
130
+ exportToP12(type, appleDir, certificatePassword);
131
+ }
132
+ certificateMap[type] = {
133
+ id: 'manual',
134
+ certificateType: type,
135
+ expirationDate: validation.expirationDate.toISOString(),
136
+ };
137
+ synced++;
138
+ } else {
139
+ console.log(` ${chalk.red('✗')} ${validation.reason} — Apple requires Account Holder login to issue this type`);
140
+ console.log(` ${chalk.gray('→')} Download the .cer from ${chalk.cyan(APPLE_PORTAL_CERTIFICATES_URL)}`);
141
+ console.log(` ${chalk.gray('→')} Save it to ${chalk.gray(certPath)} and re-run`);
142
+ manualMissing++;
143
+ }
144
+ continue;
145
+ }
146
+
147
+ if (dryRun) {
148
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would create via CSR + download + export .p12`);
149
+ planned.push(`create ${type}`);
150
+ continue;
151
+ }
152
+
153
+ const csrContent = getCSRContent(type, appleDir, appleSecrets.teamId);
154
+ const newCert = await createCertificate(appleClient, type, csrContent);
155
+ console.log(` ${chalk.green('✓')} Created ${chalk.dim(`(expires ${newCert.attributes.expirationDate})`)}`);
156
+ await downloadCertificate(appleClient, newCert.id, certPath);
157
+ exportToP12(type, appleDir, certificatePassword);
158
+ certificateMap[type] = trimCert(newCert);
159
+ created++;
160
+ }
161
+
162
+ // Import config-listed .p12 files into the login keychain (macOS only)
163
+ if (!dryRun) {
164
+ keychainImport({
165
+ certificatesDir: join(appleDir, 'certificates'),
166
+ keychainPassword: appleSecrets.keychainPassword,
167
+ certificatePassword,
168
+ allowedTypes: certConfigs.map((c) => c.type),
169
+ });
170
+ }
171
+
172
+ const summary = dryRun
173
+ ? { planned }
174
+ : { synced, downloaded, created };
175
+
176
+ if (manualMissing > 0) {
177
+ console.log(` ${chalk.yellow('⚠')} ${manualMissing} manual cert(s) missing — download from the Apple Developer portal and re-run`);
178
+ return {
179
+ state: { certificateMap },
180
+ output: { certificates: { ...summary, manualMissing } },
181
+ status: 'warned',
182
+ reason: `${manualMissing} manual cert(s) missing — download them from the Apple Developer portal`,
183
+ };
184
+ }
185
+
186
+ return { state: { certificateMap }, output: { certificates: summary } };
187
+ });
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Ensure provisioning profiles exist for the brand's bundle ID × every
3
+ * applicable cert type/platform combo, downloading each to
4
+ * .omega/certificates/apple/profiles/{BRAND_ID}/{CERT_TYPE}/{PLATFORM}.mobileprovision
5
+ * (delete the local file to force a re-download). The brand segment matters:
6
+ * the signing tree is shared at companyRoot, but a profile binds ONE bundle
7
+ * ID — without the segment, sibling brands overwrite each other's profiles.
8
+ *
9
+ * Reads the bundle ID + certificateMap the previous handlers stashed in
10
+ * state. Device lists are fetched lazily — only when a development
11
+ * profile actually needs creating (omega-manager fetched them every run).
12
+ */
13
+ const { join } = require('node:path');
14
+ const jetpack = require('fs-jetpack');
15
+ const chalk = require('chalk').default;
16
+ const { catchAgreements } = require('../lib/apple-api.js');
17
+
18
+ const {
19
+ listProfiles,
20
+ listDevices,
21
+ downloadProfile,
22
+ createProfile,
23
+ findValidProfile,
24
+ getProfileTypeForCertType,
25
+ certAppliesToPlatform,
26
+ } = require('../lib/profile-manager.js');
27
+
28
+ module.exports = catchAgreements(async (context) => {
29
+ const { appleClient, appleDir, brandConfig, brandId, serviceData } = context;
30
+ const dryRun = context.options?.dryRun || false;
31
+
32
+ const bundleId = serviceData?.bundleId;
33
+ if (!bundleId?.id) {
34
+ if (dryRun) {
35
+ console.log(` ${chalk.cyan('[DRY RUN]')} Bundle ID not created yet — profiles follow once it exists`);
36
+ return { output: { profiles: { planned: 'after bundle ID exists' } } };
37
+ }
38
+ console.log(` ${chalk.yellow('⚠')} No bundle ID resolved — skipping`);
39
+ return {};
40
+ }
41
+
42
+ const certificateMap = serviceData?.certificateMap || {};
43
+ if (Object.keys(certificateMap).length === 0) {
44
+ console.log(` ${chalk.yellow('⚠')} No certificates available — skipping`);
45
+ return {};
46
+ }
47
+
48
+ const profilesDir = join(appleDir, 'profiles');
49
+ const profileCertTypes = brandConfig.certificates.providers?.apple?.profiles || [];
50
+ const brandName = brandConfig.brand?.name || brandId;
51
+
52
+ const existingProfiles = await listProfiles(appleClient);
53
+ console.log(` ${chalk.green('✓')} Found ${existingProfiles.length} existing profiles`);
54
+
55
+ const profileMap = {};
56
+ const planned = [];
57
+ let downloaded = 0;
58
+ let created = 0;
59
+ let synced = 0;
60
+ let deviceIds = null;
61
+
62
+ for (const platform of bundleId.platforms) {
63
+ for (const certType of profileCertTypes) {
64
+ if (!certAppliesToPlatform(certType, platform)) {
65
+ continue;
66
+ }
67
+
68
+ const profileType = getProfileTypeForCertType(certType);
69
+ if (!profileType) {
70
+ continue; // e.g. MAC_INSTALLER_DISTRIBUTION — no profile needed
71
+ }
72
+
73
+ const cert = certificateMap[certType];
74
+ if (!cert) {
75
+ console.log(` ${chalk.yellow('⚠')} Skipping ${chalk.cyan(certType)} (${platform}) — cert not available`);
76
+ continue;
77
+ }
78
+
79
+ const profileName = `${brandName} - ${certType} (${platform})`;
80
+ const profileKey = `${certType.toLowerCase()}-${platform.toLowerCase()}`;
81
+ const profilePath = join(profilesDir, brandId, certType, `${platform}.mobileprovision`);
82
+
83
+ console.log(` ${chalk.dim('•')} ${chalk.cyan(profileName)}`);
84
+
85
+ const validProfile = findValidProfile(existingProfiles, profileType, profileName);
86
+
87
+ if (validProfile) {
88
+ profileMap[profileKey] = { id: validProfile.id, expirationDate: validProfile.attributes.expirationDate };
89
+
90
+ if (!jetpack.exists(profilePath)) {
91
+ if (dryRun) {
92
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would download`);
93
+ planned.push(`download ${profileName}`);
94
+ } else {
95
+ await downloadProfile(appleClient, validProfile.id, profilePath);
96
+ downloaded++;
97
+ }
98
+ } else {
99
+ console.log(` ${chalk.green('✓')} Valid profile exists ${chalk.dim(`(expires ${validProfile.attributes.expirationDate})`)}`);
100
+ synced++;
101
+ }
102
+ continue;
103
+ }
104
+
105
+ // A cert known only from its local file has no API id to link the
106
+ // profile to — the account's API key lacks permission to list it.
107
+ if (cert.id === 'manual') {
108
+ console.log(` ${chalk.yellow('⚠')} Cert known only from the local file (API didn't list it) — cannot create the profile; check the API key's permissions`);
109
+ continue;
110
+ }
111
+
112
+ if (dryRun) {
113
+ console.log(` ${chalk.cyan('[DRY RUN]')} Would create + download`);
114
+ planned.push(`create ${profileName}`);
115
+ continue;
116
+ }
117
+
118
+ // Development profiles bind devices; distribution profiles don't
119
+ const requiresDevices = profileType === 'IOS_APP_DEVELOPMENT' || profileType === 'MAC_APP_DEVELOPMENT';
120
+ if (requiresDevices && deviceIds === null) {
121
+ const devices = await listDevices(appleClient);
122
+ deviceIds = devices.map((d) => d.id);
123
+ console.log(` ${chalk.green('✓')} Found ${devices.length} registered devices`);
124
+ }
125
+
126
+ const newProfile = await createProfile(
127
+ appleClient,
128
+ profileName,
129
+ profileType,
130
+ bundleId.id,
131
+ [cert.id],
132
+ requiresDevices ? deviceIds : [],
133
+ );
134
+ console.log(` ${chalk.green('✓')} Created ${chalk.dim(`(expires ${newProfile.attributes.expirationDate})`)}`);
135
+ await downloadProfile(appleClient, newProfile.id, profilePath);
136
+ profileMap[profileKey] = { id: newProfile.id, expirationDate: newProfile.attributes.expirationDate };
137
+ created++;
138
+ }
139
+ }
140
+
141
+ const summary = dryRun ? { planned } : { synced, downloaded, created };
142
+ return { state: { profiles: profileMap }, output: { profiles: summary } };
143
+ });