create-ailk 0.2.0 → 0.4.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 (292) hide show
  1. package/component-catalog.md +50 -53
  2. package/dist/cli.js +19 -9
  3. package/dist/lib/apply-module-patches.d.ts +47 -0
  4. package/dist/lib/apply-module-patches.js +370 -0
  5. package/dist/lib/extract-module-bundle.d.ts +51 -3
  6. package/dist/lib/extract-module-bundle.js +327 -28
  7. package/dist/lib/fetch-module.d.ts +3 -38
  8. package/dist/lib/fetch-module.js +26 -8
  9. package/dist/lib/fetch-template.d.ts +20 -0
  10. package/dist/lib/fetch-template.js +129 -0
  11. package/dist/lib/module-license-gate.d.ts +10 -0
  12. package/dist/lib/module-license-gate.js +13 -4
  13. package/dist/lib/paid-paths.d.ts +16 -0
  14. package/dist/lib/paid-paths.js +90 -0
  15. package/dist/module-architecture.d.ts +197 -23
  16. package/dist/module-architecture.js +820 -25
  17. package/dist/parse-args.d.ts +9 -2
  18. package/dist/parse-args.js +46 -1
  19. package/dist/programmatic.d.ts +28 -2
  20. package/dist/programmatic.js +115 -8
  21. package/dist/surfaces.d.ts +12 -36
  22. package/dist/surfaces.js +77 -5
  23. package/dist/sync-routes.js +31 -1
  24. package/dist/template-manifest.d.ts +92 -0
  25. package/dist/template-manifest.js +168 -0
  26. package/dist/template-names.d.ts +41 -0
  27. package/dist/template-names.js +44 -0
  28. package/package.json +3 -2
  29. package/site-templates/marketing-site-free/ailk-template.json +8 -0
  30. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +103 -0
  31. package/site-templates/marketing-site-free/content/en/PrivacyPage/privacy.mdx +79 -0
  32. package/site-templates/marketing-site-free/content/en/TermsPage/terms.mdx +80 -0
  33. package/site-templates/marketing-site-free/provenance.json +4 -0
  34. package/templates/.claude/agents/web.md +2 -3
  35. package/templates/.claude/rules/architecture.md +5 -5
  36. package/templates/.claude/skills/README.md +2 -1
  37. package/templates/.env.example +21 -9
  38. package/templates/CONVENTIONS.md +6 -7
  39. package/templates/apps/api/.env.example +9 -0
  40. package/templates/apps/api/CLAUDE.md +33 -5
  41. package/templates/apps/api/package.json +0 -1
  42. package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
  43. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
  44. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
  45. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +166 -0
  46. package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
  47. package/templates/apps/api/src/config/index.ts +19 -0
  48. package/templates/apps/api/src/config/modules.ts +12 -14
  49. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  50. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
  51. package/templates/apps/api/src/lib/slice-load.ts +90 -0
  52. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
  53. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
  54. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
  55. package/templates/apps/api/src/openapi/index.ts +9 -3
  56. package/templates/apps/api/src/openapi/spec.ts +505 -65
  57. package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
  58. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
  59. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
  60. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
  61. package/templates/apps/api/src/routes/content/index.ts +38 -11
  62. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +17 -0
  63. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +17 -0
  64. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
  65. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +10 -0
  66. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
  67. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
  68. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +10 -0
  69. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +24 -0
  70. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +17 -0
  71. package/templates/apps/api/src/routes/project-listings/index.ts +37 -10
  72. package/templates/apps/api/src/routes/project-listings/me.ts +10 -0
  73. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
  74. package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
  75. package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
  76. package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
  77. package/templates/apps/api/src/routes/project-listings/start.ts +30 -0
  78. package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
  79. package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
  80. package/templates/apps/api/src/server.ts +504 -137
  81. package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
  82. package/templates/apps/api/src/services/listing-config.ts +58 -0
  83. package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
  84. package/templates/apps/api/src/services/project-listings.ts +229 -4
  85. package/templates/apps/api/src/vercel-handler.ts +60 -26
  86. package/templates/apps/mcp/.env.example +8 -0
  87. package/templates/apps/mcp/CLAUDE.md +2 -2
  88. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
  89. package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
  90. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
  91. package/templates/apps/mcp/src/config/modules.ts +14 -13
  92. package/templates/apps/mcp/src/server.ts +8 -4
  93. package/templates/apps/mcp/src/tools/index.ts +7 -22
  94. package/templates/apps/web/.env.example +15 -0
  95. package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
  96. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
  97. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
  98. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
  99. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
  100. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
  101. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
  102. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
  103. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
  104. package/templates/apps/web/app/[locale]/pricing/page.tsx +40 -0
  105. package/templates/apps/web/app/[locale]/privacy/page.tsx +40 -0
  106. package/templates/apps/web/app/[locale]/terms/page.tsx +40 -0
  107. package/templates/apps/web/app/llms.txt/route.ts +12 -9
  108. package/templates/apps/web/jest.config.cjs +19 -13
  109. package/templates/apps/web/lib/__tests__/server-api.test.ts +61 -0
  110. package/templates/apps/web/lib/server-api.ts +28 -12
  111. package/templates/apps/web/messages/en.json +58 -0
  112. package/templates/apps/web/next.config.mjs +1 -3
  113. package/templates/apps/web/package.json +0 -6
  114. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  115. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  116. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  117. package/templates/apps/web/public/favicon-16x16.png +0 -0
  118. package/templates/apps/web/public/favicon-32x32.png +0 -0
  119. package/templates/apps/web/public/favicon.ico +0 -0
  120. package/templates/apps/web/public/favicon.svg +6 -3
  121. package/templates/apps/web/public/lockup-horizontal.svg +4 -0
  122. package/templates/apps/web/public/logomark.svg +4 -0
  123. package/templates/apps/web/public/site.webmanifest +2 -2
  124. package/templates/apps/web/public/wordmark.svg +4 -0
  125. package/templates/database/CHANGELOG.md +47 -0
  126. package/templates/database/inbox/schema.prisma +67 -0
  127. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
  128. package/templates/database/package.json +1 -1
  129. package/templates/package.json +1 -1
  130. package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
  131. package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
  132. package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
  133. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
  134. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
  135. package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
  136. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
  137. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
  138. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
  139. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
  140. package/templates/apps/api/src/__tests__/server.test.ts +0 -253
  141. package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
  142. package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
  143. package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
  144. package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
  145. package/templates/apps/api/src/bin/seed-presets.ts +0 -58
  146. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
  147. package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
  148. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
  149. package/templates/apps/api/src/lib/stripe.ts +0 -52
  150. package/templates/apps/api/src/lib/tenant-db.ts +0 -225
  151. package/templates/apps/api/src/lib/ws-token.ts +0 -91
  152. package/templates/apps/api/src/middleware/tenant.ts +0 -99
  153. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
  154. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
  155. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
  156. package/templates/apps/api/src/routes/billing/index.ts +0 -36
  157. package/templates/apps/api/src/routes/billing/portal.ts +0 -182
  158. package/templates/apps/api/src/routes/billing/read.ts +0 -75
  159. package/templates/apps/api/src/routes/billing/usage.ts +0 -153
  160. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
  161. package/templates/apps/api/src/routes/checkout/index.ts +0 -13
  162. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
  163. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
  164. package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
  165. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
  166. package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
  167. package/templates/apps/api/src/routes/flows/README.md +0 -147
  168. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
  169. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
  170. package/templates/apps/api/src/routes/flows/index.ts +0 -202
  171. package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
  172. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
  173. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +0 -221
  174. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
  175. package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
  176. package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
  177. package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
  178. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
  179. package/templates/apps/api/src/routes/schedule/index.ts +0 -249
  180. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
  181. package/templates/apps/api/src/routes/slack/actions.ts +0 -177
  182. package/templates/apps/api/src/routes/slack/index.ts +0 -38
  183. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
  184. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
  185. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
  186. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
  187. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
  188. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
  189. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
  190. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
  191. package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
  192. package/templates/apps/api/src/routes/webhooks/README.md +0 -80
  193. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
  194. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
  195. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
  196. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
  197. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
  198. package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
  199. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
  200. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
  201. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
  202. package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
  203. package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
  204. package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
  205. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
  206. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
  207. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
  208. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
  209. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +0 -684
  210. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
  211. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
  212. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
  213. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
  214. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
  215. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
  216. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
  217. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
  218. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
  219. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
  220. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
  221. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
  222. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
  223. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
  224. package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
  225. package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
  226. package/templates/apps/api/src/services/flow-engine.ts +0 -1296
  227. package/templates/apps/api/src/services/lead-promotion.ts +0 -176
  228. package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
  229. package/templates/apps/api/src/services/listing-promotion.ts +0 -342
  230. package/templates/apps/api/src/services/playbook-compile.ts +0 -398
  231. package/templates/apps/api/src/services/playbook-render.ts +0 -263
  232. package/templates/apps/api/src/services/project-listing-decision.ts +0 -510
  233. package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
  234. package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
  235. package/templates/apps/api/src/services/recommender-capture.ts +0 -835
  236. package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
  237. package/templates/apps/api/src/services/scheduling/index.ts +0 -63
  238. package/templates/apps/api/src/services/scheduling/types.ts +0 -88
  239. package/templates/apps/api/src/services/tenant-context.ts +0 -451
  240. package/templates/apps/api/src/services/usage-metering.ts +0 -699
  241. package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
  242. package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
  243. package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
  244. package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
  245. package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
  246. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
  247. package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
  248. package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
  249. package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
  250. package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
  252. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
  253. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
  254. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
  255. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
  256. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
  257. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
  258. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
  259. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
  260. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
  261. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
  262. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
  263. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
  264. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
  265. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
  266. package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
  267. package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
  268. package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
  269. package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
  270. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
  271. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
  272. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
  273. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
  274. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
  275. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
  276. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
  277. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
  278. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
  279. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
  280. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
  281. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
  282. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
  283. package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
  284. package/templates/apps/web/content/en/blog/README.txt +0 -25
  285. package/templates/apps/web/content/en/docs/index.mdx +0 -28
  286. package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
  287. package/templates/apps/web/lib/blog-list-data.ts +0 -95
  288. package/templates/apps/web/lib/blog-post-data.ts +0 -79
  289. package/templates/apps/web/lib/source.ts +0 -34
  290. package/templates/apps/web/public/logomark-dark.svg +0 -4
  291. package/templates/apps/web/public/logomark-light.svg +0 -4
  292. package/templates/apps/web/source.config.ts +0 -78
@@ -1,125 +0,0 @@
1
- // oss: false
2
- /**
3
- * Workspace site-config routes (multi-tenant module — FEAT-042; STC-P4 #3634).
4
- *
5
- * The write surface an end-tenant uses to theme THEIR OWN workspace at runtime:
6
- *
7
- * GET /v1/workspaces/:workspaceId/theme · PUT /v1/workspaces/:workspaceId/theme
8
- * GET /v1/workspaces/:workspaceId/identity · PUT /v1/workspaces/:workspaceId/identity
9
- * GET /v1/workspaces/:workspaceId/seo · PUT /v1/workspaces/:workspaceId/seo
10
- *
11
- * All tenant-guarded (tenantPreHandlerPlugin runs first): the cross-workspace
12
- * 403 is enforced by the guard's `params.workspaceId ≠ resolved` check BEFORE
13
- * any handler runs — no config query executes for a foreign workspace. The
14
- * scope is ALWAYS `request.tenant.workspaceId` (server-resolved from the
15
- * validated session); no handler reads a scope from the body/query. Each config
16
- * model is `workspaceId @unique`, so the `tenantDb` accessor's
17
- * `findUnique`/`upsert` on `{ workspaceId }` is inherently tenant-scoped.
18
- *
19
- * PUT validates the body against the shared @working-theory/site-config schema
20
- * (400 on failure) and stores the validated blob in the `data` Json column. GET
21
- * returns the stored blob, or `{ ok: true, data: null }` for a not-yet-configured
22
- * workspace (a valid runtime state — the render/seed default is Phase 5).
23
- *
24
- * Envelope per apps/api/CLAUDE.md: `{ ok: true, data }` /
25
- * `{ ok: false, error: { code, message } }`, real HTTP status codes.
26
- *
27
- * MCP parity note: identity-bound tenant routes; the OSS MCP surface is
28
- * identity-blind (apps/mcp/CLAUDE.md), so — like the sibling /v1/workspaces
29
- * routes — they have no anonymous MCP twin. The tenant-scoped authed MCP
30
- * surface is tracked in #3591.
31
- */
32
- import { zWorkspaceId } from "@working-theory/ids";
33
- import {
34
- SEOSchema,
35
- SiteIdentitySchema,
36
- ThemeSchema,
37
- } from "@working-theory/site-config/schema";
38
- import type { FastifyPluginAsync, FastifyReply } from "fastify";
39
- import type { ZodType } from "zod";
40
- import { z } from "zod";
41
-
42
- import { tenantDb } from "../../lib/tenant-db.js";
43
- import type { TenantDb } from "../../lib/tenant-db.js";
44
-
45
- function unauthorized(reply: FastifyReply) {
46
- return reply.status(401).send({
47
- ok: false,
48
- error: { code: "unauthorized", message: "Authentication required" },
49
- });
50
- }
51
-
52
- function invalidId(reply: FastifyReply) {
53
- return reply.status(400).send({
54
- ok: false,
55
- error: { code: "validation_failed", message: "Invalid workspace id" },
56
- });
57
- }
58
-
59
- // Boundary id validation (ADR-0031 rule 5): the :workspaceId param must be a
60
- // well-formed workspace id before any handler logic runs.
61
- const workspaceParamsSchema = z.object({ workspaceId: zWorkspaceId });
62
-
63
- /**
64
- * The three config types share one GET/PUT shape — the only per-type differences
65
- * are the URL segment, the validation schema, and which `tenantDb` accessor to
66
- * reach. `accessor` names a config accessor on `TenantDb` (`get` + `upsert`).
67
- */
68
- type ConfigAccessorKey = "theme" | "siteIdentity" | "seo";
69
-
70
- interface ConfigType {
71
- segment: string;
72
- schema: ZodType;
73
- accessor: ConfigAccessorKey;
74
- }
75
-
76
- const CONFIG_TYPES: ConfigType[] = [
77
- { segment: "theme", schema: ThemeSchema, accessor: "theme" },
78
- { segment: "identity", schema: SiteIdentitySchema, accessor: "siteIdentity" },
79
- { segment: "seo", schema: SEOSchema, accessor: "seo" },
80
- ];
81
-
82
- export const workspaceConfigRoutes: FastifyPluginAsync = async (fastify) => {
83
- for (const { segment, schema, accessor } of CONFIG_TYPES) {
84
- fastify.get<{ Params: { workspaceId: string } }>(
85
- `/v1/workspaces/:workspaceId/${segment}`,
86
- async (request, reply) => {
87
- const tenant = request.tenant;
88
- if (!tenant) return unauthorized(reply);
89
-
90
- const params = workspaceParamsSchema.safeParse(request.params);
91
- if (!params.success) return invalidId(reply);
92
-
93
- const db: TenantDb = tenantDb(tenant);
94
- const row = await db[accessor].get();
95
- // A not-yet-configured workspace is a valid state, not an error (D6).
96
- return reply.send({ ok: true, data: row?.data ?? null });
97
- },
98
- );
99
-
100
- fastify.put<{ Params: { workspaceId: string } }>(
101
- `/v1/workspaces/:workspaceId/${segment}`,
102
- async (request, reply) => {
103
- const tenant = request.tenant;
104
- if (!tenant) return unauthorized(reply);
105
-
106
- const params = workspaceParamsSchema.safeParse(request.params);
107
- if (!params.success) return invalidId(reply);
108
-
109
- // The ONLY untrusted input: validate against the shared schema. The
110
- // scope is NEVER read from the body — it is tenant.workspaceId.
111
- const parsed = schema.safeParse(request.body);
112
- if (!parsed.success) {
113
- return reply.status(400).send({
114
- ok: false,
115
- error: { code: "validation_failed", message: "Invalid config" },
116
- });
117
- }
118
-
119
- const db: TenantDb = tenantDb(tenant);
120
- const row = await db[accessor].upsert(parsed.data);
121
- return reply.send({ ok: true, data: row.data });
122
- },
123
- );
124
- }
125
- };
@@ -1,194 +0,0 @@
1
- // oss: false
2
- /**
3
- * Workspace routes (multi-tenant module — FEAT-042).
4
- *
5
- * Two registration scopes (see server.ts):
6
- *
7
- * AUTH-ONLY (no tenant guard — they establish/inspect tenant context):
8
- * GET /v1/workspaces — switcher listing (orgs + workspaces)
9
- * POST /v1/workspaces — create workspace (role admin+)
10
- * POST /v1/workspaces/:workspaceId/activate — switch active workspace
11
- *
12
- * TENANT-GUARDED (tenantPreHandlerPlugin runs first; request.tenant set):
13
- * GET /v1/workspaces/:workspaceId/sessions — list agent sessions (+unread)
14
- * POST /v1/workspaces/:workspaceId/sessions — create agent session
15
- *
16
- * Envelope per apps/api/CLAUDE.md: `{ ok: true, data }` /
17
- * `{ ok: false, error: { code, message } }`, real HTTP status codes.
18
- *
19
- * MCP parity note: these are identity-bound tenant routes; the OSS MCP
20
- * surface is identity-blind (apps/mcp/CLAUDE.md), so — like the existing
21
- * /v1/auth/tokens and /v1/user routes — they have no anonymous MCP twin.
22
- * The tenant-scoped authed MCP surface is tracked in #3591.
23
- */
24
- import { zOrganizationId, zWorkspaceId } from "@working-theory/ids";
25
- import type { FastifyPluginAsync, FastifyReply } from "fastify";
26
- import { z } from "zod";
27
-
28
- import { tenantDb } from "../../lib/tenant-db.js";
29
- import {
30
- activateWorkspace,
31
- createWorkspace,
32
- listTenancy,
33
- type TenantErrorKind,
34
- } from "../../services/tenant-context.js";
35
-
36
- const TENANT_ERROR_STATUS: Record<TenantErrorKind, number> = {
37
- forbidden: 403,
38
- not_found: 404,
39
- conflict: 409,
40
- };
41
-
42
- function sendTenantError(
43
- reply: FastifyReply,
44
- error: { kind: TenantErrorKind; message: string },
45
- ) {
46
- return reply.status(TENANT_ERROR_STATUS[error.kind]).send({
47
- ok: false,
48
- error: { code: error.kind, message: error.message },
49
- });
50
- }
51
-
52
- function unauthorized(reply: FastifyReply) {
53
- return reply.status(401).send({
54
- ok: false,
55
- error: { code: "unauthorized", message: "Authentication required" },
56
- });
57
- }
58
-
59
- const createWorkspaceSchema = z.object({
60
- // Boundary id validation (ADR-0031 rule 5): the entity-typed Zod schema,
61
- // not z.string().min(1) — a user_/wksp_ id passed here is a 400, not a query.
62
- organizationId: zOrganizationId,
63
- name: z.string().min(1).max(80),
64
- });
65
-
66
- const workspaceParamsSchema = z.object({ workspaceId: zWorkspaceId });
67
-
68
- const createSessionSchema = z.object({
69
- title: z.string().min(1).max(200).optional(),
70
- });
71
-
72
- const listSessionsQuerySchema = z.object({
73
- limit: z.coerce.number().int().min(1).max(100).default(50),
74
- });
75
-
76
- /** Auth-only workspace routes — establish/inspect tenant context. */
77
- export const workspaceContextRoutes: FastifyPluginAsync = async (fastify) => {
78
- fastify.get("/v1/workspaces", async (request, reply) => {
79
- const userId = request.userId;
80
- if (!userId) return unauthorized(reply);
81
-
82
- const result = await listTenancy({
83
- userId,
84
- sessionTenant: request.sessionTenant,
85
- });
86
- if (!result.ok) return sendTenantError(reply, result.error);
87
- return reply.send({ ok: true, data: result.value });
88
- });
89
-
90
- fastify.post("/v1/workspaces", async (request, reply) => {
91
- const userId = request.userId;
92
- if (!userId) return unauthorized(reply);
93
-
94
- const parsed = createWorkspaceSchema.safeParse(request.body);
95
- if (!parsed.success) {
96
- return reply.status(400).send({
97
- ok: false,
98
- error: { code: "validation_failed", message: "Invalid workspace input" },
99
- });
100
- }
101
-
102
- const result = await createWorkspace({ userId, ...parsed.data });
103
- if (!result.ok) return sendTenantError(reply, result.error);
104
- return reply.status(201).send({ ok: true, data: result.value });
105
- });
106
-
107
- fastify.post<{ Params: { workspaceId: string } }>(
108
- "/v1/workspaces/:workspaceId/activate",
109
- async (request, reply) => {
110
- const userId = request.userId;
111
- if (!userId) return unauthorized(reply);
112
-
113
- const params = workspaceParamsSchema.safeParse(request.params);
114
- if (!params.success) {
115
- return reply.status(400).send({
116
- ok: false,
117
- error: { code: "validation_failed", message: "Invalid workspace id" },
118
- });
119
- }
120
-
121
- const result = await activateWorkspace({
122
- userId,
123
- sessionTenant: request.sessionTenant,
124
- workspaceId: params.data.workspaceId,
125
- });
126
- if (!result.ok) return sendTenantError(reply, result.error);
127
- return reply.send({ ok: true, data: result.value });
128
- },
129
- );
130
- };
131
-
132
- /** Tenant-guarded, workspace-scoped domain routes. */
133
- export const workspaceSessionRoutes: FastifyPluginAsync = async (fastify) => {
134
- fastify.get<{ Params: { workspaceId: string } }>(
135
- "/v1/workspaces/:workspaceId/sessions",
136
- async (request, reply) => {
137
- const tenant = request.tenant;
138
- if (!tenant) return unauthorized(reply);
139
-
140
- const query = listSessionsQuerySchema.safeParse(request.query ?? {});
141
- if (!query.success) {
142
- return reply.status(400).send({
143
- ok: false,
144
- error: { code: "validation_failed", message: "Invalid query" },
145
- });
146
- }
147
-
148
- const db = tenantDb(tenant);
149
- const [sessions, receipts] = await Promise.all([
150
- db.agentSession.list(query.data.limit),
151
- db.readReceipt.listForUser(),
152
- ]);
153
- const readBySession = new Map(
154
- receipts.map((r) => [r.sessionId, r.lastReadSeq]),
155
- );
156
-
157
- return reply.send({
158
- ok: true,
159
- data: {
160
- sessions: sessions.map((s) => ({
161
- id: s.id,
162
- title: s.title,
163
- status: s.status,
164
- lastMessageAt: s.lastMessageAt,
165
- isWorking: s.status === "working",
166
- hasUnread: s.lastSeq > (readBySession.get(s.id) ?? 0),
167
- createdAt: s.createdAt,
168
- })),
169
- },
170
- });
171
- },
172
- );
173
-
174
- fastify.post<{ Params: { workspaceId: string } }>(
175
- "/v1/workspaces/:workspaceId/sessions",
176
- async (request, reply) => {
177
- const tenant = request.tenant;
178
- if (!tenant) return unauthorized(reply);
179
-
180
- const parsed = createSessionSchema.safeParse(request.body ?? {});
181
- if (!parsed.success) {
182
- return reply.status(400).send({
183
- ok: false,
184
- error: { code: "validation_failed", message: "Invalid session input" },
185
- });
186
- }
187
-
188
- const session = await tenantDb(tenant).agentSession.create({
189
- title: parsed.data.title ?? null,
190
- });
191
- return reply.status(201).send({ ok: true, data: session });
192
- },
193
- );
194
- };
@@ -1,218 +0,0 @@
1
- // oss: false
2
- /**
3
- * Session-target routes (multi-tenant module — FEAT-042; transport contract
4
- * from docs/research/ploy-capture/BACKEND-REQUIREMENTS.md §1/§3).
5
- *
6
- * All tenant-guarded (tenantPreHandlerPlugin): request.tenant is the caller's
7
- * validated active workspace. Targets are session/artifact ids; every lookup
8
- * goes through tenantDb's workspace-scoped `findFirst({ id, workspaceId })`,
9
- * so a foreign-tenant target 404s exactly like a missing one — cross-org
10
- * reads AND writes are denied without revealing existence.
11
- *
12
- * GET /v1/sessions/:sessionId/conversation-events?sinceSeq=N&limit=M
13
- * → { events[], lastSeq } (R-T4 replay)
14
- * POST /v1/sessions/:sessionId/mark-read (R-T7)
15
- * GET /v1/sessions/:sessionId/ws-token (R-T3 short-lived token)
16
- * GET /v1/sessions/:sessionId/artifacts (canvas hydration)
17
- * GET /v1/artifacts/:artifactId/versions (VersionHistory hydration)
18
- *
19
- * The realtime push channel itself (WS server consuming these tokens) is
20
- * Phase-6 scope; this run supplies the replayable, workspace-scoped data
21
- * plane the frame reads.
22
- */
23
- import { zAgentSessionId, zArtifactId } from "@working-theory/ids";
24
- import type { FastifyPluginAsync, FastifyReply } from "fastify";
25
- import { z } from "zod";
26
-
27
- import { tenantDb } from "../../lib/tenant-db.js";
28
- import { mintWsToken } from "../../lib/ws-token.js";
29
-
30
- function notFound(reply: FastifyReply) {
31
- return reply.status(404).send({
32
- ok: false,
33
- error: { code: "not_found", message: "Not found" },
34
- });
35
- }
36
-
37
- function invalidId(reply: FastifyReply) {
38
- return reply.status(400).send({
39
- ok: false,
40
- error: { code: "validation_failed", message: "Invalid id" },
41
- });
42
- }
43
-
44
- // Boundary id validation (ADR-0031 rule 5): target params must be well-formed
45
- // entity ids (agsess_/artif_ prefixed uuids) before any lookup runs.
46
- const sessionParamsSchema = z.object({ sessionId: zAgentSessionId });
47
- const artifactParamsSchema = z.object({ artifactId: zArtifactId });
48
-
49
- function unauthorized(reply: FastifyReply) {
50
- return reply.status(401).send({
51
- ok: false,
52
- error: { code: "unauthorized", message: "Authentication required" },
53
- });
54
- }
55
-
56
- const eventsQuerySchema = z.object({
57
- sinceSeq: z.coerce.number().int().min(0).default(0),
58
- limit: z.coerce.number().int().min(1).max(500).default(200),
59
- });
60
-
61
- const markReadSchema = z.object({
62
- seq: z.coerce.number().int().min(0).optional(),
63
- });
64
-
65
- export const sessionRoutes: FastifyPluginAsync = async (fastify) => {
66
- fastify.get<{ Params: { sessionId: string } }>(
67
- "/v1/sessions/:sessionId/conversation-events",
68
- async (request, reply) => {
69
- const tenant = request.tenant;
70
- if (!tenant) return unauthorized(reply);
71
-
72
- const query = eventsQuerySchema.safeParse(request.query ?? {});
73
- if (!query.success) {
74
- return reply.status(400).send({
75
- ok: false,
76
- error: { code: "validation_failed", message: "Invalid query" },
77
- });
78
- }
79
-
80
- const params = sessionParamsSchema.safeParse(request.params);
81
- if (!params.success) return invalidId(reply);
82
-
83
- const db = tenantDb(tenant);
84
- const session = await db.agentSession.byId(params.data.sessionId);
85
- if (!session) return notFound(reply);
86
-
87
- const events = await db.conversationEvent.listSince(
88
- params.data.sessionId,
89
- query.data.sinceSeq,
90
- query.data.limit,
91
- );
92
-
93
- return reply.send({
94
- ok: true,
95
- data: {
96
- events: events.map((e) => ({
97
- agentId: e.agentId,
98
- seq: e.seq,
99
- type: e.type,
100
- turnId: e.turnId,
101
- payload: e.payload,
102
- createdAt: e.createdAt,
103
- })),
104
- lastSeq: session.lastSeq,
105
- },
106
- });
107
- },
108
- );
109
-
110
- fastify.post<{ Params: { sessionId: string } }>(
111
- "/v1/sessions/:sessionId/mark-read",
112
- async (request, reply) => {
113
- const tenant = request.tenant;
114
- if (!tenant) return unauthorized(reply);
115
-
116
- const parsed = markReadSchema.safeParse(request.body ?? {});
117
- if (!parsed.success) {
118
- return reply.status(400).send({
119
- ok: false,
120
- error: { code: "validation_failed", message: "Invalid body" },
121
- });
122
- }
123
-
124
- const params = sessionParamsSchema.safeParse(request.params);
125
- if (!params.success) return invalidId(reply);
126
-
127
- const db = tenantDb(tenant);
128
- // Scoped resolve FIRST — the upsert below must never run for a session
129
- // outside the tenant (a receipt row would attach to a foreign session).
130
- const session = await db.agentSession.byId(params.data.sessionId);
131
- if (!session) return notFound(reply);
132
-
133
- const lastReadSeq = parsed.data.seq ?? session.lastSeq;
134
- await db.readReceipt.upsert(params.data.sessionId, lastReadSeq);
135
- return reply.send({ ok: true, data: { sessionId: session.id, lastReadSeq } });
136
- },
137
- );
138
-
139
- fastify.get<{ Params: { sessionId: string } }>(
140
- "/v1/sessions/:sessionId/ws-token",
141
- async (request, reply) => {
142
- const tenant = request.tenant;
143
- if (!tenant) return unauthorized(reply);
144
-
145
- const params = sessionParamsSchema.safeParse(request.params);
146
- if (!params.success) return invalidId(reply);
147
-
148
- const session = await tenantDb(tenant).agentSession.byId(
149
- params.data.sessionId,
150
- );
151
- if (!session) return notFound(reply);
152
-
153
- const minted = mintWsToken({
154
- userId: tenant.userId,
155
- sessionId: params.data.sessionId,
156
- workspaceId: tenant.workspaceId,
157
- });
158
- return reply.send({ ok: true, data: minted });
159
- },
160
- );
161
-
162
- fastify.get<{ Params: { sessionId: string } }>(
163
- "/v1/sessions/:sessionId/artifacts",
164
- async (request, reply) => {
165
- const tenant = request.tenant;
166
- if (!tenant) return unauthorized(reply);
167
-
168
- const params = sessionParamsSchema.safeParse(request.params);
169
- if (!params.success) return invalidId(reply);
170
-
171
- const db = tenantDb(tenant);
172
- const session = await db.agentSession.byId(params.data.sessionId);
173
- if (!session) return notFound(reply);
174
-
175
- const artifacts = await db.artifact.listBySession(params.data.sessionId);
176
- return reply.send({
177
- ok: true,
178
- data: {
179
- artifacts: artifacts.map((a) => ({
180
- id: a.id,
181
- type: a.type,
182
- title: a.title,
183
- updatedAt: a.updatedAt,
184
- })),
185
- },
186
- });
187
- },
188
- );
189
-
190
- fastify.get<{ Params: { artifactId: string } }>(
191
- "/v1/artifacts/:artifactId/versions",
192
- async (request, reply) => {
193
- const tenant = request.tenant;
194
- if (!tenant) return unauthorized(reply);
195
-
196
- const params = artifactParamsSchema.safeParse(request.params);
197
- if (!params.success) return invalidId(reply);
198
-
199
- const db = tenantDb(tenant);
200
- const artifact = await db.artifact.byId(params.data.artifactId);
201
- if (!artifact) return notFound(reply);
202
-
203
- const versions = await db.artifactVersion.listByArtifact(
204
- params.data.artifactId,
205
- );
206
- return reply.send({
207
- ok: true,
208
- data: {
209
- versions: versions.map((v) => ({
210
- id: v.id,
211
- savedAt: v.savedAt,
212
- isCurrent: v.isCurrent,
213
- })),
214
- },
215
- });
216
- },
217
- );
218
- };