create-ailk 0.18.0 → 0.19.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 (167) hide show
  1. package/LICENSE +6 -5
  2. package/NOTICE +2 -0
  3. package/dist/add-module.js +4 -1
  4. package/dist/category-profile.d.ts +10 -0
  5. package/dist/category-profile.js +21 -0
  6. package/dist/derive-architecture.d.ts +5 -1
  7. package/dist/derive-architecture.js +8 -3
  8. package/dist/lib/license-key-files.d.ts +6 -3
  9. package/dist/lib/license-key-files.js +8 -2
  10. package/dist/lib/scaffold-render-sweep.d.ts +9 -0
  11. package/dist/lib/scaffold-render-sweep.js +24 -3
  12. package/dist/license-set.d.ts +1 -1
  13. package/dist/license-set.js +13 -8
  14. package/dist/module-architecture.js +119 -0
  15. package/dist/programmatic.js +3 -0
  16. package/dist/spoke-checkout.d.ts +11 -3
  17. package/dist/spoke-checkout.js +62 -12
  18. package/dist/starter-grid.d.ts +1 -1
  19. package/dist/surfaces.js +140 -1
  20. package/dist/template-manifest.d.ts +4 -3
  21. package/dist/topology.d.ts +7 -2
  22. package/dist/topology.js +60 -3
  23. package/package.json +5 -4
  24. package/templates/.env.example +3 -3
  25. package/templates/.github/workflows/ci.yml +5 -0
  26. package/templates/apps/api/CLAUDE.md +25 -7
  27. package/templates/apps/api/LICENSE +6 -5
  28. package/templates/apps/api/NOTICE +2 -0
  29. package/templates/apps/api/jest.config.cjs +4 -29
  30. package/templates/apps/api/package.json +1 -0
  31. package/templates/apps/api/src/lib/__mocks__/prisma.ts +9 -0
  32. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +38 -0
  33. package/templates/apps/api/src/lib/deliverable-asset-store.ts +5 -4
  34. package/templates/apps/api/src/lib/stripe.ts +81 -0
  35. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +266 -39
  36. package/templates/apps/api/src/middleware/auth.ts +206 -46
  37. package/templates/apps/api/src/routes/leads/index.ts +4 -0
  38. package/templates/apps/api/src/server.ts +107 -0
  39. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +58 -0
  40. package/templates/apps/api/src/services/lead-routing.ts +30 -2
  41. package/templates/apps/api/vercel.json +2 -2
  42. package/templates/apps/mcp/.env.example +10 -0
  43. package/templates/apps/mcp/CLAUDE.md +9 -4
  44. package/templates/apps/mcp/LICENSE +6 -5
  45. package/templates/apps/mcp/NOTICE +2 -0
  46. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +198 -0
  47. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +15 -3
  48. package/templates/apps/mcp/__tests__/boot.test.ts +78 -1
  49. package/templates/apps/mcp/__tests__/http-transport.test.ts +291 -2
  50. package/templates/apps/mcp/__tests__/request_callback.test.ts +69 -7
  51. package/templates/apps/mcp/__tests__/submit_lead.test.ts +69 -8
  52. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +69 -7
  53. package/templates/apps/mcp/jest.config.cjs +3 -44
  54. package/templates/apps/mcp/src/abuse/index.ts +117 -21
  55. package/templates/apps/mcp/src/boot.ts +5 -23
  56. package/templates/apps/mcp/src/http-transport.ts +93 -73
  57. package/templates/apps/mcp/src/index.ts +2 -0
  58. package/templates/apps/mcp/src/request-context.ts +52 -0
  59. package/templates/apps/mcp/src/tools/capture_lead.ts +9 -1
  60. package/templates/apps/mcp/src/tools/request_callback.ts +9 -1
  61. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +9 -1
  62. package/templates/apps/web/LICENSE +6 -5
  63. package/templates/apps/web/NOTICE +2 -0
  64. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +3 -3
  65. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +288 -0
  66. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +568 -0
  67. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +272 -42
  68. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +11 -13
  69. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +115 -16
  70. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +305 -0
  71. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-popups.test.tsx +82 -0
  72. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +126 -0
  73. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +163 -72
  74. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +451 -0
  75. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +139 -0
  76. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +20 -390
  77. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/support.test.tsx +16 -11
  78. package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +1 -1
  79. package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +114 -50
  80. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +24 -45
  81. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +253 -0
  82. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +145 -0
  83. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +100 -0
  84. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +179 -0
  85. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +514 -0
  86. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +288 -0
  87. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +123 -0
  88. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +82 -0
  89. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +177 -0
  90. package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +172 -0
  91. package/templates/apps/web/app/[locale]/(authed)/account/billing-source.ts +147 -0
  92. package/templates/apps/web/app/[locale]/(authed)/account/components/request-quote.tsx +229 -0
  93. package/templates/apps/web/app/[locale]/(authed)/account/components/schedule-service.tsx +276 -0
  94. package/templates/apps/web/app/[locale]/(authed)/account/components/send-message.tsx +128 -0
  95. package/templates/apps/web/app/[locale]/(authed)/account/general/billing-anchor.tsx +27 -0
  96. package/templates/apps/web/app/[locale]/(authed)/account/general/deletion-request.tsx +132 -0
  97. package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +131 -0
  98. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-form.tsx +104 -0
  99. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-source.ts +73 -0
  100. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +18 -8
  101. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +4 -6
  102. package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +4 -1
  103. package/templates/apps/web/app/[locale]/(authed)/account/new-actions.ts +219 -0
  104. package/templates/apps/web/app/[locale]/(authed)/account/new-menu.tsx +340 -0
  105. package/templates/apps/web/app/[locale]/(authed)/account/new-popups.tsx +107 -0
  106. package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +57 -0
  107. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +85 -26
  108. package/templates/apps/web/app/[locale]/(authed)/account/profile-actions.ts +116 -0
  109. package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +80 -0
  110. package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +13 -354
  111. package/templates/apps/web/app/[locale]/(authed)/account/settings-frame.tsx +108 -0
  112. package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +37 -0
  113. package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +9 -10
  114. package/templates/apps/web/app/[locale]/layout.tsx +23 -4
  115. package/templates/apps/web/app/api/auth/hub/__tests__/continuation.test.ts +143 -0
  116. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +62 -10
  117. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +22 -7
  118. package/templates/apps/web/app/api/auth/hub/continuation.ts +73 -0
  119. package/templates/apps/web/app/api/auth/hub/continue/[ref]/__tests__/route.test.ts +188 -0
  120. package/templates/apps/web/app/api/auth/hub/continue/[ref]/route.ts +53 -0
  121. package/templates/apps/web/app/robots.txt/route.ts +10 -4
  122. package/templates/apps/web/auth.ts +5 -1
  123. package/templates/apps/web/lib/__tests__/account-config.test.ts +258 -46
  124. package/templates/apps/web/lib/account-config.ts +188 -52
  125. package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +143 -0
  126. package/templates/apps/web/lib/hub-session/index.ts +1 -0
  127. package/templates/apps/web/lib/hub-session/reseal.ts +90 -0
  128. package/templates/apps/web/lib/hub-session/seal.ts +13 -9
  129. package/templates/apps/web/lib/topology.generated.ts +24 -0
  130. package/templates/apps/web/messages/en.json +156 -35
  131. package/templates/database/CHANGELOG.md +21 -0
  132. package/templates/database/LICENSE +6 -5
  133. package/templates/database/NOTICE +2 -0
  134. package/templates/database/README.md +1 -1
  135. package/templates/database/__tests__/member-area.db.test.ts +15 -3
  136. package/templates/database/__tests__/member-contract-migration.test.ts +85 -0
  137. package/templates/database/__tests__/member-contract.db.test.ts +413 -0
  138. package/templates/database/__tests__/seed.test.ts +54 -2
  139. package/templates/database/inbox/schema.prisma +90 -15
  140. package/templates/database/migrations/20261012120000_member_contract/migration.sql +192 -0
  141. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +20 -0
  142. package/templates/database/package.json +3 -2
  143. package/templates/database/seed.ts +34 -2
  144. package/versions.json +36 -36
  145. package/templates/apps/api/src/__tests__/content-write-production-boot.test.ts +0 -104
  146. package/templates/apps/api/src/__tests__/cors.test.ts +0 -276
  147. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +0 -279
  148. package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +0 -122
  149. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +0 -167
  150. package/templates/apps/api/src/__tests__/root-modules.test.ts +0 -175
  151. package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +0 -117
  152. package/templates/apps/api/src/__tests__/server.test.ts +0 -591
  153. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +0 -175
  154. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +0 -293
  155. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +0 -1620
  156. package/templates/apps/api/src/routes/project-listings/__tests__/directory.test.ts +0 -528
  157. package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +0 -198
  158. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +0 -652
  159. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -408
  160. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +0 -476
  161. package/templates/apps/mcp/__tests__/audit.test.ts +0 -346
  162. package/templates/apps/mcp/__tests__/parity.test.ts +0 -325
  163. package/templates/apps/mcp/__tests__/workspace-surface.test.ts +0 -364
  164. package/templates/apps/web/app/[locale]/(authed)/account/settings/color-mode.tsx +0 -68
  165. package/templates/apps/web/app/[locale]/(authed)/account/settings/deletion-request.tsx +0 -55
  166. package/templates/apps/web/app/[locale]/(authed)/account/settings/name-form.tsx +0 -118
  167. package/templates/database/__tests__/build.test.ts +0 -131
@@ -7,6 +7,8 @@
7
7
  // `--hub-sign-in` (#7894) it reads the sealed hub-session cookie.
8
8
  // UNSERVED_MCP_TOOLS names the MCP tools /.well-known/mcp.json leaves out
9
9
  // on this topology (#8070).
10
+ // loadSpokeBilling reaches the spoke billing action only on a spoke that
11
+ // signs in through its hub and carries commerce (#8217); elsewhere, null.
10
12
 
11
13
  export const TOPOLOGY: "hub" | "spoke" = "hub";
12
14
  export const UNSERVED_MCP_TOOLS: readonly string[] = [];
@@ -34,3 +36,25 @@ export async function registerSiteStores(): Promise<void> {
34
36
  const { registerLicenseKeyStore } = await import("./license-key-store");
35
37
  await registerLicenseKeyStore();
36
38
  }
39
+
40
+ export type SpokeCard = { brand: string; last4: string; expMonth: number; expYear: number };
41
+
42
+ export type SpokeBillingRead =
43
+ | {
44
+ kind: "subscription";
45
+ status: "trialing" | "active" | "past_due" | "canceled";
46
+ planName: string | null;
47
+ paymentMethod: SpokeCard | null;
48
+ unreadable: boolean;
49
+ }
50
+ | { kind: "none"; unreadable: boolean }
51
+ | { kind: "failed" };
52
+
53
+ export type SpokeBilling = {
54
+ read(): Promise<SpokeBillingRead>;
55
+ portalUrl(): Promise<string | null>;
56
+ };
57
+
58
+ export function loadSpokeBilling(): Promise<SpokeBilling | null> {
59
+ return Promise.resolve(null);
60
+ }
@@ -70,7 +70,7 @@
70
70
  },
71
71
  "home": {
72
72
  "heading": "My Site",
73
- "emptyState": "AX-first OSS template for shipping software with API + MCP as canonical surfaces."
73
+ "emptyState": "Home page content coming soon."
74
74
  },
75
75
  "about": {
76
76
  "heading": "About",
@@ -573,6 +573,57 @@
573
573
  "account": {
574
574
  "title": "Your account",
575
575
  "description": "What changed, what's coming, and what needs you.",
576
+ "artifacts": {
577
+ "title": "Artifacts",
578
+ "lead": "Work delivered to you, newest first.",
579
+ "all": "All artifacts",
580
+ "facets": "Filter artifacts",
581
+ "reviewGroup": "Review",
582
+ "filter": "Show",
583
+ "empty": "Nothing has been delivered to you yet.",
584
+ "facetEmpty": "No artifacts here.",
585
+ "error": "We couldn't load your artifacts.",
586
+ "itemError": "We couldn't load this artifact.",
587
+ "retry": "Retry",
588
+ "back": "Artifacts",
589
+ "caption": "Artifact",
590
+ "versions": "Versions",
591
+ "version": "Version {version}",
592
+ "deliveredOn": "Delivered {date}",
593
+ "approvedOn": "Approved {date}",
594
+ "download": "Download {filename}",
595
+ "view": "View",
596
+ "columns": {
597
+ "title": "Title",
598
+ "latest": "Latest",
599
+ "delivered": "Delivered",
600
+ "review": "Review"
601
+ },
602
+ "states": {
603
+ "approved": "Approved",
604
+ "changesRequested": "Changes requested",
605
+ "ready": "Ready for review"
606
+ },
607
+ "reviewApproved": "You approved this version on {date}.",
608
+ "reviewChangesRequested": "You requested changes on {date}.",
609
+ "review": {
610
+ "heading": "Review version {version}",
611
+ "comment": "Comment",
612
+ "commentHint": "Needed to request changes.",
613
+ "approve": "Approve",
614
+ "requestChanges": "Request changes",
615
+ "submitting": "Sending…",
616
+ "sent": "Thanks — your review was sent.",
617
+ "waiting": "You requested changes. The next version will appear here.",
618
+ "errors": {
619
+ "commentRequired": "Add a comment to request changes.",
620
+ "closed": "This version can't be reviewed now. It may already be approved.",
621
+ "notLatest": "A newer version has arrived. Reload the page to review it.",
622
+ "rateLimited": "Too many requests. Try again later.",
623
+ "failed": "We couldn't send your review. Try again."
624
+ }
625
+ }
626
+ },
576
627
  "frame": {
577
628
  "home": "Home",
578
629
  "inbox": "Inbox",
@@ -590,7 +641,9 @@
590
641
  "collapseAside": "Collapse panel",
591
642
  "expandAside": "Expand panel",
592
643
  "openAside": "Open For you",
593
- "closeAside": "Close For you"
644
+ "closeAside": "Close For you",
645
+ "library": "Library",
646
+ "artifacts": "Artifacts"
594
647
  },
595
648
  "forYou": {
596
649
  "title": "For you",
@@ -617,10 +670,9 @@
617
670
  "title": "Inbox",
618
671
  "lead": "What changed, and what's coming.",
619
672
  "tabs": {
620
- "recents": "Recents",
621
- "suggested": "Suggested",
622
- "scheduled": "Scheduled",
623
- "all": "All"
673
+ "all": "All",
674
+ "purchases": "Purchases",
675
+ "scheduled": "Scheduled"
624
676
  },
625
677
  "empty": "Nothing here yet.",
626
678
  "error": "We couldn't load everything in your Inbox.",
@@ -642,60 +694,73 @@
642
694
  },
643
695
  "settings": {
644
696
  "title": "Settings",
645
- "lead": "Your profile, sign-in, notifications and privacy.",
697
+ "back": "Back",
698
+ "account": "Account",
646
699
  "general": {
647
700
  "title": "General",
648
- "lead": "Your name, email and appearance.",
701
+ "lead": "Your profile and appearance.",
649
702
  "colorMode": "Color mode",
650
703
  "light": "Light",
651
704
  "auto": "Auto",
652
705
  "dark": "Dark",
653
706
  "name": "Name",
654
- "namePlaceholder": "Your name",
707
+ "photo": "Photo",
708
+ "photoPlaceholder": "https://",
709
+ "photoHint": "A link to a square image, starting with https://. Leave it empty to show your initials.",
655
710
  "email": "Email",
656
711
  "save": "Save changes",
657
712
  "saving": "Saving…",
658
713
  "saved": "Saved.",
659
- "unsaved": "Unsaved changes",
660
- "clean": "No unsaved changes",
661
714
  "nameRequired": "Enter your name.",
662
- "saveError": "We couldn't save your name. Try again.",
663
- "manageOnHub": "Manage your account on our main site"
715
+ "saveError": "We couldn't save your changes. Try again.",
716
+ "photoInvalid": "Enter a link that starts with https://.",
717
+ "unverified": "Confirm your email address first, then try again.",
718
+ "unavailable": "Profile changes aren't available on this site yet."
664
719
  },
665
720
  "security": {
666
- "title": "Security",
667
- "lead": "The ways you can sign in to this site.",
668
- "methods": "Sign-in methods",
669
- "google": "Google",
670
- "github": "GitHub",
671
- "magicLink": "Email sign-in link",
672
- "password": "Password",
673
- "available": "Available",
674
- "none": "No sign-in method is configured.",
675
- "viaHub": "You sign in through our main site. Your sign-in methods are managed there."
721
+ "title": "Security"
676
722
  },
677
723
  "notifications": {
678
- "title": "Notifications",
679
- "lead": "How this site contacts you.",
680
- "placeholder": "Notification preferences are coming. Until then, we email you only about your account and your requests."
724
+ "title": "Notifications"
725
+ },
726
+ "signIn": {
727
+ "lead": "How you sign in to this site.",
728
+ "methods": {
729
+ "google": "Google",
730
+ "github": "GitHub",
731
+ "magicLink": "Email sign-in link",
732
+ "password": "Password"
733
+ },
734
+ "available": "Available",
735
+ "none": "No sign-in method is configured.",
736
+ "viaHub": "You sign in through {hub}.",
737
+ "viaHubUnnamed": "You sign in through our main site."
681
738
  },
682
739
  "privacy": {
683
- "title": "Privacy",
684
740
  "lead": "Cookies and your data.",
685
741
  "cookies": "Cookie settings",
686
742
  "deletion": "Request account deletion",
687
743
  "deletionLead": "Ask the site owner to delete your account and its data. We'll confirm by email.",
688
- "deletionSent": "Your deletion request is on its way. We'll confirm by email.",
744
+ "deletionSentSite": "Sent to the site operator. They'll confirm by email.",
745
+ "deletionSentHub": "Sent to the hub operator. They'll confirm by email.",
746
+ "deletionUnverified": "Confirm your email address first, then ask again.",
747
+ "deletionUnavailable": "This site can't take deletion requests right now.",
689
748
  "deletionError": "We couldn't send your request. Try again in a moment.",
690
- "deletionSubject": "Account deletion request",
691
- "deletionMessage": "Please delete my account and its data."
749
+ "deletionViaSupport": "To ask for your account to be deleted, contact support.",
750
+ "deletionSupportLink": "Contact support"
692
751
  },
693
752
  "billing": {
694
753
  "title": "Billing",
695
- "lead": "Your subscription.",
696
- "status": "Status",
697
- "accessRevokesAt": "Access ends",
698
- "manage": "Manage billing",
754
+ "lead": "Your plan and payment method.",
755
+ "currentPlan": "Current plan",
756
+ "unnamedPlan": "Your subscription",
757
+ "accessEnds": "Access ends {date}",
758
+ "card": "{brand} ending in {last4}, expires {month}/{year}",
759
+ "noCard": "No card on file",
760
+ "manage": "Manage in Stripe",
761
+ "none": "You don't have a subscription.",
762
+ "unavailable": "We couldn't load your billing details.",
763
+ "unreadable": "Some of your billing is with another Stripe account, so it isn't shown here.",
699
764
  "statuses": {
700
765
  "trialing": "Trial",
701
766
  "active": "Active",
@@ -731,7 +796,19 @@
731
796
  "offer": "Offer",
732
797
  "amount": "Amount",
733
798
  "period": "Period",
734
- "date": "Date"
799
+ "date": "Date",
800
+ "unavailable": "Not recorded"
801
+ },
802
+ "appointments": {
803
+ "heading": "My appointments",
804
+ "untitled": "Appointment",
805
+ "starts": "Starts",
806
+ "ends": "Ends",
807
+ "statuses": {
808
+ "pending": "Pending",
809
+ "confirmed": "Confirmed",
810
+ "cancelled": "Cancelled"
811
+ }
735
812
  },
736
813
  "pieces": {
737
814
  "heading": "My pieces",
@@ -750,6 +827,50 @@
750
827
  "subscription": {
751
828
  "headline": "Your payment didn't go through",
752
829
  "action": "Update payment method"
830
+ },
831
+ "new": {
832
+ "trigger": "New",
833
+ "close": "Close",
834
+ "loading": "Loading…",
835
+ "loadFailed": "This could not load.",
836
+ "retry": "Try again",
837
+ "submitting": "Sending…",
838
+ "optional": "Optional",
839
+ "actions": {
840
+ "scheduleService": "Schedule service",
841
+ "requestQuote": "Request quote",
842
+ "sendMessage": "Send message"
843
+ },
844
+ "schedule": {
845
+ "service": "Service",
846
+ "duration": "{minutes} min",
847
+ "time": "Time",
848
+ "notes": "Notes",
849
+ "submit": "Book",
850
+ "none": "No times are offered right now. You can send a message instead.",
851
+ "suggest": "Send a message",
852
+ "noSlots": "No open times in the next 14 days."
853
+ },
854
+ "quote": {
855
+ "submit": "Send request"
856
+ },
857
+ "message": {
858
+ "subject": "Subject",
859
+ "body": "Message",
860
+ "link": "Link",
861
+ "linkHelp": "Must start with https://.",
862
+ "submit": "Send"
863
+ },
864
+ "errors": {
865
+ "linkNotHttps": "A link must start with https://.",
866
+ "answersInvalid": "Some answers do not fit this form. Check them and try again.",
867
+ "requestFormOff": "Requests are switched off right now.",
868
+ "meetingTypeNotOffered": "This service is no longer offered.",
869
+ "slotUnavailable": "That time was just taken. Pick another.",
870
+ "tooManyRequests": "Too many requests. Try again later.",
871
+ "invalidInput": "Check what you entered and try again.",
872
+ "failed": "This could not be sent. Try again."
873
+ }
753
874
  }
754
875
  },
755
876
  "admin": {
@@ -1,5 +1,26 @@
1
1
  # @working-theory/database
2
2
 
3
+ ## 0.39.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#8177](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/8177) [`6fa07d3`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/6fa07d36321033e1661de34eaee8427eb7e1a6ca) Thanks [@samuelhenry](https://github.com/samuelhenry)! - The member area data model ([#8168](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/8168)): the schema and wire contract the member reads, member writes and provider routes build on. No route changes.
8
+
9
+ - `@working-theory/database` — `PieceVersion` (`piece_versions`): one delivery of a piece to its client, a private file or an https link, numbered per piece, with the client's review on it; held to its shape by CHECKs and by one composite foreign key onto `pieces(id, person_id, workspace_id)`. `Piece.approvedAt`. `Interaction.kind` (`message` · `quote` · `review`) and `payload`, and `inbound` beside `outbound`; an outbound row stays a payload-free `message`. `FlowDefinition.customersEnabled`. Migration `20261012120000_member_contract` backfills a draft `request-quote` contact magnet into every workspace, never overwriting an existing one, and `db:seed` gives the default workspace the same magnet.
10
+ - `@working-theory/validation` — the Phase 1 member contract in `member.ts`: reads, writes, write responses, the stored payloads, the vocabularies and `memberErrorCodeSchema`, and `isHttpsLink`. `requestQuoteFlowConfig`, `REQUEST_QUOTE_FLOW_SLUG`, `REQUEST_QUOTE_FLOW_TITLE`, `flowCustomersEnabledSchema` and `FLOW_CUSTOMERS_ENABLED_DEFAULT` in `flow.ts`. `plainText` is exported. `subscriptionMeResponseSchema` gains optional `planName` and `paymentMethod`; today's body still parses.
11
+ - `@working-theory/site-config` — `MeetingsSchema` gains optional `customerSlugs` (no default, duplicates refused).
12
+ - `@working-theory/ids` — the `pver_` prefix (`pieceVersion`).
13
+
14
+ - [#8235](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/8235) [`2f8a0fe`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/2f8a0fed78739b758f9db44caecbb297e009d841) Thanks [@samuelhenry](https://github.com/samuelhenry)! - `Lead.spokeCheckout` ([#8185](https://github.com/working-theory-labs/ai-launch-kit-internal/issues/8185)): a `leads.spoke_checkout BOOLEAN NOT NULL DEFAULT false` column, migration `20261013120000_lead_spoke_checkout`. The hub sets it at creation for a spoke's live paid checkout lead (a registered spoke's key, not relayed, a `cs_live_` Idempotency-Key and the checkout shape) and never updates it. Existing rows read false; there is no backfill. Apply the migration before deploying the API that writes it.
15
+
16
+ ### Patch Changes
17
+
18
+ - [#8321](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/pull/8321) [`f17db78`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/f17db78cc14671473c976d4098b1bdc001f517be) Thanks [@samuelhenry](https://github.com/samuelhenry)! - Licence metadata only: `LICENSE` is now the canonical, unmodified Apache-2.0 text, and the package ships a `NOTICE` file carrying the copyright notice (`Copyright 2026 SalesSmyth LLC`). The previous `LICENSE` appendix named a stale holder. No behavior change.
19
+
20
+ - Updated dependencies [[`6fb73eb`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/6fb73eb106836a6d201111f460de0811711a1f39), [`6fa07d3`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/6fa07d36321033e1661de34eaee8427eb7e1a6ca), [`55567c6`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/55567c678b2338523959569457499531c6aac374), [`f17db78`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/f17db78cc14671473c976d4098b1bdc001f517be), [`caeffba`](https://github.com/Working-Theory-Labs/ai-launch-kit-internal/commit/caeffba335a8c8f232cedc901babfb6674e319f5)]:
21
+ - @working-theory/validation@0.39.0
22
+ - @working-theory/ids@0.12.0
23
+
3
24
  ## 0.38.0
4
25
 
5
26
  ### Minor Changes
@@ -1,3 +1,4 @@
1
+
1
2
  Apache License
2
3
  Version 2.0, January 2004
3
4
  http://www.apache.org/licenses/
@@ -137,8 +138,8 @@
137
138
 
138
139
  6. Trademarks. This License does not grant permission to use the trade
139
140
  names, trademarks, service marks, or product names of the Licensor,
140
- except as required for describing the origin of the Work and
141
- reproducing the content of the NOTICE file.
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
142
143
 
143
144
  7. Disclaimer of Warranty. Unless required by applicable law or
144
145
  agreed to in writing, Licensor provides the Work (and each
@@ -162,7 +163,7 @@
162
163
  other commercial damages or losses), even if such Contributor
163
164
  has been advised of the possibility of such damages.
164
165
 
165
- 9. Accepting Warranty or Support. While redistributing
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
167
  the Work or Derivative Works thereof, You may choose to offer,
167
168
  and charge a fee for, acceptance of support, warranty, indemnity,
168
169
  or other liability obligations and/or rights consistent with this
@@ -171,7 +172,7 @@
171
172
  of any other Contributor, and only if You agree to indemnify,
172
173
  defend, and hold each Contributor harmless for any liability
173
174
  incurred by, or claims asserted against, such Contributor by reason
174
- of your accepting any such warranty or support.
175
+ of your accepting any such warranty or additional liability.
175
176
 
176
177
  END OF TERMS AND CONDITIONS
177
178
 
@@ -186,7 +187,7 @@
186
187
  same "printed page" as the copyright notice for easier
187
188
  identification within third-party archives.
188
189
 
189
- Copyright 2026 Tailwind AI
190
+ Copyright [yyyy] [name of copyright owner]
190
191
 
191
192
  Licensed under the Apache License, Version 2.0 (the "License");
192
193
  you may not use this file except in compliance with the License.
@@ -0,0 +1,2 @@
1
+ AI Launch Kit
2
+ Copyright 2026 SalesSmyth LLC
@@ -69,7 +69,7 @@ model RefreshToken {
69
69
 
70
70
  ```bash
71
71
  # Mobile app authenticates with token
72
- curl -H "Authorization: Bearer tok_abc123..." \
72
+ curl -H "Authorization: Bearer <access-token>" \
73
73
  https://api.customer-site.com/api/user/profile
74
74
 
75
75
  # Refresh expired access token
@@ -61,14 +61,26 @@ const describeMonorepo = existsSync(join(REPO_ROOT, "packages", "validation"))
61
61
  : describe.skip;
62
62
 
63
63
  describeMonorepo("the member-area wire contract (#7877 AC-1, no database)", () => {
64
- it("packages/validation's pieces and member schemas name no stage literal and no enum", () => {
64
+ it("packages/validation's pieces and member schemas name no stage literal and no stage enum", () => {
65
+ // #8168 narrowed this match to its intent: a stage LITERAL is a quoted
66
+ // stage name. member.ts now legitimately carries `deliveredAt`, the
67
+ // `changes-requested` review value and non-stage vocabularies
68
+ // (`z.enum(["message", "quote", "review"])`, D11) — none of them a stage.
69
+ // A stage enum would have to quote a stage name, so the quoted match
70
+ // still catches one; pieces.ts, where stages and offerings live, keeps
71
+ // the blanket no-`z.enum` rule.
65
72
  for (const file of ["pieces.ts", "member.ts"]) {
66
73
  const text = readFileSync(
67
74
  join(REPO_ROOT, "packages", "validation", "src", "schemas", file),
68
75
  "utf8",
69
76
  );
70
- expect(text).not.toMatch(/requested|in-progress|in-review|delivered|scoping|z\.enum/);
77
+ expect(text).not.toMatch(/["'`](requested|in-progress|in-review|delivered|scoping)["'`]/);
71
78
  }
79
+ const pieces = readFileSync(
80
+ join(REPO_ROOT, "packages", "validation", "src", "schemas", "pieces.ts"),
81
+ "utf8",
82
+ );
83
+ expect(pieces).not.toMatch(/z\.enum/);
72
84
  });
73
85
 
74
86
  it("the product default stage list is named by no package", () => {
@@ -197,7 +209,7 @@ describeDb("the member-area tables (#7877 D1–D3, AC-1, live DB)", () => {
197
209
 
198
210
  it.each([
199
211
  ["channel `email`", { channel: "email" }, "interactions_channel_allowed"],
200
- ["direction `inbound`", { direction: "inbound" }, "interactions_direction_allowed"],
212
+ ["direction `sideways`", { direction: "sideways" }, "interactions_direction_allowed"],
201
213
  ["a lead_ asset id", { asset_id: LEAD_ID }, "interactions_asset_id_format"],
202
214
  ["an empty subject", { subject: "" }, "interactions_subject_length"],
203
215
  ["a 10001-character body", { body: "b".repeat(10001) }, "interactions_body_length"],
@@ -0,0 +1,85 @@
1
+ /**
2
+ * @file member-contract-migration.test.ts
3
+ *
4
+ * DB-free: the `request-quote` backfill in
5
+ * migrations/20261012120000_member_contract writes its config as a JSONB
6
+ * literal, because SQL cannot import TS. This test holds that copy to the one
7
+ * TS source, `requestQuoteFlowConfig` (#8168 spec D8, D9; AC-6), and the
8
+ * block's slug and title literals to their constants — so the two can never
9
+ * drift. `member-contract.db.test.ts` re-executes the same block live.
10
+ */
11
+ import { readFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+
14
+ import {
15
+ flowConfigSchema,
16
+ REQUEST_QUOTE_FLOW_SLUG,
17
+ REQUEST_QUOTE_FLOW_TITLE,
18
+ requestQuoteFlowConfig,
19
+ } from "@working-theory/validation";
20
+
21
+ const MIGRATION_SQL = readFileSync(
22
+ join(
23
+ __dirname,
24
+ "..",
25
+ "migrations",
26
+ "20261012120000_member_contract",
27
+ "migration.sql",
28
+ ),
29
+ "utf8",
30
+ );
31
+
32
+ /** The text between the `-- BEGIN request-quote backfill` / `-- END …` markers. */
33
+ function backfillBlock(sql: string): string {
34
+ const match =
35
+ /-- BEGIN request-quote backfill\n([\s\S]*?)-- END request-quote backfill/.exec(
36
+ sql,
37
+ );
38
+ if (!match?.[1]) throw new Error("request-quote backfill markers not found");
39
+ return match[1];
40
+ }
41
+
42
+ describe("the request-quote backfill literal (#8168 D9, AC-6)", () => {
43
+ const block = backfillBlock(MIGRATION_SQL);
44
+
45
+ it("carries exactly one JSONB config literal, and it deep-equals requestQuoteFlowConfig", () => {
46
+ const literals = [...block.matchAll(/'(\{[\s\S]*?\})'::jsonb/g)];
47
+ expect(literals).toHaveLength(1);
48
+ const parsed: unknown = JSON.parse(literals[0]![1]!);
49
+ expect(parsed).toEqual(requestQuoteFlowConfig);
50
+ expect(flowConfigSchema.safeParse(parsed).success).toBe(true);
51
+ });
52
+
53
+ it("writes the constants' slug and title, kind contact, status draft, customers_enabled true", () => {
54
+ expect(block).toContain(
55
+ `SELECT w."id", w."id", '${REQUEST_QUOTE_FLOW_SLUG}',`,
56
+ );
57
+ expect(block).toContain(
58
+ `'contact', '${REQUEST_QUOTE_FLOW_TITLE}', 'draft', true, CURRENT_TIMESTAMP`,
59
+ );
60
+ expect(block).toContain(
61
+ `("site_id", "workspace_id", "slug", "config", "kind", "title", "status", "customers_enabled", "updated_at")`,
62
+ );
63
+ });
64
+
65
+ it("never overwrites a workspace's existing (site_id, slug) row", () => {
66
+ expect(block).toContain(`ON CONFLICT ("site_id", "slug") DO NOTHING;`);
67
+ expect(block).not.toMatch(/DO UPDATE/i);
68
+ });
69
+ });
70
+
71
+ describe("no round count, limit or remaining count in the migration (#8168 AC-7)", () => {
72
+ it("names no such column in piece_versions or in the pieces ADD COLUMN", () => {
73
+ const createTable = /CREATE TABLE "piece_versions" \(([\s\S]*?)\n\);/.exec(
74
+ MIGRATION_SQL,
75
+ )?.[1];
76
+ const addPieceColumn = /ALTER TABLE "pieces" ADD COLUMN[^;]*;/.exec(
77
+ MIGRATION_SQL,
78
+ )?.[0];
79
+ expect(createTable).toContain('"review_comment"');
80
+ expect(addPieceColumn).toContain('"approved_at"');
81
+ for (const ddl of [createTable ?? "", addPieceColumn ?? ""]) {
82
+ expect(ddl).not.toMatch(/round|remaining|limit|left/i);
83
+ }
84
+ });
85
+ });