@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,43 @@
1
+ /**
2
+ * Omega API Proxy Worker — for domains whose origin is NOT Firebase Hosting.
3
+ *
4
+ * Firebase-hosted api domains don't need this (hosting rewrites serve /omega
5
+ * directly). This worker is for brands that front a DEDICATED backend on the
6
+ * api domain (proxifly's shape: api.{domain} is its own API server) and still
7
+ * need the omega routes to reach the brand's Firebase Functions — the worker
8
+ * carves /omega (and the legacy /backend-manager alias) out at the Cloudflare
9
+ * edge and proxies it to
10
+ * us-central1-{FIREBASE_PROJECT_ID}.cloudfunctions.net/omega_api; everything
11
+ * else passes through to the origin untouched.
12
+ *
13
+ * Attach via edge.providers.cloudflare.workers config, e.g.:
14
+ * { script: 'omega-api-proxy.js', route: 'api.{ domain }/omega*',
15
+ * env: { FIREBASE_PROJECT_ID: '…' } }
16
+ */
17
+
18
+ export default {
19
+ async fetch(request, env) {
20
+ const url = new URL(request.url);
21
+
22
+ // Get Firebase project ID from environment variable
23
+ const firebaseProjectId = env.FIREBASE_PROJECT_ID;
24
+
25
+ // Check if path starts with /omega or the legacy /backend-manager alias
26
+ const prefix = ['/omega', '/backend-manager'].find((p) => url.pathname.startsWith(p));
27
+ if (prefix) {
28
+ // Build Firebase URL, removing the matched prefix
29
+ const firebasePath = url.pathname.replace(prefix, '') || '/';
30
+ const firebaseUrl = `https://us-central1-${firebaseProjectId}.cloudfunctions.net/omega_api${firebasePath}${url.search}`;
31
+
32
+ // Proxy the request
33
+ return fetch(firebaseUrl, {
34
+ method: request.method,
35
+ headers: request.headers,
36
+ body: request.body,
37
+ });
38
+ }
39
+
40
+ // Pass everything else through
41
+ return fetch(request);
42
+ }
43
+ };
@@ -0,0 +1,4 @@
1
+ -to:(
2
+ alerts@{ domain }
3
+ OR account@{ domain }
4
+ )
@@ -0,0 +1,86 @@
1
+ <billing_payments>
2
+ <cancellation_unsubscribe followup="0">
3
+ <reasoning>
4
+ - You CANNOT cancel a user's subscription for them, they must do it themselves. Emailing us is not a valid way to cancel a subscription.
5
+ - Users CANNOT upgrade or downgrade their plans. They must cancel their current plan and re-subscribe to the new plan they want.
6
+ </reasoning>
7
+ <facts>
8
+ - Users CAN cancel by visiting their account at { brand.url }/account#billing and clicking the "Manage Plan" button. From there, they can cancel their subscription.
9
+ - Users CAN change their payment method on an existing subscription by visiting their account at { brand.url }/account#billing and clicking the "Manage Plan" button.
10
+ </facts>
11
+ </cancellation_unsubscribe>
12
+
13
+ <subscription followup="0">
14
+ <facts>
15
+ - All current subscription plans and details are found at { brand.url }/pricing
16
+ - Users can cancel any time within their 14-day free trial and won't be charged.
17
+ - If the user is outside their free trial window, they can cancel their subscription at any time and won't be charged again.
18
+ - Sometimes, bank statements might show a charge during the free trial, but it's just a pre-authorization and will be refunded if the user cancels within the free trial period.
19
+ </facts>
20
+ </subscription>
21
+ {discountSection}
22
+ <refund followup="0">
23
+ <reasoning>
24
+ - Before requesting a refund, users must cancel their subscription.
25
+ - DO NOT mention anything about a refund unless the user specifically asks for one.
26
+ </reasoning>
27
+ <facts>
28
+ - Users can receive a partial refund by visiting { brand.url }/refund and filling out the refund request form.
29
+ - Refunds are prorated based on the unused portion of the subscription.
30
+ </facts>
31
+ </refund>
32
+
33
+ <disputes followup="0">
34
+ <reasoning>
35
+ - DO NOT ever recommend that a user dispute their charge with their bank.
36
+ - Encourage the user to resolve disputes with us first.
37
+ </reasoning>
38
+ </disputes>
39
+ </billing_payments>
40
+
41
+ <delete_account_gdpr followup="0">
42
+ <facts>
43
+ - Users can delete their account, data, and request data by visiting { brand.url }/privacy
44
+ </facts>
45
+ </delete_account_gdpr>
46
+
47
+ <something_not_working followup="0">
48
+ <facts>
49
+ - Instruct users to try basic troubleshooting steps like signing out and back in, ensuring they are using the correct account, trying again later, etc.
50
+ - If they are using a browser, they should try clearing their cache and cookies and try a different browser.
51
+ - If they are using an app, they should try uninstalling and reinstalling the app.
52
+ - User should be instructed to send a screenshot of the issue.
53
+ - Getting "out of requests" error: Users need to upgrade their plan to get more requests or make sure they are using the right API key.
54
+ </facts>
55
+ </something_not_working>
56
+
57
+ <bug_bounty followup="0">
58
+ <reasoning>
59
+ - We ONLY reward for reports that have not been reported before.
60
+ </reasoning>
61
+ <facts>
62
+ - If the user has found a vulnerability, you can invite them to report it @ { brand.url }/security
63
+ </facts>
64
+ </bug_bounty>
65
+
66
+ <outreach_responses>
67
+ <reasoning>
68
+ - When the conversation starts with an outreach/marketing email from us (e.g. a discount nudge, a premium upgrade offer, a check-in), and the user replies positively or with interest, you MUST follow through on whatever was offered in that original email.
69
+ - If the original email offered a code, discount, upgrade, or link — provide it in your reply. DO NOT just say "you're welcome" or give a generic acknowledgment.
70
+ - Treat positive replies like "Thank you", "Yes please", "I'd love that", "Sure", "Sounds great" as the user ACCEPTING the offer. Deliver on it immediately.
71
+ - If you don't have the specific thing that was promised (e.g. a unique code that needs to be generated), acknowledge what was offered and provide the closest available resource (e.g. the discount code from the discount section, or a link to the pricing page).
72
+ </reasoning>
73
+ </outreach_responses>
74
+
75
+ <contact>
76
+ <reasoning>
77
+ - DO NOT agree to joining a phone call, video call, or any other form of communication outside of email. All communication should be done through this thread.
78
+ </reasoning>
79
+ </contact>
80
+
81
+ <resources>
82
+ <facts>
83
+ - Terms of Service: { brand.url }/terms
84
+ - Privacy Policy: { brand.url }/privacy
85
+ </facts>
86
+ </resources>
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Ensure the brand's Replyify agent has the correct filter + knowledge.
3
+ *
4
+ * Filter = the brand's Gmail query (config/replyify.md `---filter---`
5
+ * section, or auto-generated to:(@domain)) ANDed with the baseline
6
+ * exclusion filter. Knowledge = the packaged baseline + the file's
7
+ * knowledge section appended (omega-manager read `.brands/{id}/replyify.md`).
8
+ *
9
+ * Diff-synced on the managed leaf fields and patched with a leaf mask so
10
+ * Replyify-owned fields (owner, id, metadata, other settings) survive —
11
+ * omega-manager merge-wrote on every run with no dry-run guard, and
12
+ * silently created an orphan agent document when the id was wrong; a
13
+ * missing agent is a visible error now (agents are created in the
14
+ * Replyify dashboard).
15
+ */
16
+ const { join } = require('node:path');
17
+ const fs = require('node:fs');
18
+ const chalk = require('chalk').default;
19
+ const { getBaselineKnowledge, getBaselineFilter } = require('../lib/baseline.js');
20
+ const { parseKnowledgeFile } = require('../lib/knowledge-file.js');
21
+
22
+ function extractDomain(websiteUrl) {
23
+ return websiteUrl.replace(/^https?:\/\//, '').replace(/\/.*$/, '');
24
+ }
25
+
26
+ /**
27
+ * The default Gmail filter query — everything addressed to the brand's domain
28
+ */
29
+ function generateFilterQuery(domain) {
30
+ return `(\n to:(\n @${domain}\n )\n)`;
31
+ }
32
+
33
+ module.exports = async function ensureAgent(context) {
34
+ const { brandConfig, brandRoot, db, agentId, options } = context;
35
+ const dryRun = options?.dryRun || false;
36
+
37
+ const brandName = brandConfig.brand.name;
38
+ const websiteUrl = brandConfig.brand.url;
39
+ const domain = extractDomain(websiteUrl);
40
+
41
+ // Brand-specific filter + knowledge file
42
+ const knowledgePath = join(brandRoot, 'config', 'replyify.md');
43
+ const rawContent = fs.existsSync(knowledgePath) ? fs.readFileSync(knowledgePath, 'utf8') : '';
44
+ const brandFile = parseKnowledgeFile(rawContent);
45
+
46
+ // Brand filter (file or auto-generated), merged with the baseline exclusions
47
+ const brandFilter = brandFile.filterQuery || generateFilterQuery(domain);
48
+ const filterQuery = `${brandFilter}\nAND\n${getBaselineFilter(brandConfig, domain)}`;
49
+
50
+ const baseline = getBaselineKnowledge(brandConfig, domain);
51
+ const knowledge = brandFile.knowledge ? `${baseline}\n\n${brandFile.knowledge}` : baseline;
52
+
53
+ const desired = {
54
+ name: `${brandName} - Customer Service`,
55
+ settings: {
56
+ filter: { query: filterQuery },
57
+ brand: {
58
+ name: brandName,
59
+ about: brandConfig.brand.description || '',
60
+ website: websiteUrl,
61
+ knowledge,
62
+ },
63
+ },
64
+ };
65
+
66
+ const fieldPaths = [
67
+ 'name',
68
+ 'settings.filter.query',
69
+ 'settings.brand.name',
70
+ 'settings.brand.about',
71
+ 'settings.brand.website',
72
+ 'settings.brand.knowledge',
73
+ ];
74
+
75
+ const agent = await db.getDoc(`agents/${agentId}`);
76
+
77
+ if (!agent) {
78
+ console.log(` ${chalk.red('✗')} Agent ${chalk.cyan(agentId)} not found in Replyify — check inbound.email.providers.replyify.agentId (agents are created at ${chalk.cyan('https://replyify.app')})`);
79
+ return { status: 'error', error: `agent ${agentId} not found` };
80
+ }
81
+
82
+ const settings = agent.settings || {};
83
+ const drifted = agent.name !== desired.name
84
+ || settings.filter?.query !== filterQuery
85
+ || settings.brand?.name !== brandName
86
+ || settings.brand?.about !== desired.settings.brand.about
87
+ || settings.brand?.website !== websiteUrl
88
+ || settings.brand?.knowledge !== knowledge;
89
+
90
+ if (!drifted) {
91
+ console.log(` ${chalk.green('✓')} Agent ${chalk.dim(agentId)} in sync ${chalk.dim(`(${desired.name})`)}`);
92
+ console.log(` ${chalk.dim(`Knowledge: ${knowledge.length} chars (baseline + ${brandFile.knowledge.length} brand-specific)`)}`);
93
+ return { state: { agentId }, output: { agent: { synced: true } } };
94
+ }
95
+
96
+ if (dryRun) {
97
+ console.log(` ${chalk.yellow('[DRY RUN]')} Would update agent ${chalk.cyan(agentId)} ${chalk.dim(`(${desired.name}, ${knowledge.length} chars knowledge)`)}`);
98
+ return { output: { agent: { planned: 'update' } } };
99
+ }
100
+
101
+ await db.patchDoc(`agents/${agentId}`, desired, fieldPaths);
102
+
103
+ console.log(` ${chalk.green('✓')} Agent ${chalk.dim(agentId)} updated ${chalk.dim(`(${desired.name})`)}`);
104
+ console.log(` ${chalk.dim(`Knowledge: ${knowledge.length} chars (baseline + ${brandFile.knowledge.length} brand-specific)`)}`);
105
+
106
+ return { state: { agentId }, output: { agent: { updated: true } } };
107
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Ensure the brand's Replyify owner account has the configured plan.
3
+ *
4
+ * The agent's owner UID identifies the user account; that user's
5
+ * subscription is set to inbound.email.providers.replyify.plan (DEFAULTS: Replyify's Max top tier)
6
+ * via the shared owner-plan reconciliation. omega-manager resolved the
7
+ * plan by reading the replyify brand's own config from `.brands/` (company
8
+ * mode); the port takes it from the brand's config with the top tier as
9
+ * the default.
10
+ */
11
+ const chalk = require('chalk').default;
12
+ const { ensureOwnerPlan } = require('../../../lib/owner-plan.js');
13
+
14
+ module.exports = async function ensureUser(context) {
15
+ const { brandConfig, db, agentId, options } = context;
16
+
17
+ const plan = brandConfig.inbound?.email?.providers?.replyify?.plan;
18
+ if (!plan?.id) {
19
+ console.log(` ${chalk.red('✗')} No inbound.email.providers.replyify.plan configured`);
20
+ return { status: 'error', error: 'no inbound.email.providers.replyify.plan.id configured' };
21
+ }
22
+
23
+ // The agent document carries the owner UID
24
+ const agent = await db.getDoc(`agents/${agentId}`);
25
+
26
+ if (!agent) {
27
+ console.log(` ${chalk.red('✗')} Agent ${chalk.cyan(agentId)} not found in Replyify — check inbound.email.providers.replyify.agentId`);
28
+ return { status: 'error', error: `agent ${agentId} not found` };
29
+ }
30
+
31
+ const ownerUid = agent.owner;
32
+
33
+ if (!ownerUid) {
34
+ console.log(` ${chalk.red('✗')} Agent ${chalk.cyan(agentId)} has no owner field`);
35
+ return { status: 'error', error: `agent ${agentId} has no owner` };
36
+ }
37
+
38
+ const result = await ensureOwnerPlan({ db, ownerUid, plan, dryRun: options?.dryRun || false });
39
+
40
+ if (result === 'planned') {
41
+ return { output: { user: { planned: plan.id } } };
42
+ }
43
+
44
+ return { state: { ownerUid }, output: { user: { plan: plan.id, [result]: true } } };
45
+ };
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Email service (`inbound.email.providers.replyify`) — the brand's
3
+ * customer-service email agent on Replyify (replyify.app): keeps the agent's
4
+ * Gmail filter + knowledge in sync (baseline templates with brand values +
5
+ * the brand repo's config/replyify.md merged in) and sets the agent-owner
6
+ * account to the configured plan (Replyify's top tier by default). Agents live in
7
+ * Replyify's own Firestore, so this service is for the Replyify operator:
8
+ * it needs a service account for Replyify's Firebase project.
9
+ *
10
+ * Auth tiers (2b, Ian 2026-07-13):
11
+ * 1. Operator SA — REPLYIFY_SERVICE_ACCOUNT in the brand .env (path to
12
+ * the service-account JSON, absolute or brand-root-relative). Full
13
+ * create + manage: a missing agentId with `inbound.email.providers.replyify.templateAgentId`
14
+ * configured (the company layer's shape donor) MINTS the brand's own
15
+ * agent — product user (email = brand contact email, password via the
16
+ * account service's owner channels) + doc shape-templated from the
17
+ * donor — and writes the new id back into omega.json5. The ensures
18
+ * then converge filter/knowledge/plan in the same run.
19
+ * 2. User API key — REPLYIFY_API_KEY (the product accepts
20
+ * user.api.privateKey): recognized, management via the product's
21
+ * public API lands when those routes are verified.
22
+ * 3. Dashboard — interactive runs offer the setup flow (open
23
+ * replyify.app, paste the agent id back — comment-preserving
24
+ * writeback, with a Disable option that sets `replyify: false`).
25
+ * Non-interactive and dry runs skip cleanly.
26
+ */
27
+ const chalk = require('chalk').default;
28
+ const { serviceInputSpec } = require('../../config.js');
29
+ const { createServiceRunner } = require('../../lib/service-runner.js');
30
+ const { requestServiceInput } = require('../../lib/service-input.js');
31
+ const { FirestoreREST, loadServiceAccount } = require('../../lib/firestore-rest.js');
32
+ const { createAuthAdmin } = require('../../lib/auth-admin.js');
33
+ const { resolveConfigValue, landValue } = require('../../lib/config-flow.js');
34
+ const { createProductAsset } = require('../../lib/product-create.js');
35
+ const { createPasswordResolver } = require('../account/lib/resolve-password.js');
36
+
37
+ const REPLYIFY_URL = 'https://replyify.app';
38
+
39
+ module.exports.run = createServiceRunner({
40
+ serviceDir: __dirname,
41
+ setup: async (context) => {
42
+ const config = context.brandConfig.inbound?.email?.providers?.replyify;
43
+
44
+ if (config === false || config?.enabled === false) {
45
+ return { skip: true, reason: 'inbound.email.providers.replyify.enabled = false' };
46
+ }
47
+
48
+ // A shared agent managed by another brand must not be rewritten here
49
+ if (config?.updateAgentInfo === false) {
50
+ return { skip: true, reason: 'agent managed by another brand (inbound.email.providers.replyify.updateAgentInfo = false)' };
51
+ }
52
+
53
+ // The agent answers the brand's support email, which the backend serves
54
+ if (!context.brandConfig.targets?.backend) {
55
+ return { skip: true, reason: 'no backend target' };
56
+ }
57
+
58
+ // Operator credentials first — create-on-missing needs them before the
59
+ // id resolution. Tests inject fakes via context.replyifyDb/-AuthAdmin.
60
+ let db = context.replyifyDb;
61
+ let authAdmin = context.replyifyAuthAdmin;
62
+ if (!db) {
63
+ const envPath = process.env.REPLYIFY_SERVICE_ACCOUNT;
64
+ if (envPath) {
65
+ const serviceAccount = loadServiceAccount(envPath, context.brandRoot);
66
+ db = new FirestoreREST(serviceAccount);
67
+ authAdmin = createAuthAdmin(serviceAccount);
68
+ }
69
+ }
70
+
71
+ const brand = context.brandConfig.brand || {};
72
+ const dryRun = context.options?.dryRun || false;
73
+ let agentId = config?.agentId;
74
+
75
+ // 2b create-on-missing: operator SA + a template donor → a missing agent
76
+ // means MINT the brand's own (product user + doc), write the id back,
77
+ // and let the ensures below converge it in this same run.
78
+ if (!agentId && db && authAdmin && config?.templateAgentId && brand.contact?.email) {
79
+ const created = await createProductAsset({
80
+ db,
81
+ authAdmin,
82
+ collection: 'agents',
83
+ templateId: config.templateAgentId,
84
+ email: brand.contact.email,
85
+ resolvePassword: createPasswordResolver({
86
+ brandRoot: context.brandRoot,
87
+ domain: new URL(brand.url || 'https://invalid.test').hostname,
88
+ brand,
89
+ dryRun,
90
+ }),
91
+ dryRun,
92
+ log: (line) => console.log(` ${line}`),
93
+ });
94
+
95
+ if (!created) {
96
+ return { skip: true, reason: 'dry run — agent creation planned (from inbound.email.providers.replyify.templateAgentId)' };
97
+ }
98
+
99
+ landValue(context, 'inbound.email.providers.replyify.agentId', created.id);
100
+ console.log(` ${chalk.green('✓')} inbound.email.providers.replyify.agentId = ${chalk.cyan(created.id)} written to omega.json5`);
101
+ agentId = created.id;
102
+ }
103
+
104
+ if (!agentId) {
105
+ agentId = await resolveConfigValue(context, {
106
+ path: 'inbound.email.providers.replyify.agentId',
107
+ label: 'Replyify email agent',
108
+ instructions: [
109
+ `1. Create an account at ${chalk.cyan(REPLYIFY_URL)} (if you haven't already)`,
110
+ `2. Create an email agent for ${chalk.cyan(brand.name || context.brandId)}`,
111
+ ],
112
+ entry: { url: REPLYIFY_URL, message: 'Replyify agent ID:' },
113
+ disablePath: 'inbound.email.providers.replyify',
114
+ });
115
+ }
116
+ if (!agentId) {
117
+ return { skip: true, reason: 'no inbound.email.providers.replyify.agentId configured — set inbound.email.providers.replyify.templateAgentId + the operator SA to mint one automatically, create one at https://replyify.app (paste back interactively), or set it in omega.json5' };
118
+ }
119
+
120
+ if (!db) {
121
+ if (process.env.REPLYIFY_API_KEY) {
122
+ return {
123
+ skip: true,
124
+ reason: 'REPLYIFY_API_KEY recognized, but product-API management is not wired yet — use the operator SA (REPLYIFY_SERVICE_ACCOUNT) or the dashboard',
125
+ };
126
+ }
127
+
128
+ // The shared setup contract (#608): the operator SA is asked for right
129
+ // here — most brands are not the Replyify operator and answer Disable,
130
+ // which retires the ask for good.
131
+ const gate = await requestServiceInput(context, serviceInputSpec('email'));
132
+ if (gate) return gate;
133
+ db = new FirestoreREST(loadServiceAccount(process.env.REPLYIFY_SERVICE_ACCOUNT, context.brandRoot));
134
+ }
135
+
136
+ return { db, agentId };
137
+ },
138
+ });
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Baseline templates for Replyify agents — the shared support knowledge and
3
+ * the baseline Gmail filter every brand agent gets, loaded from data/ with
4
+ * the brand's values filled in via the manager's templateObject (flat
5
+ * `{ brand.url }` / `{ domain }` placeholders).
6
+ *
7
+ * De-ITW'd from omega-manager's baseline: the company sponsorship business
8
+ * block (guest-post rules, the company sponsorship URL, its promo code) is
9
+ * GONE from the packaged template — company business prose belongs in the
10
+ * brand/company config/replyify.md that gets appended to the baseline. The
11
+ * discount section only renders when `inbound.email.providers.replyify.discount` is configured
12
+ * (omega-manager hardcoded the company's GIFT15 code for every brand); with
13
+ * no code configured the section is omitted entirely so agents can never
14
+ * invent one.
15
+ */
16
+ const { join } = require('node:path');
17
+ const fs = require('node:fs');
18
+ const { templateObject } = require('../../../config.js');
19
+
20
+ const BASELINE_KNOWLEDGE = fs.readFileSync(join(__dirname, '..', 'data', 'baseline-knowledge.md'), 'utf8').trimEnd();
21
+ const BASELINE_FILTER = fs.readFileSync(join(__dirname, '..', 'data', 'baseline-filter.md'), 'utf8').trimEnd();
22
+
23
+ // Spliced into baseline-knowledge.md's {discountSection} slot only when
24
+ // inbound.email.providers.replyify.discount carries a code
25
+ const DISCOUNT_SECTION = ` <discount followup="0">
26
+ <reasoning>
27
+ - DO NOT offer or mention a discount proactively. Only provide a code if the user SPECIFICALLY asks for one (e.g. they reply asking for a deal, a promo code, or a way to save).
28
+ - Provide the code warmly and only ONCE per conversation. DO NOT repeat the code if you have already shared it.
29
+ - DO NOT invent or guess a code. Only ever share the exact code below.
30
+ </reasoning>
31
+ <facts>
32
+ - Discount code: { discount.code } ({ discount.label }).
33
+ - Users redeem the code at checkout when subscribing at { brand.url }/pricing?utm_source=replyify&utm_medium=customer_support&utm_campaign=discount_code
34
+ </facts>
35
+ </discount>`;
36
+
37
+ /**
38
+ * Build the flat placeholder context templateObject resolves against.
39
+ * Discount keys join only when configured — templateObject would stringify
40
+ * an undefined value.
41
+ */
42
+ function templateContext(brandConfig, domain) {
43
+ const discount = brandConfig.inbound?.email?.providers?.replyify?.discount;
44
+
45
+ return {
46
+ 'brand.name': brandConfig.brand.name,
47
+ 'brand.url': brandConfig.brand.url,
48
+ domain,
49
+ ...(discount?.code ? { 'discount.code': discount.code, 'discount.label': discount.label || '' } : {}),
50
+ };
51
+ }
52
+
53
+ /**
54
+ * Get the baseline knowledge with placeholders replaced and the discount
55
+ * section rendered only when inbound.email.providers.replyify.discount is configured.
56
+ *
57
+ * @param {Object} brandConfig - The full brand config object
58
+ * @param {string} domain - Brand domain (e.g. "fixture-brand.test")
59
+ * @returns {string}
60
+ */
61
+ function getBaselineKnowledge(brandConfig, domain) {
62
+ const discount = brandConfig.inbound?.email?.providers?.replyify?.discount;
63
+ const withDiscount = BASELINE_KNOWLEDGE.replace(
64
+ '{discountSection}',
65
+ discount?.code ? `\n${DISCOUNT_SECTION}\n` : '',
66
+ );
67
+
68
+ return templateObject(withDiscount, templateContext(brandConfig, domain));
69
+ }
70
+
71
+ /**
72
+ * Get the baseline Gmail filter with the brand's domain filled in.
73
+ *
74
+ * @param {Object} brandConfig - The full brand config object
75
+ * @param {string} domain - Brand domain
76
+ * @returns {string}
77
+ */
78
+ function getBaselineFilter(brandConfig, domain) {
79
+ return templateObject(BASELINE_FILTER, templateContext(brandConfig, domain));
80
+ }
81
+
82
+ module.exports = { getBaselineKnowledge, getBaselineFilter };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Parser for the brand's config/replyify.md — an optional custom Gmail
3
+ * filter section plus brand-specific knowledge appended to the baseline
4
+ * (omega-manager read `.brands/{id}/replyify.md`).
5
+ *
6
+ * Supported formats:
7
+ * 1. Both delimiters: ---filter---\n(query)\n---knowledge---\n(text)
8
+ * 2. Filter only: ---filter---\n(query)
9
+ * 3. Knowledge delimiter: ---knowledge---\n(text)
10
+ * 4. No delimiters: the whole file is knowledge
11
+ */
12
+ const FILTER_DELIMITER = '---filter---';
13
+ const KNOWLEDGE_DELIMITER = '---knowledge---';
14
+
15
+ /**
16
+ * @param {string} content - Raw file content
17
+ * @returns {{ filterQuery: string|null, knowledge: string }}
18
+ */
19
+ function parseKnowledgeFile(content) {
20
+ const hasFilter = content.includes(FILTER_DELIMITER);
21
+ const hasKnowledge = content.includes(KNOWLEDGE_DELIMITER);
22
+
23
+ if (hasFilter && hasKnowledge) {
24
+ const afterFilter = content.split(FILTER_DELIMITER)[1];
25
+ const [filterPart, ...knowledgeParts] = afterFilter.split(KNOWLEDGE_DELIMITER);
26
+
27
+ return {
28
+ filterQuery: filterPart.trim(),
29
+ knowledge: knowledgeParts.join(KNOWLEDGE_DELIMITER).trim(),
30
+ };
31
+ }
32
+
33
+ if (hasFilter) {
34
+ return {
35
+ filterQuery: content.split(FILTER_DELIMITER)[1].trim(),
36
+ knowledge: '',
37
+ };
38
+ }
39
+
40
+ if (hasKnowledge) {
41
+ return {
42
+ filterQuery: null,
43
+ knowledge: content.split(KNOWLEDGE_DELIMITER)[1].trim(),
44
+ };
45
+ }
46
+
47
+ return {
48
+ filterQuery: null,
49
+ knowledge: content.trim(),
50
+ };
51
+ }
52
+
53
+ module.exports = { parseKnowledgeFile };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Ensure the brand's Slapform contact form has the correct settings —
3
+ * name matches the brand, form enabled.
4
+ *
5
+ * Diff-synced: the form document is read first and only patched on drift
6
+ * (omega-manager merge-wrote it on every run, with no dry-run guard). A
7
+ * formId that points at no document is an error — the form must be created
8
+ * in the Slapform dashboard first (omega-manager silently created a
9
+ * name-only orphan document in that case).
10
+ */
11
+ const chalk = require('chalk').default;
12
+
13
+ module.exports = async function ensureForm(context) {
14
+ const { brandConfig, db, formId, options } = context;
15
+ const dryRun = options?.dryRun || false;
16
+
17
+ const desiredName = `Contact Form - ${brandConfig.brand.name}`;
18
+
19
+ const form = await db.getDoc(`forms/${formId}`);
20
+
21
+ if (!form) {
22
+ console.log(` ${chalk.red('✗')} Form ${chalk.cyan(formId)} not found in Slapform — check forms.providers.slapform.formId (forms are created at ${chalk.cyan('https://slapform.com')})`);
23
+ return { status: 'error', error: `form ${formId} not found` };
24
+ }
25
+
26
+ const drifted = form.name !== desiredName || form.settings?.enabled !== true;
27
+
28
+ if (!drifted) {
29
+ console.log(` ${chalk.green('✓')} Form ${chalk.dim(formId)} in sync ${chalk.dim(`(${desiredName})`)}`);
30
+ return { state: { formId }, output: { form: { synced: true } } };
31
+ }
32
+
33
+ if (dryRun) {
34
+ console.log(` ${chalk.yellow('[DRY RUN]')} Would update form ${chalk.cyan(formId)} ${chalk.dim(`(name → ${desiredName}, enabled → true)`)}`);
35
+ return { output: { form: { planned: 'update' } } };
36
+ }
37
+
38
+ await db.patchDoc(`forms/${formId}`, {
39
+ name: desiredName,
40
+ settings: { enabled: true },
41
+ }, ['name', 'settings.enabled']);
42
+
43
+ console.log(` ${chalk.green('✓')} Form ${chalk.dim(formId)} updated ${chalk.dim(`(${desiredName})`)}`);
44
+
45
+ return { state: { formId }, output: { form: { updated: true } } };
46
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Ensure the brand's Slapform owner account has the configured plan.
3
+ *
4
+ * The form's owner UID identifies the user account; that user's
5
+ * subscription is set to forms.providers.slapform.plan (DEFAULTS: Slapform's Grandmaster
6
+ * top tier) via the shared owner-plan reconciliation. omega-manager
7
+ * resolved the plan by reading the slapform brand's own config from
8
+ * `.brands/` (company mode); the port takes it from the brand's config
9
+ * with the top tier as the default.
10
+ */
11
+ const chalk = require('chalk').default;
12
+ const { ensureOwnerPlan } = require('../../../lib/owner-plan.js');
13
+
14
+ module.exports = async function ensureUser(context) {
15
+ const { brandConfig, db, formId, options } = context;
16
+
17
+ const plan = brandConfig.forms?.providers?.slapform?.plan;
18
+ if (!plan?.id) {
19
+ console.log(` ${chalk.red('✗')} No forms.providers.slapform.plan configured`);
20
+ return { status: 'error', error: 'no forms.providers.slapform.plan.id configured' };
21
+ }
22
+
23
+ // The form document carries the owner UID
24
+ const form = await db.getDoc(`forms/${formId}`);
25
+
26
+ if (!form) {
27
+ console.log(` ${chalk.red('✗')} Form ${chalk.cyan(formId)} not found in Slapform — check forms.providers.slapform.formId`);
28
+ return { status: 'error', error: `form ${formId} not found` };
29
+ }
30
+
31
+ const ownerUid = form.owner;
32
+
33
+ if (!ownerUid) {
34
+ console.log(` ${chalk.red('✗')} Form ${chalk.cyan(formId)} has no owner field`);
35
+ return { status: 'error', error: `form ${formId} has no owner` };
36
+ }
37
+
38
+ const result = await ensureOwnerPlan({ db, ownerUid, plan, dryRun: options?.dryRun || false });
39
+
40
+ if (result === 'planned') {
41
+ return { output: { user: { planned: plan.id } } };
42
+ }
43
+
44
+ return { state: { ownerUid }, output: { user: { plan: plan.id, [result]: true } } };
45
+ };