create-ailk 0.4.0 → 0.6.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.
- package/README.md +55 -8
- package/component-catalog.json +12248 -0
- package/component-catalog.md +193 -68
- package/dist/cli.js +29 -1
- package/dist/derive-architecture.js +5 -2
- package/dist/lib/api-boot-assertion.d.ts +46 -0
- package/dist/lib/api-boot-assertion.js +70 -0
- package/dist/lib/extract-module-bundle.d.ts +50 -2
- package/dist/lib/extract-module-bundle.js +223 -15
- package/dist/lib/fetch-guard.d.ts +28 -0
- package/dist/lib/fetch-guard.js +101 -0
- package/dist/lib/fetch-module.js +4 -0
- package/dist/lib/fetch-template.js +4 -0
- package/dist/lib/import-graph.d.ts +29 -0
- package/dist/lib/import-graph.js +140 -0
- package/dist/module-architecture.d.ts +60 -8
- package/dist/module-architecture.js +587 -242
- package/dist/parse-args.js +6 -2
- package/dist/programmatic.d.ts +10 -0
- package/dist/programmatic.js +134 -19
- package/dist/site-chrome.d.ts +80 -0
- package/dist/site-chrome.js +135 -0
- package/dist/surfaces.d.ts +21 -6
- package/dist/surfaces.js +209 -36
- package/dist/template-manifest.d.ts +88 -3
- package/dist/template-manifest.js +281 -29
- package/dist/template-names.d.ts +1 -1
- package/dist/template-names.js +2 -0
- package/package.json +10 -2
- package/site-templates/_base/ailk-template.json +6 -0
- package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
- package/site-templates/marketing-site/ailk-template.json +11 -0
- package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
- package/site-templates/marketing-site/provenance.json +4 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
- package/site-templates/marketing-site-free/ailk-template.json +2 -1
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
- package/site-templates/minimalism-site/ailk-template.json +22 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
- package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
- package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
- package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
- package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
- package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
- package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
- package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
- package/site-templates/minimalism-site/provenance.json +4 -0
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
- package/site-templates/saas-product/ailk-template.json +16 -0
- package/site-templates/saas-product/provenance.json +10 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
- package/site-templates/simple-service/ailk-template.json +10 -0
- package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
- package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
- package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
- package/site-templates/simple-service/provenance.json +4 -0
- package/templates/.claude/skills/README.md +16 -25
- package/templates/.env.example +102 -12
- package/templates/.github/workflows/ci.yml +48 -0
- package/templates/README.md +3 -1
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
- package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
- package/templates/apps/api/.env.example +13 -0
- package/templates/apps/api/CLAUDE.md +29 -2
- package/templates/apps/api/eslint.config.mjs +69 -0
- package/templates/apps/api/jest.config.cjs +3 -0
- package/templates/apps/api/package.json +7 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
- package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
- package/templates/apps/api/src/__tests__/server.test.ts +471 -0
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
- package/templates/apps/api/src/config/index.ts +62 -0
- package/templates/apps/api/src/config/modules.ts +7 -3
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
- package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
- package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
- package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
- package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
- package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
- package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
- package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
- package/templates/apps/api/src/lib/content-adapter.ts +57 -31
- package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
- package/templates/apps/api/src/lib/error-serializer.ts +23 -3
- package/templates/apps/api/src/lib/outbound-http.ts +7 -2
- package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
- package/templates/apps/api/src/lib/site.ts +208 -9
- package/templates/apps/api/src/lib/stripe.ts +66 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
- package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
- package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
- package/templates/apps/api/src/openapi/spec.ts +543 -2
- package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
- package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
- package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
- package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
- package/templates/apps/api/src/routes/auth/user.ts +61 -0
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
- package/templates/apps/api/src/routes/checkout/index.ts +34 -0
- package/templates/apps/api/src/routes/checkout/session.ts +139 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
- package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
- package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
- package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
- package/templates/apps/api/src/routes/content/get-page.ts +94 -12
- package/templates/apps/api/src/routes/content/index.ts +13 -73
- package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
- package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
- package/templates/apps/api/src/routes/content/search.ts +129 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
- package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
- package/templates/apps/api/src/routes/leads/index.ts +100 -13
- package/templates/apps/api/src/routes/leads/list.ts +104 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
- package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
- package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
- package/templates/apps/api/src/routes/requests/index.ts +54 -0
- package/templates/apps/api/src/routes/webhooks/README.md +265 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
- package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
- package/templates/apps/api/src/server.ts +521 -55
- package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
- package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
- package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
- package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
- package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
- package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
- package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
- package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
- package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
- package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
- package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
- package/templates/apps/api/src/services/abandon-email.ts +8 -2
- package/templates/apps/api/src/services/aeo-score.ts +49 -12
- package/templates/apps/api/src/services/audit-log.ts +668 -0
- package/templates/apps/api/src/services/consent.ts +18 -0
- package/templates/apps/api/src/services/default-tenant.ts +300 -0
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
- package/templates/apps/api/src/services/dunning-policy.ts +294 -0
- package/templates/apps/api/src/services/entitlements.ts +80 -0
- package/templates/apps/api/src/services/lead-promotion.ts +160 -0
- package/templates/apps/api/src/services/lead-routing.ts +161 -2
- package/templates/apps/api/src/services/leads-inbox.ts +284 -0
- package/templates/apps/api/src/services/listing-config.ts +27 -9
- package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
- package/templates/apps/api/src/services/project-listings.ts +201 -6
- package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
- package/templates/apps/api/src/services/request-confirm.ts +646 -0
- package/templates/apps/api/src/services/revalidation.ts +173 -0
- package/templates/apps/api/src/services/verified-email.ts +71 -0
- package/templates/apps/api/src/vercel-handler.ts +75 -35
- package/templates/apps/mcp/.env.example +17 -0
- package/templates/apps/mcp/CLAUDE.md +58 -25
- package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
- package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
- package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
- package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
- package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
- package/templates/apps/mcp/__tests__/index.test.ts +39 -0
- package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
- package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
- package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
- package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
- package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
- package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
- package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
- package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
- package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
- package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
- package/templates/apps/mcp/__tests__/server.test.ts +67 -14
- package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
- package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
- package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
- package/templates/apps/mcp/eslint.config.mjs +69 -0
- package/templates/apps/mcp/jest.config.cjs +13 -0
- package/templates/apps/mcp/package.json +8 -2
- package/templates/apps/mcp/src/abuse/index.ts +159 -24
- package/templates/apps/mcp/src/config/modules.ts +10 -5
- package/templates/apps/mcp/src/http-transport.ts +102 -29
- package/templates/apps/mcp/src/index.ts +73 -11
- package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
- package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
- package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
- package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
- package/templates/apps/mcp/src/lib/prisma.ts +39 -0
- package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
- package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
- package/templates/apps/mcp/src/server.ts +61 -37
- package/templates/apps/mcp/src/tools/audit.ts +26 -2
- package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
- package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
- package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
- package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
- package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
- package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
- package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
- package/templates/apps/mcp/src/tools/index.ts +9 -7
- package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
- package/templates/apps/mcp/src/tools/purchase.ts +128 -45
- package/templates/apps/mcp/src/tools/read_content.ts +20 -6
- package/templates/apps/mcp/src/tools/search_content.ts +11 -4
- package/templates/apps/web/.env.example +74 -8
- package/templates/apps/web/CLAUDE.md +46 -0
- package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
- package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
- package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
- package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
- package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
- package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
- package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
- package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
- package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
- package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
- package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
- package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
- package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
- package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
- package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
- package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
- package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +308 -65
- package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
- package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
- package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
- package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
- package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
- package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
- package/templates/apps/web/app/api/revalidate/route.ts +194 -0
- package/templates/apps/web/auth.ts +407 -8
- package/templates/apps/web/components/google-analytics.tsx +18 -1
- package/templates/apps/web/eslint.config.mjs +69 -0
- package/templates/apps/web/jest.config.cjs +15 -19
- package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
- package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
- package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
- package/templates/apps/web/lib/auth-email.ts +318 -0
- package/templates/apps/web/lib/auth-env.ts +85 -0
- package/templates/apps/web/lib/auth-routes.ts +55 -0
- package/templates/apps/web/lib/meeting-slugs.ts +53 -0
- package/templates/apps/web/lib/nav-config.ts +27 -7
- package/templates/apps/web/lib/server-api.ts +1 -1
- package/templates/apps/web/lib/site-chrome.ts +29 -0
- package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
- package/templates/apps/web/messages/en.json +216 -2
- package/templates/apps/web/next.config.mjs +20 -0
- package/templates/apps/web/package.json +8 -3
- package/templates/apps/web/sentry.client.config.ts +131 -0
- package/templates/apps/web/sentry.edge.config.ts +72 -0
- package/templates/apps/web/sentry.server.config.ts +72 -0
- package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
- package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
- package/templates/apps/web/src/lib/routes.ts +13 -18
- package/templates/brand_assets/README.md +24 -0
- package/templates/content/en/AboutPage/about.mdx +160 -15
- package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
- package/templates/content/en/ContactPage/contact.mdx +90 -13
- package/templates/content/en/FAQPage/faq.mdx +62 -22
- package/templates/content/en/HomePage/home.mdx +244 -20
- package/templates/content/en/LandingPage/blog.mdx +83 -0
- package/templates/content/en/LandingPage/features.mdx +103 -0
- package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
- package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
- package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
- package/templates/database/CHANGELOG.md +149 -0
- package/templates/database/README.md +10 -3
- package/templates/database/__tests__/appointment.db.test.ts +106 -0
- package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
- package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
- package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
- package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
- package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
- package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
- package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
- package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
- package/templates/database/__tests__/seed.test.ts +1 -1
- package/templates/database/__tests__/tenancy.test.ts +141 -0
- package/templates/database/config/schema.prisma +79 -2
- package/templates/database/content/schema.prisma +87 -11
- package/templates/database/eslint.config.mjs +69 -0
- package/templates/database/inbox/schema.prisma +452 -9
- package/templates/database/jest.config.cjs +5 -3
- package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
- package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
- package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
- package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
- package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
- package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
- package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
- package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
- package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
- package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
- package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
- package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
- package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
- package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
- package/templates/database/package.json +7 -3
- package/templates/database/seed.ts +1 -1
- package/templates/database/tenancy.ts +86 -1
- package/templates/database/tsconfig.json +1 -0
- package/templates/package.json +1 -14
- package/versions.json +52 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
- package/templates/apps/api/src/routes/content/create.ts +0 -115
- package/templates/apps/api/src/routes/content/delete.ts +0 -82
- package/templates/apps/api/src/routes/content/update.ts +0 -112
- package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
- package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
- package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
- package/templates/apps/mcp/src/tools/create_page.ts +0 -96
- package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
- package/templates/apps/mcp/src/tools/update_page.ts +0 -92
- package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
- /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
- /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
- /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
- /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the MCP server's own workspace resolution (#5876 spec D2/D4/D6/D7).
|
|
3
|
+
*
|
|
4
|
+
* Drives the REAL `initMcpPublicWorkspace()` (not a mock) so its resolution
|
|
5
|
+
* arms are exercised for real, over an injected `WorkspacePredicateClient`
|
|
6
|
+
* stub — no database, no `@working-theory/database` root (client) import,
|
|
7
|
+
* and no internal-module mock of `./prisma.js` either: the `not-required`
|
|
8
|
+
* cases prove the D6 no-op by the RETURN VALUE alone. `resolvedAdapterKind()
|
|
9
|
+
* !== "database"` is this function's first statement and returns before any
|
|
10
|
+
* `await import()` — the source itself, read at `public-workspace.ts:88-91`,
|
|
11
|
+
* is the proof that no lazy import can fire on those two paths; a real
|
|
12
|
+
* unconfigured `PrismaPg({connectionString: undefined})` wouldn't throw
|
|
13
|
+
* synchronously anyway, so mocking the module would prove nothing further.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_ORG_ID,
|
|
18
|
+
DEFAULT_WORKSPACE_ID,
|
|
19
|
+
type WorkspacePredicateClient,
|
|
20
|
+
} from "@working-theory/database/tenancy";
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
initMcpPublicWorkspace,
|
|
24
|
+
mcpPublicWorkspace,
|
|
25
|
+
resetMcpPublicWorkspace,
|
|
26
|
+
} from "../../src/lib/public-workspace.js";
|
|
27
|
+
|
|
28
|
+
const NON_SENTINEL_ORG_ID = "org_01930000-0000-7bcd-8000-000000000002";
|
|
29
|
+
|
|
30
|
+
function makeStub(
|
|
31
|
+
count: number,
|
|
32
|
+
found: { organizationId: string } | null,
|
|
33
|
+
): WorkspacePredicateClient {
|
|
34
|
+
return {
|
|
35
|
+
workspace: {
|
|
36
|
+
count: jest.fn().mockResolvedValue(count),
|
|
37
|
+
findUnique: jest.fn().mockResolvedValue(found),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe("initMcpPublicWorkspace", () => {
|
|
43
|
+
const previous = process.env.CONTENT_ADAPTER;
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
resetMcpPublicWorkspace();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
if (previous === undefined) delete process.env.CONTENT_ADAPTER;
|
|
51
|
+
else process.env.CONTENT_ADAPTER = previous;
|
|
52
|
+
resetMcpPublicWorkspace();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("resolves not-required on mdx", async () => {
|
|
56
|
+
process.env.CONTENT_ADAPTER = "mdx";
|
|
57
|
+
|
|
58
|
+
const result = await initMcpPublicWorkspace();
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({ kind: "not-required" });
|
|
61
|
+
expect(mcpPublicWorkspace()).toEqual({ kind: "not-required" });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("resolves not-required on projection", async () => {
|
|
65
|
+
process.env.CONTENT_ADAPTER = "projection";
|
|
66
|
+
|
|
67
|
+
const result = await initMcpPublicWorkspace();
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual({ kind: "not-required" });
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("CONTENT_ADAPTER=database", () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
process.env.CONTENT_ADAPTER = "database";
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("resolves { kind: resolved } when the sole workspace is the well-known default", async () => {
|
|
78
|
+
const stub = makeStub(1, { organizationId: DEFAULT_ORG_ID });
|
|
79
|
+
|
|
80
|
+
const result = await initMcpPublicWorkspace({ prisma: stub });
|
|
81
|
+
|
|
82
|
+
expect(result).toEqual({
|
|
83
|
+
kind: "resolved",
|
|
84
|
+
workspaceId: DEFAULT_WORKSPACE_ID,
|
|
85
|
+
prisma: stub,
|
|
86
|
+
});
|
|
87
|
+
expect(mcpPublicWorkspace()).toEqual(result);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("resolves { kind: unresolved } when zero workspaces exist", async () => {
|
|
91
|
+
const stub = makeStub(0, null);
|
|
92
|
+
|
|
93
|
+
const result = await initMcpPublicWorkspace({ prisma: stub });
|
|
94
|
+
|
|
95
|
+
expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("resolves { kind: unresolved } when two or more workspaces exist", async () => {
|
|
99
|
+
const stub = makeStub(2, null);
|
|
100
|
+
|
|
101
|
+
const result = await initMcpPublicWorkspace({ prisma: stub });
|
|
102
|
+
|
|
103
|
+
expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("resolves { kind: unresolved } when the sole workspace is not the well-known default", async () => {
|
|
107
|
+
const stub = makeStub(1, { organizationId: NON_SENTINEL_ORG_ID });
|
|
108
|
+
|
|
109
|
+
const result = await initMcpPublicWorkspace({ prisma: stub });
|
|
110
|
+
|
|
111
|
+
expect(result).toEqual({ kind: "unresolved", reason: "ambiguous" });
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe("mcpPublicWorkspace", () => {
|
|
117
|
+
afterEach(() => {
|
|
118
|
+
resetMcpPublicWorkspace();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("returns { kind: not-required } before initMcpPublicWorkspace has ever run", () => {
|
|
122
|
+
expect(mcpPublicWorkspace()).toEqual({ kind: "not-required" });
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -13,15 +13,85 @@
|
|
|
13
13
|
import { readFileSync } from "node:fs";
|
|
14
14
|
import { resolve, join } from "node:path";
|
|
15
15
|
|
|
16
|
+
import {
|
|
17
|
+
resetResolvedContentAdapter,
|
|
18
|
+
type ArtifactPrismaClient,
|
|
19
|
+
} from "@working-theory/content-adapters";
|
|
20
|
+
|
|
16
21
|
import {
|
|
17
22
|
ailkListPages,
|
|
18
23
|
listPagesInputSchema,
|
|
19
24
|
listPagesOutputSchema,
|
|
20
25
|
} from "../src/tools/list_pages.js";
|
|
26
|
+
import {
|
|
27
|
+
__setMcpPublicWorkspaceForTest,
|
|
28
|
+
resetMcpPublicWorkspace,
|
|
29
|
+
} from "../src/lib/public-workspace.js";
|
|
21
30
|
|
|
22
31
|
// Content root for real-filesystem tests — repo-relative
|
|
23
32
|
const REPO_ROOT = resolve(__dirname, "../../..");
|
|
24
33
|
const CONTENT_ROOT = join(REPO_ROOT, "content");
|
|
34
|
+
const WORKSPACE_ID = "wksp_00000000-0000-7000-8000-000000000001";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A structural `ArtifactPrismaClient` stub (#5876 spec D11) — a published
|
|
38
|
+
* HomePage plus a draft-only HomePage that must never surface (AC-1's
|
|
39
|
+
* published-only half is a reach test — the invariant is the adapter's).
|
|
40
|
+
*/
|
|
41
|
+
function makeArtifactPrismaClientStub(): ArtifactPrismaClient {
|
|
42
|
+
return {
|
|
43
|
+
artifact: {
|
|
44
|
+
findFirst: jest.fn(),
|
|
45
|
+
findMany: jest.fn().mockResolvedValue([
|
|
46
|
+
{
|
|
47
|
+
id: "artifact_published",
|
|
48
|
+
workspaceId: WORKSPACE_ID,
|
|
49
|
+
contentType: "text/page",
|
|
50
|
+
locale: "en",
|
|
51
|
+
slug: "home",
|
|
52
|
+
title: "Home",
|
|
53
|
+
publishedVersionId: "version_published",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "artifact_draft",
|
|
57
|
+
workspaceId: WORKSPACE_ID,
|
|
58
|
+
contentType: "text/page",
|
|
59
|
+
locale: "en",
|
|
60
|
+
slug: "draft-page",
|
|
61
|
+
title: "Draft Page",
|
|
62
|
+
publishedVersionId: null,
|
|
63
|
+
},
|
|
64
|
+
]),
|
|
65
|
+
create: jest.fn(),
|
|
66
|
+
updateMany: jest.fn(),
|
|
67
|
+
deleteMany: jest.fn(),
|
|
68
|
+
},
|
|
69
|
+
artifactVersion: {
|
|
70
|
+
findFirst: jest.fn(),
|
|
71
|
+
// Only the PUBLISHED version's row is ever returned — the adapter
|
|
72
|
+
// filters publishedIds to non-null before this query, so a draft-only
|
|
73
|
+
// artifact's version id never appears in the `id: { in: [...] }` set.
|
|
74
|
+
findMany: jest.fn().mockResolvedValue([
|
|
75
|
+
{
|
|
76
|
+
id: "version_published",
|
|
77
|
+
artifactId: "artifact_published",
|
|
78
|
+
workspaceId: WORKSPACE_ID,
|
|
79
|
+
content: {
|
|
80
|
+
type: "HomePage",
|
|
81
|
+
slug: "home",
|
|
82
|
+
title: "Home",
|
|
83
|
+
description: "The homepage.",
|
|
84
|
+
body: "# Hello",
|
|
85
|
+
},
|
|
86
|
+
isCurrent: false,
|
|
87
|
+
},
|
|
88
|
+
]),
|
|
89
|
+
create: jest.fn(),
|
|
90
|
+
updateMany: jest.fn(),
|
|
91
|
+
},
|
|
92
|
+
$transaction: jest.fn(),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
25
95
|
|
|
26
96
|
describe("listPagesInputSchema", () => {
|
|
27
97
|
it("accepts empty input (type is optional)", () => {
|
|
@@ -101,6 +171,52 @@ describe("listPagesInputSchema — Zod boundary security", () => {
|
|
|
101
171
|
});
|
|
102
172
|
});
|
|
103
173
|
|
|
174
|
+
describe("ailkListPages — CONTENT_ADAPTER=database (#5876)", () => {
|
|
175
|
+
const previous = process.env.CONTENT_ADAPTER;
|
|
176
|
+
|
|
177
|
+
beforeEach(() => {
|
|
178
|
+
process.env.CONTENT_ADAPTER = "database";
|
|
179
|
+
resetResolvedContentAdapter();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
afterEach(() => {
|
|
183
|
+
if (previous === undefined) delete process.env.CONTENT_ADAPTER;
|
|
184
|
+
else process.env.CONTENT_ADAPTER = previous;
|
|
185
|
+
resetMcpPublicWorkspace();
|
|
186
|
+
resetResolvedContentAdapter();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("AC-1: lists only published pages", async () => {
|
|
190
|
+
const stub = makeArtifactPrismaClientStub();
|
|
191
|
+
__setMcpPublicWorkspaceForTest({
|
|
192
|
+
kind: "resolved",
|
|
193
|
+
workspaceId: WORKSPACE_ID,
|
|
194
|
+
prisma: stub,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const result = await ailkListPages({ type: "HomePage" });
|
|
198
|
+
|
|
199
|
+
expect(result.pages).toEqual([
|
|
200
|
+
expect.objectContaining({ slug: "home", type: "HomePage" }),
|
|
201
|
+
]);
|
|
202
|
+
expect(stub.artifact.findMany).toHaveBeenCalledWith(
|
|
203
|
+
expect.objectContaining({
|
|
204
|
+
where: expect.objectContaining({ workspaceId: WORKSPACE_ID }),
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("AC-2: returns { pages: [] } when the workspace is unresolved, with no query", async () => {
|
|
210
|
+
const stub = makeArtifactPrismaClientStub();
|
|
211
|
+
__setMcpPublicWorkspaceForTest({ kind: "unresolved", reason: "ambiguous" });
|
|
212
|
+
|
|
213
|
+
const result = await ailkListPages({ type: "HomePage" });
|
|
214
|
+
|
|
215
|
+
expect(result.pages).toEqual([]);
|
|
216
|
+
expect(stub.artifact.findMany).not.toHaveBeenCalled();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
104
220
|
describe("architectural guard — no console calls", () => {
|
|
105
221
|
it("apps/mcp/src/tools/list_pages.ts does not call console.*", () => {
|
|
106
222
|
const src = readFileSync(
|
|
@@ -18,7 +18,13 @@ import { getMcpToolRegistrations } from "@working-theory/metadata";
|
|
|
18
18
|
|
|
19
19
|
import { createMcpServer, listRegisteredTools } from "../src/server.js";
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
// `content-write` left this list in #5853: its three tools (create_page /
|
|
22
|
+
// update_page / delete_page) were deleted with the untenanted HTTP routes they
|
|
23
|
+
// twinned, so the module now owns NO catalog tool at all and the
|
|
24
|
+
// "owns at least one" fixture check below would fail for it. The flag itself
|
|
25
|
+
// is unchanged — it still gates the tenant-guarded editor routes in apps/api
|
|
26
|
+
// (D8), which this MCP-side matrix has never covered.
|
|
27
|
+
const PAID_MODULES = ["scheduling", "signal-collectors"] as const;
|
|
22
28
|
|
|
23
29
|
function withEnabledModules<T>(enabled: string, fn: () => T): T {
|
|
24
30
|
const prior = process.env.AILK_ENABLED_MODULES;
|
|
@@ -105,10 +105,7 @@ const MCP_ONLY_TOOLS = new Set([
|
|
|
105
105
|
"ailkAudit", // audit — AEO scoring tool, no web action equivalent (I5; ADR 0013)
|
|
106
106
|
"ailkSubscribeNewsletter", // subscribe_newsletter — anonymous write via MCP; no Server Action (B7)
|
|
107
107
|
"ailkRequestCallback", // request_callback — anonymous write via MCP; no Server Action (B7)
|
|
108
|
-
"
|
|
109
|
-
"ailkCreatePage", // create_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
|
|
110
|
-
"ailkUpdatePage", // update_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
|
|
111
|
-
"ailkDeletePage", // delete_page — agent-first Pro content write; HTTP twin is apps/api, not a Server Action (D14a #55)
|
|
108
|
+
"ailkCreateProject", // create_project — agent-only project creator; no Server Action equivalent (#299)
|
|
112
109
|
"ailkGetFlow", // get_flow — agent-callable flow-engine read; no web Server Action (#3956 D7)
|
|
113
110
|
"ailkListAvailability", // list_availability — anonymous scheduling read via MCP; the booker section is a client island calling apps/api directly, not a Server Action (#4416)
|
|
114
111
|
"ailkGetEventMeta", // get_event_meta — meta-only scheduling read via MCP; a tile-rendering page is an RSC calling apps/api via api-client, not a Server Action (#4633)
|
|
@@ -246,9 +243,15 @@ describe("MCP ↔ Server-Action parity (filesystem)", () => {
|
|
|
246
243
|
// - ailkAudit: MCP-only (AEO scoring; I5; in MCP_ONLY_TOOLS allow-list).
|
|
247
244
|
// - ailkSubscribeNewsletter, ailkRequestCallback:
|
|
248
245
|
// MCP-only anonymous-write tools (B7; in MCP_ONLY_TOOLS allow-list).
|
|
249
|
-
// -
|
|
250
|
-
// - ailkCreatePage, ailkUpdatePage, ailkDeletePage:
|
|
251
|
-
//
|
|
246
|
+
// - ailkCreateProject: MCP-only agent project creator (#299; in MCP_ONLY_TOOLS).
|
|
247
|
+
// - ailkCreatePage, ailkUpdatePage, ailkDeletePage: RETIRED by #5853
|
|
248
|
+
// (D7). Their HTTP twins — the untenanted /v1/content writes — were
|
|
249
|
+
// deleted in the same PR, so keeping the tools would have left three
|
|
250
|
+
// MCP tools with no HTTP equivalent, a direct breach of the parity rule
|
|
251
|
+
// this file enforces. Their replacement is identity-BOUND
|
|
252
|
+
// (/v1/workspaces/:workspaceId/content), which the identity-blind
|
|
253
|
+
// anonymous MCP surface can never carry (D15); the authed MCP surface
|
|
254
|
+
// is #3591.
|
|
252
255
|
// - ailkGetFlow: MCP-only agent-callable flow-engine read; HTTP twin is
|
|
253
256
|
// apps/api, not a Server Action (#3956 D7) — the page fetch in
|
|
254
257
|
// apps/web/app/[locale]/flows/[slug]/page.tsx is a direct api-client
|
|
@@ -257,12 +260,12 @@ describe("MCP ↔ Server-Action parity (filesystem)", () => {
|
|
|
257
260
|
// GET /v1/schedule/availability and POST /v1/schedule/book (#4416). The
|
|
258
261
|
// `booker` section is a "use client" island that fetches those routes
|
|
259
262
|
// directly (the ContactForm/StepFlow pattern) — no Server Action, so
|
|
260
|
-
// the HTTP twin is apps/api
|
|
263
|
+
// the HTTP twin is apps/api.
|
|
261
264
|
// - ailkGetEventMeta: MCP-only meta-only scheduling read, twin of
|
|
262
265
|
// GET /v1/schedule/event-meta (#4633) — same posture as the other
|
|
263
266
|
// scheduling tools.
|
|
264
267
|
expect(actions.size).toBe(5);
|
|
265
|
-
expect(tools.size).toBe(
|
|
268
|
+
expect(tools.size).toBe(20);
|
|
266
269
|
expect(r.missingTools).toEqual([]);
|
|
267
270
|
expect(r.orphanTools).toEqual([]);
|
|
268
271
|
});
|
|
@@ -1,34 +1,101 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* `start_purchase` visibility — the commerce add-on claim matrix (#5971).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* `
|
|
4
|
+
* Supersedes S15a (#610), which asserted blanket absence while
|
|
5
|
+
* `OSS_DISABLED_TOOLS` carried the name. The gate is now the deployment's
|
|
6
|
+
* `commerce` licence claim, so the assertion is a MATRIX, not an absence:
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* re-enablement criteria in
|
|
13
|
-
* `docs/internal/roadmaps/phase-3-readiness.md`.
|
|
8
|
+
* keyless (no claim) → absent from `listRegisteredTools()` and from
|
|
9
|
+
* `createMcpServer()._registeredTools`, and calling `ailkPurchase` BY
|
|
10
|
+
* NAME returns the module-disabled body with no `checkout.createSession`
|
|
11
|
+
* call;
|
|
12
|
+
* keyed (claim held) → present in both surfaces.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
14
|
+
* Both halves are driven through `__setMcpCommerceClaimForTest()`, the same
|
|
15
|
+
* injector idiom `lib/public-workspace.ts` established (#5876). No test
|
|
16
|
+
* reaches a licence server.
|
|
16
17
|
*/
|
|
17
18
|
|
|
19
|
+
import { checkout } from "@working-theory/api-client";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
__setMcpCommerceClaimForTest,
|
|
23
|
+
resetMcpCommerceClaim,
|
|
24
|
+
} from "../src/lib/commerce-claim.js";
|
|
18
25
|
import { createMcpServer, listRegisteredTools } from "../src/server.js";
|
|
26
|
+
import { ailkPurchase } from "../src/tools/purchase.js";
|
|
27
|
+
|
|
28
|
+
jest.mock("@working-theory/api-client", () => ({
|
|
29
|
+
checkout: { createSession: jest.fn() },
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
const createSession = checkout.createSession as jest.MockedFunction<
|
|
33
|
+
typeof checkout.createSession
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
function registeredNames(): string[] {
|
|
37
|
+
const server = createMcpServer();
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- #506/#507: pre-existing test-hygiene violation
|
|
39
|
+
const tools = (server as any)._registeredTools as Record<string, unknown>;
|
|
40
|
+
return Object.keys(tools);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
jest.clearAllMocks();
|
|
45
|
+
resetMcpCommerceClaim();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("start_purchase — keyless site (no commerce claim)", () => {
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
__setMcpCommerceClaimForTest("unclaimed");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("is NOT in listRegisteredTools()", () => {
|
|
54
|
+
expect(listRegisteredTools().map((t) => t.name)).not.toContain(
|
|
55
|
+
"start_purchase",
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("is NOT in createMcpServer()._registeredTools", () => {
|
|
60
|
+
expect(registeredNames()).not.toContain("start_purchase");
|
|
61
|
+
});
|
|
19
62
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
expect(
|
|
63
|
+
it("calling it by name returns the module-disabled body and creates no session", async () => {
|
|
64
|
+
const result = await ailkPurchase({ productCode: "pro-plan" });
|
|
65
|
+
|
|
66
|
+
expect(result).toEqual({ error: "not_found", message: "Not found" });
|
|
67
|
+
expect(createSession).not.toHaveBeenCalled();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("the module-disabled body does not hint that a licence gate exists", async () => {
|
|
71
|
+
const result = await ailkPurchase({ productCode: "pro-plan" });
|
|
72
|
+
|
|
73
|
+
expect(JSON.stringify(result)).not.toMatch(/commerce|licen[cs]e|claim/i);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("start_purchase — keyed site (commerce claim held)", () => {
|
|
78
|
+
beforeEach(() => {
|
|
79
|
+
__setMcpCommerceClaimForTest("claimed");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("IS in listRegisteredTools()", () => {
|
|
83
|
+
expect(listRegisteredTools().map((t) => t.name)).toContain(
|
|
84
|
+
"start_purchase",
|
|
85
|
+
);
|
|
25
86
|
});
|
|
26
87
|
|
|
27
|
-
it("
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
88
|
+
it("IS in createMcpServer()._registeredTools", () => {
|
|
89
|
+
expect(registeredNames()).toContain("start_purchase");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("fail-closed default", () => {
|
|
94
|
+
it("the un-initialised module state is unclaimed", () => {
|
|
95
|
+
// No injector call — this is the state a boot that never resolved the
|
|
96
|
+
// licence (or resolved it to a rejection) leaves behind.
|
|
97
|
+
expect(listRegisteredTools().map((t) => t.name)).not.toContain(
|
|
98
|
+
"start_purchase",
|
|
99
|
+
);
|
|
33
100
|
});
|
|
34
101
|
});
|