@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,466 @@
1
+ /**
2
+ * Service Runner — shared library for running service operations. Ported from
3
+ * omega-manager's src/lib/service-runner.js with the SAME strict contract so
4
+ * services port over without semantic drift.
5
+ *
6
+ * All services use this same pattern:
7
+ * 1. Operations are declared in src/config.js OPERATIONS[serviceName]
8
+ * 2. Handlers live in ensure/, read/, transform/, write/ directories
9
+ * 3. This runner loads and executes handlers in operation order
10
+ *
11
+ * @example
12
+ * // In src/services/my-service/index.js
13
+ * const { createServiceRunner } = require('../../lib/service-runner.js');
14
+ * module.exports.run = createServiceRunner({ serviceDir: __dirname });
15
+ *
16
+ * @example
17
+ * // With custom setup/hooks
18
+ * module.exports.run = createServiceRunner({
19
+ * serviceDir: __dirname,
20
+ * setup: (context) => {
21
+ * if (!context.brandConfig.myService) {
22
+ * return { skip: true, reason: 'no config' };
23
+ * }
24
+ * return { api: new MyAPI() };
25
+ * },
26
+ * });
27
+ */
28
+
29
+ const fs = require('node:fs');
30
+ const { join } = require('node:path');
31
+ const chalk = require('chalk').default;
32
+
33
+ const { needsInteractiveSkip } = require('./run-gates.js');
34
+ const { CONSENT_REQUIRED } = require('./google-auth.js');
35
+
36
+ /**
37
+ * Class a caught error (#228): a consent gate is a pending HUMAN step, not a
38
+ * failure — it becomes the warned step-aside the run summary's ⚑ section
39
+ * names, so the walk (and an `omega dev` boot on top of it) continues.
40
+ * Everything else stays loud.
41
+ *
42
+ * @param {Error} error - The caught error.
43
+ * @param {string} operationName - The operation's output key.
44
+ * @returns {object|null} The needsInteractiveSkip return, or null when the
45
+ * error is a real failure.
46
+ */
47
+ function pendingGate(error, operationName) {
48
+ if (error?.code !== CONSENT_REQUIRED) {
49
+ return null;
50
+ }
51
+
52
+ console.log(` ${chalk.yellow('⚑')} Needs an interactive run${chalk.dim(`: ${error.message}`)}`);
53
+ return needsInteractiveSkip(operationName, error.message);
54
+ }
55
+
56
+ /**
57
+ * Load an operation handler from a service directory.
58
+ *
59
+ * Missing handler file → null (the runner logs and continues). A handler file
60
+ * that EXISTS but fails to require throws loudly — omega-manager's message
61
+ * sniffing silently swallowed broken handlers; existence-check-first doesn't.
62
+ *
63
+ * @param {string} serviceDir - Absolute path to service directory
64
+ * @param {string} operationName - Name of the operation (e.g., 'targets')
65
+ * @param {string} type - Handler type ('ensure', 'read', 'transform', 'write')
66
+ * @returns {Function|null} - Handler function or null if not found
67
+ */
68
+ function getOperationHandler(serviceDir, operationName, type) {
69
+ const modulePath = join(serviceDir, type, `${operationName}.js`);
70
+
71
+ if (!fs.existsSync(modulePath)) {
72
+ return null;
73
+ }
74
+
75
+ const module = require(modulePath);
76
+ return module.default || module || null;
77
+ }
78
+
79
+ /**
80
+ * Split a handler return into the within-run carry vs transient output.
81
+ *
82
+ * STRICT contract — handler returns MUST be one of:
83
+ * - undefined / null → no-op
84
+ * - { state: {...} } → carry only (this run's serviceData, never a file)
85
+ * - { output: {...} } → transient only (lands in .omega/runs/{ts}.json)
86
+ * - { state, output } → both
87
+ * - { status, error?, reason?, state?, output? } → status/error/reason are metadata, allowed alongside state/output
88
+ *
89
+ * Any other top-level key throws. This prevents accidental data leakage into
90
+ * the carry via an "everything dumps to serviceData" pattern.
91
+ *
92
+ * `state` is the LATER-OPERATIONS channel and nothing more (#434 retired the
93
+ * .omega/state.json cache): a value a following operation in the same service needs
94
+ * (the zone id, the bundle id, the Sentry project map). A fact that must
95
+ * OUTLIVE the run belongs in config/omega.json5 via lib/config-write.js — or
96
+ * in the brand .env via lib/env-secret.js when it is secret-shaped. Anything
97
+ * else (counts, success/fail flags, "what happened this run") goes to `output`.
98
+ */
99
+ const ALLOWED_KEYS = new Set(['state', 'output', 'status', 'error', 'reason']);
100
+
101
+ function splitReturn(result, operationName = 'unknown') {
102
+ if (result === undefined || result === null) {
103
+ return { state: null, output: null };
104
+ }
105
+ if (typeof result !== 'object' || Array.isArray(result)) {
106
+ throw new Error(
107
+ `Handler "${operationName}" returned ${Array.isArray(result) ? 'an array' : typeof result}. `
108
+ + `Returns must be undefined or an object with shape { state?, output?, status?, error?, reason? }.`,
109
+ );
110
+ }
111
+
112
+ const keys = Object.keys(result);
113
+ const invalidKeys = keys.filter((k) => !ALLOWED_KEYS.has(k));
114
+ if (invalidKeys.length > 0) {
115
+ throw new Error(
116
+ `Handler "${operationName}" returned invalid top-level key(s): ${invalidKeys.join(', ')}. `
117
+ + `Allowed keys: state, output, status, error, reason. `
118
+ + `Wrap durable IDs in { state: {...} } and transient flags/counts in { output: {...} }.`,
119
+ );
120
+ }
121
+
122
+ return {
123
+ state: 'state' in result ? result.state : null,
124
+ output: 'output' in result ? result.output : null,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Run ensure operation
130
+ */
131
+ async function runEnsure(serviceDir, operation, context, accumulators) {
132
+ const handler = getOperationHandler(serviceDir, operation.name, 'ensure');
133
+
134
+ if (!handler) {
135
+ console.log(` ${chalk.yellow('⚠')} No ensure handler for ${chalk.bold(operation.name)}`);
136
+ return { status: 'continue' };
137
+ }
138
+
139
+ try {
140
+ // serviceData passed to handler = merged state + output (so handlers can read previous handler state)
141
+ const result = await handler({ ...context, serviceData: { ...accumulators.state, ...accumulators.output } });
142
+
143
+ const { state, output } = splitReturn(result, operation.name);
144
+ if (state) Object.assign(accumulators.state, state);
145
+ if (output) Object.assign(accumulators.output, output);
146
+
147
+ // Status check on the raw handler return
148
+ if (result?.status === 'error') return { status: 'error', error: result.error };
149
+ if (result?.status === 'warned') return { status: 'warned', reason: result.reason };
150
+
151
+ return { status: 'continue' };
152
+ } catch (error) {
153
+ const pending = pendingGate(error, operation.name);
154
+ if (pending) {
155
+ Object.assign(accumulators.output, pending.output);
156
+ return { status: 'warned', reason: pending.reason };
157
+ }
158
+
159
+ console.error(` ${chalk.red('❌')} Ensure failed${chalk.dim(`: ${error.message}`)}`);
160
+ return { status: 'error', error: error.message };
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Run read operation
166
+ */
167
+ async function runRead(serviceDir, operation, context, serviceData) {
168
+ const handler = getOperationHandler(serviceDir, operation.name, 'read');
169
+
170
+ if (!handler) {
171
+ return { data: null, status: 'continue' };
172
+ }
173
+
174
+ try {
175
+ const result = await handler({ ...context, serviceData });
176
+
177
+ if (result?.count !== undefined) {
178
+ console.log(` ${chalk.green('✓')} Read ${chalk.dim(`(${result.count} items)`)}`);
179
+ } else {
180
+ console.log(` ${chalk.green('✓')} Read`);
181
+ }
182
+
183
+ return { data: result, status: 'continue' };
184
+ } catch (error) {
185
+ const pending = pendingGate(error, operation.name);
186
+ if (pending) {
187
+ // No accumulators here — the caller merges the marker
188
+ return { data: null, status: 'warned', output: pending.output, reason: pending.reason };
189
+ }
190
+
191
+ console.error(` ${chalk.red('❌')} Read failed${chalk.dim(`: ${error.message}`)}`);
192
+ return { data: null, status: 'error' };
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Run transform operation
198
+ */
199
+ async function runTransform(serviceDir, operation, context, serviceData, readData) {
200
+ const handler = getOperationHandler(serviceDir, operation.name, 'transform');
201
+
202
+ if (!handler) {
203
+ return { data: readData, status: 'continue' };
204
+ }
205
+
206
+ try {
207
+ const result = await handler({ ...context, serviceData, readData });
208
+
209
+ if (!result) {
210
+ console.log(` ${chalk.dim('⊘ No changes needed')}`);
211
+ return { data: null, status: 'skip' };
212
+ }
213
+
214
+ console.log(` ${chalk.green('✓')} Transform`);
215
+ return { data: result, status: 'continue' };
216
+ } catch (error) {
217
+ const pending = pendingGate(error, operation.name);
218
+ if (pending) {
219
+ return { data: null, status: 'warned', output: pending.output, reason: pending.reason };
220
+ }
221
+
222
+ console.error(` ${chalk.red('❌')} Transform failed${chalk.dim(`: ${error.message}`)}`);
223
+ return { data: null, status: 'error' };
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Run write operation
229
+ */
230
+ async function runWrite(serviceDir, operation, context, accumulators, data) {
231
+ const handler = getOperationHandler(serviceDir, operation.name, 'write');
232
+
233
+ if (!handler) {
234
+ console.log(` ${chalk.yellow('⚠')} No write handler for ${chalk.bold(operation.name)}`);
235
+ return { status: 'continue' };
236
+ }
237
+
238
+ try {
239
+ const result = await handler({
240
+ ...context,
241
+ serviceData: { ...accumulators.state, ...accumulators.output },
242
+ data,
243
+ });
244
+
245
+ console.log(` ${chalk.green('✓')} Write`);
246
+
247
+ const { state, output } = splitReturn(result, operation.name);
248
+ if (state) Object.assign(accumulators.state, state);
249
+ if (output) Object.assign(accumulators.output, output);
250
+
251
+ if (result?.status === 'error') return { status: 'error', error: result.error };
252
+ if (result?.status === 'warned') return { status: 'warned', reason: result.reason };
253
+
254
+ return { status: 'continue' };
255
+ } catch (error) {
256
+ const pending = pendingGate(error, operation.name);
257
+ if (pending) {
258
+ Object.assign(accumulators.output, pending.output);
259
+ return { status: 'warned', reason: pending.reason };
260
+ }
261
+
262
+ console.error(` ${chalk.red('❌')} Write failed${chalk.dim(`: ${error.message}`)}`);
263
+ return { status: 'error', error: error.message };
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Create a service runner function
269
+ *
270
+ * @param {Object} options - Configuration options
271
+ * @param {string} options.serviceDir - Absolute path to the service's directory
272
+ * (handlers load from its ensure/read/transform/write subdirs). Services pass
273
+ * __dirname; context.serviceDir is the fallback for tests/embedding.
274
+ * @param {Function} options.setup - Optional setup function called before operations
275
+ * - Receives: (context) => { skip?, reason?, ...additionalContext }
276
+ * - Return { skip: true, reason: 'message' } to skip the service
277
+ * - Return additional properties to merge into context (e.g., { api: new API() })
278
+ * @param {boolean} options.logOperations - Whether to log each operation (default: true)
279
+ * @param {boolean} options.stopOnError - Whether to stop on first error (default: true)
280
+ * @returns {Function} - Service runner function
281
+ */
282
+ function createServiceRunner(options = {}) {
283
+ const {
284
+ serviceDir: ownServiceDir = null,
285
+ setup = null,
286
+ logOperations = true,
287
+ stopOnError = true,
288
+ } = options;
289
+
290
+ return async function run(context) {
291
+ // Run setup if provided
292
+ let setupResult = {};
293
+ if (setup) {
294
+ setupResult = await setup(context);
295
+
296
+ if (setupResult?.skip) {
297
+ console.log(` ${chalk.dim(`⊘ Skipped (${setupResult.reason || 'setup returned skip'})`)}`);
298
+ return {
299
+ status: 'skipped',
300
+ reason: setupResult.reason,
301
+ // Machine-readable missing-secret list (cp114) — the run summary
302
+ // aggregates these into the 🔑 section
303
+ ...(setupResult.missingEnv ? { missingEnv: setupResult.missingEnv } : {}),
304
+ };
305
+ }
306
+ }
307
+
308
+ // Use operations from setup result if provided, otherwise from context
309
+ const operations = setupResult.operations || context.operations || [];
310
+
311
+ // Merge setup result into context
312
+ const enrichedContext = { ...context, ...setupResult };
313
+
314
+ const serviceDir = ownServiceDir || enrichedContext.serviceDir;
315
+ if (!serviceDir) {
316
+ throw new Error('serviceDir must be provided via createServiceRunner options or context');
317
+ }
318
+
319
+ // Accumulators: the within-run carry (→ each later operation's
320
+ // serviceData) vs transient output (→ .omega/runs/{ts}.json). Setup's
321
+ // serviceData seeds the carry — that's how a service hands its
322
+ // operations something it resolved once (the Stripe account id).
323
+ const accumulators = {
324
+ state: {
325
+ ...context.serviceData,
326
+ ...(setupResult.serviceData || {}),
327
+ },
328
+ output: {},
329
+ };
330
+ let overallStatus = 'success';
331
+ let firstError = null;
332
+
333
+ // What warned/failed, by NAME (#643): the run summary's breakdown reads
334
+ // these instead of collapsing every operation into "some operations had
335
+ // issues". `reason` is the operation's own one-liner (never secret
336
+ // values); null when it gave none, and the summary prints the name alone.
337
+ const warned = [];
338
+ const failed = [];
339
+ const recordWarned = (operation, reason) => warned.push({ operation, reason: reason || null });
340
+ const recordFailed = (operation, reason) => failed.push({ operation, reason: reason || null });
341
+
342
+ // Process operations in order
343
+ for (const operation of operations) {
344
+ if (logOperations) {
345
+ console.log(` ${chalk.dim('→')} ${operation.name}`);
346
+ }
347
+
348
+ // Handle ensure operations
349
+ if (operation.ensure) {
350
+ const result = await runEnsure(serviceDir, operation, enrichedContext, accumulators);
351
+
352
+ if (result.status === 'error') {
353
+ overallStatus = 'error';
354
+ firstError = firstError || result.error || `${operation.name} failed`;
355
+ recordFailed(operation.name, result.error);
356
+ if (stopOnError) break;
357
+ } else if (result.status === 'warned') {
358
+ recordWarned(operation.name, result.reason);
359
+ if (overallStatus !== 'error') overallStatus = 'warned';
360
+ }
361
+
362
+ continue;
363
+ }
364
+
365
+ // Handle read/write operations
366
+ if (operation.read || operation.write) {
367
+ let readData = null;
368
+ const mergedView = { ...accumulators.state, ...accumulators.output };
369
+
370
+ // Read phase
371
+ if (operation.read) {
372
+ const readResult = await runRead(serviceDir, operation, enrichedContext, mergedView);
373
+ readData = readResult.data;
374
+
375
+ if (readResult.status === 'error') {
376
+ overallStatus = 'error';
377
+ firstError = firstError || `${operation.name} read failed`;
378
+ recordFailed(operation.name, 'read failed');
379
+ if (stopOnError) break;
380
+ }
381
+
382
+ // A pending gate (#228): carry its marker and move on — there is
383
+ // nothing to transform or write on top of an operation that never
384
+ // read
385
+ if (readResult.status === 'warned') {
386
+ Object.assign(accumulators.output, readResult.output);
387
+ recordWarned(operation.name, readResult.reason);
388
+ if (overallStatus !== 'error') overallStatus = 'warned';
389
+ continue;
390
+ }
391
+
392
+ // Call onRead hook if provided (e.g., to save read data to disk)
393
+ if (readData && enrichedContext.onRead) {
394
+ await enrichedContext.onRead(operation.name, readData);
395
+ }
396
+ }
397
+
398
+ // Write phase (with optional transform)
399
+ if (operation.write && readData) {
400
+ // Transform phase
401
+ const transformResult = await runTransform(
402
+ serviceDir, operation, enrichedContext, mergedView, readData,
403
+ );
404
+
405
+ if (transformResult.status === 'error') {
406
+ overallStatus = 'error';
407
+ firstError = firstError || `${operation.name} transform failed`;
408
+ recordFailed(operation.name, 'transform failed');
409
+ if (stopOnError) break;
410
+ }
411
+
412
+ if (transformResult.status === 'warned') {
413
+ Object.assign(accumulators.output, transformResult.output);
414
+ recordWarned(operation.name, transformResult.reason);
415
+ if (overallStatus !== 'error') overallStatus = 'warned';
416
+ continue;
417
+ }
418
+
419
+ if (transformResult.status === 'skip') {
420
+ continue;
421
+ }
422
+
423
+ const writeResult = await runWrite(
424
+ serviceDir, operation, enrichedContext, accumulators, transformResult.data,
425
+ );
426
+
427
+ if (writeResult.status === 'error') {
428
+ overallStatus = 'error';
429
+ firstError = firstError || writeResult.error || `${operation.name} write failed`;
430
+ recordFailed(operation.name, writeResult.error || 'write failed');
431
+ if (stopOnError) break;
432
+ } else if (writeResult.status === 'warned') {
433
+ recordWarned(operation.name, writeResult.reason);
434
+ if (overallStatus !== 'error') overallStatus = 'warned';
435
+ }
436
+ } else if (operation.write && !operation.read) {
437
+ // Write-only operation (no read phase)
438
+ const writeResult = await runWrite(
439
+ serviceDir, operation, enrichedContext, accumulators, null,
440
+ );
441
+
442
+ if (writeResult.status === 'error') {
443
+ overallStatus = 'error';
444
+ firstError = firstError || writeResult.error || `${operation.name} write failed`;
445
+ recordFailed(operation.name, writeResult.error || 'write failed');
446
+ if (stopOnError) break;
447
+ } else if (writeResult.status === 'warned') {
448
+ recordWarned(operation.name, writeResult.reason);
449
+ if (overallStatus !== 'error') overallStatus = 'warned';
450
+ }
451
+ }
452
+ }
453
+ }
454
+
455
+ return {
456
+ status: overallStatus,
457
+ error: firstError,
458
+ state: Object.keys(accumulators.state).length > 0 ? accumulators.state : null,
459
+ output: Object.keys(accumulators.output).length > 0 ? accumulators.output : null,
460
+ ...(warned.length > 0 ? { warned } : {}),
461
+ ...(failed.length > 0 ? { failed } : {}),
462
+ };
463
+ };
464
+ }
465
+
466
+ module.exports = { createServiceRunner, getOperationHandler, splitReturn };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Freshness check for derived files — a derived file needs regeneration
3
+ * when it's missing or older than its source. This is what makes local
4
+ * file-producing operations idempotent diff-syncs (assets: logo variants
5
+ * from their SVG sources; certificates: .p12 exports from their .cer):
6
+ * omega-manager regenerated blindly; the port regenerates only what's
7
+ * stale, so a converged brand is a zero-work no-op on every run.
8
+ */
9
+ const { statSync } = require('node:fs');
10
+ const jetpack = require('fs-jetpack');
11
+
12
+ /**
13
+ * @param {string} sourcePath - The source file (must exist)
14
+ * @param {string} derivedPath - The derived file
15
+ * @returns {boolean} true when the derived file must be (re)generated
16
+ */
17
+ function isStale(sourcePath, derivedPath) {
18
+ if (!jetpack.exists(derivedPath)) {
19
+ return true;
20
+ }
21
+ return statSync(derivedPath).mtimeMs < statSync(sourcePath).mtimeMs;
22
+ }
23
+
24
+ module.exports = { isStale };
@@ -0,0 +1,155 @@
1
+ /**
2
+ * WHICH targets a brand-root fan-out runs over, in WHICH order, with WHICH
3
+ * flags — the two pure helpers every fan-out shares.
4
+ *
5
+ * They started in commands/deploy.js because deploy was the first fan-out;
6
+ * `omega update` and the build/clean fan-out (lib/verb-fanout.js) then imported
7
+ * them from there, which put a lib file under a command. They live HERE, one
8
+ * home, and deploy.js re-exports them for its own callers.
9
+ */
10
+
11
+ // Deploy order — backend's API goes live before the surfaces that call it, and
12
+ // every other fan-out reuses it as the dependency order. A custom target (#603)
13
+ // has no rank, so it lands after every framework one.
14
+ const DEPLOY_ORDER = ['backend', 'web', 'extension', 'desktop', 'mobile'];
15
+
16
+ // The flag that spells the target picker, on every brand-root verb (#780).
17
+ // ONE name, one constant: commands/test.js reads this very export, so
18
+ // `omega test --target=` and `omega deploy --target=` can never drift.
19
+ const PICKER_FLAG = 'target';
20
+
21
+ // The pickers `--target=` replaced (#780). Removed, never aliased (no legacy
22
+ // accommodations); `--only` was the collision that forced the rename, since
23
+ // `firebase deploy --only hosting` picks a SERVICE, not a brand target.
24
+ const RETIRED_PICKERS = ['only', 'except'];
25
+
26
+ // Brand-level flags consumed by the fan-out — everything else forwards to the
27
+ // targets. `_`/`$0` are yargs bookkeeping; continue-on-error is the
28
+ // manage-parity bail switch; --target is the target picker. A caller adds
29
+ // the flags IT consumes (verb-fanout's --dry-run) per call.
30
+ const CONSUMED_KEYS = new Set(['_', '$0', PICKER_FLAG, 'continue-on-error', 'continueOnError']);
31
+
32
+ /**
33
+ * Refuse a retired picker (#780), loudly: silently ignoring `--only` would run
34
+ * the whole brand when a subset was asked for, and aliasing it would keep the
35
+ * firebase collision alive. A `refusal` error prints its message alone (the
36
+ * cli-router keeps stacks for bugs), so the one sentence IS the output.
37
+ *
38
+ * @param {object} [options] - the parsed CLI options
39
+ * @throws {Error} when --only or --except was passed
40
+ */
41
+ function assertPickerFlags(options = {}) {
42
+ const used = RETIRED_PICKERS.filter((flag) => options[flag] !== undefined);
43
+ if (used.length === 0) return;
44
+
45
+ const error = new Error(`${used.map((flag) => `--${flag}`).join(' and ')} ${used.length > 1 ? 'are' : 'is'} retired: pick targets with --${PICKER_FLAG}=<target|targetDir>[,…] instead (firebase's own --only hosting runs from targets/backend).`);
46
+ error.refusal = true;
47
+ throw error;
48
+ }
49
+
50
+ /**
51
+ * Refuse a picker token that names nothing (#780). A typo can never quietly
52
+ * narrow the set: on a publishing verb that would under-deploy, and on any
53
+ * verb a green exit would stand for work that never ran. The whole run stops,
54
+ * naming the token and what the brand actually has.
55
+ *
56
+ * The MATCHING is the caller's (a fan-out matches key-or-dir, `omega dev`
57
+ * matches the targets that have a dev leg); the refusal is one implementation.
58
+ *
59
+ * @param {string[]} unknown - the tokens that matched nothing
60
+ * @param {string[]} known - the names a token could have spelled
61
+ * @throws {Error} when any token matched nothing
62
+ */
63
+ function assertKnownTargets(unknown, known) {
64
+ if (unknown.length === 0) return;
65
+
66
+ const error = new Error(`Unknown --${PICKER_FLAG} ${unknown.length > 1 ? 'tokens' : 'token'} ${unknown.map((token) => `"${token}"`).join(', ')}: this brand's targets are ${known.join(', ')}. Nothing ran.`);
67
+ error.refusal = true;
68
+ throw error;
69
+ }
70
+
71
+ /**
72
+ * A comma list of target tokens, trimmed and emptied of blanks.
73
+ *
74
+ * @param {string} [value] - the raw flag value
75
+ * @returns {string[]}
76
+ */
77
+ function parseTargetTokens(value) {
78
+ return String(value || '').split(',').map((part) => part.trim()).filter(Boolean);
79
+ }
80
+
81
+ /**
82
+ * Does this target answer to this token? A target is named by its KEY ('web')
83
+ * or by its dir name ('website') — the ONE matcher every brand-root picker
84
+ * uses, so `--target=` cannot mean one thing on `deploy` and another on `test`.
85
+ *
86
+ * @param {{ target: string|null, name: string }} entry - a discovered target
87
+ * @param {string} token - the picker token
88
+ * @returns {boolean}
89
+ */
90
+ function targetMatches(entry, token) {
91
+ return entry.target === token || entry.name === token;
92
+ }
93
+
94
+ /**
95
+ * Pure target selection — which targets run for a given picker, in order.
96
+ * Picker tokens match a target's key ('web') or its dir name ('website'); no
97
+ * picker means every target.
98
+ *
99
+ * @param {object} input
100
+ * @param {Array<{ name: string, target: string|null }>} input.targets - the target-mapped target dirs
101
+ * @param {string} [input.target] - the --target= comma list: the exact set to run
102
+ * @returns {{ selected: Array }}
103
+ * @throws {Error} when a token matches no target
104
+ */
105
+ function selectTargets({ targets, target }) {
106
+ const tokens = parseTargetTokens(target);
107
+ assertKnownTargets(
108
+ tokens.filter((token) => !targets.some((entry) => targetMatches(entry, token))),
109
+ targets.map((entry) => entry.target || entry.name),
110
+ );
111
+
112
+ const selected = targets
113
+ .filter((entry) => tokens.length === 0 || tokens.some((token) => targetMatches(entry, token)))
114
+ .sort((a, b) => {
115
+ const rank = (entry) => {
116
+ const index = DEPLOY_ORDER.indexOf(entry.target);
117
+ return index === -1 ? DEPLOY_ORDER.length : index;
118
+ };
119
+ return rank(a) - rank(b);
120
+ });
121
+
122
+ return { selected };
123
+ }
124
+
125
+ /**
126
+ * Rebuild forwardable CLI flags from the yargs-parsed options: brand-level
127
+ * keys are consumed, camelCase twins of kebab-case flags are skipped (yargs
128
+ * mints both), booleans re-spell as --flag/--no-flag, values as --flag=value.
129
+ *
130
+ * @param {object} options - the yargs-parsed options
131
+ * @param {string[]} [alsoConsumed] - extra keys THIS fan-out consumes (both spellings)
132
+ * @returns {string[]}
133
+ */
134
+ function buildForwardedFlags(options, alsoConsumed = []) {
135
+ const flags = [];
136
+ const consumed = alsoConsumed.length ? new Set([...CONSUMED_KEYS, ...alsoConsumed]) : CONSUMED_KEYS;
137
+
138
+ for (const [key, value] of Object.entries(options)) {
139
+ if (consumed.has(key)) continue;
140
+ if (value === undefined || value === null) continue;
141
+ // Skip yargs' camelCase duplicate when the kebab-case original exists
142
+ if (/[A-Z]/.test(key) && key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`) in options) continue;
143
+
144
+ const values = Array.isArray(value) ? value : [value];
145
+ for (const entry of values) {
146
+ if (entry === true) flags.push(`--${key}`);
147
+ else if (entry === false) flags.push(`--no-${key}`);
148
+ else flags.push(`--${key}=${entry}`);
149
+ }
150
+ }
151
+
152
+ return flags;
153
+ }
154
+
155
+ module.exports = { DEPLOY_ORDER, PICKER_FLAG, CONSUMED_KEYS, assertPickerFlags, assertKnownTargets, selectTargets, buildForwardedFlags, parseTargetTokens, targetMatches };