create-ailk 0.17.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 (193) 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/env-catalog.js +5 -9
  9. package/dist/lib/license-key-files.d.ts +6 -3
  10. package/dist/lib/license-key-files.js +8 -2
  11. package/dist/lib/scaffold-render-sweep.d.ts +9 -0
  12. package/dist/lib/scaffold-render-sweep.js +24 -3
  13. package/dist/license-set.d.ts +1 -1
  14. package/dist/license-set.js +13 -8
  15. package/dist/module-architecture.js +119 -0
  16. package/dist/programmatic.js +3 -0
  17. package/dist/spoke-checkout.d.ts +11 -3
  18. package/dist/spoke-checkout.js +62 -12
  19. package/dist/starter-grid.d.ts +1 -1
  20. package/dist/surfaces.js +140 -1
  21. package/dist/template-manifest.d.ts +4 -3
  22. package/dist/topology.d.ts +15 -2
  23. package/dist/topology.js +83 -4
  24. package/package.json +5 -4
  25. package/templates/.env.example +3 -3
  26. package/templates/.github/workflows/ci.yml +5 -0
  27. package/templates/apps/api/CLAUDE.md +25 -7
  28. package/templates/apps/api/LICENSE +6 -5
  29. package/templates/apps/api/NOTICE +2 -0
  30. package/templates/apps/api/jest.config.cjs +4 -29
  31. package/templates/apps/api/package.json +1 -0
  32. package/templates/apps/api/src/lib/__mocks__/prisma.ts +9 -0
  33. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +38 -0
  34. package/templates/apps/api/src/lib/deliverable-asset-store.ts +5 -4
  35. package/templates/apps/api/src/lib/stripe.ts +81 -0
  36. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +266 -39
  37. package/templates/apps/api/src/middleware/auth.ts +206 -46
  38. package/templates/apps/api/src/routes/leads/index.ts +4 -0
  39. package/templates/apps/api/src/server.ts +107 -0
  40. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +58 -0
  41. package/templates/apps/api/src/services/lead-routing.ts +30 -2
  42. package/templates/apps/api/vercel.json +2 -2
  43. package/templates/apps/mcp/.env.example +10 -0
  44. package/templates/apps/mcp/CLAUDE.md +58 -7
  45. package/templates/apps/mcp/LICENSE +6 -5
  46. package/templates/apps/mcp/NOTICE +2 -0
  47. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +198 -0
  48. package/templates/apps/mcp/__tests__/app-entry.test.ts +325 -0
  49. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +15 -3
  50. package/templates/apps/mcp/__tests__/boot.test.ts +280 -0
  51. package/templates/apps/mcp/__tests__/http-transport.test.ts +291 -2
  52. package/templates/apps/mcp/__tests__/index.test.ts +86 -47
  53. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +5 -6
  54. package/templates/apps/mcp/__tests__/lib/content-root.test.ts +93 -0
  55. package/templates/apps/mcp/__tests__/request_callback.test.ts +69 -7
  56. package/templates/apps/mcp/__tests__/server.test.ts +63 -6
  57. package/templates/apps/mcp/__tests__/submit_lead.test.ts +69 -8
  58. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +69 -7
  59. package/templates/apps/mcp/jest.config.cjs +3 -44
  60. package/templates/apps/mcp/src/abuse/index.ts +117 -21
  61. package/templates/apps/mcp/src/app.ts +32 -0
  62. package/templates/apps/mcp/src/boot.ts +167 -0
  63. package/templates/apps/mcp/src/http-transport.ts +132 -88
  64. package/templates/apps/mcp/src/index.ts +19 -126
  65. package/templates/apps/mcp/src/lib/audit-single-page.ts +7 -6
  66. package/templates/apps/mcp/src/lib/content-adapter.ts +34 -5
  67. package/templates/apps/mcp/src/lib/topology.generated.ts +3 -0
  68. package/templates/apps/mcp/src/request-context.ts +52 -0
  69. package/templates/apps/mcp/src/server.ts +10 -2
  70. package/templates/apps/mcp/src/serverless-app.ts +88 -0
  71. package/templates/apps/mcp/src/tools/capture_lead.ts +9 -1
  72. package/templates/apps/mcp/src/tools/get_business_hours.ts +6 -5
  73. package/templates/apps/mcp/src/tools/get_faq.ts +6 -5
  74. package/templates/apps/mcp/src/tools/get_locations.ts +6 -5
  75. package/templates/apps/mcp/src/tools/get_offerings.ts +6 -5
  76. package/templates/apps/mcp/src/tools/get_pricing.ts +6 -5
  77. package/templates/apps/mcp/src/tools/get_site_info.ts +6 -5
  78. package/templates/apps/mcp/src/tools/list_pages.ts +8 -7
  79. package/templates/apps/mcp/src/tools/read_content.ts +7 -7
  80. package/templates/apps/mcp/src/tools/request_callback.ts +9 -1
  81. package/templates/apps/mcp/src/tools/search_content.ts +6 -5
  82. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +9 -1
  83. package/templates/apps/mcp/tsconfig.json +2 -0
  84. package/templates/apps/web/LICENSE +6 -5
  85. package/templates/apps/web/NOTICE +2 -0
  86. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +3 -3
  87. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +16 -4
  88. package/templates/apps/web/app/.well-known/mcp.json/route.ts +3 -0
  89. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +288 -0
  90. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/general.test.tsx +568 -0
  91. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/hub-mode.test.tsx +272 -42
  92. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/item.test.tsx +11 -13
  93. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/layout.test.tsx +115 -16
  94. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-menu.test.tsx +305 -0
  95. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/new-popups.test.tsx +82 -0
  96. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/notifications.test.tsx +126 -0
  97. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +163 -72
  98. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/popups.test.tsx +451 -0
  99. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/security.test.tsx +139 -0
  100. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +20 -390
  101. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/support.test.tsx +16 -11
  102. package/templates/apps/web/app/[locale]/(authed)/account/account-data.ts +1 -1
  103. package/templates/apps/web/app/[locale]/(authed)/account/account-frame.tsx +114 -50
  104. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +25 -32
  105. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/page.tsx +253 -0
  106. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/review-form.tsx +145 -0
  107. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/[pieceId]/versions/[version]/file/route.ts +100 -0
  108. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/file-route.test.ts +179 -0
  109. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/page.test.tsx +514 -0
  110. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/__tests__/review.test.tsx +288 -0
  111. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-actions.ts +123 -0
  112. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/artifact-data.ts +82 -0
  113. package/templates/apps/web/app/[locale]/(authed)/account/artifacts/page.tsx +177 -0
  114. package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +172 -0
  115. package/templates/apps/web/app/[locale]/(authed)/account/billing-source.ts +147 -0
  116. package/templates/apps/web/app/[locale]/(authed)/account/components/request-quote.tsx +229 -0
  117. package/templates/apps/web/app/[locale]/(authed)/account/components/schedule-service.tsx +276 -0
  118. package/templates/apps/web/app/[locale]/(authed)/account/components/send-message.tsx +128 -0
  119. package/templates/apps/web/app/[locale]/(authed)/account/general/billing-anchor.tsx +27 -0
  120. package/templates/apps/web/app/[locale]/(authed)/account/general/deletion-request.tsx +132 -0
  121. package/templates/apps/web/app/[locale]/(authed)/account/general/page.tsx +131 -0
  122. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-form.tsx +104 -0
  123. package/templates/apps/web/app/[locale]/(authed)/account/general/profile-source.ts +73 -0
  124. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/__tests__/route.test.ts +18 -8
  125. package/templates/apps/web/app/[locale]/(authed)/account/inbox/message/[id]/file/route.ts +4 -6
  126. package/templates/apps/web/app/[locale]/(authed)/account/layout.tsx +4 -1
  127. package/templates/apps/web/app/[locale]/(authed)/account/new-actions.ts +219 -0
  128. package/templates/apps/web/app/[locale]/(authed)/account/new-menu.tsx +340 -0
  129. package/templates/apps/web/app/[locale]/(authed)/account/new-popups.tsx +107 -0
  130. package/templates/apps/web/app/[locale]/(authed)/account/notifications/page.tsx +57 -0
  131. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +85 -26
  132. package/templates/apps/web/app/[locale]/(authed)/account/profile-actions.ts +116 -0
  133. package/templates/apps/web/app/[locale]/(authed)/account/security/page.tsx +80 -0
  134. package/templates/apps/web/app/[locale]/(authed)/account/settings/page.tsx +13 -354
  135. package/templates/apps/web/app/[locale]/(authed)/account/settings-frame.tsx +108 -0
  136. package/templates/apps/web/app/[locale]/(authed)/account/sign-in-method.ts +37 -0
  137. package/templates/apps/web/app/[locale]/(authed)/account/support-actions.ts +9 -10
  138. package/templates/apps/web/app/[locale]/layout.tsx +23 -4
  139. package/templates/apps/web/app/api/auth/hub/__tests__/continuation.test.ts +143 -0
  140. package/templates/apps/web/app/api/auth/hub/authorize/__tests__/route.test.ts +62 -10
  141. package/templates/apps/web/app/api/auth/hub/authorize/route.ts +22 -7
  142. package/templates/apps/web/app/api/auth/hub/continuation.ts +73 -0
  143. package/templates/apps/web/app/api/auth/hub/continue/[ref]/__tests__/route.test.ts +188 -0
  144. package/templates/apps/web/app/api/auth/hub/continue/[ref]/route.ts +53 -0
  145. package/templates/apps/web/app/robots.txt/route.ts +10 -4
  146. package/templates/apps/web/auth.ts +5 -1
  147. package/templates/apps/web/lib/__tests__/account-config.test.ts +258 -46
  148. package/templates/apps/web/lib/__tests__/site-brand.test.ts +42 -0
  149. package/templates/apps/web/lib/account-config.ts +188 -52
  150. package/templates/apps/web/lib/hub-session/__tests__/hub-session.test.ts +143 -0
  151. package/templates/apps/web/lib/hub-session/index.ts +1 -0
  152. package/templates/apps/web/lib/hub-session/reseal.ts +90 -0
  153. package/templates/apps/web/lib/hub-session/seal.ts +13 -9
  154. package/templates/apps/web/lib/site-brand.tsx +22 -6
  155. package/templates/apps/web/lib/topology.generated.ts +27 -0
  156. package/templates/apps/web/messages/en.json +156 -35
  157. package/templates/database/CHANGELOG.md +21 -0
  158. package/templates/database/LICENSE +6 -5
  159. package/templates/database/NOTICE +2 -0
  160. package/templates/database/README.md +1 -1
  161. package/templates/database/__tests__/member-area.db.test.ts +15 -3
  162. package/templates/database/__tests__/member-contract-migration.test.ts +85 -0
  163. package/templates/database/__tests__/member-contract.db.test.ts +413 -0
  164. package/templates/database/__tests__/seed.test.ts +54 -2
  165. package/templates/database/inbox/schema.prisma +90 -15
  166. package/templates/database/migrations/20261012120000_member_contract/migration.sql +192 -0
  167. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +20 -0
  168. package/templates/database/package.json +3 -2
  169. package/templates/database/seed.ts +34 -2
  170. package/versions.json +36 -36
  171. package/templates/apps/api/src/__tests__/content-write-production-boot.test.ts +0 -104
  172. package/templates/apps/api/src/__tests__/cors.test.ts +0 -276
  173. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +0 -279
  174. package/templates/apps/api/src/__tests__/openapi-keyless-hub.test.ts +0 -122
  175. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +0 -167
  176. package/templates/apps/api/src/__tests__/root-modules.test.ts +0 -175
  177. package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +0 -117
  178. package/templates/apps/api/src/__tests__/server.test.ts +0 -591
  179. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +0 -175
  180. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +0 -293
  181. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +0 -1620
  182. package/templates/apps/api/src/routes/project-listings/__tests__/directory.test.ts +0 -528
  183. package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +0 -198
  184. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +0 -652
  185. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -408
  186. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +0 -476
  187. package/templates/apps/mcp/__tests__/audit.test.ts +0 -346
  188. package/templates/apps/mcp/__tests__/parity.test.ts +0 -325
  189. package/templates/apps/mcp/__tests__/workspace-surface.test.ts +0 -364
  190. package/templates/apps/web/app/[locale]/(authed)/account/settings/color-mode.tsx +0 -68
  191. package/templates/apps/web/app/[locale]/(authed)/account/settings/deletion-request.tsx +0 -55
  192. package/templates/apps/web/app/[locale]/(authed)/account/settings/name-form.tsx +0 -118
  193. package/templates/database/__tests__/build.test.ts +0 -131
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * The spoke checkout slice (#7838) — what a spoke carrying `commerce` gets in
3
- * `apps/web`: the buy route and the Stripe webhook route (the only two files
4
- * allowed to import the checkout package, #7873), the site's own
5
- * `lib/checkout-config.ts`, and the thank-you and unavailable return pages.
3
+ * `apps/web`: the buy route and the Stripe webhook route (allowed to import
4
+ * the checkout package, #7873), the site's own `lib/checkout-config.ts`, and
5
+ * the thank-you and unavailable return pages. A spoke that signs in through
6
+ * its hub also gets the billing action, the third file allowed to import the
7
+ * package, and the web seam render that reaches it (#8217 D6–D8).
6
8
  *
7
9
  * Every guarantee lives in `@working-theory/spoke-checkout` and arrives by a
8
10
  * package bump; these files are thin and, apart from the config's one stamp
@@ -23,7 +25,7 @@ import { SPOKE_CHECKOUT_ROUTE_FILES } from "./derive-architecture.js";
23
25
  import { ENV_CATALOG } from "./lib/env-catalog.js";
24
26
  import { diffTwoStrings, } from "./lib/extract-module-bundle.js";
25
27
  import { substituteWorkspaceVersions, } from "./substitute.js";
26
- import { TopologyError } from "./topology.js";
28
+ import { TopologyError, WEB_SEAM_PATH, renderWebSeam, } from "./topology.js";
27
29
  /** The checkout package a spoke's two route files import. */
28
30
  export const SPOKE_CHECKOUT_PACKAGE = "@working-theory/spoke-checkout";
29
31
  const API_CLIENT_PACKAGE = "@working-theory/api-client";
@@ -131,6 +133,33 @@ const WEBHOOK_ROUTE = renderRoute({
131
133
  waiver: "the endpoint URL registered in Stripe, not a versioned API contract",
132
134
  exportLine: "export const { POST } = createSpokeCheckout(checkoutConfig).webhook;",
133
135
  });
136
+ /**
137
+ * The spoke billing action (#8217 D6): wiring only, byte-identical across
138
+ * sites, so every guarantee arrives by a package bump. A plain server module
139
+ * (C2): its two exports take no argument, and the hub's read is its only
140
+ * source of customer ids.
141
+ */
142
+ const BILLING_ACTION = [
143
+ "// The spoke billing action — GENERATED by create-ailk (#8217). Do not edit:",
144
+ "// every guarantee lives in the spoke-checkout package and arrives by a",
145
+ "// package bump. A plain server module, not a Server Action file: neither",
146
+ "// export takes an argument, and the hub's GET /v1/member/billing is the only",
147
+ "// source of customer ids. billing-source.ts reaches it through",
148
+ "// lib/topology.generated.ts and never imports the package.",
149
+ `import { member } from "${API_CLIENT_PACKAGE}";`,
150
+ `import { createSpokeCheckout } from "${SPOKE_CHECKOUT_PACKAGE}";`,
151
+ "",
152
+ `import { checkoutConfig } from "../../../../../lib/checkout-config";`,
153
+ `import { forwardedCookieInit } from "../../../../../lib/server-api";`,
154
+ "",
155
+ "const billing = createSpokeCheckout(checkoutConfig).billing.forMember(",
156
+ " async () => member.billing(await forwardedCookieInit()),",
157
+ ");",
158
+ "",
159
+ "export const readSpokeBilling = billing.read;",
160
+ "export const spokeBillingPortalUrl = billing.portalUrl;",
161
+ "",
162
+ ].join("\n");
134
163
  /** The site's checkout data (D5). Only the `const SITE` line varies by site. */
135
164
  function renderConfig(site) {
136
165
  return [
@@ -138,9 +167,10 @@ function renderConfig(site) {
138
167
  "//",
139
168
  "// Written once by create-ailk and yours from then on: create-ailk never",
140
169
  "// rewrites it. The buy route and the Stripe webhook route build their",
141
- "// handlers from it. Those two are the only files allowed to import the",
142
- "// checkout package (architecture.yaml), so this file never imports it,",
143
- "// stripe-client or the stripe SDK. Keep it to data and the fulfil callback.",
170
+ "// handlers from it, and so does the billing action on a spoke that signs",
171
+ "// in through its hub. Only those files may import the checkout package",
172
+ "// (architecture.yaml), so this file never imports it, stripe-client or the",
173
+ "// stripe SDK. Keep it to data and the fulfil callback.",
144
174
  "//",
145
175
  "// site Stamped on every Checkout Session as metadata.site. This site's",
146
176
  "// webhook acts only on its own stamp, and the hub ignores any stamp",
@@ -462,15 +492,20 @@ jobs:
462
492
  /**
463
493
  * The slice for `site`, in order: the two route files, the config, the two
464
494
  * pages (D3), then the checkout e2e workflow (#7839 D8), which does not
465
- * depend on `site`. Throws `TopologyError` for an invalid stamp (SEC-7838-10).
495
+ * depend on `site`. With `signIn: "hub"` the billing action is the third
496
+ * pair, so the first three paths are `SPOKE_CHECKOUT_ROUTE_FILES` (#8217 D8).
497
+ * Throws `TopologyError` for an invalid stamp (SEC-7838-10).
466
498
  */
467
- export function renderSpokeCheckoutFiles(site) {
499
+ export function renderSpokeCheckoutFiles(site, opts = {}) {
468
500
  assertSite(site);
469
- const [buyPath, webhookPath] = SPOKE_CHECKOUT_ROUTE_FILES;
501
+ const [buyPath, webhookPath, billingActionPath] = SPOKE_CHECKOUT_ROUTE_FILES;
470
502
  const [thankYouPath, unavailablePath] = SPOKE_CHECKOUT_PAGE_FILES;
471
503
  return [
472
504
  [buyPath, BUY_ROUTE],
473
505
  [webhookPath, WEBHOOK_ROUTE],
506
+ ...(opts.signIn === "hub"
507
+ ? [[billingActionPath, BILLING_ACTION]]
508
+ : []),
474
509
  [SPOKE_CHECKOUT_CONFIG_PATH, renderConfig(site)],
475
510
  [thankYouPath, THANK_YOU_PAGE],
476
511
  [unavailablePath, UNAVAILABLE_PAGE],
@@ -516,7 +551,9 @@ function isOlderPin(current, wanted) {
516
551
  /**
517
552
  * Write the slice into `targetDir` (D3). A file that already exists is left
518
553
  * byte-for-byte and reported `skipped` — the config is the site's once
519
- * written. Adds the package to `apps/web/package.json` when absent, and
554
+ * written. With `signIn: "hub"` (#8217 D8) the slice carries the billing
555
+ * action, and the web seam — generated, so always rewritten — becomes the
556
+ * billing render: reported `patched` when it changed, not at all when equal. Adds the package to `apps/web/package.json` when absent, and
520
557
  * raises a plain pin older than the one it writes (#7839): the generated
521
558
  * checkout e2e workflow runs a command a `^0.2.0` pin never reaches. Any
522
559
  * other range — newer, `workspace:`, a tag — is the site's and is kept.
@@ -524,7 +561,7 @@ function isOlderPin(current, wanted) {
524
561
  * missing or malformed `apps/web/package.json`) throws before the first write.
525
562
  */
526
563
  export function writeSpokeCheckoutSlice(targetDir, opts) {
527
- const files = renderSpokeCheckoutFiles(opts.site);
564
+ const files = renderSpokeCheckoutFiles(opts.site, { signIn: opts.signIn });
528
565
  const range = JSON.parse(substituteWorkspaceVersions(`{"${SPOKE_CHECKOUT_PACKAGE}": "workspace:*"}`, opts.versions));
529
566
  const version = range[SPOKE_CHECKOUT_PACKAGE];
530
567
  const pkgPath = join(targetDir, WEB_PACKAGE_JSON);
@@ -541,6 +578,19 @@ export function writeSpokeCheckoutSlice(targetDir, opts) {
541
578
  writeFileSync(abs, text);
542
579
  rows.push({ path, action: "written" });
543
580
  }
581
+ if (opts.signIn === "hub") {
582
+ const seamPath = join(targetDir, WEB_SEAM_PATH);
583
+ const seamBefore = existsSync(seamPath) ? readFileSync(seamPath, "utf8") : "";
584
+ const seam = renderWebSeam("spoke", "hub", { spokeBilling: true });
585
+ if (seamBefore !== seam) {
586
+ writeFileSync(seamPath, seam);
587
+ rows.push({
588
+ path: WEB_SEAM_PATH,
589
+ action: "patched",
590
+ change: diffTwoStrings(WEB_SEAM_PATH, seamBefore, seam, true),
591
+ });
592
+ }
593
+ }
544
594
  const deps = pkg.dependencies ?? {};
545
595
  const current = Object.prototype.hasOwnProperty.call(deps, SPOKE_CHECKOUT_PACKAGE)
546
596
  ? deps[SPOKE_CHECKOUT_PACKAGE]
@@ -14,7 +14,7 @@
14
14
  * lives in `template-manifest.ts`, whose `resolveTemplate` is the one reader
15
15
  * that yields a template's roster. Nothing but those two files imports this one.
16
16
  */
17
- import type { CategorySlug } from "@working-theory/schema/categories";
17
+ import type { CategorySlug } from "./category-profile.js";
18
18
  import type { ModuleName } from "./parse-args.js";
19
19
  import type { TemplateLayout } from "./site-chrome.js";
20
20
  import type { StarterDesign, StarterShape, TemplateRosterRow } from "./template-manifest.js";
package/dist/surfaces.js CHANGED
@@ -630,6 +630,23 @@ export const ROUTE_SINCE = {
630
630
  // new, so it attaches at today's fixed-group version, the same rule #7430
631
631
  // applied above.
632
632
  "(authed)/account/inbox/message/[id]/file": "0.37.0",
633
+ // #8232 — User settings' two pages, General and Billing (the old
634
+ // `(authed)/account/settings` now redirects to General). Genuinely new, so
635
+ // they attach at today's fixed-group version, the same rule #7430 applied.
636
+ "(authed)/account/general": "0.38.0",
637
+ "(authed)/account/billing": "0.38.0",
638
+ // #8339 — User settings' two new tabs, Security and Notifications
639
+ // (General and Billing now render through the shared account-settings
640
+ // composition). Genuinely new, so they attach at today's fixed-group
641
+ // version, the same rule #7430/#8232 applied.
642
+ "(authed)/account/security": "0.38.0",
643
+ "(authed)/account/notifications": "0.38.0",
644
+ // #8234 — the member Artifacts pages and a version's file route. Genuinely
645
+ // new, so they attach at today's fixed-group version, the same rule #7430
646
+ // applied.
647
+ "(authed)/account/artifacts": "0.38.0",
648
+ "(authed)/account/artifacts/[pieceId]": "0.38.0",
649
+ "(authed)/account/artifacts/[pieceId]/versions/[version]/file": "0.38.0",
633
650
  // The leads inbox (#5678). The ROUTE ships in every scaffold, Free
634
651
  // included — lead capture is a never-gate surface, so reading a lead back
635
652
  // is one too. Whether it shows a ROW is a separate question with two
@@ -759,24 +776,139 @@ export const ROUTE_DEPENDENCIES = {
759
776
  `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
760
777
  `${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
761
778
  `${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
779
+ // #8233 C9 — the New menu's files beside account-config.ts: new-popups.tsx
780
+ // binds the config's actions to the popups, which import the menu and its
781
+ // server actions. (account-config.ts itself imports none of them — a
782
+ // spoke without sign-in drops the `(authed)` group and keeps the config.)
783
+ `${ROUTE_ROOT}/(authed)/account/new-menu.tsx`,
784
+ `${ROUTE_ROOT}/(authed)/account/new-actions.ts`,
785
+ `${ROUTE_ROOT}/(authed)/account/new-popups.tsx`,
786
+ `${ROUTE_ROOT}/(authed)/account/components/schedule-service.tsx`,
787
+ `${ROUTE_ROOT}/(authed)/account/components/request-quote.tsx`,
788
+ `${ROUTE_ROOT}/(authed)/account/components/send-message.tsx`,
762
789
  ],
763
790
  // #7920 — the member file read re-serves the hub's bytes with the
764
791
  // credential `forwardedCookieInit` returns.
765
792
  "(authed)/account/inbox/message/[id]/file": ["apps/web/lib/server-api.ts"],
793
+ // #8232 D13 — the settings route is a redirect now; what it still imports
794
+ // from outside itself is `theme-modes.ts`'s theme reader and the brand file
795
+ // that reader imports.
766
796
  "(authed)/account/settings": [
797
+ "apps/web/lib/site-brand.tsx",
798
+ "apps/web/lib/site-theme.ts",
799
+ ],
800
+ // #8232 D13 — General and Billing import files in their parent's directory
801
+ // too. Each listed parent file is new in #8232 or unchanged by it, so a
802
+ // scaffold that already has one keeps a copy that still compiles; the
803
+ // settings components General changed moved into `general/` instead (C3).
804
+ // `profile-actions.ts` imports `lib/hub-session/reseal.ts` by its own path,
805
+ // since a scaffold's existing `lib/hub-session/index.ts` is never updated.
806
+ //
807
+ // #8339 D11 — `settings/color-mode.tsx` drops: General's own color-mode
808
+ // control is now the shared `AccountSettings` composition's
809
+ // (@working-theory/templates/account). `sign-in-method.ts` stays: General no
810
+ // longer renders the Sign-in section itself (moved to Security), but it
811
+ // still imports `sign-in-method.ts` transitively through
812
+ // `profile-source.ts`'s `via` derivation.
813
+ "(authed)/account/general": [
767
814
  "apps/web/lib/account-config.ts",
768
815
  "apps/web/lib/auth-env.ts",
769
816
  "apps/web/lib/server-api.ts",
770
817
  "apps/web/lib/site-brand.tsx",
771
818
  "apps/web/lib/site-theme.ts",
819
+ "apps/web/lib/hub-session/reseal.ts",
772
820
  `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
773
- `${ROUTE_ROOT}/(authed)/account/support-actions.ts`,
821
+ `${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
822
+ `${ROUTE_ROOT}/(authed)/account/sign-in-method.ts`,
823
+ `${ROUTE_ROOT}/(authed)/account/profile-actions.ts`,
824
+ `${ROUTE_ROOT}/(authed)/account/settings/theme-modes.ts`,
825
+ // #8233 C9 — the New menu's files beside account-config.ts: new-popups.tsx
826
+ // binds the config's actions to the popups, which import the menu and its
827
+ // server actions. (account-config.ts itself imports none of them — a
828
+ // spoke without sign-in drops the `(authed)` group and keeps the config.)
829
+ `${ROUTE_ROOT}/(authed)/account/new-menu.tsx`,
830
+ `${ROUTE_ROOT}/(authed)/account/new-actions.ts`,
831
+ `${ROUTE_ROOT}/(authed)/account/new-popups.tsx`,
832
+ `${ROUTE_ROOT}/(authed)/account/components/schedule-service.tsx`,
833
+ `${ROUTE_ROOT}/(authed)/account/components/request-quote.tsx`,
834
+ `${ROUTE_ROOT}/(authed)/account/components/send-message.tsx`,
835
+ ],
836
+ // #8339 D11 — Security holds the former Sign-in section, moved verbatim
837
+ // from General: `sign-in-method.ts` and what it imports.
838
+ "(authed)/account/security": [
839
+ "apps/web/lib/account-config.ts",
840
+ "apps/web/lib/auth-env.ts",
841
+ "apps/web/lib/server-api.ts",
842
+ "apps/web/lib/hub-session/config.ts",
843
+ `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
844
+ `${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
845
+ `${ROUTE_ROOT}/(authed)/account/sign-in-method.ts`,
846
+ ],
847
+ // #8339 D11 — Notifications holds the former Privacy section's
848
+ // cookie-settings link, moved verbatim from General.
849
+ "(authed)/account/notifications": [
850
+ "apps/web/lib/account-config.ts",
851
+ "apps/web/lib/server-api.ts",
852
+ `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
853
+ `${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
854
+ ],
855
+ "(authed)/account/billing": [
856
+ "apps/web/lib/account-config.ts",
857
+ "apps/web/lib/server-api.ts",
858
+ `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
859
+ `${ROUTE_ROOT}/(authed)/account/actions.ts`,
860
+ `${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
861
+ `${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
862
+ `${ROUTE_ROOT}/(authed)/account/settings-frame.tsx`,
863
+ `${ROUTE_ROOT}/(authed)/account/billing-source.ts`,
864
+ // #8233 C9 — the New menu's files beside account-config.ts: new-popups.tsx
865
+ // binds the config's actions to the popups, which import the menu and its
866
+ // server actions. (account-config.ts itself imports none of them — a
867
+ // spoke without sign-in drops the `(authed)` group and keeps the config.)
868
+ `${ROUTE_ROOT}/(authed)/account/new-menu.tsx`,
869
+ `${ROUTE_ROOT}/(authed)/account/new-actions.ts`,
870
+ `${ROUTE_ROOT}/(authed)/account/new-popups.tsx`,
871
+ `${ROUTE_ROOT}/(authed)/account/components/schedule-service.tsx`,
872
+ `${ROUTE_ROOT}/(authed)/account/components/request-quote.tsx`,
873
+ `${ROUTE_ROOT}/(authed)/account/components/send-message.tsx`,
874
+ ],
875
+ // #8234 — the Artifacts pages import files in their parent's directory, as
876
+ // #7430's children do; the artifact page also imports its own parent's
877
+ // `artifact-data.ts` and `artifact-actions.ts`, which an older synced
878
+ // `artifacts/` might lack. The file route re-serves the hub's bytes with
879
+ // the credential `forwardedCookieInit` returns.
880
+ "(authed)/account/artifacts": [
881
+ "apps/web/lib/account-config.ts",
882
+ "apps/web/lib/server-api.ts",
883
+ `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
884
+ `${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
885
+ `${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
886
+ ],
887
+ "(authed)/account/artifacts/[pieceId]": [
888
+ "apps/web/lib/account-config.ts",
889
+ "apps/web/lib/server-api.ts",
890
+ `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
891
+ `${ROUTE_ROOT}/(authed)/account/error-row.tsx`,
892
+ `${ROUTE_ROOT}/(authed)/account/retry-button.tsx`,
893
+ `${ROUTE_ROOT}/(authed)/account/artifacts/artifact-data.ts`,
894
+ `${ROUTE_ROOT}/(authed)/account/artifacts/artifact-actions.ts`,
774
895
  ],
896
+ "(authed)/account/artifacts/[pieceId]/versions/[version]/file": ["apps/web/lib/server-api.ts"],
775
897
  "(authed)/account/support": [
776
898
  "apps/web/lib/account-config.ts",
777
899
  "apps/web/lib/server-api.ts",
778
900
  `${ROUTE_ROOT}/(authed)/account/account-data.ts`,
779
901
  `${ROUTE_ROOT}/(authed)/account/support-actions.ts`,
902
+ // #8233 C9 — the New menu's files beside account-config.ts: new-popups.tsx
903
+ // binds the config's actions to the popups, which import the menu and its
904
+ // server actions. (account-config.ts itself imports none of them — a
905
+ // spoke without sign-in drops the `(authed)` group and keeps the config.)
906
+ `${ROUTE_ROOT}/(authed)/account/new-menu.tsx`,
907
+ `${ROUTE_ROOT}/(authed)/account/new-actions.ts`,
908
+ `${ROUTE_ROOT}/(authed)/account/new-popups.tsx`,
909
+ `${ROUTE_ROOT}/(authed)/account/components/schedule-service.tsx`,
910
+ `${ROUTE_ROOT}/(authed)/account/components/request-quote.tsx`,
911
+ `${ROUTE_ROOT}/(authed)/account/components/send-message.tsx`,
780
912
  ],
781
913
  "(authed)/admin": [
782
914
  "apps/web/lib/server-api.ts",
@@ -832,6 +964,13 @@ export const ROUTE_MESSAGE_NAMESPACES = {
832
964
  "(authed)/account/inbox/[kind]/[id]": ["account"],
833
965
  "(authed)/account/settings": ["account"],
834
966
  "(authed)/account/support": ["account"],
967
+ "(authed)/account/general": ["account"],
968
+ "(authed)/account/security": ["account"],
969
+ "(authed)/account/notifications": ["account"],
970
+ "(authed)/account/billing": ["account"],
971
+ // #8234 — the two Artifacts pages; the file route renders no string.
972
+ "(authed)/account/artifacts": ["account"],
973
+ "(authed)/account/artifacts/[pieceId]": ["account"],
835
974
  "(authed)/admin": ["admin"],
836
975
  "(authed)/leads": ["leadsInbox"],
837
976
  // #5977 — every string on both editor routes comes from the `edit`
@@ -14,7 +14,7 @@
14
14
  * package already twice records that a fixed template FILE is the drift a
15
15
  * registry removes.
16
16
  */
17
- import type { CategorySlug } from "@working-theory/schema/categories";
17
+ import type { CategorySlug } from "./category-profile.js";
18
18
  import { type PageRosterRow } from "./module-architecture.js";
19
19
  import type { ModuleName } from "./parse-args.js";
20
20
  import type { TemplateLayout } from "./site-chrome.js";
@@ -66,8 +66,9 @@ export interface TemplateManifest {
66
66
  * The business categories this template is FOR — one or more LEAF slugs from
67
67
  * the site-category registry (`@working-theory/schema/categories`, #5830),
68
68
  * shared with content's frontmatter `categories`. Required, and typed against
69
- * `CategorySlug`, for the same reason `requires` is typed against
70
- * `ModuleName`: an unrecognized name fails type-check rather than a runtime
69
+ * `CategorySlug` — create-ailk's drift-tested copy of the registry's leaves
70
+ * (`category-profile.ts`, #8412) — for the same reason `requires` is typed
71
+ * against `ModuleName`: an unrecognized name fails type-check rather than a runtime
71
72
  * validation, and every template must be findable under at least one
72
73
  * category on the hub's "Browse by business" band.
73
74
  *
@@ -122,13 +122,26 @@ export declare function isSpokeExcluded(path: string, opts: IsSpokeExcludedOptio
122
122
  * install passes no remap.
123
123
  */
124
124
  export declare function spokeDestination(destination: string): string;
125
+ /**
126
+ * The MCP tools a spoke does not serve (#8070 D7). `create_project` spawns
127
+ * `packages/create-ailk/dist/cli.js` from the MCP host's working directory,
128
+ * which a spoke does not carry. Both seams carry the list, so the MCP
129
+ * server's registration and the web discovery manifest agree; a hub's is
130
+ * empty.
131
+ */
132
+ export declare const SPOKE_UNSERVED_MCP_TOOLS: readonly string[];
125
133
  /**
126
134
  * `apps/web/lib/topology.generated.ts` for `topology` (D5) and, on a spoke,
127
135
  * its sign-in mode (#7894 D8). The hub render loads `auth.ts` only when a
128
136
  * session is read, so a module that imports this seam for a constant never
129
- * loads Better Auth.
137
+ * loads Better Auth. `opts.spokeBilling` (#8217 D7) makes `loadSpokeBilling`
138
+ * reach the spoke billing action — only on a spoke that signs in through its
139
+ * hub, the one render whose account area can call it.
140
+ * @throws TopologyError for `spokeBilling` on a hub or a default-mode spoke.
130
141
  */
131
- export declare function renderWebSeam(topology: Topology, signIn?: SignInMode): string;
142
+ export declare function renderWebSeam(topology: Topology, signIn?: SignInMode, opts?: {
143
+ spokeBilling?: boolean;
144
+ }): string;
132
145
  /**
133
146
  * `apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx` (#7894 D8).
134
147
  * The hub render is the `(authed)` layout's no-session branch, moved; the
package/dist/topology.js CHANGED
@@ -269,13 +269,32 @@ const WEB_SEAM_HEADER = `// topology.generated.ts — how this site reads a sess
269
269
  // every scaffold rewrites it. A hub reads the session through Better Auth.
270
270
  // A spoke (\`--topology spoke\`) has no database of its own: by default it
271
271
  // has no sign-in, so its render reads no session and imports nothing; with
272
- // \`--hub-sign-in\` (#7894) it reads the sealed hub-session cookie.`;
272
+ // \`--hub-sign-in\` (#7894) it reads the sealed hub-session cookie.
273
+ // UNSERVED_MCP_TOOLS names the MCP tools /.well-known/mcp.json leaves out
274
+ // on this topology (#8070).
275
+ // loadSpokeBilling reaches the spoke billing action only on a spoke that
276
+ // signs in through its hub and carries commerce (#8217); elsewhere, null.`;
273
277
  const MCP_SEAM_HEADER = `// topology.generated.ts — how this MCP server resolves its workspace store.
274
278
  //
275
279
  // GENERATED by create-ailk for this site's topology (#7426). Do not edit:
276
280
  // every scaffold rewrites it. A hub resolves its workspace through the
277
281
  // database; a spoke (\`--topology spoke\`) has none, so its content tools
278
- // read the spoke's own MDX and its writes go to the hub at AILK_API_URL.`;
282
+ // read the spoke's own MDX and its writes go to the hub at AILK_API_URL.
283
+ // UNSERVED_MCP_TOOLS names the tools this server does not register on this
284
+ // topology (#8070).`;
285
+ /**
286
+ * The MCP tools a spoke does not serve (#8070 D7). `create_project` spawns
287
+ * `packages/create-ailk/dist/cli.js` from the MCP host's working directory,
288
+ * which a spoke does not carry. Both seams carry the list, so the MCP
289
+ * server's registration and the web discovery manifest agree; a hub's is
290
+ * empty.
291
+ */
292
+ export const SPOKE_UNSERVED_MCP_TOOLS = ["create_project"];
293
+ /** The `UNSERVED_MCP_TOOLS` export every web and mcp seam render carries. */
294
+ function unservedToolsExport(topology) {
295
+ const list = topology === "spoke" ? SPOKE_UNSERVED_MCP_TOOLS : [];
296
+ return `export const UNSERVED_MCP_TOOLS: readonly string[] = ${JSON.stringify(list)};`;
297
+ }
279
298
  const SITE_SESSION_TYPE = `export type SiteSession = {
280
299
  user: {
281
300
  name?: string | null;
@@ -293,17 +312,67 @@ export const SIGN_IN_PATH: string | null = ${literal(values.signInPath)};
293
312
  export const SIGN_UP_PATH: string | null = ${literal(values.signUpPath)};
294
313
  export const SIGN_OUT_ENDPOINT: string | null = ${literal(values.signOutEndpoint)};`;
295
314
  }
315
+ /** The spoke billing action (#8217 D6), relative to the web seam. */
316
+ const SPOKE_BILLING_ACTION_IMPORT = "../app/[locale]/(authed)/account/billing/spoke-billing.action";
317
+ /** The billing types every web seam render carries (#8217 D7). */
318
+ const SPOKE_BILLING_TYPES = `export type SpokeCard = { brand: string; last4: string; expMonth: number; expYear: number };
319
+
320
+ export type SpokeBillingRead =
321
+ | {
322
+ kind: "subscription";
323
+ status: "trialing" | "active" | "past_due" | "canceled";
324
+ planName: string | null;
325
+ paymentMethod: SpokeCard | null;
326
+ unreadable: boolean;
327
+ }
328
+ | { kind: "none"; unreadable: boolean }
329
+ | { kind: "failed" };
330
+
331
+ export type SpokeBilling = {
332
+ read(): Promise<SpokeBillingRead>;
333
+ portalUrl(): Promise<string | null>;
334
+ };`;
335
+ /**
336
+ * `loadSpokeBilling` (#8217 D7): the billing render imports the action, by a
337
+ * dynamic import, so a module that reads this seam for a constant never
338
+ * loads the checkout package; every other render resolves `null`.
339
+ */
340
+ function spokeBillingLoader(spokeBilling) {
341
+ if (!spokeBilling) {
342
+ return `${SPOKE_BILLING_TYPES}
343
+
344
+ export function loadSpokeBilling(): Promise<SpokeBilling | null> {
345
+ return Promise.resolve(null);
346
+ }`;
347
+ }
348
+ return `${SPOKE_BILLING_TYPES}
349
+
350
+ export async function loadSpokeBilling(): Promise<SpokeBilling | null> {
351
+ const { readSpokeBilling, spokeBillingPortalUrl } = await import(
352
+ "${SPOKE_BILLING_ACTION_IMPORT}"
353
+ );
354
+ return { read: readSpokeBilling, portalUrl: spokeBillingPortalUrl };
355
+ }`;
356
+ }
296
357
  /**
297
358
  * `apps/web/lib/topology.generated.ts` for `topology` (D5) and, on a spoke,
298
359
  * its sign-in mode (#7894 D8). The hub render loads `auth.ts` only when a
299
360
  * session is read, so a module that imports this seam for a constant never
300
- * loads Better Auth.
361
+ * loads Better Auth. `opts.spokeBilling` (#8217 D7) makes `loadSpokeBilling`
362
+ * reach the spoke billing action — only on a spoke that signs in through its
363
+ * hub, the one render whose account area can call it.
364
+ * @throws TopologyError for `spokeBilling` on a hub or a default-mode spoke.
301
365
  */
302
- export function renderWebSeam(topology, signIn = "off") {
366
+ export function renderWebSeam(topology, signIn = "off", opts = {}) {
367
+ const spokeBilling = opts.spokeBilling === true;
368
+ if (spokeBilling && (topology !== "spoke" || signIn !== "hub")) {
369
+ throw new TopologyError("the spoke billing loader renders only on a spoke that signs in through its hub (--hub-sign-in)");
370
+ }
303
371
  if (topology === "hub") {
304
372
  return `${WEB_SEAM_HEADER}
305
373
 
306
374
  export const TOPOLOGY: "hub" | "spoke" = "hub";
375
+ ${unservedToolsExport("hub")}
307
376
  ${signInExports({ enabled: true, provider: "site", signInPath: "/sign-in", signUpPath: "/sign-up", signOutEndpoint: null })}
308
377
 
309
378
  ${SITE_SESSION_TYPE}
@@ -317,6 +386,8 @@ export async function registerSiteStores(): Promise<void> {
317
386
  const { registerLicenseKeyStore } = await import("./license-key-store");
318
387
  await registerLicenseKeyStore();
319
388
  }
389
+
390
+ ${spokeBillingLoader(false)}
320
391
  `;
321
392
  }
322
393
  if (signIn === "hub") {
@@ -324,6 +395,7 @@ export async function registerSiteStores(): Promise<void> {
324
395
  import { readHubSession } from "./hub-session/session";
325
396
 
326
397
  export const TOPOLOGY: "hub" | "spoke" = "spoke";
398
+ ${unservedToolsExport("spoke")}
327
399
  ${signInExports({ enabled: true, provider: "hub", signInPath: "/account", signUpPath: null, signOutEndpoint: "/api/hub-session/sign-out" })}
328
400
 
329
401
  ${SITE_SESSION_TYPE}
@@ -335,11 +407,14 @@ export function readSession(headers: Headers): Promise<SiteSession> {
335
407
  export function registerSiteStores(): Promise<void> {
336
408
  return Promise.resolve();
337
409
  }
410
+
411
+ ${spokeBillingLoader(spokeBilling)}
338
412
  `;
339
413
  }
340
414
  return `${WEB_SEAM_HEADER}
341
415
 
342
416
  export const TOPOLOGY: "hub" | "spoke" = "spoke";
417
+ ${unservedToolsExport("spoke")}
343
418
  ${signInExports({ enabled: false, provider: "none", signInPath: null, signUpPath: null, signOutEndpoint: null })}
344
419
 
345
420
  ${SITE_SESSION_TYPE}
@@ -352,6 +427,8 @@ export function readSession(headers: Headers): Promise<SiteSession> {
352
427
  export function registerSiteStores(): Promise<void> {
353
428
  return Promise.resolve();
354
429
  }
430
+
431
+ ${spokeBillingLoader(false)}
355
432
  `;
356
433
  }
357
434
  const AUTHED_GATE_SEAM_HEADER = `// signed-out-gate.generated.tsx — what the (authed) group shows a signed-out visitor.
@@ -504,6 +581,7 @@ export { registerLicenseKeyStore } from "./license-key-store.js";
504
581
  export type { McpWorkspaceResolution };
505
582
 
506
583
  export const TOPOLOGY: "hub" | "spoke" = "hub";
584
+ ${unservedToolsExport("hub")}
507
585
 
508
586
  export async function initPublicWorkspace(): Promise<void> {
509
587
  await initMcpPublicWorkspace();
@@ -523,6 +601,7 @@ export type McpWorkspaceResolution =
523
601
  | { kind: "unresolved"; reason: "ambiguous" };
524
602
 
525
603
  export const TOPOLOGY: "hub" | "spoke" = "spoke";
604
+ ${unservedToolsExport("spoke")}
526
605
 
527
606
  export function initPublicWorkspace(): Promise<void> {
528
607
  return Promise.resolve();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-ailk",
3
- "version": "0.17.0",
3
+ "version": "0.19.0",
4
4
  "description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,8 @@
18
18
  "component-catalog.json",
19
19
  "component-catalog.md",
20
20
  "README.md",
21
- "LICENSE"
21
+ "LICENSE",
22
+ "NOTICE"
22
23
  ],
23
24
  "publishConfig": {
24
25
  "access": "public"
@@ -27,8 +28,8 @@
27
28
  "tar": "^7.5.21"
28
29
  },
29
30
  "devDependencies": {
30
- "@working-theory/templates": "0.38.0",
31
- "@working-theory/schema": "0.38.0"
31
+ "@working-theory/schema": "0.39.0",
32
+ "@working-theory/templates": "0.39.0"
32
33
  },
33
34
  "repository": {
34
35
  "type": "git",
@@ -9,9 +9,9 @@ AILK_PUBLIC_DOMAIN=ailaunchkit.ai
9
9
  # STEALTH defaults to "true" for new installs so a freshly-deployed instance
10
10
  # isn't indexed by crawlers or unfurled by Slack/Twitter/LinkedIn before the
11
11
  # operator is ready. Three layers gate on this single flag:
12
- # 1. apps-paid/marketing-web/public/robots.txt (always-blocking static file)
13
- # 2. X-Robots-Tag: noindex, nofollow, noarchive HTTP header (next.config.ts)
14
- # 3. <meta name="robots" content="noindex,nofollow"> tag (app/layout.tsx)
12
+ # 1. robots.txt answers "Disallow: /" for every crawler (apps/web/app/robots.txt/route.ts)
13
+ # 2. X-Robots-Tag: noindex, nofollow, noarchive HTTP header (apps/web/next.config.mjs)
14
+ # 3. <meta name="robots" content="noindex,nofollow"> tag (apps/web/app/[locale]/layout.tsx)
15
15
  # At launch, set STEALTH=false on the hosting platform's env vars and redeploy.
16
16
  # See README.md § "Going live" for the full flip checklist.
17
17
  STEALTH=true
@@ -54,4 +54,9 @@ jobs:
54
54
 
55
55
  - run: pnpm test
56
56
 
57
+ # apps/web's `next build` with the docs module outgrows Node's default
58
+ # ~2 GB heap and aborts with "JavaScript heap out of memory" (#8069).
59
+ # The hosted runner has ~7 GB; give the build room.
57
60
  - run: pnpm build
61
+ env:
62
+ NODE_OPTIONS: --max-old-space-size=6144